feat(cli): move dictionary release dispatch into Rust
Route FIO_freeDict through the Rust filesystem backend for malloc-backed and mapped dictionaries. Keep enum validation in C, pair Windows views with their file handles, support the non-POSIX malloc fallback, and clear every ownership field so repeated cleanup remains harmless. Test Plan: - cargo test --manifest-path rust/cli/Cargo.toml --lib fileio_backend -- --test-threads=1 (25 passed) - cargo clippy --manifest-path rust/cli/Cargo.toml --lib -- -D warnings - rustfmt +nightly --edition 2021 rust/src/fileio_backend.rs --check - make -C programs -j2 zstd - git diff --check
This commit is contained in:
+20
-23
@@ -380,8 +380,11 @@ int FIO_rust_setDictBufferMMap(const char* fileName,
|
|||||||
void** buffer,
|
void** buffer,
|
||||||
size_t* mappedSize,
|
size_t* mappedSize,
|
||||||
void** dictHandle);
|
void** dictHandle);
|
||||||
void FIO_rust_munmapDict(void** buffer, size_t* bufferSize, void** dictHandle);
|
|
||||||
#endif
|
#endif
|
||||||
|
void FIO_rust_freeDict(int dictBufferType,
|
||||||
|
void** buffer,
|
||||||
|
size_t* bufferSize,
|
||||||
|
void** dictHandle);
|
||||||
int FIO_rust_removeFile(const char* path);
|
int FIO_rust_removeFile(const char* path);
|
||||||
int FIO_rust_passThrough(ReadPoolCtx_t* readCtx, WritePoolCtx_t* writeCtx);
|
int FIO_rust_passThrough(ReadPoolCtx_t* readCtx, WritePoolCtx_t* writeCtx);
|
||||||
#ifdef ZSTD_LZ4COMPRESS
|
#ifdef ZSTD_LZ4COMPRESS
|
||||||
@@ -656,10 +659,6 @@ static size_t FIO_setDictBufferMalloc(FIO_Dict_t* dict, const char* fileName, FI
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if (PLATFORM_POSIX_VERSION > 0)
|
#if (PLATFORM_POSIX_VERSION > 0)
|
||||||
static void FIO_munmap(FIO_Dict_t* dict)
|
|
||||||
{
|
|
||||||
FIO_rust_munmapDict(&dict->dictBuffer, &dict->dictBufferSize, NULL);
|
|
||||||
}
|
|
||||||
static size_t FIO_setDictBufferMMap(FIO_Dict_t* dict, const char* fileName, FIO_prefs_t* const prefs, stat_t* dictFileStat)
|
static size_t FIO_setDictBufferMMap(FIO_Dict_t* dict, const char* fileName, FIO_prefs_t* const prefs, stat_t* dictFileStat)
|
||||||
{
|
{
|
||||||
U64 fileSize;
|
U64 fileSize;
|
||||||
@@ -701,12 +700,6 @@ static size_t FIO_setDictBufferMMap(FIO_Dict_t* dict, const char* fileName, FIO_
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#elif defined(_MSC_VER) || defined(_WIN32)
|
#elif defined(_MSC_VER) || defined(_WIN32)
|
||||||
static void FIO_munmap(FIO_Dict_t* dict)
|
|
||||||
{
|
|
||||||
FIO_rust_munmapDict(&dict->dictBuffer,
|
|
||||||
&dict->dictBufferSize,
|
|
||||||
(void**)&dict->dictHandle);
|
|
||||||
}
|
|
||||||
static size_t FIO_setDictBufferMMap(FIO_Dict_t* dict, const char* fileName, FIO_prefs_t* const prefs, stat_t* dictFileStat)
|
static size_t FIO_setDictBufferMMap(FIO_Dict_t* dict, const char* fileName, FIO_prefs_t* const prefs, stat_t* dictFileStat)
|
||||||
{
|
{
|
||||||
U64 fileSize;
|
U64 fileSize;
|
||||||
@@ -755,26 +748,30 @@ static size_t FIO_setDictBufferMMap(FIO_Dict_t* dict, const char* fileName, FIO_
|
|||||||
{
|
{
|
||||||
return FIO_setDictBufferMalloc(dict, fileName, prefs, dictFileStat);
|
return FIO_setDictBufferMalloc(dict, fileName, prefs, dictFileStat);
|
||||||
}
|
}
|
||||||
static void FIO_munmap(FIO_Dict_t* dict) {
|
|
||||||
free(dict->dictBuffer);
|
|
||||||
dict->dictBuffer = NULL;
|
|
||||||
dict->dictBufferSize = 0;
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void FIO_freeDict(FIO_Dict_t* dict) {
|
static void FIO_freeDict(FIO_Dict_t* dict) {
|
||||||
if (dict->dictBufferType == FIO_mallocDict) {
|
if (dict->dictBufferType != FIO_mallocDict
|
||||||
free(dict->dictBuffer);
|
&& dict->dictBufferType != FIO_mmapDict) {
|
||||||
dict->dictBuffer = NULL;
|
|
||||||
dict->dictBufferSize = 0;
|
|
||||||
} else if (dict->dictBufferType == FIO_mmapDict) {
|
|
||||||
FIO_munmap(dict);
|
|
||||||
} else {
|
|
||||||
assert(0); /* Should not reach this case */
|
assert(0); /* Should not reach this case */
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FIO_rust_freeDict((int)dict->dictBufferType,
|
||||||
|
&dict->dictBuffer,
|
||||||
|
&dict->dictBufferSize,
|
||||||
|
#if defined(_MSC_VER) || defined(_WIN32)
|
||||||
|
(void**)&dict->dictHandle
|
||||||
|
#else
|
||||||
|
NULL
|
||||||
|
#endif
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void FIO_initDict(FIO_Dict_t* dict, const char* fileName, FIO_prefs_t* const prefs, stat_t* dictFileStat, FIO_dictBufferType_t dictBufferType) {
|
static void FIO_initDict(FIO_Dict_t* dict, const char* fileName, FIO_prefs_t* const prefs, stat_t* dictFileStat, FIO_dictBufferType_t dictBufferType) {
|
||||||
|
#if defined(_MSC_VER) || defined(_WIN32)
|
||||||
|
dict->dictHandle = NULL;
|
||||||
|
#endif
|
||||||
dict->dictBufferType = dictBufferType;
|
dict->dictBufferType = dictBufferType;
|
||||||
if (dict->dictBufferType == FIO_mallocDict) {
|
if (dict->dictBufferType == FIO_mallocDict) {
|
||||||
dict->dictBufferSize = FIO_setDictBufferMalloc(dict, fileName, prefs, dictFileStat);
|
dict->dictBufferSize = FIO_setDictBufferMalloc(dict, fileName, prefs, dictFileStat);
|
||||||
|
|||||||
@@ -454,6 +454,13 @@ pub unsafe extern "C" fn FIO_rust_munmapDict(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(any(unix, windows)))]
|
||||||
|
{
|
||||||
|
if !mapping.is_null() {
|
||||||
|
unsafe { libc::free(mapping) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
*buffer = ptr::null_mut();
|
*buffer = ptr::null_mut();
|
||||||
*buffer_size = 0;
|
*buffer_size = 0;
|
||||||
@@ -463,6 +470,47 @@ pub unsafe extern "C" fn FIO_rust_munmapDict(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const FIO_MALLOC_DICT: c_int = 0;
|
||||||
|
const FIO_MMAP_DICT: c_int = 1;
|
||||||
|
|
||||||
|
/// Releases either kind of dictionary buffer and clears its C-visible fields.
|
||||||
|
///
|
||||||
|
/// C retains the enum validation and calls this leaf only for the two valid
|
||||||
|
/// `FIO_dictBufferType_t` values. Mapped buffers go through the existing
|
||||||
|
/// platform-aware release leaf so Windows views and file handles remain paired
|
||||||
|
/// correctly, while malloc-backed buffers use the matching libc allocator.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn FIO_rust_freeDict(
|
||||||
|
dict_buffer_type: c_int,
|
||||||
|
buffer: *mut *mut c_void,
|
||||||
|
buffer_size: *mut usize,
|
||||||
|
dict_handle: *mut *mut c_void,
|
||||||
|
) {
|
||||||
|
match dict_buffer_type {
|
||||||
|
FIO_MALLOC_DICT => {
|
||||||
|
if !buffer.is_null() {
|
||||||
|
let allocation = unsafe { *buffer };
|
||||||
|
if !allocation.is_null() {
|
||||||
|
unsafe { libc::free(allocation) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unsafe {
|
||||||
|
if !buffer.is_null() {
|
||||||
|
*buffer = ptr::null_mut();
|
||||||
|
}
|
||||||
|
if !buffer_size.is_null() {
|
||||||
|
*buffer_size = 0;
|
||||||
|
}
|
||||||
|
if !dict_handle.is_null() {
|
||||||
|
*dict_handle = ptr::null_mut();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FIO_MMAP_DICT => unsafe { FIO_rust_munmapDict(buffer, buffer_size, dict_handle) },
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
@@ -664,12 +712,59 @@ mod tests {
|
|||||||
let mapped = unsafe { std::slice::from_raw_parts(buffer.cast::<u8>(), mapped_size) };
|
let mapped = unsafe { std::slice::from_raw_parts(buffer.cast::<u8>(), mapped_size) };
|
||||||
assert_eq!(mapped, payload);
|
assert_eq!(mapped, payload);
|
||||||
|
|
||||||
unsafe { FIO_rust_munmapDict(&mut buffer, &mut mapped_size, ptr::null_mut()) };
|
let mut dict_handle = 1usize as *mut c_void;
|
||||||
|
unsafe {
|
||||||
|
FIO_rust_freeDict(
|
||||||
|
FIO_MMAP_DICT,
|
||||||
|
&mut buffer,
|
||||||
|
&mut mapped_size,
|
||||||
|
&mut dict_handle,
|
||||||
|
)
|
||||||
|
};
|
||||||
assert!(buffer.is_null());
|
assert!(buffer.is_null());
|
||||||
assert_eq!(mapped_size, 0);
|
assert_eq!(mapped_size, 0);
|
||||||
|
assert!(dict_handle.is_null());
|
||||||
|
|
||||||
|
// The release leaf is idempotent after the dispatch has cleared the
|
||||||
|
// ownership fields, so cleanup cannot double-release the mapping.
|
||||||
|
unsafe { FIO_rust_munmapDict(&mut buffer, &mut mapped_size, &mut dict_handle) };
|
||||||
fs::remove_file(path).expect("remove temporary file");
|
fs::remove_file(path).expect("remove temporary file");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn frees_malloc_dictionary_and_clears_ownership() {
|
||||||
|
let allocation = unsafe { libc::malloc(16) };
|
||||||
|
assert!(!allocation.is_null());
|
||||||
|
let mut buffer = allocation;
|
||||||
|
let mut buffer_size = 16;
|
||||||
|
let mut dict_handle = 1usize as *mut c_void;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
FIO_rust_freeDict(
|
||||||
|
FIO_MALLOC_DICT,
|
||||||
|
&mut buffer,
|
||||||
|
&mut buffer_size,
|
||||||
|
&mut dict_handle,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
assert!(buffer.is_null());
|
||||||
|
assert_eq!(buffer_size, 0);
|
||||||
|
assert!(dict_handle.is_null());
|
||||||
|
|
||||||
|
// A second release sees only cleared fields and must not double-free.
|
||||||
|
unsafe {
|
||||||
|
FIO_rust_freeDict(
|
||||||
|
FIO_MALLOC_DICT,
|
||||||
|
&mut buffer,
|
||||||
|
&mut buffer_size,
|
||||||
|
&mut dict_handle,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
assert!(buffer.is_null());
|
||||||
|
assert_eq!(buffer_size, 0);
|
||||||
|
assert!(dict_handle.is_null());
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
#[test]
|
#[test]
|
||||||
fn maps_empty_dictionary_with_owned_zero_size_sentinel() {
|
fn maps_empty_dictionary_with_owned_zero_size_sentinel() {
|
||||||
|
|||||||
Reference in New Issue
Block a user