feat(cli): move compressed filename helper to Rust
Port the compressed destination-name helper to the Rust file-I/O preference module. Preserve stdin/stdout sentinels, output-directory basename handling, suffix assembly, static-buffer reuse, and allocation failure behavior behind the existing C shim. Test Plan:\n- cargo test --manifest-path rust/cli/Cargo.toml --no-default-features --features cli,compression,decompression,benchmark\n- cargo test --manifest-path rust/cli/Cargo.toml --no-default-features --features helpers\n- cargo clippy --manifest-path rust/cli/Cargo.toml --all-targets --no-default-features --features cli,compression,decompression,benchmark\n- cargo clippy --manifest-path rust/cli/Cargo.toml --all-targets --no-default-features --features helpers\n- make -B -C programs -j2 zstd zstd-small zstd-frugal\n- make -B -C tests -j2 test-cli-tests
This commit is contained in:
+2
-35
@@ -311,6 +311,7 @@ unsigned FIO_rust_highbit64(unsigned long long v);
|
||||
unsigned long long FIO_rust_getLargestFileSize(const char** inFileNames, unsigned nbFiles);
|
||||
void FIO_rust_setInBuffer(ZSTD_inBuffer* output, const void* buf, size_t s, size_t pos);
|
||||
void FIO_rust_setOutBuffer(ZSTD_outBuffer* output, void* buf, size_t s, size_t pos);
|
||||
const char* FIO_rust_determineCompressedName(const char* srcFileName, const char* outDirName, const char* suffix);
|
||||
#ifdef ZSTD_LZ4COMPRESS
|
||||
int FIO_rust_LZ4_GetBlockSize_FromBlockId(int id);
|
||||
#endif
|
||||
@@ -1912,41 +1913,7 @@ int FIO_compressFilename(FIO_ctx_t* const fCtx, FIO_prefs_t* const prefs, const
|
||||
static const char*
|
||||
FIO_determineCompressedName(const char* srcFileName, const char* outDirName, const char* suffix)
|
||||
{
|
||||
static size_t dfnbCapacity = 0;
|
||||
static char* dstFileNameBuffer = NULL; /* using static allocation : this function cannot be multi-threaded */
|
||||
char* outDirFilename = NULL;
|
||||
size_t sfnSize = strlen(srcFileName);
|
||||
size_t const srcSuffixLen = strlen(suffix);
|
||||
|
||||
if(!strcmp(srcFileName, stdinmark)) {
|
||||
return stdoutmark;
|
||||
}
|
||||
|
||||
if (outDirName) {
|
||||
outDirFilename = FIO_createFilename_fromOutDir(srcFileName, outDirName, srcSuffixLen);
|
||||
sfnSize = strlen(outDirFilename);
|
||||
assert(outDirFilename != NULL);
|
||||
}
|
||||
|
||||
if (dfnbCapacity <= sfnSize+srcSuffixLen+1) {
|
||||
/* resize buffer for dstName */
|
||||
free(dstFileNameBuffer);
|
||||
dfnbCapacity = sfnSize + srcSuffixLen + 30;
|
||||
dstFileNameBuffer = (char*)malloc(dfnbCapacity);
|
||||
if (!dstFileNameBuffer) {
|
||||
EXM_THROW(30, "zstd: %s", strerror(errno));
|
||||
}
|
||||
}
|
||||
assert(dstFileNameBuffer != NULL);
|
||||
|
||||
if (outDirFilename) {
|
||||
memcpy(dstFileNameBuffer, outDirFilename, sfnSize);
|
||||
free(outDirFilename);
|
||||
} else {
|
||||
memcpy(dstFileNameBuffer, srcFileName, sfnSize);
|
||||
}
|
||||
memcpy(dstFileNameBuffer+sfnSize, suffix, srcSuffixLen+1 /* Include terminating null */);
|
||||
return dstFileNameBuffer;
|
||||
return FIO_rust_determineCompressedName(srcFileName, outDirName, suffix);
|
||||
}
|
||||
|
||||
static unsigned long long FIO_getLargestFileSize(const char** inFileNames, unsigned nbFiles)
|
||||
|
||||
@@ -19,6 +19,10 @@ use std::ptr;
|
||||
const FIO_ZSTD_COMPRESSION: c_int = 0;
|
||||
const FIO_OVERLAP_LOG_NOTSET: c_int = 9999;
|
||||
const FIO_LDM_PARAM_NOTSET: c_int = 9999;
|
||||
static STDOUT_MARK: &[u8] = b"/*stdout*\\\0";
|
||||
|
||||
static mut COMPRESSED_NAME_CAPACITY: usize = 0;
|
||||
static mut COMPRESSED_NAME_BUFFER: *mut c_char = ptr::null_mut();
|
||||
|
||||
#[repr(C)]
|
||||
pub struct FIO_inBuffer {
|
||||
@@ -578,6 +582,73 @@ pub extern "C" fn FIO_rust_LZ4_GetBlockSize_FromBlockId(id: c_int) -> c_int {
|
||||
lz4_block_size_from_block_id(id)
|
||||
}
|
||||
|
||||
/// Build the compressed destination name used by the multi-file CLI path.
|
||||
///
|
||||
/// This deliberately keeps the original static allocation contract: callers
|
||||
/// must consume the returned pointer before the next call, and the helper is
|
||||
/// not thread-safe. The output-directory basename construction is shared
|
||||
/// with `UTIL_createFilenameFromOutDir`, while suffix assembly remains here.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn FIO_rust_determineCompressedName(
|
||||
src_file_name: *const c_char,
|
||||
out_dir_name: *const c_char,
|
||||
suffix: *const c_char,
|
||||
) -> *const c_char {
|
||||
let source = unsafe { CStr::from_ptr(src_file_name).to_bytes() };
|
||||
let suffix_bytes = unsafe { CStr::from_ptr(suffix).to_bytes() };
|
||||
|
||||
if source == b"/*stdin*\\" {
|
||||
return STDOUT_MARK.as_ptr().cast();
|
||||
}
|
||||
|
||||
let mut out_dir_filename = ptr::null_mut();
|
||||
let source_bytes = if out_dir_name.is_null() {
|
||||
source
|
||||
} else {
|
||||
out_dir_filename = unsafe {
|
||||
crate::util::UTIL_createFilenameFromOutDir(
|
||||
src_file_name,
|
||||
out_dir_name,
|
||||
suffix_bytes.len(),
|
||||
)
|
||||
};
|
||||
assert!(!out_dir_filename.is_null());
|
||||
unsafe { CStr::from_ptr(out_dir_filename).to_bytes() }
|
||||
};
|
||||
|
||||
let source_len = source_bytes.len();
|
||||
let suffix_len = suffix_bytes.len();
|
||||
let required = source_len.wrapping_add(suffix_len).wrapping_add(1);
|
||||
|
||||
unsafe {
|
||||
let capacity = ptr::addr_of_mut!(COMPRESSED_NAME_CAPACITY);
|
||||
let buffer = ptr::addr_of_mut!(COMPRESSED_NAME_BUFFER);
|
||||
if capacity.read() <= required {
|
||||
libc::free(buffer.read().cast::<c_void>());
|
||||
let new_capacity = source_len.wrapping_add(suffix_len).wrapping_add(30);
|
||||
let new_buffer = libc::malloc(new_capacity).cast::<c_char>();
|
||||
if new_buffer.is_null() {
|
||||
throw(30, "Allocation error : not enough memory");
|
||||
}
|
||||
capacity.write(new_capacity);
|
||||
buffer.write(new_buffer);
|
||||
}
|
||||
|
||||
let destination = buffer.read();
|
||||
assert!(!destination.is_null());
|
||||
ptr::copy_nonoverlapping(source_bytes.as_ptr(), destination.cast(), source_len);
|
||||
ptr::copy_nonoverlapping(
|
||||
suffix_bytes.as_ptr(),
|
||||
destination.add(source_len).cast(),
|
||||
suffix_len + 1,
|
||||
);
|
||||
if !out_dir_filename.is_null() {
|
||||
libc::free(out_dir_filename.cast::<c_void>());
|
||||
}
|
||||
destination.cast()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn filename_path_separator() -> u8 {
|
||||
if cfg!(windows) {
|
||||
@@ -781,6 +852,31 @@ mod tests {
|
||||
assert_eq!(FIO_rust_LZ4_GetBlockSize_FromBlockId(4), 1 << 16);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compressed_filename_shim_preserves_sentinels_suffixes_and_output_dirs() {
|
||||
let source = CString::new("input/nested/file").unwrap();
|
||||
let suffix = CString::new(".zst").unwrap();
|
||||
let output_dir = CString::new("out").unwrap();
|
||||
|
||||
unsafe {
|
||||
let name =
|
||||
FIO_rust_determineCompressedName(source.as_ptr(), ptr::null(), suffix.as_ptr());
|
||||
assert_eq!(CStr::from_ptr(name).to_bytes(), b"input/nested/file.zst");
|
||||
|
||||
let name = FIO_rust_determineCompressedName(
|
||||
source.as_ptr(),
|
||||
output_dir.as_ptr(),
|
||||
suffix.as_ptr(),
|
||||
);
|
||||
assert_eq!(CStr::from_ptr(name).to_bytes(), b"out/file.zst");
|
||||
|
||||
let stdin = CString::new("/*stdin*\\").unwrap();
|
||||
let name =
|
||||
FIO_rust_determineCompressedName(stdin.as_ptr(), ptr::null(), suffix.as_ptr());
|
||||
assert_eq!(CStr::from_ptr(name).to_bytes(), b"/*stdout*\\");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn c_layouts_match_the_headers() {
|
||||
let word = size_of::<usize>();
|
||||
|
||||
Reference in New Issue
Block a user