feat(cli): move dictionary mmap lifecycle into Rust
Move the platform-specific dictionary mapping and release operations behind the Rust CLI archive while keeping size policy, diagnostics, and FIO_Dict_t ownership decisions in C. POSIX descriptors and Windows handles are cleaned up on every failure path, and zero-length dictionaries use an owned sentinel so the existing non-null dictionary invariant remains valid. Test Plan: - cargo test --manifest-path rust/cli/Cargo.toml --lib fileio_backend (24 passed) - cargo clippy --manifest-path rust/cli/Cargo.toml --lib -- -D warnings - make -C programs -j2 zstd - --mmap-dict compression/decompression round trip with programs/README.md - programs/zstd --mmap-dict --test on the generated frame - rustfmt +nightly --edition 2021 rust/src/fileio_backend.rs --check - git diff --check
This commit is contained in:
+67
-46
@@ -366,6 +366,22 @@ int FIO_rust_setDictBufferMalloc(const char* fileName,
|
|||||||
size_t maxSize,
|
size_t maxSize,
|
||||||
void** buffer,
|
void** buffer,
|
||||||
size_t* loadedSize);
|
size_t* loadedSize);
|
||||||
|
#if (PLATFORM_POSIX_VERSION > 0) || defined(_MSC_VER) || defined(_WIN32)
|
||||||
|
enum {
|
||||||
|
FIO_DICT_MMAP_SUCCESS = 0,
|
||||||
|
FIO_DICT_MMAP_OPEN_FAILED = 1,
|
||||||
|
FIO_DICT_MMAP_TOO_LARGE = 2,
|
||||||
|
FIO_DICT_MMAP_FAILED = 3,
|
||||||
|
FIO_DICT_MMAP_VIEW_FAILED = 4,
|
||||||
|
};
|
||||||
|
int FIO_rust_setDictBufferMMap(const char* fileName,
|
||||||
|
unsigned long long expectedFileSize,
|
||||||
|
size_t maxSize,
|
||||||
|
void** buffer,
|
||||||
|
size_t* mappedSize,
|
||||||
|
void** dictHandle);
|
||||||
|
void FIO_rust_munmapDict(void** buffer, size_t* bufferSize, void** dictHandle);
|
||||||
|
#endif
|
||||||
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
|
||||||
@@ -640,18 +656,16 @@ static size_t FIO_setDictBufferMalloc(FIO_Dict_t* dict, const char* fileName, FI
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if (PLATFORM_POSIX_VERSION > 0)
|
#if (PLATFORM_POSIX_VERSION > 0)
|
||||||
#include <sys/mman.h>
|
|
||||||
static void FIO_munmap(FIO_Dict_t* dict)
|
static void FIO_munmap(FIO_Dict_t* dict)
|
||||||
{
|
{
|
||||||
munmap(dict->dictBuffer, dict->dictBufferSize);
|
FIO_rust_munmapDict(&dict->dictBuffer, &dict->dictBufferSize, NULL);
|
||||||
dict->dictBuffer = NULL;
|
|
||||||
dict->dictBufferSize = 0;
|
|
||||||
}
|
}
|
||||||
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)
|
||||||
{
|
{
|
||||||
int fileHandle;
|
|
||||||
U64 fileSize;
|
U64 fileSize;
|
||||||
|
size_t const dictSizeMax = prefs->patchFromMode ? prefs->memLimit : DICTSIZE_MAX;
|
||||||
void** bufferPtr = &dict->dictBuffer;
|
void** bufferPtr = &dict->dictBuffer;
|
||||||
|
int status;
|
||||||
|
|
||||||
assert(bufferPtr != NULL);
|
assert(bufferPtr != NULL);
|
||||||
assert(dictFileStat != NULL);
|
assert(dictFileStat != NULL);
|
||||||
@@ -660,41 +674,45 @@ static size_t FIO_setDictBufferMMap(FIO_Dict_t* dict, const char* fileName, FIO_
|
|||||||
|
|
||||||
DISPLAYLEVEL(4,"Loading %s as dictionary \n", fileName);
|
DISPLAYLEVEL(4,"Loading %s as dictionary \n", fileName);
|
||||||
|
|
||||||
fileHandle = open(fileName, O_RDONLY);
|
fileSize = UTIL_getFileSizeStat(dictFileStat);
|
||||||
|
if (fileSize > dictSizeMax) {
|
||||||
|
EXM_THROW(34, "Dictionary file %s is too large (> %u bytes)",
|
||||||
|
fileName, (unsigned)dictSizeMax); /* avoid extreme cases */
|
||||||
|
}
|
||||||
|
|
||||||
if (fileHandle == -1) {
|
status = FIO_rust_setDictBufferMMap(fileName,
|
||||||
|
(unsigned long long)fileSize,
|
||||||
|
dictSizeMax,
|
||||||
|
bufferPtr,
|
||||||
|
&dict->dictBufferSize,
|
||||||
|
NULL);
|
||||||
|
if (status == FIO_DICT_MMAP_SUCCESS) return dict->dictBufferSize;
|
||||||
|
if (status == FIO_DICT_MMAP_OPEN_FAILED) {
|
||||||
EXM_THROW(33, "Couldn't open dictionary %s: %s", fileName, strerror(errno));
|
EXM_THROW(33, "Couldn't open dictionary %s: %s", fileName, strerror(errno));
|
||||||
}
|
}
|
||||||
|
if (status == FIO_DICT_MMAP_TOO_LARGE) {
|
||||||
fileSize = UTIL_getFileSizeStat(dictFileStat);
|
EXM_THROW(34, "Dictionary file %s is too large (> %u bytes)",
|
||||||
{
|
fileName, (unsigned)dictSizeMax); /* avoid extreme cases */
|
||||||
size_t const dictSizeMax = prefs->patchFromMode ? prefs->memLimit : DICTSIZE_MAX;
|
|
||||||
if (fileSize > dictSizeMax) {
|
|
||||||
EXM_THROW(34, "Dictionary file %s is too large (> %u bytes)",
|
|
||||||
fileName, (unsigned)dictSizeMax); /* avoid extreme cases */
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if (status == FIO_DICT_MMAP_FAILED) {
|
||||||
*bufferPtr = mmap(NULL, (size_t)fileSize, PROT_READ, MAP_PRIVATE, fileHandle, 0);
|
EXM_THROW(34, "%s", strerror(errno));
|
||||||
if (*bufferPtr==NULL) EXM_THROW(34, "%s", strerror(errno));
|
}
|
||||||
|
assert(0); /* unexpected Rust status */
|
||||||
close(fileHandle);
|
return 0;
|
||||||
return (size_t)fileSize;
|
|
||||||
}
|
}
|
||||||
#elif defined(_MSC_VER) || defined(_WIN32)
|
#elif defined(_MSC_VER) || defined(_WIN32)
|
||||||
#include <windows.h>
|
|
||||||
static void FIO_munmap(FIO_Dict_t* dict)
|
static void FIO_munmap(FIO_Dict_t* dict)
|
||||||
{
|
{
|
||||||
UnmapViewOfFile(dict->dictBuffer);
|
FIO_rust_munmapDict(&dict->dictBuffer,
|
||||||
CloseHandle(dict->dictHandle);
|
&dict->dictBufferSize,
|
||||||
dict->dictBuffer = NULL;
|
(void**)&dict->dictHandle);
|
||||||
dict->dictBufferSize = 0;
|
|
||||||
}
|
}
|
||||||
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)
|
||||||
{
|
{
|
||||||
HANDLE fileHandle, mapping;
|
|
||||||
U64 fileSize;
|
U64 fileSize;
|
||||||
|
size_t const dictSizeMax = prefs->patchFromMode ? prefs->memLimit : DICTSIZE_MAX;
|
||||||
void** bufferPtr = &dict->dictBuffer;
|
void** bufferPtr = &dict->dictBuffer;
|
||||||
|
int status;
|
||||||
|
|
||||||
assert(bufferPtr != NULL);
|
assert(bufferPtr != NULL);
|
||||||
assert(dictFileStat != NULL);
|
assert(dictFileStat != NULL);
|
||||||
@@ -703,31 +721,34 @@ static size_t FIO_setDictBufferMMap(FIO_Dict_t* dict, const char* fileName, FIO_
|
|||||||
|
|
||||||
DISPLAYLEVEL(4,"Loading %s as dictionary \n", fileName);
|
DISPLAYLEVEL(4,"Loading %s as dictionary \n", fileName);
|
||||||
|
|
||||||
fileHandle = CreateFileA(fileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL);
|
fileSize = UTIL_getFileSizeStat(dictFileStat);
|
||||||
|
if (fileSize > dictSizeMax) {
|
||||||
|
EXM_THROW(34, "Dictionary file %s is too large (> %u bytes)",
|
||||||
|
fileName, (unsigned)dictSizeMax); /* avoid extreme cases */
|
||||||
|
}
|
||||||
|
|
||||||
if (fileHandle == INVALID_HANDLE_VALUE) {
|
status = FIO_rust_setDictBufferMMap(fileName,
|
||||||
|
(unsigned long long)fileSize,
|
||||||
|
dictSizeMax,
|
||||||
|
bufferPtr,
|
||||||
|
&dict->dictBufferSize,
|
||||||
|
(void**)&dict->dictHandle);
|
||||||
|
if (status == FIO_DICT_MMAP_SUCCESS) return dict->dictBufferSize;
|
||||||
|
if (status == FIO_DICT_MMAP_OPEN_FAILED) {
|
||||||
EXM_THROW(33, "Couldn't open dictionary %s: %s", fileName, strerror(errno));
|
EXM_THROW(33, "Couldn't open dictionary %s: %s", fileName, strerror(errno));
|
||||||
}
|
}
|
||||||
|
if (status == FIO_DICT_MMAP_TOO_LARGE) {
|
||||||
fileSize = UTIL_getFileSizeStat(dictFileStat);
|
EXM_THROW(34, "Dictionary file %s is too large (> %u bytes)",
|
||||||
{
|
fileName, (unsigned)dictSizeMax); /* avoid extreme cases */
|
||||||
size_t const dictSizeMax = prefs->patchFromMode ? prefs->memLimit : DICTSIZE_MAX;
|
|
||||||
if (fileSize > dictSizeMax) {
|
|
||||||
EXM_THROW(34, "Dictionary file %s is too large (> %u bytes)",
|
|
||||||
fileName, (unsigned)dictSizeMax); /* avoid extreme cases */
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if (status == FIO_DICT_MMAP_FAILED) {
|
||||||
mapping = CreateFileMapping(fileHandle, NULL, PAGE_READONLY, 0, 0, NULL);
|
|
||||||
if (mapping == NULL) {
|
|
||||||
EXM_THROW(35, "Couldn't map dictionary %s: %s", fileName, strerror(errno));
|
EXM_THROW(35, "Couldn't map dictionary %s: %s", fileName, strerror(errno));
|
||||||
}
|
}
|
||||||
|
if (status == FIO_DICT_MMAP_VIEW_FAILED) {
|
||||||
*bufferPtr = MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, (DWORD)fileSize); /* we can only cast to DWORD here because dictSize <= 2GB */
|
EXM_THROW(36, "%s", strerror(errno));
|
||||||
if (*bufferPtr==NULL) EXM_THROW(36, "%s", strerror(errno));
|
}
|
||||||
|
assert(0); /* unexpected Rust status */
|
||||||
dict->dictHandle = fileHandle;
|
return 0;
|
||||||
return (size_t)fileSize;
|
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
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)
|
||||||
|
|||||||
@@ -27,6 +27,14 @@ const FIO_DICT_LOAD_TOO_LARGE: c_int = 2;
|
|||||||
const FIO_DICT_LOAD_ALLOCATION_FAILED: c_int = 3;
|
const FIO_DICT_LOAD_ALLOCATION_FAILED: c_int = 3;
|
||||||
const FIO_DICT_LOAD_READ_FAILED: c_int = 4;
|
const FIO_DICT_LOAD_READ_FAILED: c_int = 4;
|
||||||
|
|
||||||
|
const FIO_DICT_MMAP_SUCCESS: c_int = 0;
|
||||||
|
const FIO_DICT_MMAP_OPEN_FAILED: c_int = 1;
|
||||||
|
const FIO_DICT_MMAP_TOO_LARGE: c_int = 2;
|
||||||
|
const FIO_DICT_MMAP_FAILED: c_int = 3;
|
||||||
|
#[cfg(windows)]
|
||||||
|
const FIO_DICT_MMAP_VIEW_FAILED: c_int = 4;
|
||||||
|
const FIO_DICT_MMAP_EMPTY_ALLOCATION_SIZE: usize = 1;
|
||||||
|
|
||||||
const FIO_DICT_STAT_SUCCESS: c_int = 0;
|
const FIO_DICT_STAT_SUCCESS: c_int = 0;
|
||||||
const FIO_DICT_STAT_FAILED: c_int = 1;
|
const FIO_DICT_STAT_FAILED: c_int = 1;
|
||||||
const FIO_DICT_STAT_NON_REGULAR: c_int = 2;
|
const FIO_DICT_STAT_NON_REGULAR: c_int = 2;
|
||||||
@@ -44,6 +52,65 @@ unsafe extern "C" {
|
|||||||
fn fopen(file_name: *const c_char, mode: *const c_char) -> *mut c_void;
|
fn fopen(file_name: *const c_char, mode: *const c_char) -> *mut c_void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
const INVALID_HANDLE_VALUE: *mut c_void = -1isize as *mut c_void;
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
const GENERIC_READ: u32 = 0x8000_0000;
|
||||||
|
#[cfg(windows)]
|
||||||
|
const FILE_SHARE_READ: u32 = 0x0000_0001;
|
||||||
|
#[cfg(windows)]
|
||||||
|
const OPEN_EXISTING: u32 = 3;
|
||||||
|
#[cfg(windows)]
|
||||||
|
const FILE_ATTRIBUTE_READONLY: u32 = 0x0000_0001;
|
||||||
|
#[cfg(windows)]
|
||||||
|
const PAGE_READONLY: u32 = 0x02;
|
||||||
|
#[cfg(windows)]
|
||||||
|
const FILE_MAP_READ: u32 = 0x0004;
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
#[link(name = "kernel32")]
|
||||||
|
unsafe extern "system" {
|
||||||
|
fn CreateFileA(
|
||||||
|
file_name: *const c_char,
|
||||||
|
desired_access: u32,
|
||||||
|
share_mode: u32,
|
||||||
|
security_attributes: *mut c_void,
|
||||||
|
creation_disposition: u32,
|
||||||
|
flags_and_attributes: u32,
|
||||||
|
template_file: *mut c_void,
|
||||||
|
) -> *mut c_void;
|
||||||
|
fn CreateFileMappingA(
|
||||||
|
file: *mut c_void,
|
||||||
|
attributes: *mut c_void,
|
||||||
|
protect: u32,
|
||||||
|
maximum_size_high: u32,
|
||||||
|
maximum_size_low: u32,
|
||||||
|
name: *const c_char,
|
||||||
|
) -> *mut c_void;
|
||||||
|
fn MapViewOfFile(
|
||||||
|
mapping: *mut c_void,
|
||||||
|
desired_access: u32,
|
||||||
|
file_offset_high: u32,
|
||||||
|
file_offset_low: u32,
|
||||||
|
number_of_bytes_to_map: usize,
|
||||||
|
) -> *mut c_void;
|
||||||
|
fn UnmapViewOfFile(address: *const c_void) -> i32;
|
||||||
|
fn CloseHandle(handle: *mut c_void) -> i32;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn publish_empty_dict_mapping(buffer: *mut *mut c_void, mapped_size: *mut usize) -> c_int {
|
||||||
|
let allocation = unsafe { libc::malloc(FIO_DICT_MMAP_EMPTY_ALLOCATION_SIZE) };
|
||||||
|
if allocation.is_null() {
|
||||||
|
return FIO_DICT_MMAP_FAILED;
|
||||||
|
}
|
||||||
|
unsafe {
|
||||||
|
*buffer = allocation;
|
||||||
|
*mapped_size = 0;
|
||||||
|
}
|
||||||
|
FIO_DICT_MMAP_SUCCESS
|
||||||
|
}
|
||||||
|
|
||||||
fn path_from_c(path: *const c_char) -> Option<PathBuf> {
|
fn path_from_c(path: *const c_char) -> Option<PathBuf> {
|
||||||
if path.is_null() {
|
if path.is_null() {
|
||||||
return None;
|
return None;
|
||||||
@@ -212,6 +279,190 @@ pub unsafe extern "C" fn FIO_rust_setDictBufferMalloc(
|
|||||||
FIO_DICT_LOAD_SUCCESS
|
FIO_DICT_LOAD_SUCCESS
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Maps a dictionary file while leaving policy and diagnostics in the C CLI.
|
||||||
|
///
|
||||||
|
/// `mapped_size` is the file size, not the allocation size used for an empty
|
||||||
|
/// dictionary. The release leaf treats a non-null zero-sized mapping as that
|
||||||
|
/// one-byte owned allocation and frees it instead of calling `munmap()` with a
|
||||||
|
/// zero length.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn FIO_rust_setDictBufferMMap(
|
||||||
|
file_name: *const c_char,
|
||||||
|
expected_file_size: u64,
|
||||||
|
max_size: usize,
|
||||||
|
buffer: *mut *mut c_void,
|
||||||
|
mapped_size: *mut usize,
|
||||||
|
dict_handle: *mut *mut c_void,
|
||||||
|
) -> c_int {
|
||||||
|
if buffer.is_null() || mapped_size.is_null() {
|
||||||
|
return FIO_DICT_MMAP_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
*buffer = ptr::null_mut();
|
||||||
|
*mapped_size = 0;
|
||||||
|
if !dict_handle.is_null() {
|
||||||
|
*dict_handle = ptr::null_mut();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let Some(expected_size) = usize::try_from(expected_file_size).ok() else {
|
||||||
|
return FIO_DICT_MMAP_TOO_LARGE;
|
||||||
|
};
|
||||||
|
if expected_size > max_size {
|
||||||
|
return FIO_DICT_MMAP_TOO_LARGE;
|
||||||
|
}
|
||||||
|
if file_name.is_null() {
|
||||||
|
return FIO_DICT_MMAP_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
{
|
||||||
|
let file_descriptor = unsafe { libc::open(file_name, libc::O_RDONLY) };
|
||||||
|
if file_descriptor < 0 {
|
||||||
|
return FIO_DICT_MMAP_OPEN_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if expected_size == 0 {
|
||||||
|
unsafe { libc::close(file_descriptor) };
|
||||||
|
return unsafe { publish_empty_dict_mapping(buffer, mapped_size) };
|
||||||
|
}
|
||||||
|
|
||||||
|
let mapping = unsafe {
|
||||||
|
libc::mmap(
|
||||||
|
ptr::null_mut(),
|
||||||
|
expected_size,
|
||||||
|
libc::PROT_READ,
|
||||||
|
libc::MAP_PRIVATE,
|
||||||
|
file_descriptor,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
unsafe { libc::close(file_descriptor) };
|
||||||
|
if mapping == (-1isize as *mut c_void) {
|
||||||
|
return FIO_DICT_MMAP_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
*buffer = mapping;
|
||||||
|
*mapped_size = expected_size;
|
||||||
|
}
|
||||||
|
FIO_DICT_MMAP_SUCCESS
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
{
|
||||||
|
let file_handle = unsafe {
|
||||||
|
CreateFileA(
|
||||||
|
file_name,
|
||||||
|
GENERIC_READ,
|
||||||
|
FILE_SHARE_READ,
|
||||||
|
ptr::null_mut(),
|
||||||
|
OPEN_EXISTING,
|
||||||
|
FILE_ATTRIBUTE_READONLY,
|
||||||
|
ptr::null_mut(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
if file_handle.is_null() || file_handle == INVALID_HANDLE_VALUE {
|
||||||
|
return FIO_DICT_MMAP_OPEN_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if expected_size == 0 {
|
||||||
|
unsafe { CloseHandle(file_handle) };
|
||||||
|
return unsafe { publish_empty_dict_mapping(buffer, mapped_size) };
|
||||||
|
}
|
||||||
|
|
||||||
|
let mapping = unsafe {
|
||||||
|
CreateFileMappingA(
|
||||||
|
file_handle,
|
||||||
|
ptr::null_mut(),
|
||||||
|
PAGE_READONLY,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
ptr::null(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
if mapping.is_null() {
|
||||||
|
unsafe { CloseHandle(file_handle) };
|
||||||
|
return FIO_DICT_MMAP_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
let view = unsafe { MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, expected_size) };
|
||||||
|
unsafe { CloseHandle(mapping) };
|
||||||
|
if view.is_null() {
|
||||||
|
unsafe { CloseHandle(file_handle) };
|
||||||
|
return FIO_DICT_MMAP_VIEW_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if dict_handle.is_null() {
|
||||||
|
unsafe { CloseHandle(file_handle) };
|
||||||
|
return FIO_DICT_MMAP_FAILED;
|
||||||
|
}
|
||||||
|
unsafe {
|
||||||
|
*buffer = view;
|
||||||
|
*mapped_size = expected_size;
|
||||||
|
*dict_handle = file_handle;
|
||||||
|
}
|
||||||
|
FIO_DICT_MMAP_SUCCESS
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(any(unix, windows)))]
|
||||||
|
{
|
||||||
|
FIO_DICT_MMAP_FAILED
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Releases a dictionary mapping and clears all C-visible ownership fields.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn FIO_rust_munmapDict(
|
||||||
|
buffer: *mut *mut c_void,
|
||||||
|
buffer_size: *mut usize,
|
||||||
|
dict_handle: *mut *mut c_void,
|
||||||
|
) {
|
||||||
|
if buffer.is_null() || buffer_size.is_null() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mapping = unsafe { *buffer };
|
||||||
|
let size = unsafe { *buffer_size };
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
{
|
||||||
|
if !mapping.is_null() {
|
||||||
|
if size == 0 {
|
||||||
|
unsafe { libc::free(mapping) };
|
||||||
|
} else {
|
||||||
|
unsafe { libc::munmap(mapping, size) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
{
|
||||||
|
if !mapping.is_null() {
|
||||||
|
if size == 0 {
|
||||||
|
unsafe { libc::free(mapping) };
|
||||||
|
} else {
|
||||||
|
unsafe { UnmapViewOfFile(mapping) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !dict_handle.is_null() {
|
||||||
|
let handle = unsafe { *dict_handle };
|
||||||
|
if !handle.is_null() && handle != INVALID_HANDLE_VALUE {
|
||||||
|
unsafe { CloseHandle(handle) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
*buffer = ptr::null_mut();
|
||||||
|
*buffer_size = 0;
|
||||||
|
if !dict_handle.is_null() {
|
||||||
|
*dict_handle = ptr::null_mut();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
@@ -388,6 +639,66 @@ mod tests {
|
|||||||
fs::remove_file(&path).expect("remove temporary file");
|
fs::remove_file(&path).expect("remove temporary file");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
#[test]
|
||||||
|
fn maps_and_releases_dictionary() {
|
||||||
|
let path = temp_path("mapped-dictionary");
|
||||||
|
let payload = b"mapped dictionary payload";
|
||||||
|
fs::write(&path, payload).expect("create temporary file");
|
||||||
|
let c_path = c_path(&path);
|
||||||
|
let mut buffer = ptr::null_mut();
|
||||||
|
let mut mapped_size = usize::MAX;
|
||||||
|
|
||||||
|
let status = unsafe {
|
||||||
|
FIO_rust_setDictBufferMMap(
|
||||||
|
c_path.as_ptr(),
|
||||||
|
payload.len() as u64,
|
||||||
|
payload.len(),
|
||||||
|
&mut buffer,
|
||||||
|
&mut mapped_size,
|
||||||
|
ptr::null_mut(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
assert_eq!(status, FIO_DICT_MMAP_SUCCESS);
|
||||||
|
assert_eq!(mapped_size, payload.len());
|
||||||
|
let mapped = unsafe { std::slice::from_raw_parts(buffer.cast::<u8>(), mapped_size) };
|
||||||
|
assert_eq!(mapped, payload);
|
||||||
|
|
||||||
|
unsafe { FIO_rust_munmapDict(&mut buffer, &mut mapped_size, ptr::null_mut()) };
|
||||||
|
assert!(buffer.is_null());
|
||||||
|
assert_eq!(mapped_size, 0);
|
||||||
|
fs::remove_file(path).expect("remove temporary file");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
#[test]
|
||||||
|
fn maps_empty_dictionary_with_owned_zero_size_sentinel() {
|
||||||
|
let path = temp_path("mapped-empty-dictionary");
|
||||||
|
fs::write(&path, b"").expect("create empty temporary file");
|
||||||
|
let c_path = c_path(&path);
|
||||||
|
let mut buffer = ptr::null_mut();
|
||||||
|
let mut mapped_size = usize::MAX;
|
||||||
|
|
||||||
|
let status = unsafe {
|
||||||
|
FIO_rust_setDictBufferMMap(
|
||||||
|
c_path.as_ptr(),
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
&mut buffer,
|
||||||
|
&mut mapped_size,
|
||||||
|
ptr::null_mut(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
assert_eq!(status, FIO_DICT_MMAP_SUCCESS);
|
||||||
|
assert!(!buffer.is_null());
|
||||||
|
assert_eq!(mapped_size, 0);
|
||||||
|
|
||||||
|
unsafe { FIO_rust_munmapDict(&mut buffer, &mut mapped_size, ptr::null_mut()) };
|
||||||
|
assert!(buffer.is_null());
|
||||||
|
assert_eq!(mapped_size, 0);
|
||||||
|
fs::remove_file(path).expect("remove temporary file");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn accepts_regular_dictionary_file() {
|
fn accepts_regular_dictionary_file() {
|
||||||
let path = temp_path("regular-dictionary-stat");
|
let path = temp_path("regular-dictionary-stat");
|
||||||
|
|||||||
Reference in New Issue
Block a user