diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index d1eafb43d..a083c7a54 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -2222,8 +2222,8 @@ typedef char ZSTD_rust_external_sequence_store_state_layout[ == 5 * sizeof(void*)) ? 1 : -1]; -typedef size_t (*ZSTD_rust_initCDictAssignContent_f)( - void* context, const void* dict, size_t dictSize, int dictLoadMethod); +typedef void* (*ZSTD_rust_initCDictReserveContent_f)( + void* context, size_t dictSize); typedef void* (*ZSTD_rust_initCDictReserveEntropy_f)(void* context); typedef void (*ZSTD_rust_initCDictResetBlockState_f)(void* context); typedef size_t (*ZSTD_rust_initCDictResetMatchState_f)( @@ -2247,7 +2247,7 @@ typedef struct { U32* dictID; int* compressionLevel; int* contentSizeFlag; - ZSTD_rust_initCDictAssignContent_f assignContent; + ZSTD_rust_initCDictReserveContent_f reserveContent; ZSTD_rust_initCDictReserveEntropy_f reserveEntropy; ZSTD_rust_initCDictResetBlockState_f resetBlockState; ZSTD_rust_initCDictResetMatchState_f resetMatchState; @@ -2283,7 +2283,7 @@ typedef char ZSTD_rust_init_cdict_state_layout[ == 12 * sizeof(void*) && offsetof(ZSTD_rust_initCDictState, contentSizeFlag) == 13 * sizeof(void*) - && offsetof(ZSTD_rust_initCDictState, assignContent) + && offsetof(ZSTD_rust_initCDictState, reserveContent) == 14 * sizeof(void*) && offsetof(ZSTD_rust_initCDictState, reserveEntropy) == 15 * sizeof(void*) @@ -6029,20 +6029,11 @@ ZSTD_compress_insertDictionary(ZSTD_compressedBlockState_t* bs, ZSTD_loadDictionaryContent_callback); } -static size_t ZSTD_rust_initCDict_assignContent( - void* context, const void* dict, size_t dictSize, int dictLoadMethod) +static void* ZSTD_rust_initCDict_reserveContent(void* context, size_t dictSize) { ZSTD_CDict* const cdict = (ZSTD_CDict*)context; - if ((dictLoadMethod == ZSTD_dlm_byRef) || (!dict) || (!dictSize)) { - cdict->dictContent = dict; - } else { - void* const internalBuffer = ZSTD_cwksp_reserve_object( - &cdict->workspace, ZSTD_cwksp_align(dictSize, sizeof(void*))); - RETURN_ERROR_IF(!internalBuffer, memory_allocation, "NULL pointer!"); - cdict->dictContent = internalBuffer; - ZSTD_memcpy(internalBuffer, dict, dictSize); - } - return 0; + return ZSTD_cwksp_reserve_object( + &cdict->workspace, ZSTD_cwksp_align(dictSize, sizeof(void*))); } static void* ZSTD_rust_initCDict_reserveEntropy(void* context) @@ -6624,7 +6615,7 @@ static size_t ZSTD_initCDict_internal( state.dictID = &cdict->dictID; state.compressionLevel = ¶ms.compressionLevel; state.contentSizeFlag = ¶ms.fParams.contentSizeFlag; - state.assignContent = ZSTD_rust_initCDict_assignContent; + state.reserveContent = ZSTD_rust_initCDict_reserveContent; state.reserveEntropy = ZSTD_rust_initCDict_reserveEntropy; state.resetBlockState = ZSTD_rust_initCDict_resetBlockState; state.resetMatchState = ZSTD_rust_initCDict_resetMatchState; diff --git a/rust/src/zstd_compress_dictionary.rs b/rust/src/zstd_compress_dictionary.rs index d47304c32..20f7a997e 100644 --- a/rust/src/zstd_compress_dictionary.rs +++ b/rust/src/zstd_compress_dictionary.rs @@ -85,8 +85,9 @@ fn dictionary_corrupted() -> usize { } const ZSTD_CCTX_INIT_STAGE: c_int = 0; -#[cfg(test)] const ZSTD_DLM_BY_REF: c_int = 1; +#[cfg(test)] +const ZSTD_DLM_BY_COPY: c_int = 0; #[inline] fn stage_wrong() -> usize { @@ -857,8 +858,7 @@ pub unsafe extern "C" fn ZSTD_rust_compressBeginUsingDict( } } -type InitCDictAssignContentFn = - unsafe extern "C" fn(*mut c_void, *const c_void, usize, c_int) -> usize; +type InitCDictReserveContentFn = unsafe extern "C" fn(*mut c_void, usize) -> *mut c_void; type InitCDictReserveEntropyFn = unsafe extern "C" fn(*mut c_void) -> *mut c_void; type InitCDictResetBlockStateFn = unsafe extern "C" fn(*mut c_void); type InitCDictResetMatchStateFn = unsafe extern "C" fn(*mut c_void, *const c_void, c_int) -> usize; @@ -867,9 +867,10 @@ type InitCDictInsertDictionaryFn = /// Projection for CDict content/state initialization. /// -/// Rust owns the initialization order and scalar field policy. C callbacks -/// retain workspace allocation, match-state reset, and dictionary insertion -/// because those operations use private CDict layouts. +/// Rust owns the content branch/copy, initialization order, and scalar field +/// policy. C callbacks retain private workspace allocation, match-state reset, +/// and dictionary insertion because those operations use private CDict +/// layouts. #[repr(C)] pub struct ZSTD_rust_initCDictState { callback_context: *mut c_void, @@ -886,7 +887,7 @@ pub struct ZSTD_rust_initCDictState { dict_id: *mut c_uint, compression_level: *mut c_int, content_size_flag: *mut c_int, - assign_content: Option, + reserve_content: Option, reserve_entropy: Option, reset_block_state: Option, reset_match_state: Option, @@ -894,7 +895,7 @@ pub struct ZSTD_rust_initCDictState { } const _: () = { - assert!(size_of::() == size_of::()); + assert!(size_of::() == size_of::()); assert!(size_of::() == size_of::()); assert!(size_of::() == size_of::()); assert!(size_of::() == size_of::()); @@ -916,7 +917,7 @@ const _: () = { assert!(offset_of!(ZSTD_rust_initCDictState, dict_id) == 11 * size_of::()); assert!(offset_of!(ZSTD_rust_initCDictState, compression_level) == 12 * size_of::()); assert!(offset_of!(ZSTD_rust_initCDictState, content_size_flag) == 13 * size_of::()); - assert!(offset_of!(ZSTD_rust_initCDictState, assign_content) == 14 * size_of::()); + assert!(offset_of!(ZSTD_rust_initCDictState, reserve_content) == 14 * size_of::()); assert!(offset_of!(ZSTD_rust_initCDictState, reserve_entropy) == 15 * size_of::()); assert!(offset_of!(ZSTD_rust_initCDictState, reset_block_state) == 16 * size_of::()); assert!(offset_of!(ZSTD_rust_initCDictState, reset_match_state) == 17 * size_of::()); @@ -939,7 +940,7 @@ pub unsafe extern "C" fn ZSTD_rust_initCDict( return ERROR(ZstdErrorCode::Generic); } let state = unsafe { &*state }; - let Some(assign_content) = state.assign_content else { + let Some(reserve_content) = state.reserve_content else { return ERROR(ZstdErrorCode::Generic); }; let Some(reserve_entropy) = state.reserve_entropy else { @@ -972,10 +973,17 @@ pub unsafe extern "C" fn ZSTD_rust_initCDict( return ERROR(ZstdErrorCode::Generic); } - let assign_result = - unsafe { assign_content(state.callback_context, dict, dict_size, dict_load_method) }; - if ERR_isError(assign_result) { - return assign_result; + if dict_load_method == ZSTD_DLM_BY_REF || dict.is_null() || dict_size == 0 { + unsafe { *state.dict_content = dict }; + } else { + let internal_buffer = unsafe { reserve_content(state.callback_context, dict_size) }; + if internal_buffer.is_null() { + return ERROR(ZstdErrorCode::MemoryAllocation); + } + unsafe { + ptr::copy_nonoverlapping(dict.cast::(), internal_buffer.cast::(), dict_size); + *state.dict_content = internal_buffer.cast_const(); + } } unsafe { @@ -2009,16 +2017,15 @@ mod tests { struct InitCDictProbe { events: Vec<&'static str>, - assigned_dict: *const c_void, - assigned_size: usize, - assigned_load_method: c_int, + reserved_content_size: usize, + content_storage: [u8; 16], + reserve_content_available: bool, reset_c_params: ZSTD_compressionParameters, reset_use_row_match_finder: c_int, inserted_params: *const c_void, inserted_dict: *const c_void, inserted_size: usize, inserted_content_type: c_int, - assign_result: usize, entropy_workspace: *mut c_void, reset_match_result: usize, insert_result: usize, @@ -2028,16 +2035,15 @@ mod tests { fn default() -> Self { Self { events: Vec::new(), - assigned_dict: ptr::null(), - assigned_size: 0, - assigned_load_method: 0, + reserved_content_size: 0, + content_storage: [0; 16], + reserve_content_available: true, reset_c_params: ZSTD_compressionParameters::default(), reset_use_row_match_finder: 0, inserted_params: ptr::null(), inserted_dict: ptr::null(), inserted_size: 0, inserted_content_type: 0, - assign_result: 0, entropy_workspace: ptr::null_mut(), reset_match_result: 0, insert_result: 0, @@ -2049,18 +2055,18 @@ mod tests { unsafe { &mut *context.cast::() } } - unsafe extern "C" fn init_cdict_assign_content( + unsafe extern "C" fn init_cdict_reserve_content( context: *mut c_void, - dict: *const c_void, dict_size: usize, - dict_load_method: c_int, - ) -> usize { + ) -> *mut c_void { let probe = unsafe { init_cdict_probe(context) }; - probe.events.push("assign"); - probe.assigned_dict = dict; - probe.assigned_size = dict_size; - probe.assigned_load_method = dict_load_method; - probe.assign_result + probe.events.push("reserve-content"); + probe.reserved_content_size = dict_size; + if !probe.reserve_content_available || dict_size > probe.content_storage.len() { + ptr::null_mut() + } else { + probe.content_storage.as_mut_ptr().cast() + } } unsafe extern "C" fn init_cdict_reserve_entropy(context: *mut c_void) -> *mut c_void { @@ -2148,7 +2154,7 @@ mod tests { dict_id: &mut dict_id, compression_level: &mut compression_level, content_size_flag: &mut content_size_flag, - assign_content: Some(init_cdict_assign_content), + reserve_content: Some(init_cdict_reserve_content), reserve_entropy: Some(init_cdict_reserve_entropy), reset_block_state: Some(init_cdict_reset_block_state), reset_match_state: Some(init_cdict_reset_match_state), @@ -2168,11 +2174,9 @@ mod tests { assert_eq!(result, 0); assert_eq!( probe.events, - ["assign", "reserve", "reset-block", "reset-match", "insert"] + ["reserve", "reset-block", "reset-match", "insert"] ); - assert_eq!(probe.assigned_dict, dictionary.as_ptr().cast()); - assert_eq!(probe.assigned_size, dictionary.len()); - assert_eq!(probe.assigned_load_method, ZSTD_DLM_BY_REF); + assert_eq!(dict_content, dictionary.as_ptr().cast()); assert_eq!(probe.reset_c_params, c_params); assert_eq!(probe.reset_use_row_match_finder, use_row_match_finder); assert_eq!(probe.inserted_params, params); @@ -2189,6 +2193,86 @@ mod tests { assert_eq!(content_size_flag, 1); } + #[test] + fn cdict_init_copies_by_copy_content_in_rust_after_reserving_storage() { + let dictionary = [9u8, 8, 7, 6, 5]; + let mut probe = InitCDictProbe { + entropy_workspace: 0x1000usize as *mut c_void, + insert_result: 0x1234, + ..Default::default() + }; + let c_params = ZSTD_compressionParameters { + windowLog: 21, + chainLog: 18, + hashLog: 19, + searchLog: 4, + minMatch: 5, + targetLength: 16, + strategy: 3, + }; + let mut match_state_c_params = ZSTD_compressionParameters::default(); + let enable_dedicated_dict_search = 1; + let use_row_match_finder = 2; + let mut dedicated_dict_search = 0; + let mut dict_content = ptr::null(); + let mut dict_content_size = 0; + let mut dict_content_type = 0; + let mut entropy_workspace = ptr::null_mut(); + let mut dict_id = 0; + let mut compression_level = 99; + let mut content_size_flag = 0; + let params = 0x2000usize as *const c_void; + let callback_context = (&mut probe as *mut InitCDictProbe).cast(); + let state = ZSTD_rust_initCDictState { + callback_context, + params, + c_params: &c_params, + match_state_c_params: &mut match_state_c_params, + dedicated_dict_search: &mut dedicated_dict_search, + enable_dedicated_dict_search: &enable_dedicated_dict_search, + use_row_match_finder: &use_row_match_finder, + dict_content: &mut dict_content, + dict_content_size: &mut dict_content_size, + dict_content_type: &mut dict_content_type, + entropy_workspace: &mut entropy_workspace, + dict_id: &mut dict_id, + compression_level: &mut compression_level, + content_size_flag: &mut content_size_flag, + reserve_content: Some(init_cdict_reserve_content), + reserve_entropy: Some(init_cdict_reserve_entropy), + reset_block_state: Some(init_cdict_reset_block_state), + reset_match_state: Some(init_cdict_reset_match_state), + insert_dictionary: Some(init_cdict_insert_dictionary), + }; + + let result = unsafe { + ZSTD_rust_initCDict( + &state, + dictionary.as_ptr().cast(), + dictionary.len(), + ZSTD_DLM_BY_COPY, + ZSTD_DCT_RAW_CONTENT, + ) + }; + + assert_eq!(result, 0); + assert_eq!( + probe.events, + [ + "reserve-content", + "reserve", + "reset-block", + "reset-match", + "insert" + ] + ); + assert_eq!(probe.reserved_content_size, dictionary.len()); + assert_eq!(&probe.content_storage[..dictionary.len()], &dictionary); + assert_eq!(dict_content, probe.content_storage.as_ptr().cast()); + assert_eq!(probe.inserted_dict, dict_content); + assert_eq!(dict_content_size, dictionary.len()); + } + struct CompressBeginProbe { events: Vec<&'static str>, reset_params: *const c_void,