diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 2d4e82722..2ad47c231 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -52,6 +52,8 @@ size_t ZSTD_compressStream2_c(ZSTD_CCtx* cctx, ZSTD_EndDirective endOp); int ZSTD_rust_simpleCompress2Level(const void* cctx); int ZSTD_rust_simpleCompressStream2Level(const void* cctx); +void ZSTD_rust_reduceTable(U32* table, U32 size, U32 reducerValue, + int preserveMark); /* Context-free compression-parameter selection and sizing leaves live in * Rust (rust/src/zstd_compress_params.rs). This file retains @@ -2076,63 +2078,20 @@ size_t ZSTD_copyCCtx(ZSTD_CCtx* dstCCtx, const ZSTD_CCtx* srcCCtx, unsigned long } -#define ZSTD_ROWSIZE 16 /*! ZSTD_reduceTable() : * reduce table indexes by `reducerValue`, or squash to zero. * PreserveMark preserves "unsorted mark" for btlazy2 strategy. * It must be set to a clear 0/1 value, to remove branch during inlining. - * Presume table size is a multiple of ZSTD_ROWSIZE + * Presume table size is a multiple of 16 cells * to help auto-vectorization */ -FORCE_INLINE_TEMPLATE void -ZSTD_reduceTable_internal (U32* const table, U32 const size, U32 const reducerValue, int const preserveMark) -{ - int const nbRows = (int)size / ZSTD_ROWSIZE; - int cellNb = 0; - int rowNb; - /* Protect special index values < ZSTD_WINDOW_START_INDEX. */ - U32 const reducerThreshold = reducerValue + ZSTD_WINDOW_START_INDEX; - assert((size & (ZSTD_ROWSIZE-1)) == 0); /* multiple of ZSTD_ROWSIZE */ - assert(size < (1U<<31)); /* can be cast to int */ - -#if ZSTD_MEMORY_SANITIZER && !defined (ZSTD_MSAN_DONT_POISON_WORKSPACE) - /* To validate that the table reuse logic is sound, and that we don't - * access table space that we haven't cleaned, we re-"poison" the table - * space every time we mark it dirty. - * - * This function however is intended to operate on those dirty tables and - * re-clean them. So when this function is used correctly, we can unpoison - * the memory it operated on. This introduces a blind spot though, since - * if we now try to operate on __actually__ poisoned memory, we will not - * detect that. */ - __msan_unpoison(table, size * sizeof(U32)); -#endif - - for (rowNb=0 ; rowNb < nbRows ; rowNb++) { - int column; - for (column=0; column ZSTD_compressedBlockState_t { /* The state contains only integer arrays and enum fields. */ @@ -566,6 +615,48 @@ mod tests { output } + #[test] + fn reduce_table_applies_threshold_and_wrapping_subtraction() { + let mut table = [0, 1, 2, 3, 4, 5, 6, u32::MAX, 0, 0, 0, 0, 0, 0, 0, 0]; + reduce_table_internal(&mut table, 3, false); + assert_eq!(&table[..8], &[0, 0, 0, 0, 0, 2, 3, u32::MAX - 3]); + + let mut wrapped_threshold = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; + reduce_table_internal(&mut wrapped_threshold, u32::MAX, false); + assert_eq!(wrapped_threshold[0], 0); + assert_eq!(wrapped_threshold[1], 2); + assert_eq!(wrapped_threshold[15], 16); + } + + #[test] + fn reduce_table_preserves_only_the_btlazy2_mark() { + let mut ordinary = [0u32; ZSTD_ROWSIZE]; + ordinary[..3].copy_from_slice(&[ZSTD_DUBT_UNSORTED_MARK, 5, 6]); + reduce_table_internal(&mut ordinary, 3, false); + assert_eq!(&ordinary[..3], &[0, 2, 3]); + + let mut btlazy2 = [0u32; ZSTD_ROWSIZE]; + btlazy2[..3].copy_from_slice(&[ZSTD_DUBT_UNSORTED_MARK, 5, 6]); + reduce_table_internal(&mut btlazy2, 3, true); + assert_eq!(&btlazy2[..3], &[ZSTD_DUBT_UNSORTED_MARK, 2, 3]); + } + + #[test] + fn reduce_table_processes_every_cell_in_multiple_rows() { + let mut table = [0u32; ZSTD_ROWSIZE * 2]; + table[0] = 2; + table[ZSTD_ROWSIZE - 1] = 8; + table[ZSTD_ROWSIZE] = 1; + table[ZSTD_ROWSIZE * 2 - 1] = u32::MAX; + + reduce_table_internal(&mut table, 4, false); + + assert_eq!(table[0], 0); + assert_eq!(table[ZSTD_ROWSIZE - 1], 4); + assert_eq!(table[ZSTD_ROWSIZE], 0); + assert_eq!(table[ZSTD_ROWSIZE * 2 - 1], u32::MAX - 4); + } + #[test] fn public_one_shot_abi_is_c_compatible() { let entry: unsafe extern "C" fn(*mut c_void, usize, *const c_void, usize, c_int) -> usize =