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
This commit is contained in:
+5
-132
@@ -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_close_fn)(void* opaque);
|
||||||
typedef void (*FIO_rust_compress_src_remove_fn)(void* opaque, const char* srcFileName);
|
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
|
/* Rust owns the source policy and exclusion diagnostic; this projection
|
||||||
* opaque C callbacks. FIO_ctx_t, cRess_t, stat_t, and all resource/diagnostic
|
* exposes only scalar names and opaque callbacks for C-owned operations.
|
||||||
* state remain private to the callback context below. */
|
* FIO_ctx_t, cRess_t, stat_t, and all resource state remain private to the
|
||||||
|
* callback context below. */
|
||||||
typedef struct {
|
typedef struct {
|
||||||
void* opaque;
|
void* opaque;
|
||||||
const char* dstFileName;
|
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];
|
+ 8 * sizeof(FIO_rust_compress_src_stat_fn)) ? 1 : -1];
|
||||||
int FIO_rust_compressFilenameSrcFile(
|
int FIO_rust_compressFilenameSrcFile(
|
||||||
const FIO_rust_compress_src_projection_t* projection);
|
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,
|
static void FIO_adjustParamsForPatchFromMode(FIO_prefs_t* const prefs,
|
||||||
ZSTD_compressionParameters* comprParams,
|
ZSTD_compressionParameters* comprParams,
|
||||||
@@ -3158,126 +3160,6 @@ static int FIO_compressFilename_dstFile(FIO_ctx_t* const fCtx,
|
|||||||
return FIO_rust_compressFilenameDstFile(&projection);
|
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 {
|
typedef struct {
|
||||||
FIO_ctx_t* fCtx;
|
FIO_ctx_t* fCtx;
|
||||||
FIO_prefs_t* prefs;
|
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;
|
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,
|
static int FIO_rust_compressSourceOpen(void* opaque, const char* srcFileName,
|
||||||
U64* fileSize)
|
U64* fileSize)
|
||||||
{
|
{
|
||||||
|
|||||||
+206
-2
@@ -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_SUCCESS: c_int = 0;
|
||||||
const FIO_LIST_INFO_FRAME_ERROR: c_int = 1;
|
const FIO_LIST_INFO_FRAME_ERROR: c_int = 1;
|
||||||
const UTIL_FILESIZE_UNKNOWN: u64 = u64::MAX;
|
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 FIO_ADAPT_WINDOWLOG_DEFAULT: c_uint = 23;
|
||||||
const ZSTD_WINDOWLOG_MIN: u32 = 10;
|
const ZSTD_WINDOWLOG_MIN: u32 = 10;
|
||||||
const ZSTD_WINDOWLOG_MAX: u32 = if size_of::<usize>() == 4 { 30 } else { 31 };
|
const ZSTD_WINDOWLOG_MAX: u32 = if size_of::<usize>() == 4 { 30 } else { 31 };
|
||||||
@@ -1173,12 +1288,61 @@ unsafe fn aio_supported() -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn display(level: c_int, message: &str) {
|
fn display(level: c_int, message: &str) {
|
||||||
let enabled = unsafe { (*display_prefs()).displayLevel >= level };
|
if display_level_enabled(level) {
|
||||||
if enabled {
|
|
||||||
eprint!("{message}");
|
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 {
|
fn checked_option(options: &[&'static str], index: c_int) -> &'static str {
|
||||||
options
|
options
|
||||||
.get(index as usize)
|
.get(index as usize)
|
||||||
@@ -2296,6 +2460,46 @@ mod tests {
|
|||||||
use std::fs;
|
use std::fs;
|
||||||
use std::mem::{align_of, offset_of, size_of};
|
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)]
|
#[derive(Default)]
|
||||||
struct FreeCResourcesTestState {
|
struct FreeCResourcesTestState {
|
||||||
events: Vec<&'static str>,
|
events: Vec<&'static str>,
|
||||||
|
|||||||
Reference in New Issue
Block a user