diff --git a/programs/fileio.c b/programs/fileio.c index 051f94c29..29aa14ab5 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -3654,7 +3654,7 @@ typedef struct { const char* srcFileName; unsigned long long windowSize; size_t headerError; - unsigned windowLog; + unsigned windowLog; /* Rust derives this from windowSize; retain ABI layout. */ unsigned memLimit; int errorCode; FIO_rust_zstdErrorHelpDisplayFn display; @@ -3725,7 +3725,6 @@ FIO_zstdErrorHelp(const FIO_prefs_t* const prefs, int const errorCode = (int)ZSTD_getErrorCode(err); size_t headerError = 1; unsigned long long windowSize = 0; - unsigned windowLog = 0; unsigned memLimit = 0; FIO_rust_zstdErrorHelpState state = { 0 }; @@ -3735,8 +3734,6 @@ FIO_zstdErrorHelp(const FIO_prefs_t* const prefs, &header, ress->readCtx->srcBuffer, ress->readCtx->srcBufferLoaded); if (headerError == 0) { windowSize = header.windowSize; - windowLog = FIO_highbit64(windowSize) - + ((windowSize & (windowSize - 1)) != 0); memLimit = prefs->memLimit; } } @@ -3745,7 +3742,6 @@ FIO_zstdErrorHelp(const FIO_prefs_t* const prefs, state.srcFileName = srcFileName; state.windowSize = windowSize; state.headerError = headerError; - state.windowLog = windowLog; state.memLimit = memLimit; state.errorCode = errorCode; state.display = FIO_rust_zstdErrorHelp_display; diff --git a/rust/src/fileio_prefs.rs b/rust/src/fileio_prefs.rs index cee745c71..681db3100 100644 --- a/rust/src/fileio_prefs.rs +++ b/rust/src/fileio_prefs.rs @@ -390,9 +390,10 @@ type FIO_rust_zstdErrorHelpDisplayFn = /// Scalar projection for the zstd window-size error diagnostic. /// -/// C keeps frame-header decoding, window-log extraction, and output formatting -/// in C. Rust owns the error filter and selects the concrete or unsupported -/// diagnostic through the display callback. +/// C keeps frame-header decoding and output formatting in C. Rust derives the +/// historical window log, owns the error filter, and selects the concrete or +/// unsupported diagnostic through the display callback. The `window_log` +/// projection field remains part of the C ABI layout but is not consumed. #[cfg(feature = "decompression")] #[repr(C)] pub struct FIO_rust_zstdErrorHelpState { @@ -406,6 +407,16 @@ pub struct FIO_rust_zstdErrorHelpState { display: Option, } +#[cfg(feature = "decompression")] +#[inline] +fn ceil_log2_window_size(window_size: u64) -> c_uint { + if window_size == 0 { + return 0; + } + highbit64(window_size) + + c_uint::from((window_size & window_size.wrapping_sub(1)) != 0) +} + #[cfg(feature = "decompression")] const _: () = { let callback_offset = (offset_of!(FIO_rust_zstdErrorHelpState, error_code) @@ -448,6 +459,7 @@ pub unsafe extern "C" fn FIO_rust_zstdErrorHelp(state: *const FIO_rust_zstdError let Some(display) = state.display else { return; }; + let window_log = ceil_log2_window_size(state.window_size); if state.header_error == 0 { assert!(state.mem_limit > 0); @@ -457,12 +469,12 @@ pub unsafe extern "C" fn FIO_rust_zstdErrorHelp(state: *const FIO_rust_zstdError FIO_RUST_ZSTD_ERROR_HELP_WINDOW_SIZE, state.src_file_name, state.window_size, - state.window_log, + window_log, 0, state.mem_limit, ); } - if state.window_log <= ZSTD_WINDOWLOG_MAX { + if window_log <= ZSTD_WINDOWLOG_MAX { let window_mb = ((state.window_size >> 20) + u64::from((state.window_size & ((1u64 << 20) - 1)) != 0)) as c_uint; @@ -473,7 +485,7 @@ pub unsafe extern "C" fn FIO_rust_zstdErrorHelp(state: *const FIO_rust_zstdError FIO_RUST_ZSTD_ERROR_HELP_WINDOW_GUIDANCE, state.src_file_name, state.window_size, - state.window_log, + window_log, window_mb, state.mem_limit, ); @@ -488,7 +500,7 @@ pub unsafe extern "C" fn FIO_rust_zstdErrorHelp(state: *const FIO_rust_zstdError FIO_RUST_ZSTD_ERROR_HELP_UNSUPPORTED_WINDOW_LOG, state.src_file_name, state.window_size, - state.window_log, + window_log, 0, state.mem_limit, ); @@ -2700,7 +2712,6 @@ mod tests { error_code: c_int, header_error: usize, window_size: u64, - window_log: c_uint, mem_limit: c_uint, ) -> FIO_rust_zstdErrorHelpState { FIO_rust_zstdErrorHelpState { @@ -2708,18 +2719,44 @@ mod tests { src_file_name: ptr::null(), window_size, header_error, - window_log, + window_log: c_uint::MAX, mem_limit, error_code, display: Some(zstd_error_help_display_test), } } + #[cfg(feature = "decompression")] + #[test] + fn ceil_log2_window_size_handles_zero_powers_non_powers_and_boundaries() { + let cases = [ + (0, 0), + (1, 0), + (2, 1), + (4, 2), + (3, 2), + (5, 3), + ((1u64 << 63) - 1, 63), + (1u64 << 63, 63), + ((1u64 << 63) + 1, 64), + (u64::MAX - 1, 64), + (u64::MAX, 64), + ]; + + for (window_size, expected_window_log) in cases { + assert_eq!( + ceil_log2_window_size(window_size), + expected_window_log, + "unexpected window log for window size {window_size}" + ); + } + } + #[cfg(feature = "decompression")] #[test] fn zstd_error_help_ignores_irrelevant_errors() { let mut context = ZstdErrorHelpTestState::default(); - let state = zstd_error_help_test_state(&mut context, 0, 0, 8u64 << 20, 23, 64); + let state = zstd_error_help_test_state(&mut context, 0, 0, 8u64 << 20, 64); unsafe { FIO_rust_zstdErrorHelp(&state) }; @@ -2735,7 +2772,6 @@ mod tests { ZSTD_ERROR_FRAME_PARAMETER_WINDOW_TOO_LARGE, 0, 9u64 << 20, - 24, 64, ); @@ -2766,7 +2802,6 @@ mod tests { ZSTD_ERROR_FRAME_PARAMETER_WINDOW_TOO_LARGE, 0, 1u64 << window_log, - window_log, 64, );