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);
|
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_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);
|
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
|
#ifdef ZSTD_LZ4COMPRESS
|
||||||
int FIO_rust_LZ4_GetBlockSize_FromBlockId(int id);
|
int FIO_rust_LZ4_GetBlockSize_FromBlockId(int id);
|
||||||
#endif
|
#endif
|
||||||
@@ -1912,41 +1913,7 @@ int FIO_compressFilename(FIO_ctx_t* const fCtx, FIO_prefs_t* const prefs, const
|
|||||||
static const char*
|
static const char*
|
||||||
FIO_determineCompressedName(const char* srcFileName, const char* outDirName, const char* suffix)
|
FIO_determineCompressedName(const char* srcFileName, const char* outDirName, const char* suffix)
|
||||||
{
|
{
|
||||||
static size_t dfnbCapacity = 0;
|
return FIO_rust_determineCompressedName(srcFileName, outDirName, suffix);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned long long FIO_getLargestFileSize(const char** inFileNames, unsigned nbFiles)
|
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_ZSTD_COMPRESSION: c_int = 0;
|
||||||
const FIO_OVERLAP_LOG_NOTSET: c_int = 9999;
|
const FIO_OVERLAP_LOG_NOTSET: c_int = 9999;
|
||||||
const FIO_LDM_PARAM_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)]
|
#[repr(C)]
|
||||||
pub struct FIO_inBuffer {
|
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)
|
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]
|
#[inline]
|
||||||
fn filename_path_separator() -> u8 {
|
fn filename_path_separator() -> u8 {
|
||||||
if cfg!(windows) {
|
if cfg!(windows) {
|
||||||
@@ -781,6 +852,31 @@ mod tests {
|
|||||||
assert_eq!(FIO_rust_LZ4_GetBlockSize_FromBlockId(4), 1 << 16);
|
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]
|
#[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