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
|
/* 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. */
|
* 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);
|
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)
|
static unsigned FIO_highbit64(unsigned long long v)
|
||||||
{
|
{
|
||||||
unsigned count = 0;
|
|
||||||
assert(v != 0);
|
assert(v != 0);
|
||||||
v >>= 1;
|
return FIO_rust_highbit64(v);
|
||||||
while (v) { v >>= 1; count++; }
|
|
||||||
return count;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void FIO_adjustMemLimitForPatchFromMode(FIO_prefs_t* const prefs,
|
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)
|
static unsigned long long FIO_getLargestFileSize(const char** inFileNames, unsigned nbFiles)
|
||||||
{
|
{
|
||||||
size_t i;
|
return FIO_rust_getLargestFileSize(inFileNames, nbFiles);
|
||||||
unsigned long long fileSize, maxFileSize = 0;
|
|
||||||
for (i = 0; i < nbFiles; i++) {
|
|
||||||
fileSize = UTIL_getFileSize(inFileNames[i]);
|
|
||||||
maxFileSize = fileSize > maxFileSize ? fileSize : maxFileSize;
|
|
||||||
}
|
|
||||||
return maxFileSize;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* FIO_compressMultipleFilenames() :
|
/* 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]
|
#[inline]
|
||||||
fn filename_path_separator() -> u8 {
|
fn filename_path_separator() -> u8 {
|
||||||
if cfg!(windows) {
|
if cfg!(windows) {
|
||||||
@@ -561,8 +602,54 @@ pub unsafe extern "C" fn FIO_rust_checkFilenameCollisions(
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
|
use std::fs;
|
||||||
use std::mem::{align_of, offset_of, size_of};
|
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]
|
#[test]
|
||||||
fn c_layouts_match_the_headers() {
|
fn c_layouts_match_the_headers() {
|
||||||
let word = size_of::<usize>();
|
let word = size_of::<usize>();
|
||||||
|
|||||||
Reference in New Issue
Block a user