feat(compress): move local dictionary assignment to Rust
The local-dictionary callback still contained the policy that selected by-reference versus by-copy loading, rejected internal copies for static CCtx instances, allocated and copied dictionary bytes, and published the resulting local-dictionary state. Move that policy into the Rust compression rewrite so the C callback is only an adapter around the private CCtx and localDict layouts. The adapter retains the C custom allocator and output slots, preserving the existing ownership and free path while keeping the ABI boundary explicit. Test Plan: - ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo check --manifest-path rust/Cargo.toml --tests - 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 make -j1 - ulimit -v 41943040; CARGO_BUILD_JOBS=1 make -j1 -C tests invalidDictionaries - ulimit -v 41943040; CARGO_BUILD_JOBS=1 make -B -j1 -C tests fuzzer - (cd tests && ulimit -v 41943040; ./fuzzer -v -i1 -s6572) - ulimit -v 41943040; make -j1 -C tests test
This commit is contained in:
@@ -462,6 +462,94 @@ pub type CctxAssignPrefixDictFn = unsafe extern "C" fn(
|
||||
dict_content_type: c_int,
|
||||
);
|
||||
|
||||
type CctxAssignLocalDictAllocateFn = unsafe extern "C" fn(*mut c_void, usize) -> *mut c_void;
|
||||
|
||||
/// C-owned outputs and allocator for assigning a local dictionary to a CCtx.
|
||||
///
|
||||
/// Rust owns the by-reference/by-copy decision, static-context rejection,
|
||||
/// allocation/copy ordering, and publication ordering. C retains the private
|
||||
/// `ZSTD_CCtx`/`ZSTD_localDict` layouts and supplies only output slots plus its
|
||||
/// custom allocator adapter.
|
||||
#[repr(C)]
|
||||
pub struct ZSTD_rust_assignLocalDictState {
|
||||
callback_context: *mut c_void,
|
||||
static_size: usize,
|
||||
dict: *mut *const c_void,
|
||||
dict_buffer: *mut *mut c_void,
|
||||
dict_size: *mut usize,
|
||||
dict_content_type: *mut c_int,
|
||||
allocate: CctxAssignLocalDictAllocateFn,
|
||||
}
|
||||
|
||||
const _: () = {
|
||||
assert!(offset_of!(ZSTD_rust_assignLocalDictState, callback_context) == 0);
|
||||
assert!(offset_of!(ZSTD_rust_assignLocalDictState, static_size) == size_of::<usize>());
|
||||
assert!(offset_of!(ZSTD_rust_assignLocalDictState, dict) == 2 * size_of::<usize>());
|
||||
assert!(offset_of!(ZSTD_rust_assignLocalDictState, dict_buffer) == 3 * size_of::<usize>());
|
||||
assert!(offset_of!(ZSTD_rust_assignLocalDictState, dict_size) == 4 * size_of::<usize>());
|
||||
assert!(offset_of!(ZSTD_rust_assignLocalDictState, dict_content_type) == 5 * size_of::<usize>());
|
||||
assert!(offset_of!(ZSTD_rust_assignLocalDictState, allocate) == 6 * size_of::<usize>());
|
||||
assert!(size_of::<ZSTD_rust_assignLocalDictState>() == size_of::<[usize; 7]>());
|
||||
};
|
||||
|
||||
/// Assign a local dictionary while keeping the CCtx and local-dictionary
|
||||
/// layouts in C.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn ZSTD_rust_assignLocalDict(
|
||||
state: *const ZSTD_rust_assignLocalDictState,
|
||||
dict: *const c_void,
|
||||
dict_size: usize,
|
||||
dict_load_method: c_int,
|
||||
dict_content_type: c_int,
|
||||
) -> usize {
|
||||
if state.is_null() {
|
||||
return ERROR(ZstdErrorCode::Generic);
|
||||
}
|
||||
let state = unsafe { &*state };
|
||||
if state.callback_context.is_null()
|
||||
|| state.dict.is_null()
|
||||
|| state.dict_buffer.is_null()
|
||||
|| state.dict_size.is_null()
|
||||
|| state.dict_content_type.is_null()
|
||||
{
|
||||
return ERROR(ZstdErrorCode::Generic);
|
||||
}
|
||||
|
||||
if dict_load_method == ZSTD_DLM_BY_REF {
|
||||
unsafe { *state.dict = dict };
|
||||
} else {
|
||||
if state.static_size != 0 {
|
||||
return ERROR(ZstdErrorCode::MemoryAllocation);
|
||||
}
|
||||
if dict.is_null() && dict_size != 0 {
|
||||
return ERROR(ZstdErrorCode::Generic);
|
||||
}
|
||||
let dict_buffer = unsafe { (state.allocate)(state.callback_context, dict_size) };
|
||||
if dict_buffer.is_null() {
|
||||
return ERROR(ZstdErrorCode::MemoryAllocation);
|
||||
}
|
||||
if dict_size != 0 {
|
||||
unsafe {
|
||||
ptr::copy_nonoverlapping(
|
||||
dict.cast::<u8>(),
|
||||
dict_buffer.cast::<u8>(),
|
||||
dict_size,
|
||||
);
|
||||
}
|
||||
}
|
||||
unsafe {
|
||||
*state.dict_buffer = dict_buffer;
|
||||
*state.dict = dict_buffer.cast_const();
|
||||
}
|
||||
}
|
||||
|
||||
unsafe {
|
||||
*state.dict_size = dict_size;
|
||||
*state.dict_content_type = dict_content_type;
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn dictionary_corrupted() -> usize {
|
||||
ERROR(ZstdErrorCode::DictionaryCorrupted)
|
||||
|
||||
Reference in New Issue
Block a user