1use crate::StringDictionary;
7use akar_common::enums::CompressionType;
8
9#[derive(Debug, Clone)]
11pub struct CompressedChunk {
12 pub compression: CompressionType,
13 pub data: Vec<u8>,
14 pub num_values: usize,
15}
16
17pub fn compress(compression: CompressionType, data: &[u8], num_values: usize) -> CompressedChunk {
19 match compression {
20 CompressionType::Constant => compress_constant(data, num_values),
21 CompressionType::Boolean => compress_boolean(data, num_values),
22 CompressionType::IntegerBitpacking | CompressionType::ListDelta => {
23 let value_size = data.len().checked_div(num_values).unwrap_or(8);
25 compress_integer_bitpacking(data, num_values, value_size)
26 }
27 CompressionType::Float => {
28 let value_size = data.len().checked_div(num_values).unwrap_or(4);
29 compress_float(data, num_values, value_size)
30 }
31 CompressionType::OneValue | CompressionType::Uncompressed => CompressedChunk {
32 compression,
33 data: data.to_vec(),
34 num_values,
35 },
36 CompressionType::StringDictionary => compress_string_dictionary(data, num_values),
37 }
38}
39
40pub fn decompress(chunk: &CompressedChunk, expected_size: usize) -> Vec<u8> {
42 match chunk.compression {
43 CompressionType::Constant => decompress_constant(&chunk.data, expected_size),
44 CompressionType::Boolean => decompress_boolean(&chunk.data, expected_size),
45 CompressionType::IntegerBitpacking => decompress_integer_bitpacking(&chunk.data, expected_size),
46 CompressionType::Float => decompress_float(&chunk.data, expected_size),
47 CompressionType::StringDictionary => decompress_string_dictionary(&chunk.data, expected_size),
48 _ => chunk.data.clone(),
49 }
50}
51
52fn compress_string_dictionary(data: &[u8], num_values: usize) -> CompressedChunk {
57 let dict = StringDictionary::deserialize(data).unwrap_or_default();
58 let dict_bytes = dict.serialize();
59 CompressedChunk {
60 compression: CompressionType::StringDictionary,
61 data: dict_bytes,
62 num_values,
63 }
64}
65
66fn decompress_string_dictionary(data: &[u8], _expected_size: usize) -> Vec<u8> {
67 let dict = match StringDictionary::deserialize(data) {
68 Ok(d) => d,
69 Err(_) => return Vec::new(),
70 };
71 dict.serialize()
72}
73
74fn compress_integer_impl(value_bytes: &[u8]) -> Vec<u8> {
90 let n = value_bytes.len();
91 let significant = (0..n).rev().find(|&i| value_bytes[i] != 0).map_or(0, |i| i + 1);
93 let used = significant.max(1); let mut out = Vec::with_capacity(1 + used);
95 out.push(used as u8);
96 out.extend_from_slice(&value_bytes[..used]);
97 out
98}
99
100fn decompress_integer_impl(data: &[u8], original_size: usize) -> Vec<u8> {
105 if data.is_empty() {
106 return vec![0u8; original_size];
107 }
108 let used = data[0] as usize;
109 let used = used.min(original_size);
110 let mut out = vec![0u8; original_size];
111 let avail = data.len().saturating_sub(1);
112 let copy = used.min(avail);
113 out[..copy].copy_from_slice(&data[1..1 + copy]);
114 out
115}
116
117pub fn compress_integer_bitpacking(data: &[u8], num_values: usize, value_size: usize) -> CompressedChunk {
121 let mut compressed = Vec::with_capacity(data.len());
122 compressed.push(value_size as u8);
123 compressed.extend_from_slice(&(num_values as u32).to_le_bytes());
124
125 let mut offset = 0;
126 for _ in 0..num_values {
127 if offset + value_size > data.len() {
128 break;
129 }
130 let val_bytes = &data[offset..offset + value_size];
131 let packed = compress_integer_impl(val_bytes);
132 compressed.extend_from_slice(&packed);
133 offset += value_size;
134 }
135
136 CompressedChunk {
137 compression: CompressionType::IntegerBitpacking,
138 data: compressed,
139 num_values,
140 }
141}
142
143fn decompress_integer_bitpacking(data: &[u8], expected_size: usize) -> Vec<u8> {
144 if data.len() < 5 {
145 return Vec::new();
146 }
147 let value_size = data[0] as usize;
148 let num_values = u32::from_le_bytes(data[1..5].try_into().unwrap()) as usize;
149 let mut result = Vec::with_capacity(expected_size.max(num_values * value_size));
150 let mut offset = 5;
151
152 for _ in 0..num_values {
153 if offset >= data.len() {
154 break;
155 }
156 let used = data[offset] as usize;
157 let total = 1 + used.min(value_size);
158 if offset + total > data.len() {
159 break;
160 }
161 let val_bytes = &data[offset..offset + total];
162 let expanded = decompress_integer_impl(val_bytes, value_size);
163 result.extend_from_slice(&expanded);
164 offset += total;
165 }
166
167 result
168}
169
170#[repr(u8)]
175#[derive(Debug, Clone, Copy, PartialEq, Eq)]
176pub enum FloatCompressionStrategy {
177 Raw = 0,
178 Delta = 1,
179 Offset = 2,
180}
181
182pub fn compress_float(data: &[u8], num_values: usize, value_size: usize) -> CompressedChunk {
185 if num_values == 0 || (value_size != 4 && value_size != 8) {
186 return compress_float_raw(data, num_values, value_size);
187 }
188
189 let mut ints_u32 = Vec::new();
190 let mut ints_u64 = Vec::new();
191
192 if value_size == 4 {
193 ints_u32.reserve(num_values);
194 let mut offset = 0;
195 for _ in 0..num_values {
196 if offset + 4 > data.len() {
197 break;
198 }
199 ints_u32.push(u32::from_le_bytes(data[offset..offset + 4].try_into().unwrap()));
200 offset += 4;
201 }
202 } else {
203 ints_u64.reserve(num_values);
204 let mut offset = 0;
205 for _ in 0..num_values {
206 if offset + 8 > data.len() {
207 break;
208 }
209 ints_u64.push(u64::from_le_bytes(data[offset..offset + 8].try_into().unwrap()));
210 offset += 8;
211 }
212 }
213
214 let actual_values = if value_size == 4 {
215 ints_u32.len()
216 } else {
217 ints_u64.len()
218 };
219 if actual_values == 0 {
220 return compress_float_raw(data, num_values, value_size);
221 }
222
223 let mut offset_data = Vec::with_capacity(actual_values * value_size);
224 let mut delta_data = Vec::with_capacity(actual_values * value_size);
225
226 if value_size == 4 {
227 let min_val = *ints_u32.iter().min().unwrap_or(&0);
228 for &v in &ints_u32 {
229 let diff = v.wrapping_sub(min_val);
230 offset_data.extend_from_slice(&diff.to_le_bytes());
231 }
232
233 let mut prev = 0u32;
234 for (i, &v) in ints_u32.iter().enumerate() {
235 let diff = if i == 0 { v } else { v.wrapping_sub(prev) };
236 delta_data.extend_from_slice(&diff.to_le_bytes());
237 prev = v;
238 }
239 } else {
240 let min_val = *ints_u64.iter().min().unwrap_or(&0);
241 for &v in &ints_u64 {
242 let diff = v.wrapping_sub(min_val);
243 offset_data.extend_from_slice(&diff.to_le_bytes());
244 }
245
246 let mut prev = 0u64;
247 for (i, &v) in ints_u64.iter().enumerate() {
248 let diff = if i == 0 { v } else { v.wrapping_sub(prev) };
249 delta_data.extend_from_slice(&diff.to_le_bytes());
250 prev = v;
251 }
252 }
253
254 let offset_chunk = compress_integer_bitpacking(&offset_data, actual_values, value_size);
255 let delta_chunk = compress_integer_bitpacking(&delta_data, actual_values, value_size);
256
257 let raw_len = actual_values * value_size;
258 let offset_payload_len = value_size + offset_chunk.data.len().saturating_sub(5);
259 let delta_payload_len = delta_chunk.data.len().saturating_sub(5);
260
261 let min_len = raw_len.min(offset_payload_len).min(delta_payload_len);
262
263 let mut compressed = Vec::new();
264 compressed.push(value_size as u8);
265 compressed.extend_from_slice(&(num_values as u32).to_le_bytes());
266
267 if min_len == raw_len {
268 compressed.push(FloatCompressionStrategy::Raw as u8);
269 compressed.extend_from_slice(&data[..raw_len]);
270 } else if min_len == offset_payload_len {
271 compressed.push(FloatCompressionStrategy::Offset as u8);
272 if value_size == 4 {
273 let min_val = *ints_u32.iter().min().unwrap();
274 compressed.extend_from_slice(&min_val.to_le_bytes());
275 } else {
276 let min_val = *ints_u64.iter().min().unwrap();
277 compressed.extend_from_slice(&min_val.to_le_bytes());
278 }
279 if offset_chunk.data.len() >= 5 {
280 compressed.extend_from_slice(&offset_chunk.data[5..]);
281 }
282 } else {
283 compressed.push(FloatCompressionStrategy::Delta as u8);
284 if delta_chunk.data.len() >= 5 {
285 compressed.extend_from_slice(&delta_chunk.data[5..]);
286 }
287 }
288
289 CompressedChunk {
290 compression: CompressionType::Float,
291 data: compressed,
292 num_values,
293 }
294}
295
296fn compress_float_raw(data: &[u8], num_values: usize, value_size: usize) -> CompressedChunk {
297 let mut compressed = Vec::with_capacity(6 + data.len());
298 compressed.push(value_size as u8);
299 compressed.extend_from_slice(&(num_values as u32).to_le_bytes());
300 compressed.push(FloatCompressionStrategy::Raw as u8);
301 let byte_count = num_values * value_size;
302 compressed.extend_from_slice(&data[..byte_count.min(data.len())]);
303
304 CompressedChunk {
305 compression: CompressionType::Float,
306 data: compressed,
307 num_values,
308 }
309}
310
311fn decompress_float(data: &[u8], expected_size: usize) -> Vec<u8> {
312 if data.len() < 6 {
313 return Vec::new();
314 }
315 let value_size = data[0] as usize;
316 let num_values = u32::from_le_bytes(data[1..5].try_into().unwrap()) as usize;
317 let strategy = data[5];
318 let expected = expected_size.max(num_values * value_size);
319
320 if strategy == FloatCompressionStrategy::Raw as u8 {
321 let mut result = vec![0u8; expected];
322 let avail = data.len().saturating_sub(6);
323 let byte_count = num_values * value_size;
324 let copy = byte_count.min(avail);
325 result[..copy].copy_from_slice(&data[6..6 + copy]);
326 return result;
327 }
328
329 let mut int_pack_data = Vec::new();
330 int_pack_data.push(value_size as u8);
331 int_pack_data.extend_from_slice(&(num_values as u32).to_le_bytes());
332
333 if strategy == FloatCompressionStrategy::Offset as u8 {
334 let mut offset_idx = 6;
335 if offset_idx + value_size > data.len() {
336 return Vec::new();
337 }
338 let min_val_bytes = &data[offset_idx..offset_idx + value_size];
339 offset_idx += value_size;
340 int_pack_data.extend_from_slice(&data[offset_idx..]);
341
342 let unpacked = decompress_integer_bitpacking(&int_pack_data, num_values * value_size);
343 let mut result = vec![0u8; expected];
344 if value_size == 4 {
345 let min_val = u32::from_le_bytes(min_val_bytes.try_into().unwrap());
346 for i in 0..num_values {
347 if i * 4 + 4 > unpacked.len() {
348 break;
349 }
350 let diff = u32::from_le_bytes(unpacked[i * 4..i * 4 + 4].try_into().unwrap());
351 let val = diff.wrapping_add(min_val);
352 result[i * 4..i * 4 + 4].copy_from_slice(&val.to_le_bytes());
353 }
354 } else {
355 let min_val = u64::from_le_bytes(min_val_bytes.try_into().unwrap());
356 for i in 0..num_values {
357 if i * 8 + 8 > unpacked.len() {
358 break;
359 }
360 let diff = u64::from_le_bytes(unpacked[i * 8..i * 8 + 8].try_into().unwrap());
361 let val = diff.wrapping_add(min_val);
362 result[i * 8..i * 8 + 8].copy_from_slice(&val.to_le_bytes());
363 }
364 }
365 return result;
366 }
367
368 if strategy == FloatCompressionStrategy::Delta as u8 {
369 int_pack_data.extend_from_slice(&data[6..]);
370 let unpacked = decompress_integer_bitpacking(&int_pack_data, num_values * value_size);
371
372 let mut result = vec![0u8; expected];
373 if value_size == 4 {
374 let mut prev = 0u32;
375 for i in 0..num_values {
376 if i * 4 + 4 > unpacked.len() {
377 break;
378 }
379 let diff = u32::from_le_bytes(unpacked[i * 4..i * 4 + 4].try_into().unwrap());
380 let val = if i == 0 { diff } else { prev.wrapping_add(diff) };
381 result[i * 4..i * 4 + 4].copy_from_slice(&val.to_le_bytes());
382 prev = val;
383 }
384 } else {
385 let mut prev = 0u64;
386 for i in 0..num_values {
387 if i * 8 + 8 > unpacked.len() {
388 break;
389 }
390 let diff = u64::from_le_bytes(unpacked[i * 8..i * 8 + 8].try_into().unwrap());
391 let val = if i == 0 { diff } else { prev.wrapping_add(diff) };
392 result[i * 8..i * 8 + 8].copy_from_slice(&val.to_le_bytes());
393 prev = val;
394 }
395 }
396 return result;
397 }
398
399 Vec::new()
400}
401
402pub fn compress_serialized_value(compression: CompressionType, raw: &[u8], value_size: usize) -> Vec<u8> {
415 if raw.is_empty() {
416 return Vec::new();
417 }
418 let tag = raw[0];
419 let payload = &raw[1..];
420
421 match compression {
422 CompressionType::IntegerBitpacking if value_size > 0 && value_size <= 8 => {
423 let packed = compress_integer_impl(payload);
425 let mut out = Vec::with_capacity(1 + packed.len());
426 out.push(tag);
427 out.extend_from_slice(&packed);
428 out
429 }
430 CompressionType::Float if value_size > 0 && value_size <= 8 => {
431 let mut out = Vec::with_capacity(1 + 1 + payload.len());
433 out.push(tag);
434 out.push(payload.len() as u8);
435 out.extend_from_slice(payload);
436 out
437 }
438 _ => {
439 let mut out = Vec::with_capacity(raw.len());
441 out.extend_from_slice(raw);
442 out
443 }
444 }
445}
446
447pub fn decompress_serialized_value(compression: CompressionType, compressed: &[u8], value_size: usize) -> Vec<u8> {
452 if compressed.is_empty() {
453 return Vec::new();
454 }
455 let tag = compressed[0];
456 let stored_payload = &compressed[1..];
457
458 match compression {
459 CompressionType::IntegerBitpacking if value_size > 0 && value_size <= 8 => {
460 let expanded = decompress_integer_impl(stored_payload, value_size);
461 let mut out = Vec::with_capacity(1 + expanded.len());
462 out.push(tag);
463 out.extend_from_slice(&expanded);
464 out
465 }
466 CompressionType::Float if value_size > 0 && value_size <= 8 => {
467 if stored_payload.is_empty() {
469 return compressed.to_vec();
470 }
471 let len = stored_payload[0] as usize;
472 let len = len.min(stored_payload.len().saturating_sub(1));
473 let mut out = Vec::with_capacity(1 + len);
474 out.push(tag);
475 out.extend_from_slice(&stored_payload[1..1 + len]);
476 out
477 }
478 _ => {
479 compressed.to_vec()
481 }
482 }
483}
484
485pub fn serialized_value_size(physical_type: akar_common::types::PhysicalTypeID) -> usize {
488 use akar_common::types::PhysicalTypeID;
489 match physical_type {
490 PhysicalTypeID::Int64 | PhysicalTypeID::UInt64 | PhysicalTypeID::Double => 8,
491 PhysicalTypeID::Int32 | PhysicalTypeID::UInt32 | PhysicalTypeID::Float => 4,
492 PhysicalTypeID::Int16 | PhysicalTypeID::UInt16 => 2,
493 PhysicalTypeID::Int8 | PhysicalTypeID::UInt8 | PhysicalTypeID::Bool => 1,
494 PhysicalTypeID::Interval => 16,
495 _ => 0, }
497}
498
499fn compress_constant(data: &[u8], num_values: usize) -> CompressedChunk {
502 let val_size = if data.is_empty() {
503 0
504 } else {
505 data.len() / num_values.max(1)
506 };
507 let mut compressed = Vec::with_capacity(4 + val_size);
508 compressed.extend_from_slice(&(num_values as u32).to_le_bytes());
509 if val_size > 0 {
510 compressed.extend_from_slice(&data[..val_size]);
511 }
512 CompressedChunk {
513 compression: CompressionType::Constant,
514 data: compressed,
515 num_values,
516 }
517}
518
519fn decompress_constant(data: &[u8], expected_size: usize) -> Vec<u8> {
520 if data.len() < 4 {
521 return Vec::new();
522 }
523 let mut arr = [0u8; 4];
524 arr.copy_from_slice(&data[..4]);
525 let num_vals = u32::from_le_bytes(arr) as usize;
526 let val_bytes = &data[4..];
527 let mut result = Vec::with_capacity(expected_size);
528 for _ in 0..num_vals {
529 result.extend_from_slice(val_bytes);
530 }
531 result
532}
533
534fn compress_boolean(data: &[u8], num_values: usize) -> CompressedChunk {
536 let packed_len = num_values.div_ceil(8);
537 let mut packed = vec![0u8; packed_len];
538 for i in 0..num_values.min(data.len()) {
539 if data[i] != 0 {
540 packed[i / 8] |= 1 << (i % 8);
541 }
542 }
543 CompressedChunk {
544 compression: CompressionType::Boolean,
545 data: packed,
546 num_values,
547 }
548}
549
550fn decompress_boolean(data: &[u8], num_values: usize) -> Vec<u8> {
551 let mut result = vec![0u8; num_values];
552 for i in 0..num_values {
553 result[i] = if data[i / 8] & (1 << (i % 8)) != 0 { 1 } else { 0 };
554 }
555 result
556}
557
558#[cfg(test)]
559mod tests {
560 use super::*;
561
562 #[test]
563 fn test_constant_roundtrip() {
564 let original = vec![42u8; 100];
565 let chunk = compress(CompressionType::Constant, &original, 100);
566 assert!(chunk.data.len() < original.len());
567 let dec = decompress(&chunk, 100);
568 assert_eq!(dec.len(), 100);
569 assert_eq!(dec[0], 42);
570 assert_eq!(dec[99], 42);
571 }
572
573 #[test]
574 fn test_boolean_roundtrip() {
575 let mut original = vec![0u8; 16];
576 original[0] = 1;
577 original[7] = 1;
578 original[15] = 1;
579 let chunk = compress(CompressionType::Boolean, &original, 16);
580 assert!(chunk.data.len() < original.len());
581 let dec = decompress(&chunk, 16);
582 assert_eq!(dec[0], 1);
583 assert_eq!(dec[7], 1);
584 assert_eq!(dec[15], 1);
585 assert_eq!(dec[1], 0);
586 assert_eq!(dec[8], 0);
587 }
588
589 #[test]
590 fn test_uncompressed_roundtrip() {
591 let original = vec![1u8, 2, 3, 4, 5];
592 let chunk = compress(CompressionType::Uncompressed, &original, 5);
593 assert_eq!(chunk.data, original);
594 let dec = decompress(&chunk, 5);
595 assert_eq!(dec, original);
596 }
597
598 #[test]
601 fn test_compress_integer_small() {
602 let val = 42i64.to_le_bytes();
604 let packed = super::compress_integer_impl(&val);
605 assert_eq!(packed[0], 1); assert_eq!(packed[1], 42);
607 }
608
609 #[test]
610 fn test_compress_integer_large() {
611 let val = 0x12345678i64.to_le_bytes();
613 let packed = super::compress_integer_impl(&val);
614 assert_eq!(packed[0], 4); assert_eq!(&packed[1..5], &val[..4]);
617 }
618
619 #[test]
620 fn test_compress_integer_negative() {
621 let val = (-1i64).to_le_bytes();
623 let packed = super::compress_integer_impl(&val);
624 assert_eq!(packed[0], 8); }
626
627 #[test]
628 fn test_integer_roundtrip_batch() {
629 let values: Vec<i64> = vec![0, 1, 42, 127, 255, 1000, 65535, 100000, -1, -128];
630 let value_size = 8;
631 let mut data = Vec::with_capacity(values.len() * value_size);
632 for v in &values {
633 data.extend_from_slice(&v.to_le_bytes());
634 }
635 let chunk = compress(CompressionType::IntegerBitpacking, &data, values.len());
636 let dec = decompress(&chunk, values.len() * value_size);
637 assert_eq!(dec.len(), values.len() * value_size);
638 for (i, v) in values.iter().enumerate() {
639 let val = i64::from_le_bytes(dec[i * value_size..(i + 1) * value_size].try_into().unwrap());
640 assert_eq!(val, *v, "mismatch at index {}", i);
641 }
642 }
643
644 #[test]
645 fn test_single_value_compress_decompress() {
646 let mut raw = vec![0x02u8]; raw.extend_from_slice(&42i64.to_le_bytes()); assert_eq!(raw.len(), 9);
650
651 let compressed = super::compress_serialized_value(
652 CompressionType::IntegerBitpacking,
653 &raw,
654 8, );
656 assert_eq!(compressed[0], 0x02);
658 assert!(compressed.len() < raw.len(), "compression should reduce size");
660
661 let decompressed = super::decompress_serialized_value(CompressionType::IntegerBitpacking, &compressed, 8);
662 assert_eq!(decompressed, raw, "full roundtrip should match original");
663
664 let restored = i64::from_le_bytes(decompressed[1..9].try_into().unwrap());
666 assert_eq!(restored, 42);
667 }
668
669 #[test]
672 fn test_float_batch_roundtrip_raw() {
673 let values: Vec<f64> = vec![1.0, 3.15, -2.5, 0.0, 1e10, f64::MAX, f64::MIN];
675 let value_size = 8;
676 let mut data = Vec::with_capacity(values.len() * value_size);
677 for v in &values {
678 data.extend_from_slice(&v.to_le_bytes());
679 }
680 let chunk = compress(CompressionType::Float, &data, values.len());
681 assert_eq!(chunk.data[5], FloatCompressionStrategy::Raw as u8);
682 let dec = decompress(&chunk, values.len() * value_size);
683 for (i, v) in values.iter().enumerate() {
684 let val = f64::from_le_bytes(dec[i * value_size..(i + 1) * value_size].try_into().unwrap());
685 assert_eq!(val.to_bits(), v.to_bits(), "mismatch at index {}", i);
686 }
687 }
688
689 #[test]
690 fn test_float_batch_roundtrip_delta() {
691 let mut values: Vec<f32> = Vec::new();
693 for i in 0..100 {
694 values.push(i as f32 * 1.5);
695 }
696 let value_size = 4;
697 let mut data = Vec::with_capacity(values.len() * value_size);
698 for v in &values {
699 data.extend_from_slice(&v.to_le_bytes());
700 }
701 let chunk = compress(CompressionType::Float, &data, values.len());
702 assert_eq!(chunk.data[5], FloatCompressionStrategy::Delta as u8);
703 assert!(chunk.data.len() < data.len() + 6);
704 let dec = decompress(&chunk, values.len() * value_size);
705 for (i, v) in values.iter().enumerate() {
706 let val = f32::from_le_bytes(dec[i * value_size..(i + 1) * value_size].try_into().unwrap());
707 assert_eq!(val.to_bits(), v.to_bits(), "mismatch at index {}", i);
708 }
709 }
710
711 #[test]
712 fn test_float_batch_roundtrip_offset() {
713 let values: Vec<f64> = vec![1000.1, 1000.15, 1000.0, 1000.05, 1000.2];
715 let value_size = 8;
716 let mut data = Vec::with_capacity(values.len() * value_size);
717 for v in &values {
718 data.extend_from_slice(&v.to_le_bytes());
719 }
720 let chunk = compress(CompressionType::Float, &data, values.len());
721 assert!(
723 chunk.data[5] == FloatCompressionStrategy::Offset as u8
724 || chunk.data[5] == FloatCompressionStrategy::Delta as u8
725 );
726 let dec = decompress(&chunk, values.len() * value_size);
727 for (i, v) in values.iter().enumerate() {
728 let val = f64::from_le_bytes(dec[i * value_size..(i + 1) * value_size].try_into().unwrap());
729 assert_eq!(val.to_bits(), v.to_bits(), "mismatch at index {}", i);
730 }
731 }
732
733 #[test]
736 fn test_pass_through_roundtrip() {
737 let mut raw = vec![0x0D]; raw.extend_from_slice(b"hello world");
741 let compressed = super::compress_serialized_value(
742 CompressionType::IntegerBitpacking,
743 &raw,
744 0, );
746 assert_eq!(compressed, raw);
748 let decompressed = super::decompress_serialized_value(CompressionType::IntegerBitpacking, &compressed, 0);
749 assert_eq!(decompressed, raw);
750 }
751
752 #[test]
753 fn test_compression_metadata() {
754 let data = vec![42u8; 100];
756 let chunk = compress(CompressionType::Constant, &data, 100);
757 assert_eq!(chunk.compression, CompressionType::Constant);
758 assert_eq!(chunk.num_values, 100);
759 assert!(chunk.data.len() < data.len());
760 }
761}