1use rudb_common::{Error, Result};
45
46use crate::chooser::{Chooser, EXHAUSTIVE};
47use crate::reader::Reader;
48
49use crate::bitpack::{self, VALUES};
50
51const MAX_DEPTH: u8 = 3;
58
59#[derive(Debug, Clone, Copy, PartialEq, Eq)]
62pub enum Kind {
63 Constant = 0,
65 Packed = 1,
68 Delta = 2,
71 Rle = 3,
73 Dict = 4,
76 Sparse = 5,
78}
79
80impl Kind {
81 fn tag(self) -> u8 {
82 self as u8
83 }
84
85 fn from_tag(tag: u8) -> Result<Self> {
86 match tag {
87 0 => Ok(Self::Constant),
88 1 => Ok(Self::Packed),
89 2 => Ok(Self::Delta),
90 3 => Ok(Self::Rle),
91 4 => Ok(Self::Dict),
92 5 => Ok(Self::Sparse),
93 other => Err(Error::internal(format!("unknown encoding tag {other}"))),
94 }
95 }
96
97 #[must_use]
99 pub fn name(self) -> &'static str {
100 match self {
101 Self::Constant => "CONSTANT",
102 Self::Packed => "FOR+BITPACK",
103 Self::Delta => "DELTA",
104 Self::Rle => "RLE",
105 Self::Dict => "DICT",
106 Self::Sparse => "SPARSE",
107 }
108 }
109}
110
111pub fn encode(values: &[i64]) -> Result<Vec<u8>> {
118 encode_with(values, &EXHAUSTIVE)
119}
120
121pub fn encode_with(values: &[i64], chooser: &dyn Chooser) -> Result<Vec<u8>> {
131 encode_at(values, 0, chooser)
132}
133
134pub fn decode(bytes: &[u8]) -> Result<Vec<i64>> {
141 let mut reader = Reader::new(bytes);
142 let values = with_decoding(|scratch| decode_chunk(&mut reader, scratch))?;
143 if reader.remaining() != 0 {
144 return Err(Error::internal(format!(
145 "{} bytes left over after decoding a chunk",
146 reader.remaining()
147 )));
148 }
149 Ok(values)
150}
151
152pub fn decode_prefix(bytes: &[u8]) -> Result<(Vec<i64>, usize)> {
162 let mut reader = Reader::new(bytes);
163 let values = with_decoding(|scratch| decode_chunk(&mut reader, scratch))?;
164 Ok((values, reader.used()))
165}
166
167pub fn describe_prefix(bytes: &[u8]) -> Result<(String, usize)> {
173 let mut reader = Reader::new(bytes);
174 let text = describe_chunk(&mut reader)?;
175 Ok((text, reader.used()))
176}
177
178pub fn candidate_sizes(values: &[i64]) -> Result<Vec<(Kind, usize)>> {
185 let mut sizes = Vec::new();
186 for kind in candidates(values, 0) {
187 if let Some(bytes) = encode_as(kind, values, 0, &EXHAUSTIVE)? {
188 sizes.push((kind, bytes.len()));
189 }
190 }
191 Ok(sizes)
192}
193
194#[must_use]
201pub fn offered(values: &[i64]) -> Vec<Kind> {
202 candidates(values, 0)
203}
204
205pub fn encode_only(kind: Kind, values: &[i64]) -> Result<Option<Vec<u8>>> {
216 encode_as(kind, values, 0, &EXHAUSTIVE)
217}
218
219pub(crate) fn size_as(kind: Kind, values: &[i64], depth: u8) -> Result<Option<usize>> {
224 Ok(encode_as(kind, values, depth, &EXHAUSTIVE)?.map(|bytes| bytes.len()))
225}
226
227pub fn describe(bytes: &[u8]) -> Result<String> {
233 let mut reader = Reader::new(bytes);
234 describe_chunk(&mut reader)
235}
236
237fn encode_at(values: &[i64], depth: u8, chooser: &dyn Chooser) -> Result<Vec<u8>> {
238 let offered = candidates(values, depth);
239 let mut best: Option<Vec<u8>> = None;
240 for kind in chooser.narrow_integers(values, &offered, depth) {
241 let Some(bytes) = encode_as(kind, values, depth, chooser)? else {
242 continue;
243 };
244 if best.as_ref().is_none_or(|current| bytes.len() < current.len()) {
245 best = Some(bytes);
246 }
247 }
248 best.ok_or_else(|| Error::internal("no encoding applied to the chunk"))
251}
252
253fn candidates(values: &[i64], depth: u8) -> Vec<Kind> {
260 let mut kinds = vec![Kind::Packed];
261 if depth >= MAX_DEPTH || values.is_empty() {
262 return kinds;
263 }
264 if values.iter().all(|value| *value == values[0]) {
265 return vec![Kind::Constant];
267 }
268 if values.len() >= 2 && deltas_fit(values) {
269 kinds.push(Kind::Delta);
270 }
271 if run_count(values) * 4 <= values.len() * 3 {
272 kinds.push(Kind::Rle);
273 }
274 let (distinct, dominant) = spread_of(values);
278 if distinct * 2 <= values.len() {
279 kinds.push(Kind::Dict);
280 }
281 match dominant {
284 Some((_, count)) if count * 10 >= values.len() * 8 => kinds.push(Kind::Sparse),
285 _ => {}
286 }
287 kinds
288}
289
290fn encode_as(
293 kind: Kind,
294 values: &[i64],
295 depth: u8,
296 chooser: &dyn Chooser,
297) -> Result<Option<Vec<u8>>> {
298 let mut out = Vec::new();
299 put_u8(&mut out, kind.tag());
300 put_u32(&mut out, u32::try_from(values.len()).map_err(|_| too_long(values.len()))?);
301 match kind {
302 Kind::Constant => {
303 let Some(first) = values.first() else {
304 return Ok(None);
305 };
306 if values.iter().any(|value| value != first) {
307 return Ok(None);
308 }
309 put_i64(&mut out, *first);
310 }
311 Kind::Packed => encode_packed(values, &mut out)?,
312 Kind::Delta => {
313 let (Some(first), Some(deltas)) = (values.first(), deltas(values)) else {
317 return Ok(None);
318 };
319 put_i64(&mut out, *first);
320 out.extend_from_slice(&encode_at(&deltas, depth + 1, chooser)?);
321 }
322 Kind::Rle => {
323 let (run_values, run_lengths) = runs(values);
324 if run_values.is_empty() {
325 return Ok(None);
326 }
327 out.extend_from_slice(&encode_at(&run_values, depth + 1, chooser)?);
328 out.extend_from_slice(&encode_at(&run_lengths, depth + 1, chooser)?);
329 }
330 Kind::Dict => {
331 let dictionary = distinct_values(values);
332 if dictionary.is_empty() {
333 return Ok(None);
334 }
335 let codes = codes_over(values, &dictionary);
336 out.extend_from_slice(&encode_at(&dictionary, depth + 1, chooser)?);
337 out.extend_from_slice(&encode_at(&codes, depth + 1, chooser)?);
338 }
339 Kind::Sparse => {
340 let Some((value, _)) = spread_of(values).1 else {
341 return Ok(None);
342 };
343 let mut positions = Vec::new();
344 let mut exceptions = Vec::new();
345 for (index, other) in values.iter().enumerate() {
346 if *other != value {
347 positions.push(index as i64);
348 exceptions.push(*other);
349 }
350 }
351 put_i64(&mut out, value);
352 put_u32(
353 &mut out,
354 u32::try_from(positions.len()).map_err(|_| too_long(positions.len()))?,
355 );
356 out.extend_from_slice(&encode_at(&positions, depth + 1, chooser)?);
357 out.extend_from_slice(&encode_at(&exceptions, depth + 1, chooser)?);
358 }
359 }
360 Ok(Some(out))
361}
362
363fn encode_packed(values: &[i64], out: &mut Vec<u8>) -> Result<()> {
375 let mut offsets: Vec<u64> = Vec::with_capacity(VALUES);
379 let mut packed: Vec<u64> = vec![0; bitpack::packed_len::<u64>(64)];
382 let mut transposed = bitpack::Scratch::<u64>::new();
383 for unit in values.chunks(VALUES) {
384 let base = unit.iter().copied().min().unwrap_or(0);
385 offsets.clear();
386 offsets.extend(unit.iter().map(|value| offset_from(*value, base)));
387 let width = bitpack::required_width(&offsets);
388 put_i64(out, base);
389 put_u8(out, u8::try_from(width).map_err(|_| Error::internal("impossible width"))?);
390 if unit.len() == VALUES {
391 let words = bitpack::packed_len::<u64>(width);
392 bitpack::pack_with(&offsets, width, &mut packed[..words], &mut transposed)?;
393 for word in &packed[..words] {
394 put_u64(out, *word);
395 }
396 } else {
397 bitpack::pack_tail(&offsets, width, out)?;
398 }
399 }
400 Ok(())
401}
402
403struct Decoding {
426 packed: Vec<u64>,
429 unit: Vec<u64>,
431 transposed: bitpack::Scratch<u64>,
433}
434
435thread_local! {
436 static DECODING: std::cell::RefCell<Decoding> =
438 const { std::cell::RefCell::new(Decoding::new()) };
439}
440
441fn with_decoding<T>(run: impl FnOnce(&mut Decoding) -> T) -> T {
448 DECODING.with(|cell| match cell.try_borrow_mut() {
449 Ok(mut scratch) => run(&mut scratch),
450 Err(_) => run(&mut Decoding::new()),
451 })
452}
453
454impl Decoding {
455 const fn new() -> Self {
457 Self { packed: Vec::new(), unit: Vec::new(), transposed: bitpack::Scratch::new() }
458 }
459
460 fn ready(&mut self) {
462 if self.unit.len() != VALUES {
463 self.unit.resize(VALUES, 0);
464 self.packed.resize(bitpack::packed_len::<u64>(64), 0);
465 }
466 }
467}
468
469fn decode_chunk(reader: &mut Reader<'_>, scratch: &mut Decoding) -> Result<Vec<i64>> {
470 let kind = Kind::from_tag(reader.u8()?)?;
471 let count = reader.u32()? as usize;
472 match kind {
473 Kind::Constant => Ok(vec![reader.i64()?; count]),
474 Kind::Packed => {
475 let mut values = Vec::with_capacity(count);
476 scratch.ready();
477 while values.len() < count {
478 let base = reader.i64()?;
479 let width = reader.u8()? as usize;
480 let wanted = (count - values.len()).min(VALUES);
481 if wanted == VALUES {
482 let words = bitpack::packed_len::<u64>(width);
483 for word in &mut scratch.packed[..words] {
484 *word = reader.u64()?;
485 }
486 bitpack::unpack_with(
487 &scratch.packed[..words],
488 width,
489 &mut scratch.unit,
490 &mut scratch.transposed,
491 )?;
492 values.extend(scratch.unit.iter().map(|offset| value_from(*offset, base)));
493 } else {
494 let bytes = reader.bytes(bitpack::tail_len(wanted, width))?;
495 let unit = bitpack::unpack_tail(bytes, width, wanted)?;
496 values.extend(unit.iter().map(|offset| value_from(*offset, base)));
497 }
498 }
499 Ok(values)
500 }
501 Kind::Delta => {
502 let first = reader.i64()?;
503 let deltas = decode_chunk(reader, scratch)?;
504 let mut values = Vec::with_capacity(count);
505 values.push(first);
506 let mut current = first;
507 for delta in deltas {
508 current = current.wrapping_add(unzigzag(delta as u64));
509 values.push(current);
510 }
511 check_count(values.len(), count)?;
512 Ok(values)
513 }
514 Kind::Rle => {
515 let run_values = decode_chunk(reader, scratch)?;
516 let run_lengths = decode_chunk(reader, scratch)?;
517 if run_values.len() != run_lengths.len() {
518 return Err(Error::internal("an RLE chunk has more runs than run lengths"));
519 }
520 let mut values = Vec::with_capacity(count);
521 for (value, length) in run_values.into_iter().zip(run_lengths) {
522 let length = usize::try_from(length)
523 .map_err(|_| Error::internal("a negative RLE run length"))?;
524 values.extend(std::iter::repeat_n(value, length));
525 }
526 check_count(values.len(), count)?;
527 Ok(values)
528 }
529 Kind::Dict => {
530 let dictionary = decode_chunk(reader, scratch)?;
531 let codes = decode_chunk(reader, scratch)?;
532 let mut values = Vec::with_capacity(count);
533 for code in codes {
534 let index =
535 usize::try_from(code).ok().and_then(|index| dictionary.get(index)).ok_or_else(
536 || Error::internal(format!("code {code} is not in the dictionary")),
537 )?;
538 values.push(*index);
539 }
540 check_count(values.len(), count)?;
541 Ok(values)
542 }
543 Kind::Sparse => {
544 let value = reader.i64()?;
545 let exception_count = reader.u32()? as usize;
546 let positions = decode_chunk(reader, scratch)?;
547 let exceptions = decode_chunk(reader, scratch)?;
548 if positions.len() != exception_count || exceptions.len() != exception_count {
549 return Err(Error::internal("a sparse chunk disagrees about its exception count"));
550 }
551 let mut values = vec![value; count];
552 for (position, exception) in positions.into_iter().zip(exceptions) {
553 let position = usize::try_from(position)
554 .ok()
555 .filter(|position| *position < count)
556 .ok_or_else(|| {
557 Error::internal(format!("exception at {position} is outside the chunk"))
558 })?;
559 values[position] = exception;
560 }
561 Ok(values)
562 }
563 }
564}
565
566fn describe_chunk(reader: &mut Reader<'_>) -> Result<String> {
567 let kind = Kind::from_tag(reader.u8()?)?;
568 let count = reader.u32()? as usize;
569 Ok(match kind {
570 Kind::Constant => {
571 reader.i64()?;
572 "CONSTANT".to_string()
573 }
574 Kind::Packed => {
575 let mut widths = Vec::new();
576 let mut seen = 0;
577 while seen < count {
578 reader.i64()?;
579 let width = reader.u8()? as usize;
580 let wanted = (count - seen).min(VALUES);
581 if wanted == VALUES {
582 for _ in 0..bitpack::packed_len::<u64>(width) {
583 reader.u64()?;
584 }
585 } else {
586 reader.bytes(bitpack::tail_len(wanted, width))?;
587 }
588 widths.push(width);
589 seen += wanted;
590 }
591 let low = widths.iter().copied().min().unwrap_or(0);
592 let high = widths.iter().copied().max().unwrap_or(0);
593 if low == high {
596 format!("FOR+BITPACK[{low}]")
597 } else {
598 format!("FOR+BITPACK[{low}..{high}]")
599 }
600 }
601 Kind::Delta => {
602 reader.i64()?;
603 format!("DELTA({})", describe_chunk(reader)?)
604 }
605 Kind::Rle => {
606 let values = describe_chunk(reader)?;
607 let lengths = describe_chunk(reader)?;
608 format!("RLE({values}, {lengths})")
609 }
610 Kind::Dict => {
611 let dictionary = describe_chunk(reader)?;
612 let codes = describe_chunk(reader)?;
613 format!("DICT({dictionary}, {codes})")
614 }
615 Kind::Sparse => {
616 reader.i64()?;
617 reader.u32()?;
618 let positions = describe_chunk(reader)?;
619 let exceptions = describe_chunk(reader)?;
620 format!("SPARSE({positions}, {exceptions})")
621 }
622 })
623}
624
625fn offset_from(value: i64, base: i64) -> u64 {
628 (i128::from(value) - i128::from(base)) as u64
629}
630
631fn value_from(offset: u64, base: i64) -> i64 {
632 (i128::from(base) + i128::from(offset)) as i64
633}
634
635fn zigzag(value: i64) -> u64 {
638 ((value << 1) ^ (value >> 63)) as u64
639}
640
641fn unzigzag(value: u64) -> i64 {
642 ((value >> 1) as i64) ^ -((value & 1) as i64)
643}
644
645fn deltas_fit(values: &[i64]) -> bool {
657 values.windows(2).all(|pair| i64::try_from(i128::from(pair[1]) - i128::from(pair[0])).is_ok())
658}
659
660fn deltas(values: &[i64]) -> Option<Vec<i64>> {
661 let mut deltas = Vec::with_capacity(values.len().saturating_sub(1));
662 for pair in values.windows(2) {
663 let difference = i128::from(pair[1]) - i128::from(pair[0]);
664 let difference = i64::try_from(difference).ok()?;
665 deltas.push(zigzag(difference) as i64);
666 }
667 Some(deltas)
668}
669
670fn run_count(values: &[i64]) -> usize {
671 let mut runs = 0;
672 let mut previous = None;
673 for value in values {
674 if previous != Some(value) {
675 runs += 1;
676 previous = Some(value);
677 }
678 }
679 runs
680}
681
682fn runs(values: &[i64]) -> (Vec<i64>, Vec<i64>) {
683 let mut run_values: Vec<i64> = Vec::new();
684 let mut run_lengths: Vec<i64> = Vec::new();
685 for value in values {
686 if run_values.last() == Some(value) {
687 *run_lengths.last_mut().expect("a run length exists beside every run value") += 1;
688 } else {
689 run_values.push(*value);
690 run_lengths.push(1);
691 }
692 }
693 (run_values, run_lengths)
694}
695
696fn spread_of(values: &[i64]) -> (usize, Option<(i64, usize)>) {
711 let mut sorted = values.to_vec();
712 sorted.sort_unstable();
713 let mut distinct = 0;
714 let mut best: Option<(i64, usize)> = None;
715 let mut index = 0;
716 while index < sorted.len() {
717 let value = sorted[index];
718 let mut end = index;
719 while end < sorted.len() && sorted[end] == value {
720 end += 1;
721 }
722 distinct += 1;
723 let count = end - index;
724 if best.is_none_or(|(_, seen)| count > seen) {
725 best = Some((value, count));
726 }
727 index = end;
728 }
729 (distinct, best)
730}
731
732fn distinct_values(values: &[i64]) -> Vec<i64> {
735 let mut distinct = values.to_vec();
736 distinct.sort_unstable();
737 distinct.dedup();
738 distinct
739}
740
741fn codes_over(values: &[i64], dictionary: &[i64]) -> Vec<i64> {
751 values
752 .iter()
753 .map(|value| {
754 dictionary
755 .binary_search(value)
756 .expect("the dictionary is the distinct values of this chunk") as i64
757 })
758 .collect()
759}
760
761fn check_count(actual: usize, expected: usize) -> Result<()> {
762 if actual == expected {
763 Ok(())
764 } else {
765 Err(Error::internal(format!(
766 "a chunk says it holds {expected} values and decoded to {actual}"
767 )))
768 }
769}
770
771fn too_long(len: usize) -> Error {
772 Error::internal(format!("a chunk of {len} values is longer than the format allows"))
773}
774
775fn put_u8(out: &mut Vec<u8>, value: u8) {
776 out.push(value);
777}
778
779fn put_u32(out: &mut Vec<u8>, value: u32) {
780 out.extend_from_slice(&value.to_le_bytes());
781}
782
783fn put_u64(out: &mut Vec<u8>, value: u64) {
784 out.extend_from_slice(&value.to_le_bytes());
785}
786
787fn put_i64(out: &mut Vec<u8>, value: i64) {
788 out.extend_from_slice(&value.to_le_bytes());
789}
790
791#[cfg(test)]
792mod tests {
793 use super::*;
794
795 fn round_trip(values: &[i64]) -> Vec<u8> {
796 let bytes = encode(values).unwrap();
797 assert_eq!(decode(&bytes).unwrap(), values, "{}", describe(&bytes).unwrap());
798 bytes
799 }
800
801 fn kind_of(bytes: &[u8]) -> Kind {
802 Kind::from_tag(bytes[0]).unwrap()
803 }
804
805 struct Random(u64);
807
808 impl Random {
809 fn new() -> Self {
810 Self(0x9e37_79b9_7f4a_7c15)
811 }
812
813 fn next(&mut self) -> u64 {
814 self.0 ^= self.0 << 13;
815 self.0 ^= self.0 >> 7;
816 self.0 ^= self.0 << 17;
817 self.0
818 }
819 }
820
821 #[test]
822 fn the_dictionary_is_sorted_and_the_codes_point_back_at_the_values() {
823 let values = vec![30i64, 10, 30, 20, 10, -5];
824 let dictionary = distinct_values(&values);
825 let codes = codes_over(&values, &dictionary);
826 assert_eq!(dictionary, vec![-5, 10, 20, 30]);
827 assert_eq!(codes, vec![3, 1, 3, 2, 1, 0]);
828 for (code, value) in codes.iter().zip(&values) {
829 assert_eq!(dictionary[*code as usize], *value);
830 }
831 }
832
833 #[test]
834 fn one_sort_gives_the_distinct_count_and_the_most_frequent_value() {
835 let values = vec![7i64, 7, 7, 1, 2, 2];
836 assert_eq!(spread_of(&values), (3, Some((7, 3))));
837 assert_eq!(spread_of(&[]), (0, None));
838 assert_eq!(spread_of(&[9]), (1, Some((9, 1))));
839
840 assert_eq!(spread_of(&[4i64, 4, 8, 8]), (2, Some((4, 2))));
843 }
844
845 #[test]
846 fn deltas_that_do_not_fit_are_refused_before_they_are_built() {
847 assert!(deltas_fit(&[1i64, 2, 3]));
848 assert!(deltas_fit(&[i64::MAX, i64::MAX]));
849 assert!(!deltas_fit(&[i64::MIN, i64::MAX]));
850 assert_eq!(deltas_fit(&[i64::MIN, i64::MAX]), deltas(&[i64::MIN, i64::MAX]).is_some());
851 assert_eq!(deltas_fit(&[1i64, 2, 3]), deltas(&[1i64, 2, 3]).is_some());
852 }
853
854 #[test]
855 fn what_the_chooser_returns_is_the_smallest_of_what_it_was_offered() {
856 let mut random = Random::new();
860 let noise: Vec<i64> = (0..2000).map(|_| (random.next() % 5000) as i64).collect();
861 let runs: Vec<i64> = (0..2000).map(|index: i64| index / 100).collect();
862 let climbing: Vec<i64> = (0..2000).map(|index| 1_700_000_000 + index).collect();
863 for values in [noise, runs, climbing, vec![7; 300], Vec::new()] {
864 let chosen = encode(&values).unwrap();
865 let mut smallest: Option<Vec<u8>> = None;
866 for kind in offered(&values) {
867 let Some(bytes) = encode_only(kind, &values).unwrap() else {
868 continue;
869 };
870 if smallest.as_ref().is_none_or(|best| bytes.len() < best.len()) {
871 smallest = Some(bytes);
872 }
873 }
874 assert_eq!(smallest.as_deref(), Some(chosen.as_slice()), "{}", values.len());
875 }
876 }
877
878 #[test]
879 fn an_empty_chunk_round_trips() {
880 let bytes = round_trip(&[]);
881 assert_eq!(bytes.len(), 5);
882 }
883
884 #[test]
885 fn a_constant_column_costs_thirteen_bytes_however_long_it_is() {
886 let bytes = round_trip(&vec![42; 1_000_000]);
887 assert_eq!(kind_of(&bytes), Kind::Constant);
888 assert_eq!(bytes.len(), 13);
889 }
890
891 #[test]
892 fn a_narrow_range_is_packed_at_the_width_of_the_range_and_not_of_the_type() {
893 let mut random = Random::new();
895 let values: Vec<i64> = (0..100_000).map(|_| 1000 + (random.next() % 64) as i64).collect();
896 let bytes = round_trip(&values);
897 assert_eq!(kind_of(&bytes), Kind::Packed);
898 let packed = 100_000 * 6 / 8;
899 assert!(bytes.len() < packed + 2000, "{} bytes for {packed} of payload", bytes.len());
900 assert!(bytes.len() > packed, "{} bytes cannot hold {packed}", bytes.len());
901 }
902
903 #[test]
904 fn a_counter_becomes_deltas_and_then_a_constant() {
905 let values: Vec<i64> = (0..1_000_000).collect();
908 let bytes = round_trip(&values);
909 assert_eq!(kind_of(&bytes), Kind::Delta);
910 assert_eq!(describe(&bytes).unwrap(), "DELTA(CONSTANT)");
911 assert!(bytes.len() < 40, "{} bytes for a counter", bytes.len());
912 }
913
914 #[test]
915 fn a_column_that_counts_down_is_as_cheap_as_one_that_counts_up() {
916 let up: Vec<i64> = (0..100_000).collect();
918 let down: Vec<i64> = (0..100_000).rev().collect();
919 assert_eq!(round_trip(&up).len(), round_trip(&down).len());
920 }
921
922 #[test]
923 fn long_runs_become_rle() {
924 let mut values = Vec::new();
925 for run in 0..1000 {
926 values.extend(std::iter::repeat_n(run % 7, 200));
927 }
928 let bytes = round_trip(&values);
929 assert_eq!(kind_of(&bytes), Kind::Rle);
930 assert!(bytes.len() < 2000, "{} bytes for 1000 runs", bytes.len());
931 }
932
933 #[test]
934 fn a_low_cardinality_column_becomes_a_dictionary() {
935 let mut random = Random::new();
938 let dictionary: Vec<i64> = (0..40).map(|index| 1_000_000_000 + index * 7919).collect();
939 let values: Vec<i64> =
940 (0..100_000).map(|_| dictionary[(random.next() % 40) as usize]).collect();
941 let bytes = round_trip(&values);
942 assert_eq!(kind_of(&bytes), Kind::Dict);
943 assert!(bytes.len() < 100_000, "{} bytes", bytes.len());
944 }
945
946 #[test]
947 fn a_nearly_constant_column_becomes_sparse() {
948 let mut values = vec![0i64; 100_000];
949 for index in 0..300 {
950 values[index * 331] = 1 << 40;
951 }
952 let bytes = round_trip(&values);
953 assert_eq!(kind_of(&bytes), Kind::Sparse);
954 assert!(bytes.len() < 3000, "{} bytes for 300 exceptions", bytes.len());
955 }
956
957 #[test]
958 fn the_cascade_goes_more_than_one_level_deep() {
959 let mut values = Vec::new();
962 for index in 0..2000i64 {
963 values.extend(std::iter::repeat_n(1_000_000 + (index % 5) * 104_729, 100));
964 }
965 let bytes = round_trip(&values);
966 let shape = describe(&bytes).unwrap();
967 assert!(shape.contains('('), "{shape} is not a cascade");
968 assert!(bytes.len() < 4000, "{} bytes: {shape}", bytes.len());
969 }
970
971 #[test]
972 fn random_data_is_packed_at_full_width_and_costs_what_it_costs() {
973 let mut random = Random::new();
976 let values: Vec<i64> = (0..10_000).map(|_| random.next() as i64).collect();
977 let bytes = round_trip(&values);
978 assert_eq!(kind_of(&bytes), Kind::Packed);
979 assert!(bytes.len() < 10_000 * 8 + 1000, "{} bytes", bytes.len());
980 }
981
982 #[test]
983 fn the_extremes_of_the_type_survive() {
984 let values = vec![i64::MIN, i64::MAX, 0, -1, i64::MIN, i64::MAX];
987 round_trip(&values);
988 round_trip(&[i64::MIN; 3]);
989 round_trip(&[i64::MIN, i64::MIN + 1]);
990 }
991
992 #[test]
993 fn a_chunk_that_is_not_a_multiple_of_the_unit_round_trips() {
994 for len in [1, 2, 1023, 1024, 1025, 2047, 2049] {
995 let values: Vec<i64> = (0..len).map(|index| (index * 31 % 97) as i64).collect();
996 round_trip(&values);
997 }
998 }
999
1000 #[test]
1001 fn units_of_different_widths_in_one_chunk_do_not_read_each_others_leftovers() {
1002 let mut random = Random::new();
1014 let mut values = Vec::new();
1015 for width in [40u32, 3, 61, 1, 17, 40] {
1016 for _ in 0..1024 {
1017 values.push((random.next() & ((1u64 << width) - 1)) as i64);
1018 }
1019 }
1020 let bytes = encode(&values).unwrap();
1021 let described = describe(&bytes).unwrap();
1022 assert!(described.starts_with("FOR+BITPACK"), "expected one packed chunk, got {described}");
1023 assert_eq!(decode(&bytes).unwrap(), values, "{described}");
1024 }
1025
1026 #[test]
1027 fn a_cascade_decodes_the_same_through_a_shared_scratch_as_through_its_own() {
1028 let mut values = Vec::new();
1033 for index in 0..8192i64 {
1034 values.push(1_600_000_000 + index / 4 + (index % 7) * 1_000);
1035 }
1036 let bytes = encode(&values).unwrap();
1037 let described = describe(&bytes).unwrap();
1038 assert!(described.contains('('), "expected a cascade, got {described}");
1039 assert_eq!(decode(&bytes).unwrap(), values, "{described}");
1040 }
1041
1042 #[test]
1043 fn a_partial_unit_costs_its_own_values_and_not_a_whole_unit() {
1044 let values = vec![1i64 << 39, (1 << 39) + 7, 1 << 38];
1048 let bytes = encode_only(Kind::Packed, &values).unwrap().unwrap();
1049 assert_eq!(bytes.len(), 5 + 9 + 15);
1050 assert_eq!(decode(&bytes).unwrap(), values);
1051 }
1052
1053 #[test]
1054 fn the_frame_of_reference_is_per_unit_and_not_per_chunk() {
1055 let values: Vec<i64> =
1059 (0..4096i64).map(|index| (index / 1024) * 1_000_000 + (index % 1024)).collect();
1060 let bytes = encode_only(Kind::Packed, &values).unwrap().unwrap();
1061 assert_eq!(describe(&bytes).unwrap(), "FOR+BITPACK[10]");
1062 assert_eq!(decode(&bytes).unwrap(), values);
1063 }
1064
1065 #[test]
1066 fn every_candidate_that_applies_decodes_to_the_input() {
1067 let mut values = vec![5i64; 3000];
1071 for (index, value) in values.iter_mut().enumerate() {
1072 if index % 500 == 0 {
1073 *value = index as i64;
1074 }
1075 }
1076 let applicable = candidates(&values, 0);
1077 assert!(applicable.len() >= 4, "{applicable:?}");
1078 for kind in applicable {
1079 let bytes = encode_only(kind, &values).unwrap().unwrap();
1080 assert_eq!(decode(&bytes).unwrap(), values, "{}", kind.name());
1081 }
1082 }
1083
1084 #[test]
1089 fn every_kind_that_applies_decodes_to_what_it_was_given() {
1090 let shapes: Vec<Vec<i64>> = vec![
1091 Vec::new(),
1092 vec![5; 1024],
1093 vec![i64::MIN, i64::MAX, 0, -1],
1094 (0..1024).map(|at| at * 7).collect(),
1095 (0..1024).map(|at| at % 17).collect(),
1096 (0..1024).map(|at| if at % 100 == 0 { at } else { 3 }).collect(),
1097 (0..1024).map(|at| -at * 1_000_003).collect(),
1098 (0..1024_i64)
1099 .map(|at| {
1100 at.wrapping_mul(6_364_136_223_846_793_005)
1101 .wrapping_add(1_442_695_040_888_963_407)
1102 })
1103 .collect(),
1104 ];
1105 let kinds =
1106 [Kind::Constant, Kind::Packed, Kind::Delta, Kind::Rle, Kind::Dict, Kind::Sparse];
1107 for values in &shapes {
1108 for kind in kinds {
1109 let Some(bytes) = encode_only(kind, values).unwrap() else {
1110 continue;
1111 };
1112 assert_eq!(
1113 &decode(&bytes).unwrap(),
1114 values,
1115 "{} over {} values",
1116 kind.name(),
1117 values.len()
1118 );
1119 }
1120 }
1121 }
1122
1123 #[test]
1124 fn the_chooser_picks_the_smallest_candidate_rather_than_the_first_that_applies() {
1125 let mut values = vec![5i64; 3000];
1126 values[1500] = 9;
1127 let chosen = encode(&values).unwrap();
1128 for (_, size) in candidate_sizes(&values).unwrap() {
1129 assert!(chosen.len() <= size);
1130 }
1131 }
1132
1133 #[test]
1134 fn a_truncated_chunk_is_an_error_and_not_a_panic() {
1135 let bytes = encode(&[1, 2, 3, 4, 5]).unwrap();
1136 for len in 0..bytes.len() {
1137 let error = decode(&bytes[..len]).unwrap_err();
1138 assert!(error.message().contains("chunk"), "{error}");
1139 }
1140 }
1141
1142 #[test]
1143 fn trailing_bytes_are_an_error() {
1144 let mut bytes = encode(&[1, 2, 3]).unwrap();
1145 bytes.push(0);
1146 let error = decode(&bytes).unwrap_err();
1147 assert!(error.message().contains("left over"), "{error}");
1148 }
1149
1150 #[test]
1151 fn an_unknown_tag_is_an_error() {
1152 let error = decode(&[99, 0, 0, 0, 0]).unwrap_err();
1153 assert!(error.message().contains("unknown encoding tag"), "{error}");
1154 }
1155
1156 #[test]
1157 fn a_dictionary_code_outside_the_dictionary_is_an_error() {
1158 let mut bytes = vec![Kind::Dict.tag()];
1163 put_u32(&mut bytes, 1);
1164 bytes.extend_from_slice(&encode(&[10]).unwrap());
1165 bytes.extend_from_slice(&encode(&[5]).unwrap());
1166 let error = decode(&bytes).unwrap_err();
1167 assert!(error.message().contains("not in the dictionary"), "{error}");
1168 }
1169
1170 #[test]
1171 fn a_negative_run_length_is_an_error() {
1172 let mut bytes = vec![Kind::Rle.tag()];
1175 put_u32(&mut bytes, 4);
1176 bytes.extend_from_slice(&encode(&[7]).unwrap());
1177 bytes.extend_from_slice(&encode(&[-4]).unwrap());
1178 let error = decode(&bytes).unwrap_err();
1179 assert!(error.message().contains("negative"), "{error}");
1180 }
1181
1182 #[test]
1183 fn the_cascade_depth_is_bounded() {
1184 let values: Vec<i64> = (0..50_000).map(|index| (index / 100) % 250).collect();
1188 let bytes = round_trip(&values);
1189 let shape = describe(&bytes).unwrap();
1190 let depth = shape.matches('(').count();
1191 assert!(depth <= MAX_DEPTH as usize, "{shape} is {depth} deep");
1192 }
1193
1194 #[test]
1195 fn candidate_sizes_reports_what_the_chooser_looked_at() {
1196 let values: Vec<i64> = (0..5000).map(|index| index % 17).collect();
1197 let sizes = candidate_sizes(&values).unwrap();
1198 assert!(sizes.iter().any(|(kind, _)| *kind == Kind::Dict));
1199 assert!(sizes.iter().any(|(kind, _)| *kind == Kind::Packed));
1200 assert!(sizes.iter().all(|(_, size)| *size > 0));
1201 }
1202
1203 #[test]
1204 fn a_chunk_can_be_read_from_the_front_of_a_longer_buffer() {
1205 let first = encode(&[1, 2, 3]).unwrap();
1208 let second: Vec<i64> = (0..3000).map(|index| index % 11).collect();
1209 let second_bytes = encode(&second).unwrap();
1210 let mut joined = first.clone();
1211 joined.extend_from_slice(&second_bytes);
1212 joined.extend_from_slice(b"and then something else");
1213
1214 let (values, used) = decode_prefix(&joined).unwrap();
1215 assert_eq!(values, vec![1, 2, 3]);
1216 assert_eq!(used, first.len());
1217 let (more, used_again) = decode_prefix(&joined[used..]).unwrap();
1218 assert_eq!(more, second);
1219 assert_eq!(used_again, second_bytes.len());
1220
1221 let (text, described) = describe_prefix(&joined).unwrap();
1222 assert_eq!(described, first.len());
1223 assert_eq!(text, describe(&first).unwrap());
1224 }
1225
1226 #[test]
1227 fn a_truncated_chunk_is_still_an_error_when_read_as_a_prefix() {
1228 let bytes = encode(&(0..2000).collect::<Vec<i64>>()).unwrap();
1229 for len in 0..bytes.len() {
1230 assert!(decode_prefix(&bytes[..len]).is_err(), "{len} bytes decoded");
1231 }
1232 }
1233}