From 882ad7ccef060d4cfc0edc146c5f97197f78baa5 Mon Sep 17 00:00:00 2001 From: ddidderr Date: Mon, 20 Jul 2026 15:56:53 +0200 Subject: [PATCH] refactor(cli): move compressed source exclusion to Rust The Rust source-file scheduler already owns the ordering around source exclusion, but its suffix policy and diagnostic still depended on a C table and helper. That left the policy leaf on the C side of the boundary and made the Rust scheduler call back into a C-owned decision. Move the complete 113-entry, case-sensitive suffix policy into the Rust CLI file-I/O layer and export the exclusion callback through the existing C ABI. The callback preserves leading dots, the stdin and NULL non-match behavior, the 0/1 return policy, and the exact display-level-4 diagnostic while leaving C resource, compression, and asynchronous callbacks unchanged. Focused checks cover representative suffixes, case sensitivity, stdin/NULL handling, and the display gate. Test Plan: - `cc -fsyntax-only -Iprograms -Ilib -Ilib/common programs/fileio.c` -- passed - Exact extracted C/Rust suffix-list comparison -- passed; all 113 entries match - `git diff --check` and `git diff --cached --check` -- passed - Rust unit tests and Cargo/Make/native suites were not run per request - `rustfmt +nightly --check --edition 2021 rust/src/fileio_prefs.rs` -- not clean because it reports pre-existing formatting drift in unchanged code --- programs/fileio.c | 137 +------------------------- rust/src/fileio_prefs.rs | 208 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 211 insertions(+), 134 deletions(-) diff --git a/programs/fileio.c b/programs/fileio.c index 5becad0b2..e6047b967 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -1659,9 +1659,10 @@ typedef int (*FIO_rust_compress_src_compress_fn)(void* opaque, typedef void (*FIO_rust_compress_src_close_fn)(void* opaque); typedef void (*FIO_rust_compress_src_remove_fn)(void* opaque, const char* srcFileName); -/* Rust owns the source policy; this projection exposes only scalar names and - * opaque C callbacks. FIO_ctx_t, cRess_t, stat_t, and all resource/diagnostic - * state remain private to the callback context below. */ +/* Rust owns the source policy and exclusion diagnostic; this projection + * exposes only scalar names and opaque callbacks for C-owned operations. + * FIO_ctx_t, cRess_t, stat_t, and all resource state remain private to the + * callback context below. */ typedef struct { void* opaque; const char* dstFileName; @@ -1708,6 +1709,7 @@ typedef char FIO_rust_compress_src_projection_size[ + 8 * sizeof(FIO_rust_compress_src_stat_fn)) ? 1 : -1]; int FIO_rust_compressFilenameSrcFile( const FIO_rust_compress_src_projection_t* projection); +int FIO_rust_compressSourceIsExcluded(void* opaque, const char* srcFileName); static void FIO_adjustParamsForPatchFromMode(FIO_prefs_t* const prefs, ZSTD_compressionParameters* comprParams, @@ -3158,126 +3160,6 @@ static int FIO_compressFilename_dstFile(FIO_ctx_t* const fCtx, return FIO_rust_compressFilenameDstFile(&projection); } -/* List used to compare file extensions (used with --exclude-compressed flag) -* Different from the suffixList and should only apply to ZSTD compress operationResult -*/ -static const char *compressedFileExtensions[] = { - ZSTD_EXTENSION, - TZSTD_EXTENSION, - GZ_EXTENSION, - TGZ_EXTENSION, - LZMA_EXTENSION, - XZ_EXTENSION, - TXZ_EXTENSION, - LZ4_EXTENSION, - TLZ4_EXTENSION, - ".7z", - ".aa3", - ".aac", - ".aar", - ".ace", - ".alac", - ".ape", - ".apk", - ".apng", - ".arc", - ".archive", - ".arj", - ".ark", - ".asf", - ".avi", - ".avif", - ".ba", - ".br", - ".bz2", - ".cab", - ".cdx", - ".chm", - ".cr2", - ".divx", - ".dmg", - ".dng", - ".docm", - ".docx", - ".dotm", - ".dotx", - ".dsft", - ".ear", - ".eftx", - ".emz", - ".eot", - ".epub", - ".f4v", - ".flac", - ".flv", - ".gho", - ".gif", - ".gifv", - ".gnp", - ".iso", - ".jar", - ".jpeg", - ".jpg", - ".jxl", - ".lz", - ".lzh", - ".m4a", - ".m4v", - ".mkv", - ".mov", - ".mp2", - ".mp3", - ".mp4", - ".mpa", - ".mpc", - ".mpe", - ".mpeg", - ".mpg", - ".mpl", - ".mpv", - ".msi", - ".odp", - ".ods", - ".odt", - ".ogg", - ".ogv", - ".otp", - ".ots", - ".ott", - ".pea", - ".png", - ".pptx", - ".qt", - ".rar", - ".s7z", - ".sfx", - ".sit", - ".sitx", - ".sqx", - ".svgz", - ".swf", - ".tbz2", - ".tib", - ".tlz", - ".vob", - ".war", - ".webm", - ".webp", - ".wma", - ".wmv", - ".woff", - ".woff2", - ".wvl", - ".xlsx", - ".xpi", - ".xps", - ".zip", - ".zipx", - ".zoo", - ".zpaq", - NULL -}; - typedef struct { FIO_ctx_t* fCtx; FIO_prefs_t* prefs; @@ -3311,15 +3193,6 @@ static int FIO_rust_compressSourceStat(void* opaque, const char* srcFileName) return FIO_RUST_COMPRESS_SRC_STAT_OK; } -static int FIO_rust_compressSourceIsExcluded(void* opaque, const char* srcFileName) -{ - int const isCompressed = UTIL_isCompressedFile(srcFileName, compressedFileExtensions); - (void)opaque; - if (isCompressed) - DISPLAYLEVEL(4, "File is already compressed : %s \n", srcFileName); - return isCompressed; -} - static int FIO_rust_compressSourceOpen(void* opaque, const char* srcFileName, U64* fileSize) { diff --git a/rust/src/fileio_prefs.rs b/rust/src/fileio_prefs.rs index 44359f7d7..d95200b57 100644 --- a/rust/src/fileio_prefs.rs +++ b/rust/src/fileio_prefs.rs @@ -31,6 +31,121 @@ const FIO_MULTI_FILES_ACTION_CONFIRM: c_int = 5; const FIO_LIST_INFO_SUCCESS: c_int = 0; const FIO_LIST_INFO_FRAME_ERROR: c_int = 1; const UTIL_FILESIZE_UNKNOWN: u64 = u64::MAX; +const COMPRESSED_FILE_EXTENSIONS: &[&[u8]] = &[ + b".zst", + b".tzst", + b".gz", + b".tgz", + b".lzma", + b".xz", + b".txz", + b".lz4", + b".tlz4", + b".7z", + b".aa3", + b".aac", + b".aar", + b".ace", + b".alac", + b".ape", + b".apk", + b".apng", + b".arc", + b".archive", + b".arj", + b".ark", + b".asf", + b".avi", + b".avif", + b".ba", + b".br", + b".bz2", + b".cab", + b".cdx", + b".chm", + b".cr2", + b".divx", + b".dmg", + b".dng", + b".docm", + b".docx", + b".dotm", + b".dotx", + b".dsft", + b".ear", + b".eftx", + b".emz", + b".eot", + b".epub", + b".f4v", + b".flac", + b".flv", + b".gho", + b".gif", + b".gifv", + b".gnp", + b".iso", + b".jar", + b".jpeg", + b".jpg", + b".jxl", + b".lz", + b".lzh", + b".m4a", + b".m4v", + b".mkv", + b".mov", + b".mp2", + b".mp3", + b".mp4", + b".mpa", + b".mpc", + b".mpe", + b".mpeg", + b".mpg", + b".mpl", + b".mpv", + b".msi", + b".odp", + b".ods", + b".odt", + b".ogg", + b".ogv", + b".otp", + b".ots", + b".ott", + b".pea", + b".png", + b".pptx", + b".qt", + b".rar", + b".s7z", + b".sfx", + b".sit", + b".sitx", + b".sqx", + b".svgz", + b".swf", + b".tbz2", + b".tib", + b".tlz", + b".vob", + b".war", + b".webm", + b".webp", + b".wma", + b".wmv", + b".woff", + b".woff2", + b".wvl", + b".xlsx", + b".xpi", + b".xps", + b".zip", + b".zipx", + b".zoo", + b".zpaq", +]; const FIO_ADAPT_WINDOWLOG_DEFAULT: c_uint = 23; const ZSTD_WINDOWLOG_MIN: u32 = 10; const ZSTD_WINDOWLOG_MAX: u32 = if size_of::() == 4 { 30 } else { 31 }; @@ -1173,12 +1288,61 @@ unsafe fn aio_supported() -> bool { } fn display(level: c_int, message: &str) { - let enabled = unsafe { (*display_prefs()).displayLevel >= level }; - if enabled { + if display_level_enabled(level) { eprint!("{message}"); } } +#[inline] +fn display_level_enabled(level: c_int) -> bool { + unsafe { (*display_prefs()).displayLevel >= level } +} + +#[inline] +fn compressed_file_name_is_excluded(src_file_name: *const c_char) -> bool { + if src_file_name.is_null() { + return false; + } + + let source = unsafe { CStr::from_ptr(src_file_name).to_bytes() }; + let Some(extension_start) = source.iter().rposition(|&byte| byte == b'.') else { + return false; + }; + if extension_start == 0 { + return false; + } + + COMPRESSED_FILE_EXTENSIONS + .iter() + .any(|extension| *extension == &source[extension_start..]) +} + +fn display_already_compressed(src_file_name: *const c_char) { + if !display_level_enabled(4) { + return; + } + + let source = unsafe { CStr::from_ptr(src_file_name).to_bytes() }; + let mut stderr = io::stderr().lock(); + let _ = stderr.write_all(b"File is already compressed : "); + let _ = stderr.write_all(source); + let _ = stderr.write_all(b" \n"); +} + +/// Return whether a source file has an extension excluded by +/// `--exclude-compressed`, preserving the C callback's exact return policy. +#[no_mangle] +pub unsafe extern "C" fn FIO_rust_compressSourceIsExcluded( + _opaque: *mut c_void, + src_file_name: *const c_char, +) -> c_int { + let is_compressed = compressed_file_name_is_excluded(src_file_name); + if is_compressed { + display_already_compressed(src_file_name); + } + c_int::from(is_compressed) +} + fn checked_option(options: &[&'static str], index: c_int) -> &'static str { options .get(index as usize) @@ -2296,6 +2460,46 @@ mod tests { use std::fs; use std::mem::{align_of, offset_of, size_of}; + #[test] + fn source_exclusion_preserves_case_sensitive_suffixes_and_null_policy() { + let cases = [ + ("archive.zst", 1), + ("archive.tlz4", 1), + ("photo.webp", 1), + ("archive.zpaq", 1), + ("archive.ZST", 0), + ("archive.zstd", 0), + ("plain.txt", 0), + (".mp3", 0), + ("/*stdin*\\", 0), + ]; + + for (file_name, expected) in cases { + let file_name = CString::new(file_name).unwrap(); + assert_eq!( + unsafe { FIO_rust_compressSourceIsExcluded(ptr::null_mut(), file_name.as_ptr()) }, + expected, + "unexpected exclusion result for {file_name:?}" + ); + } + + assert_eq!( + unsafe { FIO_rust_compressSourceIsExcluded(ptr::null_mut(), ptr::null()) }, + 0 + ); + } + + #[test] + fn source_exclusion_diagnostic_is_gated_at_display_level_four() { + FIO_setNotificationLevel(3); + assert!(!display_level_enabled(4)); + + FIO_setNotificationLevel(4); + assert!(display_level_enabled(4)); + + FIO_setNotificationLevel(2); + } + #[derive(Default)] struct FreeCResourcesTestState { events: Vec<&'static str>,