From e02cf8b0522ddb1aac0adc5a987b4a4929b9322c Mon Sep 17 00:00:00 2001 From: ddidderr Date: Sat, 18 Jul 2026 17:54:12 +0200 Subject: [PATCH] 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 --- programs/fileio.c | 43 +-------------- rust/src/fileio_prefs.rs | 115 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 117 insertions(+), 41 deletions(-) diff --git a/programs/fileio.c b/programs/fileio.c index 81b9991d7..8c8e854c2 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -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) diff --git a/rust/src/fileio_prefs.rs b/rust/src/fileio_prefs.rs index f4fa7c732..8b360db44 100644 --- a/rust/src/fileio_prefs.rs +++ b/rust/src/fileio_prefs.rs @@ -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::() }; + 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() };