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
59const RUN: usize = 8;
69
70#[derive(Debug, Clone, Copy, PartialEq, Eq)]
73pub enum Kind {
74 Constant = 0,
76 Packed = 1,
79 Delta = 2,
82 Rle = 3,
84 Dict = 4,
87 Sparse = 5,
89 Strided = 6,
92}
93
94impl Kind {
95 pub const ALL: [Self; 7] = [
97 Self::Constant,
98 Self::Packed,
99 Self::Delta,
100 Self::Rle,
101 Self::Dict,
102 Self::Sparse,
103 Self::Strided,
104 ];
105
106 fn tag(self) -> u8 {
107 self as u8
108 }
109
110 fn from_tag(tag: u8) -> Result<Self> {
111 match tag {
112 0 => Ok(Self::Constant),
113 1 => Ok(Self::Packed),
114 2 => Ok(Self::Delta),
115 3 => Ok(Self::Rle),
116 4 => Ok(Self::Dict),
117 5 => Ok(Self::Sparse),
118 6 => Ok(Self::Strided),
119 other => Err(Error::internal(format!("unknown encoding tag {other}"))),
120 }
121 }
122
123 #[must_use]
125 pub fn name(self) -> &'static str {
126 match self {
127 Self::Constant => "CONSTANT",
128 Self::Packed => "FOR+BITPACK",
129 Self::Delta => "DELTA",
130 Self::Rle => "RLE",
131 Self::Dict => "DICT",
132 Self::Sparse => "SPARSE",
133 Self::Strided => "STRIDE",
134 }
135 }
136}
137
138pub fn encode(values: &[i64]) -> Result<Vec<u8>> {
145 encode_with(values, &EXHAUSTIVE)
146}
147
148pub fn encode_with(values: &[i64], chooser: &dyn Chooser) -> Result<Vec<u8>> {
158 encode_at(values, 0, chooser)
159}
160
161pub fn decode(bytes: &[u8]) -> Result<Vec<i64>> {
168 let mut reader = Reader::new(bytes);
169 let values = with_decoding(|scratch| decode_chunk(&mut reader, scratch))?;
170 if reader.remaining() != 0 {
171 return Err(Error::internal(format!(
172 "{} bytes left over after decoding a chunk",
173 reader.remaining()
174 )));
175 }
176 Ok(values)
177}
178
179pub fn decode_selected(bytes: &[u8], positions: &[usize]) -> Result<Vec<i64>> {
190 if positions.windows(2).any(|pair| pair[0] >= pair[1]) {
191 return Err(Error::internal("selected integer positions are not sorted and unique"));
192 }
193 let mut reader = Reader::new(bytes);
194 let values = with_decoding(|scratch| decode_selected_chunk(&mut reader, positions, scratch))?;
195 if reader.remaining() != 0 {
196 return Err(Error::internal(format!(
197 "{} bytes left over after decoding selected values",
198 reader.remaining()
199 )));
200 }
201 Ok(values)
202}
203
204pub fn decode_prefix(bytes: &[u8]) -> Result<(Vec<i64>, usize)> {
214 let mut reader = Reader::new(bytes);
215 let values = with_decoding(|scratch| decode_chunk(&mut reader, scratch))?;
216 Ok((values, reader.used()))
217}
218
219pub fn describe_prefix(bytes: &[u8]) -> Result<(String, usize)> {
225 let mut reader = Reader::new(bytes);
226 let text = describe_chunk(&mut reader)?;
227 Ok((text, reader.used()))
228}
229
230pub fn candidate_sizes(values: &[i64]) -> Result<Vec<(Kind, usize)>> {
237 let mut sizes = Vec::new();
238 for kind in candidates(values, 0, &EXHAUSTIVE) {
239 if let Some(bytes) = encode_as(kind, values, 0, &EXHAUSTIVE)? {
240 sizes.push((kind, bytes.len()));
241 }
242 }
243 Ok(sizes)
244}
245
246#[must_use]
253pub fn offered(values: &[i64]) -> Vec<Kind> {
254 candidates(values, 0, &EXHAUSTIVE)
255}
256
257pub fn encode_only(kind: Kind, values: &[i64]) -> Result<Option<Vec<u8>>> {
268 encode_as(kind, values, 0, &EXHAUSTIVE)
269}
270
271pub(crate) fn size_as(kind: Kind, values: &[i64], depth: u8) -> Result<Option<usize>> {
276 Ok(encode_as(kind, values, depth, &EXHAUSTIVE)?.map(|bytes| bytes.len()))
277}
278
279pub fn shape(bytes: &[u8]) -> Result<Vec<Kind>> {
291 let mut reader = Reader::new(bytes);
292 let mut kinds = Vec::new();
293 shape_chunk(&mut reader, &mut kinds)?;
294 Ok(kinds)
295}
296
297pub fn describe(bytes: &[u8]) -> Result<String> {
303 let mut reader = Reader::new(bytes);
304 describe_chunk(&mut reader)
305}
306
307fn encode_at(values: &[i64], depth: u8, chooser: &dyn Chooser) -> Result<Vec<u8>> {
308 let offered = candidates(values, depth, chooser);
309 let mut best: Option<Vec<u8>> = None;
310 for kind in chooser.narrow_integers(values, &offered, depth) {
311 let Some(bytes) = encode_as(kind, values, depth, chooser)? else {
312 continue;
313 };
314 if best.as_ref().is_none_or(|current| bytes.len() < current.len()) {
315 best = Some(bytes);
316 }
317 }
318 best.ok_or_else(|| Error::internal("no encoding applied to the chunk"))
321}
322
323fn candidates(values: &[i64], depth: u8, chooser: &dyn Chooser) -> Vec<Kind> {
334 let mut kinds = vec![Kind::Packed];
335 if depth >= MAX_DEPTH || values.is_empty() {
336 return kinds;
337 }
338 if values.iter().all(|value| *value == values[0]) {
339 return vec![Kind::Constant];
341 }
342 let considered = |kind| chooser.considers_integer(kind, depth);
343 if considered(Kind::Delta) && values.len() >= 2 && deltas_fit(values) {
344 kinds.push(Kind::Delta);
345 }
346 if considered(Kind::Rle) && run_count(values) * 4 <= values.len() * 3 {
347 kinds.push(Kind::Rle);
348 }
349 if considered(Kind::Dict) && spread_of(values).0 * 2 <= values.len() {
350 kinds.push(Kind::Dict);
351 }
352 if considered(Kind::Sparse)
353 && majority(values).is_some_and(|(_, count)| count * 10 >= values.len() * 8)
354 {
355 kinds.push(Kind::Sparse);
356 }
357 if considered(Kind::Strided) && stride_of(values).is_some() {
358 kinds.push(Kind::Strided);
359 }
360 kinds
361}
362
363fn encode_as(
366 kind: Kind,
367 values: &[i64],
368 depth: u8,
369 chooser: &dyn Chooser,
370) -> Result<Option<Vec<u8>>> {
371 let mut out = Vec::new();
372 put_u8(&mut out, kind.tag());
373 put_u32(&mut out, u32::try_from(values.len()).map_err(|_| too_long(values.len()))?);
374 match kind {
375 Kind::Constant => {
376 let Some(first) = values.first() else {
377 return Ok(None);
378 };
379 if values.iter().any(|value| value != first) {
380 return Ok(None);
381 }
382 put_i64(&mut out, *first);
383 }
384 Kind::Packed => encode_packed(values, &mut out)?,
385 Kind::Delta => {
386 let (Some(first), Some(deltas)) = (values.first(), deltas(values)) else {
390 return Ok(None);
391 };
392 put_i64(&mut out, *first);
393 out.extend_from_slice(&encode_at(&deltas, depth + 1, chooser)?);
394 }
395 Kind::Rle => {
396 let (run_values, run_lengths) = runs(values);
397 if run_values.is_empty() {
398 return Ok(None);
399 }
400 out.extend_from_slice(&encode_at(&run_values, depth + 1, chooser)?);
401 out.extend_from_slice(&encode_at(&run_lengths, depth + 1, chooser)?);
402 }
403 Kind::Dict => {
404 let dictionary = distinct_values(values);
405 if dictionary.is_empty() {
406 return Ok(None);
407 }
408 let codes = codes_over(values, &dictionary);
409 out.extend_from_slice(&encode_at(&dictionary, depth + 1, chooser)?);
410 out.extend_from_slice(&encode_at(&codes, depth + 1, chooser)?);
411 }
412 Kind::Sparse => {
413 let Some((value, _)) = majority(values).or_else(|| spread_of(values).1) else {
417 return Ok(None);
418 };
419 let mut positions = Vec::new();
420 let mut exceptions = Vec::new();
421 for (index, other) in values.iter().enumerate() {
422 if *other != value {
423 positions.push(index as i64);
424 exceptions.push(*other);
425 }
426 }
427 put_i64(&mut out, value);
428 put_u32(
429 &mut out,
430 u32::try_from(positions.len()).map_err(|_| too_long(positions.len()))?,
431 );
432 out.extend_from_slice(&encode_at(&positions, depth + 1, chooser)?);
433 out.extend_from_slice(&encode_at(&exceptions, depth + 1, chooser)?);
434 }
435 Kind::Strided => {
436 let (Some(base), Some(stride)) = (values.iter().min().copied(), stride_of(values))
437 else {
438 return Ok(None);
439 };
440 let mut steps = Vec::with_capacity(values.len());
441 for value in values {
442 let step = offset_from(*value, base) / stride;
443 let Ok(step) = i64::try_from(step) else {
448 return Ok(None);
449 };
450 steps.push(step);
451 }
452 put_i64(&mut out, base);
453 put_u64(&mut out, stride);
454 out.extend_from_slice(&encode_at(&steps, depth + 1, chooser)?);
455 }
456 }
457 Ok(Some(out))
458}
459
460fn encode_packed(values: &[i64], out: &mut Vec<u8>) -> Result<()> {
472 let mut offsets: Vec<u64> = Vec::with_capacity(VALUES);
476 let mut packed: Vec<u64> = vec![0; bitpack::packed_len::<u64>(64)];
479 let mut transposed = bitpack::Scratch::<u64>::new();
480 for unit in values.chunks(VALUES) {
481 let base = unit.iter().copied().min().unwrap_or(0);
482 offsets.clear();
483 offsets.extend(unit.iter().map(|value| offset_from(*value, base)));
484 let width = bitpack::required_width(&offsets);
485 put_i64(out, base);
486 put_u8(out, u8::try_from(width).map_err(|_| Error::internal("impossible width"))?);
487 if unit.len() == VALUES {
488 let words = bitpack::packed_len::<u64>(width);
489 bitpack::pack_with(&offsets, width, &mut packed[..words], &mut transposed)?;
490 for word in &packed[..words] {
491 put_u64(out, *word);
492 }
493 } else {
494 bitpack::pack_tail(&offsets, width, out)?;
495 }
496 }
497 Ok(())
498}
499
500struct Decoding {
527 packed: Vec<u64>,
530}
531
532thread_local! {
533 static DECODING: std::cell::RefCell<Decoding> =
535 const { std::cell::RefCell::new(Decoding::new()) };
536}
537
538fn with_decoding<T>(run: impl FnOnce(&mut Decoding) -> T) -> T {
545 DECODING.with(|cell| match cell.try_borrow_mut() {
546 Ok(mut scratch) => run(&mut scratch),
547 Err(_) => run(&mut Decoding::new()),
548 })
549}
550
551impl Decoding {
552 const fn new() -> Self {
554 Self { packed: Vec::new() }
555 }
556
557 fn ready(&mut self) {
559 if self.packed.len() != bitpack::packed_len::<u64>(64) {
560 self.packed.resize(bitpack::packed_len::<u64>(64), 0);
561 }
562 }
563}
564
565fn decode_chunk(reader: &mut Reader<'_>, scratch: &mut Decoding) -> Result<Vec<i64>> {
566 let kind = Kind::from_tag(reader.u8()?)?;
567 let count = reader.u32()? as usize;
568 match kind {
569 Kind::Constant => Ok(vec![reader.i64()?; count]),
570 Kind::Packed => {
571 let mut values = vec![0i64; count];
575 scratch.ready();
576 let mut done = 0;
577 while done < count {
578 let base = reader.i64()?;
579 let width = reader.u8()? as usize;
580 let wanted = (count - done).min(VALUES);
581 let into = &mut values[done..done + wanted];
582 if wanted == VALUES {
583 let words = bitpack::packed_len::<u64>(width);
584 for word in &mut scratch.packed[..words] {
585 *word = reader.u64()?;
586 }
587 bitpack::unpack_mapped(&scratch.packed[..words], width, into, |offset| {
588 value_from(offset, base)
589 })?;
590 } else {
591 let bytes = reader.bytes(bitpack::tail_len(wanted, width))?;
592 bitpack::unpack_tail_into(bytes, width, into, |offset| {
593 value_from(offset, base)
594 })?;
595 }
596 done += wanted;
597 }
598 Ok(values)
599 }
600 Kind::Delta => {
601 let first = reader.i64()?;
602 let deltas = decode_chunk(reader, scratch)?;
603 let mut values = Vec::with_capacity(count);
604 values.push(first);
605 let mut current = first;
606 for delta in deltas {
607 current = current.wrapping_add(unzigzag(delta as u64));
608 values.push(current);
609 }
610 check_count(values.len(), count)?;
611 Ok(values)
612 }
613 Kind::Rle => {
614 let run_values = decode_chunk(reader, scratch)?;
615 let run_lengths = decode_chunk(reader, scratch)?;
616 if run_values.len() != run_lengths.len() {
617 return Err(Error::internal("an RLE chunk has more runs than run lengths"));
618 }
619 let mut values = vec![0; count + RUN];
622 let mut at = 0usize;
623 for (value, length) in run_values.into_iter().zip(run_lengths) {
624 let length = usize::try_from(length)
625 .map_err(|_| Error::internal("a negative RLE run length"))?;
626 let end = at
627 .checked_add(length)
628 .filter(|end| *end <= count)
629 .ok_or_else(|| Error::internal("an RLE run ends past its chunk"))?;
630 let short =
631 if length <= RUN { values[at..].first_chunk_mut::<RUN>() } else { None };
632 match short {
633 Some(window) => window.fill(value),
634 None => values[at..end].fill(value),
635 }
636 at = end;
637 }
638 check_count(at, count)?;
639 values.truncate(count);
640 Ok(values)
641 }
642 Kind::Dict => {
643 let dictionary = decode_chunk(reader, scratch)?;
644 let codes = decode_chunk(reader, scratch)?;
645 let mut values = Vec::with_capacity(count);
646 for code in codes {
647 let index =
648 usize::try_from(code).ok().and_then(|index| dictionary.get(index)).ok_or_else(
649 || Error::internal(format!("code {code} is not in the dictionary")),
650 )?;
651 values.push(*index);
652 }
653 check_count(values.len(), count)?;
654 Ok(values)
655 }
656 Kind::Sparse => {
657 let value = reader.i64()?;
658 let exception_count = reader.u32()? as usize;
659 let positions = decode_chunk(reader, scratch)?;
660 let exceptions = decode_chunk(reader, scratch)?;
661 if positions.len() != exception_count || exceptions.len() != exception_count {
662 return Err(Error::internal("a sparse chunk disagrees about its exception count"));
663 }
664 let mut values = vec![value; count];
665 for (position, exception) in positions.into_iter().zip(exceptions) {
666 let position = usize::try_from(position)
667 .ok()
668 .filter(|position| *position < count)
669 .ok_or_else(|| {
670 Error::internal(format!("exception at {position} is outside the chunk"))
671 })?;
672 values[position] = exception;
673 }
674 Ok(values)
675 }
676 Kind::Strided => {
677 let base = reader.i64()?;
678 let stride = reader.u64()?;
679 let steps = decode_chunk(reader, scratch)?;
680 check_count(steps.len(), count)?;
681 let mut values = Vec::with_capacity(count);
682 for step in steps {
683 let step = u64::try_from(step)
684 .map_err(|_| Error::internal("a negative number of strides"))?;
685 values.push(value_from(step.wrapping_mul(stride), base));
686 }
687 Ok(values)
688 }
689 }
690}
691
692fn decode_selected_chunk(
693 reader: &mut Reader<'_>,
694 positions: &[usize],
695 scratch: &mut Decoding,
696) -> Result<Vec<i64>> {
697 let Some(&tag) = reader.rest().first() else {
698 return Err(Error::internal("a chunk ended before its encoding tag"));
699 };
700 let kind = Kind::from_tag(tag)?;
701 if !matches!(kind, Kind::Constant | Kind::Packed | Kind::Rle) {
702 let values = decode_chunk(reader, scratch)?;
703 return positions
704 .iter()
705 .map(|&position| {
706 values.get(position).copied().ok_or_else(|| {
707 Error::internal(format!(
708 "selected integer position {position} is outside {} values",
709 values.len()
710 ))
711 })
712 })
713 .collect();
714 }
715
716 let decoded = Kind::from_tag(reader.u8()?)?;
717 debug_assert_eq!(decoded, kind);
718 let count = reader.u32()? as usize;
719 if positions.last().is_some_and(|&position| position >= count) {
720 return Err(Error::internal(format!(
721 "selected integer position {} is outside {count} values",
722 positions.last().expect("a last position exists")
723 )));
724 }
725 match kind {
726 Kind::Constant => {
727 let value = reader.i64()?;
728 Ok(vec![value; positions.len()])
729 }
730 Kind::Packed => {
731 let mut out = Vec::with_capacity(positions.len());
732 let mut from = 0;
733 let mut done = 0;
734 while done < count {
735 let base = reader.i64()?;
736 let width = reader.u8()? as usize;
737 let wanted = (count - done).min(VALUES);
738 let upto = positions.partition_point(|&position| position < done + wanted);
739 if wanted == VALUES {
740 let bytes = reader.bytes(bitpack::packed_len::<u64>(width) * 8)?;
741 for &position in &positions[from..upto] {
742 let offset = bitpack::unpack_u64_at(bytes, width, position - done)?;
743 out.push(value_from(offset, base));
744 }
745 } else {
746 let bytes = reader.bytes(bitpack::tail_len(wanted, width))?;
747 for &position in &positions[from..upto] {
748 let offset = bitpack::tail_at(bytes, width, position - done)?;
749 out.push(value_from(offset, base));
750 }
751 }
752 from = upto;
753 done += wanted;
754 }
755 Ok(out)
756 }
757 Kind::Rle => {
758 let run_value_bytes = reader.rest();
759 let mut run_value_reader = Reader::new(run_value_bytes);
760 let run_value_count = skip_chunk(&mut run_value_reader)?;
761 let run_value_len = run_value_reader.used();
762 reader.skip(run_value_len)?;
763 let run_lengths = decode_chunk(reader, scratch)?;
764 if run_value_count != run_lengths.len() {
765 return Err(Error::internal("an RLE chunk has more runs than run lengths"));
766 }
767 let mut wanted_runs = Vec::new();
768 let mut selected_per_run = Vec::new();
769 let mut selected = 0;
770 let mut at = 0usize;
771 for (run, length) in run_lengths.into_iter().enumerate() {
772 let length = usize::try_from(length)
773 .map_err(|_| Error::internal("a negative RLE run length"))?;
774 let end = at
775 .checked_add(length)
776 .filter(|end| *end <= count)
777 .ok_or_else(|| Error::internal("an RLE run ends past its chunk"))?;
778 let before = selected;
779 while selected < positions.len() && positions[selected] < end {
780 if positions[selected] < at {
781 return Err(Error::internal("selected integer positions went backwards"));
782 }
783 selected += 1;
784 }
785 if selected != before {
786 wanted_runs.push(run);
787 selected_per_run.push(selected - before);
788 }
789 at = end;
790 }
791 check_count(at, count)?;
792 if selected != positions.len() {
793 return Err(Error::internal("an RLE chunk ended before a selected position"));
794 }
795 let run_values = decode_selected(&run_value_bytes[..run_value_len], &wanted_runs)?;
796 let mut out = Vec::with_capacity(positions.len());
797 for (value, repeat) in run_values.into_iter().zip(selected_per_run) {
798 out.extend(std::iter::repeat_n(value, repeat));
799 }
800 Ok(out)
801 }
802 _ => unreachable!("unsupported kinds used the full decoder"),
803 }
804}
805
806fn skip_chunk(reader: &mut Reader<'_>) -> Result<usize> {
808 let kind = Kind::from_tag(reader.u8()?)?;
809 let count = reader.u32()? as usize;
810 match kind {
811 Kind::Constant => reader.skip(8)?,
812 Kind::Packed => skip_packed(reader, count)?,
813 Kind::Delta => {
814 reader.skip(8)?;
815 skip_chunk(reader)?;
816 }
817 Kind::Rle | Kind::Dict => {
818 skip_chunk(reader)?;
819 skip_chunk(reader)?;
820 }
821 Kind::Sparse => {
822 reader.skip(12)?;
823 skip_chunk(reader)?;
824 skip_chunk(reader)?;
825 }
826 Kind::Strided => {
827 reader.skip(16)?;
828 skip_chunk(reader)?;
829 }
830 }
831 Ok(count)
832}
833
834fn skip_packed(reader: &mut Reader<'_>, count: usize) -> Result<()> {
836 let mut done = 0;
837 while done < count {
838 reader.skip(8)?;
839 let width = reader.u8()? as usize;
840 if width > 64 {
841 return Err(Error::internal(format!("a packed integer width of {width} is past 64")));
842 }
843 let wanted = (count - done).min(VALUES);
844 let bytes = if wanted == VALUES {
845 bitpack::packed_len::<u64>(width)
846 .checked_mul(8)
847 .ok_or_else(|| Error::internal("packed integer size overflow"))?
848 } else {
849 bitpack::tail_len(wanted, width)
850 };
851 reader.skip(bytes)?;
852 done += wanted;
853 }
854 Ok(())
855}
856
857fn shape_chunk(reader: &mut Reader<'_>, kinds: &mut Vec<Kind>) -> Result<()> {
859 let kind = Kind::from_tag(reader.u8()?)?;
860 let count = reader.u32()? as usize;
861 kinds.push(kind);
862 match kind {
863 Kind::Constant => reader.skip(8)?,
864 Kind::Packed => skip_packed(reader, count)?,
865 Kind::Delta => {
866 reader.skip(8)?;
867 shape_chunk(reader, kinds)?;
868 }
869 Kind::Rle | Kind::Dict => {
870 shape_chunk(reader, kinds)?;
871 shape_chunk(reader, kinds)?;
872 }
873 Kind::Sparse => {
874 reader.skip(12)?;
875 shape_chunk(reader, kinds)?;
876 shape_chunk(reader, kinds)?;
877 }
878 Kind::Strided => {
879 reader.skip(16)?;
880 shape_chunk(reader, kinds)?;
881 }
882 }
883 Ok(())
884}
885
886fn describe_chunk(reader: &mut Reader<'_>) -> Result<String> {
887 let kind = Kind::from_tag(reader.u8()?)?;
888 let count = reader.u32()? as usize;
889 Ok(match kind {
890 Kind::Constant => {
891 reader.i64()?;
892 "CONSTANT".to_string()
893 }
894 Kind::Packed => {
895 let mut widths = Vec::new();
896 let mut seen = 0;
897 while seen < count {
898 reader.i64()?;
899 let width = reader.u8()? as usize;
900 let wanted = (count - seen).min(VALUES);
901 if wanted == VALUES {
902 for _ in 0..bitpack::packed_len::<u64>(width) {
903 reader.u64()?;
904 }
905 } else {
906 reader.bytes(bitpack::tail_len(wanted, width))?;
907 }
908 widths.push(width);
909 seen += wanted;
910 }
911 let low = widths.iter().copied().min().unwrap_or(0);
912 let high = widths.iter().copied().max().unwrap_or(0);
913 if low == high {
916 format!("FOR+BITPACK[{low}]")
917 } else {
918 format!("FOR+BITPACK[{low}..{high}]")
919 }
920 }
921 Kind::Delta => {
922 reader.i64()?;
923 format!("DELTA({})", describe_chunk(reader)?)
924 }
925 Kind::Rle => {
926 let values = describe_chunk(reader)?;
927 let lengths = describe_chunk(reader)?;
928 format!("RLE({values}, {lengths})")
929 }
930 Kind::Dict => {
931 let dictionary = describe_chunk(reader)?;
932 let codes = describe_chunk(reader)?;
933 format!("DICT({dictionary}, {codes})")
934 }
935 Kind::Sparse => {
936 reader.i64()?;
937 reader.u32()?;
938 let positions = describe_chunk(reader)?;
939 let exceptions = describe_chunk(reader)?;
940 format!("SPARSE({positions}, {exceptions})")
941 }
942 Kind::Strided => {
943 reader.i64()?;
944 let stride = reader.u64()?;
945 format!("STRIDE[{stride}]({})", describe_chunk(reader)?)
946 }
947 })
948}
949
950fn stride_of(values: &[i64]) -> Option<u64> {
962 let base = values.iter().min().copied()?;
963 let mut divisor = 0u64;
964 for value in values {
965 divisor = gcd(divisor, offset_from(*value, base));
966 if divisor == 1 {
967 return None;
968 }
969 }
970 (divisor > 1).then_some(divisor)
973}
974
975fn gcd(mut left: u64, mut right: u64) -> u64 {
977 if left == 0 {
978 return right;
979 }
980 if right == 0 {
981 return left;
982 }
983 let shift = (left | right).trailing_zeros();
984 left >>= left.trailing_zeros();
985 loop {
986 right >>= right.trailing_zeros();
987 if left > right {
988 std::mem::swap(&mut left, &mut right);
989 }
990 right -= left;
991 if right == 0 {
992 return left << shift;
993 }
994 }
995}
996
997fn offset_from(value: i64, base: i64) -> u64 {
1000 (i128::from(value) - i128::from(base)) as u64
1001}
1002
1003fn value_from(offset: u64, base: i64) -> i64 {
1004 (i128::from(base) + i128::from(offset)) as i64
1005}
1006
1007fn zigzag(value: i64) -> u64 {
1010 ((value << 1) ^ (value >> 63)) as u64
1011}
1012
1013fn unzigzag(value: u64) -> i64 {
1014 ((value >> 1) as i64) ^ -((value & 1) as i64)
1015}
1016
1017fn deltas_fit(values: &[i64]) -> bool {
1029 values.windows(2).all(|pair| i64::try_from(i128::from(pair[1]) - i128::from(pair[0])).is_ok())
1030}
1031
1032fn deltas(values: &[i64]) -> Option<Vec<i64>> {
1033 let mut deltas = Vec::with_capacity(values.len().saturating_sub(1));
1034 for pair in values.windows(2) {
1035 let difference = i128::from(pair[1]) - i128::from(pair[0]);
1036 let difference = i64::try_from(difference).ok()?;
1037 deltas.push(zigzag(difference) as i64);
1038 }
1039 Some(deltas)
1040}
1041
1042fn run_count(values: &[i64]) -> usize {
1043 let mut runs = 0;
1044 let mut previous = None;
1045 for value in values {
1046 if previous != Some(value) {
1047 runs += 1;
1048 previous = Some(value);
1049 }
1050 }
1051 runs
1052}
1053
1054fn runs(values: &[i64]) -> (Vec<i64>, Vec<i64>) {
1055 let mut run_values: Vec<i64> = Vec::new();
1056 let mut run_lengths: Vec<i64> = Vec::new();
1057 for value in values {
1058 if run_values.last() == Some(value) {
1059 *run_lengths.last_mut().expect("a run length exists beside every run value") += 1;
1060 } else {
1061 run_values.push(*value);
1062 run_lengths.push(1);
1063 }
1064 }
1065 (run_values, run_lengths)
1066}
1067
1068fn spread_of(values: &[i64]) -> (usize, Option<(i64, usize)>) {
1083 let mut sorted = values.to_vec();
1084 sorted.sort_unstable();
1085 let mut distinct = 0;
1086 let mut best: Option<(i64, usize)> = None;
1087 let mut index = 0;
1088 while index < sorted.len() {
1089 let value = sorted[index];
1090 let mut end = index;
1091 while end < sorted.len() && sorted[end] == value {
1092 end += 1;
1093 }
1094 distinct += 1;
1095 let count = end - index;
1096 if best.is_none_or(|(_, seen)| count > seen) {
1097 best = Some((value, count));
1098 }
1099 index = end;
1100 }
1101 (distinct, best)
1102}
1103
1104fn majority(values: &[i64]) -> Option<(i64, usize)> {
1112 let mut candidate = *values.first()?;
1113 let mut lead = 0usize;
1114 for value in values {
1115 if lead == 0 {
1116 candidate = *value;
1117 lead = 1;
1118 } else if *value == candidate {
1119 lead += 1;
1120 } else {
1121 lead -= 1;
1122 }
1123 }
1124 let count = values.iter().filter(|value| **value == candidate).count();
1125 (count * 2 > values.len()).then_some((candidate, count))
1126}
1127
1128fn distinct_values(values: &[i64]) -> Vec<i64> {
1131 let mut distinct = values.to_vec();
1132 distinct.sort_unstable();
1133 distinct.dedup();
1134 distinct
1135}
1136
1137fn codes_over(values: &[i64], dictionary: &[i64]) -> Vec<i64> {
1147 values
1148 .iter()
1149 .map(|value| {
1150 dictionary
1151 .binary_search(value)
1152 .expect("the dictionary is the distinct values of this chunk") as i64
1153 })
1154 .collect()
1155}
1156
1157fn check_count(actual: usize, expected: usize) -> Result<()> {
1158 if actual == expected {
1159 Ok(())
1160 } else {
1161 Err(Error::internal(format!(
1162 "a chunk says it holds {expected} values and decoded to {actual}"
1163 )))
1164 }
1165}
1166
1167fn too_long(len: usize) -> Error {
1168 Error::internal(format!("a chunk of {len} values is longer than the format allows"))
1169}
1170
1171fn put_u8(out: &mut Vec<u8>, value: u8) {
1172 out.push(value);
1173}
1174
1175fn put_u32(out: &mut Vec<u8>, value: u32) {
1176 out.extend_from_slice(&value.to_le_bytes());
1177}
1178
1179fn put_u64(out: &mut Vec<u8>, value: u64) {
1180 out.extend_from_slice(&value.to_le_bytes());
1181}
1182
1183fn put_i64(out: &mut Vec<u8>, value: i64) {
1184 out.extend_from_slice(&value.to_le_bytes());
1185}
1186
1187#[cfg(test)]
1188mod tests {
1189 use super::*;
1190
1191 fn round_trip(values: &[i64]) -> Vec<u8> {
1192 let bytes = encode(values).unwrap();
1193 assert_eq!(decode(&bytes).unwrap(), values, "{}", describe(&bytes).unwrap());
1194 bytes
1195 }
1196
1197 fn kind_of(bytes: &[u8]) -> Kind {
1198 Kind::from_tag(bytes[0]).unwrap()
1199 }
1200
1201 struct Random(u64);
1203
1204 impl Random {
1205 fn new() -> Self {
1206 Self(0x9e37_79b9_7f4a_7c15)
1207 }
1208
1209 fn next(&mut self) -> u64 {
1210 self.0 ^= self.0 << 13;
1211 self.0 ^= self.0 >> 7;
1212 self.0 ^= self.0 << 17;
1213 self.0
1214 }
1215 }
1216
1217 #[test]
1218 fn the_dictionary_is_sorted_and_the_codes_point_back_at_the_values() {
1219 let values = vec![30i64, 10, 30, 20, 10, -5];
1220 let dictionary = distinct_values(&values);
1221 let codes = codes_over(&values, &dictionary);
1222 assert_eq!(dictionary, vec![-5, 10, 20, 30]);
1223 assert_eq!(codes, vec![3, 1, 3, 2, 1, 0]);
1224 for (code, value) in codes.iter().zip(&values) {
1225 assert_eq!(dictionary[*code as usize], *value);
1226 }
1227 }
1228
1229 #[test]
1230 fn one_sort_gives_the_distinct_count_and_the_most_frequent_value() {
1231 let values = vec![7i64, 7, 7, 1, 2, 2];
1232 assert_eq!(spread_of(&values), (3, Some((7, 3))));
1233 assert_eq!(spread_of(&[]), (0, None));
1234 assert_eq!(spread_of(&[9]), (1, Some((9, 1))));
1235
1236 assert_eq!(spread_of(&[4i64, 4, 8, 8]), (2, Some((4, 2))));
1239 }
1240
1241 #[test]
1242 fn the_majority_is_the_most_frequent_value_whenever_there_is_one() {
1243 let chunks: Vec<Vec<i64>> = vec![
1244 vec![],
1245 vec![3],
1246 vec![1, 2],
1247 vec![1, 1, 2],
1248 vec![2, 1, 1],
1249 vec![4, 4, 8, 8],
1250 vec![7, 1, 7, 2, 7, 3, 7],
1251 vec![1, 2, 3, 9, 9, 9, 9],
1252 (0..1000).map(|index| if index % 5 == 0 { index } else { -4 }).collect(),
1253 (0..1000).map(|index| index % 3).collect(),
1254 ];
1255 for chunk in chunks {
1256 let (_, dominant) = spread_of(&chunk);
1257 let expected = dominant.filter(|(_, count)| count * 2 > chunk.len());
1258 assert_eq!(majority(&chunk), expected, "{chunk:?}");
1259 }
1260 }
1261
1262 #[test]
1263 fn deltas_that_do_not_fit_are_refused_before_they_are_built() {
1264 assert!(deltas_fit(&[1i64, 2, 3]));
1265 assert!(deltas_fit(&[i64::MAX, i64::MAX]));
1266 assert!(!deltas_fit(&[i64::MIN, i64::MAX]));
1267 assert_eq!(deltas_fit(&[i64::MIN, i64::MAX]), deltas(&[i64::MIN, i64::MAX]).is_some());
1268 assert_eq!(deltas_fit(&[1i64, 2, 3]), deltas(&[1i64, 2, 3]).is_some());
1269 }
1270
1271 #[test]
1272 fn what_the_chooser_returns_is_the_smallest_of_what_it_was_offered() {
1273 let mut random = Random::new();
1277 let noise: Vec<i64> = (0..2000).map(|_| (random.next() % 5000) as i64).collect();
1278 let runs: Vec<i64> = (0..2000).map(|index: i64| index / 100).collect();
1279 let climbing: Vec<i64> = (0..2000).map(|index| 1_700_000_000 + index).collect();
1280 for values in [noise, runs, climbing, vec![7; 300], Vec::new()] {
1281 let chosen = encode(&values).unwrap();
1282 let mut smallest: Option<Vec<u8>> = None;
1283 for kind in offered(&values) {
1284 let Some(bytes) = encode_only(kind, &values).unwrap() else {
1285 continue;
1286 };
1287 if smallest.as_ref().is_none_or(|best| bytes.len() < best.len()) {
1288 smallest = Some(bytes);
1289 }
1290 }
1291 assert_eq!(smallest.as_deref(), Some(chosen.as_slice()), "{}", values.len());
1292 }
1293 }
1294
1295 #[test]
1296 fn a_column_of_whole_seconds_in_microseconds_pays_nothing_for_the_zeroes() {
1297 let mut random = Random::new();
1301 let day = 1_374_000_000_000_000i64;
1302 let values: Vec<i64> =
1303 (0..100_000).map(|_| day + (random.next() % 68_400) as i64 * 1_000_000).collect();
1304 let bytes = round_trip(&values);
1305 assert_eq!(kind_of(&bytes), Kind::Strided);
1306 assert!(describe(&bytes).unwrap().starts_with("STRIDE[1000000]"), "{:?}", describe(&bytes));
1307 let strided = 100_000 * 17 / 8;
1309 assert!(bytes.len() < strided + 2000, "{} bytes for {strided} of payload", bytes.len());
1310
1311 let plain = encode_only(Kind::Packed, &values).unwrap().expect("packing always applies");
1312 assert!(
1313 bytes.len() * 2 < plain.len(),
1314 "{} strided against {} packed",
1315 bytes.len(),
1316 plain.len()
1317 );
1318 }
1319
1320 #[test]
1321 fn a_stride_is_the_common_factor_of_the_distances_from_the_smallest_value() {
1322 assert_eq!(stride_of(&[10i64, 20, 40]), Some(10));
1323 assert_eq!(stride_of(&[7i64, 17, 37]), Some(10));
1326 assert_eq!(stride_of(&[10i64, 20, 23]), None);
1327 assert_eq!(stride_of(&[5i64; 100]), None);
1330 assert_eq!(stride_of(&[]), None);
1331 assert_eq!(stride_of(&[i64::MIN, i64::MAX]), Some(u64::MAX));
1333 }
1334
1335 #[test]
1336 fn a_stride_across_the_whole_of_the_type_round_trips() {
1337 for values in [vec![i64::MIN, i64::MAX], vec![i64::MIN, 0, i64::MAX]] {
1340 let bytes = round_trip(&values);
1341 assert_eq!(decode(&bytes).unwrap(), values);
1342 }
1343 }
1344
1345 #[test]
1346 fn a_column_with_no_common_factor_is_not_offered_a_stride() {
1347 let mut random = Random::new();
1348 let values: Vec<i64> = (0..2000).map(|_| (random.next() % 1_000_000) as i64).collect();
1349 assert!(!offered(&values).contains(&Kind::Strided));
1350 assert!(encode_only(Kind::Strided, &values).unwrap().is_none());
1351 }
1352
1353 #[test]
1354 fn an_empty_chunk_round_trips() {
1355 let bytes = round_trip(&[]);
1356 assert_eq!(bytes.len(), 5);
1357 }
1358
1359 #[test]
1360 fn a_constant_column_costs_thirteen_bytes_however_long_it_is() {
1361 let bytes = round_trip(&vec![42; 1_000_000]);
1362 assert_eq!(kind_of(&bytes), Kind::Constant);
1363 assert_eq!(bytes.len(), 13);
1364 }
1365
1366 #[test]
1367 fn a_narrow_range_is_packed_at_the_width_of_the_range_and_not_of_the_type() {
1368 let mut random = Random::new();
1370 let values: Vec<i64> = (0..100_000).map(|_| 1000 + (random.next() % 64) as i64).collect();
1371 let bytes = round_trip(&values);
1372 assert_eq!(kind_of(&bytes), Kind::Packed);
1373 let packed = 100_000 * 6 / 8;
1374 assert!(bytes.len() < packed + 2000, "{} bytes for {packed} of payload", bytes.len());
1375 assert!(bytes.len() > packed, "{} bytes cannot hold {packed}", bytes.len());
1376 }
1377
1378 #[test]
1379 fn a_counter_becomes_deltas_and_then_a_constant() {
1380 let values: Vec<i64> = (0..1_000_000).collect();
1383 let bytes = round_trip(&values);
1384 assert_eq!(kind_of(&bytes), Kind::Delta);
1385 assert_eq!(describe(&bytes).unwrap(), "DELTA(CONSTANT)");
1386 assert!(bytes.len() < 40, "{} bytes for a counter", bytes.len());
1387 }
1388
1389 #[test]
1390 fn a_column_that_counts_down_is_as_cheap_as_one_that_counts_up() {
1391 let up: Vec<i64> = (0..100_000).collect();
1393 let down: Vec<i64> = (0..100_000).rev().collect();
1394 assert_eq!(round_trip(&up).len(), round_trip(&down).len());
1395 }
1396
1397 #[test]
1398 fn long_runs_become_rle() {
1399 let mut values = Vec::new();
1400 for run in 0..1000 {
1401 values.extend(std::iter::repeat_n(run % 7, 200));
1402 }
1403 let bytes = round_trip(&values);
1404 assert_eq!(kind_of(&bytes), Kind::Rle);
1405 assert!(bytes.len() < 2000, "{} bytes for 1000 runs", bytes.len());
1406 }
1407
1408 #[test]
1409 fn a_low_cardinality_column_becomes_a_dictionary() {
1410 let mut random = Random::new();
1416 let dictionary: Vec<i64> =
1417 (0..40).map(|_| 1_000_000_000 + (random.next() % (1 << 30)) as i64).collect();
1418 let values: Vec<i64> =
1419 (0..100_000).map(|_| dictionary[(random.next() % 40) as usize]).collect();
1420 let bytes = round_trip(&values);
1421 assert_eq!(kind_of(&bytes), Kind::Dict);
1422 assert!(bytes.len() < 100_000, "{} bytes", bytes.len());
1423 }
1424
1425 #[test]
1426 fn a_nearly_constant_column_becomes_sparse() {
1427 let mut values = vec![0i64; 100_000];
1428 for index in 0..300 {
1429 values[index * 331] = 1 << 40;
1430 }
1431 let bytes = round_trip(&values);
1432 assert_eq!(kind_of(&bytes), Kind::Sparse);
1433 assert!(bytes.len() < 3000, "{} bytes for 300 exceptions", bytes.len());
1434 }
1435
1436 #[test]
1437 fn the_cascade_goes_more_than_one_level_deep() {
1438 let mut values = Vec::new();
1441 for index in 0..2000i64 {
1442 values.extend(std::iter::repeat_n(1_000_000 + (index % 5) * 104_729, 100));
1443 }
1444 let bytes = round_trip(&values);
1445 let shape = describe(&bytes).unwrap();
1446 assert!(shape.contains('('), "{shape} is not a cascade");
1447 assert!(bytes.len() < 4000, "{} bytes: {shape}", bytes.len());
1448 }
1449
1450 #[test]
1451 fn random_data_is_packed_at_full_width_and_costs_what_it_costs() {
1452 let mut random = Random::new();
1455 let values: Vec<i64> = (0..10_000).map(|_| random.next() as i64).collect();
1456 let bytes = round_trip(&values);
1457 assert_eq!(kind_of(&bytes), Kind::Packed);
1458 assert!(bytes.len() < 10_000 * 8 + 1000, "{} bytes", bytes.len());
1459 }
1460
1461 #[test]
1462 fn the_extremes_of_the_type_survive() {
1463 let values = vec![i64::MIN, i64::MAX, 0, -1, i64::MIN, i64::MAX];
1466 round_trip(&values);
1467 round_trip(&[i64::MIN; 3]);
1468 round_trip(&[i64::MIN, i64::MIN + 1]);
1469 }
1470
1471 #[test]
1472 fn a_chunk_that_is_not_a_multiple_of_the_unit_round_trips() {
1473 for len in [1, 2, 1023, 1024, 1025, 2047, 2049] {
1474 let values: Vec<i64> = (0..len).map(|index| (index * 31 % 97) as i64).collect();
1475 round_trip(&values);
1476 }
1477 }
1478
1479 #[test]
1480 fn units_of_different_widths_in_one_chunk_do_not_read_each_others_leftovers() {
1481 let mut random = Random::new();
1493 let mut values = Vec::new();
1494 for width in [40u32, 3, 61, 1, 17, 40] {
1495 for _ in 0..1024 {
1496 values.push((random.next() & ((1u64 << width) - 1)) as i64);
1497 }
1498 }
1499 let bytes = encode(&values).unwrap();
1500 let described = describe(&bytes).unwrap();
1501 assert!(described.starts_with("FOR+BITPACK"), "expected one packed chunk, got {described}");
1502 assert_eq!(decode(&bytes).unwrap(), values, "{described}");
1503 }
1504
1505 #[test]
1506 fn a_cascade_decodes_the_same_through_a_shared_scratch_as_through_its_own() {
1507 let mut values = Vec::new();
1512 for index in 0..8192i64 {
1513 values.push(1_600_000_000 + index / 4 + (index % 7) * 1_000);
1514 }
1515 let bytes = encode(&values).unwrap();
1516 let described = describe(&bytes).unwrap();
1517 assert!(described.contains('('), "expected a cascade, got {described}");
1518 assert_eq!(decode(&bytes).unwrap(), values, "{described}");
1519 }
1520
1521 #[test]
1522 fn selected_positions_agree_with_a_full_decode_for_packed_and_run_length_chunks() {
1523 let positions = [0, 1, 17, 1023, 1024, 4097, 8191];
1524 let packed: Vec<i64> = (0..8192).map(|index| index * 31 % 1_000_003).collect();
1525 let mut runs = Vec::new();
1526 for run in 0..160i64 {
1527 runs.extend(std::iter::repeat_n(run * 13, (run as usize % 71) + 2));
1528 }
1529 runs.resize(8192, -7);
1530
1531 for (kind, values) in [(Kind::Packed, packed), (Kind::Rle, runs)] {
1532 let bytes = encode_only(kind, &values).unwrap().expect("encoding applies");
1533 let selected = decode_selected(&bytes, &positions).unwrap();
1534 let expected = positions.iter().map(|&position| values[position]).collect::<Vec<_>>();
1535 assert_eq!(selected, expected, "{}", kind.name());
1536 }
1537 }
1538
1539 #[test]
1540 fn selected_positions_must_be_ordered_and_inside_the_chunk() {
1541 let bytes = encode_only(Kind::Packed, &(0..2048).collect::<Vec<_>>())
1542 .unwrap()
1543 .expect("packed applies");
1544 assert!(decode_selected(&bytes, &[7, 7]).is_err());
1545 assert!(decode_selected(&bytes, &[8, 3]).is_err());
1546 assert!(decode_selected(&bytes, &[2048]).is_err());
1547 }
1548
1549 #[test]
1550 fn a_partial_unit_costs_its_own_values_and_not_a_whole_unit() {
1551 let values = vec![1i64 << 39, (1 << 39) + 7, 1 << 38];
1555 let bytes = encode_only(Kind::Packed, &values).unwrap().unwrap();
1556 assert_eq!(bytes.len(), 5 + 9 + 15);
1557 assert_eq!(decode(&bytes).unwrap(), values);
1558 }
1559
1560 #[test]
1561 fn the_frame_of_reference_is_per_unit_and_not_per_chunk() {
1562 let values: Vec<i64> =
1566 (0..4096i64).map(|index| (index / 1024) * 1_000_000 + (index % 1024)).collect();
1567 let bytes = encode_only(Kind::Packed, &values).unwrap().unwrap();
1568 assert_eq!(describe(&bytes).unwrap(), "FOR+BITPACK[10]");
1569 assert_eq!(decode(&bytes).unwrap(), values);
1570 }
1571
1572 #[test]
1573 fn every_candidate_that_applies_decodes_to_the_input() {
1574 let mut values = vec![5i64; 3000];
1578 for (index, value) in values.iter_mut().enumerate() {
1579 if index % 500 == 0 {
1580 *value = index as i64;
1581 }
1582 }
1583 let applicable = candidates(&values, 0, &EXHAUSTIVE);
1584 assert!(applicable.len() >= 4, "{applicable:?}");
1585 for kind in applicable {
1586 let bytes = encode_only(kind, &values).unwrap().unwrap();
1587 assert_eq!(decode(&bytes).unwrap(), values, "{}", kind.name());
1588 }
1589 }
1590
1591 #[test]
1596 fn every_kind_that_applies_decodes_to_what_it_was_given() {
1597 let shapes: Vec<Vec<i64>> = vec![
1598 Vec::new(),
1599 vec![5; 1024],
1600 vec![i64::MIN, i64::MAX, 0, -1],
1601 (0..1024).map(|at| at * 7).collect(),
1602 (0..1024).map(|at| at % 17).collect(),
1603 (0..1024).map(|at| if at % 100 == 0 { at } else { 3 }).collect(),
1604 (0..1024).map(|at| -at * 1_000_003).collect(),
1605 (0..1024_i64)
1606 .map(|at| {
1607 at.wrapping_mul(6_364_136_223_846_793_005)
1608 .wrapping_add(1_442_695_040_888_963_407)
1609 })
1610 .collect(),
1611 ];
1612 let kinds =
1613 [Kind::Constant, Kind::Packed, Kind::Delta, Kind::Rle, Kind::Dict, Kind::Sparse];
1614 for values in &shapes {
1615 for kind in kinds {
1616 let Some(bytes) = encode_only(kind, values).unwrap() else {
1617 continue;
1618 };
1619 assert_eq!(
1620 &decode(&bytes).unwrap(),
1621 values,
1622 "{} over {} values",
1623 kind.name(),
1624 values.len()
1625 );
1626 }
1627 }
1628 }
1629
1630 #[test]
1631 fn the_chooser_picks_the_smallest_candidate_rather_than_the_first_that_applies() {
1632 let mut values = vec![5i64; 3000];
1633 values[1500] = 9;
1634 let chosen = encode(&values).unwrap();
1635 for (_, size) in candidate_sizes(&values).unwrap() {
1636 assert!(chosen.len() <= size);
1637 }
1638 }
1639
1640 #[test]
1641 fn a_truncated_chunk_is_an_error_and_not_a_panic() {
1642 let bytes = encode(&[1, 2, 3, 4, 5]).unwrap();
1643 for len in 0..bytes.len() {
1644 let error = decode(&bytes[..len]).unwrap_err();
1645 assert!(error.message().contains("chunk"), "{error}");
1646 }
1647 }
1648
1649 #[test]
1650 fn trailing_bytes_are_an_error() {
1651 let mut bytes = encode(&[1, 2, 3]).unwrap();
1652 bytes.push(0);
1653 let error = decode(&bytes).unwrap_err();
1654 assert!(error.message().contains("left over"), "{error}");
1655 }
1656
1657 #[test]
1658 fn an_unknown_tag_is_an_error() {
1659 let error = decode(&[99, 0, 0, 0, 0]).unwrap_err();
1660 assert!(error.message().contains("unknown encoding tag"), "{error}");
1661 }
1662
1663 #[test]
1664 fn a_dictionary_code_outside_the_dictionary_is_an_error() {
1665 let mut bytes = vec![Kind::Dict.tag()];
1670 put_u32(&mut bytes, 1);
1671 bytes.extend_from_slice(&encode(&[10]).unwrap());
1672 bytes.extend_from_slice(&encode(&[5]).unwrap());
1673 let error = decode(&bytes).unwrap_err();
1674 assert!(error.message().contains("not in the dictionary"), "{error}");
1675 }
1676
1677 #[test]
1678 fn a_negative_run_length_is_an_error() {
1679 let mut bytes = vec![Kind::Rle.tag()];
1682 put_u32(&mut bytes, 4);
1683 bytes.extend_from_slice(&encode(&[7]).unwrap());
1684 bytes.extend_from_slice(&encode(&[-4]).unwrap());
1685 let error = decode(&bytes).unwrap_err();
1686 assert!(error.message().contains("negative"), "{error}");
1687 }
1688
1689 #[test]
1691 fn a_run_that_runs_past_its_chunk_is_an_error() {
1692 let mut bytes = vec![Kind::Rle.tag()];
1697 put_u32(&mut bytes, 4);
1698 bytes.extend_from_slice(&encode(&[7]).unwrap());
1699 bytes.extend_from_slice(&encode(&[9]).unwrap());
1700 let error = decode(&bytes).unwrap_err();
1701 assert!(error.message().contains("past its chunk"), "{error}");
1702 }
1703
1704 #[test]
1706 fn runs_shorter_and_longer_than_the_width_they_are_written_in_all_come_back() {
1707 let lengths = [1, 3, 7, 8, 9, 40, 2, 1];
1712 let mut values = Vec::new();
1713 for (at, length) in lengths.iter().enumerate() {
1714 let value = i64::try_from(at).expect("eight runs") * 1000 - 3;
1715 values.extend(std::iter::repeat_n(value, *length));
1716 }
1717 let bytes = encode(&values).expect("encodes");
1718 assert_eq!(decode(&bytes).expect("decodes"), values, "runs around the write width");
1719 let singles: Vec<i64> = (0..300).map(|index| index * 7 % 11).collect();
1722 let bytes = encode(&singles).expect("encodes");
1723 assert_eq!(decode(&bytes).expect("decodes"), singles, "no run longer than one");
1724 }
1725
1726 #[test]
1727 fn the_cascade_depth_is_bounded() {
1728 let values: Vec<i64> = (0..50_000).map(|index| (index / 100) % 250).collect();
1732 let bytes = round_trip(&values);
1733 let shape = describe(&bytes).unwrap();
1734 let depth = shape.matches('(').count();
1735 assert!(depth <= MAX_DEPTH as usize, "{shape} is {depth} deep");
1736 }
1737
1738 #[test]
1739 fn candidate_sizes_reports_what_the_chooser_looked_at() {
1740 let values: Vec<i64> = (0..5000).map(|index| index % 17).collect();
1741 let sizes = candidate_sizes(&values).unwrap();
1742 assert!(sizes.iter().any(|(kind, _)| *kind == Kind::Dict));
1743 assert!(sizes.iter().any(|(kind, _)| *kind == Kind::Packed));
1744 assert!(sizes.iter().all(|(_, size)| *size > 0));
1745 }
1746
1747 #[test]
1748 fn a_chunk_can_be_read_from_the_front_of_a_longer_buffer() {
1749 let first = encode(&[1, 2, 3]).unwrap();
1752 let second: Vec<i64> = (0..3000).map(|index| index % 11).collect();
1753 let second_bytes = encode(&second).unwrap();
1754 let mut joined = first.clone();
1755 joined.extend_from_slice(&second_bytes);
1756 joined.extend_from_slice(b"and then something else");
1757
1758 let (values, used) = decode_prefix(&joined).unwrap();
1759 assert_eq!(values, vec![1, 2, 3]);
1760 assert_eq!(used, first.len());
1761 let (more, used_again) = decode_prefix(&joined[used..]).unwrap();
1762 assert_eq!(more, second);
1763 assert_eq!(used_again, second_bytes.len());
1764
1765 let (text, described) = describe_prefix(&joined).unwrap();
1766 assert_eq!(described, first.len());
1767 assert_eq!(text, describe(&first).unwrap());
1768 }
1769
1770 #[test]
1771 fn a_truncated_chunk_is_still_an_error_when_read_as_a_prefix() {
1772 let bytes = encode(&(0..2000).collect::<Vec<i64>>()).unwrap();
1773 for len in 0..bytes.len() {
1774 assert!(decode_prefix(&bytes[..len]).is_err(), "{len} bytes decoded");
1775 }
1776 }
1777}