refactor(cli): classify dictionary load diagnostics in Rust
The file-I/O wrapper already delegated dictionary loading to Rust but kept all status interpretation in C. That duplicated the malloc and mmap status families and made the platform-specific error branches part of the C policy surface. Add a Rust classifier that maps the shared numeric loader statuses to diagnostic actions, including the distinct mmap failure classes. Keep metadata lookup, platform handles, ownership, and the exact EXM_THROW text in C, where the configured platform APIs still belong. The classifier also rejects out-of-range type/status values; the valid malloc and mmap enums intentionally share numeric values and therefore cannot be distinguished beyond their family tag. Test Plan: - `ulimit -v 41943040; cargo +nightly fmt --manifest-path rust/Cargo.toml --all -- --check` -- passed - `ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo clippy --manifest-path rust/cli/Cargo.toml --all-targets -- -D warnings` -- passed - `ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo test --manifest-path rust/cli/Cargo.toml --all-targets` -- passed (192 tests) - Broader native and original-test verification remains pending for the complete batch.
This commit is contained in:
+64
-43
@@ -378,6 +378,20 @@ int FIO_rust_setDictBufferMalloc(const char* fileName,
|
||||
size_t maxSize,
|
||||
void** buffer,
|
||||
size_t* loadedSize);
|
||||
/* Rust owns the dictionary-loader status classification. C keeps the
|
||||
* metadata, ownership handles, and exact diagnostics around the existing
|
||||
* narrow loader status ABI. */
|
||||
enum {
|
||||
FIO_RUST_DICT_DIAGNOSTIC_OK = 0,
|
||||
FIO_RUST_DICT_DIAGNOSTIC_OPEN_FAILED = 1,
|
||||
FIO_RUST_DICT_DIAGNOSTIC_TOO_LARGE = 2,
|
||||
FIO_RUST_DICT_DIAGNOSTIC_ALLOCATION_FAILED = 3,
|
||||
FIO_RUST_DICT_DIAGNOSTIC_READ_FAILED = 4,
|
||||
FIO_RUST_DICT_DIAGNOSTIC_MAP_FAILED = 5,
|
||||
FIO_RUST_DICT_DIAGNOSTIC_VIEW_FAILED = 6,
|
||||
FIO_RUST_DICT_DIAGNOSTIC_INVALID = 7,
|
||||
};
|
||||
int FIO_rust_dictLoadDiagnostic(int dictBufferType, int status);
|
||||
#if (PLATFORM_POSIX_VERSION > 0) || defined(_MSC_VER) || defined(_WIN32)
|
||||
enum {
|
||||
FIO_DICT_MMAP_SUCCESS = 0,
|
||||
@@ -805,6 +819,7 @@ static size_t FIO_setDictBufferMalloc(FIO_Dict_t* dict, const char* fileName, FI
|
||||
FIO_DICT_LOAD_READ_FAILED = 4,
|
||||
};
|
||||
int status;
|
||||
int diagnostic;
|
||||
|
||||
assert(bufferPtr != NULL);
|
||||
assert(dictFileStat != NULL);
|
||||
@@ -824,23 +839,24 @@ static size_t FIO_setDictBufferMalloc(FIO_Dict_t* dict, const char* fileName, FI
|
||||
dictSizeMax,
|
||||
bufferPtr,
|
||||
&loadedSize);
|
||||
if (status == FIO_DICT_LOAD_SUCCESS) return loadedSize;
|
||||
if (status == FIO_DICT_LOAD_OPEN_FAILED) {
|
||||
EXM_THROW(33, "Couldn't open dictionary %s: %s", fileName, strerror(errno));
|
||||
diagnostic = FIO_rust_dictLoadDiagnostic((int)FIO_mallocDict, status);
|
||||
switch (diagnostic) {
|
||||
case FIO_RUST_DICT_DIAGNOSTIC_OK:
|
||||
return loadedSize;
|
||||
case FIO_RUST_DICT_DIAGNOSTIC_OPEN_FAILED:
|
||||
EXM_THROW(33, "Couldn't open dictionary %s: %s", fileName, strerror(errno));
|
||||
case FIO_RUST_DICT_DIAGNOSTIC_TOO_LARGE:
|
||||
EXM_THROW(34, "Dictionary file %s is too large (> %u bytes)",
|
||||
fileName, (unsigned)dictSizeMax); /* avoid extreme cases */
|
||||
case FIO_RUST_DICT_DIAGNOSTIC_ALLOCATION_FAILED:
|
||||
EXM_THROW(34, "%s", strerror(errno));
|
||||
case FIO_RUST_DICT_DIAGNOSTIC_READ_FAILED:
|
||||
EXM_THROW(35, "Error reading dictionary file %s : %s",
|
||||
fileName, strerror(errno));
|
||||
default:
|
||||
assert(0); /* unexpected Rust diagnostic */
|
||||
return 0;
|
||||
}
|
||||
if (status == FIO_DICT_LOAD_TOO_LARGE) {
|
||||
EXM_THROW(34, "Dictionary file %s is too large (> %u bytes)",
|
||||
fileName, (unsigned)dictSizeMax); /* avoid extreme cases */
|
||||
}
|
||||
if (status == FIO_DICT_LOAD_ALLOCATION_FAILED) {
|
||||
EXM_THROW(34, "%s", strerror(errno));
|
||||
}
|
||||
if (status == FIO_DICT_LOAD_READ_FAILED) {
|
||||
EXM_THROW(35, "Error reading dictionary file %s : %s",
|
||||
fileName, strerror(errno));
|
||||
}
|
||||
assert(0); /* unexpected Rust status */
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if (PLATFORM_POSIX_VERSION > 0)
|
||||
@@ -850,6 +866,7 @@ static size_t FIO_setDictBufferMMap(FIO_Dict_t* dict, const char* fileName, FIO_
|
||||
size_t const dictSizeMax = prefs->patchFromMode ? prefs->memLimit : DICTSIZE_MAX;
|
||||
void** bufferPtr = &dict->dictBuffer;
|
||||
int status;
|
||||
int diagnostic;
|
||||
|
||||
assert(bufferPtr != NULL);
|
||||
assert(dictFileStat != NULL);
|
||||
@@ -870,19 +887,21 @@ static size_t FIO_setDictBufferMMap(FIO_Dict_t* dict, const char* fileName, FIO_
|
||||
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));
|
||||
diagnostic = FIO_rust_dictLoadDiagnostic((int)FIO_mmapDict, status);
|
||||
switch (diagnostic) {
|
||||
case FIO_RUST_DICT_DIAGNOSTIC_OK:
|
||||
return dict->dictBufferSize;
|
||||
case FIO_RUST_DICT_DIAGNOSTIC_OPEN_FAILED:
|
||||
EXM_THROW(33, "Couldn't open dictionary %s: %s", fileName, strerror(errno));
|
||||
case FIO_RUST_DICT_DIAGNOSTIC_TOO_LARGE:
|
||||
EXM_THROW(34, "Dictionary file %s is too large (> %u bytes)",
|
||||
fileName, (unsigned)dictSizeMax); /* avoid extreme cases */
|
||||
case FIO_RUST_DICT_DIAGNOSTIC_MAP_FAILED:
|
||||
EXM_THROW(34, "%s", strerror(errno));
|
||||
default:
|
||||
assert(0); /* unexpected Rust diagnostic */
|
||||
return 0;
|
||||
}
|
||||
if (status == FIO_DICT_MMAP_TOO_LARGE) {
|
||||
EXM_THROW(34, "Dictionary file %s is too large (> %u bytes)",
|
||||
fileName, (unsigned)dictSizeMax); /* avoid extreme cases */
|
||||
}
|
||||
if (status == FIO_DICT_MMAP_FAILED) {
|
||||
EXM_THROW(34, "%s", strerror(errno));
|
||||
}
|
||||
assert(0); /* unexpected Rust status */
|
||||
return 0;
|
||||
}
|
||||
#elif defined(_MSC_VER) || defined(_WIN32)
|
||||
static size_t FIO_setDictBufferMMap(FIO_Dict_t* dict, const char* fileName, FIO_prefs_t* const prefs, stat_t* dictFileStat)
|
||||
@@ -891,6 +910,7 @@ static size_t FIO_setDictBufferMMap(FIO_Dict_t* dict, const char* fileName, FIO_
|
||||
size_t const dictSizeMax = prefs->patchFromMode ? prefs->memLimit : DICTSIZE_MAX;
|
||||
void** bufferPtr = &dict->dictBuffer;
|
||||
int status;
|
||||
int diagnostic;
|
||||
|
||||
assert(bufferPtr != NULL);
|
||||
assert(dictFileStat != NULL);
|
||||
@@ -911,22 +931,23 @@ static size_t FIO_setDictBufferMMap(FIO_Dict_t* dict, const char* fileName, FIO_
|
||||
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));
|
||||
diagnostic = FIO_rust_dictLoadDiagnostic((int)FIO_mmapDict, status);
|
||||
switch (diagnostic) {
|
||||
case FIO_RUST_DICT_DIAGNOSTIC_OK:
|
||||
return dict->dictBufferSize;
|
||||
case FIO_RUST_DICT_DIAGNOSTIC_OPEN_FAILED:
|
||||
EXM_THROW(33, "Couldn't open dictionary %s: %s", fileName, strerror(errno));
|
||||
case FIO_RUST_DICT_DIAGNOSTIC_TOO_LARGE:
|
||||
EXM_THROW(34, "Dictionary file %s is too large (> %u bytes)",
|
||||
fileName, (unsigned)dictSizeMax); /* avoid extreme cases */
|
||||
case FIO_RUST_DICT_DIAGNOSTIC_MAP_FAILED:
|
||||
EXM_THROW(35, "Couldn't map dictionary %s: %s", fileName, strerror(errno));
|
||||
case FIO_RUST_DICT_DIAGNOSTIC_VIEW_FAILED:
|
||||
EXM_THROW(36, "%s", strerror(errno));
|
||||
default:
|
||||
assert(0); /* unexpected Rust diagnostic */
|
||||
return 0;
|
||||
}
|
||||
if (status == FIO_DICT_MMAP_TOO_LARGE) {
|
||||
EXM_THROW(34, "Dictionary file %s is too large (> %u bytes)",
|
||||
fileName, (unsigned)dictSizeMax); /* avoid extreme cases */
|
||||
}
|
||||
if (status == FIO_DICT_MMAP_FAILED) {
|
||||
EXM_THROW(35, "Couldn't map dictionary %s: %s", fileName, strerror(errno));
|
||||
}
|
||||
if (status == FIO_DICT_MMAP_VIEW_FAILED) {
|
||||
EXM_THROW(36, "%s", strerror(errno));
|
||||
}
|
||||
assert(0); /* unexpected Rust status */
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static size_t FIO_setDictBufferMMap(FIO_Dict_t* dict, const char* fileName, FIO_prefs_t* const prefs, stat_t* dictFileStat)
|
||||
|
||||
+134
-3
@@ -7,7 +7,10 @@
|
||||
//! This module implements the filesystem operations behind
|
||||
//! `FIO_openDstFile()`, `FIO_openSrcFile()`, `FIO_removeFile()`, and dictionary
|
||||
//! loading, returning small status codes so the C wrappers can retain their
|
||||
//! existing messages, policy fields, and conventions.
|
||||
//! existing messages, policy fields, and conventions. Rust also classifies
|
||||
//! dictionary-loader statuses into diagnostic actions; C retains the
|
||||
//! metadata and exact diagnostic text while the existing loader leaves
|
||||
//! continue to publish their narrow status ABI.
|
||||
|
||||
use std::ffi::{c_char, c_void, CStr};
|
||||
use std::fs::File;
|
||||
@@ -31,10 +34,18 @@ 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_DIAGNOSTIC_OK: c_int = 0;
|
||||
const FIO_DICT_DIAGNOSTIC_OPEN_FAILED: c_int = 1;
|
||||
const FIO_DICT_DIAGNOSTIC_TOO_LARGE: c_int = 2;
|
||||
const FIO_DICT_DIAGNOSTIC_ALLOCATION_FAILED: c_int = 3;
|
||||
const FIO_DICT_DIAGNOSTIC_READ_FAILED: c_int = 4;
|
||||
const FIO_DICT_DIAGNOSTIC_MAP_FAILED: c_int = 5;
|
||||
const FIO_DICT_DIAGNOSTIC_VIEW_FAILED: c_int = 6;
|
||||
const FIO_DICT_DIAGNOSTIC_INVALID: c_int = 7;
|
||||
|
||||
const FIO_DICT_STAT_SUCCESS: c_int = 0;
|
||||
const FIO_DICT_STAT_FAILED: c_int = 1;
|
||||
const FIO_DICT_STAT_NON_REGULAR: c_int = 2;
|
||||
@@ -449,7 +460,39 @@ pub unsafe extern "C" fn FIO_rust_setDictBufferMalloc(
|
||||
FIO_DICT_LOAD_SUCCESS
|
||||
}
|
||||
|
||||
/// Maps a dictionary file while leaving policy and diagnostics in the C CLI.
|
||||
/// Classifies a dictionary-loader status for the C diagnostic adapter.
|
||||
///
|
||||
/// C retains the metadata query, ownership handles, and exact `EXM_THROW` text;
|
||||
/// the existing loader leaves continue to publish status codes. Rust owns the
|
||||
/// policy that interprets each loader family/status pair, including the
|
||||
/// distinct mmap failure classes. Unknown combinations intentionally select
|
||||
/// the C assert fallback instead of being treated as success.
|
||||
#[inline]
|
||||
fn dict_load_diagnostic(dict_buffer_type: c_int, status: c_int) -> c_int {
|
||||
match (dict_buffer_type, status) {
|
||||
(FIO_MALLOC_DICT, FIO_DICT_LOAD_SUCCESS) | (FIO_MMAP_DICT, FIO_DICT_MMAP_SUCCESS) => {
|
||||
FIO_DICT_DIAGNOSTIC_OK
|
||||
}
|
||||
(FIO_MALLOC_DICT, FIO_DICT_LOAD_OPEN_FAILED)
|
||||
| (FIO_MMAP_DICT, FIO_DICT_MMAP_OPEN_FAILED) => FIO_DICT_DIAGNOSTIC_OPEN_FAILED,
|
||||
(FIO_MALLOC_DICT, FIO_DICT_LOAD_TOO_LARGE) | (FIO_MMAP_DICT, FIO_DICT_MMAP_TOO_LARGE) => {
|
||||
FIO_DICT_DIAGNOSTIC_TOO_LARGE
|
||||
}
|
||||
(FIO_MALLOC_DICT, FIO_DICT_LOAD_ALLOCATION_FAILED) => FIO_DICT_DIAGNOSTIC_ALLOCATION_FAILED,
|
||||
(FIO_MALLOC_DICT, FIO_DICT_LOAD_READ_FAILED) => FIO_DICT_DIAGNOSTIC_READ_FAILED,
|
||||
(FIO_MMAP_DICT, FIO_DICT_MMAP_FAILED) => FIO_DICT_DIAGNOSTIC_MAP_FAILED,
|
||||
(FIO_MMAP_DICT, FIO_DICT_MMAP_VIEW_FAILED) => FIO_DICT_DIAGNOSTIC_VIEW_FAILED,
|
||||
_ => FIO_DICT_DIAGNOSTIC_INVALID,
|
||||
}
|
||||
}
|
||||
|
||||
/// Exposes the dictionary-loader status policy to the C diagnostic adapter.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn FIO_rust_dictLoadDiagnostic(dict_buffer_type: c_int, status: c_int) -> c_int {
|
||||
dict_load_diagnostic(dict_buffer_type, status)
|
||||
}
|
||||
|
||||
/// Maps a dictionary file while keeping the platform loader in this module.
|
||||
///
|
||||
/// `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
|
||||
@@ -848,6 +891,94 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dictionary_loader_diagnostics_preserve_each_backend_status_class() {
|
||||
let cases = [
|
||||
(
|
||||
FIO_MALLOC_DICT,
|
||||
FIO_DICT_LOAD_SUCCESS,
|
||||
FIO_DICT_DIAGNOSTIC_OK,
|
||||
),
|
||||
(
|
||||
FIO_MALLOC_DICT,
|
||||
FIO_DICT_LOAD_OPEN_FAILED,
|
||||
FIO_DICT_DIAGNOSTIC_OPEN_FAILED,
|
||||
),
|
||||
(
|
||||
FIO_MALLOC_DICT,
|
||||
FIO_DICT_LOAD_TOO_LARGE,
|
||||
FIO_DICT_DIAGNOSTIC_TOO_LARGE,
|
||||
),
|
||||
(
|
||||
FIO_MALLOC_DICT,
|
||||
FIO_DICT_LOAD_ALLOCATION_FAILED,
|
||||
FIO_DICT_DIAGNOSTIC_ALLOCATION_FAILED,
|
||||
),
|
||||
(
|
||||
FIO_MALLOC_DICT,
|
||||
FIO_DICT_LOAD_READ_FAILED,
|
||||
FIO_DICT_DIAGNOSTIC_READ_FAILED,
|
||||
),
|
||||
(FIO_MMAP_DICT, FIO_DICT_MMAP_SUCCESS, FIO_DICT_DIAGNOSTIC_OK),
|
||||
(
|
||||
FIO_MMAP_DICT,
|
||||
FIO_DICT_MMAP_OPEN_FAILED,
|
||||
FIO_DICT_DIAGNOSTIC_OPEN_FAILED,
|
||||
),
|
||||
(
|
||||
FIO_MMAP_DICT,
|
||||
FIO_DICT_MMAP_TOO_LARGE,
|
||||
FIO_DICT_DIAGNOSTIC_TOO_LARGE,
|
||||
),
|
||||
(
|
||||
FIO_MMAP_DICT,
|
||||
FIO_DICT_MMAP_FAILED,
|
||||
FIO_DICT_DIAGNOSTIC_MAP_FAILED,
|
||||
),
|
||||
(
|
||||
FIO_MMAP_DICT,
|
||||
FIO_DICT_MMAP_VIEW_FAILED,
|
||||
FIO_DICT_DIAGNOSTIC_VIEW_FAILED,
|
||||
),
|
||||
];
|
||||
|
||||
for (dict_buffer_type, status, expected_diagnostic) in cases {
|
||||
assert_eq!(
|
||||
dict_load_diagnostic(dict_buffer_type, status),
|
||||
expected_diagnostic,
|
||||
"unexpected dictionary diagnostic for buffer type {dict_buffer_type} and status {status}"
|
||||
);
|
||||
assert_eq!(
|
||||
FIO_rust_dictLoadDiagnostic(dict_buffer_type, status),
|
||||
expected_diagnostic,
|
||||
"export disagrees for buffer type {dict_buffer_type} and status {status}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dictionary_loader_diagnostics_reject_invalid_combinations() {
|
||||
let cases = [
|
||||
(FIO_MALLOC_DICT, c_int::MAX),
|
||||
(FIO_MMAP_DICT, -1),
|
||||
(c_int::MAX, FIO_DICT_LOAD_SUCCESS),
|
||||
(-1, FIO_DICT_MMAP_SUCCESS),
|
||||
];
|
||||
|
||||
for (dict_buffer_type, status) in cases {
|
||||
assert_eq!(
|
||||
dict_load_diagnostic(dict_buffer_type, status),
|
||||
FIO_DICT_DIAGNOSTIC_INVALID,
|
||||
"invalid dictionary loader combination was accepted"
|
||||
);
|
||||
assert_eq!(
|
||||
FIO_rust_dictLoadDiagnostic(dict_buffer_type, status),
|
||||
FIO_DICT_DIAGNOSTIC_INVALID,
|
||||
"export accepted invalid dictionary loader combination"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn destination_test_mode_returns_before_path_validation() {
|
||||
let mut is_dst_reg_file = c_int::MAX;
|
||||
|
||||
Reference in New Issue
Block a user