Skip to main content

rudb_encoding/
integer.rs

1//! The single column integer encodings and the cascade over them.
2//!
3//! `spec/06-compression.md` section 6.2 lists the encoding set and section 6.3 says the ratios are
4//! in the cascade rather than in any one encoding. This module is both: the seven candidate shapes
5//! for an integer column, each of which encodes its own output by calling back into the chooser, so
6//! that RLE over a dictionary over a bit packed code array is a thing that happens by construction
7//! rather than a case somebody wrote out.
8//!
9//! Everything here works on `i64`. A narrower column is widened on the way in and nothing is lost
10//! by it, because every encoding's size comes from the range of the values rather than from the
11//! declared width of the type: a `SMALLINT` column of values 100 to 130 packs to 5 bits whether it
12//! arrived as `i16` or as `i64`. The one place the widening would cost something is a raw copy, and
13//! there is no raw copy, because a bit packed unit at width 64 is exactly that and the chooser
14//! reaches it on its own when nothing else fits.
15//!
16//! ## The unit
17//!
18//! Bit packing is per 1024 values, per [`crate::bitpack`]. Everything else is per chunk, where a
19//! chunk is however many values the caller passes in and is meant to be a row group. The two
20//! granularities are the point rather than an accident. A frame of reference base that is chosen
21//! per 1024 values tracks a column that drifts, which is what a timestamp column and an
22//! autoincrementing key both do, and one base per row group would pay the whole range of the row
23//! group on every value. A dictionary, on the other hand, is worth more the larger the unit it
24//! covers, which is the argument section 6.5 takes all the way to a dictionary per table.
25//!
26//! ## The serialized form
27//!
28//! A chunk is a tag byte, a value count, and a body whose shape depends on the tag. Bodies that
29//! contain another array of integers contain a whole chunk, tag and all, which is what makes the
30//! decoder a fold and what makes the cascade free: nothing in `Rle` knows what its run lengths are
31//! encoded as. The header is fixed width little endian rather than a varint, because 5 bytes per
32//! chunk against a chunk that holds a row group is not worth the branch on the decode path.
33//!
34//! ## What the chooser does, and what it will have to do instead
35//!
36//! It encodes every candidate and keeps the smallest. That is the honest baseline for M1, which is
37//! a measurement of what the format can do rather than of how fast a writer can decide, and it is
38//! not what a write path can afford. Section 6.3 describes the real thing: evaluate the candidates
39//! on a systematic sample, not the first N rows, because column data is frequently clustered and
40//! the first 1024 rows of a sorted column look constant. Building that first would mean the numbers
41//! this milestone produces are the sampler's numbers rather than the format's, and there would be
42//! no way to tell how much the sampler is leaving behind.
43
44use rudb_common::{Error, Result};
45
46use crate::chooser::{Chooser, EXHAUSTIVE};
47use crate::reader::Reader;
48
49use crate::bitpack::{self, VALUES};
50
51/// How deep a cascade is allowed to go.
52///
53/// Three levels is what section 6.3 says captures most of what a general compressor would find:
54/// dictionary, then bit packed codes, then nothing left worth doing. The limit exists because the
55/// chooser is exhaustive and a cascade that could nest forever would be exponential, and because a
56/// fourth level has never once been the smallest candidate in anything measured so far.
57const MAX_DEPTH: u8 = 3;
58
59/// How many values a run writes at once, whatever the run is.
60///
61/// A run length decode used to write a value at a time for the length of the run, which reads well
62/// and is the wrong shape for the data: a clustered join key runs two or three long, so the loop
63/// spent its time mispredicting its own exit and the branch cost more than the stores did. Writing
64/// a fixed eight and then moving on by the run's real length has no exit to predict, and whatever
65/// of the eight was surplus is overwritten by the run that follows, because every run writes at
66/// least its own length. Eight because it is two vector stores on every machine this runs on and
67/// longer than nearly every run in a column worth run length encoding at all.
68const RUN: usize = 8;
69
70/// What a chunk is encoded as. The discriminant is the tag byte in the serialized form and is part
71/// of the format, so the numbers are written down rather than left to the compiler.
72#[derive(Debug, Clone, Copy, PartialEq, Eq)]
73pub enum Kind {
74    /// One value repeated. The whole chunk is the tag, the count and the value.
75    Constant = 0,
76    /// Frame of reference then bit packed, per 1024 values. Covers plain bit packing at base zero
77    /// and a raw copy at width 64.
78    Packed = 1,
79    /// Differences between neighbours, zigzagged so a decreasing column is as cheap as an
80    /// increasing one, then encoded as a chunk in its own right.
81    Delta = 2,
82    /// Run values and run lengths, each encoded as a chunk in its own right.
83    Rle = 3,
84    /// A dictionary of the distinct values and an array of codes into it, both encoded as chunks in
85    /// their own right.
86    Dict = 4,
87    /// One dominant value with an exception list of positions and values.
88    Sparse = 5,
89    /// A base and a common step, with the number of steps to each value encoded as a chunk in its
90    /// own right.
91    Strided = 6,
92}
93
94impl Kind {
95    /// Every kind, in tag order.
96    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    /// The name that goes in a report.
124    #[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
138/// Encodes a chunk of integers, choosing the cascade that comes out smallest.
139///
140/// # Errors
141///
142/// If the chunk is longer than `u32::MAX`, or if an encoding produces something its own decoder
143/// would not accept, which is an internal inconsistency rather than a caller error.
144pub fn encode(values: &[i64]) -> Result<Vec<u8>> {
145    encode_with(values, &EXHAUSTIVE)
146}
147
148/// [`encode`] with somebody else deciding which candidates are worth encoding in full.
149///
150/// A chooser narrows the list and nothing else. It cannot offer a candidate that does not apply, so
151/// whatever it picks still has to encode the whole chunk and still has to decode, and the worst a
152/// bad one can do is come out bigger than [`encode`] would have.
153///
154/// # Errors
155///
156/// As [`encode`].
157pub fn encode_with(values: &[i64], chooser: &dyn Chooser) -> Result<Vec<u8>> {
158    encode_at(values, 0, chooser)
159}
160
161/// Decodes a chunk written by [`encode`].
162///
163/// # Errors
164///
165/// If the bytes are truncated, carry an unknown tag, or describe a chunk whose parts do not agree
166/// with each other.
167pub 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
179/// Decodes selected row positions from a chunk written by [`encode`].
180///
181/// Positions must be sorted and unique. Packed chunks read only the words holding those positions,
182/// and run length chunks walk their run boundaries without expanding the output. Other cascade
183/// shapes use the full decoder and select afterward until they have a point form of their own.
184///
185/// # Errors
186///
187/// As [`decode`], or if a position is outside the chunk or the positions are not strictly
188/// increasing.
189pub 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
204/// Decodes a chunk that sits at the front of a longer buffer, and says how many bytes it took.
205///
206/// A string column holds integer chunks inside its own body, and the reader on that side cannot
207/// know where the nested chunk ends until it has been read. A chunk is self delimiting, so this is
208/// the same work [`decode`] does without the check that nothing follows.
209///
210/// # Errors
211///
212/// As [`decode`], except that trailing bytes are what the caller asked about rather than an error.
213pub 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
219/// [`describe`] over a chunk at the front of a longer buffer, and how many bytes it took.
220///
221/// # Errors
222///
223/// As [`decode_prefix`].
224pub 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
230/// The size in bytes of every candidate, for a report that wants to say what the cascade was
231/// chosen over rather than only what it chose. A candidate that does not apply is absent.
232///
233/// # Errors
234///
235/// As [`encode`].
236pub 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/// Which candidates [`encode`] would try on this chunk, in the order it tries them.
247///
248/// The chooser is exhaustive, so this is also the list of encodes it pays for to return one of
249/// them. A caller measuring where the encode time goes needs the list separately from the sizes,
250/// because a candidate that is offered and turns out not to apply still costs whatever it spent
251/// finding that out.
252#[must_use]
253pub fn offered(values: &[i64]) -> Vec<Kind> {
254    candidates(values, 0, &EXHAUSTIVE)
255}
256
257/// One candidate on its own, which is what the chooser calls once per entry in [`offered`].
258///
259/// `None` when the encoding does not apply. This is here so that the time the chooser spends can be
260/// attributed to the candidate that spent it, which is the measurement F2 wants before anybody
261/// replaces the exhaustive search with a sampled one. It is not how a writer encodes a chunk:
262/// [`encode`] is, and picking a kind by hand gives up the only thing the chooser is for.
263///
264/// # Errors
265///
266/// As [`encode`].
267pub fn encode_only(kind: Kind, values: &[i64]) -> Result<Option<Vec<u8>>> {
268    encode_as(kind, values, 0, &EXHAUSTIVE)
269}
270
271/// How big one candidate comes out, which is all a sampling chooser needs from it.
272///
273/// The bytes are thrown away, so this says nothing [`encode_only`] does not. It is `pub(crate)` and
274/// separate so that the sampler in [`crate::chooser`] is not handing back buffers it will not read.
275pub(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
279/// The kind at every level of an encoded chunk, in the order the encoder chose them.
280///
281/// The order is the one [`encode_with`] asks its chooser in: a level, then everything under its
282/// first inner chunk, then everything under its second. So a chooser that hands these back one per
283/// question gets the same cascade on a chunk that offers the same kinds, without searching any of
284/// it. That is what a writer with many small parts of one column wants, because the search is
285/// most of what the encode costs and neighbouring parts nearly always come out the same shape.
286///
287/// # Errors
288///
289/// As [`decode`].
290pub 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
297/// The cascade a chunk was encoded as, as a line of text like `DICT(PACKED, PACKED)`.
298///
299/// # Errors
300///
301/// As [`decode`].
302pub 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    // `Packed` applies to every input including the empty one, so the chooser always has at least
319    // one candidate and this cannot be reached without a bug in `candidates`.
320    best.ok_or_else(|| Error::internal("no encoding applied to the chunk"))
321}
322
323/// Which candidates are worth encoding for this input.
324///
325/// The filters here are not the cost model. They are the cases where the encoding cannot be
326/// expressed at all, or is provably larger than `Packed` on the same data, so that the exhaustive
327/// chooser does not spend a dictionary build on a column of 100,000 distinct values to discover
328/// what its distinct count already said.
329///
330/// A kind the chooser says it will never keep is not tested for at all. The test for a dictionary
331/// sorts a copy of the chunk, and this runs at every level of the cascade, so a chooser that never
332/// keeps a dictionary was paying for a sort per level to find out something it would ignore.
333fn 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        // Nothing else can beat 13 bytes, so this is the whole answer rather than a candidate.
340        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
363/// `None` when the encoding does not apply to this input, which the caller treats as a candidate
364/// that did not run rather than as a failure.
365fn 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            // An empty chunk has no first value to hang the differences off. The search never asks
387            // for one because `candidates` rules it out, but `encode_only` goes straight past that
388            // and used to index into the chunk anyway.
389            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            // The majority is the most frequent value whenever there is one, and a chunk the search
414            // offers this for always has one. `encode_only` can ask about any chunk, so the sort is
415            // still there for a chunk with no majority.
416            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                // A step count the recursion cannot hold. An offset is at most 65 bits because both
444                // ends came from an `i64`, and only a stride of one leaves it that wide, which is a
445                // stride this never offers. Refused rather than wrapped, because a candidate that
446                // does not apply is one the chooser skips.
447                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
460/// Frame of reference and bit packing, one base and one width per 1024 values.
461///
462/// A base per unit rather than per chunk is most of what makes this work on real columns. A
463/// timestamp column over a day drifts across a range that needs 47 bits, and the same column inside
464/// any one unit spans a few seconds and needs 12. One base per row group would pay the 47 on every
465/// value.
466///
467/// A unit shorter than 1024 values, which is the last one of any chunk whose length is not a
468/// multiple of the unit and is the only one of every short array in a cascade, goes through
469/// [`bitpack::pack_tail`] instead. The transposed layout has no partial form and would charge a
470/// five entry dictionary for 1024 entries.
471fn encode_packed(values: &[i64], out: &mut Vec<u8>) -> Result<()> {
472    // The same three buffers for every unit, for the reason written on `Decoding` on the other side.
473    // The chooser encodes every candidate it is offered before it picks one, so this loop runs more
474    // often on the way in than the decoding loop does on the way out.
475    let mut offsets: Vec<u64> = Vec::with_capacity(VALUES);
476    // Held at the width 64 length for the reason written on `Decoding`, so a narrower unit writes
477    // the front of it and the resize per unit goes away.
478    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
500/// The buffer a decode reuses from one unit of 1024 values to the next.
501///
502/// This used to be allocated inside the loop, and because it was allocated with a value rather than
503/// grown, the allocator zeroed it and then the decode overwrote every byte. In a ClickBench profile
504/// that zeroing was the single largest item, ahead of the unpacking it was making room for, because
505/// a scan pays it once per 1024 rows of every packed integer column it reads.
506///
507/// It is threaded through the recursion rather than made per call because a chunk is a cascade. A
508/// dictionary of deltas is three nested decodes, and each of them would otherwise make its own.
509///
510/// It starts empty and is grown on the first unit that needs it, to its largest size rather than to
511/// the size that unit wants, so that every unit after the first finds it the right length already
512/// and nothing is zeroed or resized again.
513///
514/// There used to be a second buffer here holding one unit of unpacked offsets, which the decode
515/// then walked to add the frame of reference base back on. The unpackers take the base now and
516/// write into the chunk directly, so that buffer and the pass over it are both gone.
517///
518/// It lives on the thread rather than in the caller, which is worth saying why. A chunk is a row
519/// group, and a row group in the native format is about a thousand rows, which is one unit. So there
520/// is no second unit in a chunk to reuse anything and holding this per call is strictly worse than
521/// allocating per unit was: it was tried, and it cost more in the growing than it saved in the
522/// zeroing. What there are many of is chunks, one per part per column, and the thread that reads
523/// them reads them one after another. That is the loop the reuse belongs to, and reaching it by
524/// passing a buffer down would mean a parameter through every page decoder in the storage layer for
525/// a buffer none of them has an opinion about.
526struct Decoding {
527    /// The packed words of one unit, as read off the wire. Held at the width 64 length, which is the
528    /// largest a unit can be, so a narrower unit uses the front of it.
529    packed: Vec<u64>,
530}
531
532thread_local! {
533    /// The buffers this thread decodes through. See [`Decoding`].
534    static DECODING: std::cell::RefCell<Decoding> =
535        const { std::cell::RefCell::new(Decoding::new()) };
536}
537
538/// Runs a decode over this thread's buffers.
539///
540/// Nothing inside a decode calls back into one, so the borrow is never already taken. It is asked
541/// for rather than assumed anyway, and a decode that somehow arrives while another is running gets
542/// buffers of its own rather than a panic, because the alternative is a crash in a reader on a
543/// path nobody exercised.
544fn 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    /// A buffer that has not made room for anything yet.
553    const fn new() -> Self {
554        Self { packed: Vec::new() }
555    }
556
557    /// Makes room for one unit. A no op every time after the first.
558    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            // One buffer for the chunk, and every value written into it once. Both unpackers take
572            // the frame of reference base and put the value it belongs to where it goes, so there
573            // is no unit of raw offsets in between and no second pass to fold the base back in.
574            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            // Room for one run past the end, so the write below never has to ask how much of its
620            // fixed width landed inside the chunk.
621            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
806/// Advances over one encoded chunk without materializing its values and returns its row count.
807fn 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
834/// Advances over the units of a `Packed` body of `count` values.
835fn 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
857/// [`shape`] for one chunk and everything inside it.
858fn 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            // Square brackets rather than round ones, so that a reader and a test can both take a
914            // parenthesis to mean one more level of cascade and nothing else.
915            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
950/// The step every value of the chunk is a whole number of, or `None` when there is not one worth
951/// having.
952///
953/// This is the greatest common divisor of every value's distance from the smallest one. A timestamp
954/// column loaded from a source that recorded whole seconds holds microseconds that are all multiples
955/// of a million, and without this the frame of reference pays twenty bits a value to write down the
956/// twenty zero bits at the bottom of every one of them.
957///
958/// The walk stops the moment the divisor reaches one, which is what makes this affordable to ask on
959/// every chunk. Two values that share no factor are enough to answer, and on a column of arbitrary
960/// numbers that is almost always the first pair.
961fn 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    // Zero is every value being the base, which `Constant` already holds for nothing, and one is
971    // the frame of reference on its own with two extra words of header.
972    (divisor > 1).then_some(divisor)
973}
974
975/// Binary GCD, which is the one without a division in it.
976fn 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
997/// The distance from the frame of reference base, which is always representable in a `u64` because
998/// both ends came from an `i64` and the width of the difference is at most 65 bits minus the sign.
999fn 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
1007/// Zigzag, so that a column that counts down packs as narrowly as one that counts up. Without it a
1008/// delta of -1 is 64 bits of ones.
1009fn 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
1017/// The zigzagged differences, or `None` if any difference is too wide to be one.
1018///
1019/// A column holding both `i64::MIN` and `i64::MAX` has a difference that does not fit in an `i64`,
1020/// and rather than widening every delta array to 128 bits for a case that does not occur in data,
1021/// the encoding declines to apply. `Packed` covers it.
1022/// Whether every neighbouring difference fits in an `i64`, which is the only thing the candidate
1023/// list needs to know about deltas.
1024///
1025/// The candidate list used to answer this by building the whole delta array and checking that it
1026/// came back, which is an allocation and a pass over the chunk thrown away on every chunk, and then
1027/// `Kind::Delta` built it again. This is the same pass with nothing kept.
1028fn 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
1068/// The distinct values in sorted order.
1069///
1070/// Sorted rather than in order of first appearance, because an ordered dictionary is what lets a
1071/// range predicate become a code range instead of a code set, per section 6.7, and because the
1072/// codes of a clustered column then run in order and delta encode.
1073/// How many distinct values there are and which one occurs most often, from one sort.
1074///
1075/// Both questions are about the histogram of the chunk and neither needs the histogram itself, so
1076/// one sorted copy and one walk over it answers both. They used to be two functions that each sorted
1077/// their own copy and threw it away, which is a chunk sorted twice on every chunk at every level of
1078/// the cascade before a single candidate has been encoded.
1079///
1080/// No hash map, because the sort is what makes the walk a scan of equal runs, and a hash map would
1081/// pay a lookup per value to learn the same thing.
1082fn 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
1104/// The value more than half of the chunk holds, and how many times, found in two passes without
1105/// sorting anything.
1106///
1107/// This is the vote that keeps one candidate and a lead: a value that holds more than half the
1108/// chunk outlasts every other value put together, so it is the candidate left at the end, and the
1109/// second pass checks that the candidate really does hold more than half. When it does it is the
1110/// value [`spread_of`] would name as the most frequent, since a value over half the chunk has no tie.
1111fn 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
1128/// The distinct values in sorted order, for the same reason the string dictionary is sorted: an
1129/// ordered dictionary turns a range predicate into a code range rather than a code set.
1130fn distinct_values(values: &[i64]) -> Vec<i64> {
1131    let mut distinct = values.to_vec();
1132    distinct.sort_unstable();
1133    distinct.dedup();
1134    distinct
1135}
1136
1137/// Where each value sits in the dictionary.
1138///
1139/// The string side builds its dictionary and its codes together from one sort of a permutation,
1140/// because the alternative there is a copy of every value onto the heap and a `memcmp` per level of
1141/// a binary search per row. This side was changed to match and it measured slower, so it was changed
1142/// back. An integer dictionary only exists when the distinct count is at most half the row count, so
1143/// the search is over something small and cache resident, the comparison is one integer rather than
1144/// a string, and carrying the source index through the sort means sorting a padded sixteen byte pair
1145/// instead of an eight byte value. The search is cheaper than the wider sort.
1146fn 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    /// The same xorshift the bit packing tests use, for the same reason.
1202    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        // A tie goes to the value that sorts first, which is arbitrary but has to be stable,
1237        // because Sparse writes the dominant value into the chunk and the size depends on it.
1238        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        // `offered` and `encode_only` are what `cargo xtask encode` splits the chooser's seconds
1274        // with, so they have to describe the chooser that actually runs rather than a second copy
1275        // of its rules that drifts. This is the assertion that keeps the two the same thing.
1276        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        // What three ClickBench columns are. `epoch_ms(EventTime * 1000)` on a source that recorded
1298        // whole seconds gives microseconds with twenty zero bits under every value, and a frame of
1299        // reference over a part that spans a working day needs 36 bits to write them down.
1300        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        // 17 bits a value for the range of seconds, against the 36 the microseconds need.
1308        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        // The base is the smallest value and not zero, so a column that does not start on a
1324        // multiple of its own step still has one.
1325        assert_eq!(stride_of(&[7i64, 17, 37]), Some(10));
1326        assert_eq!(stride_of(&[10i64, 20, 23]), None);
1327        // Every value the same is `Constant`'s case and this declines it rather than dividing by a
1328        // stride of zero.
1329        assert_eq!(stride_of(&[5i64; 100]), None);
1330        assert_eq!(stride_of(&[]), None);
1331        // The two ends of the type, where the distance needs 65 bits and only a `u64` holds it.
1332        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        // The distance is 65 bits, so the step count is one and the offset it comes back as is a
1338        // number no `i64` holds. This is the arithmetic the encoder has to do in `u64`.
1339        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        // 100_000 values between 1000 and 1063 is 6 bits each, plus 9 bytes of header per 1024.
1369        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        // The classic case and the reason DELTA exists. A million consecutive integers is a
1381        // difference of 1 a million times, which is a constant chunk under the delta.
1382        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        // What zigzag is for. Without it every delta is -1, which is 64 bits of ones.
1392        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        // Values that are far apart so that packing them directly is 30 bits each, and only 40 of
1411        // them so that the codes are 6 bits each. The dictionary has to win by a factor of five.
1412        //
1413        // Drawn at random rather than laid out at a fixed interval, because a fixed interval is a
1414        // stride and STRIDE writes the same codes without a dictionary to point them at.
1415        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        // The whole point of section 6.3. A dictionary over a clustered column produces codes that
1439        // run in long stretches, and the run lengths of those are themselves compressible.
1440        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        // The case where nothing works, which has to come out at eight bytes a value plus change
1453        // rather than at eight bytes a value plus a dictionary of every value in the column.
1454        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        // Every offset and every delta in here overflows something if the arithmetic is done in 64
1464        // bits, which is why it is done in 128.
1465        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        // A decode reuses its buffers from one unit to the next instead of getting a zeroed one
1482        // each time, so a unit that wrote fewer bits than the unit before it would come back with
1483        // the older unit's values in the bits it did not write. Each run of 1024 here needs a
1484        // different width and the widths go up and down, and the last run repeats the first, which
1485        // is the pair that would agree by accident if the reuse were wrong in the obvious way.
1486        //
1487        // The values are random rather than written out because this has to stay one packed chunk
1488        // of six units to be testing anything, and the first version of it was arithmetic and got
1489        // cascaded into a delta of runs where every nested array was under a unit long. That was
1490        // caught by gating a panic on the second unit and rerunning, which this version reaches and
1491        // the old one did not, and the assertion on the shape below is there so it stays reached.
1492        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        // The scratch is threaded through the recursion, so a dictionary of deltas is three nested
1508        // decodes sharing one set of buffers. Nothing in the nesting arms holds a buffer across the
1509        // call it makes, and this is the test that says so: a chunk long enough to cascade and wide
1510        // enough to bit pack at more than one level, decoded whole.
1511        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        // Three values that need 40 bits each. In the transposed layout a unit is 1024 values
1552        // whether it holds them or not, so this would be 5 KB, and every nested array in a cascade
1553        // is this short. It is 15 bytes of payload and 14 of header.
1554        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        // A column that drifts, which is what a timestamp column and a clustered key both do. Each
1563        // unit here spans 1023 and packs at 10 bits, and a base per chunk would pay the 22 bits the
1564        // whole chunk spans on every value in it.
1565        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        // The chooser only ever hands back the smallest, so without this the other five are only
1575        // tested when they happen to win. Any of them being wrong is a wrong answer that appears
1576        // when a column's distribution shifts.
1577        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    /// The test above only asks the kinds `candidates` offered, so between them the two cover the
1592    /// encoders on input the search would give them and nothing else. `encode_only` does not go
1593    /// through `candidates` at all, so every one of its callers can hand an encoder a shape the
1594    /// filter would have refused, and the empty chunk is the shape that used to panic.
1595    #[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        // A corrupted or malicious chunk must not index out of bounds, and this is the one place in
1666        // the decoder where a number that came off the disk is used as an index. Built by hand
1667        // rather than by corrupting a real chunk, because a byte offset into an encoding that the
1668        // chooser is free to change is a test that breaks for the wrong reason.
1669        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        // The other number off the disk that the decoder would otherwise trust, and the one that
1680        // would turn into an allocation of nine quintillion values.
1681        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    /// A run that ends past the chunk it is in is an error and not a write past the end.
1690    #[test]
1691    fn a_run_that_runs_past_its_chunk_is_an_error() {
1692        // The decode writes a fixed eight values per run and moves on by the run's own length, so
1693        // the buffer carries eight values of slack and a run that claims more rows than the chunk
1694        // holds would be the one way to reach past it. It is refused before the write rather than
1695        // caught by the count afterwards.
1696        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    /// Runs of every length around the eight that a run is written in, in one chunk.
1705    #[test]
1706    fn runs_shorter_and_longer_than_the_width_they_are_written_in_all_come_back() {
1707        // A run of one, several shorter than eight, one of exactly eight and two longer, with the
1708        // shortest run last so that the surplus of the write before it has nothing after it to be
1709        // overwritten by. The values differ from each other, because a surplus that was left in
1710        // place would be invisible against a neighbour holding the same value.
1711        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        // And the same rows a run at a time, which is the run length encoder's worst case and the
1720        // shape a column with no runs in it decodes as.
1721        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        // Without the limit a chooser that finds a dictionary of a dictionary of a dictionary would
1729        // recurse until the values ran out, and the encode time of a wide column would be a
1730        // surprise rather than a number.
1731        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        // What a string column does. It writes an integer chunk of lengths into the middle of its
1750        // own body and has to find the end of it again on the way back.
1751        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}