diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 15ac7cf35..d0754fdfe 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -7284,23 +7284,72 @@ size_t ZSTD_compressBegin(ZSTD_CCtx* cctx, int compressionLevel) } +typedef void (*ZSTD_rust_traceEnd_f)( + void* context, unsigned long long traceCtx, const void* trace); +typedef struct { + void* callbackContext; + unsigned long long traceCtx; + int streaming; + unsigned dictionaryID; + size_t dictionarySize; + size_t uncompressedSize; + size_t compressedSize; + const void* params; + const void* cctx; + ZSTD_rust_traceEnd_f traceEnd; +} ZSTD_rust_traceEndState; +typedef char ZSTD_rust_trace_end_state_layout[ + (offsetof(ZSTD_rust_traceEndState, callbackContext) == 0 + && offsetof(ZSTD_rust_traceEndState, traceCtx) == sizeof(void*) + && offsetof(ZSTD_rust_traceEndState, streaming) + == sizeof(void*) + sizeof(unsigned long long) + && offsetof(ZSTD_rust_traceEndState, dictionaryID) + == sizeof(void*) + sizeof(unsigned long long) + sizeof(int) + && offsetof(ZSTD_rust_traceEndState, dictionarySize) + == sizeof(void*) + sizeof(unsigned long long) + 2 * sizeof(int) + && offsetof(ZSTD_rust_traceEndState, uncompressedSize) + == offsetof(ZSTD_rust_traceEndState, dictionarySize) + sizeof(size_t) + && offsetof(ZSTD_rust_traceEndState, compressedSize) + == offsetof(ZSTD_rust_traceEndState, dictionarySize) + 2 * sizeof(size_t) + && offsetof(ZSTD_rust_traceEndState, params) + == offsetof(ZSTD_rust_traceEndState, dictionarySize) + 3 * sizeof(size_t) + && offsetof(ZSTD_rust_traceEndState, cctx) + == offsetof(ZSTD_rust_traceEndState, dictionarySize) + 4 * sizeof(size_t) + && offsetof(ZSTD_rust_traceEndState, traceEnd) + == offsetof(ZSTD_rust_traceEndState, dictionarySize) + 5 * sizeof(size_t) + && sizeof(ZSTD_rust_traceEndState) + == offsetof(ZSTD_rust_traceEndState, dictionarySize) + 6 * sizeof(size_t)) + ? 1 : -1]; +void ZSTD_rust_traceEnd(const ZSTD_rust_traceEndState* state); + +#if ZSTD_TRACE +static void ZSTD_rust_traceEnd_callback(void* context, + unsigned long long traceCtx, const void* trace) +{ + (void)context; + if (ZSTD_trace_compress_end != NULL) { + ZSTD_trace_compress_end((ZSTD_TraceCtx)traceCtx, + (ZSTD_Trace const*)trace); + } +} +#endif + void ZSTD_CCtx_trace(ZSTD_CCtx* cctx, size_t extraCSize) { #if ZSTD_TRACE - if (cctx->traceCtx && ZSTD_trace_compress_end != NULL) { - int const streaming = cctx->inBuffSize > 0 || cctx->outBuffSize > 0 || cctx->appliedParams.nbWorkers > 0; - ZSTD_Trace trace; - ZSTD_memset(&trace, 0, sizeof(trace)); - trace.version = ZSTD_VERSION_NUMBER; - trace.streaming = streaming; - trace.dictionaryID = cctx->dictID; - trace.dictionarySize = cctx->dictContentSize; - trace.uncompressedSize = cctx->consumedSrcSize; - trace.compressedSize = cctx->producedCSize + extraCSize; - trace.params = &cctx->appliedParams; - trace.cctx = cctx; - ZSTD_trace_compress_end(cctx->traceCtx, &trace); - } + ZSTD_rust_traceEndState state; + state.callbackContext = cctx; + state.traceCtx = cctx->traceCtx; + state.streaming = cctx->inBuffSize > 0 || cctx->outBuffSize > 0 + || cctx->appliedParams.nbWorkers > 0; + state.dictionaryID = cctx->dictID; + state.dictionarySize = cctx->dictContentSize; + state.uncompressedSize = cctx->consumedSrcSize; + state.compressedSize = cctx->producedCSize + extraCSize; + state.params = &cctx->appliedParams; + state.cctx = cctx; + state.traceEnd = ZSTD_rust_traceEnd_callback; + ZSTD_rust_traceEnd(&state); cctx->traceCtx = 0; #else (void)cctx; diff --git a/rust/src/zstd_compress.rs b/rust/src/zstd_compress.rs index 81499d971..e654c5698 100644 --- a/rust/src/zstd_compress.rs +++ b/rust/src/zstd_compress.rs @@ -57,6 +57,7 @@ use crate::zstd_compress_stats::{ ZSTD_rust_validateSeqStore, ZSTD_LLT_LITERAL_LENGTH, ZSTD_LLT_MATCH_LENGTH, }; use crate::zstd_compress_superblock::ZSTD_rust_compressSuperBlock; +use crate::zstd_common::ZSTD_VERSION_NUMBER; use std::ffi::c_void; use std::mem::{offset_of, size_of, MaybeUninit}; use std::os::raw::{c_int, c_longlong, c_uint}; @@ -2796,6 +2797,105 @@ pub unsafe extern "C" fn ZSTD_rust_compressContinue( type CompressEndTraceFn = unsafe extern "C" fn(*mut c_void, usize); +type CompressTraceEndFn = unsafe extern "C" fn(*mut c_void, u64, *const c_void); + +/// ABI-compatible compression trace record. The weak trace hook remains a C +/// callback, but Rust owns construction of the record and its enabled/reset +/// policy. +#[repr(C)] +struct ZSTD_Trace { + version: c_uint, + streaming: c_int, + dictionary_id: c_uint, + dictionary_is_cold: c_int, + dictionary_size: usize, + uncompressed_size: usize, + compressed_size: usize, + params: *const c_void, + cctx: *const c_void, + dctx: *const c_void, +} + +const _: () = { + assert!(offset_of!(ZSTD_Trace, version) == 0); + assert!(offset_of!(ZSTD_Trace, streaming) == size_of::()); + assert!(offset_of!(ZSTD_Trace, dictionary_id) == size_of::() + size_of::()); + assert!( + offset_of!(ZSTD_Trace, dictionary_is_cold) + == 2 * size_of::() + size_of::() + ); + assert!( + offset_of!(ZSTD_Trace, dictionary_size) + == 2 * size_of::() + 2 * size_of::() + ); + assert!(offset_of!(ZSTD_Trace, uncompressed_size) == offset_of!(ZSTD_Trace, dictionary_size) + size_of::()); + assert!(offset_of!(ZSTD_Trace, compressed_size) == offset_of!(ZSTD_Trace, dictionary_size) + 2 * size_of::()); + assert!(offset_of!(ZSTD_Trace, params) == offset_of!(ZSTD_Trace, dictionary_size) + 3 * size_of::()); + assert!(offset_of!(ZSTD_Trace, cctx) == offset_of!(ZSTD_Trace, dictionary_size) + 4 * size_of::()); + assert!(offset_of!(ZSTD_Trace, dctx) == offset_of!(ZSTD_Trace, dictionary_size) + 5 * size_of::()); + assert!(size_of::() == offset_of!(ZSTD_Trace, dictionary_size) + 6 * size_of::()); +}; + +/// Inputs for Rust-owned compression trace construction. +#[repr(C)] +pub struct ZSTD_rust_traceEndState { + callback_context: *mut c_void, + trace_ctx: u64, + streaming: c_int, + dictionary_id: c_uint, + dictionary_size: usize, + uncompressed_size: usize, + compressed_size: usize, + params: *const c_void, + cctx: *const c_void, + trace_end: CompressTraceEndFn, +} + +const _: () = { + assert!(size_of::() == size_of::()); + assert!(offset_of!(ZSTD_rust_traceEndState, callback_context) == 0); + assert!(offset_of!(ZSTD_rust_traceEndState, trace_ctx) == size_of::()); + assert!(offset_of!(ZSTD_rust_traceEndState, streaming) == size_of::() + size_of::()); + assert!(offset_of!(ZSTD_rust_traceEndState, dictionary_id) == size_of::() + size_of::() + size_of::()); + assert!(offset_of!(ZSTD_rust_traceEndState, dictionary_size) == size_of::() + size_of::() + 2 * size_of::()); + assert!(offset_of!(ZSTD_rust_traceEndState, uncompressed_size) == offset_of!(ZSTD_rust_traceEndState, dictionary_size) + size_of::()); + assert!(offset_of!(ZSTD_rust_traceEndState, compressed_size) == offset_of!(ZSTD_rust_traceEndState, dictionary_size) + 2 * size_of::()); + assert!(offset_of!(ZSTD_rust_traceEndState, params) == offset_of!(ZSTD_rust_traceEndState, dictionary_size) + 3 * size_of::()); + assert!(offset_of!(ZSTD_rust_traceEndState, cctx) == offset_of!(ZSTD_rust_traceEndState, dictionary_size) + 4 * size_of::()); + assert!(offset_of!(ZSTD_rust_traceEndState, trace_end) == offset_of!(ZSTD_rust_traceEndState, dictionary_size) + 5 * size_of::()); + assert!(size_of::() == offset_of!(ZSTD_rust_traceEndState, dictionary_size) + 6 * size_of::()); +}; + +/// Construct and publish a compression trace record, if tracing is enabled. +#[no_mangle] +pub unsafe extern "C" fn ZSTD_rust_traceEnd(state: *const ZSTD_rust_traceEndState) { + let Some(state) = (unsafe { state.as_ref() }) else { + return; + }; + if state.trace_ctx == 0 { + return; + } + let trace = ZSTD_Trace { + version: ZSTD_VERSION_NUMBER as c_uint, + streaming: state.streaming, + dictionary_id: state.dictionary_id, + dictionary_is_cold: 0, + dictionary_size: state.dictionary_size, + uncompressed_size: state.uncompressed_size, + compressed_size: state.compressed_size, + params: state.params, + cctx: state.cctx, + dctx: ptr::null(), + }; + unsafe { + (state.trace_end)( + state.callback_context, + state.trace_ctx, + (&trace as *const ZSTD_Trace).cast(), + ); + } +} + /// Explicit projection for the public end-of-frame orchestration. /// /// Rust owns callback ordering, output offset/capacity accounting, and