use crate::compression::{
AdvancedCompressionType, CompressedData, CompressionAlgorithm, CompressionMetadata,
MAX_BLOCK_COUNT, MAX_COMPRESSION_INPUT_SIZE,
};
use anyhow::{anyhow, Result};
use std::collections::HashMap;
use std::time::Instant;
pub struct RunLengthEncoder;
impl RunLengthEncoder {
pub fn encode(data: &[u8]) -> Result<Vec<u8>> {
if data.is_empty() {
return Ok(Vec::new());
}
if data.len() > MAX_COMPRESSION_INPUT_SIZE {
return Err(anyhow!(
"Input data too large for compression: {} bytes (max: {})",
data.len(),
MAX_COMPRESSION_INPUT_SIZE
));
}
let mut encoded = Vec::new();
let mut current_byte = data[0];
let mut count = 1u32;
for &byte in &data[1..] {
if byte == current_byte && count < u32::MAX {
count += 1;
} else {
encoded.extend_from_slice(&count.to_le_bytes());
encoded.push(current_byte);
current_byte = byte;
count = 1;
}
}
encoded.extend_from_slice(&count.to_le_bytes());
encoded.push(current_byte);
Ok(encoded)
}
pub fn decode(encoded: &[u8]) -> Result<Vec<u8>> {
if encoded.is_empty() {
return Ok(Vec::new());
}
if encoded.len() % 5 != 0 {
return Err(anyhow!(
"Invalid run-length encoded data length: {} bytes",
encoded.len()
));
}
if encoded.len() > MAX_COMPRESSION_INPUT_SIZE {
return Err(anyhow!(
"Encoded data too large for decompression: {} bytes (max: {})",
encoded.len(),
MAX_COMPRESSION_INPUT_SIZE
));
}
let run_count = encoded.len() / 5;
if run_count > MAX_BLOCK_COUNT {
return Err(anyhow!(
"Too many runs in encoded data: {} (max: {})",
run_count,
MAX_BLOCK_COUNT
));
}
let mut decoded = Vec::new();
let mut total_output_size = 0u64;
for chunk in encoded.chunks_exact(5) {
let count = u32::from_le_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]);
let byte_value = chunk[4];
total_output_size += count as u64;
if total_output_size > MAX_COMPRESSION_INPUT_SIZE as u64 {
return Err(anyhow!(
"Decompressed data would exceed size limit: {} bytes (max: {})",
total_output_size,
MAX_COMPRESSION_INPUT_SIZE
));
}
if count > 10_000_000 {
return Err(anyhow!(
"Suspicious run length detected: {} (possible decompression bomb)",
count
));
}
for _ in 0..count {
decoded.push(byte_value);
}
}
Ok(decoded)
}
pub fn encode_u64(values: &[u64]) -> Vec<u8> {
if values.is_empty() {
return Vec::new();
}
let mut out = Vec::new();
let mut current = values[0];
let mut count = 1u32;
for &v in &values[1..] {
if v == current && count < u32::MAX {
count += 1;
} else {
out.extend_from_slice(¤t.to_le_bytes());
out.extend_from_slice(&count.to_le_bytes());
current = v;
count = 1;
}
}
out.extend_from_slice(¤t.to_le_bytes());
out.extend_from_slice(&count.to_le_bytes());
out
}
pub fn decode_u64(data: &[u8]) -> Result<Vec<u64>> {
if data.is_empty() {
return Ok(Vec::new());
}
if data.len() % 12 != 0 {
return Err(anyhow!(
"Invalid u64 RLE data: length {} is not a multiple of 12",
data.len()
));
}
let run_count = data.len() / 12;
if run_count > MAX_BLOCK_COUNT {
return Err(anyhow!(
"Too many u64 RLE runs: {} (max: {})",
run_count,
MAX_BLOCK_COUNT
));
}
let mut values = Vec::new();
let mut total: u64 = 0;
for chunk in data.chunks_exact(12) {
let value = u64::from_le_bytes(
chunk[..8]
.try_into()
.map_err(|_| anyhow!("u64 RLE slice error"))?,
);
let count = u32::from_le_bytes(
chunk[8..12]
.try_into()
.map_err(|_| anyhow!("u64 RLE count slice error"))?,
);
total += count as u64;
if total > 100_000_000 {
return Err(anyhow!(
"u64 RLE output exceeds 100M elements (possible decompression bomb)"
));
}
for _ in 0..count {
values.push(value);
}
}
Ok(values)
}
pub fn estimate_compression_ratio(data: &[u8]) -> f64 {
if data.is_empty() {
return 1.0;
}
let mut runs = 0;
let mut current_byte = data[0];
for &byte in &data[1..] {
if byte != current_byte {
runs += 1;
current_byte = byte;
}
}
runs += 1;
let compressed_size = runs * 5; compressed_size as f64 / data.len() as f64
}
}
impl CompressionAlgorithm for RunLengthEncoder {
fn compress(&self, data: &[u8]) -> Result<CompressedData> {
let start = Instant::now();
let compressed_bytes = Self::encode(data)?;
let compression_time = start.elapsed();
let mut metadata_map = HashMap::new();
metadata_map.insert("runs".to_string(), (compressed_bytes.len() / 5).to_string());
let metadata = CompressionMetadata {
algorithm: AdvancedCompressionType::RunLength,
original_size: data.len() as u64,
compressed_size: compressed_bytes.len() as u64,
compression_time_us: compression_time.as_micros() as u64,
metadata: metadata_map,
};
Ok(CompressedData {
data: compressed_bytes,
metadata,
})
}
fn decompress(&self, compressed: &CompressedData) -> Result<Vec<u8>> {
if compressed.metadata.algorithm != AdvancedCompressionType::RunLength {
return Err(anyhow!(
"Invalid compression algorithm: expected RunLength, got {}",
compressed.metadata.algorithm
));
}
Self::decode(&compressed.data)
}
fn algorithm_type(&self) -> AdvancedCompressionType {
AdvancedCompressionType::RunLength
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_run_length_encoding() {
let data = vec![1, 1, 1, 2, 2, 3, 3, 3, 3];
let encoded = RunLengthEncoder::encode(&data).unwrap();
let decoded = RunLengthEncoder::decode(&encoded).unwrap();
assert_eq!(data, decoded);
}
#[test]
fn test_empty_data() {
let data = vec![];
let encoded = RunLengthEncoder::encode(&data).unwrap();
assert!(encoded.is_empty());
}
#[test]
fn test_single_byte() {
let data = vec![42];
let encoded = RunLengthEncoder::encode(&data).unwrap();
let decoded = RunLengthEncoder::decode(&encoded).unwrap();
assert_eq!(data, decoded);
}
#[test]
fn test_compression_algorithm_trait() {
let encoder = RunLengthEncoder;
let data = vec![1, 1, 1, 2, 2, 3, 3, 3, 3];
let compressed = encoder.compress(&data).unwrap();
assert_eq!(
compressed.metadata.algorithm,
AdvancedCompressionType::RunLength
);
assert_eq!(compressed.metadata.original_size, data.len() as u64);
let decompressed = encoder.decompress(&compressed).unwrap();
assert_eq!(data, decompressed);
}
#[test]
fn test_estimate_compression_ratio() {
let highly_repetitive = vec![1; 1000];
let ratio = RunLengthEncoder::estimate_compression_ratio(&highly_repetitive);
assert!(ratio < 0.1);
let random_data = (0..100).collect::<Vec<u8>>();
let ratio = RunLengthEncoder::estimate_compression_ratio(&random_data);
assert!(ratio > 1.0); }
#[test]
fn test_encode_u64_empty() {
let encoded = RunLengthEncoder::encode_u64(&[]);
assert!(encoded.is_empty());
}
#[test]
fn test_decode_u64_empty() {
let decoded = RunLengthEncoder::decode_u64(&[]).unwrap();
assert!(decoded.is_empty());
}
#[test]
fn test_encode_u64_single_value() {
let values = vec![42u64];
let encoded = RunLengthEncoder::encode_u64(&values);
let decoded = RunLengthEncoder::decode_u64(&encoded).unwrap();
assert_eq!(decoded, values);
}
#[test]
fn test_encode_u64_all_same() {
let values = vec![7u64; 100];
let encoded = RunLengthEncoder::encode_u64(&values);
assert_eq!(encoded.len(), 12);
let decoded = RunLengthEncoder::decode_u64(&encoded).unwrap();
assert_eq!(decoded, values);
}
#[test]
fn test_encode_u64_all_different() {
let values: Vec<u64> = (0..50).collect();
let encoded = RunLengthEncoder::encode_u64(&values);
assert_eq!(encoded.len(), 50 * 12);
let decoded = RunLengthEncoder::decode_u64(&encoded).unwrap();
assert_eq!(decoded, values);
}
#[test]
fn test_encode_u64_mixed_runs() {
let values = vec![1u64, 1, 1, 2, 2, 3];
let encoded = RunLengthEncoder::encode_u64(&values);
let decoded = RunLengthEncoder::decode_u64(&encoded).unwrap();
assert_eq!(decoded, values);
}
#[test]
fn test_encode_u64_max_value() {
let values = vec![u64::MAX, u64::MAX, u64::MAX];
let encoded = RunLengthEncoder::encode_u64(&values);
let decoded = RunLengthEncoder::decode_u64(&encoded).unwrap();
assert_eq!(decoded, values);
}
#[test]
fn test_encode_u64_large_roundtrip() {
let mut values = Vec::new();
for i in 0..100u64 {
for _ in 0..(i % 10 + 1) {
values.push(i * 1000);
}
}
let encoded = RunLengthEncoder::encode_u64(&values);
let decoded = RunLengthEncoder::decode_u64(&encoded).unwrap();
assert_eq!(decoded, values);
}
#[test]
fn test_decode_u64_invalid_length() {
let bad_data = vec![0u8; 11];
let result = RunLengthEncoder::decode_u64(&bad_data);
assert!(result.is_err());
}
}