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:
@@ -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