Decompress LZ4 block-compressed bytes back to raw data.
(data: &[u8])
| 75 | |
| 76 | /// Decompress LZ4 block-compressed bytes back to raw data. |
| 77 | pub fn decode(data: &[u8]) -> Result<Vec<u8>, CodecError> { |
| 78 | let header = read_header(data)?; |
| 79 | |
| 80 | if header.block_count == 0 { |
| 81 | return Ok(Vec::new()); |
| 82 | } |
| 83 | |
| 84 | let mut result = Vec::with_capacity(header.uncompressed_size); |
| 85 | let mut block_offset = header.data_offset; |
| 86 | |
| 87 | for i in 0..header.block_count { |
| 88 | let compressed_len = header.block_lengths[i]; |
| 89 | let block_end = block_offset + compressed_len; |
| 90 | |
| 91 | if block_end > data.len() { |
| 92 | return Err(CodecError::Truncated { |
| 93 | expected: block_end, |
| 94 | actual: data.len(), |
| 95 | }); |
| 96 | } |
| 97 | |
| 98 | let block_data = &data[block_offset..block_end]; |
| 99 | let decompressed = lz4_flex::decompress_size_prepended(block_data).map_err(|e| { |
| 100 | CodecError::DecompressFailed { |
| 101 | detail: format!("LZ4 block {i}: {e}"), |
| 102 | } |
| 103 | })?; |
| 104 | |
| 105 | result.extend_from_slice(&decompressed); |
| 106 | block_offset = block_end; |
| 107 | } |
| 108 | |
| 109 | if result.len() != header.uncompressed_size { |
| 110 | return Err(CodecError::Corrupt { |
| 111 | detail: format!( |
| 112 | "uncompressed size mismatch: header says {}, got {}", |
| 113 | header.uncompressed_size, |
| 114 | result.len() |
| 115 | ), |
| 116 | }); |
| 117 | } |
| 118 | |
| 119 | Ok(result) |
| 120 | } |
| 121 | |
| 122 | /// Decompress a single block by index (for random access). |
| 123 | /// |