From f43ecc9c610b1aa13f5991ac5a2751eda8ebee1e Mon Sep 17 00:00:00 2001 From: ddidderr Date: Tue, 21 Jul 2026 10:34:57 +0200 Subject: [PATCH] refactor(mt): move serial reset policy to Rust Project the MT serial-reset scalar inputs into a narrow Rust ABI and let Rust own LDM normalization, table sizing, callback order, error short-circuiting, and parameter publication. Keep SerialState, ldmState_t, allocators, dictionary/window/checksum operations, and table mutation in C callbacks while preserving the previous bucket-log policy and the size_t-to-U32 job-size cast. Test Plan: - ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo +nightly fmt --manifest-path rust/Cargo.toml --all -- --check - ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo check --manifest-path rust/Cargo.toml --all-targets - ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo clippy --manifest-path rust/Cargo.toml --all-targets -- -D warnings - ulimit -v 41943040; cc -std=c99 -fsyntax-only -Ilib -Ilib/common -Ilib/compress -Ilib/dict -Ilib/deprecated -Iprograms lib/compress/zstdmt_compress.c - ulimit -v 41943040; clang -std=c99 -fsyntax-only -Ilib -Ilib/common -Ilib/compress -Ilib/dict -Ilib/deprecated -Iprograms lib/compress/zstdmt_compress.c - Focused Rust tests added but not executed per the no-heavy-test instruction. --- lib/compress/zstdmt_compress.c | 148 ++++++++---- rust/src/zstdmt_compress.rs | 425 +++++++++++++++++++++++++++++++-- 2 files changed, 507 insertions(+), 66 deletions(-) diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 89976a091..fc4fe063b 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -786,19 +786,69 @@ size_t ZSTDMT_rust_updateCStreamDictionary( ZSTDMT_initResetStreamFn attachBorrowed, ZSTDMT_initResetStreamFn setRawPrefix, ZSTDMT_initDictionaryFn createReferenced); +typedef struct { + int enableLdm; + int checksumEnabled; + size_t jobSize; + unsigned windowLog; + int strategy; + unsigned hashLog; + unsigned bucketSizeLog; + unsigned minMatchLength; + unsigned hashRateLog; + unsigned previousHashLog; + unsigned previousBucketSizeLog; + size_t ldmEntrySize; +} ZSTDMT_RustSerialResetProjection; +typedef char ZSTDMT_rust_serial_reset_projection_layout[ + (offsetof(ZSTDMT_RustSerialResetProjection, enableLdm) == 0 + && offsetof(ZSTDMT_RustSerialResetProjection, checksumEnabled) + == sizeof(int) + && offsetof(ZSTDMT_RustSerialResetProjection, jobSize) == 8 + && offsetof(ZSTDMT_RustSerialResetProjection, windowLog) + == (sizeof(void*) == 8 ? 16 : 12) + && offsetof(ZSTDMT_RustSerialResetProjection, strategy) + == (sizeof(void*) == 8 ? 20 : 16) + && offsetof(ZSTDMT_RustSerialResetProjection, hashLog) + == (sizeof(void*) == 8 ? 24 : 20) + && offsetof(ZSTDMT_RustSerialResetProjection, bucketSizeLog) + == (sizeof(void*) == 8 ? 28 : 24) + && offsetof(ZSTDMT_RustSerialResetProjection, minMatchLength) + == (sizeof(void*) == 8 ? 32 : 28) + && offsetof(ZSTDMT_RustSerialResetProjection, hashRateLog) + == (sizeof(void*) == 8 ? 36 : 32) + && offsetof(ZSTDMT_RustSerialResetProjection, previousHashLog) + == (sizeof(void*) == 8 ? 40 : 36) + && offsetof(ZSTDMT_RustSerialResetProjection, previousBucketSizeLog) + == (sizeof(void*) == 8 ? 44 : 40) + && offsetof(ZSTDMT_RustSerialResetProjection, ldmEntrySize) + == (sizeof(void*) == 8 ? 48 : 44) + && sizeof(ZSTDMT_RustSerialResetProjection) + == (sizeof(void*) == 8 ? 56 : 48)) ? 1 : -1]; +typedef void (*ZSTDMT_serialResetSetLdmParamsFn)( + void* opaque, int enableLdm, unsigned hashLog, + unsigned bucketSizeLog, unsigned minMatchLength, + unsigned hashRateLog, unsigned windowLog); typedef void (*ZSTDMT_serialResetVoidFn)(void* opaque); typedef void (*ZSTDMT_serialResetSetNbSeqFn)(void* opaque, size_t nbSeq); -typedef int (*ZSTDMT_serialResetResizeFn)(void* opaque); +typedef int (*ZSTDMT_serialResetResizeFn)( + void* opaque, size_t hashSize, size_t numBuckets, + unsigned bucketLog, unsigned previousBucketLog); +typedef void (*ZSTDMT_serialResetZeroTablesFn)( + void* opaque, size_t hashSize, size_t numBuckets); +typedef void (*ZSTDMT_serialResetPublishFn)(void* opaque, size_t jobSize); int ZSTDMT_rust_serialStateReset( - int enableLdm, int checksumEnabled, size_t maxNbSeq, void* opaque, + const ZSTDMT_RustSerialResetProjection* projection, void* opaque, + ZSTDMT_serialResetSetLdmParamsFn setLdmParams, ZSTDMT_serialResetVoidFn resetNextJob, ZSTDMT_serialResetVoidFn resetChecksum, ZSTDMT_serialResetSetNbSeqFn setNbSeq, ZSTDMT_serialResetVoidFn resetWindow, ZSTDMT_serialResetResizeFn resizeTables, - ZSTDMT_serialResetVoidFn zeroTables, + ZSTDMT_serialResetZeroTablesFn zeroTables, ZSTDMT_serialResetVoidFn loadDictionary, - ZSTDMT_serialResetVoidFn copyWindow); + ZSTDMT_serialResetVoidFn copyWindow, + ZSTDMT_serialResetPublishFn publishParams); typedef void (*ZSTDMT_serialStateVoidFn)(void* opaque); typedef int (*ZSTDMT_serialStateInitFn)(void* opaque); int ZSTDMT_rust_serialStateInit( @@ -1360,13 +1410,24 @@ typedef struct { const void* dict; size_t dictSize; ZSTD_dictContentType_e dictContentType; - size_t hashSize; - size_t numBuckets; - unsigned bucketLog; - unsigned prevBucketLog; ZSTD_customMem cMem; } ZSTDMT_serialResetContext; +static void ZSTDMT_serialResetSetLdmParams( + void* opaque, int enableLdm, unsigned hashLog, + unsigned bucketSizeLog, unsigned minMatchLength, + unsigned hashRateLog, unsigned windowLog) +{ + ZSTDMT_serialResetContext* const context = (ZSTDMT_serialResetContext*)opaque; + ldmParams_t* const ldmParams = &context->params->ldmParams; + ldmParams->enableLdm = (ZSTD_ParamSwitch_e)enableLdm; + ldmParams->hashLog = hashLog; + ldmParams->bucketSizeLog = bucketSizeLog; + ldmParams->minMatchLength = minMatchLength; + ldmParams->hashRateLog = hashRateLog; + ldmParams->windowLog = windowLog; +} + static void ZSTDMT_serialResetNextJob(void* opaque) { ZSTDMT_serialResetContext* const context = (ZSTDMT_serialResetContext*)opaque; @@ -1391,7 +1452,9 @@ static void ZSTDMT_serialResetWindow(void* opaque) ZSTD_window_init(&context->serialState->ldmState.window); } -static int ZSTDMT_serialResetResizeTables(void* opaque) +static int ZSTDMT_serialResetResizeTables( + void* opaque, size_t hashSize, size_t numBuckets, + unsigned bucketLog, unsigned previousBucketLog) { ZSTDMT_serialResetContext* const context = (ZSTDMT_serialResetContext*)opaque; SerialState* const serialState = context->serialState; @@ -1401,23 +1464,24 @@ static int ZSTDMT_serialResetResizeTables(void* opaque) serialState->params.ldmParams.hashLog < ldmParams->hashLog) { ZSTD_customFree(serialState->ldmState.hashTable, context->cMem); serialState->ldmState.hashTable = (ldmEntry_t*)ZSTD_customMalloc( - context->hashSize, context->cMem); + hashSize, context->cMem); } if (serialState->ldmState.bucketOffsets == NULL || - context->prevBucketLog < context->bucketLog) { + previousBucketLog < bucketLog) { ZSTD_customFree(serialState->ldmState.bucketOffsets, context->cMem); serialState->ldmState.bucketOffsets = (BYTE*)ZSTD_customMalloc( - context->numBuckets, context->cMem); + numBuckets, context->cMem); } return !serialState->ldmState.hashTable || !serialState->ldmState.bucketOffsets; } -static void ZSTDMT_serialResetZeroTables(void* opaque) +static void ZSTDMT_serialResetZeroTables( + void* opaque, size_t hashSize, size_t numBuckets) { ZSTDMT_serialResetContext* const context = (ZSTDMT_serialResetContext*)opaque; SerialState* const serialState = context->serialState; - ZSTD_memset(serialState->ldmState.hashTable, 0, context->hashSize); - ZSTD_memset(serialState->ldmState.bucketOffsets, 0, context->numBuckets); + ZSTD_memset(serialState->ldmState.hashTable, 0, hashSize); + ZSTD_memset(serialState->ldmState.bucketOffsets, 0, numBuckets); } static void ZSTDMT_serialResetLoadDictionary(void* opaque) @@ -1446,6 +1510,13 @@ static void ZSTDMT_serialResetCopyWindow(void* opaque) context->serialState->ldmWindow = context->serialState->ldmState.window; } +static void ZSTDMT_serialResetPublishParams(void* opaque, size_t jobSize) +{ + ZSTDMT_serialResetContext* const context = (ZSTDMT_serialResetContext*)opaque; + context->serialState->params = *context->params; + context->serialState->params.jobSize = (U32)jobSize; +} + static int ZSTDMT_serialState_reset(SerialState* serialState, ZSTDMT_seqPool* seqPool, @@ -1456,16 +1527,26 @@ ZSTDMT_serialState_reset(SerialState* serialState, { ZSTDMT_serialResetContext context; - /* Adjust parameters */ + /* Rust owns LDM normalization; retain the original diagnostic only. */ if (params.ldmParams.enableLdm == ZSTD_ps_enable) { DEBUGLOG(4, "LDM window size = %u KB", (1U << params.cParams.windowLog) >> 10); - ZSTD_ldm_adjustParameters(¶ms.ldmParams, ¶ms.cParams); - assert(params.ldmParams.hashLog >= params.ldmParams.bucketSizeLog); - assert(params.ldmParams.hashRateLog < 32); - } else { - ZSTD_memset(¶ms.ldmParams, 0, sizeof(params.ldmParams)); } + ZSTDMT_RustSerialResetProjection const projection = { + params.ldmParams.enableLdm, + params.fParams.checksumFlag, + jobSize, + params.cParams.windowLog, + params.cParams.strategy, + params.ldmParams.hashLog, + params.ldmParams.bucketSizeLog, + params.ldmParams.minMatchLength, + params.ldmParams.hashRateLog, + serialState->params.ldmParams.hashLog, + serialState->params.ldmParams.bucketSizeLog, + sizeof(ldmEntry_t) + }; + context.serialState = serialState; context.seqPool = seqPool; context.params = ¶ms; @@ -1473,25 +1554,10 @@ ZSTDMT_serialState_reset(SerialState* serialState, context.dictSize = dictSize; context.dictContentType = dictContentType; context.cMem = params.customMem; - context.hashSize = 0; - context.numBuckets = 0; - context.bucketLog = 0; - context.prevBucketLog = 0; - if (params.ldmParams.enableLdm == ZSTD_ps_enable) { - unsigned const hashLog = params.ldmParams.hashLog; - context.hashSize = ((size_t)1 << hashLog) * sizeof(ldmEntry_t); - context.bucketLog = params.ldmParams.hashLog - params.ldmParams.bucketSizeLog; - context.prevBucketLog = - serialState->params.ldmParams.hashLog - - serialState->params.ldmParams.bucketSizeLog; - context.numBuckets = (size_t)1 << context.bucketLog; - } if (ZSTDMT_rust_serialStateReset( - params.ldmParams.enableLdm == ZSTD_ps_enable, - params.fParams.checksumFlag, - ZSTD_ldm_getMaxNbSeq(params.ldmParams, jobSize), - &context, + &projection, &context, + ZSTDMT_serialResetSetLdmParams, ZSTDMT_serialResetNextJob, ZSTDMT_serialResetChecksum, ZSTDMT_serialResetSetNbSeq, @@ -1499,12 +1565,10 @@ ZSTDMT_serialState_reset(SerialState* serialState, ZSTDMT_serialResetResizeTables, ZSTDMT_serialResetZeroTables, ZSTDMT_serialResetLoadDictionary, - ZSTDMT_serialResetCopyWindow)) { + ZSTDMT_serialResetCopyWindow, + ZSTDMT_serialResetPublishParams)) { return 1; } - - serialState->params = params; - serialState->params.jobSize = (U32)jobSize; return 0; } diff --git a/rust/src/zstdmt_compress.rs b/rust/src/zstdmt_compress.rs index d74931495..e584aff46 100644 --- a/rust/src/zstdmt_compress.rs +++ b/rust/src/zstdmt_compress.rs @@ -1345,12 +1345,127 @@ pub unsafe extern "C" fn ZSTDMT_rust_updateCStreamDictionary( } type ZSTDMT_resetStreamCallbackFn = unsafe extern "C" fn(*mut c_void); +/// Scalar inputs for the MT serial-state reset. The private C parameter, +/// serial-state, LDM-state, and sequence-pool layouts stay behind callbacks; +/// Rust owns only their scalar policy and ordering. +#[repr(C)] +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] +pub struct ZSTDMT_RustSerialResetProjection { + pub enableLdm: c_int, + pub checksumEnabled: c_int, + pub jobSize: usize, + pub windowLog: c_uint, + pub strategy: c_int, + pub hashLog: c_uint, + pub bucketSizeLog: c_uint, + pub minMatchLength: c_uint, + pub hashRateLog: c_uint, + pub previousHashLog: c_uint, + pub previousBucketSizeLog: c_uint, + pub ldmEntrySize: usize, +} + +const _: () = { + assert!(offset_of!(ZSTDMT_RustSerialResetProjection, enableLdm) == 0); + assert!(offset_of!(ZSTDMT_RustSerialResetProjection, checksumEnabled) == size_of::()); + assert!(offset_of!(ZSTDMT_RustSerialResetProjection, jobSize) == 8); + assert!( + offset_of!(ZSTDMT_RustSerialResetProjection, windowLog) + == if size_of::() == 8 { 16 } else { 12 } + ); + assert!( + offset_of!(ZSTDMT_RustSerialResetProjection, strategy) + == if size_of::() == 8 { 20 } else { 16 } + ); + assert!( + offset_of!(ZSTDMT_RustSerialResetProjection, hashLog) + == if size_of::() == 8 { 24 } else { 20 } + ); + assert!( + offset_of!(ZSTDMT_RustSerialResetProjection, bucketSizeLog) + == if size_of::() == 8 { 28 } else { 24 } + ); + assert!( + offset_of!(ZSTDMT_RustSerialResetProjection, minMatchLength) + == if size_of::() == 8 { 32 } else { 28 } + ); + assert!( + offset_of!(ZSTDMT_RustSerialResetProjection, hashRateLog) + == if size_of::() == 8 { 36 } else { 32 } + ); + assert!( + offset_of!(ZSTDMT_RustSerialResetProjection, previousHashLog) + == if size_of::() == 8 { 40 } else { 36 } + ); + assert!( + offset_of!(ZSTDMT_RustSerialResetProjection, previousBucketSizeLog) + == if size_of::() == 8 { 44 } else { 40 } + ); + assert!( + offset_of!(ZSTDMT_RustSerialResetProjection, ldmEntrySize) + == if size_of::() == 8 { 48 } else { 44 } + ); + assert!( + size_of::() + == if size_of::() == 8 { 56 } else { 48 } + ); +}; + +pub type ZSTDMT_serialResetSetLdmParamsFn = + unsafe extern "C" fn(*mut c_void, c_int, c_uint, c_uint, c_uint, c_uint, c_uint); pub type ZSTDMT_serialResetVoidFn = unsafe extern "C" fn(*mut c_void); pub type ZSTDMT_serialResetSetNbSeqFn = unsafe extern "C" fn(*mut c_void, usize); -pub type ZSTDMT_serialResetResizeFn = unsafe extern "C" fn(*mut c_void) -> c_int; +pub type ZSTDMT_serialResetResizeFn = + unsafe extern "C" fn(*mut c_void, usize, usize, c_uint, c_uint) -> c_int; +pub type ZSTDMT_serialResetZeroTablesFn = unsafe extern "C" fn(*mut c_void, usize, usize); +pub type ZSTDMT_serialResetPublishFn = unsafe extern "C" fn(*mut c_void, usize); pub type ZSTDMT_serialStateVoidFn = unsafe extern "C" fn(*mut c_void); pub type ZSTDMT_serialStateInitFn = unsafe extern "C" fn(*mut c_void) -> c_int; +/// Rust's LDM module already owns the scalar adjustment and maximum-sequence +/// leaves. This mirror is Rust-internal: it contains only the six scalar LDM +/// fields needed to call those leaves and is never exposed in the C ABI. +#[repr(C)] +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] +struct ZSTDMT_serialResetLdmParameters { + enableLdm: c_int, + hashLog: c_uint, + bucketSizeLog: c_uint, + minMatchLength: c_uint, + hashRateLog: c_uint, + windowLog: c_uint, +} + +const _: () = { + assert!(offset_of!(ZSTDMT_serialResetLdmParameters, enableLdm) == 0); + assert!(offset_of!(ZSTDMT_serialResetLdmParameters, hashLog) == size_of::()); + assert!( + offset_of!(ZSTDMT_serialResetLdmParameters, bucketSizeLog) + == size_of::() + size_of::() + ); + assert!( + offset_of!(ZSTDMT_serialResetLdmParameters, minMatchLength) + == size_of::() + 2 * size_of::() + ); + assert!( + offset_of!(ZSTDMT_serialResetLdmParameters, hashRateLog) + == size_of::() + 3 * size_of::() + ); + assert!( + offset_of!(ZSTDMT_serialResetLdmParameters, windowLog) + == size_of::() + 4 * size_of::() + ); + assert!(size_of::() == 6 * size_of::()); +}; + +unsafe extern "C" { + fn ZSTD_ldm_adjustParameters( + params: *mut ZSTDMT_serialResetLdmParameters, + cParams: *const ZSTD_compressionParameters, + ); + fn ZSTD_ldm_getMaxNbSeq(params: ZSTDMT_serialResetLdmParameters, maxChunkSize: usize) -> usize; +} + /// Callback projection for the MT stream reset. C retains the private buffer, /// job, flag, and progress fields; Rust owns the order in which each group is /// reset. @@ -2777,24 +2892,97 @@ where /// Run the MT serial-state reset policy while C retains its private LDM /// tables, dictionary/window state, checksum state, and allocator callbacks. -/// The callback order mirrors the original reset path and stops immediately -/// when table allocation reports failure. +/// Rust prepares the scalar LDM policy and table sizes, then the callback order +/// mirrors the original reset path and stops immediately when table allocation +/// reports failure. +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] +struct ZSTDMT_serialResetPlan { + ldmParams: ZSTDMT_serialResetLdmParameters, + maxNbSeq: usize, + hashSize: usize, + numBuckets: usize, + bucketLog: c_uint, + previousBucketLog: c_uint, +} + +#[inline] +unsafe fn serial_state_reset_plan( + projection: ZSTDMT_RustSerialResetProjection, +) -> ZSTDMT_serialResetPlan { + let mut ldm_params = ZSTDMT_serialResetLdmParameters { + enableLdm: projection.enableLdm, + hashLog: projection.hashLog, + bucketSizeLog: projection.bucketSizeLog, + minMatchLength: projection.minMatchLength, + hashRateLog: projection.hashRateLog, + windowLog: projection.windowLog, + }; + if projection.enableLdm == ZSTD_PS_ENABLE { + let c_params = ZSTD_compressionParameters { + windowLog: projection.windowLog, + strategy: projection.strategy, + ..Default::default() + }; + unsafe { ZSTD_ldm_adjustParameters(&mut ldm_params, &c_params) }; + } else { + ldm_params = ZSTDMT_serialResetLdmParameters::default(); + } + + let ldm_enabled = ldm_params.enableLdm == ZSTD_PS_ENABLE; + let bucket_log = if ldm_enabled { + ldm_params.hashLog.wrapping_sub(ldm_params.bucketSizeLog) + } else { + 0 + }; + let previous_bucket_log = if ldm_enabled { + projection + .previousHashLog + .wrapping_sub(projection.previousBucketSizeLog) + } else { + 0 + }; + let hash_size = if ldm_enabled { + (1usize.wrapping_shl(ldm_params.hashLog)).wrapping_mul(projection.ldmEntrySize) + } else { + 0 + }; + let num_buckets = if ldm_enabled { + 1usize.wrapping_shl(bucket_log) + } else { + 0 + }; + let max_nb_seq = unsafe { ZSTD_ldm_getMaxNbSeq(ldm_params, projection.jobSize) }; + + ZSTDMT_serialResetPlan { + ldmParams: ldm_params, + maxNbSeq: max_nb_seq, + hashSize: hash_size, + numBuckets: num_buckets, + bucketLog: bucket_log, + previousBucketLog: previous_bucket_log, + } +} + #[no_mangle] pub unsafe extern "C" fn ZSTDMT_rust_serialStateReset( - enable_ldm: c_int, - checksum_enabled: c_int, - max_nb_seq: usize, + projection: *const ZSTDMT_RustSerialResetProjection, opaque: *mut c_void, + set_ldm_params: Option, reset_next_job: Option, reset_checksum: Option, set_nb_seq: Option, reset_window: Option, resize_tables: Option, - zero_tables: Option, + zero_tables: Option, load_dictionary: Option, copy_window: Option, + publish_params: Option, ) -> c_int { + let Some(projection) = (unsafe { projection.as_ref() }).copied() else { + return 1; + }; let ( + Some(set_ldm_params), Some(reset_next_job), Some(reset_checksum), Some(set_nb_seq), @@ -2803,7 +2991,9 @@ pub unsafe extern "C" fn ZSTDMT_rust_serialStateReset( Some(zero_tables), Some(load_dictionary), Some(copy_window), + Some(publish_params), ) = ( + set_ldm_params, reset_next_job, reset_checksum, set_nb_seq, @@ -2812,26 +3002,45 @@ pub unsafe extern "C" fn ZSTDMT_rust_serialStateReset( zero_tables, load_dictionary, copy_window, + publish_params, ) else { return 1; }; + let plan = unsafe { serial_state_reset_plan(projection) }; unsafe { + set_ldm_params( + opaque, + plan.ldmParams.enableLdm, + plan.ldmParams.hashLog, + plan.ldmParams.bucketSizeLog, + plan.ldmParams.minMatchLength, + plan.ldmParams.hashRateLog, + plan.ldmParams.windowLog, + ); reset_next_job(opaque); - if checksum_enabled != 0 { + if projection.checksumEnabled != 0 { reset_checksum(opaque); } - if enable_ldm == ZSTD_PS_ENABLE { - set_nb_seq(opaque, max_nb_seq); + if plan.ldmParams.enableLdm == ZSTD_PS_ENABLE { + set_nb_seq(opaque, plan.maxNbSeq); reset_window(opaque); - if resize_tables(opaque) != 0 { + if resize_tables( + opaque, + plan.hashSize, + plan.numBuckets, + plan.bucketLog, + plan.previousBucketLog, + ) != 0 + { return 1; } - zero_tables(opaque); + zero_tables(opaque, plan.hashSize, plan.numBuckets); load_dictionary(opaque); copy_window(opaque); } + publish_params(opaque, projection.jobSize); } 0 } @@ -6867,8 +7076,38 @@ mod tests { #[derive(Default)] struct SerialResetTestContext { events: Vec<&'static str>, + ldm_params: ZSTDMT_serialResetLdmParameters, max_nb_seq: usize, resize_result: c_int, + hash_size: usize, + num_buckets: usize, + bucket_log: c_uint, + previous_bucket_log: c_uint, + published_job_size: usize, + published_job_size_u32: u32, + } + + unsafe extern "C" fn serial_reset_test_set_ldm_params( + context: *mut c_void, + enable_ldm: c_int, + hash_log: c_uint, + bucket_size_log: c_uint, + min_match_length: c_uint, + hash_rate_log: c_uint, + window_log: c_uint, + ) { + unsafe { + let context = &mut *context.cast::(); + context.events.push("ldm-params"); + context.ldm_params = ZSTDMT_serialResetLdmParameters { + enableLdm: enable_ldm, + hashLog: hash_log, + bucketSizeLog: bucket_size_log, + minMatchLength: min_match_length, + hashRateLog: hash_rate_log, + windowLog: window_log, + }; + } } unsafe extern "C" fn serial_reset_test_next_job(context: *mut c_void) { @@ -6903,15 +7142,29 @@ mod tests { } } - unsafe extern "C" fn serial_reset_test_resize(context: *mut c_void) -> c_int { + unsafe extern "C" fn serial_reset_test_resize( + context: *mut c_void, + hash_size: usize, + num_buckets: usize, + bucket_log: c_uint, + previous_bucket_log: c_uint, + ) -> c_int { unsafe { let context = &mut *context.cast::(); context.events.push("resize"); + context.hash_size = hash_size; + context.num_buckets = num_buckets; + context.bucket_log = bucket_log; + context.previous_bucket_log = previous_bucket_log; context.resize_result } } - unsafe extern "C" fn serial_reset_test_zero(context: *mut c_void) { + unsafe extern "C" fn serial_reset_test_zero( + context: *mut c_void, + _hash_size: usize, + _num_buckets: usize, + ) { unsafe { (*context.cast::()) .events @@ -6935,19 +7188,31 @@ mod tests { } } + unsafe extern "C" fn serial_reset_test_publish(context: *mut c_void, job_size: usize) { + unsafe { + let context = &mut *context.cast::(); + context.events.push("publish"); + context.published_job_size = job_size; + context.published_job_size_u32 = job_size as u32; + } + } + type SerialResetTestCallbacks = ( + Option, Option, Option, Option, Option, Option, + Option, Option, Option, - Option, + Option, ); fn serial_reset_test_callbacks() -> SerialResetTestCallbacks { ( + Some(serial_reset_test_set_ldm_params), Some(serial_reset_test_next_job), Some(serial_reset_test_checksum), Some(serial_reset_test_set_nb_seq), @@ -6956,19 +7221,40 @@ mod tests { Some(serial_reset_test_zero), Some(serial_reset_test_dictionary), Some(serial_reset_test_copy_window), + Some(serial_reset_test_publish), ) } + fn serial_reset_test_projection( + enable_ldm: c_int, + checksum_enabled: c_int, + job_size: usize, + ) -> ZSTDMT_RustSerialResetProjection { + ZSTDMT_RustSerialResetProjection { + enableLdm: enable_ldm, + checksumEnabled: checksum_enabled, + jobSize: job_size, + windowLog: 20, + strategy: ZSTD_GREEDY, + hashLog: 0, + bucketSizeLog: 0, + minMatchLength: 0, + hashRateLog: 0, + previousHashLog: 10, + previousBucketSizeLog: 4, + ldmEntrySize: 8, + } + } + #[test] - fn serial_state_reset_runs_ldm_operations_in_c_order() { + fn serial_state_reset_enabled_prepares_scalars_and_preserves_order() { let mut context = SerialResetTestContext::default(); let callbacks = serial_reset_test_callbacks(); + let projection = serial_reset_test_projection(ZSTD_PS_ENABLE, 1, 640); let result = unsafe { ZSTDMT_rust_serialStateReset( - ZSTD_PS_ENABLE, - 1, - 123, + &projection, (&mut context as *mut SerialResetTestContext).cast(), callbacks.0, callbacks.1, @@ -6978,14 +7264,33 @@ mod tests { callbacks.5, callbacks.6, callbacks.7, + callbacks.8, + callbacks.9, ) }; assert_eq!(result, 0); - assert_eq!(context.max_nb_seq, 123); + assert_eq!( + context.ldm_params, + ZSTDMT_serialResetLdmParameters { + enableLdm: ZSTD_PS_ENABLE, + hashLog: 14, + bucketSizeLog: 4, + minMatchLength: 64, + hashRateLog: 6, + windowLog: 20, + } + ); + assert_eq!(context.max_nb_seq, 10); + assert_eq!(context.hash_size, 1 << 14 << 3); + assert_eq!(context.num_buckets, 1 << 10); + assert_eq!(context.bucket_log, 10); + assert_eq!(context.previous_bucket_log, 6); + assert_eq!(context.published_job_size, 640); assert_eq!( context.events, vec![ + "ldm-params", "next-job", "checksum", "seq-size", @@ -6994,10 +7299,77 @@ mod tests { "zero", "dictionary", "copy-window", + "publish", ] ); } + #[test] + fn serial_state_reset_disabled_zeroes_ldm_scalars_and_skips_ldm_work() { + let mut context = SerialResetTestContext::default(); + let callbacks = serial_reset_test_callbacks(); + let mut projection = serial_reset_test_projection(ZSTD_PS_DISABLE, 0, 640); + projection.hashLog = 20; + projection.bucketSizeLog = 4; + projection.minMatchLength = 32; + projection.hashRateLog = 5; + + let result = unsafe { + ZSTDMT_rust_serialStateReset( + &projection, + (&mut context as *mut SerialResetTestContext).cast(), + callbacks.0, + callbacks.1, + callbacks.2, + callbacks.3, + callbacks.4, + callbacks.5, + callbacks.6, + callbacks.7, + callbacks.8, + callbacks.9, + ) + }; + + assert_eq!(result, 0); + assert_eq!( + context.ldm_params, + ZSTDMT_serialResetLdmParameters::default() + ); + assert_eq!(context.max_nb_seq, 0); + assert_eq!(context.published_job_size, 640); + assert_eq!(context.events, vec!["ldm-params", "next-job", "publish"]); + } + + #[test] + fn serial_state_reset_preserves_size_t_job_size_overflow_and_cast() { + let mut context = SerialResetTestContext::default(); + let callbacks = serial_reset_test_callbacks(); + let projection = serial_reset_test_projection(ZSTD_PS_ENABLE, 0, usize::MAX); + + let result = unsafe { + ZSTDMT_rust_serialStateReset( + &projection, + (&mut context as *mut SerialResetTestContext).cast(), + callbacks.0, + callbacks.1, + callbacks.2, + callbacks.3, + callbacks.4, + callbacks.5, + callbacks.6, + callbacks.7, + callbacks.8, + callbacks.9, + ) + }; + + assert_eq!(result, 0); + assert_eq!(context.max_nb_seq, usize::MAX / 64); + assert_eq!(context.published_job_size, usize::MAX); + assert_eq!(context.published_job_size_u32, usize::MAX as u32); + } + #[test] fn serial_state_reset_stops_before_zeroing_after_resize_failure() { let mut context = SerialResetTestContext { @@ -7005,12 +7377,11 @@ mod tests { ..Default::default() }; let callbacks = serial_reset_test_callbacks(); + let projection = serial_reset_test_projection(ZSTD_PS_ENABLE, 0, 640); let result = unsafe { ZSTDMT_rust_serialStateReset( - ZSTD_PS_ENABLE, - 0, - 123, + &projection, (&mut context as *mut SerialResetTestContext).cast(), callbacks.0, callbacks.1, @@ -7020,13 +7391,19 @@ mod tests { callbacks.5, callbacks.6, callbacks.7, + callbacks.8, + callbacks.9, ) }; assert_eq!(result, 1); assert_eq!( context.events, - vec!["next-job", "seq-size", "window", "resize"] + vec!["ldm-params", "next-job", "seq-size", "window", "resize"] + ); + assert_eq!( + ZSTDMT_rust_initSerialResetResult(result as c_int), + ERROR(ZstdErrorCode::MemoryAllocation) ); }