feat(cli): move compression preference formatting into Rust

The verbose compression-preference summary was still formatted by the C
file-I/O orchestration layer even though the preference object and the rest of
its policy helpers already lived in Rust. Move the option selection and exact
summary formatting into the Rust CLI archive, keeping the public C function as
a narrow assertion-and-dispatch shim. The Rust formatter preserves the legacy
option spellings, default memory limit, integer casts, and stderr output, with
a focused string-format regression test.

Test Plan:
- `cargo fmt --manifest-path rust/cli/Cargo.toml -- --check` -- passed
- `cargo test --manifest-path rust/cli/Cargo.toml --lib fileio_prefs -- --test-threads=1` -- 46 passed
- `git diff --check` -- passed
This commit is contained in:
2026-07-18 17:54:12 +02:00
parent a4a64f2e38
commit e02cf8b052
2 changed files with 117 additions and 41 deletions
+2 -41
View File
@@ -387,6 +387,7 @@ void FIO_rust_freeDict(int dictBufferType,
void** dictHandle);
int FIO_rust_removeFile(const char* path);
int FIO_rust_passThrough(ReadPoolCtx_t* readCtx, WritePoolCtx_t* writeCtx);
void FIO_rust_displayCompressionParameters(const FIO_prefs_t* prefs);
#ifdef ZSTD_LZ4COMPRESS
int FIO_rust_LZ4_GetBlockSize_FromBlockId(int id);
#endif
@@ -1944,52 +1945,12 @@ FIO_compressFilename_srcFile(FIO_ctx_t* const fCtx,
return result;
}
static const char*
checked_index(const char* options[], size_t length, size_t index) {
assert(index < length);
/* Necessary to avoid warnings since -O3 will omit the above `assert` */
(void) length;
return options[index];
}
#define INDEX(options, index) checked_index((options), sizeof(options) / sizeof(char*), (size_t)(index))
void FIO_displayCompressionParameters(const FIO_prefs_t* prefs)
{
static const char* formatOptions[5] = {ZSTD_EXTENSION, GZ_EXTENSION, XZ_EXTENSION,
LZMA_EXTENSION, LZ4_EXTENSION};
static const char* sparseOptions[3] = {" --no-sparse", "", " --sparse"};
static const char* checkSumOptions[3] = {" --no-check", "", " --check"};
static const char* rowMatchFinderOptions[3] = {"", " --no-row-match-finder", " --row-match-finder"};
static const char* compressLiteralsOptions[3] = {"", " --compress-literals", " --no-compress-literals"};
assert(g_display_prefs.displayLevel >= 4);
DISPLAY("--format=%s", formatOptions[prefs->compressionType]);
DISPLAY("%s", INDEX(sparseOptions, prefs->sparseFileSupport));
DISPLAY("%s", prefs->dictIDFlag ? "" : " --no-dictID");
DISPLAY("%s", INDEX(checkSumOptions, prefs->checksumFlag));
DISPLAY(" --block-size=%d", prefs->blockSize);
if (prefs->adaptiveMode)
DISPLAY(" --adapt=min=%d,max=%d", prefs->minAdaptLevel, prefs->maxAdaptLevel);
DISPLAY("%s", INDEX(rowMatchFinderOptions, prefs->useRowMatchFinder));
DISPLAY("%s", prefs->rsyncable ? " --rsyncable" : "");
if (prefs->streamSrcSize)
DISPLAY(" --stream-size=%u", (unsigned) prefs->streamSrcSize);
if (prefs->srcSizeHint)
DISPLAY(" --size-hint=%d", prefs->srcSizeHint);
if (prefs->targetCBlockSize)
DISPLAY(" --target-compressed-block-size=%u", (unsigned) prefs->targetCBlockSize);
DISPLAY("%s", INDEX(compressLiteralsOptions, prefs->literalCompressionMode));
DISPLAY(" --memory=%u", prefs->memLimit ? prefs->memLimit : 128 MB);
DISPLAY(" --threads=%d", prefs->nbWorkers);
DISPLAY("%s", prefs->excludeCompressedFiles ? " --exclude-compressed" : "");
DISPLAY(" --%scontent-size", prefs->contentSize ? "" : "no-");
DISPLAY("\n");
FIO_rust_displayCompressionParameters(prefs);
}
#undef INDEX
int FIO_compressFilename(FIO_ctx_t* const fCtx, FIO_prefs_t* const prefs, const char* dstFileName,
const char* srcFileName, const char* dictFileName,
int compressionLevel, ZSTD_compressionParameters comprParams)
+115
View File
@@ -712,6 +712,89 @@ fn display(level: c_int, message: &str) {
}
}
fn checked_option(options: &[&'static str], index: c_int) -> &'static str {
options
.get(index as usize)
.copied()
.unwrap_or_else(|| std::process::abort())
}
fn format_compression_parameters(prefs: &FIO_prefs_t) -> String {
let format = checked_option(
&[".zst", ".gz", ".xz", ".lzma", ".lz4"],
prefs.compressionType,
);
let sparse = checked_option(&[" --no-sparse", "", " --sparse"], prefs.sparseFileSupport);
let checksum = checked_option(&[" --no-check", "", " --check"], prefs.checksumFlag);
let row_match_finder = checked_option(
&["", " --no-row-match-finder", " --row-match-finder"],
prefs.useRowMatchFinder,
);
let compress_literals = checked_option(
&["", " --compress-literals", " --no-compress-literals"],
prefs.literalCompressionMode,
);
let mut result = format!("--format={format}{sparse}");
if prefs.dictIDFlag == 0 {
result.push_str(" --no-dictID");
}
result.push_str(checksum);
result.push_str(&format!(" --block-size={}", prefs.blockSize));
if prefs.adaptiveMode != 0 {
result.push_str(&format!(
" --adapt=min={},max={}",
prefs.minAdaptLevel, prefs.maxAdaptLevel
));
}
result.push_str(row_match_finder);
if prefs.rsyncable != 0 {
result.push_str(" --rsyncable");
}
if prefs.streamSrcSize != 0 {
result.push_str(&format!(" --stream-size={}", prefs.streamSrcSize as c_uint));
}
if prefs.srcSizeHint != 0 {
result.push_str(&format!(" --size-hint={}", prefs.srcSizeHint));
}
if prefs.targetCBlockSize != 0 {
result.push_str(&format!(
" --target-compressed-block-size={}",
prefs.targetCBlockSize as c_uint
));
}
result.push_str(compress_literals);
result.push_str(&format!(
" --memory={} --threads={}",
if prefs.memLimit != 0 {
prefs.memLimit
} else {
128 * 1024 * 1024
},
prefs.nbWorkers
));
if prefs.excludeCompressedFiles != 0 {
result.push_str(" --exclude-compressed");
}
result.push_str(if prefs.contentSize != 0 {
" --content-size"
} else {
" --no-content-size"
});
result.push('\n');
result
}
/// Rust implementation of the verbose preference summary used by CLI diagnostics.
#[no_mangle]
pub unsafe extern "C" fn FIO_rust_displayCompressionParameters(prefs: *const FIO_prefs_t) {
if prefs.is_null() {
return;
}
let output = format_compression_parameters(unsafe { &*prefs });
eprint!("{output}");
}
fn throw(error: c_int, message: &str) -> ! {
let enabled = unsafe { (*display_prefs()).displayLevel >= 1 };
if enabled {
@@ -2557,6 +2640,38 @@ mod tests {
unsafe { FIO_freePreferences(prefs as *const FIO_prefs_t as *mut FIO_prefs_t) };
}
#[test]
fn compression_parameter_format_matches_fileio_c() {
let mut prefs = unsafe { std::mem::zeroed::<FIO_prefs_t>() };
prefs.compressionType = FIO_ZSTD_COMPRESSION;
prefs.sparseFileSupport = 2;
prefs.dictIDFlag = 0;
prefs.checksumFlag = 2;
prefs.blockSize = 131_072;
prefs.adaptiveMode = 1;
prefs.minAdaptLevel = 1;
prefs.maxAdaptLevel = 9;
prefs.useRowMatchFinder = 1;
prefs.rsyncable = 1;
prefs.streamSrcSize = 4096;
prefs.srcSizeHint = 8192;
prefs.targetCBlockSize = 16_384;
prefs.literalCompressionMode = 2;
prefs.memLimit = 64 * 1024 * 1024;
prefs.nbWorkers = 3;
prefs.excludeCompressedFiles = 1;
prefs.contentSize = 0;
assert_eq!(
format_compression_parameters(&prefs),
"--format=.zst --sparse --no-dictID --check --block-size=131072 ".to_owned()
+ "--adapt=min=1,max=9 --no-row-match-finder --rsyncable "
+ "--stream-size=4096 --size-hint=8192 "
+ "--target-compressed-block-size=16384 --no-compress-literals "
+ "--memory=67108864 --threads=3 --exclude-compressed --no-content-size\n"
);
}
#[test]
fn context_defaults_and_setters_match_fileio_c() {
let ctx = unsafe { FIO_createContext() };