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/// What a chunk is encoded as. The discriminant is the tag byte in the serialized form and is part
60/// of the format, so the numbers are written down rather than left to the compiler.
61#[derive(Debug, Clone, Copy, PartialEq, Eq)]
62pub enum Kind {
63    /// One value repeated. The whole chunk is the tag, the count and the value.
64    Constant = 0,
65    /// Frame of reference then bit packed, per 1024 values. Covers plain bit packing at base zero
66    /// and a raw copy at width 64.
67    Packed = 1,
68    /// Differences between neighbours, zigzagged so a decreasing column is as cheap as an
69    /// increasing one, then encoded as a chunk in its own right.
70    Delta = 2,
71    /// Run values and run lengths, each encoded as a chunk in its own right.
72    Rle = 3,
73    /// A dictionary of the distinct values and an array of codes into it, both encoded as chunks in
74    /// their own right.
75    Dict = 4,
76    /// One dominant value with an exception list of positions and values.
77    Sparse = 5,
78    /// A base and a common step, with the number of steps to each value encoded as a chunk in its
79    /// own right.
80    Strided = 6,
81}
82
83impl Kind {
84    fn tag(self) -> u8 {
85        self as u8
86    }
87
88    fn from_tag(tag: u8) -> Result<Self> {
89        match tag {
90            0 => Ok(Self::Constant),
91            1 => Ok(Self::Packed),
92            2 => Ok(Self::Delta),
93            3 => Ok(Self::Rle),
94            4 => Ok(Self::Dict),
95            5 => Ok(Self::Sparse),
96            6 => Ok(Self::Strided),
97            other => Err(Error::internal(format!("unknown encoding tag {other}"))),
98        }
99    }
100
101    /// The name that goes in a report.
102    #[must_use]
103    pub fn name(self) -> &'static str {
104        match self {
105            Self::Constant => "CONSTANT",
106            Self::Packed => "FOR+BITPACK",
107            Self::Delta => "DELTA",
108            Self::Rle => "RLE",
109            Self::Dict => "DICT",
110            Self::Sparse => "SPARSE",
111            Self::Strided => "STRIDE",
112        }
113    }
114}
115
116/// Encodes a chunk of integers, choosing the cascade that comes out smallest.
117///
118/// # Errors
119///
120/// If the chunk is longer than `u32::MAX`, or if an encoding produces something its own decoder
121/// would not accept, which is an internal inconsistency rather than a caller error.
122pub fn encode(values: &[i64]) -> Result<Vec<u8>> {
123    encode_with(values, &EXHAUSTIVE)
124}
125
126/// [`encode`] with somebody else deciding which candidates are worth encoding in full.
127///
128/// A chooser narrows the list and nothing else. It cannot offer a candidate that does not apply, so
129/// whatever it picks still has to encode the whole chunk and still has to decode, and the worst a
130/// bad one can do is come out bigger than [`encode`] would have.
131///
132/// # Errors
133///
134/// As [`encode`].
135pub fn encode_with(values: &[i64], chooser: &dyn Chooser) -> Result<Vec<u8>> {
136    encode_at(values, 0, chooser)
137}
138
139/// Decodes a chunk written by [`encode`].
140///
141/// # Errors
142///
143/// If the bytes are truncated, carry an unknown tag, or describe a chunk whose parts do not agree
144/// with each other.
145pub fn decode(bytes: &[u8]) -> Result<Vec<i64>> {
146    let mut reader = Reader::new(bytes);
147    let values = with_decoding(|scratch| decode_chunk(&mut reader, scratch))?;
148    if reader.remaining() != 0 {
149        return Err(Error::internal(format!(
150            "{} bytes left over after decoding a chunk",
151            reader.remaining()
152        )));
153    }
154    Ok(values)
155}
156
157/// Decodes a chunk that sits at the front of a longer buffer, and says how many bytes it took.
158///
159/// A string column holds integer chunks inside its own body, and the reader on that side cannot
160/// know where the nested chunk ends until it has been read. A chunk is self delimiting, so this is
161/// the same work [`decode`] does without the check that nothing follows.
162///
163/// # Errors
164///
165/// As [`decode`], except that trailing bytes are what the caller asked about rather than an error.
166pub fn decode_prefix(bytes: &[u8]) -> Result<(Vec<i64>, usize)> {
167    let mut reader = Reader::new(bytes);
168    let values = with_decoding(|scratch| decode_chunk(&mut reader, scratch))?;
169    Ok((values, reader.used()))
170}
171
172/// [`describe`] over a chunk at the front of a longer buffer, and how many bytes it took.
173///
174/// # Errors
175///
176/// As [`decode_prefix`].
177pub fn describe_prefix(bytes: &[u8]) -> Result<(String, usize)> {
178    let mut reader = Reader::new(bytes);
179    let text = describe_chunk(&mut reader)?;
180    Ok((text, reader.used()))
181}
182
183/// The size in bytes of every candidate, for a report that wants to say what the cascade was
184/// chosen over rather than only what it chose. A candidate that does not apply is absent.
185///
186/// # Errors
187///
188/// As [`encode`].
189pub fn candidate_sizes(values: &[i64]) -> Result<Vec<(Kind, usize)>> {
190    let mut sizes = Vec::new();
191    for kind in candidates(values, 0) {
192        if let Some(bytes) = encode_as(kind, values, 0, &EXHAUSTIVE)? {
193            sizes.push((kind, bytes.len()));
194        }
195    }
196    Ok(sizes)
197}
198
199/// Which candidates [`encode`] would try on this chunk, in the order it tries them.
200///
201/// The chooser is exhaustive, so this is also the list of encodes it pays for to return one of
202/// them. A caller measuring where the encode time goes needs the list separately from the sizes,
203/// because a candidate that is offered and turns out not to apply still costs whatever it spent
204/// finding that out.
205#[must_use]
206pub fn offered(values: &[i64]) -> Vec<Kind> {
207    candidates(values, 0)
208}
209
210/// One candidate on its own, which is what the chooser calls once per entry in [`offered`].
211///
212/// `None` when the encoding does not apply. This is here so that the time the chooser spends can be
213/// attributed to the candidate that spent it, which is the measurement F2 wants before anybody
214/// replaces the exhaustive search with a sampled one. It is not how a writer encodes a chunk:
215/// [`encode`] is, and picking a kind by hand gives up the only thing the chooser is for.
216///
217/// # Errors
218///
219/// As [`encode`].
220pub fn encode_only(kind: Kind, values: &[i64]) -> Result<Option<Vec<u8>>> {
221    encode_as(kind, values, 0, &EXHAUSTIVE)
222}
223
224/// How big one candidate comes out, which is all a sampling chooser needs from it.
225///
226/// The bytes are thrown away, so this says nothing [`encode_only`] does not. It is `pub(crate)` and
227/// separate so that the sampler in [`crate::chooser`] is not handing back buffers it will not read.
228pub(crate) fn size_as(kind: Kind, values: &[i64], depth: u8) -> Result<Option<usize>> {
229    Ok(encode_as(kind, values, depth, &EXHAUSTIVE)?.map(|bytes| bytes.len()))
230}
231
232/// The cascade a chunk was encoded as, as a line of text like `DICT(PACKED, PACKED)`.
233///
234/// # Errors
235///
236/// As [`decode`].
237pub fn describe(bytes: &[u8]) -> Result<String> {
238    let mut reader = Reader::new(bytes);
239    describe_chunk(&mut reader)
240}
241
242fn encode_at(values: &[i64], depth: u8, chooser: &dyn Chooser) -> Result<Vec<u8>> {
243    let offered = candidates(values, depth);
244    let mut best: Option<Vec<u8>> = None;
245    for kind in chooser.narrow_integers(values, &offered, depth) {
246        let Some(bytes) = encode_as(kind, values, depth, chooser)? else {
247            continue;
248        };
249        if best.as_ref().is_none_or(|current| bytes.len() < current.len()) {
250            best = Some(bytes);
251        }
252    }
253    // `Packed` applies to every input including the empty one, so the chooser always has at least
254    // one candidate and this cannot be reached without a bug in `candidates`.
255    best.ok_or_else(|| Error::internal("no encoding applied to the chunk"))
256}
257
258/// Which candidates are worth encoding for this input.
259///
260/// The filters here are not the cost model. They are the cases where the encoding cannot be
261/// expressed at all, or is provably larger than `Packed` on the same data, so that the exhaustive
262/// chooser does not spend a dictionary build on a column of 100,000 distinct values to discover
263/// what its distinct count already said.
264fn candidates(values: &[i64], depth: u8) -> Vec<Kind> {
265    let mut kinds = vec![Kind::Packed];
266    if depth >= MAX_DEPTH || values.is_empty() {
267        return kinds;
268    }
269    if values.iter().all(|value| *value == values[0]) {
270        // Nothing else can beat 13 bytes, so this is the whole answer rather than a candidate.
271        return vec![Kind::Constant];
272    }
273    if values.len() >= 2 && deltas_fit(values) {
274        kinds.push(Kind::Delta);
275    }
276    if run_count(values) * 4 <= values.len() * 3 {
277        kinds.push(Kind::Rle);
278    }
279    // One sort answers both of the remaining questions. It used to be two, because the distinct
280    // count and the most frequent value were asked for separately and each one sorted its own copy
281    // of the chunk and threw it away.
282    let (distinct, dominant) = spread_of(values);
283    if distinct * 2 <= values.len() {
284        kinds.push(Kind::Dict);
285    }
286    // Written as a match rather than as a chained `if let` because the minimum supported Rust
287    // version is 1.85 and let chains landed in 1.88.
288    match dominant {
289        Some((_, count)) if count * 10 >= values.len() * 8 => kinds.push(Kind::Sparse),
290        _ => {}
291    }
292    if stride_of(values).is_some() {
293        kinds.push(Kind::Strided);
294    }
295    kinds
296}
297
298/// `None` when the encoding does not apply to this input, which the caller treats as a candidate
299/// that did not run rather than as a failure.
300fn encode_as(
301    kind: Kind,
302    values: &[i64],
303    depth: u8,
304    chooser: &dyn Chooser,
305) -> Result<Option<Vec<u8>>> {
306    let mut out = Vec::new();
307    put_u8(&mut out, kind.tag());
308    put_u32(&mut out, u32::try_from(values.len()).map_err(|_| too_long(values.len()))?);
309    match kind {
310        Kind::Constant => {
311            let Some(first) = values.first() else {
312                return Ok(None);
313            };
314            if values.iter().any(|value| value != first) {
315                return Ok(None);
316            }
317            put_i64(&mut out, *first);
318        }
319        Kind::Packed => encode_packed(values, &mut out)?,
320        Kind::Delta => {
321            // An empty chunk has no first value to hang the differences off. The search never asks
322            // for one because `candidates` rules it out, but `encode_only` goes straight past that
323            // and used to index into the chunk anyway.
324            let (Some(first), Some(deltas)) = (values.first(), deltas(values)) else {
325                return Ok(None);
326            };
327            put_i64(&mut out, *first);
328            out.extend_from_slice(&encode_at(&deltas, depth + 1, chooser)?);
329        }
330        Kind::Rle => {
331            let (run_values, run_lengths) = runs(values);
332            if run_values.is_empty() {
333                return Ok(None);
334            }
335            out.extend_from_slice(&encode_at(&run_values, depth + 1, chooser)?);
336            out.extend_from_slice(&encode_at(&run_lengths, depth + 1, chooser)?);
337        }
338        Kind::Dict => {
339            let dictionary = distinct_values(values);
340            if dictionary.is_empty() {
341                return Ok(None);
342            }
343            let codes = codes_over(values, &dictionary);
344            out.extend_from_slice(&encode_at(&dictionary, depth + 1, chooser)?);
345            out.extend_from_slice(&encode_at(&codes, depth + 1, chooser)?);
346        }
347        Kind::Sparse => {
348            let Some((value, _)) = spread_of(values).1 else {
349                return Ok(None);
350            };
351            let mut positions = Vec::new();
352            let mut exceptions = Vec::new();
353            for (index, other) in values.iter().enumerate() {
354                if *other != value {
355                    positions.push(index as i64);
356                    exceptions.push(*other);
357                }
358            }
359            put_i64(&mut out, value);
360            put_u32(
361                &mut out,
362                u32::try_from(positions.len()).map_err(|_| too_long(positions.len()))?,
363            );
364            out.extend_from_slice(&encode_at(&positions, depth + 1, chooser)?);
365            out.extend_from_slice(&encode_at(&exceptions, depth + 1, chooser)?);
366        }
367        Kind::Strided => {
368            let (Some(base), Some(stride)) = (values.iter().min().copied(), stride_of(values))
369            else {
370                return Ok(None);
371            };
372            let mut steps = Vec::with_capacity(values.len());
373            for value in values {
374                let step = offset_from(*value, base) / stride;
375                // A step count the recursion cannot hold. An offset is at most 65 bits because both
376                // ends came from an `i64`, and only a stride of one leaves it that wide, which is a
377                // stride this never offers. Refused rather than wrapped, because a candidate that
378                // does not apply is one the chooser skips.
379                let Ok(step) = i64::try_from(step) else {
380                    return Ok(None);
381                };
382                steps.push(step);
383            }
384            put_i64(&mut out, base);
385            put_u64(&mut out, stride);
386            out.extend_from_slice(&encode_at(&steps, depth + 1, chooser)?);
387        }
388    }
389    Ok(Some(out))
390}
391
392/// Frame of reference and bit packing, one base and one width per 1024 values.
393///
394/// A base per unit rather than per chunk is most of what makes this work on real columns. A
395/// timestamp column over a day drifts across a range that needs 47 bits, and the same column inside
396/// any one unit spans a few seconds and needs 12. One base per row group would pay the 47 on every
397/// value.
398///
399/// A unit shorter than 1024 values, which is the last one of any chunk whose length is not a
400/// multiple of the unit and is the only one of every short array in a cascade, goes through
401/// [`bitpack::pack_tail`] instead. The transposed layout has no partial form and would charge a
402/// five entry dictionary for 1024 entries.
403fn encode_packed(values: &[i64], out: &mut Vec<u8>) -> Result<()> {
404    // The same three buffers for every unit, for the reason written on `Decoding` on the other side.
405    // The chooser encodes every candidate it is offered before it picks one, so this loop runs more
406    // often on the way in than the decoding loop does on the way out.
407    let mut offsets: Vec<u64> = Vec::with_capacity(VALUES);
408    // Held at the width 64 length for the reason written on `Decoding`, so a narrower unit writes
409    // the front of it and the resize per unit goes away.
410    let mut packed: Vec<u64> = vec![0; bitpack::packed_len::<u64>(64)];
411    let mut transposed = bitpack::Scratch::<u64>::new();
412    for unit in values.chunks(VALUES) {
413        let base = unit.iter().copied().min().unwrap_or(0);
414        offsets.clear();
415        offsets.extend(unit.iter().map(|value| offset_from(*value, base)));
416        let width = bitpack::required_width(&offsets);
417        put_i64(out, base);
418        put_u8(out, u8::try_from(width).map_err(|_| Error::internal("impossible width"))?);
419        if unit.len() == VALUES {
420            let words = bitpack::packed_len::<u64>(width);
421            bitpack::pack_with(&offsets, width, &mut packed[..words], &mut transposed)?;
422            for word in &packed[..words] {
423                put_u64(out, *word);
424            }
425        } else {
426            bitpack::pack_tail(&offsets, width, out)?;
427        }
428    }
429    Ok(())
430}
431
432/// The buffers a decode reuses from one unit of 1024 values to the next.
433///
434/// Every one of these used to be allocated inside the loop, and because they were allocated with a
435/// value rather than grown, the allocator zeroed them and then the decode overwrote every byte. In a
436/// ClickBench profile that zeroing was the single largest item, ahead of the unpacking it was making
437/// room for, because a scan pays it once per 1024 rows of every packed integer column it reads.
438///
439/// It is threaded through the recursion rather than made per call because a chunk is a cascade. A
440/// dictionary of deltas is three nested decodes, and each of them would otherwise make its own.
441///
442/// All three start empty and are grown on the first unit that needs them, to their largest size
443/// rather than to the size that unit wants, so that every unit after the first finds them the right
444/// length already and nothing is zeroed or resized again.
445///
446/// It lives on the thread rather than in the caller, which is worth saying why. A chunk is a row
447/// group, and a row group in the native format is about a thousand rows, which is one unit. So there
448/// is no second unit in a chunk to reuse anything and holding this per call is strictly worse than
449/// allocating per unit was: it was tried, and it cost more in the growing than it saved in the
450/// zeroing. What there are many of is chunks, one per part per column, and the thread that reads
451/// them reads them one after another. That is the loop the reuse belongs to, and reaching it by
452/// passing a buffer down would mean a parameter through every page decoder in the storage layer for
453/// a buffer none of them has an opinion about.
454struct Decoding {
455    /// The packed words of one unit, as read off the wire. Held at the width 64 length, which is the
456    /// largest a unit can be, so a narrower unit uses the front of it.
457    packed: Vec<u64>,
458    /// One unit of unpacked offsets, before the base is added back.
459    unit: Vec<u64>,
460    /// What the unpack transposes through.
461    transposed: bitpack::Scratch<u64>,
462}
463
464thread_local! {
465    /// The buffers this thread decodes through. See [`Decoding`].
466    static DECODING: std::cell::RefCell<Decoding> =
467        const { std::cell::RefCell::new(Decoding::new()) };
468}
469
470/// Runs a decode over this thread's buffers.
471///
472/// Nothing inside a decode calls back into one, so the borrow is never already taken. It is asked
473/// for rather than assumed anyway, and a decode that somehow arrives while another is running gets
474/// buffers of its own rather than a panic, because the alternative is a crash in a reader on a
475/// path nobody exercised.
476fn with_decoding<T>(run: impl FnOnce(&mut Decoding) -> T) -> T {
477    DECODING.with(|cell| match cell.try_borrow_mut() {
478        Ok(mut scratch) => run(&mut scratch),
479        Err(_) => run(&mut Decoding::new()),
480    })
481}
482
483impl Decoding {
484    /// Buffers that have not made room for anything yet.
485    const fn new() -> Self {
486        Self { packed: Vec::new(), unit: Vec::new(), transposed: bitpack::Scratch::new() }
487    }
488
489    /// Makes room for one unit. A no op every time after the first.
490    fn ready(&mut self) {
491        if self.unit.len() != VALUES {
492            self.unit.resize(VALUES, 0);
493            self.packed.resize(bitpack::packed_len::<u64>(64), 0);
494        }
495    }
496}
497
498fn decode_chunk(reader: &mut Reader<'_>, scratch: &mut Decoding) -> Result<Vec<i64>> {
499    let kind = Kind::from_tag(reader.u8()?)?;
500    let count = reader.u32()? as usize;
501    match kind {
502        Kind::Constant => Ok(vec![reader.i64()?; count]),
503        Kind::Packed => {
504            let mut values = Vec::with_capacity(count);
505            scratch.ready();
506            while values.len() < count {
507                let base = reader.i64()?;
508                let width = reader.u8()? as usize;
509                let wanted = (count - values.len()).min(VALUES);
510                if wanted == VALUES {
511                    let words = bitpack::packed_len::<u64>(width);
512                    for word in &mut scratch.packed[..words] {
513                        *word = reader.u64()?;
514                    }
515                    bitpack::unpack_with(
516                        &scratch.packed[..words],
517                        width,
518                        &mut scratch.unit,
519                        &mut scratch.transposed,
520                    )?;
521                    values.extend(scratch.unit.iter().map(|offset| value_from(*offset, base)));
522                } else {
523                    let bytes = reader.bytes(bitpack::tail_len(wanted, width))?;
524                    let unit = bitpack::unpack_tail(bytes, width, wanted)?;
525                    values.extend(unit.iter().map(|offset| value_from(*offset, base)));
526                }
527            }
528            Ok(values)
529        }
530        Kind::Delta => {
531            let first = reader.i64()?;
532            let deltas = decode_chunk(reader, scratch)?;
533            let mut values = Vec::with_capacity(count);
534            values.push(first);
535            let mut current = first;
536            for delta in deltas {
537                current = current.wrapping_add(unzigzag(delta as u64));
538                values.push(current);
539            }
540            check_count(values.len(), count)?;
541            Ok(values)
542        }
543        Kind::Rle => {
544            let run_values = decode_chunk(reader, scratch)?;
545            let run_lengths = decode_chunk(reader, scratch)?;
546            if run_values.len() != run_lengths.len() {
547                return Err(Error::internal("an RLE chunk has more runs than run lengths"));
548            }
549            let mut values = Vec::with_capacity(count);
550            for (value, length) in run_values.into_iter().zip(run_lengths) {
551                let length = usize::try_from(length)
552                    .map_err(|_| Error::internal("a negative RLE run length"))?;
553                values.extend(std::iter::repeat_n(value, length));
554            }
555            check_count(values.len(), count)?;
556            Ok(values)
557        }
558        Kind::Dict => {
559            let dictionary = decode_chunk(reader, scratch)?;
560            let codes = decode_chunk(reader, scratch)?;
561            let mut values = Vec::with_capacity(count);
562            for code in codes {
563                let index =
564                    usize::try_from(code).ok().and_then(|index| dictionary.get(index)).ok_or_else(
565                        || Error::internal(format!("code {code} is not in the dictionary")),
566                    )?;
567                values.push(*index);
568            }
569            check_count(values.len(), count)?;
570            Ok(values)
571        }
572        Kind::Sparse => {
573            let value = reader.i64()?;
574            let exception_count = reader.u32()? as usize;
575            let positions = decode_chunk(reader, scratch)?;
576            let exceptions = decode_chunk(reader, scratch)?;
577            if positions.len() != exception_count || exceptions.len() != exception_count {
578                return Err(Error::internal("a sparse chunk disagrees about its exception count"));
579            }
580            let mut values = vec![value; count];
581            for (position, exception) in positions.into_iter().zip(exceptions) {
582                let position = usize::try_from(position)
583                    .ok()
584                    .filter(|position| *position < count)
585                    .ok_or_else(|| {
586                        Error::internal(format!("exception at {position} is outside the chunk"))
587                    })?;
588                values[position] = exception;
589            }
590            Ok(values)
591        }
592        Kind::Strided => {
593            let base = reader.i64()?;
594            let stride = reader.u64()?;
595            let steps = decode_chunk(reader, scratch)?;
596            check_count(steps.len(), count)?;
597            let mut values = Vec::with_capacity(count);
598            for step in steps {
599                let step = u64::try_from(step)
600                    .map_err(|_| Error::internal("a negative number of strides"))?;
601                values.push(value_from(step.wrapping_mul(stride), base));
602            }
603            Ok(values)
604        }
605    }
606}
607
608fn describe_chunk(reader: &mut Reader<'_>) -> Result<String> {
609    let kind = Kind::from_tag(reader.u8()?)?;
610    let count = reader.u32()? as usize;
611    Ok(match kind {
612        Kind::Constant => {
613            reader.i64()?;
614            "CONSTANT".to_string()
615        }
616        Kind::Packed => {
617            let mut widths = Vec::new();
618            let mut seen = 0;
619            while seen < count {
620                reader.i64()?;
621                let width = reader.u8()? as usize;
622                let wanted = (count - seen).min(VALUES);
623                if wanted == VALUES {
624                    for _ in 0..bitpack::packed_len::<u64>(width) {
625                        reader.u64()?;
626                    }
627                } else {
628                    reader.bytes(bitpack::tail_len(wanted, width))?;
629                }
630                widths.push(width);
631                seen += wanted;
632            }
633            let low = widths.iter().copied().min().unwrap_or(0);
634            let high = widths.iter().copied().max().unwrap_or(0);
635            // Square brackets rather than round ones, so that a reader and a test can both take a
636            // parenthesis to mean one more level of cascade and nothing else.
637            if low == high {
638                format!("FOR+BITPACK[{low}]")
639            } else {
640                format!("FOR+BITPACK[{low}..{high}]")
641            }
642        }
643        Kind::Delta => {
644            reader.i64()?;
645            format!("DELTA({})", describe_chunk(reader)?)
646        }
647        Kind::Rle => {
648            let values = describe_chunk(reader)?;
649            let lengths = describe_chunk(reader)?;
650            format!("RLE({values}, {lengths})")
651        }
652        Kind::Dict => {
653            let dictionary = describe_chunk(reader)?;
654            let codes = describe_chunk(reader)?;
655            format!("DICT({dictionary}, {codes})")
656        }
657        Kind::Sparse => {
658            reader.i64()?;
659            reader.u32()?;
660            let positions = describe_chunk(reader)?;
661            let exceptions = describe_chunk(reader)?;
662            format!("SPARSE({positions}, {exceptions})")
663        }
664        Kind::Strided => {
665            reader.i64()?;
666            let stride = reader.u64()?;
667            format!("STRIDE[{stride}]({})", describe_chunk(reader)?)
668        }
669    })
670}
671
672/// The step every value of the chunk is a whole number of, or `None` when there is not one worth
673/// having.
674///
675/// This is the greatest common divisor of every value's distance from the smallest one. A timestamp
676/// column loaded from a source that recorded whole seconds holds microseconds that are all multiples
677/// of a million, and without this the frame of reference pays twenty bits a value to write down the
678/// twenty zero bits at the bottom of every one of them.
679///
680/// The walk stops the moment the divisor reaches one, which is what makes this affordable to ask on
681/// every chunk. Two values that share no factor are enough to answer, and on a column of arbitrary
682/// numbers that is almost always the first pair.
683fn stride_of(values: &[i64]) -> Option<u64> {
684    let base = values.iter().min().copied()?;
685    let mut divisor = 0u64;
686    for value in values {
687        divisor = gcd(divisor, offset_from(*value, base));
688        if divisor == 1 {
689            return None;
690        }
691    }
692    // Zero is every value being the base, which `Constant` already holds for nothing, and one is
693    // the frame of reference on its own with two extra words of header.
694    (divisor > 1).then_some(divisor)
695}
696
697/// Binary GCD, which is the one without a division in it.
698fn gcd(mut left: u64, mut right: u64) -> u64 {
699    if left == 0 {
700        return right;
701    }
702    if right == 0 {
703        return left;
704    }
705    let shift = (left | right).trailing_zeros();
706    left >>= left.trailing_zeros();
707    loop {
708        right >>= right.trailing_zeros();
709        if left > right {
710            std::mem::swap(&mut left, &mut right);
711        }
712        right -= left;
713        if right == 0 {
714            return left << shift;
715        }
716    }
717}
718
719/// The distance from the frame of reference base, which is always representable in a `u64` because
720/// both ends came from an `i64` and the width of the difference is at most 65 bits minus the sign.
721fn offset_from(value: i64, base: i64) -> u64 {
722    (i128::from(value) - i128::from(base)) as u64
723}
724
725fn value_from(offset: u64, base: i64) -> i64 {
726    (i128::from(base) + i128::from(offset)) as i64
727}
728
729/// Zigzag, so that a column that counts down packs as narrowly as one that counts up. Without it a
730/// delta of -1 is 64 bits of ones.
731fn zigzag(value: i64) -> u64 {
732    ((value << 1) ^ (value >> 63)) as u64
733}
734
735fn unzigzag(value: u64) -> i64 {
736    ((value >> 1) as i64) ^ -((value & 1) as i64)
737}
738
739/// The zigzagged differences, or `None` if any difference is too wide to be one.
740///
741/// A column holding both `i64::MIN` and `i64::MAX` has a difference that does not fit in an `i64`,
742/// and rather than widening every delta array to 128 bits for a case that does not occur in data,
743/// the encoding declines to apply. `Packed` covers it.
744/// Whether every neighbouring difference fits in an `i64`, which is the only thing the candidate
745/// list needs to know about deltas.
746///
747/// The candidate list used to answer this by building the whole delta array and checking that it
748/// came back, which is an allocation and a pass over the chunk thrown away on every chunk, and then
749/// `Kind::Delta` built it again. This is the same pass with nothing kept.
750fn deltas_fit(values: &[i64]) -> bool {
751    values.windows(2).all(|pair| i64::try_from(i128::from(pair[1]) - i128::from(pair[0])).is_ok())
752}
753
754fn deltas(values: &[i64]) -> Option<Vec<i64>> {
755    let mut deltas = Vec::with_capacity(values.len().saturating_sub(1));
756    for pair in values.windows(2) {
757        let difference = i128::from(pair[1]) - i128::from(pair[0]);
758        let difference = i64::try_from(difference).ok()?;
759        deltas.push(zigzag(difference) as i64);
760    }
761    Some(deltas)
762}
763
764fn run_count(values: &[i64]) -> usize {
765    let mut runs = 0;
766    let mut previous = None;
767    for value in values {
768        if previous != Some(value) {
769            runs += 1;
770            previous = Some(value);
771        }
772    }
773    runs
774}
775
776fn runs(values: &[i64]) -> (Vec<i64>, Vec<i64>) {
777    let mut run_values: Vec<i64> = Vec::new();
778    let mut run_lengths: Vec<i64> = Vec::new();
779    for value in values {
780        if run_values.last() == Some(value) {
781            *run_lengths.last_mut().expect("a run length exists beside every run value") += 1;
782        } else {
783            run_values.push(*value);
784            run_lengths.push(1);
785        }
786    }
787    (run_values, run_lengths)
788}
789
790/// The distinct values in sorted order.
791///
792/// Sorted rather than in order of first appearance, because an ordered dictionary is what lets a
793/// range predicate become a code range instead of a code set, per section 6.7, and because the
794/// codes of a clustered column then run in order and delta encode.
795/// How many distinct values there are and which one occurs most often, from one sort.
796///
797/// Both questions are about the histogram of the chunk and neither needs the histogram itself, so
798/// one sorted copy and one walk over it answers both. They used to be two functions that each sorted
799/// their own copy and threw it away, which is a chunk sorted twice on every chunk at every level of
800/// the cascade before a single candidate has been encoded.
801///
802/// No hash map, because the sort is what makes the walk a scan of equal runs, and a hash map would
803/// pay a lookup per value to learn the same thing.
804fn spread_of(values: &[i64]) -> (usize, Option<(i64, usize)>) {
805    let mut sorted = values.to_vec();
806    sorted.sort_unstable();
807    let mut distinct = 0;
808    let mut best: Option<(i64, usize)> = None;
809    let mut index = 0;
810    while index < sorted.len() {
811        let value = sorted[index];
812        let mut end = index;
813        while end < sorted.len() && sorted[end] == value {
814            end += 1;
815        }
816        distinct += 1;
817        let count = end - index;
818        if best.is_none_or(|(_, seen)| count > seen) {
819            best = Some((value, count));
820        }
821        index = end;
822    }
823    (distinct, best)
824}
825
826/// The distinct values in sorted order, for the same reason the string dictionary is sorted: an
827/// ordered dictionary turns a range predicate into a code range rather than a code set.
828fn distinct_values(values: &[i64]) -> Vec<i64> {
829    let mut distinct = values.to_vec();
830    distinct.sort_unstable();
831    distinct.dedup();
832    distinct
833}
834
835/// Where each value sits in the dictionary.
836///
837/// The string side builds its dictionary and its codes together from one sort of a permutation,
838/// because the alternative there is a copy of every value onto the heap and a `memcmp` per level of
839/// a binary search per row. This side was changed to match and it measured slower, so it was changed
840/// back. An integer dictionary only exists when the distinct count is at most half the row count, so
841/// the search is over something small and cache resident, the comparison is one integer rather than
842/// a string, and carrying the source index through the sort means sorting a padded sixteen byte pair
843/// instead of an eight byte value. The search is cheaper than the wider sort.
844fn codes_over(values: &[i64], dictionary: &[i64]) -> Vec<i64> {
845    values
846        .iter()
847        .map(|value| {
848            dictionary
849                .binary_search(value)
850                .expect("the dictionary is the distinct values of this chunk") as i64
851        })
852        .collect()
853}
854
855fn check_count(actual: usize, expected: usize) -> Result<()> {
856    if actual == expected {
857        Ok(())
858    } else {
859        Err(Error::internal(format!(
860            "a chunk says it holds {expected} values and decoded to {actual}"
861        )))
862    }
863}
864
865fn too_long(len: usize) -> Error {
866    Error::internal(format!("a chunk of {len} values is longer than the format allows"))
867}
868
869fn put_u8(out: &mut Vec<u8>, value: u8) {
870    out.push(value);
871}
872
873fn put_u32(out: &mut Vec<u8>, value: u32) {
874    out.extend_from_slice(&value.to_le_bytes());
875}
876
877fn put_u64(out: &mut Vec<u8>, value: u64) {
878    out.extend_from_slice(&value.to_le_bytes());
879}
880
881fn put_i64(out: &mut Vec<u8>, value: i64) {
882    out.extend_from_slice(&value.to_le_bytes());
883}
884
885#[cfg(test)]
886mod tests {
887    use super::*;
888
889    fn round_trip(values: &[i64]) -> Vec<u8> {
890        let bytes = encode(values).unwrap();
891        assert_eq!(decode(&bytes).unwrap(), values, "{}", describe(&bytes).unwrap());
892        bytes
893    }
894
895    fn kind_of(bytes: &[u8]) -> Kind {
896        Kind::from_tag(bytes[0]).unwrap()
897    }
898
899    /// The same xorshift the bit packing tests use, for the same reason.
900    struct Random(u64);
901
902    impl Random {
903        fn new() -> Self {
904            Self(0x9e37_79b9_7f4a_7c15)
905        }
906
907        fn next(&mut self) -> u64 {
908            self.0 ^= self.0 << 13;
909            self.0 ^= self.0 >> 7;
910            self.0 ^= self.0 << 17;
911            self.0
912        }
913    }
914
915    #[test]
916    fn the_dictionary_is_sorted_and_the_codes_point_back_at_the_values() {
917        let values = vec![30i64, 10, 30, 20, 10, -5];
918        let dictionary = distinct_values(&values);
919        let codes = codes_over(&values, &dictionary);
920        assert_eq!(dictionary, vec![-5, 10, 20, 30]);
921        assert_eq!(codes, vec![3, 1, 3, 2, 1, 0]);
922        for (code, value) in codes.iter().zip(&values) {
923            assert_eq!(dictionary[*code as usize], *value);
924        }
925    }
926
927    #[test]
928    fn one_sort_gives_the_distinct_count_and_the_most_frequent_value() {
929        let values = vec![7i64, 7, 7, 1, 2, 2];
930        assert_eq!(spread_of(&values), (3, Some((7, 3))));
931        assert_eq!(spread_of(&[]), (0, None));
932        assert_eq!(spread_of(&[9]), (1, Some((9, 1))));
933
934        // A tie goes to the value that sorts first, which is arbitrary but has to be stable,
935        // because Sparse writes the dominant value into the chunk and the size depends on it.
936        assert_eq!(spread_of(&[4i64, 4, 8, 8]), (2, Some((4, 2))));
937    }
938
939    #[test]
940    fn deltas_that_do_not_fit_are_refused_before_they_are_built() {
941        assert!(deltas_fit(&[1i64, 2, 3]));
942        assert!(deltas_fit(&[i64::MAX, i64::MAX]));
943        assert!(!deltas_fit(&[i64::MIN, i64::MAX]));
944        assert_eq!(deltas_fit(&[i64::MIN, i64::MAX]), deltas(&[i64::MIN, i64::MAX]).is_some());
945        assert_eq!(deltas_fit(&[1i64, 2, 3]), deltas(&[1i64, 2, 3]).is_some());
946    }
947
948    #[test]
949    fn what_the_chooser_returns_is_the_smallest_of_what_it_was_offered() {
950        // `offered` and `encode_only` are what `cargo xtask encode` splits the chooser's seconds
951        // with, so they have to describe the chooser that actually runs rather than a second copy
952        // of its rules that drifts. This is the assertion that keeps the two the same thing.
953        let mut random = Random::new();
954        let noise: Vec<i64> = (0..2000).map(|_| (random.next() % 5000) as i64).collect();
955        let runs: Vec<i64> = (0..2000).map(|index: i64| index / 100).collect();
956        let climbing: Vec<i64> = (0..2000).map(|index| 1_700_000_000 + index).collect();
957        for values in [noise, runs, climbing, vec![7; 300], Vec::new()] {
958            let chosen = encode(&values).unwrap();
959            let mut smallest: Option<Vec<u8>> = None;
960            for kind in offered(&values) {
961                let Some(bytes) = encode_only(kind, &values).unwrap() else {
962                    continue;
963                };
964                if smallest.as_ref().is_none_or(|best| bytes.len() < best.len()) {
965                    smallest = Some(bytes);
966                }
967            }
968            assert_eq!(smallest.as_deref(), Some(chosen.as_slice()), "{}", values.len());
969        }
970    }
971
972    #[test]
973    fn a_column_of_whole_seconds_in_microseconds_pays_nothing_for_the_zeroes() {
974        // What three ClickBench columns are. `epoch_ms(EventTime * 1000)` on a source that recorded
975        // whole seconds gives microseconds with twenty zero bits under every value, and a frame of
976        // reference over a part that spans a working day needs 36 bits to write them down.
977        let mut random = Random::new();
978        let day = 1_374_000_000_000_000i64;
979        let values: Vec<i64> =
980            (0..100_000).map(|_| day + (random.next() % 68_400) as i64 * 1_000_000).collect();
981        let bytes = round_trip(&values);
982        assert_eq!(kind_of(&bytes), Kind::Strided);
983        assert!(describe(&bytes).unwrap().starts_with("STRIDE[1000000]"), "{:?}", describe(&bytes));
984        // 17 bits a value for the range of seconds, against the 36 the microseconds need.
985        let strided = 100_000 * 17 / 8;
986        assert!(bytes.len() < strided + 2000, "{} bytes for {strided} of payload", bytes.len());
987
988        let plain = encode_only(Kind::Packed, &values).unwrap().expect("packing always applies");
989        assert!(
990            bytes.len() * 2 < plain.len(),
991            "{} strided against {} packed",
992            bytes.len(),
993            plain.len()
994        );
995    }
996
997    #[test]
998    fn a_stride_is_the_common_factor_of_the_distances_from_the_smallest_value() {
999        assert_eq!(stride_of(&[10i64, 20, 40]), Some(10));
1000        // The base is the smallest value and not zero, so a column that does not start on a
1001        // multiple of its own step still has one.
1002        assert_eq!(stride_of(&[7i64, 17, 37]), Some(10));
1003        assert_eq!(stride_of(&[10i64, 20, 23]), None);
1004        // Every value the same is `Constant`'s case and this declines it rather than dividing by a
1005        // stride of zero.
1006        assert_eq!(stride_of(&[5i64; 100]), None);
1007        assert_eq!(stride_of(&[]), None);
1008        // The two ends of the type, where the distance needs 65 bits and only a `u64` holds it.
1009        assert_eq!(stride_of(&[i64::MIN, i64::MAX]), Some(u64::MAX));
1010    }
1011
1012    #[test]
1013    fn a_stride_across_the_whole_of_the_type_round_trips() {
1014        // The distance is 65 bits, so the step count is one and the offset it comes back as is a
1015        // number no `i64` holds. This is the arithmetic the encoder has to do in `u64`.
1016        for values in [vec![i64::MIN, i64::MAX], vec![i64::MIN, 0, i64::MAX]] {
1017            let bytes = round_trip(&values);
1018            assert_eq!(decode(&bytes).unwrap(), values);
1019        }
1020    }
1021
1022    #[test]
1023    fn a_column_with_no_common_factor_is_not_offered_a_stride() {
1024        let mut random = Random::new();
1025        let values: Vec<i64> = (0..2000).map(|_| (random.next() % 1_000_000) as i64).collect();
1026        assert!(!offered(&values).contains(&Kind::Strided));
1027        assert!(encode_only(Kind::Strided, &values).unwrap().is_none());
1028    }
1029
1030    #[test]
1031    fn an_empty_chunk_round_trips() {
1032        let bytes = round_trip(&[]);
1033        assert_eq!(bytes.len(), 5);
1034    }
1035
1036    #[test]
1037    fn a_constant_column_costs_thirteen_bytes_however_long_it_is() {
1038        let bytes = round_trip(&vec![42; 1_000_000]);
1039        assert_eq!(kind_of(&bytes), Kind::Constant);
1040        assert_eq!(bytes.len(), 13);
1041    }
1042
1043    #[test]
1044    fn a_narrow_range_is_packed_at_the_width_of_the_range_and_not_of_the_type() {
1045        // 100_000 values between 1000 and 1063 is 6 bits each, plus 9 bytes of header per 1024.
1046        let mut random = Random::new();
1047        let values: Vec<i64> = (0..100_000).map(|_| 1000 + (random.next() % 64) as i64).collect();
1048        let bytes = round_trip(&values);
1049        assert_eq!(kind_of(&bytes), Kind::Packed);
1050        let packed = 100_000 * 6 / 8;
1051        assert!(bytes.len() < packed + 2000, "{} bytes for {packed} of payload", bytes.len());
1052        assert!(bytes.len() > packed, "{} bytes cannot hold {packed}", bytes.len());
1053    }
1054
1055    #[test]
1056    fn a_counter_becomes_deltas_and_then_a_constant() {
1057        // The classic case and the reason DELTA exists. A million consecutive integers is a
1058        // difference of 1 a million times, which is a constant chunk under the delta.
1059        let values: Vec<i64> = (0..1_000_000).collect();
1060        let bytes = round_trip(&values);
1061        assert_eq!(kind_of(&bytes), Kind::Delta);
1062        assert_eq!(describe(&bytes).unwrap(), "DELTA(CONSTANT)");
1063        assert!(bytes.len() < 40, "{} bytes for a counter", bytes.len());
1064    }
1065
1066    #[test]
1067    fn a_column_that_counts_down_is_as_cheap_as_one_that_counts_up() {
1068        // What zigzag is for. Without it every delta is -1, which is 64 bits of ones.
1069        let up: Vec<i64> = (0..100_000).collect();
1070        let down: Vec<i64> = (0..100_000).rev().collect();
1071        assert_eq!(round_trip(&up).len(), round_trip(&down).len());
1072    }
1073
1074    #[test]
1075    fn long_runs_become_rle() {
1076        let mut values = Vec::new();
1077        for run in 0..1000 {
1078            values.extend(std::iter::repeat_n(run % 7, 200));
1079        }
1080        let bytes = round_trip(&values);
1081        assert_eq!(kind_of(&bytes), Kind::Rle);
1082        assert!(bytes.len() < 2000, "{} bytes for 1000 runs", bytes.len());
1083    }
1084
1085    #[test]
1086    fn a_low_cardinality_column_becomes_a_dictionary() {
1087        // Values that are far apart so that packing them directly is 30 bits each, and only 40 of
1088        // them so that the codes are 6 bits each. The dictionary has to win by a factor of five.
1089        //
1090        // Drawn at random rather than laid out at a fixed interval, because a fixed interval is a
1091        // stride and STRIDE writes the same codes without a dictionary to point them at.
1092        let mut random = Random::new();
1093        let dictionary: Vec<i64> =
1094            (0..40).map(|_| 1_000_000_000 + (random.next() % (1 << 30)) as i64).collect();
1095        let values: Vec<i64> =
1096            (0..100_000).map(|_| dictionary[(random.next() % 40) as usize]).collect();
1097        let bytes = round_trip(&values);
1098        assert_eq!(kind_of(&bytes), Kind::Dict);
1099        assert!(bytes.len() < 100_000, "{} bytes", bytes.len());
1100    }
1101
1102    #[test]
1103    fn a_nearly_constant_column_becomes_sparse() {
1104        let mut values = vec![0i64; 100_000];
1105        for index in 0..300 {
1106            values[index * 331] = 1 << 40;
1107        }
1108        let bytes = round_trip(&values);
1109        assert_eq!(kind_of(&bytes), Kind::Sparse);
1110        assert!(bytes.len() < 3000, "{} bytes for 300 exceptions", bytes.len());
1111    }
1112
1113    #[test]
1114    fn the_cascade_goes_more_than_one_level_deep() {
1115        // The whole point of section 6.3. A dictionary over a clustered column produces codes that
1116        // run in long stretches, and the run lengths of those are themselves compressible.
1117        let mut values = Vec::new();
1118        for index in 0..2000i64 {
1119            values.extend(std::iter::repeat_n(1_000_000 + (index % 5) * 104_729, 100));
1120        }
1121        let bytes = round_trip(&values);
1122        let shape = describe(&bytes).unwrap();
1123        assert!(shape.contains('('), "{shape} is not a cascade");
1124        assert!(bytes.len() < 4000, "{} bytes: {shape}", bytes.len());
1125    }
1126
1127    #[test]
1128    fn random_data_is_packed_at_full_width_and_costs_what_it_costs() {
1129        // The case where nothing works, which has to come out at eight bytes a value plus change
1130        // rather than at eight bytes a value plus a dictionary of every value in the column.
1131        let mut random = Random::new();
1132        let values: Vec<i64> = (0..10_000).map(|_| random.next() as i64).collect();
1133        let bytes = round_trip(&values);
1134        assert_eq!(kind_of(&bytes), Kind::Packed);
1135        assert!(bytes.len() < 10_000 * 8 + 1000, "{} bytes", bytes.len());
1136    }
1137
1138    #[test]
1139    fn the_extremes_of_the_type_survive() {
1140        // Every offset and every delta in here overflows something if the arithmetic is done in 64
1141        // bits, which is why it is done in 128.
1142        let values = vec![i64::MIN, i64::MAX, 0, -1, i64::MIN, i64::MAX];
1143        round_trip(&values);
1144        round_trip(&[i64::MIN; 3]);
1145        round_trip(&[i64::MIN, i64::MIN + 1]);
1146    }
1147
1148    #[test]
1149    fn a_chunk_that_is_not_a_multiple_of_the_unit_round_trips() {
1150        for len in [1, 2, 1023, 1024, 1025, 2047, 2049] {
1151            let values: Vec<i64> = (0..len).map(|index| (index * 31 % 97) as i64).collect();
1152            round_trip(&values);
1153        }
1154    }
1155
1156    #[test]
1157    fn units_of_different_widths_in_one_chunk_do_not_read_each_others_leftovers() {
1158        // A decode reuses its buffers from one unit to the next instead of getting a zeroed one
1159        // each time, so a unit that wrote fewer bits than the unit before it would come back with
1160        // the older unit's values in the bits it did not write. Each run of 1024 here needs a
1161        // different width and the widths go up and down, and the last run repeats the first, which
1162        // is the pair that would agree by accident if the reuse were wrong in the obvious way.
1163        //
1164        // The values are random rather than written out because this has to stay one packed chunk
1165        // of six units to be testing anything, and the first version of it was arithmetic and got
1166        // cascaded into a delta of runs where every nested array was under a unit long. That was
1167        // caught by gating a panic on the second unit and rerunning, which this version reaches and
1168        // the old one did not, and the assertion on the shape below is there so it stays reached.
1169        let mut random = Random::new();
1170        let mut values = Vec::new();
1171        for width in [40u32, 3, 61, 1, 17, 40] {
1172            for _ in 0..1024 {
1173                values.push((random.next() & ((1u64 << width) - 1)) as i64);
1174            }
1175        }
1176        let bytes = encode(&values).unwrap();
1177        let described = describe(&bytes).unwrap();
1178        assert!(described.starts_with("FOR+BITPACK"), "expected one packed chunk, got {described}");
1179        assert_eq!(decode(&bytes).unwrap(), values, "{described}");
1180    }
1181
1182    #[test]
1183    fn a_cascade_decodes_the_same_through_a_shared_scratch_as_through_its_own() {
1184        // The scratch is threaded through the recursion, so a dictionary of deltas is three nested
1185        // decodes sharing one set of buffers. Nothing in the nesting arms holds a buffer across the
1186        // call it makes, and this is the test that says so: a chunk long enough to cascade and wide
1187        // enough to bit pack at more than one level, decoded whole.
1188        let mut values = Vec::new();
1189        for index in 0..8192i64 {
1190            values.push(1_600_000_000 + index / 4 + (index % 7) * 1_000);
1191        }
1192        let bytes = encode(&values).unwrap();
1193        let described = describe(&bytes).unwrap();
1194        assert!(described.contains('('), "expected a cascade, got {described}");
1195        assert_eq!(decode(&bytes).unwrap(), values, "{described}");
1196    }
1197
1198    #[test]
1199    fn a_partial_unit_costs_its_own_values_and_not_a_whole_unit() {
1200        // Three values that need 40 bits each. In the transposed layout a unit is 1024 values
1201        // whether it holds them or not, so this would be 5 KB, and every nested array in a cascade
1202        // is this short. It is 15 bytes of payload and 14 of header.
1203        let values = vec![1i64 << 39, (1 << 39) + 7, 1 << 38];
1204        let bytes = encode_only(Kind::Packed, &values).unwrap().unwrap();
1205        assert_eq!(bytes.len(), 5 + 9 + 15);
1206        assert_eq!(decode(&bytes).unwrap(), values);
1207    }
1208
1209    #[test]
1210    fn the_frame_of_reference_is_per_unit_and_not_per_chunk() {
1211        // A column that drifts, which is what a timestamp column and a clustered key both do. Each
1212        // unit here spans 1023 and packs at 10 bits, and a base per chunk would pay the 22 bits the
1213        // whole chunk spans on every value in it.
1214        let values: Vec<i64> =
1215            (0..4096i64).map(|index| (index / 1024) * 1_000_000 + (index % 1024)).collect();
1216        let bytes = encode_only(Kind::Packed, &values).unwrap().unwrap();
1217        assert_eq!(describe(&bytes).unwrap(), "FOR+BITPACK[10]");
1218        assert_eq!(decode(&bytes).unwrap(), values);
1219    }
1220
1221    #[test]
1222    fn every_candidate_that_applies_decodes_to_the_input() {
1223        // The chooser only ever hands back the smallest, so without this the other five are only
1224        // tested when they happen to win. Any of them being wrong is a wrong answer that appears
1225        // when a column's distribution shifts.
1226        let mut values = vec![5i64; 3000];
1227        for (index, value) in values.iter_mut().enumerate() {
1228            if index % 500 == 0 {
1229                *value = index as i64;
1230            }
1231        }
1232        let applicable = candidates(&values, 0);
1233        assert!(applicable.len() >= 4, "{applicable:?}");
1234        for kind in applicable {
1235            let bytes = encode_only(kind, &values).unwrap().unwrap();
1236            assert_eq!(decode(&bytes).unwrap(), values, "{}", kind.name());
1237        }
1238    }
1239
1240    /// The test above only asks the kinds `candidates` offered, so between them the two cover the
1241    /// encoders on input the search would give them and nothing else. `encode_only` does not go
1242    /// through `candidates` at all, so every one of its callers can hand an encoder a shape the
1243    /// filter would have refused, and the empty chunk is the shape that used to panic.
1244    #[test]
1245    fn every_kind_that_applies_decodes_to_what_it_was_given() {
1246        let shapes: Vec<Vec<i64>> = vec![
1247            Vec::new(),
1248            vec![5; 1024],
1249            vec![i64::MIN, i64::MAX, 0, -1],
1250            (0..1024).map(|at| at * 7).collect(),
1251            (0..1024).map(|at| at % 17).collect(),
1252            (0..1024).map(|at| if at % 100 == 0 { at } else { 3 }).collect(),
1253            (0..1024).map(|at| -at * 1_000_003).collect(),
1254            (0..1024_i64)
1255                .map(|at| {
1256                    at.wrapping_mul(6_364_136_223_846_793_005)
1257                        .wrapping_add(1_442_695_040_888_963_407)
1258                })
1259                .collect(),
1260        ];
1261        let kinds =
1262            [Kind::Constant, Kind::Packed, Kind::Delta, Kind::Rle, Kind::Dict, Kind::Sparse];
1263        for values in &shapes {
1264            for kind in kinds {
1265                let Some(bytes) = encode_only(kind, values).unwrap() else {
1266                    continue;
1267                };
1268                assert_eq!(
1269                    &decode(&bytes).unwrap(),
1270                    values,
1271                    "{} over {} values",
1272                    kind.name(),
1273                    values.len()
1274                );
1275            }
1276        }
1277    }
1278
1279    #[test]
1280    fn the_chooser_picks_the_smallest_candidate_rather_than_the_first_that_applies() {
1281        let mut values = vec![5i64; 3000];
1282        values[1500] = 9;
1283        let chosen = encode(&values).unwrap();
1284        for (_, size) in candidate_sizes(&values).unwrap() {
1285            assert!(chosen.len() <= size);
1286        }
1287    }
1288
1289    #[test]
1290    fn a_truncated_chunk_is_an_error_and_not_a_panic() {
1291        let bytes = encode(&[1, 2, 3, 4, 5]).unwrap();
1292        for len in 0..bytes.len() {
1293            let error = decode(&bytes[..len]).unwrap_err();
1294            assert!(error.message().contains("chunk"), "{error}");
1295        }
1296    }
1297
1298    #[test]
1299    fn trailing_bytes_are_an_error() {
1300        let mut bytes = encode(&[1, 2, 3]).unwrap();
1301        bytes.push(0);
1302        let error = decode(&bytes).unwrap_err();
1303        assert!(error.message().contains("left over"), "{error}");
1304    }
1305
1306    #[test]
1307    fn an_unknown_tag_is_an_error() {
1308        let error = decode(&[99, 0, 0, 0, 0]).unwrap_err();
1309        assert!(error.message().contains("unknown encoding tag"), "{error}");
1310    }
1311
1312    #[test]
1313    fn a_dictionary_code_outside_the_dictionary_is_an_error() {
1314        // A corrupted or malicious chunk must not index out of bounds, and this is the one place in
1315        // the decoder where a number that came off the disk is used as an index. Built by hand
1316        // rather than by corrupting a real chunk, because a byte offset into an encoding that the
1317        // chooser is free to change is a test that breaks for the wrong reason.
1318        let mut bytes = vec![Kind::Dict.tag()];
1319        put_u32(&mut bytes, 1);
1320        bytes.extend_from_slice(&encode(&[10]).unwrap());
1321        bytes.extend_from_slice(&encode(&[5]).unwrap());
1322        let error = decode(&bytes).unwrap_err();
1323        assert!(error.message().contains("not in the dictionary"), "{error}");
1324    }
1325
1326    #[test]
1327    fn a_negative_run_length_is_an_error() {
1328        // The other number off the disk that the decoder would otherwise trust, and the one that
1329        // would turn into an allocation of nine quintillion values.
1330        let mut bytes = vec![Kind::Rle.tag()];
1331        put_u32(&mut bytes, 4);
1332        bytes.extend_from_slice(&encode(&[7]).unwrap());
1333        bytes.extend_from_slice(&encode(&[-4]).unwrap());
1334        let error = decode(&bytes).unwrap_err();
1335        assert!(error.message().contains("negative"), "{error}");
1336    }
1337
1338    #[test]
1339    fn the_cascade_depth_is_bounded() {
1340        // Without the limit a chooser that finds a dictionary of a dictionary of a dictionary would
1341        // recurse until the values ran out, and the encode time of a wide column would be a
1342        // surprise rather than a number.
1343        let values: Vec<i64> = (0..50_000).map(|index| (index / 100) % 250).collect();
1344        let bytes = round_trip(&values);
1345        let shape = describe(&bytes).unwrap();
1346        let depth = shape.matches('(').count();
1347        assert!(depth <= MAX_DEPTH as usize, "{shape} is {depth} deep");
1348    }
1349
1350    #[test]
1351    fn candidate_sizes_reports_what_the_chooser_looked_at() {
1352        let values: Vec<i64> = (0..5000).map(|index| index % 17).collect();
1353        let sizes = candidate_sizes(&values).unwrap();
1354        assert!(sizes.iter().any(|(kind, _)| *kind == Kind::Dict));
1355        assert!(sizes.iter().any(|(kind, _)| *kind == Kind::Packed));
1356        assert!(sizes.iter().all(|(_, size)| *size > 0));
1357    }
1358
1359    #[test]
1360    fn a_chunk_can_be_read_from_the_front_of_a_longer_buffer() {
1361        // What a string column does. It writes an integer chunk of lengths into the middle of its
1362        // own body and has to find the end of it again on the way back.
1363        let first = encode(&[1, 2, 3]).unwrap();
1364        let second: Vec<i64> = (0..3000).map(|index| index % 11).collect();
1365        let second_bytes = encode(&second).unwrap();
1366        let mut joined = first.clone();
1367        joined.extend_from_slice(&second_bytes);
1368        joined.extend_from_slice(b"and then something else");
1369
1370        let (values, used) = decode_prefix(&joined).unwrap();
1371        assert_eq!(values, vec![1, 2, 3]);
1372        assert_eq!(used, first.len());
1373        let (more, used_again) = decode_prefix(&joined[used..]).unwrap();
1374        assert_eq!(more, second);
1375        assert_eq!(used_again, second_bytes.len());
1376
1377        let (text, described) = describe_prefix(&joined).unwrap();
1378        assert_eq!(described, first.len());
1379        assert_eq!(text, describe(&first).unwrap());
1380    }
1381
1382    #[test]
1383    fn a_truncated_chunk_is_still_an_error_when_read_as_a_prefix() {
1384        let bytes = encode(&(0..2000).collect::<Vec<i64>>()).unwrap();
1385        for len in 0..bytes.len() {
1386            assert!(decode_prefix(&bytes[..len]).is_err(), "{len} bytes decoded");
1387        }
1388    }
1389}