From f542dc11b80198bbe7b47eef4fde3884e7181c37 Mon Sep 17 00:00:00 2001 From: Dmitry Prudnikov Date: Wed, 25 Mar 2026 19:44:29 +0200 Subject: [PATCH 1/6] feat: replace unimplemented! panic with proper error handling for large literals MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace `unimplemented!("too many literals")` with `unreachable!` and descriptive message explaining the MAX_BLOCK_SIZE invariant - Add debug_assert validating literals never exceed MAX_BLOCK_SIZE (128KB) - Add RFC 8878 §3.1.1.3.1.1 size format documentation comments - Add roundtrip tests exercising all 4 size format boundaries (0b00–0b11) - Add cross-validation tests (Rust ↔ C FFI) for large blocks up to 128KB Closes #15 --- zstd/src/encoding/blocks/compressed.rs | 22 ++++++++- zstd/src/tests/roundtrip_integrity.rs | 58 +++++++++++++++++++++++ zstd/tests/cross_validation.rs | 65 ++++++++++++++++++++++++++ 3 files changed, 144 insertions(+), 1 deletion(-) diff --git a/zstd/src/encoding/blocks/compressed.rs b/zstd/src/encoding/blocks/compressed.rs index ad7f413c..9b411b74 100644 --- a/zstd/src/encoding/blocks/compressed.rs +++ b/zstd/src/encoding/blocks/compressed.rs @@ -316,6 +316,15 @@ fn compress_literals( last_table: Option<&huff0_encoder::HuffmanTable>, writer: &mut BitWriter<&mut Vec>, ) -> Option { + // The 18-bit size format supports up to 262143 bytes, which covers MAX_BLOCK_SIZE (128KB). + // Literals within a single block cannot exceed the block's content size. + debug_assert!( + literals.len() <= crate::common::MAX_BLOCK_SIZE as usize, + "literals size {} exceeds MAX_BLOCK_SIZE ({})", + literals.len(), + crate::common::MAX_BLOCK_SIZE, + ); + let reset_idx = writer.index(); let new_encoder_table = huff0_encoder::HuffmanTable::build_from_data(literals); @@ -341,12 +350,23 @@ fn compress_literals( writer.write_bits(3u8, 2); // treeless compressed literals type } + // RFC 8878 §3.1.1.3.1.1: Size_Format determines header size and stream count. + // - 0b00: single stream, 10-bit sizes (regenerated ≤ 1023) + // - 0b01: 4 streams, 10-bit sizes (regenerated ≤ 1023) + // - 0b10: 4 streams, 14-bit sizes (regenerated ≤ 16383) + // - 0b11: 4 streams, 18-bit sizes (regenerated ≤ 262143) + // MAX_BLOCK_SIZE (128KB = 131072) fits within the 18-bit format. let (size_format, size_bits) = match literals.len() { 0..6 => (0b00u8, 10), 6..1024 => (0b01, 10), 1024..16384 => (0b10, 14), 16384..262144 => (0b11, 18), - _ => unimplemented!("too many literals"), + _ => unreachable!( + "literals size {} exceeds 18-bit maximum (262143); \ + blocks are limited to MAX_BLOCK_SIZE ({}) which always fits", + literals.len(), + crate::common::MAX_BLOCK_SIZE, + ), }; writer.write_bits(size_format, 2); diff --git a/zstd/src/tests/roundtrip_integrity.rs b/zstd/src/tests/roundtrip_integrity.rs index b246ced8..8c9f7cdc 100644 --- a/zstd/src/tests/roundtrip_integrity.rs +++ b/zstd/src/tests/roundtrip_integrity.rs @@ -60,6 +60,20 @@ fn roundtrip_streaming(data: &[u8]) -> Vec { result } +/// Generate data with limited alphabet for better Huffman compressibility +/// but enough variety to avoid RLE path. +fn generate_huffman_friendly(seed: u64, len: usize, alphabet_size: u8) -> Vec { + let mut state = seed; + let mut data = Vec::with_capacity(len); + for _ in 0..len { + state = state + .wrapping_mul(6364136223846793005) + .wrapping_add(1442695040888963407); + data.push(((state >> 33) as u8) % alphabet_size); + } + data +} + // Cross-validation tests (pure Rust ↔ C FFI) are in tests/cross_validation.rs // because dev-dependencies (zstd) aren't available in library test modules. @@ -130,3 +144,47 @@ fn roundtrip_edge_cases() { let rle = vec![0xABu8; 1_000_000]; assert_eq!(roundtrip_simple(&rle), rle); } + +/// Tests that exercise all 4 compressed literals size format paths: +/// - Size format 0b00: single stream, 10-bit (regenerated_size < 6) +/// - Size format 0b01: 4 streams, 10-bit (6 ≤ regenerated_size < 1024) +/// - Size format 0b10: 4 streams, 14-bit (1024 ≤ regenerated_size < 16384) +/// - Size format 0b11: 4 streams, 18-bit (16384 ≤ regenerated_size < 262144) +#[test] +fn roundtrip_literals_size_format_boundaries() { + // Size format 0b10 boundary: data just over 1KB forces 14-bit path. + // Uses limited alphabet so Huffman encoding is used (not raw fallback). + let data_1025 = generate_huffman_friendly(42, 1025, 16); + assert_eq!(roundtrip_simple(&data_1025), data_1025); + assert_eq!(roundtrip_streaming(&data_1025), data_1025); + + // Size format 0b10 upper boundary: just under 16KB. + let data_16383 = generate_huffman_friendly(43, 16383, 32); + assert_eq!(roundtrip_simple(&data_16383), data_16383); + + // Size format 0b11 boundary: data at exactly 16384 forces 18-bit path. + let data_16384 = generate_huffman_friendly(44, 16384, 32); + assert_eq!(roundtrip_simple(&data_16384), data_16384); + assert_eq!(roundtrip_streaming(&data_16384), data_16384); + + // Size format 0b11: data at 64KB (well within 18-bit range). + let data_64k = generate_huffman_friendly(45, 65536, 64); + assert_eq!(roundtrip_simple(&data_64k), data_64k); + + // Size format 0b11: data at MAX_BLOCK_SIZE (128KB) — maximum valid literal size. + // This is the largest possible literal section in a single block. + let data_128k = generate_huffman_friendly(46, 128 * 1024, 64); + assert_eq!(roundtrip_simple(&data_128k), data_128k); + assert_eq!(roundtrip_streaming(&data_128k), data_128k); +} + +/// Multi-block data larger than MAX_BLOCK_SIZE that exercises the 4-stream +/// Huffman encoding across multiple blocks, each with large literal sections. +#[test] +fn roundtrip_multi_block_large_literals() { + // 512KB of Huffman-friendly data — will be split into multiple 128KB blocks, + // each exercising the 18-bit (0b11) size format with 4-stream encoding. + let data = generate_huffman_friendly(100, 512 * 1024, 48); + assert_eq!(roundtrip_simple(&data), data); + assert_eq!(roundtrip_streaming(&data), data); +} diff --git a/zstd/tests/cross_validation.rs b/zstd/tests/cross_validation.rs index 619521d0..48447499 100644 --- a/zstd/tests/cross_validation.rs +++ b/zstd/tests/cross_validation.rs @@ -21,6 +21,19 @@ fn generate_data(seed: u64, len: usize) -> Vec { data } +/// Generate data with limited alphabet for Huffman-friendly compression. +fn generate_huffman_friendly(seed: u64, len: usize, alphabet_size: u8) -> Vec { + let mut state = seed; + let mut data = Vec::with_capacity(len); + for _ in 0..len { + state = state + .wrapping_mul(6364136223846793005) + .wrapping_add(1442695040888963407); + data.push(((state >> 33) as u8) % alphabet_size); + } + data +} + #[test] fn cross_rust_compress_ffi_decompress_1000() { for i in 0..1000u64 { @@ -52,3 +65,55 @@ fn cross_ffi_compress_rust_decompress_1000() { ); } } + +/// Cross-validate large blocks (>16KB up to 128KB) that exercise the 18-bit +/// size format (0b11) with 4-stream Huffman encoding. +#[test] +fn cross_rust_compress_ffi_decompress_large_blocks() { + // Sizes targeting each compressed literals size format boundary: + // 1025 (14-bit), 16384 (18-bit), 65536 (18-bit), 128*1024 (18-bit max) + let sizes = [1025, 16384, 65536, 128 * 1024]; + for (i, &size) in sizes.iter().enumerate() { + // Huffman-friendly data ensures compressed path is taken (not raw fallback) + let data = generate_huffman_friendly(i as u64 + 200, size, 48); + + let compressed = compress_to_vec(&data[..], CompressionLevel::Fastest); + let result = zstd::decode_all(compressed.as_slice()).unwrap(); + assert_eq!( + data, result, + "rust→ffi large block roundtrip failed at size={size}" + ); + } + + // Multi-block: 512KB forces multiple blocks, each with large literals + let data = generate_huffman_friendly(300, 512 * 1024, 48); + let compressed = compress_to_vec(&data[..], CompressionLevel::Fastest); + let result = zstd::decode_all(compressed.as_slice()).unwrap(); + assert_eq!(data, result, "rust→ffi multi-block roundtrip failed"); +} + +/// Cross-validate C FFI compress → Rust decompress for large blocks. +#[test] +fn cross_ffi_compress_rust_decompress_large_blocks() { + let sizes = [1025, 16384, 65536, 128 * 1024]; + for (i, &size) in sizes.iter().enumerate() { + let data = generate_huffman_friendly(i as u64 + 400, size, 48); + + let compressed = zstd::encode_all(&data[..], 1).unwrap(); + let mut decoder = StreamingDecoder::new(compressed.as_slice()).unwrap(); + let mut result = Vec::new(); + decoder.read_to_end(&mut result).unwrap(); + assert_eq!( + data, result, + "ffi→rust large block roundtrip failed at size={size}" + ); + } + + // Multi-block: 512KB + let data = generate_huffman_friendly(500, 512 * 1024, 48); + let compressed = zstd::encode_all(&data[..], 1).unwrap(); + let mut decoder = StreamingDecoder::new(compressed.as_slice()).unwrap(); + let mut result = Vec::new(); + decoder.read_to_end(&mut result).unwrap(); + assert_eq!(data, result, "ffi→rust multi-block roundtrip failed"); +} From 0dfc7cc204b1ef1d07da62c114fdc5717e7a9973 Mon Sep 17 00:00:00 2001 From: Dmitry Prudnikov Date: Wed, 25 Mar 2026 20:06:59 +0200 Subject: [PATCH 2/6] docs: clarify reachable size formats in encoder and test docs - Remove redundant debug_assert (unreachable! already guards the invariant) - Clarify RFC comment: only formats 0b10/0b11 are reachable in encoder - Fix test docs: roundtrip tests verify correctness, not specific format selection - Rename test to roundtrip_large_literals (accurate scope) --- zstd/src/encoding/blocks/compressed.rs | 21 +++++++-------------- zstd/src/tests/roundtrip_integrity.rs | 24 +++++++++++------------- zstd/tests/cross_validation.rs | 7 ++----- 3 files changed, 20 insertions(+), 32 deletions(-) diff --git a/zstd/src/encoding/blocks/compressed.rs b/zstd/src/encoding/blocks/compressed.rs index 9b411b74..d3838c17 100644 --- a/zstd/src/encoding/blocks/compressed.rs +++ b/zstd/src/encoding/blocks/compressed.rs @@ -316,15 +316,6 @@ fn compress_literals( last_table: Option<&huff0_encoder::HuffmanTable>, writer: &mut BitWriter<&mut Vec>, ) -> Option { - // The 18-bit size format supports up to 262143 bytes, which covers MAX_BLOCK_SIZE (128KB). - // Literals within a single block cannot exceed the block's content size. - debug_assert!( - literals.len() <= crate::common::MAX_BLOCK_SIZE as usize, - "literals size {} exceeds MAX_BLOCK_SIZE ({})", - literals.len(), - crate::common::MAX_BLOCK_SIZE, - ); - let reset_idx = writer.index(); let new_encoder_table = huff0_encoder::HuffmanTable::build_from_data(literals); @@ -350,11 +341,13 @@ fn compress_literals( writer.write_bits(3u8, 2); // treeless compressed literals type } - // RFC 8878 §3.1.1.3.1.1: Size_Format determines header size and stream count. - // - 0b00: single stream, 10-bit sizes (regenerated ≤ 1023) - // - 0b01: 4 streams, 10-bit sizes (regenerated ≤ 1023) - // - 0b10: 4 streams, 14-bit sizes (regenerated ≤ 16383) - // - 0b11: 4 streams, 18-bit sizes (regenerated ≤ 262143) + // RFC 8878 §3.1.1.3.1.1 Size_Format (spec limits): + // 0b00: single stream, 10-bit (≤ 1023) | 0b01: 4 streams, 10-bit (≤ 1023) + // 0b10: 4 streams, 14-bit (≤ 16383) | 0b11: 4 streams, 18-bit (≤ 262143) + // + // The encoder currently only calls this function for literals > 1024 bytes + // (smaller literals use raw_literals), so only formats 0b10 and 0b11 are + // reachable in practice. The 0b00/0b01 arms are kept for completeness. // MAX_BLOCK_SIZE (128KB = 131072) fits within the 18-bit format. let (size_format, size_bits) = match literals.len() { 0..6 => (0b00u8, 10), diff --git a/zstd/src/tests/roundtrip_integrity.rs b/zstd/src/tests/roundtrip_integrity.rs index 8c9f7cdc..928f3d01 100644 --- a/zstd/src/tests/roundtrip_integrity.rs +++ b/zstd/src/tests/roundtrip_integrity.rs @@ -145,34 +145,32 @@ fn roundtrip_edge_cases() { assert_eq!(roundtrip_simple(&rle), rle); } -/// Tests that exercise all 4 compressed literals size format paths: -/// - Size format 0b00: single stream, 10-bit (regenerated_size < 6) -/// - Size format 0b01: 4 streams, 10-bit (6 ≤ regenerated_size < 1024) -/// - Size format 0b10: 4 streams, 14-bit (1024 ≤ regenerated_size < 16384) -/// - Size format 0b11: 4 streams, 18-bit (16384 ≤ regenerated_size < 262144) +/// Roundtrip tests with large inputs that produce large literal sections. +/// +/// The encoder uses `compress_literals` (Huffman) for literals > 1024 bytes, +/// so these inputs exercise the 14-bit (0b10) and 18-bit (0b11) size formats. +/// The exact literals size depends on how many matches the encoder finds, +/// so we verify roundtrip correctness rather than specific format selection. #[test] -fn roundtrip_literals_size_format_boundaries() { - // Size format 0b10 boundary: data just over 1KB forces 14-bit path. - // Uses limited alphabet so Huffman encoding is used (not raw fallback). +fn roundtrip_large_literals() { + // ~1KB input — just above the raw→Huffman threshold. let data_1025 = generate_huffman_friendly(42, 1025, 16); assert_eq!(roundtrip_simple(&data_1025), data_1025); assert_eq!(roundtrip_streaming(&data_1025), data_1025); - // Size format 0b10 upper boundary: just under 16KB. + // ~16KB input — near the 14-bit/18-bit boundary. let data_16383 = generate_huffman_friendly(43, 16383, 32); assert_eq!(roundtrip_simple(&data_16383), data_16383); - // Size format 0b11 boundary: data at exactly 16384 forces 18-bit path. let data_16384 = generate_huffman_friendly(44, 16384, 32); assert_eq!(roundtrip_simple(&data_16384), data_16384); assert_eq!(roundtrip_streaming(&data_16384), data_16384); - // Size format 0b11: data at 64KB (well within 18-bit range). + // 64KB input — well within the 18-bit range. let data_64k = generate_huffman_friendly(45, 65536, 64); assert_eq!(roundtrip_simple(&data_64k), data_64k); - // Size format 0b11: data at MAX_BLOCK_SIZE (128KB) — maximum valid literal size. - // This is the largest possible literal section in a single block. + // 128KB input — MAX_BLOCK_SIZE, the largest single block. let data_128k = generate_huffman_friendly(46, 128 * 1024, 64); assert_eq!(roundtrip_simple(&data_128k), data_128k); assert_eq!(roundtrip_streaming(&data_128k), data_128k); diff --git a/zstd/tests/cross_validation.rs b/zstd/tests/cross_validation.rs index 48447499..835e7bd7 100644 --- a/zstd/tests/cross_validation.rs +++ b/zstd/tests/cross_validation.rs @@ -66,15 +66,12 @@ fn cross_ffi_compress_rust_decompress_1000() { } } -/// Cross-validate large blocks (>16KB up to 128KB) that exercise the 18-bit -/// size format (0b11) with 4-stream Huffman encoding. +/// Cross-validate large inputs (1KB–512KB) that produce large literal sections, +/// verifying C zstd can decompress what our encoder produces. #[test] fn cross_rust_compress_ffi_decompress_large_blocks() { - // Sizes targeting each compressed literals size format boundary: - // 1025 (14-bit), 16384 (18-bit), 65536 (18-bit), 128*1024 (18-bit max) let sizes = [1025, 16384, 65536, 128 * 1024]; for (i, &size) in sizes.iter().enumerate() { - // Huffman-friendly data ensures compressed path is taken (not raw fallback) let data = generate_huffman_friendly(i as u64 + 200, size, 48); let compressed = compress_to_vec(&data[..], CompressionLevel::Fastest); From efe30ae362b408219479f3542326fa5de8bc1b82 Mon Sep 17 00:00:00 2001 From: Dmitry Prudnikov Date: Wed, 25 Mar 2026 20:19:11 +0200 Subject: [PATCH 3/6] fix(encoding): replace runtime unreachable with compile-time const assert - Use `const { assert!(MAX_BLOCK_SIZE <= 262143) }` for compile-time safety - Replace `_ => unreachable!()` with `_ => (0b11, 18)` wildcard arm - Add assert for alphabet_size > 0 in test helpers Eliminates uncoverable dead code that caused 0% patch coverage. --- zstd/src/encoding/blocks/compressed.rs | 12 ++++-------- zstd/src/tests/roundtrip_integrity.rs | 1 + zstd/tests/cross_validation.rs | 1 + 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/zstd/src/encoding/blocks/compressed.rs b/zstd/src/encoding/blocks/compressed.rs index d3838c17..0337d5d8 100644 --- a/zstd/src/encoding/blocks/compressed.rs +++ b/zstd/src/encoding/blocks/compressed.rs @@ -348,18 +348,14 @@ fn compress_literals( // The encoder currently only calls this function for literals > 1024 bytes // (smaller literals use raw_literals), so only formats 0b10 and 0b11 are // reachable in practice. The 0b00/0b01 arms are kept for completeness. - // MAX_BLOCK_SIZE (128KB = 131072) fits within the 18-bit format. + // + // Compile-time guarantee that MAX_BLOCK_SIZE fits in the 18-bit format. + const { assert!(crate::common::MAX_BLOCK_SIZE <= 262143) }; let (size_format, size_bits) = match literals.len() { 0..6 => (0b00u8, 10), 6..1024 => (0b01, 10), 1024..16384 => (0b10, 14), - 16384..262144 => (0b11, 18), - _ => unreachable!( - "literals size {} exceeds 18-bit maximum (262143); \ - blocks are limited to MAX_BLOCK_SIZE ({}) which always fits", - literals.len(), - crate::common::MAX_BLOCK_SIZE, - ), + _ => (0b11, 18), }; writer.write_bits(size_format, 2); diff --git a/zstd/src/tests/roundtrip_integrity.rs b/zstd/src/tests/roundtrip_integrity.rs index 928f3d01..6aeb7350 100644 --- a/zstd/src/tests/roundtrip_integrity.rs +++ b/zstd/src/tests/roundtrip_integrity.rs @@ -63,6 +63,7 @@ fn roundtrip_streaming(data: &[u8]) -> Vec { /// Generate data with limited alphabet for better Huffman compressibility /// but enough variety to avoid RLE path. fn generate_huffman_friendly(seed: u64, len: usize, alphabet_size: u8) -> Vec { + assert!(alphabet_size > 0, "alphabet_size must be non-zero"); let mut state = seed; let mut data = Vec::with_capacity(len); for _ in 0..len { diff --git a/zstd/tests/cross_validation.rs b/zstd/tests/cross_validation.rs index 835e7bd7..83b6e783 100644 --- a/zstd/tests/cross_validation.rs +++ b/zstd/tests/cross_validation.rs @@ -23,6 +23,7 @@ fn generate_data(seed: u64, len: usize) -> Vec { /// Generate data with limited alphabet for Huffman-friendly compression. fn generate_huffman_friendly(seed: u64, len: usize, alphabet_size: u8) -> Vec { + assert!(alphabet_size > 0, "alphabet_size must be non-zero"); let mut state = seed; let mut data = Vec::with_capacity(len); for _ in 0..len { From b82f8092b3d34a0f86ecef8713e56b37eb85526c Mon Sep 17 00:00:00 2001 From: Dmitry Prudnikov Date: Wed, 25 Mar 2026 20:30:01 +0200 Subject: [PATCH 4/6] fix(encoding): add runtime debug_assert for literals size invariant Guard against custom Matcher implementations that might produce literals exceeding the 18-bit size format limit (262143 bytes). --- zstd/src/encoding/blocks/compressed.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/zstd/src/encoding/blocks/compressed.rs b/zstd/src/encoding/blocks/compressed.rs index 0337d5d8..a4bab969 100644 --- a/zstd/src/encoding/blocks/compressed.rs +++ b/zstd/src/encoding/blocks/compressed.rs @@ -349,8 +349,10 @@ fn compress_literals( // (smaller literals use raw_literals), so only formats 0b10 and 0b11 are // reachable in practice. The 0b00/0b01 arms are kept for completeness. // - // Compile-time guarantee that MAX_BLOCK_SIZE fits in the 18-bit format. + // Compile-time: MAX_BLOCK_SIZE fits in the 18-bit format. + // Runtime (debug): guard against custom Matchers exceeding the limit. const { assert!(crate::common::MAX_BLOCK_SIZE <= 262143) }; + debug_assert!(literals.len() < 262144); let (size_format, size_bits) = match literals.len() { 0..6 => (0b00u8, 10), 6..1024 => (0b01, 10), From dd73f355259f2dbf507d2ea96f351495d2e46d45 Mon Sep 17 00:00:00 2001 From: Dmitry Prudnikov Date: Wed, 25 Mar 2026 20:41:18 +0200 Subject: [PATCH 5/6] fix(encoding): use release-mode assert for literals size guard MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Upgrade debug_assert! to assert! — truncated 18-bit writes in release builds would produce corrupt streams silently. The assert fires in all build profiles, preventing invalid output from custom Matcher impls. --- zstd/src/encoding/blocks/compressed.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zstd/src/encoding/blocks/compressed.rs b/zstd/src/encoding/blocks/compressed.rs index a4bab969..bf4c8c2e 100644 --- a/zstd/src/encoding/blocks/compressed.rs +++ b/zstd/src/encoding/blocks/compressed.rs @@ -350,9 +350,9 @@ fn compress_literals( // reachable in practice. The 0b00/0b01 arms are kept for completeness. // // Compile-time: MAX_BLOCK_SIZE fits in the 18-bit format. - // Runtime (debug): guard against custom Matchers exceeding the limit. + // Runtime: hard guard — truncated 18-bit writes produce corrupt streams. const { assert!(crate::common::MAX_BLOCK_SIZE <= 262143) }; - debug_assert!(literals.len() < 262144); + assert!(literals.len() < 262144); let (size_format, size_bits) = match literals.len() { 0..6 => (0b00u8, 10), 6..1024 => (0b01, 10), From 0f7cf20ffd48b849ea84eec63ec4ab81296c92a1 Mon Sep 17 00:00:00 2001 From: Dmitry Prudnikov Date: Wed, 25 Mar 2026 20:49:49 +0200 Subject: [PATCH 6/6] refactor(encoding): move const assert to module scope, add assert message - Move compile-time MAX_BLOCK_SIZE check to idiomatic `const _: () = assert!(...)` at module scope instead of inline `const { ... }` expression - Add static panic message to runtime assert (format args omitted to avoid uncoverable dead code in coverage instrumentation) --- zstd/src/encoding/blocks/compressed.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/zstd/src/encoding/blocks/compressed.rs b/zstd/src/encoding/blocks/compressed.rs index bf4c8c2e..1d0fbac2 100644 --- a/zstd/src/encoding/blocks/compressed.rs +++ b/zstd/src/encoding/blocks/compressed.rs @@ -8,6 +8,9 @@ use crate::{ huff0::huff0_encoder, }; +/// Compile-time guarantee that MAX_BLOCK_SIZE fits in the 18-bit size format. +const _: () = assert!(crate::common::MAX_BLOCK_SIZE <= 262_143); + /// A block of [`crate::common::BlockType::Compressed`] pub fn compress_block(state: &mut CompressState, output: &mut Vec) { let mut literals_vec = Vec::new(); @@ -349,10 +352,12 @@ fn compress_literals( // (smaller literals use raw_literals), so only formats 0b10 and 0b11 are // reachable in practice. The 0b00/0b01 arms are kept for completeness. // - // Compile-time: MAX_BLOCK_SIZE fits in the 18-bit format. // Runtime: hard guard — truncated 18-bit writes produce corrupt streams. - const { assert!(crate::common::MAX_BLOCK_SIZE <= 262143) }; - assert!(literals.len() < 262144); + // Note: format args omitted intentionally to avoid uncoverable dead code in coverage. + assert!( + literals.len() <= 262_143, + "literals exceed RFC 8878 18-bit size limit (262143)" + ); let (size_format, size_bits) = match literals.len() { 0..6 => (0b00u8, 10), 6..1024 => (0b01, 10),