1use std::collections::BTreeMap;
45use std::time::Instant;
46
47use rudb_common::{Error, Result};
48
49use crate::chooser::{Chooser, EXHAUSTIVE};
50use crate::reader::Reader;
51use crate::tally::{self, Family};
52
53use crate::bitpack::{self, VALUES};
54
55const MAX_DEPTH: u8 = 3;
62
63const RUN: usize = 8;
73
74const LONG_RUN: usize = 64;
78
79#[derive(Debug, Clone, Copy, PartialEq, Eq)]
82pub enum Kind {
83 Constant = 0,
85 Packed = 1,
88 Delta = 2,
91 Rle = 3,
93 Dict = 4,
96 Sparse = 5,
98 Strided = 6,
101}
102
103impl Kind {
104 pub const ALL: [Self; 7] = [
106 Self::Constant,
107 Self::Packed,
108 Self::Delta,
109 Self::Rle,
110 Self::Dict,
111 Self::Sparse,
112 Self::Strided,
113 ];
114
115 fn tag(self) -> u8 {
116 self as u8
117 }
118
119 fn from_tag(tag: u8) -> Result<Self> {
120 match tag {
121 0 => Ok(Self::Constant),
122 1 => Ok(Self::Packed),
123 2 => Ok(Self::Delta),
124 3 => Ok(Self::Rle),
125 4 => Ok(Self::Dict),
126 5 => Ok(Self::Sparse),
127 6 => Ok(Self::Strided),
128 other => Err(Error::internal(format!("unknown encoding tag {other}"))),
129 }
130 }
131
132 #[must_use]
134 pub fn name(self) -> &'static str {
135 match self {
136 Self::Constant => "CONSTANT",
137 Self::Packed => "FOR+BITPACK",
138 Self::Delta => "DELTA",
139 Self::Rle => "RLE",
140 Self::Dict => "DICT",
141 Self::Sparse => "SPARSE",
142 Self::Strided => "STRIDE",
143 }
144 }
145}
146
147pub fn encode(values: &[i64]) -> Result<Vec<u8>> {
154 encode_with(values, &EXHAUSTIVE)
155}
156
157pub fn encode_with(values: &[i64], chooser: &dyn Chooser) -> Result<Vec<u8>> {
167 encode_at(values, 0, chooser)
168}
169
170pub fn decode(bytes: &[u8]) -> Result<Vec<i64>> {
177 let mut reader = Reader::new(bytes);
178 let values = with_decoding(|scratch| decode_chunk(&mut reader, scratch))?;
179 if reader.remaining() != 0 {
180 return Err(Error::internal(format!(
181 "{} bytes left over after decoding a chunk",
182 reader.remaining()
183 )));
184 }
185 Ok(values)
186}
187
188pub fn decode_as<T: Lane>(bytes: &[u8]) -> Result<Vec<T>> {
201 let mut reader = Reader::new(bytes);
202 let values = with_decoding(|scratch| decode_chunk_as(&mut reader, scratch))?;
203 if reader.remaining() != 0 {
204 return Err(Error::internal(format!(
205 "{} bytes left over after decoding a chunk",
206 reader.remaining()
207 )));
208 }
209 Ok(values)
210}
211
212pub trait Lane: Copy + Default {
214 fn fit(value: i64) -> Option<Self>;
216
217 fn wrap(value: i64) -> Self;
219}
220
221macro_rules! lanes {
222 ($($ty:ty),* $(,)?) => {$(
223 impl Lane for $ty {
224 fn fit(value: i64) -> Option<Self> {
225 Self::try_from(value).ok()
226 }
227
228 #[allow(
229 clippy::cast_possible_truncation,
230 clippy::cast_sign_loss,
231 clippy::unnecessary_cast,
232 reason = "only called on a value the caller has checked fits"
233 )]
234 fn wrap(value: i64) -> Self {
235 value as Self
236 }
237 }
238 )*};
239}
240
241lanes!(i8, u8, i16, u16, i32, u32, i64, u64);
242
243fn lane<T: Lane>(value: i64) -> Result<T> {
245 T::fit(value).ok_or_else(|| Error::internal(format!("{value} is outside the chunk's type")))
246}
247
248pub fn tally(bytes: &[u8]) -> Result<(usize, Vec<(i64, u64)>)> {
257 let mut counts = BTreeMap::<i64, u64>::new();
258 let rows = fold(bytes, |value, count| {
259 *counts.entry(value).or_default() += count;
260 Ok(())
261 })?;
262 Ok((rows, counts.into_iter().collect()))
263}
264
265pub fn fold(bytes: &[u8], mut emit: impl FnMut(i64, u64) -> Result<()>) -> Result<usize> {
273 let mut reader = Reader::new(bytes);
274 let kind = Kind::from_tag(reader.u8()?)?;
275 let count = reader.u32()? as usize;
276 match kind {
277 Kind::Constant => {
278 let value = reader.i64()?;
279 if count != 0 {
280 emit(value, count as u64)?;
281 }
282 }
283 Kind::Sparse => {
284 let dominant = reader.i64()?;
285 let exception_count = reader.u32()? as usize;
286 let (positions, values) = with_decoding(|scratch| -> Result<_> {
287 Ok((decode_chunk(&mut reader, scratch)?, decode_chunk(&mut reader, scratch)?))
288 })?;
289 if positions.len() != exception_count || values.len() != exception_count {
290 return Err(Error::internal("a sparse chunk disagrees about its exception count"));
291 }
292 let mut ordered = true;
293 let mut previous = None;
294 for &position in &positions {
295 let position = usize::try_from(position)
296 .ok()
297 .filter(|&position| position < count)
298 .ok_or_else(|| Error::internal("a sparse exception is outside the chunk"))?;
299 if previous.is_some_and(|last| position <= last) {
300 ordered = false;
301 }
302 previous = Some(position);
303 }
304 if ordered {
305 if count != exception_count {
306 emit(dominant, (count - exception_count) as u64)?;
307 }
308 for value in values {
309 emit(value, 1)?;
310 }
311 } else {
312 let mut exceptions = BTreeMap::<usize, i64>::new();
315 for (position, value) in positions.into_iter().zip(values) {
316 exceptions.insert(position as usize, value);
317 }
318 if count != exceptions.len() {
319 emit(dominant, (count - exceptions.len()) as u64)?;
320 }
321 for value in exceptions.into_values() {
322 emit(value, 1)?;
323 }
324 }
325 }
326 Kind::Rle => {
327 let (values, lengths) = with_decoding(|scratch| -> Result<_> {
328 Ok((decode_chunk(&mut reader, scratch)?, decode_chunk(&mut reader, scratch)?))
329 })?;
330 if values.len() != lengths.len() {
331 return Err(Error::internal("an RLE chunk has more runs than run lengths"));
332 }
333 let mut rows = 0_usize;
334 for (value, length) in values.into_iter().zip(lengths) {
335 let length = usize::try_from(length)
336 .map_err(|_| Error::internal("a negative RLE run length"))?;
337 rows = rows
338 .checked_add(length)
339 .filter(|&rows| rows <= count)
340 .ok_or_else(|| Error::internal("an RLE run ends past its chunk"))?;
341 if length != 0 {
342 emit(value, length as u64)?;
343 }
344 }
345 check_count(rows, count)?;
346 }
347 _ => {
348 reader = Reader::new(bytes);
350 let values = with_decoding(|scratch| decode_chunk(&mut reader, scratch))?;
351 check_count(values.len(), count)?;
352 for value in values {
353 emit(value, 1)?;
354 }
355 }
356 }
357 if reader.remaining() != 0 {
358 return Err(Error::internal(format!(
359 "{} bytes left over after counting a chunk",
360 reader.remaining()
361 )));
362 }
363 Ok(count)
364}
365
366pub fn decode_selected(bytes: &[u8], positions: &[usize]) -> Result<Vec<i64>> {
377 if positions.windows(2).any(|pair| pair[0] >= pair[1]) {
378 return Err(Error::internal("selected integer positions are not sorted and unique"));
379 }
380 let mut reader = Reader::new(bytes);
381 let values = with_decoding(|scratch| decode_selected_chunk(&mut reader, positions, scratch))?;
382 if reader.remaining() != 0 {
383 return Err(Error::internal(format!(
384 "{} bytes left over after decoding selected values",
385 reader.remaining()
386 )));
387 }
388 Ok(values)
389}
390
391pub fn decode_prefix(bytes: &[u8]) -> Result<(Vec<i64>, usize)> {
401 let mut reader = Reader::new(bytes);
402 let values = with_decoding(|scratch| decode_chunk(&mut reader, scratch))?;
403 Ok((values, reader.used()))
404}
405
406pub fn describe_prefix(bytes: &[u8]) -> Result<(String, usize)> {
412 let mut reader = Reader::new(bytes);
413 let text = describe_chunk(&mut reader)?;
414 Ok((text, reader.used()))
415}
416
417pub fn candidate_sizes(values: &[i64]) -> Result<Vec<(Kind, usize)>> {
424 let mut sizes = Vec::new();
425 for kind in candidates(values, 0, &EXHAUSTIVE) {
426 if let Some(bytes) = encode_as(kind, values, 0, &EXHAUSTIVE)? {
427 sizes.push((kind, bytes.len()));
428 }
429 }
430 Ok(sizes)
431}
432
433#[must_use]
440pub fn offered(values: &[i64]) -> Vec<Kind> {
441 candidates(values, 0, &EXHAUSTIVE)
442}
443
444pub fn encode_only(kind: Kind, values: &[i64]) -> Result<Option<Vec<u8>>> {
455 encode_as(kind, values, 0, &EXHAUSTIVE)
456}
457
458pub(crate) fn size_as(kind: Kind, values: &[i64], depth: u8) -> Result<Option<usize>> {
463 Ok(encode_as(kind, values, depth, &EXHAUSTIVE)?.map(|bytes| bytes.len()))
464}
465
466pub fn shape(bytes: &[u8]) -> Result<Vec<Kind>> {
478 let mut reader = Reader::new(bytes);
479 let mut kinds = Vec::new();
480 shape_chunk(&mut reader, &mut kinds)?;
481 Ok(kinds)
482}
483
484pub fn describe(bytes: &[u8]) -> Result<String> {
490 let mut reader = Reader::new(bytes);
491 describe_chunk(&mut reader)
492}
493
494fn encode_at(values: &[i64], depth: u8, chooser: &dyn Chooser) -> Result<Vec<u8>> {
495 let started = Instant::now();
496 let offered = candidates(values, depth, chooser);
497 let narrowed = chooser.narrow_integers(values, &offered, depth);
498 let counted = depth == 0;
500 if counted {
501 tally::chose(Family::Integer, started);
502 }
503 let mut best: Option<(Kind, Vec<u8>)> = None;
504 for kind in narrowed {
505 let encoded = if counted {
506 tally::offer(Family::Integer, kind.tag(), || encode_as(kind, values, depth, chooser))?
507 } else {
508 encode_as(kind, values, depth, chooser)?
509 };
510 let Some(bytes) = encoded else {
511 continue;
512 };
513 if best.as_ref().is_none_or(|(_, current)| bytes.len() < current.len()) {
514 best = Some((kind, bytes));
515 }
516 }
517 let (kind, bytes) = best.ok_or_else(|| Error::internal("no encoding applied to the chunk"))?;
520 if counted {
521 tally::kept(Family::Integer, kind.tag());
522 }
523 Ok(bytes)
524}
525
526fn candidates(values: &[i64], depth: u8, chooser: &dyn Chooser) -> Vec<Kind> {
537 let mut kinds = vec![Kind::Packed];
538 if depth >= MAX_DEPTH {
539 return kinds;
540 }
541 let Some(profile) = Profile::of(values) else {
542 return kinds;
543 };
544 if profile.runs == 1 {
545 return vec![Kind::Constant];
547 }
548 let considered = |kind| chooser.considers_integer(kind, depth);
549 let fits = profile.max.checked_sub(profile.min).is_some();
552 if considered(Kind::Delta)
553 && (if fits { profile.deltas_pay(values) } else { deltas_fit(values) })
554 {
555 kinds.push(Kind::Delta);
556 }
557 if considered(Kind::Rle) && profile.runs * 4 <= values.len() * 3 {
558 kinds.push(Kind::Rle);
559 }
560 if considered(Kind::Dict) && profile.width() > 1 {
564 let distinct = spread_of(values).0;
565 if distinct * 2 <= values.len() && width_of(distinct as u64 - 1) < profile.width() {
566 kinds.push(Kind::Dict);
567 }
568 }
569 if considered(Kind::Sparse)
573 && (profile.runs - 1) * 5 <= values.len() * 2
574 && majority(values).is_some_and(|(_, count)| count * 10 >= values.len() * 8)
575 {
576 kinds.push(Kind::Sparse);
577 }
578 if considered(Kind::Strided) && stride_from(values, profile.min).is_some() {
579 kinds.push(Kind::Strided);
580 }
581 kinds
582}
583
584struct Profile {
598 min: i64,
599 max: i64,
600 runs: usize,
602 delta_low: u64,
605 delta_high: u64,
606 delta_runs: usize,
608}
609
610impl Profile {
611 fn of(values: &[i64]) -> Option<Self> {
612 let first = *values.first()?;
613 let (mut min, mut max, mut breaks) = (first, first, 0usize);
614 let mut last = values.get(1).map_or(0, |second| second.wrapping_sub(first));
615 let (mut delta_low, mut delta_high, mut turns) = (u64::MAX, 0u64, 0usize);
616 for (before, after) in values.iter().zip(&values[1..]) {
617 min = min.min(*after);
618 max = max.max(*after);
619 breaks += usize::from(before != after);
620 let delta = after.wrapping_sub(*before);
621 let zigzagged = zigzag(delta);
622 delta_low = delta_low.min(zigzagged);
623 delta_high = delta_high.max(zigzagged);
624 turns += usize::from(delta != last);
625 last = delta;
626 }
627 Some(Self { min, max, runs: breaks + 1, delta_low, delta_high, delta_runs: turns + 1 })
628 }
629
630 fn width(&self) -> u32 {
632 width_of(self.max.wrapping_sub(self.min) as u64)
633 }
634
635 fn deltas_pay(&self, values: &[i64]) -> bool {
643 let len = values.len();
644 let narrower = width_of(self.delta_high.wrapping_sub(self.delta_low)) < self.width();
645 let unruly = self.runs * 4 > len * 3;
648 let repeat = self.delta_runs * 4 <= len.saturating_sub(1) * 3;
649 narrower || (unruly && (repeat || few_deltas(values)))
650 }
651}
652
653fn few_deltas(values: &[i64]) -> bool {
658 let mut seen = [0i64; FEW_DELTAS];
659 let mut count = 0;
660 for pair in values.windows(2).take(FEW_DELTAS_SEEN) {
661 let delta = pair[1].wrapping_sub(pair[0]);
662 if seen[..count].contains(&delta) {
663 continue;
664 }
665 if count == FEW_DELTAS {
666 return false;
667 }
668 seen[count] = delta;
669 count += 1;
670 }
671 true
672}
673
674const FEW_DELTAS: usize = 4;
676
677const FEW_DELTAS_SEEN: usize = 64;
679
680fn width_of(range: u64) -> u32 {
682 u64::BITS - range.leading_zeros()
683}
684
685fn encode_as(
688 kind: Kind,
689 values: &[i64],
690 depth: u8,
691 chooser: &dyn Chooser,
692) -> Result<Option<Vec<u8>>> {
693 let mut out = Vec::new();
694 put_u8(&mut out, kind.tag());
695 put_u32(&mut out, u32::try_from(values.len()).map_err(|_| too_long(values.len()))?);
696 match kind {
697 Kind::Constant => {
698 let Some(first) = values.first() else {
699 return Ok(None);
700 };
701 if values.iter().any(|value| value != first) {
702 return Ok(None);
703 }
704 put_i64(&mut out, *first);
705 }
706 Kind::Packed => encode_packed(values, &mut out)?,
707 Kind::Delta => {
708 let (Some(first), Some(deltas)) = (values.first(), deltas(values)) else {
712 return Ok(None);
713 };
714 put_i64(&mut out, *first);
715 out.extend_from_slice(&encode_at(&deltas, depth + 1, chooser)?);
716 }
717 Kind::Rle => {
718 let (run_values, run_lengths) = runs(values);
719 if run_values.is_empty() {
720 return Ok(None);
721 }
722 out.extend_from_slice(&encode_at(&run_values, depth + 1, chooser)?);
723 out.extend_from_slice(&encode_at(&run_lengths, depth + 1, chooser)?);
724 }
725 Kind::Dict => {
726 let dictionary = distinct_values(values);
727 if dictionary.is_empty() {
728 return Ok(None);
729 }
730 let codes = codes_over(values, &dictionary);
731 out.extend_from_slice(&encode_at(&dictionary, depth + 1, chooser)?);
732 out.extend_from_slice(&encode_at(&codes, depth + 1, chooser)?);
733 }
734 Kind::Sparse => {
735 let Some((value, _)) = majority(values).or_else(|| spread_of(values).1) else {
739 return Ok(None);
740 };
741 let mut positions = Vec::new();
742 let mut exceptions = Vec::new();
743 for (index, other) in values.iter().enumerate() {
744 if *other != value {
745 positions.push(index as i64);
746 exceptions.push(*other);
747 }
748 }
749 put_i64(&mut out, value);
750 put_u32(
751 &mut out,
752 u32::try_from(positions.len()).map_err(|_| too_long(positions.len()))?,
753 );
754 out.extend_from_slice(&encode_at(&positions, depth + 1, chooser)?);
755 out.extend_from_slice(&encode_at(&exceptions, depth + 1, chooser)?);
756 }
757 Kind::Strided => {
758 let (Some(base), Some(stride)) = (values.iter().min().copied(), stride_of(values))
759 else {
760 return Ok(None);
761 };
762 let mut steps = Vec::with_capacity(values.len());
763 for value in values {
764 let step = offset_from(*value, base) / stride;
765 let Ok(step) = i64::try_from(step) else {
770 return Ok(None);
771 };
772 steps.push(step);
773 }
774 put_i64(&mut out, base);
775 put_u64(&mut out, stride);
776 out.extend_from_slice(&encode_at(&steps, depth + 1, chooser)?);
777 }
778 }
779 Ok(Some(out))
780}
781
782fn encode_packed(values: &[i64], out: &mut Vec<u8>) -> Result<()> {
794 let mut offsets: Vec<u64> = Vec::with_capacity(VALUES);
798 let mut packed: Vec<u64> = vec![0; bitpack::packed_len::<u64>(64)];
801 let mut transposed = bitpack::Scratch::<u64>::new();
802 for unit in values.chunks(VALUES) {
803 let base = unit.iter().copied().min().unwrap_or(0);
804 offsets.clear();
805 offsets.extend(unit.iter().map(|value| offset_from(*value, base)));
806 let width = bitpack::required_width(&offsets);
807 put_i64(out, base);
808 put_u8(out, u8::try_from(width).map_err(|_| Error::internal("impossible width"))?);
809 if unit.len() == VALUES {
810 let words = bitpack::packed_len::<u64>(width);
811 bitpack::pack_with(&offsets, width, &mut packed[..words], &mut transposed)?;
812 for word in &packed[..words] {
813 put_u64(out, *word);
814 }
815 } else {
816 bitpack::pack_tail(&offsets, width, out)?;
817 }
818 }
819 Ok(())
820}
821
822struct Decoding {
849 packed: Vec<u64>,
852}
853
854thread_local! {
855 static DECODING: std::cell::RefCell<Decoding> =
857 const { std::cell::RefCell::new(Decoding::new()) };
858}
859
860fn with_decoding<T>(run: impl FnOnce(&mut Decoding) -> T) -> T {
867 DECODING.with(|cell| match cell.try_borrow_mut() {
868 Ok(mut scratch) => run(&mut scratch),
869 Err(_) => run(&mut Decoding::new()),
870 })
871}
872
873impl Decoding {
874 const fn new() -> Self {
876 Self { packed: Vec::new() }
877 }
878
879 fn ready(&mut self) {
881 if self.packed.len() != bitpack::packed_len::<u64>(64) {
882 self.packed.resize(bitpack::packed_len::<u64>(64), 0);
883 }
884 }
885}
886
887fn decode_chunk(reader: &mut Reader<'_>, scratch: &mut Decoding) -> Result<Vec<i64>> {
888 let kind = Kind::from_tag(reader.u8()?)?;
889 let count = reader.u32()? as usize;
890 match kind {
891 Kind::Constant => Ok(vec![reader.i64()?; count]),
892 Kind::Packed => {
893 let mut values = vec![0i64; count];
897 scratch.ready();
898 let mut done = 0;
899 while done < count {
900 let base = reader.i64()?;
901 let width = reader.u8()? as usize;
902 let wanted = (count - done).min(VALUES);
903 let into = &mut values[done..done + wanted];
904 if wanted == VALUES {
905 let words = bitpack::packed_len::<u64>(width);
906 for word in &mut scratch.packed[..words] {
907 *word = reader.u64()?;
908 }
909 bitpack::unpack_mapped(&scratch.packed[..words], width, into, |offset| {
910 value_from(offset, base)
911 })?;
912 } else {
913 let bytes = reader.bytes(bitpack::tail_len(wanted, width))?;
914 bitpack::unpack_tail_into(bytes, width, into, |offset| {
915 value_from(offset, base)
916 })?;
917 }
918 done += wanted;
919 }
920 Ok(values)
921 }
922 Kind::Delta => {
923 let first = reader.i64()?;
924 let deltas = decode_chunk(reader, scratch)?;
925 let mut values = Vec::with_capacity(count);
926 values.push(first);
927 let mut current = first;
928 for delta in deltas {
929 current = current.wrapping_add(unzigzag(delta as u64));
930 values.push(current);
931 }
932 check_count(values.len(), count)?;
933 Ok(values)
934 }
935 Kind::Rle => {
936 let run_values = decode_chunk(reader, scratch)?;
937 let run_lengths = decode_chunk(reader, scratch)?;
938 if run_values.len() != run_lengths.len() {
939 return Err(Error::internal("an RLE chunk has more runs than run lengths"));
940 }
941 let long = run_values.len().saturating_mul(LONG_RUN) <= count;
948 let mut values = if long { Vec::with_capacity(count) } else { vec![0; count + RUN] };
949 let mut at = 0usize;
950 for (value, length) in run_values.into_iter().zip(run_lengths) {
951 let length = usize::try_from(length)
952 .map_err(|_| Error::internal("a negative RLE run length"))?;
953 let end = at
954 .checked_add(length)
955 .filter(|end| *end <= count)
956 .ok_or_else(|| Error::internal("an RLE run ends past its chunk"))?;
957 if long {
958 values.resize(end, value);
959 } else {
960 let short =
961 if length <= RUN { values[at..].first_chunk_mut::<RUN>() } else { None };
962 match short {
963 Some(window) => window.fill(value),
964 None => values[at..end].fill(value),
965 }
966 }
967 at = end;
968 }
969 check_count(at, count)?;
970 values.truncate(count);
971 Ok(values)
972 }
973 Kind::Dict => {
974 let dictionary = decode_chunk(reader, scratch)?;
975 let codes = decode_chunk(reader, scratch)?;
976 let mut values = Vec::with_capacity(count);
977 for code in codes {
978 let index =
979 usize::try_from(code).ok().and_then(|index| dictionary.get(index)).ok_or_else(
980 || Error::internal(format!("code {code} is not in the dictionary")),
981 )?;
982 values.push(*index);
983 }
984 check_count(values.len(), count)?;
985 Ok(values)
986 }
987 Kind::Sparse => {
988 let value = reader.i64()?;
989 let exception_count = reader.u32()? as usize;
990 let positions = decode_chunk(reader, scratch)?;
991 let exceptions = decode_chunk(reader, scratch)?;
992 if positions.len() != exception_count || exceptions.len() != exception_count {
993 return Err(Error::internal("a sparse chunk disagrees about its exception count"));
994 }
995 let mut values = vec![value; count];
996 for (position, exception) in positions.into_iter().zip(exceptions) {
997 let position = usize::try_from(position)
998 .ok()
999 .filter(|position| *position < count)
1000 .ok_or_else(|| {
1001 Error::internal(format!("exception at {position} is outside the chunk"))
1002 })?;
1003 values[position] = exception;
1004 }
1005 Ok(values)
1006 }
1007 Kind::Strided => {
1008 let base = reader.i64()?;
1009 let stride = reader.u64()?;
1010 let steps = decode_chunk(reader, scratch)?;
1011 check_count(steps.len(), count)?;
1012 let mut values = Vec::with_capacity(count);
1013 for step in steps {
1014 let step = u64::try_from(step)
1015 .map_err(|_| Error::internal("a negative number of strides"))?;
1016 values.push(value_from(step.wrapping_mul(stride), base));
1017 }
1018 Ok(values)
1019 }
1020 }
1021}
1022
1023fn decode_chunk_as<T: Lane>(reader: &mut Reader<'_>, scratch: &mut Decoding) -> Result<Vec<T>> {
1025 let Some(&tag) = reader.rest().first() else {
1026 return Err(Error::internal("a chunk ended before its encoding tag"));
1027 };
1028 if !matches!(Kind::from_tag(tag)?, Kind::Constant | Kind::Packed | Kind::Sparse | Kind::Rle) {
1029 return decode_chunk(reader, scratch)?.into_iter().map(lane).collect();
1030 }
1031 let kind = Kind::from_tag(reader.u8()?)?;
1032 let count = reader.u32()? as usize;
1033 match kind {
1034 Kind::Constant => Ok(vec![lane(reader.i64()?)?; count]),
1035 Kind::Packed => {
1036 let mut values = vec![T::default(); count];
1037 scratch.ready();
1038 let mut wide = [0i64; VALUES];
1039 let mut done = 0;
1040 while done < count {
1041 let base = reader.i64()?;
1042 let width = reader.u8()? as usize;
1043 let wanted = (count - done).min(VALUES);
1044 let into = &mut values[done..done + wanted];
1045 let bytes = if wanted == VALUES {
1046 let words = bitpack::packed_len::<u64>(width);
1047 for word in &mut scratch.packed[..words] {
1048 *word = reader.u64()?;
1049 }
1050 None
1051 } else {
1052 Some(reader.bytes(bitpack::tail_len(wanted, width))?)
1053 };
1054 let mask = if width >= 64 { u64::MAX } else { (1u64 << width) - 1 };
1059 let top = i64::try_from(i128::from(base) + i128::from(mask)).ok();
1060 if T::fit(base).is_some() && top.and_then(T::fit).is_some() {
1061 let map = |offset| T::wrap(value_from(offset, base));
1062 match bytes {
1063 None => {
1064 let words = bitpack::packed_len::<u64>(width);
1065 bitpack::unpack_mapped(&scratch.packed[..words], width, into, map)?;
1066 }
1067 Some(bytes) => bitpack::unpack_tail_into(bytes, width, into, map)?,
1068 }
1069 } else {
1070 let wide = &mut wide[..wanted];
1071 let map = |offset| value_from(offset, base);
1072 match bytes {
1073 None => {
1074 let words = bitpack::packed_len::<u64>(width);
1075 bitpack::unpack_mapped(&scratch.packed[..words], width, wide, map)?;
1076 }
1077 Some(bytes) => bitpack::unpack_tail_into(bytes, width, wide, map)?,
1078 }
1079 for (value, &held) in into.iter_mut().zip(wide.iter()) {
1080 *value = lane(held)?;
1081 }
1082 }
1083 done += wanted;
1084 }
1085 Ok(values)
1086 }
1087 Kind::Rle => {
1088 let run_values = decode_chunk(reader, scratch)?;
1089 let run_lengths = decode_chunk(reader, scratch)?;
1090 if run_values.len() != run_lengths.len() {
1091 return Err(Error::internal("an RLE chunk has more runs than run lengths"));
1092 }
1093 let long = run_values.len().saturating_mul(LONG_RUN) <= count;
1095 let mut values =
1096 if long { Vec::with_capacity(count) } else { vec![T::default(); count + RUN] };
1097 let mut at = 0usize;
1098 for (value, length) in run_values.into_iter().zip(run_lengths) {
1099 let value = lane::<T>(value)?;
1100 let length = usize::try_from(length)
1101 .map_err(|_| Error::internal("a negative RLE run length"))?;
1102 let end = at
1103 .checked_add(length)
1104 .filter(|end| *end <= count)
1105 .ok_or_else(|| Error::internal("an RLE run ends past its chunk"))?;
1106 if long {
1107 values.resize(end, value);
1108 } else {
1109 let short =
1110 if length <= RUN { values[at..].first_chunk_mut::<RUN>() } else { None };
1111 match short {
1112 Some(window) => window.fill(value),
1113 None => values[at..end].fill(value),
1114 }
1115 }
1116 at = end;
1117 }
1118 check_count(at, count)?;
1119 values.truncate(count);
1120 Ok(values)
1121 }
1122 Kind::Sparse => {
1123 let value = lane::<T>(reader.i64()?)?;
1124 let exception_count = reader.u32()? as usize;
1125 let positions = decode_chunk(reader, scratch)?;
1126 let exceptions = decode_chunk(reader, scratch)?;
1127 if positions.len() != exception_count || exceptions.len() != exception_count {
1128 return Err(Error::internal("a sparse chunk disagrees about its exception count"));
1129 }
1130 let mut values = vec![value; count];
1131 for (position, exception) in positions.into_iter().zip(exceptions) {
1132 let position = usize::try_from(position)
1133 .ok()
1134 .filter(|position| *position < count)
1135 .ok_or_else(|| {
1136 Error::internal(format!("exception at {position} is outside the chunk"))
1137 })?;
1138 values[position] = lane(exception)?;
1139 }
1140 Ok(values)
1141 }
1142 Kind::Delta | Kind::Dict | Kind::Strided => {
1143 Err(Error::internal("a chunk kind that decodes wide reached the narrow decoder"))
1144 }
1145 }
1146}
1147
1148fn decode_selected_chunk(
1149 reader: &mut Reader<'_>,
1150 positions: &[usize],
1151 scratch: &mut Decoding,
1152) -> Result<Vec<i64>> {
1153 let Some(&tag) = reader.rest().first() else {
1154 return Err(Error::internal("a chunk ended before its encoding tag"));
1155 };
1156 let kind = Kind::from_tag(tag)?;
1157 if !matches!(kind, Kind::Constant | Kind::Packed | Kind::Rle) {
1158 let values = decode_chunk(reader, scratch)?;
1159 return positions
1160 .iter()
1161 .map(|&position| {
1162 values.get(position).copied().ok_or_else(|| {
1163 Error::internal(format!(
1164 "selected integer position {position} is outside {} values",
1165 values.len()
1166 ))
1167 })
1168 })
1169 .collect();
1170 }
1171
1172 let decoded = Kind::from_tag(reader.u8()?)?;
1173 debug_assert_eq!(decoded, kind);
1174 let count = reader.u32()? as usize;
1175 if positions.last().is_some_and(|&position| position >= count) {
1176 return Err(Error::internal(format!(
1177 "selected integer position {} is outside {count} values",
1178 positions.last().expect("a last position exists")
1179 )));
1180 }
1181 match kind {
1182 Kind::Constant => {
1183 let value = reader.i64()?;
1184 Ok(vec![value; positions.len()])
1185 }
1186 Kind::Packed => {
1187 let mut out = Vec::with_capacity(positions.len());
1188 let mut from = 0;
1189 let mut done = 0;
1190 while done < count {
1191 let base = reader.i64()?;
1192 let width = reader.u8()? as usize;
1193 let wanted = (count - done).min(VALUES);
1194 let upto = positions.partition_point(|&position| position < done + wanted);
1195 if wanted == VALUES {
1196 let bytes = reader.bytes(bitpack::packed_len::<u64>(width) * 8)?;
1197 for &position in &positions[from..upto] {
1198 let offset = bitpack::unpack_u64_at(bytes, width, position - done)?;
1199 out.push(value_from(offset, base));
1200 }
1201 } else {
1202 let bytes = reader.bytes(bitpack::tail_len(wanted, width))?;
1203 for &position in &positions[from..upto] {
1204 let offset = bitpack::tail_at(bytes, width, position - done)?;
1205 out.push(value_from(offset, base));
1206 }
1207 }
1208 from = upto;
1209 done += wanted;
1210 }
1211 Ok(out)
1212 }
1213 Kind::Rle => {
1214 let run_value_bytes = reader.rest();
1215 let mut run_value_reader = Reader::new(run_value_bytes);
1216 let run_value_count = skip_chunk(&mut run_value_reader)?;
1217 let run_value_len = run_value_reader.used();
1218 reader.skip(run_value_len)?;
1219 let run_lengths = decode_chunk(reader, scratch)?;
1220 if run_value_count != run_lengths.len() {
1221 return Err(Error::internal("an RLE chunk has more runs than run lengths"));
1222 }
1223 let mut wanted_runs = Vec::new();
1224 let mut selected_per_run = Vec::new();
1225 let mut selected = 0;
1226 let mut at = 0usize;
1227 for (run, length) in run_lengths.into_iter().enumerate() {
1228 let length = usize::try_from(length)
1229 .map_err(|_| Error::internal("a negative RLE run length"))?;
1230 let end = at
1231 .checked_add(length)
1232 .filter(|end| *end <= count)
1233 .ok_or_else(|| Error::internal("an RLE run ends past its chunk"))?;
1234 let before = selected;
1235 while selected < positions.len() && positions[selected] < end {
1236 if positions[selected] < at {
1237 return Err(Error::internal("selected integer positions went backwards"));
1238 }
1239 selected += 1;
1240 }
1241 if selected != before {
1242 wanted_runs.push(run);
1243 selected_per_run.push(selected - before);
1244 }
1245 at = end;
1246 }
1247 check_count(at, count)?;
1248 if selected != positions.len() {
1249 return Err(Error::internal("an RLE chunk ended before a selected position"));
1250 }
1251 let run_values = decode_selected(&run_value_bytes[..run_value_len], &wanted_runs)?;
1252 let mut out = Vec::with_capacity(positions.len());
1253 for (value, repeat) in run_values.into_iter().zip(selected_per_run) {
1254 out.extend(std::iter::repeat_n(value, repeat));
1255 }
1256 Ok(out)
1257 }
1258 _ => unreachable!("unsupported kinds used the full decoder"),
1259 }
1260}
1261
1262fn skip_chunk(reader: &mut Reader<'_>) -> Result<usize> {
1264 let kind = Kind::from_tag(reader.u8()?)?;
1265 let count = reader.u32()? as usize;
1266 match kind {
1267 Kind::Constant => reader.skip(8)?,
1268 Kind::Packed => skip_packed(reader, count)?,
1269 Kind::Delta => {
1270 reader.skip(8)?;
1271 skip_chunk(reader)?;
1272 }
1273 Kind::Rle | Kind::Dict => {
1274 skip_chunk(reader)?;
1275 skip_chunk(reader)?;
1276 }
1277 Kind::Sparse => {
1278 reader.skip(12)?;
1279 skip_chunk(reader)?;
1280 skip_chunk(reader)?;
1281 }
1282 Kind::Strided => {
1283 reader.skip(16)?;
1284 skip_chunk(reader)?;
1285 }
1286 }
1287 Ok(count)
1288}
1289
1290fn skip_packed(reader: &mut Reader<'_>, count: usize) -> Result<()> {
1292 let mut done = 0;
1293 while done < count {
1294 reader.skip(8)?;
1295 let width = reader.u8()? as usize;
1296 if width > 64 {
1297 return Err(Error::internal(format!("a packed integer width of {width} is past 64")));
1298 }
1299 let wanted = (count - done).min(VALUES);
1300 let bytes = if wanted == VALUES {
1301 bitpack::packed_len::<u64>(width)
1302 .checked_mul(8)
1303 .ok_or_else(|| Error::internal("packed integer size overflow"))?
1304 } else {
1305 bitpack::tail_len(wanted, width)
1306 };
1307 reader.skip(bytes)?;
1308 done += wanted;
1309 }
1310 Ok(())
1311}
1312
1313fn shape_chunk(reader: &mut Reader<'_>, kinds: &mut Vec<Kind>) -> Result<()> {
1315 let kind = Kind::from_tag(reader.u8()?)?;
1316 let count = reader.u32()? as usize;
1317 kinds.push(kind);
1318 match kind {
1319 Kind::Constant => reader.skip(8)?,
1320 Kind::Packed => skip_packed(reader, count)?,
1321 Kind::Delta => {
1322 reader.skip(8)?;
1323 shape_chunk(reader, kinds)?;
1324 }
1325 Kind::Rle | Kind::Dict => {
1326 shape_chunk(reader, kinds)?;
1327 shape_chunk(reader, kinds)?;
1328 }
1329 Kind::Sparse => {
1330 reader.skip(12)?;
1331 shape_chunk(reader, kinds)?;
1332 shape_chunk(reader, kinds)?;
1333 }
1334 Kind::Strided => {
1335 reader.skip(16)?;
1336 shape_chunk(reader, kinds)?;
1337 }
1338 }
1339 Ok(())
1340}
1341
1342fn describe_chunk(reader: &mut Reader<'_>) -> Result<String> {
1343 let kind = Kind::from_tag(reader.u8()?)?;
1344 let count = reader.u32()? as usize;
1345 Ok(match kind {
1346 Kind::Constant => {
1347 reader.i64()?;
1348 "CONSTANT".to_string()
1349 }
1350 Kind::Packed => {
1351 let mut widths = Vec::new();
1352 let mut seen = 0;
1353 while seen < count {
1354 reader.i64()?;
1355 let width = reader.u8()? as usize;
1356 let wanted = (count - seen).min(VALUES);
1357 if wanted == VALUES {
1358 for _ in 0..bitpack::packed_len::<u64>(width) {
1359 reader.u64()?;
1360 }
1361 } else {
1362 reader.bytes(bitpack::tail_len(wanted, width))?;
1363 }
1364 widths.push(width);
1365 seen += wanted;
1366 }
1367 let low = widths.iter().copied().min().unwrap_or(0);
1368 let high = widths.iter().copied().max().unwrap_or(0);
1369 if low == high {
1372 format!("FOR+BITPACK[{low}]")
1373 } else {
1374 format!("FOR+BITPACK[{low}..{high}]")
1375 }
1376 }
1377 Kind::Delta => {
1378 reader.i64()?;
1379 format!("DELTA({})", describe_chunk(reader)?)
1380 }
1381 Kind::Rle => {
1382 let values = describe_chunk(reader)?;
1383 let lengths = describe_chunk(reader)?;
1384 format!("RLE({values}, {lengths})")
1385 }
1386 Kind::Dict => {
1387 let dictionary = describe_chunk(reader)?;
1388 let codes = describe_chunk(reader)?;
1389 format!("DICT({dictionary}, {codes})")
1390 }
1391 Kind::Sparse => {
1392 reader.i64()?;
1393 reader.u32()?;
1394 let positions = describe_chunk(reader)?;
1395 let exceptions = describe_chunk(reader)?;
1396 format!("SPARSE({positions}, {exceptions})")
1397 }
1398 Kind::Strided => {
1399 reader.i64()?;
1400 let stride = reader.u64()?;
1401 format!("STRIDE[{stride}]({})", describe_chunk(reader)?)
1402 }
1403 })
1404}
1405
1406fn stride_of(values: &[i64]) -> Option<u64> {
1418 stride_from(values, values.iter().min().copied()?)
1419}
1420
1421fn stride_from(values: &[i64], base: i64) -> Option<u64> {
1423 let mut divisor = 0u64;
1424 for value in values {
1425 divisor = gcd(divisor, offset_from(*value, base));
1426 if divisor == 1 {
1427 return None;
1428 }
1429 }
1430 (divisor > 1).then_some(divisor)
1433}
1434
1435fn gcd(mut left: u64, mut right: u64) -> u64 {
1437 if left == 0 {
1438 return right;
1439 }
1440 if right == 0 {
1441 return left;
1442 }
1443 let shift = (left | right).trailing_zeros();
1444 left >>= left.trailing_zeros();
1445 loop {
1446 right >>= right.trailing_zeros();
1447 if left > right {
1448 std::mem::swap(&mut left, &mut right);
1449 }
1450 right -= left;
1451 if right == 0 {
1452 return left << shift;
1453 }
1454 }
1455}
1456
1457fn offset_from(value: i64, base: i64) -> u64 {
1460 (i128::from(value) - i128::from(base)) as u64
1461}
1462
1463fn value_from(offset: u64, base: i64) -> i64 {
1464 (i128::from(base) + i128::from(offset)) as i64
1465}
1466
1467fn zigzag(value: i64) -> u64 {
1470 ((value << 1) ^ (value >> 63)) as u64
1471}
1472
1473fn unzigzag(value: u64) -> i64 {
1474 ((value >> 1) as i64) ^ -((value & 1) as i64)
1475}
1476
1477fn deltas_fit(values: &[i64]) -> bool {
1489 values.windows(2).all(|pair| pair[1].checked_sub(pair[0]).is_some())
1490}
1491
1492fn deltas(values: &[i64]) -> Option<Vec<i64>> {
1493 let mut deltas = Vec::with_capacity(values.len().saturating_sub(1));
1494 for pair in values.windows(2) {
1495 let difference = pair[1].checked_sub(pair[0])?;
1496 deltas.push(zigzag(difference) as i64);
1497 }
1498 Some(deltas)
1499}
1500
1501fn runs(values: &[i64]) -> (Vec<i64>, Vec<i64>) {
1508 let mut run_values: Vec<i64> = Vec::new();
1509 let mut run_lengths: Vec<i64> = Vec::new();
1510 let mut start = 0;
1511 while let Some(&value) = values.get(start) {
1512 let length = values[start..].iter().take_while(|other| **other == value).count();
1513 run_values.push(value);
1514 run_lengths.push(length as i64);
1515 start += length;
1516 }
1517 (run_values, run_lengths)
1518}
1519
1520fn spread_of(values: &[i64]) -> (usize, Option<(i64, usize)>) {
1535 let mut sorted = values.to_vec();
1536 sorted.sort_unstable();
1537 let mut distinct = 0;
1538 let mut best: Option<(i64, usize)> = None;
1539 let mut index = 0;
1540 while index < sorted.len() {
1541 let value = sorted[index];
1542 let mut end = index;
1543 while end < sorted.len() && sorted[end] == value {
1544 end += 1;
1545 }
1546 distinct += 1;
1547 let count = end - index;
1548 if best.is_none_or(|(_, seen)| count > seen) {
1549 best = Some((value, count));
1550 }
1551 index = end;
1552 }
1553 (distinct, best)
1554}
1555
1556fn majority(values: &[i64]) -> Option<(i64, usize)> {
1564 let mut candidate = *values.first()?;
1565 let mut lead = 0usize;
1566 for value in values {
1567 if lead == 0 {
1568 candidate = *value;
1569 lead = 1;
1570 } else if *value == candidate {
1571 lead += 1;
1572 } else {
1573 lead -= 1;
1574 }
1575 }
1576 let count = values.iter().filter(|value| **value == candidate).count();
1577 (count * 2 > values.len()).then_some((candidate, count))
1578}
1579
1580fn distinct_values(values: &[i64]) -> Vec<i64> {
1583 let mut distinct = values.to_vec();
1584 distinct.sort_unstable();
1585 distinct.dedup();
1586 distinct
1587}
1588
1589fn codes_over(values: &[i64], dictionary: &[i64]) -> Vec<i64> {
1599 values
1600 .iter()
1601 .map(|value| {
1602 dictionary
1603 .binary_search(value)
1604 .expect("the dictionary is the distinct values of this chunk") as i64
1605 })
1606 .collect()
1607}
1608
1609fn check_count(actual: usize, expected: usize) -> Result<()> {
1610 if actual == expected {
1611 Ok(())
1612 } else {
1613 Err(Error::internal(format!(
1614 "a chunk says it holds {expected} values and decoded to {actual}"
1615 )))
1616 }
1617}
1618
1619fn too_long(len: usize) -> Error {
1620 Error::internal(format!("a chunk of {len} values is longer than the format allows"))
1621}
1622
1623fn put_u8(out: &mut Vec<u8>, value: u8) {
1624 out.push(value);
1625}
1626
1627fn put_u32(out: &mut Vec<u8>, value: u32) {
1628 out.extend_from_slice(&value.to_le_bytes());
1629}
1630
1631fn put_u64(out: &mut Vec<u8>, value: u64) {
1632 out.extend_from_slice(&value.to_le_bytes());
1633}
1634
1635fn put_i64(out: &mut Vec<u8>, value: i64) {
1636 out.extend_from_slice(&value.to_le_bytes());
1637}
1638
1639#[cfg(test)]
1640mod tests {
1641 use super::*;
1642
1643 fn round_trip(values: &[i64]) -> Vec<u8> {
1644 let bytes = encode(values).unwrap();
1645 assert_eq!(decode(&bytes).unwrap(), values, "{}", describe(&bytes).unwrap());
1646 bytes
1647 }
1648
1649 fn kind_of(bytes: &[u8]) -> Kind {
1650 Kind::from_tag(bytes[0]).unwrap()
1651 }
1652
1653 struct Random(u64);
1655
1656 impl Random {
1657 fn new() -> Self {
1658 Self(0x9e37_79b9_7f4a_7c15)
1659 }
1660
1661 fn next(&mut self) -> u64 {
1662 self.0 ^= self.0 << 13;
1663 self.0 ^= self.0 >> 7;
1664 self.0 ^= self.0 << 17;
1665 self.0
1666 }
1667 }
1668
1669 #[test]
1670 fn the_dictionary_is_sorted_and_the_codes_point_back_at_the_values() {
1671 let values = vec![30i64, 10, 30, 20, 10, -5];
1672 let dictionary = distinct_values(&values);
1673 let codes = codes_over(&values, &dictionary);
1674 assert_eq!(dictionary, vec![-5, 10, 20, 30]);
1675 assert_eq!(codes, vec![3, 1, 3, 2, 1, 0]);
1676 for (code, value) in codes.iter().zip(&values) {
1677 assert_eq!(dictionary[*code as usize], *value);
1678 }
1679 }
1680
1681 #[test]
1682 fn one_sort_gives_the_distinct_count_and_the_most_frequent_value() {
1683 let values = vec![7i64, 7, 7, 1, 2, 2];
1684 assert_eq!(spread_of(&values), (3, Some((7, 3))));
1685 assert_eq!(spread_of(&[]), (0, None));
1686 assert_eq!(spread_of(&[9]), (1, Some((9, 1))));
1687
1688 assert_eq!(spread_of(&[4i64, 4, 8, 8]), (2, Some((4, 2))));
1691 }
1692
1693 #[test]
1694 fn the_majority_is_the_most_frequent_value_whenever_there_is_one() {
1695 let chunks: Vec<Vec<i64>> = vec![
1696 vec![],
1697 vec![3],
1698 vec![1, 2],
1699 vec![1, 1, 2],
1700 vec![2, 1, 1],
1701 vec![4, 4, 8, 8],
1702 vec![7, 1, 7, 2, 7, 3, 7],
1703 vec![1, 2, 3, 9, 9, 9, 9],
1704 (0..1000).map(|index| if index % 5 == 0 { index } else { -4 }).collect(),
1705 (0..1000).map(|index| index % 3).collect(),
1706 ];
1707 for chunk in chunks {
1708 let (_, dominant) = spread_of(&chunk);
1709 let expected = dominant.filter(|(_, count)| count * 2 > chunk.len());
1710 assert_eq!(majority(&chunk), expected, "{chunk:?}");
1711 }
1712 }
1713
1714 #[test]
1718 fn the_one_pass_offers_what_the_separate_tests_offered() {
1719 let mut random = Random::new();
1720 let mut chunks: Vec<Vec<i64>> = vec![
1721 vec![],
1722 vec![5],
1723 vec![5, 5, 5],
1724 vec![i64::MIN, i64::MAX],
1725 vec![i64::MAX, i64::MIN, i64::MAX],
1726 vec![i64::MIN, 0, i64::MAX],
1727 vec![-1, i64::MAX],
1728 (0..1000).map(|index| if index % 5 == 0 { index } else { -4 }).collect(),
1729 (0..1000).map(|index| if index % 4 == 0 { index } else { -4 }).collect(),
1730 (0..1000).map(|index| index / 7).collect(),
1731 (0..1000).map(|index| index * 1_000_000).collect(),
1732 ];
1733 for _ in 0..200 {
1734 let len = (random.next() % 300) as usize;
1735 let spread = 1 + random.next() % 8;
1736 let common = (random.next() % 5) as i64;
1737 chunks.push(
1738 (0..len)
1739 .map(|_| {
1740 let draw = random.next();
1741 if draw % 10 < spread { (draw >> 8) as i64 % 50 } else { common }
1742 })
1743 .collect(),
1744 );
1745 }
1746 for chunk in chunks {
1747 let mut expected = vec![Kind::Packed];
1748 if !chunk.is_empty() {
1749 if chunk.iter().all(|value| *value == chunk[0]) {
1750 expected = vec![Kind::Constant];
1751 } else {
1752 let runs = 1 + chunk.windows(2).filter(|pair| pair[0] != pair[1]).count();
1753 let low = *chunk.iter().min().unwrap();
1754 let high = *chunk.iter().max().unwrap();
1755 let bits = |range: u128| 128 - range.leading_zeros();
1756 let width = bits((i128::from(high) - i128::from(low)) as u128);
1757 let zigzags: Vec<u128> = chunk
1758 .windows(2)
1759 .map(|pair| u128::from(zigzag(pair[1].wrapping_sub(pair[0]))))
1760 .collect();
1761 let spread = zigzags.iter().max().unwrap() - zigzags.iter().min().unwrap();
1762 let turns = 1 + zigzags.windows(2).filter(|pair| pair[0] != pair[1]).count();
1763 let mut first: Vec<u128> = zigzags.iter().take(64).copied().collect();
1764 first.sort_unstable();
1765 first.dedup();
1766 let pays = bits(spread) < width
1767 || (runs * 4 > chunk.len() * 3
1768 && (turns * 4 <= (chunk.len() - 1) * 3 || first.len() <= 4));
1769 if deltas_fit(&chunk) && pays {
1770 expected.push(Kind::Delta);
1771 }
1772 if runs * 4 <= chunk.len() * 3 {
1773 expected.push(Kind::Rle);
1774 }
1775 let distinct = spread_of(&chunk).0;
1776 if distinct * 2 <= chunk.len() && bits(distinct as u128 - 1) < width {
1777 expected.push(Kind::Dict);
1778 }
1779 if majority(&chunk).is_some_and(|(_, count)| count * 10 >= chunk.len() * 8) {
1780 expected.push(Kind::Sparse);
1781 }
1782 if stride_of(&chunk).is_some() {
1783 expected.push(Kind::Strided);
1784 }
1785 }
1786 }
1787 assert_eq!(candidates(&chunk, 0, &EXHAUSTIVE), expected, "{chunk:?}");
1788 }
1789 }
1790
1791 #[test]
1792 fn runs_are_every_stretch_of_equal_neighbours_in_order() {
1793 assert_eq!(runs(&[]), (vec![], vec![]));
1794 assert_eq!(runs(&[4]), (vec![4], vec![1]));
1795 assert_eq!(runs(&[1, 1, 2, 1, 1, 1]), (vec![1, 2, 1], vec![2, 1, 3]));
1796 }
1797
1798 #[test]
1799 fn deltas_that_do_not_fit_are_refused_before_they_are_built() {
1800 assert!(deltas_fit(&[1i64, 2, 3]));
1801 assert!(deltas_fit(&[i64::MAX, i64::MAX]));
1802 assert!(!deltas_fit(&[i64::MIN, i64::MAX]));
1803 assert_eq!(deltas_fit(&[i64::MIN, i64::MAX]), deltas(&[i64::MIN, i64::MAX]).is_some());
1804 assert_eq!(deltas_fit(&[1i64, 2, 3]), deltas(&[1i64, 2, 3]).is_some());
1805 }
1806
1807 #[test]
1808 fn what_the_chooser_returns_is_the_smallest_of_what_it_was_offered() {
1809 let mut random = Random::new();
1813 let noise: Vec<i64> = (0..2000).map(|_| (random.next() % 5000) as i64).collect();
1814 let runs: Vec<i64> = (0..2000).map(|index: i64| index / 100).collect();
1815 let climbing: Vec<i64> = (0..2000).map(|index| 1_700_000_000 + index).collect();
1816 for values in [noise, runs, climbing, vec![7; 300], Vec::new()] {
1817 let chosen = encode(&values).unwrap();
1818 let mut smallest: Option<Vec<u8>> = None;
1819 for kind in offered(&values) {
1820 let Some(bytes) = encode_only(kind, &values).unwrap() else {
1821 continue;
1822 };
1823 if smallest.as_ref().is_none_or(|best| bytes.len() < best.len()) {
1824 smallest = Some(bytes);
1825 }
1826 }
1827 assert_eq!(smallest.as_deref(), Some(chosen.as_slice()), "{}", values.len());
1828 }
1829 }
1830
1831 #[test]
1832 fn a_column_of_whole_seconds_in_microseconds_pays_nothing_for_the_zeroes() {
1833 let mut random = Random::new();
1837 let day = 1_374_000_000_000_000i64;
1838 let values: Vec<i64> =
1839 (0..100_000).map(|_| day + (random.next() % 68_400) as i64 * 1_000_000).collect();
1840 let bytes = round_trip(&values);
1841 assert_eq!(kind_of(&bytes), Kind::Strided);
1842 assert!(describe(&bytes).unwrap().starts_with("STRIDE[1000000]"), "{:?}", describe(&bytes));
1843 let strided = 100_000 * 17 / 8;
1845 assert!(bytes.len() < strided + 2000, "{} bytes for {strided} of payload", bytes.len());
1846
1847 let plain = encode_only(Kind::Packed, &values).unwrap().expect("packing always applies");
1848 assert!(
1849 bytes.len() * 2 < plain.len(),
1850 "{} strided against {} packed",
1851 bytes.len(),
1852 plain.len()
1853 );
1854 }
1855
1856 #[test]
1857 fn a_stride_is_the_common_factor_of_the_distances_from_the_smallest_value() {
1858 assert_eq!(stride_of(&[10i64, 20, 40]), Some(10));
1859 assert_eq!(stride_of(&[7i64, 17, 37]), Some(10));
1862 assert_eq!(stride_of(&[10i64, 20, 23]), None);
1863 assert_eq!(stride_of(&[5i64; 100]), None);
1866 assert_eq!(stride_of(&[]), None);
1867 assert_eq!(stride_of(&[i64::MIN, i64::MAX]), Some(u64::MAX));
1869 }
1870
1871 #[test]
1872 fn a_stride_across_the_whole_of_the_type_round_trips() {
1873 for values in [vec![i64::MIN, i64::MAX], vec![i64::MIN, 0, i64::MAX]] {
1876 let bytes = round_trip(&values);
1877 assert_eq!(decode(&bytes).unwrap(), values);
1878 }
1879 }
1880
1881 #[test]
1882 fn a_column_with_no_common_factor_is_not_offered_a_stride() {
1883 let mut random = Random::new();
1884 let values: Vec<i64> = (0..2000).map(|_| (random.next() % 1_000_000) as i64).collect();
1885 assert!(!offered(&values).contains(&Kind::Strided));
1886 assert!(encode_only(Kind::Strided, &values).unwrap().is_none());
1887 }
1888
1889 #[test]
1890 fn an_empty_chunk_round_trips() {
1891 let bytes = round_trip(&[]);
1892 assert_eq!(bytes.len(), 5);
1893 }
1894
1895 #[test]
1896 fn a_constant_column_costs_thirteen_bytes_however_long_it_is() {
1897 let bytes = round_trip(&vec![42; 1_000_000]);
1898 assert_eq!(kind_of(&bytes), Kind::Constant);
1899 assert_eq!(bytes.len(), 13);
1900 }
1901
1902 #[test]
1903 fn a_narrow_range_is_packed_at_the_width_of_the_range_and_not_of_the_type() {
1904 let mut random = Random::new();
1906 let values: Vec<i64> = (0..100_000).map(|_| 1000 + (random.next() % 64) as i64).collect();
1907 let bytes = round_trip(&values);
1908 assert_eq!(kind_of(&bytes), Kind::Packed);
1909 let packed = 100_000 * 6 / 8;
1910 assert!(bytes.len() < packed + 2000, "{} bytes for {packed} of payload", bytes.len());
1911 assert!(bytes.len() > packed, "{} bytes cannot hold {packed}", bytes.len());
1912 }
1913
1914 #[test]
1915 fn a_counter_becomes_deltas_and_then_a_constant() {
1916 let values: Vec<i64> = (0..1_000_000).collect();
1919 let bytes = round_trip(&values);
1920 assert_eq!(kind_of(&bytes), Kind::Delta);
1921 assert_eq!(describe(&bytes).unwrap(), "DELTA(CONSTANT)");
1922 assert!(bytes.len() < 40, "{} bytes for a counter", bytes.len());
1923 }
1924
1925 #[test]
1926 fn a_column_that_counts_down_is_as_cheap_as_one_that_counts_up() {
1927 let up: Vec<i64> = (0..100_000).collect();
1929 let down: Vec<i64> = (0..100_000).rev().collect();
1930 assert_eq!(round_trip(&up).len(), round_trip(&down).len());
1931 }
1932
1933 #[test]
1934 fn long_runs_become_rle() {
1935 let mut values = Vec::new();
1936 for run in 0..1000 {
1937 values.extend(std::iter::repeat_n(run % 7, 200));
1938 }
1939 let bytes = round_trip(&values);
1940 assert_eq!(kind_of(&bytes), Kind::Rle);
1941 assert!(bytes.len() < 2000, "{} bytes for 1000 runs", bytes.len());
1942 }
1943
1944 #[test]
1945 fn a_low_cardinality_column_becomes_a_dictionary() {
1946 let mut random = Random::new();
1952 let dictionary: Vec<i64> =
1953 (0..40).map(|_| 1_000_000_000 + (random.next() % (1 << 30)) as i64).collect();
1954 let values: Vec<i64> =
1955 (0..100_000).map(|_| dictionary[(random.next() % 40) as usize]).collect();
1956 let bytes = round_trip(&values);
1957 assert_eq!(kind_of(&bytes), Kind::Dict);
1958 assert!(bytes.len() < 100_000, "{} bytes", bytes.len());
1959 }
1960
1961 #[test]
1962 fn a_nearly_constant_column_becomes_sparse() {
1963 let mut values = vec![0i64; 100_000];
1964 for index in 0..300 {
1965 values[index * 331] = 1 << 40;
1966 }
1967 let bytes = round_trip(&values);
1968 assert_eq!(kind_of(&bytes), Kind::Sparse);
1969 assert!(bytes.len() < 3000, "{} bytes for 300 exceptions", bytes.len());
1970 }
1971
1972 #[test]
1973 fn encoded_counts_match_decoded_rows_across_integer_shapes() {
1974 let mut sparse = vec![0_i64; 4096];
1975 for (index, value) in [(7, -3), (91, 12), (1001, -3), (3000, 12)] {
1976 sparse[index] = value;
1977 }
1978 let mut runs = Vec::new();
1979 for value in [0, 7, 0, -5] {
1980 runs.extend(std::iter::repeat_n(value, 500));
1981 }
1982 let mut random = Random::new();
1983 let packed = (0..2000).map(|_| (random.next() % 251) as i64).collect::<Vec<_>>();
1984 for values in [vec![0_i64; 1024], sparse, runs, packed] {
1985 let bytes = encode(&values).unwrap();
1986 let (rows, counts) = tally(&bytes).unwrap();
1987 let mut expected = BTreeMap::<i64, u64>::new();
1988 for value in decode(&bytes).unwrap() {
1989 *expected.entry(value).or_default() += 1;
1990 }
1991 assert_eq!(rows, values.len());
1992 assert_eq!(counts, expected.into_iter().collect::<Vec<_>>());
1993 }
1994 }
1995
1996 #[test]
1997 fn folded_sparse_exceptions_keep_the_last_value_at_a_repeated_position() {
1998 let mut bytes = vec![Kind::Sparse.tag()];
1999 put_u32(&mut bytes, 10);
2000 put_i64(&mut bytes, 0);
2001 put_u32(&mut bytes, 2);
2002 bytes.extend(encode(&[7, 7]).unwrap());
2003 bytes.extend(encode(&[3, 5]).unwrap());
2004
2005 let mut counts = BTreeMap::<i64, u64>::new();
2006 assert_eq!(
2007 fold(&bytes, |value, count| {
2008 *counts.entry(value).or_default() += count;
2009 Ok(())
2010 })
2011 .unwrap(),
2012 10
2013 );
2014 assert_eq!(counts, BTreeMap::from([(0, 9), (5, 1)]));
2015 assert_eq!(decode(&bytes).unwrap()[7], 5);
2016 }
2017
2018 #[test]
2019 fn the_cascade_goes_more_than_one_level_deep() {
2020 let mut values = Vec::new();
2023 for index in 0..2000i64 {
2024 values.extend(std::iter::repeat_n(1_000_000 + (index % 5) * 104_729, 100));
2025 }
2026 let bytes = round_trip(&values);
2027 let shape = describe(&bytes).unwrap();
2028 assert!(shape.contains('('), "{shape} is not a cascade");
2029 assert!(bytes.len() < 4000, "{} bytes: {shape}", bytes.len());
2030 }
2031
2032 #[test]
2033 fn random_data_is_packed_at_full_width_and_costs_what_it_costs() {
2034 let mut random = Random::new();
2037 let values: Vec<i64> = (0..10_000).map(|_| random.next() as i64).collect();
2038 let bytes = round_trip(&values);
2039 assert_eq!(kind_of(&bytes), Kind::Packed);
2040 assert!(bytes.len() < 10_000 * 8 + 1000, "{} bytes", bytes.len());
2041 }
2042
2043 #[test]
2044 fn the_extremes_of_the_type_survive() {
2045 let values = vec![i64::MIN, i64::MAX, 0, -1, i64::MIN, i64::MAX];
2048 round_trip(&values);
2049 round_trip(&[i64::MIN; 3]);
2050 round_trip(&[i64::MIN, i64::MIN + 1]);
2051 }
2052
2053 #[test]
2054 fn a_chunk_that_is_not_a_multiple_of_the_unit_round_trips() {
2055 for len in [1, 2, 1023, 1024, 1025, 2047, 2049] {
2056 let values: Vec<i64> = (0..len).map(|index| (index * 31 % 97) as i64).collect();
2057 round_trip(&values);
2058 }
2059 }
2060
2061 #[test]
2062 fn units_of_different_widths_in_one_chunk_do_not_read_each_others_leftovers() {
2063 let mut random = Random::new();
2075 let mut values = Vec::new();
2076 for width in [40u32, 3, 61, 1, 17, 40] {
2077 for _ in 0..1024 {
2078 values.push((random.next() & ((1u64 << width) - 1)) as i64);
2079 }
2080 }
2081 let bytes = encode(&values).unwrap();
2082 let described = describe(&bytes).unwrap();
2083 assert!(described.starts_with("FOR+BITPACK"), "expected one packed chunk, got {described}");
2084 assert_eq!(decode(&bytes).unwrap(), values, "{described}");
2085 }
2086
2087 #[test]
2088 fn a_cascade_decodes_the_same_through_a_shared_scratch_as_through_its_own() {
2089 let mut values = Vec::new();
2094 for index in 0..8192i64 {
2095 values.push(1_600_000_000 + index / 4 + (index % 7) * 1_000);
2096 }
2097 let bytes = encode(&values).unwrap();
2098 let described = describe(&bytes).unwrap();
2099 assert!(described.contains('('), "expected a cascade, got {described}");
2100 assert_eq!(decode(&bytes).unwrap(), values, "{described}");
2101 }
2102
2103 #[test]
2104 fn selected_positions_agree_with_a_full_decode_for_packed_and_run_length_chunks() {
2105 let positions = [0, 1, 17, 1023, 1024, 4097, 8191];
2106 let packed: Vec<i64> = (0..8192).map(|index| index * 31 % 1_000_003).collect();
2107 let mut runs = Vec::new();
2108 for run in 0..160i64 {
2109 runs.extend(std::iter::repeat_n(run * 13, (run as usize % 71) + 2));
2110 }
2111 runs.resize(8192, -7);
2112
2113 for (kind, values) in [(Kind::Packed, packed), (Kind::Rle, runs)] {
2114 let bytes = encode_only(kind, &values).unwrap().expect("encoding applies");
2115 let selected = decode_selected(&bytes, &positions).unwrap();
2116 let expected = positions.iter().map(|&position| values[position]).collect::<Vec<_>>();
2117 assert_eq!(selected, expected, "{}", kind.name());
2118 }
2119 }
2120
2121 #[test]
2122 fn selected_positions_must_be_ordered_and_inside_the_chunk() {
2123 let bytes = encode_only(Kind::Packed, &(0..2048).collect::<Vec<_>>())
2124 .unwrap()
2125 .expect("packed applies");
2126 assert!(decode_selected(&bytes, &[7, 7]).is_err());
2127 assert!(decode_selected(&bytes, &[8, 3]).is_err());
2128 assert!(decode_selected(&bytes, &[2048]).is_err());
2129 }
2130
2131 #[test]
2132 fn a_partial_unit_costs_its_own_values_and_not_a_whole_unit() {
2133 let values = vec![1i64 << 39, (1 << 39) + 7, 1 << 38];
2137 let bytes = encode_only(Kind::Packed, &values).unwrap().unwrap();
2138 assert_eq!(bytes.len(), 5 + 9 + 15);
2139 assert_eq!(decode(&bytes).unwrap(), values);
2140 }
2141
2142 #[test]
2143 fn the_frame_of_reference_is_per_unit_and_not_per_chunk() {
2144 let values: Vec<i64> =
2148 (0..4096i64).map(|index| (index / 1024) * 1_000_000 + (index % 1024)).collect();
2149 let bytes = encode_only(Kind::Packed, &values).unwrap().unwrap();
2150 assert_eq!(describe(&bytes).unwrap(), "FOR+BITPACK[10]");
2151 assert_eq!(decode(&bytes).unwrap(), values);
2152 }
2153
2154 #[test]
2155 fn every_candidate_that_applies_decodes_to_the_input() {
2156 let mut values = vec![5i64; 3000];
2160 for (index, value) in values.iter_mut().enumerate() {
2161 if index % 500 == 0 {
2162 *value = index as i64;
2163 }
2164 }
2165 let applicable = candidates(&values, 0, &EXHAUSTIVE);
2166 assert!(applicable.len() >= 4, "{applicable:?}");
2167 for kind in applicable {
2168 let bytes = encode_only(kind, &values).unwrap().unwrap();
2169 assert_eq!(decode(&bytes).unwrap(), values, "{}", kind.name());
2170 }
2171 }
2172
2173 #[test]
2178 fn every_kind_that_applies_decodes_to_what_it_was_given() {
2179 let shapes: Vec<Vec<i64>> = vec![
2180 Vec::new(),
2181 vec![5; 1024],
2182 vec![i64::MIN, i64::MAX, 0, -1],
2183 (0..1024).map(|at| at * 7).collect(),
2184 (0..1024).map(|at| at % 17).collect(),
2185 (0..1024).map(|at| if at % 100 == 0 { at } else { 3 }).collect(),
2186 (0..1024).map(|at| -at * 1_000_003).collect(),
2187 (0..1024_i64)
2188 .map(|at| {
2189 at.wrapping_mul(6_364_136_223_846_793_005)
2190 .wrapping_add(1_442_695_040_888_963_407)
2191 })
2192 .collect(),
2193 ];
2194 let kinds =
2195 [Kind::Constant, Kind::Packed, Kind::Delta, Kind::Rle, Kind::Dict, Kind::Sparse];
2196 for values in &shapes {
2197 for kind in kinds {
2198 let Some(bytes) = encode_only(kind, values).unwrap() else {
2199 continue;
2200 };
2201 assert_eq!(
2202 &decode(&bytes).unwrap(),
2203 values,
2204 "{} over {} values",
2205 kind.name(),
2206 values.len()
2207 );
2208 }
2209 }
2210 }
2211
2212 #[test]
2213 fn the_chooser_picks_the_smallest_candidate_rather_than_the_first_that_applies() {
2214 let mut values = vec![5i64; 3000];
2215 values[1500] = 9;
2216 let chosen = encode(&values).unwrap();
2217 for (_, size) in candidate_sizes(&values).unwrap() {
2218 assert!(chosen.len() <= size);
2219 }
2220 }
2221
2222 #[test]
2223 fn a_truncated_chunk_is_an_error_and_not_a_panic() {
2224 let bytes = encode(&[1, 2, 3, 4, 5]).unwrap();
2225 for len in 0..bytes.len() {
2226 let error = decode(&bytes[..len]).unwrap_err();
2227 assert!(error.message().contains("chunk"), "{error}");
2228 }
2229 }
2230
2231 #[test]
2232 fn trailing_bytes_are_an_error() {
2233 let mut bytes = encode(&[1, 2, 3]).unwrap();
2234 bytes.push(0);
2235 let error = decode(&bytes).unwrap_err();
2236 assert!(error.message().contains("left over"), "{error}");
2237 }
2238
2239 #[test]
2240 fn an_unknown_tag_is_an_error() {
2241 let error = decode(&[99, 0, 0, 0, 0]).unwrap_err();
2242 assert!(error.message().contains("unknown encoding tag"), "{error}");
2243 }
2244
2245 #[test]
2246 fn a_dictionary_code_outside_the_dictionary_is_an_error() {
2247 let mut bytes = vec![Kind::Dict.tag()];
2252 put_u32(&mut bytes, 1);
2253 bytes.extend_from_slice(&encode(&[10]).unwrap());
2254 bytes.extend_from_slice(&encode(&[5]).unwrap());
2255 let error = decode(&bytes).unwrap_err();
2256 assert!(error.message().contains("not in the dictionary"), "{error}");
2257 }
2258
2259 #[test]
2260 fn a_negative_run_length_is_an_error() {
2261 let mut bytes = vec![Kind::Rle.tag()];
2264 put_u32(&mut bytes, 4);
2265 bytes.extend_from_slice(&encode(&[7]).unwrap());
2266 bytes.extend_from_slice(&encode(&[-4]).unwrap());
2267 let error = decode(&bytes).unwrap_err();
2268 assert!(error.message().contains("negative"), "{error}");
2269 }
2270
2271 #[test]
2273 fn a_run_that_runs_past_its_chunk_is_an_error() {
2274 let mut bytes = vec![Kind::Rle.tag()];
2279 put_u32(&mut bytes, 4);
2280 bytes.extend_from_slice(&encode(&[7]).unwrap());
2281 bytes.extend_from_slice(&encode(&[9]).unwrap());
2282 let error = decode(&bytes).unwrap_err();
2283 assert!(error.message().contains("past its chunk"), "{error}");
2284 }
2285
2286 #[test]
2288 fn runs_shorter_and_longer_than_the_width_they_are_written_in_all_come_back() {
2289 let lengths = [1, 3, 7, 8, 9, 40, 2, 1];
2294 let mut values = Vec::new();
2295 for (at, length) in lengths.iter().enumerate() {
2296 let value = i64::try_from(at).expect("eight runs") * 1000 - 3;
2297 values.extend(std::iter::repeat_n(value, *length));
2298 }
2299 let bytes = encode(&values).expect("encodes");
2300 assert_eq!(decode(&bytes).expect("decodes"), values, "runs around the write width");
2301 let singles: Vec<i64> = (0..300).map(|index| index * 7 % 11).collect();
2304 let bytes = encode(&singles).expect("encodes");
2305 assert_eq!(decode(&bytes).expect("decodes"), singles, "no run longer than one");
2306 }
2307
2308 #[test]
2309 fn the_cascade_depth_is_bounded() {
2310 let values: Vec<i64> = (0..50_000).map(|index| (index / 100) % 250).collect();
2314 let bytes = round_trip(&values);
2315 let shape = describe(&bytes).unwrap();
2316 let depth = shape.matches('(').count();
2317 assert!(depth <= MAX_DEPTH as usize, "{shape} is {depth} deep");
2318 }
2319
2320 #[test]
2321 fn candidate_sizes_reports_what_the_chooser_looked_at() {
2322 let values: Vec<i64> = (0..5000).map(|index| index % 17 * 1000).collect();
2323 let sizes = candidate_sizes(&values).unwrap();
2324 assert!(sizes.iter().any(|(kind, _)| *kind == Kind::Dict));
2325 assert!(sizes.iter().any(|(kind, _)| *kind == Kind::Packed));
2326 assert!(sizes.iter().all(|(_, size)| *size > 0));
2327 }
2328
2329 #[test]
2330 fn a_chunk_can_be_read_from_the_front_of_a_longer_buffer() {
2331 let first = encode(&[1, 2, 3]).unwrap();
2334 let second: Vec<i64> = (0..3000).map(|index| index % 11).collect();
2335 let second_bytes = encode(&second).unwrap();
2336 let mut joined = first.clone();
2337 joined.extend_from_slice(&second_bytes);
2338 joined.extend_from_slice(b"and then something else");
2339
2340 let (values, used) = decode_prefix(&joined).unwrap();
2341 assert_eq!(values, vec![1, 2, 3]);
2342 assert_eq!(used, first.len());
2343 let (more, used_again) = decode_prefix(&joined[used..]).unwrap();
2344 assert_eq!(more, second);
2345 assert_eq!(used_again, second_bytes.len());
2346
2347 let (text, described) = describe_prefix(&joined).unwrap();
2348 assert_eq!(described, first.len());
2349 assert_eq!(text, describe(&first).unwrap());
2350 }
2351
2352 #[test]
2353 fn a_truncated_chunk_is_still_an_error_when_read_as_a_prefix() {
2354 let bytes = encode(&(0..2000).collect::<Vec<i64>>()).unwrap();
2355 for len in 0..bytes.len() {
2356 assert!(decode_prefix(&bytes[..len]).is_err(), "{len} bytes decoded");
2357 }
2358 }
2359
2360 #[test]
2363 fn decoding_into_a_narrow_type_agrees_with_the_wide_decoder() {
2364 let columns: Vec<Vec<i64>> = vec![
2365 vec![7; 3000],
2366 (0..3000).map(|i| (i * 37) % 200 - 100).collect(),
2367 (0..3000).map(|i| if i % 97 == 0 { i % 50 } else { 0 }).collect(),
2368 (0..3000).map(|i| i / 250).collect(),
2369 (0..3000).map(|i| i * 3 + 11).collect(),
2370 (0..3000).map(|i| [5, -9, 120][i as usize % 3]).collect(),
2371 ];
2372 let kinds = [
2373 Kind::Constant,
2374 Kind::Packed,
2375 Kind::Delta,
2376 Kind::Rle,
2377 Kind::Dict,
2378 Kind::Sparse,
2379 Kind::Strided,
2380 ];
2381 for values in &columns {
2382 for kind in kinds {
2383 let Some(bytes) = encode_only(kind, values).unwrap() else { continue };
2384 let wide = decode(&bytes).unwrap();
2385 let as_i16: Vec<i64> =
2386 decode_as::<i16>(&bytes).unwrap().into_iter().map(i64::from).collect();
2387 let as_i32: Vec<i64> =
2388 decode_as::<i32>(&bytes).unwrap().into_iter().map(i64::from).collect();
2389 assert_eq!(as_i16, wide, "{kind:?} as i16");
2390 assert_eq!(as_i32, wide, "{kind:?} as i32");
2391 assert_eq!(decode_as::<i64>(&bytes).unwrap(), wide, "{kind:?} as i64");
2392 }
2393 let chosen = encode(values).unwrap();
2394 let narrow: Vec<i64> =
2395 decode_as::<i16>(&chosen).unwrap().into_iter().map(i64::from).collect();
2396 assert_eq!(narrow, decode(&chosen).unwrap(), "the chosen cascade as i16");
2397 }
2398 }
2399
2400 #[test]
2402 fn decoding_into_a_narrow_type_takes_what_fits_and_refuses_what_does_not() {
2403 fn check<T: Lane + TryFrom<i64> + PartialEq + std::fmt::Debug>(edges: [i64; 2]) {
2404 for edge in edges {
2405 for value in [edge - 1, edge, edge + 1] {
2406 for kind in [Kind::Constant, Kind::Packed, Kind::Rle, Kind::Sparse] {
2407 let values = [value, value, edges[0].max(0).min(edges[1]), value];
2408 let Some(bytes) = encode_only(kind, &values).unwrap() else { continue };
2409 let fits = values.iter().all(|&value| T::try_from(value).is_ok());
2410 assert_eq!(decode_as::<T>(&bytes).is_ok(), fits, "{value} {kind:?}");
2411 }
2412 }
2413 }
2414 }
2415 check::<i8>([-128, 127]);
2416 check::<u8>([0, 255]);
2417 check::<i16>([-32_768, 32_767]);
2418 check::<u16>([0, 65_535]);
2419 check::<i32>([i64::from(i32::MIN), i64::from(i32::MAX)]);
2420 check::<u32>([0, i64::from(u32::MAX)]);
2421 }
2422
2423 #[test]
2426 fn a_packed_block_wider_than_its_type_still_decodes_when_its_values_fit() {
2427 let values: Vec<i64> = (0..1500).map(|i| if i % 2 == 0 { -5 } else { 32_767 }).collect();
2428 let bytes = encode_only(Kind::Packed, &values).unwrap().expect("packing always applies");
2429 let narrow: Vec<i64> =
2430 decode_as::<i16>(&bytes).unwrap().into_iter().map(i64::from).collect();
2431 assert_eq!(narrow, values);
2432 let over: Vec<i64> = values.iter().map(|&value| value + 1).collect();
2433 let bytes = encode_only(Kind::Packed, &over).unwrap().expect("packing always applies");
2434 assert!(decode_as::<i16>(&bytes).is_err(), "32768 is not an i16");
2435 }
2436}