Skip to main content

oxirs_tdb/compression/
run_length.rs

1//! Run-Length Encoding implementation for TDB storage
2
3use crate::compression::{
4    AdvancedCompressionType, CompressedData, CompressionAlgorithm, CompressionMetadata,
5    MAX_BLOCK_COUNT, MAX_COMPRESSION_INPUT_SIZE,
6};
7use anyhow::{anyhow, Result};
8use std::collections::HashMap;
9use std::time::Instant;
10
11/// Run-Length Encoding implementation
12pub struct RunLengthEncoder;
13
14impl RunLengthEncoder {
15    /// Encode data using run-length encoding
16    pub fn encode(data: &[u8]) -> Result<Vec<u8>> {
17        // Production hardening: Input validation
18        if data.is_empty() {
19            return Ok(Vec::new());
20        }
21
22        if data.len() > MAX_COMPRESSION_INPUT_SIZE {
23            return Err(anyhow!(
24                "Input data too large for compression: {} bytes (max: {})",
25                data.len(),
26                MAX_COMPRESSION_INPUT_SIZE
27            ));
28        }
29
30        let mut encoded = Vec::new();
31        let mut current_byte = data[0];
32        let mut count = 1u32;
33
34        for &byte in &data[1..] {
35            if byte == current_byte && count < u32::MAX {
36                count += 1;
37            } else {
38                // Encode current run
39                encoded.extend_from_slice(&count.to_le_bytes());
40                encoded.push(current_byte);
41                current_byte = byte;
42                count = 1;
43            }
44        }
45
46        // Encode final run
47        encoded.extend_from_slice(&count.to_le_bytes());
48        encoded.push(current_byte);
49
50        Ok(encoded)
51    }
52
53    /// Decode run-length encoded data
54    pub fn decode(encoded: &[u8]) -> Result<Vec<u8>> {
55        // Production hardening: Input validation
56        if encoded.is_empty() {
57            return Ok(Vec::new());
58        }
59
60        if encoded.len() % 5 != 0 {
61            return Err(anyhow!(
62                "Invalid run-length encoded data length: {} bytes",
63                encoded.len()
64            ));
65        }
66
67        if encoded.len() > MAX_COMPRESSION_INPUT_SIZE {
68            return Err(anyhow!(
69                "Encoded data too large for decompression: {} bytes (max: {})",
70                encoded.len(),
71                MAX_COMPRESSION_INPUT_SIZE
72            ));
73        }
74
75        let run_count = encoded.len() / 5;
76        if run_count > MAX_BLOCK_COUNT {
77            return Err(anyhow!(
78                "Too many runs in encoded data: {} (max: {})",
79                run_count,
80                MAX_BLOCK_COUNT
81            ));
82        }
83
84        let mut decoded = Vec::new();
85        let mut total_output_size = 0u64;
86
87        for chunk in encoded.chunks_exact(5) {
88            let count = u32::from_le_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]);
89            let byte_value = chunk[4];
90
91            // Production hardening: Prevent decompression bombs
92            total_output_size += count as u64;
93            if total_output_size > MAX_COMPRESSION_INPUT_SIZE as u64 {
94                return Err(anyhow!(
95                    "Decompressed data would exceed size limit: {} bytes (max: {})",
96                    total_output_size,
97                    MAX_COMPRESSION_INPUT_SIZE
98                ));
99            }
100
101            // Production hardening: Reasonable run length check
102            if count > 10_000_000 {
103                return Err(anyhow!(
104                    "Suspicious run length detected: {} (possible decompression bomb)",
105                    count
106                ));
107            }
108
109            for _ in 0..count {
110                decoded.push(byte_value);
111            }
112        }
113
114        Ok(decoded)
115    }
116
117    /// Encode a u64 sequence using run-length encoding.
118    ///
119    /// Consecutive equal values are grouped into runs of `(value: u64 LE, count: u32 LE)` = 12 bytes.
120    pub fn encode_u64(values: &[u64]) -> Vec<u8> {
121        if values.is_empty() {
122            return Vec::new();
123        }
124
125        let mut out = Vec::new();
126        let mut current = values[0];
127        let mut count = 1u32;
128
129        for &v in &values[1..] {
130            if v == current && count < u32::MAX {
131                count += 1;
132            } else {
133                out.extend_from_slice(&current.to_le_bytes());
134                out.extend_from_slice(&count.to_le_bytes());
135                current = v;
136                count = 1;
137            }
138        }
139        // Final run
140        out.extend_from_slice(&current.to_le_bytes());
141        out.extend_from_slice(&count.to_le_bytes());
142
143        out
144    }
145
146    /// Decode a u64 run-length encoded byte sequence produced by `encode_u64`.
147    pub fn decode_u64(data: &[u8]) -> Result<Vec<u64>> {
148        if data.is_empty() {
149            return Ok(Vec::new());
150        }
151
152        // Each run is 8 (value) + 4 (count) = 12 bytes
153        if data.len() % 12 != 0 {
154            return Err(anyhow!(
155                "Invalid u64 RLE data: length {} is not a multiple of 12",
156                data.len()
157            ));
158        }
159
160        let run_count = data.len() / 12;
161        if run_count > MAX_BLOCK_COUNT {
162            return Err(anyhow!(
163                "Too many u64 RLE runs: {} (max: {})",
164                run_count,
165                MAX_BLOCK_COUNT
166            ));
167        }
168
169        let mut values = Vec::new();
170        let mut total: u64 = 0;
171
172        for chunk in data.chunks_exact(12) {
173            let value = u64::from_le_bytes(
174                chunk[..8]
175                    .try_into()
176                    .map_err(|_| anyhow!("u64 RLE slice error"))?,
177            );
178            let count = u32::from_le_bytes(
179                chunk[8..12]
180                    .try_into()
181                    .map_err(|_| anyhow!("u64 RLE count slice error"))?,
182            );
183
184            // Decompression bomb guard
185            total += count as u64;
186            if total > 100_000_000 {
187                return Err(anyhow!(
188                    "u64 RLE output exceeds 100M elements (possible decompression bomb)"
189                ));
190            }
191
192            for _ in 0..count {
193                values.push(value);
194            }
195        }
196
197        Ok(values)
198    }
199
200    /// Estimate compression ratio for given data
201    pub fn estimate_compression_ratio(data: &[u8]) -> f64 {
202        if data.is_empty() {
203            return 1.0;
204        }
205
206        let mut runs = 0;
207        let mut current_byte = data[0];
208        for &byte in &data[1..] {
209            if byte != current_byte {
210                runs += 1;
211                current_byte = byte;
212            }
213        }
214        runs += 1; // Count final run
215
216        let compressed_size = runs * 5; // 4 bytes count + 1 byte value
217        compressed_size as f64 / data.len() as f64
218    }
219}
220
221impl CompressionAlgorithm for RunLengthEncoder {
222    fn compress(&self, data: &[u8]) -> Result<CompressedData> {
223        let start = Instant::now();
224        let compressed_bytes = Self::encode(data)?;
225        let compression_time = start.elapsed();
226
227        let mut metadata_map = HashMap::new();
228        metadata_map.insert("runs".to_string(), (compressed_bytes.len() / 5).to_string());
229
230        let metadata = CompressionMetadata {
231            algorithm: AdvancedCompressionType::RunLength,
232            original_size: data.len() as u64,
233            compressed_size: compressed_bytes.len() as u64,
234            compression_time_us: compression_time.as_micros() as u64,
235            metadata: metadata_map,
236        };
237
238        Ok(CompressedData {
239            data: compressed_bytes,
240            metadata,
241        })
242    }
243
244    fn decompress(&self, compressed: &CompressedData) -> Result<Vec<u8>> {
245        if compressed.metadata.algorithm != AdvancedCompressionType::RunLength {
246            return Err(anyhow!(
247                "Invalid compression algorithm: expected RunLength, got {}",
248                compressed.metadata.algorithm
249            ));
250        }
251
252        Self::decode(&compressed.data)
253    }
254
255    fn algorithm_type(&self) -> AdvancedCompressionType {
256        AdvancedCompressionType::RunLength
257    }
258}
259
260#[cfg(test)]
261mod tests {
262    use super::*;
263
264    #[test]
265    fn test_run_length_encoding() {
266        let data = vec![1, 1, 1, 2, 2, 3, 3, 3, 3];
267        let encoded = RunLengthEncoder::encode(&data).unwrap();
268        let decoded = RunLengthEncoder::decode(&encoded).unwrap();
269        assert_eq!(data, decoded);
270    }
271
272    #[test]
273    fn test_empty_data() {
274        let data = vec![];
275        let encoded = RunLengthEncoder::encode(&data).unwrap();
276        assert!(encoded.is_empty());
277    }
278
279    #[test]
280    fn test_single_byte() {
281        let data = vec![42];
282        let encoded = RunLengthEncoder::encode(&data).unwrap();
283        let decoded = RunLengthEncoder::decode(&encoded).unwrap();
284        assert_eq!(data, decoded);
285    }
286
287    #[test]
288    fn test_compression_algorithm_trait() {
289        let encoder = RunLengthEncoder;
290        let data = vec![1, 1, 1, 2, 2, 3, 3, 3, 3];
291
292        let compressed = encoder.compress(&data).unwrap();
293        assert_eq!(
294            compressed.metadata.algorithm,
295            AdvancedCompressionType::RunLength
296        );
297        assert_eq!(compressed.metadata.original_size, data.len() as u64);
298
299        let decompressed = encoder.decompress(&compressed).unwrap();
300        assert_eq!(data, decompressed);
301    }
302
303    #[test]
304    fn test_estimate_compression_ratio() {
305        let highly_repetitive = vec![1; 1000];
306        let ratio = RunLengthEncoder::estimate_compression_ratio(&highly_repetitive);
307        assert!(ratio < 0.1); // Should compress very well
308
309        let random_data = (0..100).collect::<Vec<u8>>();
310        let ratio = RunLengthEncoder::estimate_compression_ratio(&random_data);
311        assert!(ratio > 1.0); // Should expand
312    }
313
314    // ── u64 RLE ──────────────────────────────────────────────────────────────
315
316    #[test]
317    fn test_encode_u64_empty() {
318        let encoded = RunLengthEncoder::encode_u64(&[]);
319        assert!(encoded.is_empty());
320    }
321
322    #[test]
323    fn test_decode_u64_empty() {
324        let decoded = RunLengthEncoder::decode_u64(&[]).unwrap();
325        assert!(decoded.is_empty());
326    }
327
328    #[test]
329    fn test_encode_u64_single_value() {
330        let values = vec![42u64];
331        let encoded = RunLengthEncoder::encode_u64(&values);
332        let decoded = RunLengthEncoder::decode_u64(&encoded).unwrap();
333        assert_eq!(decoded, values);
334    }
335
336    #[test]
337    fn test_encode_u64_all_same() {
338        let values = vec![7u64; 100];
339        let encoded = RunLengthEncoder::encode_u64(&values);
340        // Should compress to a single run: 12 bytes
341        assert_eq!(encoded.len(), 12);
342        let decoded = RunLengthEncoder::decode_u64(&encoded).unwrap();
343        assert_eq!(decoded, values);
344    }
345
346    #[test]
347    fn test_encode_u64_all_different() {
348        let values: Vec<u64> = (0..50).collect();
349        let encoded = RunLengthEncoder::encode_u64(&values);
350        // Each value has run length 1: 50 × 12 bytes
351        assert_eq!(encoded.len(), 50 * 12);
352        let decoded = RunLengthEncoder::decode_u64(&encoded).unwrap();
353        assert_eq!(decoded, values);
354    }
355
356    #[test]
357    fn test_encode_u64_mixed_runs() {
358        let values = vec![1u64, 1, 1, 2, 2, 3];
359        let encoded = RunLengthEncoder::encode_u64(&values);
360        let decoded = RunLengthEncoder::decode_u64(&encoded).unwrap();
361        assert_eq!(decoded, values);
362    }
363
364    #[test]
365    fn test_encode_u64_max_value() {
366        let values = vec![u64::MAX, u64::MAX, u64::MAX];
367        let encoded = RunLengthEncoder::encode_u64(&values);
368        let decoded = RunLengthEncoder::decode_u64(&encoded).unwrap();
369        assert_eq!(decoded, values);
370    }
371
372    #[test]
373    fn test_encode_u64_large_roundtrip() {
374        let mut values = Vec::new();
375        for i in 0..100u64 {
376            for _ in 0..(i % 10 + 1) {
377                values.push(i * 1000);
378            }
379        }
380        let encoded = RunLengthEncoder::encode_u64(&values);
381        let decoded = RunLengthEncoder::decode_u64(&encoded).unwrap();
382        assert_eq!(decoded, values);
383    }
384
385    #[test]
386    fn test_decode_u64_invalid_length() {
387        // 11 bytes is not a multiple of 12
388        let bad_data = vec![0u8; 11];
389        let result = RunLengthEncoder::decode_u64(&bad_data);
390        assert!(result.is_err());
391    }
392}