feat(fileio): move size helpers to Rust
Move the file-stat size scan and highest-bit helper behind Rust ABI implementations while preserving the C file-I/O surface and unknown-size sentinel behavior. Add temporary-file and boundary coverage for the migrated helpers. Test Plan: cargo test --manifest-path rust/cli/Cargo.toml --no-default-features --features cli,compression,decompression,benchmark; cargo test --manifest-path rust/cli/Cargo.toml --no-default-features --features helpers; cargo clippy --manifest-path rust/cli/Cargo.toml --all-targets --no-default-features --features cli,compression,decompression,benchmark; cargo clippy --manifest-path rust/cli/Cargo.toml --all-targets --no-default-features --features helpers; make -B -C programs -j2 zstd zstd-small zstd-frugal; make -B -C tests -j2 test-cli-tests
This commit is contained in:
+4
-11
@@ -307,6 +307,8 @@ static int FIO_shouldDisplayMultipleFileSummary(FIO_ctx_t const* fCtx)
|
||||
/* These symbols are implemented by rust/src/fileio_prefs.rs. The declarations
|
||||
* in fileio.h remain the C ABI shims while all actual file I/O stays here. */
|
||||
int FIO_rust_checkFilenameCollisions(const char** filenameTable, unsigned nbFiles);
|
||||
unsigned FIO_rust_highbit64(unsigned long long v);
|
||||
unsigned long long FIO_rust_getLargestFileSize(const char** inFileNames, unsigned nbFiles);
|
||||
|
||||
|
||||
/*-*************************************
|
||||
@@ -702,11 +704,8 @@ FIO_createFilename_fromOutDir(const char* path, const char* outDirName, const si
|
||||
*/
|
||||
static unsigned FIO_highbit64(unsigned long long v)
|
||||
{
|
||||
unsigned count = 0;
|
||||
assert(v != 0);
|
||||
v >>= 1;
|
||||
while (v) { v >>= 1; count++; }
|
||||
return count;
|
||||
return FIO_rust_highbit64(v);
|
||||
}
|
||||
|
||||
static void FIO_adjustMemLimitForPatchFromMode(FIO_prefs_t* const prefs,
|
||||
@@ -1948,13 +1947,7 @@ FIO_determineCompressedName(const char* srcFileName, const char* outDirName, con
|
||||
|
||||
static unsigned long long FIO_getLargestFileSize(const char** inFileNames, unsigned nbFiles)
|
||||
{
|
||||
size_t i;
|
||||
unsigned long long fileSize, maxFileSize = 0;
|
||||
for (i = 0; i < nbFiles; i++) {
|
||||
fileSize = UTIL_getFileSize(inFileNames[i]);
|
||||
maxFileSize = fileSize > maxFileSize ? fileSize : maxFileSize;
|
||||
}
|
||||
return maxFileSize;
|
||||
return FIO_rust_getLargestFileSize(inFileNames, nbFiles);
|
||||
}
|
||||
|
||||
/* FIO_compressMultipleFilenames() :
|
||||
|
||||
@@ -476,6 +476,47 @@ pub unsafe extern "C" fn FIO_determineHasStdinInput(
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the position of the highest set bit in a non-zero 64-bit value.
|
||||
///
|
||||
/// This mirrors `FIO_highbit64()` and deliberately keeps its zero-input
|
||||
/// assertion semantics. In builds where assertions are disabled, zero
|
||||
/// still produces the same result as the original shift loop.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn FIO_rust_highbit64(value: u64) -> c_uint {
|
||||
debug_assert!(value != 0);
|
||||
if value == 0 {
|
||||
return 0;
|
||||
}
|
||||
(u64::BITS - 1 - value.leading_zeros()) as c_uint
|
||||
}
|
||||
|
||||
fn largest_file_size<I>(sizes: I) -> u64
|
||||
where
|
||||
I: IntoIterator<Item = u64>,
|
||||
{
|
||||
let mut largest = 0;
|
||||
for size in sizes {
|
||||
largest = size.max(largest);
|
||||
}
|
||||
largest
|
||||
}
|
||||
|
||||
/// Rust implementation of the size scan used by `FIO_getLargestFileSize`.
|
||||
///
|
||||
/// `UTIL_getFileSize()` remains responsible for the actual stat operation.
|
||||
/// In particular, an unknown size is `UTIL_FILESIZE_UNKNOWN` and therefore
|
||||
/// wins the max scan exactly as it does in the original C implementation.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn FIO_rust_getLargestFileSize(
|
||||
file_names: *const *const c_char,
|
||||
nb_files: c_uint,
|
||||
) -> u64 {
|
||||
largest_file_size(
|
||||
(0..nb_files as usize)
|
||||
.map(|index| unsafe { crate::util::UTIL_getFileSize(*file_names.add(index)) }),
|
||||
)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn filename_path_separator() -> u8 {
|
||||
if cfg!(windows) {
|
||||
@@ -561,8 +602,54 @@ pub unsafe extern "C" fn FIO_rust_checkFilenameCollisions(
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::ffi::CString;
|
||||
use std::fs;
|
||||
use std::mem::{align_of, offset_of, size_of};
|
||||
|
||||
const UTIL_FILESIZE_UNKNOWN: u64 = u64::MAX;
|
||||
|
||||
fn temporary_file_path(name: &str) -> std::path::PathBuf {
|
||||
std::env::temp_dir().join(format!("zstd-fileio-prefs-{}-{name}", std::process::id()))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn largest_file_size_scan_handles_empty_input() {
|
||||
assert_eq!(unsafe { FIO_rust_getLargestFileSize(ptr::null(), 0) }, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn largest_file_size_scan_handles_ordinary_files() {
|
||||
let smaller_path = temporary_file_path("smaller");
|
||||
let larger_path = temporary_file_path("larger");
|
||||
fs::write(&smaller_path, b"small").unwrap();
|
||||
fs::write(&larger_path, b"larger file").unwrap();
|
||||
let smaller = CString::new(smaller_path.to_string_lossy().as_bytes()).unwrap();
|
||||
let larger = CString::new(larger_path.to_string_lossy().as_bytes()).unwrap();
|
||||
let names = [smaller.as_ptr(), larger.as_ptr()];
|
||||
|
||||
assert_eq!(
|
||||
unsafe { FIO_rust_getLargestFileSize(names.as_ptr(), names.len() as c_uint) },
|
||||
11
|
||||
);
|
||||
|
||||
fs::remove_file(smaller_path).unwrap();
|
||||
fs::remove_file(larger_path).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn largest_file_size_scan_preserves_large_values_and_errors() {
|
||||
assert_eq!(largest_file_size([0, 1u64 << 63, 7]), 1u64 << 63);
|
||||
assert_eq!(FIO_rust_highbit64(1u64 << 63), 63);
|
||||
assert_eq!(FIO_rust_highbit64(u64::MAX), 63);
|
||||
|
||||
let missing_path = temporary_file_path("missing");
|
||||
let missing = CString::new(missing_path.to_string_lossy().as_bytes()).unwrap();
|
||||
let names = [missing.as_ptr()];
|
||||
assert_eq!(
|
||||
unsafe { FIO_rust_getLargestFileSize(names.as_ptr(), 1) },
|
||||
UTIL_FILESIZE_UNKNOWN
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn c_layouts_match_the_headers() {
|
||||
let word = size_of::<usize>();
|
||||
|
||||
Reference in New Issue
Block a user