oxirs_tdb/compression/
frame_of_reference.rs1use super::{
7 AdvancedCompressionType, CompressedData, CompressionAlgorithm, CompressionMetadata,
8 MAX_COMPRESSION_INPUT_SIZE,
9};
10use anyhow::{anyhow, Result};
11use std::collections::HashMap;
12use std::time::Instant;
13
14#[derive(Debug, Clone)]
16pub struct FrameOfReferenceEncoder {
17 pub bits_per_delta: u8,
19}
20
21impl Default for FrameOfReferenceEncoder {
22 fn default() -> Self {
23 Self {
24 bits_per_delta: 8, }
26 }
27}
28
29impl FrameOfReferenceEncoder {
30 pub fn new(bits_per_delta: u8) -> Self {
32 Self {
33 bits_per_delta: bits_per_delta.min(32), }
35 }
36
37 pub fn compress_u32(&self, values: &[u32]) -> Result<CompressedData> {
39 if values.is_empty() {
40 return Ok(CompressedData {
41 data: vec![],
42 metadata: CompressionMetadata {
43 algorithm: AdvancedCompressionType::FrameOfReference,
44 original_size: 0,
45 compressed_size: 0,
46 compression_time_us: 0,
47 metadata: HashMap::new(),
48 },
49 });
50 }
51
52 let start_time = Instant::now();
53
54 let base = *values
56 .iter()
57 .min()
58 .expect("collection validated to be non-empty");
59
60 let deltas: Vec<u32> = values.iter().map(|&v| v - base).collect();
62
63 let max_delta = *deltas
65 .iter()
66 .max()
67 .expect("collection validated to be non-empty");
68 let required_bits = if max_delta == 0 {
69 1
70 } else {
71 32 - max_delta.leading_zeros() as u8
72 };
73
74 let bits_per_delta = required_bits.min(self.bits_per_delta);
76
77 let mut result = Vec::new();
79 result.extend_from_slice(&base.to_le_bytes());
80 result.push(bits_per_delta);
81 result.extend_from_slice(&(values.len() as u32).to_le_bytes());
82
83 if bits_per_delta > 0 {
85 let packed_deltas = self.pack_deltas(&deltas, bits_per_delta)?;
86 result.extend_from_slice(&packed_deltas);
87 }
88
89 let compression_time = start_time.elapsed().as_micros() as u64;
90
91 let mut metadata_map = HashMap::new();
92 metadata_map.insert("base".to_string(), base.to_string());
93 metadata_map.insert("bits_per_delta".to_string(), bits_per_delta.to_string());
94 metadata_map.insert("count".to_string(), values.len().to_string());
95
96 let compressed_size = result.len() as u64;
97 Ok(CompressedData {
98 data: result,
99 metadata: CompressionMetadata {
100 algorithm: AdvancedCompressionType::FrameOfReference,
101 original_size: (values.len() * 4) as u64,
102 compressed_size,
103 compression_time_us: compression_time,
104 metadata: metadata_map,
105 },
106 })
107 }
108
109 fn pack_deltas(&self, deltas: &[u32], bits_per_delta: u8) -> Result<Vec<u8>> {
111 if bits_per_delta == 0 {
112 return Ok(vec![]);
113 }
114
115 let mut result = Vec::new();
116 let mut current_byte = 0u8;
117 let mut bits_in_current_byte = 0u8;
118
119 for &delta in deltas {
120 let masked_delta = delta & ((1u32 << bits_per_delta) - 1);
122
123 let mut remaining_bits = bits_per_delta;
124 let mut value = masked_delta;
125
126 while remaining_bits > 0 {
127 let bits_to_write = remaining_bits.min(8 - bits_in_current_byte);
128 let shift = remaining_bits - bits_to_write;
129 let bits_value = ((value >> shift) & ((1u32 << bits_to_write) - 1)) as u8;
130
131 current_byte |= bits_value << (8 - bits_in_current_byte - bits_to_write);
132 bits_in_current_byte += bits_to_write;
133 remaining_bits -= bits_to_write;
134 value &= (1u32 << shift) - 1;
135
136 if bits_in_current_byte == 8 {
137 result.push(current_byte);
138 current_byte = 0;
139 bits_in_current_byte = 0;
140 }
141 }
142 }
143
144 if bits_in_current_byte > 0 {
146 result.push(current_byte);
147 }
148
149 Ok(result)
150 }
151
152 pub fn decompress_u32(&self, data: &[u8]) -> Result<Vec<u32>> {
154 if data.is_empty() {
155 return Ok(vec![]);
156 }
157
158 if data.len() < 9 {
159 return Err(anyhow!("Invalid FOR compressed data: too short"));
160 }
161
162 let base = u32::from_le_bytes([data[0], data[1], data[2], data[3]]);
164 let bits_per_delta = data[4];
165 let count = u32::from_le_bytes([data[5], data[6], data[7], data[8]]) as usize;
166
167 if count == 0 {
168 return Ok(vec![]);
169 }
170
171 let deltas = if bits_per_delta == 0 {
173 vec![0; count]
174 } else {
175 self.unpack_deltas(&data[9..], bits_per_delta, count)?
176 };
177
178 let values: Vec<u32> = deltas.iter().map(|&delta| base + delta).collect();
180
181 Ok(values)
182 }
183
184 fn unpack_deltas(&self, data: &[u8], bits_per_delta: u8, count: usize) -> Result<Vec<u32>> {
186 let mut result = Vec::with_capacity(count);
187 let mut byte_index = 0;
188 let mut bit_index = 0u8;
189
190 for _ in 0..count {
191 let mut value = 0u32;
192 let mut remaining_bits = bits_per_delta;
193
194 while remaining_bits > 0 && byte_index < data.len() {
195 let bits_available = 8 - bit_index;
196 let bits_to_read = remaining_bits.min(bits_available);
197
198 let byte_value = data[byte_index];
199 let shift = bits_available - bits_to_read;
200 let mask = if bits_to_read >= 8 {
201 0xFFu8
202 } else {
203 ((1u8 << bits_to_read) - 1) << shift
204 };
205 let extracted_bits = (byte_value & mask) >> shift;
206
207 value = (value << bits_to_read) | (extracted_bits as u32);
208 remaining_bits -= bits_to_read;
209 bit_index += bits_to_read;
210
211 if bit_index == 8 {
212 byte_index += 1;
213 bit_index = 0;
214 }
215 }
216
217 result.push(value);
218 }
219
220 Ok(result)
221 }
222}
223
224impl CompressionAlgorithm for FrameOfReferenceEncoder {
225 fn compress(&self, data: &[u8]) -> Result<CompressedData> {
226 if data.len() > MAX_COMPRESSION_INPUT_SIZE {
227 return Err(anyhow!("Input too large for compression"));
228 }
229
230 if data.len() % 4 != 0 {
232 return Err(anyhow!(
233 "Input data must be 4-byte aligned for FOR compression"
234 ));
235 }
236
237 let values: Vec<u32> = data
238 .chunks_exact(4)
239 .map(|chunk| u32::from_le_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]))
240 .collect();
241
242 self.compress_u32(&values)
243 }
244
245 fn decompress(&self, compressed: &CompressedData) -> Result<Vec<u8>> {
246 let values = self.decompress_u32(&compressed.data)?;
247
248 let mut result = Vec::with_capacity(values.len() * 4);
249 for value in values {
250 result.extend_from_slice(&value.to_le_bytes());
251 }
252
253 Ok(result)
254 }
255
256 fn algorithm_type(&self) -> AdvancedCompressionType {
257 AdvancedCompressionType::FrameOfReference
258 }
259}
260
261#[cfg(test)]
262mod tests {
263 use super::*;
264
265 #[test]
266 fn test_compression_algorithm_trait() {
267 let encoder = FrameOfReferenceEncoder::default();
268
269 let values = vec![100u32, 101u32, 102u32, 103u32];
271 let mut data = Vec::new();
272 for value in values {
273 data.extend_from_slice(&value.to_le_bytes());
274 }
275
276 let compressed = encoder.compress(&data).unwrap();
277 assert_eq!(
278 compressed.metadata.algorithm,
279 AdvancedCompressionType::FrameOfReference
280 );
281
282 let decompressed = encoder.decompress(&compressed).unwrap();
283 assert_eq!(data, decompressed);
284 }
285
286 #[test]
287 fn test_constant_sequence() {
288 let encoder = FrameOfReferenceEncoder::default();
289 let values = vec![42u32; 100];
290
291 let compressed = encoder.compress_u32(&values).unwrap();
292 let decompressed = encoder.decompress_u32(&compressed.data).unwrap();
293
294 assert_eq!(values, decompressed);
295 assert!(compressed.metadata.compressed_size < compressed.metadata.original_size);
297 }
298
299 #[test]
300 fn test_empty_sequence() {
301 let encoder = FrameOfReferenceEncoder::default();
302 let values = vec![];
303
304 let compressed = encoder.compress_u32(&values).unwrap();
305 let decompressed = encoder.decompress_u32(&compressed.data).unwrap();
306
307 assert_eq!(values, decompressed);
308 assert_eq!(compressed.metadata.compressed_size, 0);
309 }
310
311 #[test]
312 fn test_small_delta_sequence() {
313 let encoder = FrameOfReferenceEncoder::new(4); let values = vec![1000, 1001, 1002, 1003, 1004, 1005];
315
316 let compressed = encoder.compress_u32(&values).unwrap();
317 let decompressed = encoder.decompress_u32(&compressed.data).unwrap();
318
319 assert_eq!(values, decompressed);
320
321 assert_eq!(compressed.metadata.metadata.get("base").unwrap(), "1000");
323 assert_eq!(compressed.metadata.metadata.get("count").unwrap(), "6");
324 }
325
326 #[test]
327 fn test_large_delta_sequence() {
328 let encoder = FrameOfReferenceEncoder::new(16); let values = vec![1000, 5000, 10000, 15000];
330
331 let compressed = encoder.compress_u32(&values).unwrap();
332 let decompressed = encoder.decompress_u32(&compressed.data).unwrap();
333
334 assert_eq!(values, decompressed);
335 }
336}