diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 16fa57639..594757046 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -2841,7 +2841,7 @@ ZSTD_compressBlock_splitBlock_internal(ZSTD_CCtx* zc, BYTE* op = (BYTE*)dst; size_t i = 0; size_t srcBytesTotal = 0; - U32* const partitions = zc->blockSplitCtx.partitions; /* size == ZSTD_MAX_NB_BLOCK_SPLITS */ + U32* const partitions = zc->blockSplitCtx.partitions; /* splits plus the terminal boundary */ SeqStore_t* const nextSeqStore = &zc->blockSplitCtx.nextSeqStore; SeqStore_t* const currSeqStore = &zc->blockSplitCtx.currSeqStore; size_t const numSplits = ZSTD_deriveBlockSplits(zc, partitions, nbSeq); diff --git a/lib/compress/zstd_compress_internal.h b/lib/compress/zstd_compress_internal.h index 93b6d4525..5ed9b02bd 100644 --- a/lib/compress/zstd_compress_internal.h +++ b/lib/compress/zstd_compress_internal.h @@ -465,7 +465,8 @@ typedef struct { SeqStore_t currSeqStore; SeqStore_t nextSeqStore; - U32 partitions[ZSTD_MAX_NB_BLOCK_SPLITS]; + /* One terminal boundary follows the at most ZSTD_MAX_NB_BLOCK_SPLITS splits. */ + U32 partitions[ZSTD_MAX_NB_BLOCK_SPLITS + 1]; ZSTD_entropyCTablesMetadata_t entropyMetadata; } ZSTD_blockSplitCtx; diff --git a/rust/src/zstd_compress_block_split.rs b/rust/src/zstd_compress_block_split.rs index 0a7ed73ee..e360c7a80 100644 --- a/rust/src/zstd_compress_block_split.rs +++ b/rust/src/zstd_compress_block_split.rs @@ -421,6 +421,47 @@ mod tests { assert_eq!(partitions[0], 600); } + #[test] + fn an_estimation_error_in_any_candidate_prevents_the_split() { + for error_index in 0..3 { + let mut sequences = vec![SeqDef::default(); 600]; + let mut partitions = [u32::MAX; 2]; + let mut calls = 0; + let splits = unsafe { + run_search(&mut sequences, 0, 0, &mut partitions, |_| { + let current_call = calls; + calls += 1; + if current_call == error_index { + ERROR(ZstdErrorCode::Generic) + } else { + 0 + } + }) + }; + + assert_eq!(splits, 0, "error in candidate {error_index}"); + assert_eq!(calls, 3, "error in candidate {error_index}"); + assert_eq!(partitions[0], 600, "error in candidate {error_index}"); + } + } + + #[test] + fn equal_estimated_cost_does_not_split() { + let mut sequences = vec![SeqDef::default(); 600]; + let mut partitions = [u32::MAX; 2]; + let mut calls = 0; + let splits = unsafe { + run_search(&mut sequences, 0, 0, &mut partitions, |_| { + calls += 1; + 0 + }) + }; + + assert_eq!(splits, 0); + assert_eq!(calls, 3); + assert_eq!(partitions[0], 600); + } + #[test] fn derived_views_keep_long_length_metadata_in_their_ranges() { let mut sequences = vec![SeqDef::default(); 600];