feat(cli): move decompression resource orchestration to Rust

Move the decompression resource creation order into the Rust CLI policy
layer: dictionary stat and patch-memory preparation, decoder allocation,
window/checksum configuration, dictionary reset/attachment, and asynchronous
pool creation now run through one Rust-owned sequence.  Keep dRess_t,
stat_t, dictionary buffers, decoder context, pools, and exact allocation
errors in C callbacks.

Preserve the original patch-from and dictionary-reference behavior while
adding ABI layout assertions, scalar policy coverage, and error short-circuit
tests for the new boundary.

Test Plan:
- ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo +nightly fmt --manifest-path rust/cli/Cargo.toml --all
- ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo clippy --manifest-path rust/cli/Cargo.toml --all-targets -- -D warnings
- ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo test --manifest-path rust/cli/Cargo.toml --all-targets (207 passed)
- ulimit -v 41943040; CARGO_BUILD_JOBS=1 make -j1
This commit is contained in:
2026-07-21 14:26:49 +02:00
parent 877eb89e6c
commit 1941154279
2 changed files with 398 additions and 34 deletions
+147 -34
View File
@@ -3820,11 +3820,62 @@ int FIO_compressMultipleFilenames(FIO_ctx_t* const fCtx,
***************************************************************************/ ***************************************************************************/
typedef struct { typedef struct {
FIO_Dict_t dict; FIO_Dict_t dict;
stat_t dictFileStat;
ZSTD_DStream* dctx; ZSTD_DStream* dctx;
WritePoolCtx_t *writeCtx; WritePoolCtx_t *writeCtx;
ReadPoolCtx_t *readCtx; ReadPoolCtx_t *readCtx;
} dRess_t; } dRess_t;
typedef void (*FIO_rust_createDResourcesPrepareDictionaryCallback_f)(
void* context, FIO_prefs_t* prefs, const char* dictFileName);
typedef void (*FIO_rust_createDResourcesCreateDctxCallback_f)(void* context);
typedef size_t (*FIO_rust_createDResourcesSetMaxWindowSizeCallback_f)(
void* context, unsigned memLimit);
typedef size_t (*FIO_rust_createDResourcesSetParameterCallback_f)(
void* context, int value);
typedef size_t (*FIO_rust_createDResourcesLoadDictionaryCallback_f)(
void* context, FIO_prefs_t* prefs, const char* dictFileName);
typedef void (*FIO_rust_createDResourcesCreatePoolCallback_f)(
void* context, const FIO_prefs_t* prefs);
typedef int (*FIO_rust_createDResourcesIsErrorCallback_f)(size_t result);
typedef struct {
void* callbackContext;
FIO_prefs_t* prefs;
const char* dictFileName;
FIO_rust_createDResourcesPrepareDictionaryCallback_f prepareDictionary;
FIO_rust_createDResourcesCreateDctxCallback_f createDctx;
FIO_rust_createDResourcesSetMaxWindowSizeCallback_f setMaxWindowSize;
FIO_rust_createDResourcesSetParameterCallback_f setParameter;
FIO_rust_createDResourcesLoadDictionaryCallback_f loadDictionary;
FIO_rust_createDResourcesCreatePoolCallback_f createWritePool;
FIO_rust_createDResourcesCreatePoolCallback_f createReadPool;
FIO_rust_createDResourcesIsErrorCallback_f isError;
} FIO_rust_createDResourcesState;
typedef char FIO_rust_create_d_resources_state_layout[
(offsetof(FIO_rust_createDResourcesState, callbackContext) == 0
&& offsetof(FIO_rust_createDResourcesState, prefs) == sizeof(void*)
&& offsetof(FIO_rust_createDResourcesState, dictFileName)
== 2 * sizeof(void*)
&& offsetof(FIO_rust_createDResourcesState, prepareDictionary)
== 3 * sizeof(void*)
&& offsetof(FIO_rust_createDResourcesState, createDctx)
== 4 * sizeof(void*)
&& offsetof(FIO_rust_createDResourcesState, setMaxWindowSize)
== 5 * sizeof(void*)
&& offsetof(FIO_rust_createDResourcesState, setParameter)
== 6 * sizeof(void*)
&& offsetof(FIO_rust_createDResourcesState, loadDictionary)
== 7 * sizeof(void*)
&& offsetof(FIO_rust_createDResourcesState, createWritePool)
== 8 * sizeof(void*)
&& offsetof(FIO_rust_createDResourcesState, createReadPool)
== 9 * sizeof(void*)
&& offsetof(FIO_rust_createDResourcesState, isError)
== 10 * sizeof(void*)
&& sizeof(FIO_rust_createDResourcesState) == 11 * sizeof(void*))
? 1 : -1];
size_t FIO_rust_createDResources(const FIO_rust_createDResourcesState* state);
typedef void (*FIO_rust_freeDResourcesCallback_f)(void* context); typedef void (*FIO_rust_freeDResourcesCallback_f)(void* context);
typedef struct { typedef struct {
void* callbackContext; void* callbackContext;
@@ -3870,45 +3921,107 @@ static void FIO_rust_freeDResources_readPool(void* context)
AIO_ReadPool_free(ress->readCtx); AIO_ReadPool_free(ress->readCtx);
} }
static void FIO_rust_createDResources_prepareDictionary(
void* context, FIO_prefs_t* prefs, const char* dictFileName)
{
dRess_t* const ress = (dRess_t*)context;
FIO_getDictFileStat(dictFileName, &ress->dictFileStat);
if (prefs->patchFromMode) {
unsigned long long const dictSize =
(unsigned long long)UTIL_getFileSizeStat(&ress->dictFileStat);
FIO_adjustMemLimitForPatchFromMode(
prefs, dictSize, 0 /* just use the dict size */);
}
}
static void FIO_rust_createDResources_createDctx(void* context)
{
dRess_t* const ress = (dRess_t*)context;
ress->dctx = ZSTD_createDStream();
if (ress->dctx == NULL)
EXM_THROW(60, "Error: %s : can't create ZSTD_DStream", strerror(errno));
}
static size_t FIO_rust_createDResources_setMaxWindowSize(
void* context, unsigned memLimit)
{
dRess_t* const ress = (dRess_t*)context;
return ZSTD_DCtx_setMaxWindowSize(ress->dctx, memLimit);
}
static size_t FIO_rust_createDResources_setParameter(void* context, int value)
{
dRess_t* const ress = (dRess_t*)context;
return ZSTD_DCtx_setParameter(
ress->dctx, ZSTD_d_forceIgnoreChecksum, value);
}
static size_t FIO_rust_createDResources_loadDictionary(
void* context, FIO_prefs_t* prefs, const char* dictFileName)
{
dRess_t* const ress = (dRess_t*)context;
unsigned long long const dictSize = prefs->patchFromMode
? (unsigned long long)UTIL_getFileSizeStat(&ress->dictFileStat)
: 0;
FIO_dictBufferType_t const dictBufferType =
(FIO_dictBufferType_t)FIO_rust_selectDictBufferType(
prefs->mmapDict, prefs->patchFromMode, dictSize,
prefs->memLimit);
FIO_initDict(&ress->dict, dictFileName, prefs, &ress->dictFileStat,
dictBufferType);
{ size_t const result = ZSTD_DCtx_reset(
ress->dctx, ZSTD_reset_session_only);
if (ZSTD_isError(result)) return result;
}
if (prefs->patchFromMode) {
return ZSTD_DCtx_refPrefix(
ress->dctx, ress->dict.dictBuffer, ress->dict.dictBufferSize);
}
return ZSTD_DCtx_loadDictionary_byReference(
ress->dctx, ress->dict.dictBuffer, ress->dict.dictBufferSize);
}
static void FIO_rust_createDResources_createWritePool(
void* context, const FIO_prefs_t* prefs)
{
dRess_t* const ress = (dRess_t*)context;
ress->writeCtx = AIO_WritePool_create(prefs, ZSTD_DStreamOutSize());
}
static void FIO_rust_createDResources_createReadPool(
void* context, const FIO_prefs_t* prefs)
{
dRess_t* const ress = (dRess_t*)context;
ress->readCtx = AIO_ReadPool_create(prefs, ZSTD_DStreamInSize());
}
static int FIO_rust_createDResources_isError(size_t result)
{
return ZSTD_isError(result);
}
static dRess_t FIO_createDResources(FIO_prefs_t* const prefs, const char* dictFileName) static dRess_t FIO_createDResources(FIO_prefs_t* const prefs, const char* dictFileName)
{ {
unsigned long long dictSize = 0;
stat_t statbuf;
dRess_t ress; dRess_t ress;
memset(&statbuf, 0, sizeof(statbuf));
memset(&ress, 0, sizeof(ress)); memset(&ress, 0, sizeof(ress));
FIO_getDictFileStat(dictFileName, &statbuf); FIO_rust_createDResourcesState const state = {
&ress,
if (prefs->patchFromMode){ prefs,
dictSize = (unsigned long long)UTIL_getFileSizeStat(&statbuf); dictFileName,
FIO_adjustMemLimitForPatchFromMode(prefs, dictSize, 0 /* just use the dict size */); FIO_rust_createDResources_prepareDictionary,
} FIO_rust_createDResources_createDctx,
FIO_rust_createDResources_setMaxWindowSize,
/* Allocation */ FIO_rust_createDResources_setParameter,
ress.dctx = ZSTD_createDStream(); FIO_rust_createDResources_loadDictionary,
if (ress.dctx==NULL) FIO_rust_createDResources_createWritePool,
EXM_THROW(60, "Error: %s : can't create ZSTD_DStream", strerror(errno)); FIO_rust_createDResources_createReadPool,
CHECK( ZSTD_DCtx_setMaxWindowSize(ress.dctx, prefs->memLimit) ); FIO_rust_createDResources_isError
CHECK( ZSTD_DCtx_setParameter(ress.dctx, ZSTD_d_forceIgnoreChecksum, !prefs->checksumFlag)); };
CHECK(FIO_rust_createDResources(&state));
/* dictionary */
{
FIO_dictBufferType_t const dictBufferType = (FIO_dictBufferType_t)FIO_rust_selectDictBufferType(
prefs->mmapDict, prefs->patchFromMode, dictSize, prefs->memLimit);
FIO_initDict(&ress.dict, dictFileName, prefs, &statbuf, dictBufferType);
CHECK(ZSTD_DCtx_reset(ress.dctx, ZSTD_reset_session_only) );
if (prefs->patchFromMode){
CHECK(ZSTD_DCtx_refPrefix(ress.dctx, ress.dict.dictBuffer, ress.dict.dictBufferSize));
} else {
CHECK(ZSTD_DCtx_loadDictionary_byReference(ress.dctx, ress.dict.dictBuffer, ress.dict.dictBufferSize));
}
}
ress.writeCtx = AIO_WritePool_create(prefs, ZSTD_DStreamOutSize());
ress.readCtx = AIO_ReadPool_create(prefs, ZSTD_DStreamInSize());
return ress; return ress;
} }
+251
View File
@@ -358,6 +358,99 @@ pub unsafe extern "C" fn FIO_rust_freeDResources(state: *const FIO_rust_freeDRes
} }
} }
pub type FIO_createDResourcesPrepareDictionaryFn =
unsafe extern "C" fn(*mut c_void, *mut FIO_prefs_t, *const c_char);
pub type FIO_createDResourcesCreateDctxFn = unsafe extern "C" fn(*mut c_void);
pub type FIO_createDResourcesSetMaxWindowSizeFn =
unsafe extern "C" fn(*mut c_void, c_uint) -> usize;
pub type FIO_createDResourcesSetParameterFn = unsafe extern "C" fn(*mut c_void, c_int) -> usize;
pub type FIO_createDResourcesLoadDictionaryFn =
unsafe extern "C" fn(*mut c_void, *mut FIO_prefs_t, *const c_char) -> usize;
pub type FIO_createDResourcesCreatePoolFn = unsafe extern "C" fn(*mut c_void, *const FIO_prefs_t);
pub type FIO_createDResourcesIsErrorFn = unsafe extern "C" fn(usize) -> c_int;
/// Decompression-resource initialization projection.
///
/// Rust owns the stable creation order and error short-circuiting. C keeps
/// the `dRess_t`, decoder context, dictionary/stat storage, and asynchronous
/// pools private behind callbacks which preserve the original diagnostics and
/// resource operations.
#[repr(C)]
pub struct FIO_rust_createDResourcesState {
callback_context: *mut c_void,
prefs: *mut FIO_prefs_t,
dict_file_name: *const c_char,
prepare_dictionary: FIO_createDResourcesPrepareDictionaryFn,
create_dctx: FIO_createDResourcesCreateDctxFn,
set_max_window_size: FIO_createDResourcesSetMaxWindowSizeFn,
set_parameter: FIO_createDResourcesSetParameterFn,
load_dictionary: FIO_createDResourcesLoadDictionaryFn,
create_write_pool: FIO_createDResourcesCreatePoolFn,
create_read_pool: FIO_createDResourcesCreatePoolFn,
is_error: FIO_createDResourcesIsErrorFn,
}
const _: () = {
let word = size_of::<usize>();
assert!(offset_of!(FIO_rust_createDResourcesState, callback_context) == 0);
assert!(offset_of!(FIO_rust_createDResourcesState, prefs) == word);
assert!(offset_of!(FIO_rust_createDResourcesState, dict_file_name) == 2 * word);
assert!(offset_of!(FIO_rust_createDResourcesState, prepare_dictionary) == 3 * word);
assert!(offset_of!(FIO_rust_createDResourcesState, create_dctx) == 4 * word);
assert!(offset_of!(FIO_rust_createDResourcesState, set_max_window_size) == 5 * word);
assert!(offset_of!(FIO_rust_createDResourcesState, set_parameter) == 6 * word);
assert!(offset_of!(FIO_rust_createDResourcesState, load_dictionary) == 7 * word);
assert!(offset_of!(FIO_rust_createDResourcesState, create_write_pool) == 8 * word);
assert!(offset_of!(FIO_rust_createDResourcesState, create_read_pool) == 9 * word);
assert!(offset_of!(FIO_rust_createDResourcesState, is_error) == 10 * word);
assert!(size_of::<FIO_rust_createDResourcesState>() == 11 * word);
};
#[no_mangle]
pub unsafe extern "C" fn FIO_rust_createDResources(
state: *const FIO_rust_createDResourcesState,
) -> usize {
let Some(state) = (unsafe { state.as_ref() }) else {
return usize::MAX;
};
if state.prefs.is_null() {
return usize::MAX;
}
unsafe {
(state.prepare_dictionary)(state.callback_context, state.prefs, state.dict_file_name);
(state.create_dctx)(state.callback_context);
}
// The preparation callback may adjust prefs->memLimit for patch mode, so
// read the scalar inputs only after that callback has completed.
let prefs = unsafe { &*state.prefs };
let result = unsafe { (state.set_max_window_size)(state.callback_context, prefs.memLimit) };
if unsafe { (state.is_error)(result) } != 0 {
return result;
}
let result = unsafe {
(state.set_parameter)(state.callback_context, c_int::from(prefs.checksumFlag == 0))
};
if unsafe { (state.is_error)(result) } != 0 {
return result;
}
let result = unsafe {
(state.load_dictionary)(state.callback_context, state.prefs, state.dict_file_name)
};
if unsafe { (state.is_error)(result) } != 0 {
return result;
}
unsafe {
(state.create_write_pool)(state.callback_context, state.prefs);
(state.create_read_pool)(state.callback_context, state.prefs);
}
0
}
#[cfg(feature = "decompression")] #[cfg(feature = "decompression")]
// `ZSTD_error_frameParameter_windowTooLarge` from zstd_errors.h. // `ZSTD_error_frameParameter_windowTooLarge` from zstd_errors.h.
const ZSTD_ERROR_FRAME_PARAMETER_WINDOW_TOO_LARGE: c_int = 16; const ZSTD_ERROR_FRAME_PARAMETER_WINDOW_TOO_LARGE: c_int = 16;
@@ -3189,6 +3282,164 @@ mod tests {
assert!(context.events.is_empty()); assert!(context.events.is_empty());
} }
#[derive(Default)]
struct CreateDResourcesTestState {
lifecycle: Vec<&'static str>,
max_window_size: Option<c_uint>,
ignore_checksum: Option<c_int>,
fail_at: Option<&'static str>,
}
unsafe extern "C" fn create_d_resources_prepare_dictionary_test(
context: *mut c_void,
_prefs: *mut FIO_prefs_t,
_dict_file_name: *const c_char,
) {
unsafe {
(*context.cast::<CreateDResourcesTestState>())
.lifecycle
.push("prepare-dictionary");
}
}
unsafe extern "C" fn create_d_resources_create_dctx_test(context: *mut c_void) {
unsafe {
(*context.cast::<CreateDResourcesTestState>())
.lifecycle
.push("create-dctx");
}
}
unsafe extern "C" fn create_d_resources_set_max_window_size_test(
context: *mut c_void,
value: c_uint,
) -> usize {
let state = unsafe { &mut *context.cast::<CreateDResourcesTestState>() };
state.lifecycle.push("set-max-window");
state.max_window_size = Some(value);
usize::from(state.fail_at == Some("set-max-window")) * usize::MAX
}
unsafe extern "C" fn create_d_resources_set_parameter_test(
context: *mut c_void,
value: c_int,
) -> usize {
let state = unsafe { &mut *context.cast::<CreateDResourcesTestState>() };
state.lifecycle.push("set-parameter");
state.ignore_checksum = Some(value);
usize::from(state.fail_at == Some("set-parameter")) * usize::MAX
}
unsafe extern "C" fn create_d_resources_load_dictionary_test(
context: *mut c_void,
_prefs: *mut FIO_prefs_t,
_dict_file_name: *const c_char,
) -> usize {
let state = unsafe { &mut *context.cast::<CreateDResourcesTestState>() };
state.lifecycle.push("load-dictionary");
usize::from(state.fail_at == Some("load-dictionary")) * usize::MAX
}
unsafe extern "C" fn create_d_resources_create_write_pool_test(
context: *mut c_void,
_prefs: *const FIO_prefs_t,
) {
unsafe {
(*context.cast::<CreateDResourcesTestState>())
.lifecycle
.push("create-write-pool");
}
}
unsafe extern "C" fn create_d_resources_create_read_pool_test(
context: *mut c_void,
_prefs: *const FIO_prefs_t,
) {
unsafe {
(*context.cast::<CreateDResourcesTestState>())
.lifecycle
.push("create-read-pool");
}
}
unsafe extern "C" fn create_d_resources_is_error_test(result: usize) -> c_int {
c_int::from(result == usize::MAX)
}
fn create_d_resources_test_prefs() -> FIO_prefs_t {
let mut prefs: FIO_prefs_t = unsafe { std::mem::zeroed() };
prefs.memLimit = 64;
prefs.checksumFlag = 1;
prefs
}
fn create_d_resources_test_state(
prefs: &FIO_prefs_t,
context: &mut CreateDResourcesTestState,
) -> FIO_rust_createDResourcesState {
FIO_rust_createDResourcesState {
callback_context: context as *mut CreateDResourcesTestState as *mut c_void,
prefs: prefs as *const FIO_prefs_t as *mut FIO_prefs_t,
dict_file_name: ptr::null(),
prepare_dictionary: create_d_resources_prepare_dictionary_test,
create_dctx: create_d_resources_create_dctx_test,
set_max_window_size: create_d_resources_set_max_window_size_test,
set_parameter: create_d_resources_set_parameter_test,
load_dictionary: create_d_resources_load_dictionary_test,
create_write_pool: create_d_resources_create_write_pool_test,
create_read_pool: create_d_resources_create_read_pool_test,
is_error: create_d_resources_is_error_test,
}
}
#[test]
fn create_d_resources_preserves_creation_order_and_scalar_policy() {
let prefs = create_d_resources_test_prefs();
let mut context = CreateDResourcesTestState::default();
let state = create_d_resources_test_state(&prefs, &mut context);
let result = unsafe { FIO_rust_createDResources(&state) };
assert_eq!(result, 0);
assert_eq!(context.max_window_size, Some(64));
assert_eq!(context.ignore_checksum, Some(0));
assert_eq!(
context.lifecycle,
vec![
"prepare-dictionary",
"create-dctx",
"set-max-window",
"set-parameter",
"load-dictionary",
"create-write-pool",
"create-read-pool",
]
);
}
#[test]
fn create_d_resources_short_circuits_on_decoder_parameter_error() {
let prefs = create_d_resources_test_prefs();
let mut context = CreateDResourcesTestState {
fail_at: Some("set-parameter"),
..CreateDResourcesTestState::default()
};
let state = create_d_resources_test_state(&prefs, &mut context);
let result = unsafe { FIO_rust_createDResources(&state) };
assert_eq!(result, usize::MAX);
assert_eq!(
context.lifecycle,
vec![
"prepare-dictionary",
"create-dctx",
"set-max-window",
"set-parameter",
]
);
}
#[cfg(feature = "decompression")] #[cfg(feature = "decompression")]
#[derive(Default)] #[derive(Default)]
struct ZstdErrorHelpTestState { struct ZstdErrorHelpTestState {