Skip to main content

rudb_encoding/
bitpack.rs

1//! Bit packing in the FastLanes unified transposed layout.
2//!
3//! Packing N values of a fixed bit width into a dense buffer is the bottom of every integer
4//! encoding in `spec/06-compression.md` section 6.2. FOR subtracts a base and packs. DELTA
5//! differences and packs. DICT produces codes and packs them. So this is the one kernel that runs
6//! over more bytes than anything else in the system, and the layout it uses decides whether the
7//! decoder can be data parallel or has to walk a dependency chain.
8//!
9//! The obvious layout writes value 0 in the low bits of the first word, value 1 above it, and so
10//! on. Unpacking that requires knowing where the previous value ended, which is a sequential
11//! dependency, and a SIMD implementation has to fight it with shuffles that differ per width and
12//! per instruction set. FastLanes takes the other road. The 1024 values of a vector are seen as a
13//! matrix of `T` rows by `1024 / T` lanes, where `T` is the bit width of the type, and the packing
14//! runs down the rows of every lane at once. Every lane has the same bit schedule, so unpacking
15//! lane 0 and lane 31 is the same instruction sequence with no cross lane data movement at all.
16//! That is what makes one scalar reference implementation and one AVX-512 implementation and one
17//! NEON implementation agree bit for bit, and it is why the vector size is 1024 rather than a
18//! rounder number.
19//!
20//! The price is that the values come out permuted within the vector. Row `r` lane `l` is not the
21//! `r * lanes + l`th value of the input. Section 6.2 says why that is acceptable: an operator
22//! working inside one vector does not care what order the rows are in, so the permutation only
23//! has to be undone when a vector is materialized in row order. [`transpose`] and [`untranspose`]
24//! are that step, and they are deliberately separate from [`pack_transposed`] and
25//! [`unpack_transposed`] so that the engine can keep data permuted through a whole pipeline and
26//! pay for the reordering once at the end rather than twice per operator.
27//!
28//! There is a second layout in here, in [`pack_tail`], and it is the sequential one this module
29//! opens by arguing against. The transposed layout is all or nothing: a value lives at a row and a
30//! lane, the lanes are interleaved through the whole buffer, and no prefix of a packed unit holds a
31//! prefix of the values. So a unit holding 3 values costs exactly what a unit holding 1024 costs,
32//! and a cascade is full of short arrays. A five entry dictionary, a run length array, an exception
33//! list. Storing three numbers in 5 KB is not a compressed format. The tail packer handles anything
34//! shorter than a unit, it has the dependency chain the transposed layout exists to avoid, and that
35//! is affordable there and nowhere else, because a tail is at most 1023 values and is decoded once
36//! while a full unit is on the hot path of every scan in the system.
37//!
38//! The permutation itself is a fixed shuffle of the eight bit groups of a row index, in the order
39//! 0, 4, 2, 6, 1, 5, 3, 7. That order is not arbitrary. It is the one that makes an eight way
40//! interleave of the rows land back in sequence under the pairwise unpacking pattern the paper
41//! uses, and the important property for us is only that it is a bijection that both directions
42//! agree on.
43
44use rudb_common::{Error, Result};
45
46/// How many values a packed unit holds. One vector, per `spec/06-compression.md` section 6.2.
47pub const VALUES: usize = 1024;
48
49/// The interleaving order of the eight row groups. See the module documentation.
50const ORDER: [usize; 8] = [0, 4, 2, 6, 1, 5, 3, 7];
51
52mod sealed {
53    pub trait Sealed {}
54    impl Sealed for u8 {}
55    impl Sealed for u16 {}
56    impl Sealed for u32 {}
57    impl Sealed for u64 {}
58}
59
60/// An unsigned integer type that can be bit packed.
61///
62/// Sealed, because the layout constants are only correct for the four widths that divide 1024 into
63/// a whole number of lanes, and because every kernel here does its arithmetic in `u64` and relies
64/// on every implementor fitting in one.
65pub trait Packable: sealed::Sealed + Copy + Ord + std::fmt::Debug {
66    /// Width of the type in bits. `T` in the module documentation.
67    const WIDTH: usize;
68    /// How many of these fit in the 1024 bit virtual register, which is how many lanes there are.
69    const LANES: usize = VALUES / Self::WIDTH;
70
71    /// Widens to the type the packing arithmetic is done in.
72    fn to_u64(self) -> u64;
73    /// Narrows back. The high bits are already known to be zero.
74    fn from_u64(value: u64) -> Self;
75}
76
77macro_rules! impl_packable {
78    ($($ty:ty),*) => {$(
79        impl Packable for $ty {
80            const WIDTH: usize = <$ty>::BITS as usize;
81
82            #[inline]
83            fn to_u64(self) -> u64 {
84                u64::from(self)
85            }
86
87            #[inline]
88            fn from_u64(value: u64) -> Self {
89                value as $ty
90            }
91        }
92    )*};
93}
94
95impl_packable!(u8, u16, u32, u64);
96
97/// A mask of the low `bits` bits, correct at 0 and at 64 where the shift would overflow.
98#[inline]
99const fn low_mask(bits: usize) -> u64 {
100    if bits >= 64 { u64::MAX } else { (1u64 << bits) - 1 }
101}
102
103/// A right shift that saturates to zero at 64 rather than overflowing.
104#[inline]
105const fn shift_right(value: u64, bits: usize) -> u64 {
106    if bits >= 64 { 0 } else { value >> bits }
107}
108
109/// Where the value at row `row` lane `lane` of the transposed matrix came from in the input.
110///
111/// The row index is split into a group and an offset within the group, the group is permuted by
112/// the fixed order in the module documentation, and the two are recombined with the offset as the
113/// high part. The lane index is untouched, which is the property that makes the layout lane
114/// parallel.
115///
116/// # Panics
117///
118/// If `row` is not below `T::WIDTH` or `lane` is not below `T::LANES`.
119#[inline]
120#[must_use]
121pub fn source_index<T: Packable>(row: usize, lane: usize) -> usize {
122    assert!(row < T::WIDTH, "row {row} is outside a {} bit type", T::WIDTH);
123    assert!(lane < T::LANES, "lane {lane} is outside {} lanes", T::LANES);
124    let group_size = T::WIDTH / 8;
125    let group = row / group_size;
126    let offset = row % group_size;
127    ((offset * 8) + ORDER[group]) * T::LANES + lane
128}
129
130/// Rewrites 1024 values from row order into the transposed layout.
131///
132/// # Errors
133///
134/// If either slice is not exactly [`VALUES`] long.
135pub fn transpose<T: Packable>(input: &[T], output: &mut [T]) -> Result<()> {
136    check_vector_len(input.len(), "input")?;
137    check_vector_len(output.len(), "output")?;
138    for row in 0..T::WIDTH {
139        for lane in 0..T::LANES {
140            output[row * T::LANES + lane] = input[source_index::<T>(row, lane)];
141        }
142    }
143    Ok(())
144}
145
146/// Rewrites 1024 values from the transposed layout back into row order.
147///
148/// # Errors
149///
150/// If either slice is not exactly [`VALUES`] long.
151pub fn untranspose<T: Packable>(input: &[T], output: &mut [T]) -> Result<()> {
152    check_vector_len(input.len(), "input")?;
153    check_vector_len(output.len(), "output")?;
154    for row in 0..T::WIDTH {
155        for lane in 0..T::LANES {
156            output[source_index::<T>(row, lane)] = input[row * T::LANES + lane];
157        }
158    }
159    Ok(())
160}
161
162/// How many words of `T` a packed vector of the given width occupies.
163///
164/// Every lane contributes `width` words, which is the same `width * 1024` bits the naive layout
165/// would use. The layout costs nothing in space.
166#[must_use]
167pub fn packed_len<T: Packable>(width: usize) -> usize {
168    width * T::LANES
169}
170
171/// How many bytes a whole unit of `u64` words occupies on the wire at the given width.
172///
173/// The serialized form of a full unit is [`packed_len`] words written little endian, so this is what
174/// a reader takes out of a chunk before handing it to [`unpack_unit_into`].
175#[must_use]
176pub fn unit_len(width: usize) -> usize {
177    packed_len::<u64>(width) * size_of::<u64>()
178}
179
180/// The smallest bit width that can hold every value in the slice. Zero for an empty slice or a
181/// slice of zeros, which [`pack_transposed`] handles as the degenerate case that stores nothing.
182#[must_use]
183pub fn required_width<T: Packable>(values: &[T]) -> usize {
184    let max = values.iter().copied().max().map_or(0, T::to_u64);
185    (64 - max.leading_zeros()) as usize
186}
187
188/// Packs a transposed vector at a fixed bit width.
189///
190/// The input is 1024 values already in the layout [`transpose`] produces, and the output is
191/// [`packed_len`] words. Every lane is packed independently and the loop over lanes is the one a
192/// SIMD implementation replaces with a single register.
193///
194/// # Errors
195///
196/// If the input is not [`VALUES`] long, if the output is not [`packed_len`] long, if `width`
197/// exceeds the width of the type, or if a value does not fit in `width` bits.
198pub fn pack_transposed<T: Packable>(input: &[T], width: usize, output: &mut [T]) -> Result<()> {
199    check_vector_len(input.len(), "input")?;
200    check_width::<T>(width)?;
201    if output.len() != packed_len::<T>(width) {
202        return Err(Error::internal(format!(
203            "a {width} bit packed vector is {} words, not {}",
204            packed_len::<T>(width),
205            output.len()
206        )));
207    }
208    if width == 0 {
209        // Nothing is stored. The caller has already established that every value is zero, either
210        // by asking for `required_width` or by being the CONSTANT encoding, and the check below
211        // enforces it rather than trusting it.
212        return check_all_zero(input);
213    }
214
215    let mask = low_mask(width);
216    let lanes = T::LANES;
217    for lane in 0..lanes {
218        // Bits already sitting in `accumulator`, always below `T::WIDTH` between iterations.
219        let mut filled = 0usize;
220        let mut accumulator = 0u64;
221        let mut word = 0usize;
222        for row in 0..T::WIDTH {
223            let value = input[row * lanes + lane].to_u64();
224            if value & !mask != 0 {
225                return Err(Error::internal(format!("value {value} does not fit in {width} bits")));
226            }
227            accumulator |= value << filled;
228            filled += width;
229            if filled >= T::WIDTH {
230                output[word * lanes + lane] = T::from_u64(accumulator & low_mask(T::WIDTH));
231                word += 1;
232                // The only value that can straddle the word boundary is the one just written, so
233                // the carry is a shift of it rather than anything kept from earlier rows.
234                let consumed = width - (filled - T::WIDTH);
235                filled -= T::WIDTH;
236                accumulator = shift_right(value, consumed);
237            }
238        }
239        debug_assert_eq!(filled, 0, "a packed lane always ends on a word boundary");
240    }
241    Ok(())
242}
243
244/// Unpacks into the transposed layout. The inverse of [`pack_transposed`].
245///
246/// # Errors
247///
248/// If the input is not [`packed_len`] long, if the output is not [`VALUES`] long, or if `width`
249/// exceeds the width of the type.
250pub fn unpack_transposed<T: Packable>(input: &[T], width: usize, output: &mut [T]) -> Result<()> {
251    check_width::<T>(width)?;
252    check_vector_len(output.len(), "output")?;
253    if input.len() != packed_len::<T>(width) {
254        return Err(Error::internal(format!(
255            "a {width} bit packed vector is {} words, not {}",
256            packed_len::<T>(width),
257            input.len()
258        )));
259    }
260    if width == 0 {
261        output.fill(T::from_u64(0));
262        return Ok(());
263    }
264
265    let mask = low_mask(width);
266    let lanes = T::LANES;
267    for lane in 0..lanes {
268        // Bits of the current word not yet handed out, right aligned in `buffer`.
269        let mut available = 0usize;
270        let mut buffer = 0u64;
271        let mut word = 0usize;
272        for row in 0..T::WIDTH {
273            let value = if available >= width {
274                let value = buffer & mask;
275                buffer = shift_right(buffer, width);
276                available -= width;
277                value
278            } else {
279                let next = input[word * lanes + lane].to_u64();
280                word += 1;
281                let taken = width - available;
282                let value = buffer | ((next & low_mask(taken)) << available);
283                buffer = shift_right(next, taken);
284                available = T::WIDTH - taken;
285                value
286            };
287            output[row * lanes + lane] = T::from_u64(value);
288        }
289    }
290    Ok(())
291}
292
293/// The buffer [`pack_with`] transposes through, kept so it can be reused.
294///
295/// Going between row order and the transposed layout needs somewhere to put the other order, and
296/// that somewhere is [`VALUES`] values, which is 8 KB for a `u64`. Allocating it per call is not the
297/// expensive part. Zeroing it is, because the allocator hands back a page it has to clear and the
298/// transpose then writes every element of it anyway. On a scan of a packed integer column that is
299/// once per 1024 rows, and it showed up as the largest single item in a ClickBench profile, larger
300/// than the unpacking it was making room for.
301///
302/// So a caller that packs more than one unit should make one of these and pass it in. The unpacking
303/// side does not need one at all any more: see [`unpack`].
304///
305/// It starts empty and grows on the first unit that needs it, because a caller holds one for a whole
306/// decode and most chunks are not bit packed at all. Making the buffer in the constructor was tried
307/// and was worse than what it replaced, by more than the zeroing it saved.
308#[derive(Debug)]
309pub struct Scratch<T: Packable> {
310    transposed: Vec<T>,
311}
312
313impl<T: Packable> Scratch<T> {
314    /// A scratch buffer that has not made room for anything yet.
315    #[must_use]
316    pub const fn new() -> Self {
317        Self { transposed: Vec::new() }
318    }
319
320    /// Makes room for one unit. A no op every time after the first.
321    fn ready(&mut self) {
322        if self.transposed.len() != VALUES {
323            self.transposed.resize(VALUES, T::from_u64(0));
324        }
325    }
326}
327
328impl<T: Packable> Default for Scratch<T> {
329    fn default() -> Self {
330        Self::new()
331    }
332}
333
334/// Packs a vector given in row order, transposing it first.
335///
336/// The engine does not use this. Data written by the storage layer is transposed once on the way
337/// in and stays that way, per the module documentation. This exists for tests, for the format lab,
338/// and for the one place that has to hand back a vector in the order the user gave it.
339///
340/// # Errors
341///
342/// As [`pack_transposed`].
343pub fn pack<T: Packable>(input: &[T], width: usize, output: &mut [T]) -> Result<()> {
344    pack_with(input, width, output, &mut Scratch::new())
345}
346
347/// As [`pack`], through a buffer the caller keeps rather than one allocated per call.
348///
349/// # Errors
350///
351/// As [`pack_transposed`].
352pub fn pack_with<T: Packable>(
353    input: &[T],
354    width: usize,
355    output: &mut [T],
356    scratch: &mut Scratch<T>,
357) -> Result<()> {
358    check_vector_len(input.len(), "input")?;
359    scratch.ready();
360    transpose(input, &mut scratch.transposed)?;
361    pack_transposed(&scratch.transposed, width, output)
362}
363
364/// Unpacks into row order. The inverse of [`pack`].
365///
366/// This is what every scan of a packed integer column goes through, so it is written as one pass
367/// rather than as [`unpack_transposed`] followed by [`untranspose`]. Those two are still here and
368/// still the definition of the layout, and the test below checks this agrees with them at every
369/// width, but running them in sequence costs three things this does not. A 1024 value buffer to
370/// hold the middle, a second read of all of it, and a scatter: `untranspose` walks its input in
371/// order and writes all over its output, which is a store that misses and a loop no compiler will
372/// turn into wider instructions.
373///
374/// The fused form works because a row has the same bit schedule in every lane. That is the whole
375/// point of the layout. Row `r` of every lane takes bits `r * width` to `(r + 1) * width` of that
376/// lane's stream, so which word to read and how far to shift it are decided once for the row, and
377/// what is left for the lanes is a load, a shift, an or, a mask and a store with no branch and no
378/// carry from the lane before. The lanes of a row are next to each other in both the packed words
379/// and the output, so that inner loop reads and writes straight lines. Where a row lands in the
380/// output is the permutation `untranspose` was applying, and since the lane index is the low part
381/// of it, it comes out as a base address for the row and costs nothing.
382///
383/// # Errors
384///
385/// As [`unpack_transposed`].
386pub fn unpack<T: Packable>(input: &[T], width: usize, output: &mut [T]) -> Result<()> {
387    unpack_mapped(input, width, output, T::from_u64)
388}
389
390/// As [`unpack`], putting each value through `value` on the way out.
391///
392/// The caller that wants this is one whose output is not the packed type. Frame of reference coding
393/// stores offsets from a base and hands back the base plus the offset, and a decode that unpacks
394/// into a buffer of offsets and then walks that buffer adding the base writes every value twice and
395/// reads it once in between. There is nowhere for the second pass to hide: the buffer is 8 KB, a
396/// chunk is about one unit, and a scan reads a chunk per part per column, so the pass is the same
397/// order of work as the unpacking it follows.
398///
399/// The mapping belongs at the store rather than after it because that is the one place the value is
400/// already in a register. Both loops below end in a store, so `value` is applied to something
401/// nothing else has to load again, and for the identity it compiles to what [`unpack`] compiled to
402/// before this existed.
403///
404/// # Errors
405///
406/// As [`unpack_transposed`].
407pub fn unpack_mapped<T: Packable, U: Copy>(
408    input: &[T],
409    width: usize,
410    output: &mut [U],
411    value: impl Fn(u64) -> U,
412) -> Result<()> {
413    check_width::<T>(width)?;
414    check_vector_len(output.len(), "output")?;
415    if input.len() != packed_len::<T>(width) {
416        return Err(Error::internal(format!(
417            "a {width} bit packed vector is {} words, not {}",
418            packed_len::<T>(width),
419            input.len()
420        )));
421    }
422    if width == 0 {
423        output.fill(value(0));
424        return Ok(());
425    }
426
427    let mask = low_mask(width);
428    let lanes = T::LANES;
429    let group_size = T::WIDTH / 8;
430    for row in 0..T::WIDTH {
431        let bit = row * width;
432        let word = bit / T::WIDTH;
433        let shift = bit % T::WIDTH;
434        // The same arithmetic as `source_index` with the lane left off, because the lane is the low
435        // part of it and the lanes of a row are consecutive from here.
436        let base = ((row % group_size) * 8 + ORDER[row / group_size]) * lanes;
437        let low = &input[word * lanes..(word + 1) * lanes];
438        let into = &mut output[base..base + lanes];
439        if shift + width <= T::WIDTH {
440            for lane in 0..lanes {
441                into[lane] = value((low[lane].to_u64() >> shift) & mask);
442            }
443        } else {
444            // The value straddles two words, so `shift` is above zero, the carry in from the word
445            // above is a left shift by less than the word width, and neither shift can overflow.
446            // There is a word above to read: a value that straddles into word `word + 1` is one the
447            // packer wrote there, and it wrote `width` words a lane.
448            let carried = T::WIDTH - shift;
449            let high = &input[(word + 1) * lanes..(word + 2) * lanes];
450            for lane in 0..lanes {
451                let bits = (low[lane].to_u64() >> shift) | (high[lane].to_u64() << carried);
452                into[lane] = value(bits & mask);
453            }
454        }
455    }
456    Ok(())
457}
458
459/// As [`unpack_mapped`] for a unit that is still the bytes it was written as.
460///
461/// This is the form every scan of a packed integer column goes through, and the reason it exists
462/// rather than the caller making a `&[u64]` first is that making one is a copy of the unit. The
463/// bytes arrive inside a chunk at whatever offset the chunk put them, so they are not eight byte
464/// aligned and cannot be looked at as words in place. Copying them somewhere aligned is 8 KB moved
465/// per thousand rows, which is the same order of work as the unpacking it feeds and which bought
466/// nothing: every word here is read exactly once, and an unaligned eight byte load is the same
467/// single instruction the aligned one is on anything this runs on.
468///
469/// The body is [`unpack_mapped`] at `T = u64` with the loads spelled out, and the test below checks
470/// the two agree at every width. It is written twice rather than made generic over where a word
471/// comes from because the slice form gets its bound checked once a row and this one cannot, so a
472/// shared inner loop would be the slower of the two shapes for both callers.
473///
474/// # Errors
475///
476/// If `width` exceeds 64, the output is not [`VALUES`] long, or the input is not [`unit_len`] bytes.
477pub fn unpack_unit_into<U: Copy>(
478    input: &[u8],
479    width: usize,
480    output: &mut [U],
481    value: impl Fn(u64) -> U,
482) -> Result<()> {
483    check_width::<u64>(width)?;
484    check_vector_len(output.len(), "output")?;
485    if input.len() != unit_len(width) {
486        return Err(Error::internal(format!(
487            "a {width} bit packed vector is {} bytes, not {}",
488            unit_len(width),
489            input.len()
490        )));
491    }
492    if width == 0 {
493        output.fill(value(0));
494        return Ok(());
495    }
496
497    let mask = low_mask(width);
498    let lanes = <u64 as Packable>::LANES;
499    let stride = lanes * size_of::<u64>();
500    for row in 0..u64::BITS as usize {
501        let bit = row * width;
502        let word = bit / u64::BITS as usize;
503        let shift = bit % u64::BITS as usize;
504        let base = ((row % 8) * 8 + ORDER[row / 8]) * lanes;
505        let low = &input[word * stride..(word + 1) * stride];
506        let into = &mut output[base..base + lanes];
507        if shift + width <= u64::BITS as usize {
508            for (lane, slot) in into.iter_mut().enumerate() {
509                *slot = value((word_at(low, lane * size_of::<u64>()) >> shift) & mask);
510            }
511        } else {
512            let carried = u64::BITS as usize - shift;
513            let high = &input[(word + 1) * stride..(word + 2) * stride];
514            for (lane, slot) in into.iter_mut().enumerate() {
515                let at = lane * size_of::<u64>();
516                let bits = (word_at(low, at) >> shift) | (word_at(high, at) << carried);
517                *slot = value(bits & mask);
518            }
519        }
520    }
521    Ok(())
522}
523
524/// Reads one value from a full transposed unit of packed `u64` values.
525///
526/// The serialized integer cascade stores packed words little endian. A point lookup starts from
527/// the row-order index, inverts the fixed FastLanes permutation, and reads the one or two words
528/// that hold that value. This is the point form of [`unpack`] for callers that need a sparse set of
529/// positions rather than a materialized vector.
530///
531/// # Errors
532///
533/// If `width` exceeds 64, `index` is outside a full unit, or `input` is not the exact byte length
534/// of a full unit at that width.
535pub fn unpack_u64_at(input: &[u8], width: usize, index: usize) -> Result<u64> {
536    check_width::<u64>(width)?;
537    if index >= VALUES {
538        return Err(Error::internal(format!(
539            "packed value {index} is outside a {VALUES} value unit"
540        )));
541    }
542    let expected = packed_len::<u64>(width) * size_of::<u64>();
543    if input.len() != expected {
544        return Err(Error::internal(format!(
545            "a {width} bit packed vector is {expected} bytes, not {}",
546            input.len()
547        )));
548    }
549    if width == 0 {
550        return Ok(0);
551    }
552
553    let lanes = <u64 as Packable>::LANES;
554    let block = index / lanes;
555    let lane = index % lanes;
556    // ORDER is its own inverse. `block` is the permuted row group and offset produced by
557    // `source_index`, so applying ORDER again recovers the original group.
558    let group = ORDER[block % 8];
559    let row = group * (<u64 as Packable>::WIDTH / 8) + block / 8;
560    let bit = row * width;
561    let word = bit / <u64 as Packable>::WIDTH;
562    let shift = bit % <u64 as Packable>::WIDTH;
563    let low = word_at(input, (word * lanes + lane) * size_of::<u64>());
564    let bits = if shift + width <= <u64 as Packable>::WIDTH {
565        low >> shift
566    } else {
567        let high = word_at(input, ((word + 1) * lanes + lane) * size_of::<u64>());
568        (low >> shift) | (high << (<u64 as Packable>::WIDTH - shift))
569    };
570    Ok(bits & low_mask(width))
571}
572
573/// How many bytes [`pack_tail`] writes for `count` values at `width` bits.
574#[must_use]
575pub fn tail_len(count: usize, width: usize) -> usize {
576    (count * width).div_ceil(8)
577}
578
579/// Packs fewer than [`VALUES`] values, sequentially and to a byte boundary.
580///
581/// The transposed layout is all or nothing. A value lives at a row and a lane, the lanes are
582/// interleaved through the whole buffer, and there is no prefix of a packed unit that holds a
583/// prefix of the values. So a unit holding 3 values costs the same as a unit holding 1024, which is
584/// 5 KB to store three numbers, and every nested array in a cascade is short: a dictionary of five
585/// entries, a run length array, an exception list.
586///
587/// This is the other layout for exactly those. It is the obvious sequential one, value 0 in the low
588/// bits, and it has the dependency chain the transposed layout was chosen to avoid. That is
589/// affordable here and only here: a tail is at most 1023 values and is decoded once, so the chain
590/// is bounded by a number that does not grow with the data, while a full unit is on the hot path of
591/// every scan in the system.
592///
593/// # Errors
594///
595/// If `count` is not below [`VALUES`], if `width` exceeds 64, or if a value does not fit.
596pub fn pack_tail(values: &[u64], width: usize, output: &mut Vec<u8>) -> Result<()> {
597    check_tail(values.len(), width)?;
598    pack_linear(values, width, output)
599}
600
601/// Packs any number of values in the layout [`pack_tail`] writes.
602///
603/// [`pack_tail`] is this with a bound, and the bound is a statement about columns rather than about
604/// the layout: a column that has a whole unit of values has a transposed unit to put them in, so
605/// the sequential layout is for the remainder and asking for it with a full unit in hand is a bug.
606///
607/// A key map is the other kind of caller. It is not a column, it is never decoded as a run, and
608/// every read of it is a single [`tail_at`] out of the middle of a binary search, so the transposed
609/// layout would buy it nothing and the bound would cost it the form: the sorted key map over
610/// fifteen million `orders` rows is fifteen million values in one array addressed by index. The
611/// writer's carry chain is still here and is still serial, and that is a build time cost paid once
612/// over a column that is being sorted anyway.
613///
614/// # Errors
615///
616/// If `width` exceeds 64, or if a value does not fit in `width` bits.
617pub fn pack_linear(values: &[u64], width: usize, output: &mut Vec<u8>) -> Result<()> {
618    if width > 64 {
619        return Err(Error::internal(format!("{width} bits does not fit in 64")));
620    }
621    if width == 0 {
622        return check_all_zero(values);
623    }
624    let mask = low_mask(width);
625    // 128 bits, because the accumulator holds up to 7 bits left over from the previous value plus a
626    // whole 64 bit one.
627    let mut accumulator: u128 = 0;
628    let mut filled = 0usize;
629    for value in values {
630        if value & !mask != 0 {
631            return Err(Error::internal(format!("value {value} does not fit in {width} bits")));
632        }
633        accumulator |= u128::from(*value) << filled;
634        filled += width;
635        while filled >= 8 {
636            output.push((accumulator & 0xff) as u8);
637            accumulator >>= 8;
638            filled -= 8;
639        }
640    }
641    if filled > 0 {
642        output.push((accumulator & 0xff) as u8);
643    }
644    Ok(())
645}
646
647/// Unpacks what [`pack_tail`] wrote.
648///
649/// The writer has a dependency chain because it has to know how many bits are left over from the
650/// value before, but the reader does not, and this does not carry one. Value `index` occupies the
651/// `width` bits starting at bit `index * width`, so its position is arithmetic rather than history,
652/// and since it begins at most seven bits into a byte and runs at most sixty four, it always lies
653/// inside sixteen bytes read from that byte. One unaligned load, one shift and one mask.
654///
655/// That matters more than the module documentation lets on. The argument there is that a tail is at
656/// most 1023 values and so is bounded by a number that does not grow with the data, which is true
657/// per call and misleading in aggregate, because a cascade puts a short array in every chunk and a
658/// scan reads every chunk. ClickBench 9 is where it showed. UserID is nearly unique, so its
659/// dictionary holds about a thousand sixty four bit values per part and lands one value short of a
660/// full unit, which sends the whole column down this path: nine hundred and seventy four parts,
661/// about a million values, and the byte at a time version fed eight bytes through a `u128` for each
662/// one. That was fifty five percent of the instructions of a scan of that column on its own.
663///
664/// # Errors
665///
666/// If `count` is not below [`VALUES`], if `width` exceeds 64, or if the input is shorter than
667/// [`tail_len`].
668pub fn unpack_tail(input: &[u8], width: usize, count: usize) -> Result<Vec<u64>> {
669    // Ahead of the buffer, so that a count off a corrupt file is refused rather than allocated for.
670    check_tail(count, width)?;
671    let mut values = vec![0u64; count];
672    unpack_tail_into(input, width, &mut values, |bits| bits)?;
673    Ok(values)
674}
675
676/// As [`unpack_tail`], into a buffer the caller owns and through a mapping on the way out.
677///
678/// How many values to read is `output.len()`. This is the form the decoders want and
679/// [`unpack_tail`] is now a wrapper over it, because a cascade calls this once per chunk and a scan
680/// reads a chunk per part per column: returning a fresh `Vec` is an allocation per chunk, and
681/// handing back raw offsets for the caller to add a base to in a second pass is a second write of
682/// every value. Both of those are per value costs wearing the clothes of a per call one. See
683/// [`unpack_mapped`] for why the mapping goes at the store.
684///
685/// # Errors
686///
687/// As [`unpack_tail`].
688pub fn unpack_tail_into<U: Copy>(
689    input: &[u8],
690    width: usize,
691    output: &mut [U],
692    value: impl Fn(u64) -> U,
693) -> Result<()> {
694    let count = output.len();
695    check_tail(count, width)?;
696    if width == 0 {
697        output.fill(value(0));
698        return Ok(());
699    }
700    if input.len() < tail_len(count, width) {
701        return Err(Error::internal(format!(
702            "{count} values at {width} bits need {} bytes and there are {}",
703            tail_len(count, width),
704            input.len()
705        )));
706    }
707    let mask = u128::from(low_mask(width));
708    let read = |window: u128, bit: usize| ((window >> bit) & mask) as u64;
709    // A buffer shorter than a window is one load for the whole call, because everything it holds is
710    // inside it. Short arrays are most of what a cascade stores, so this is the common case by
711    // count of calls even though it is the rare one by count of values.
712    if input.len() < WINDOW {
713        let mut window = [0u8; WINDOW];
714        window[..input.len()].copy_from_slice(input);
715        let word = u128::from_le_bytes(window);
716        for (index, slot) in output.iter_mut().enumerate() {
717            *slot = value(read(word, index * width));
718        }
719        return Ok(());
720    }
721    // Otherwise a value is read where it lies, until the window would run off the end.
722    let whole = (((input.len() - WINDOW) * 8) / width + 1).min(count);
723    if width <= NARROW {
724        // Half the window, because a value this wide that starts at most seven bits into a byte
725        // ends inside the eight bytes from that byte. The shift and the mask are then one
726        // instruction each where a 128 bit shift is three, and every real width is down here: the
727        // offsets a text block carries are seventeen bits and a dictionary code is fewer.
728        let mask = low_mask(width);
729        for (index, slot) in output[..whole].iter_mut().enumerate() {
730            let bit = index * width;
731            let word = word_at(input, bit / 8);
732            *slot = value((word >> (bit % 8)) & mask);
733        }
734    } else {
735        for (index, slot) in output[..whole].iter_mut().enumerate() {
736            let bit = index * width;
737            let mut window = [0u8; WINDOW];
738            window.copy_from_slice(&input[bit / 8..bit / 8 + WINDOW]);
739            *slot = value(read(u128::from_le_bytes(window), bit % 8));
740        }
741    }
742    if whole < count {
743        // Every value left over begins past the sixteenth byte from the end, by the definition of
744        // `whole` just above, and the buffer stops on the byte holding the top bits of the last
745        // one. So all of them lie inside the final window and one load serves the lot.
746        let base = input.len() - WINDOW;
747        let mut window = [0u8; WINDOW];
748        window.copy_from_slice(&input[base..]);
749        let word = u128::from_le_bytes(window);
750        for (offset, slot) in output[whole..].iter_mut().enumerate() {
751            *slot = value(read(word, (whole + offset) * width - base * 8));
752        }
753    }
754    Ok(())
755}
756
757/// One value of a run written by [`pack_tail`], read where it lies.
758///
759/// [`unpack_tail`] decodes the whole run, which is what a scan wants and what nearly every caller
760/// here is. A binary search is the other kind of caller: it wants one value out of the middle of a
761/// block, it makes about as many probes as the block has bits, and decoding the block to answer one
762/// of them would cost more than reading the value it was avoiding.
763///
764/// # Errors
765///
766/// If `width` exceeds 64, or if the value would run past the end of `input`.
767#[inline]
768pub fn tail_at(input: &[u8], width: usize, index: usize) -> Result<u64> {
769    if width > 64 {
770        return Err(Error::internal(format!("a width of {width} is past what a u64 holds")));
771    }
772    if width == 0 {
773        return Ok(0);
774    }
775    let start = index * width;
776    let end = start + width;
777    if end.div_ceil(8) > input.len() {
778        return Err(Error::internal(format!(
779            "value {index} at {width} bits ends past the {} bytes there are",
780            input.len()
781        )));
782    }
783    let first = start / 8;
784    let last = (end - 1) / 8;
785    // A value that ends inside the eight bytes it starts in is one load, one shift and one mask.
786    // The window below copies a length the compiler does not know, which is a call to `memcpy`
787    // rather than a load, and this reads one value at a time for every string a text column hands
788    // out. It was fifteen percent of ClickBench 27.
789    if first + 8 <= input.len() && last - first < 8 {
790        return Ok((word_at(input, first) >> (start % 8)) & low_mask(width));
791    }
792    let mut window = [0u8; WINDOW];
793    window[..=last - first].copy_from_slice(&input[first..=last]);
794    let word = u128::from_le_bytes(window);
795    Ok(((word >> (start % 8)) & u128::from(low_mask(width))) as u64)
796}
797
798/// Two neighbouring values of a run, read from one load where the pair fits inside it.
799///
800/// `index` is the later of the two and the answer is the pair at `index - 1` and `index`. A text
801/// column asks for exactly this once per string it hands out, because a value starts where the one
802/// before it ended. Two calls to [`tail_at`] read the same eight bytes twice and do the bounds
803/// arithmetic twice, where a pair of seventeen bit offsets, which is what a block of text carries,
804/// both lie inside one load.
805///
806/// # Errors
807///
808/// If `index` is zero, if `width` exceeds 64, or if the pair would run past the end of `input`.
809#[inline]
810pub fn tail_pair(input: &[u8], width: usize, index: usize) -> Result<(u64, u64)> {
811    let Some(before) = index.checked_sub(1) else {
812        return Err(Error::internal("a tail pair has nothing before its first value"));
813    };
814    if width == 0 {
815        return Ok((0, 0));
816    }
817    let start = before * width;
818    let shift = start % 8;
819    let first = start / 8;
820    if shift + 2 * width <= u64::BITS as usize && first + 8 <= input.len() {
821        let word = word_at(input, first) >> shift;
822        let mask = low_mask(width);
823        return Ok((word & mask, (word >> width) & mask));
824    }
825    Ok((tail_at(input, width, before)?, tail_at(input, width, index)?))
826}
827
828/// The bytes a single tail value can span, which is a shift of at most seven plus a width of at
829/// most sixty four, so seventy one bits and therefore nine bytes, rounded up to the load that
830/// covers it.
831const WINDOW: usize = 16;
832
833/// The widest value that always ends inside the eight bytes it starts in, which is sixty four bits
834/// less the seven a value can begin into its first byte.
835const NARROW: usize = 57;
836
837/// Eight bytes read where they lie, as one load.
838///
839/// The length is a constant the compiler can see, which is what makes it a load. The caller is
840/// responsible for `at + 8` being inside `input`, and the index below says so where it is not.
841#[inline]
842fn word_at(input: &[u8], at: usize) -> u64 {
843    let run: [u8; 8] = input[at..at + 8].try_into().expect("eight bytes");
844    u64::from_le_bytes(run)
845}
846
847fn check_tail(count: usize, width: usize) -> Result<()> {
848    if count >= VALUES {
849        return Err(Error::internal(format!(
850            "{count} values is a whole unit and belongs in the transposed layout"
851        )));
852    }
853    if width > 64 {
854        return Err(Error::internal(format!("{width} bits does not fit in 64")));
855    }
856    Ok(())
857}
858
859fn check_vector_len(len: usize, what: &str) -> Result<()> {
860    if len == VALUES {
861        Ok(())
862    } else {
863        Err(Error::internal(format!("{what} is {len} values, and a packed unit is {VALUES}")))
864    }
865}
866
867fn check_width<T: Packable>(width: usize) -> Result<()> {
868    if width <= T::WIDTH {
869        Ok(())
870    } else {
871        Err(Error::internal(format!("{width} bits does not fit in a {} bit type", T::WIDTH)))
872    }
873}
874
875fn check_all_zero<T: Packable>(input: &[T]) -> Result<()> {
876    match input.iter().position(|value| value.to_u64() != 0) {
877        None => Ok(()),
878        Some(index) => Err(Error::internal(format!(
879            "a zero bit vector cannot hold {:?} at {index}",
880            input[index]
881        ))),
882    }
883}
884
885#[cfg(test)]
886mod tests {
887    use super::*;
888
889    /// A xorshift, so that the test data is the same on every host and in every run without the
890    /// workspace growing a dependency for it.
891    struct Random(u64);
892
893    impl Random {
894        fn new() -> Self {
895            Self(0x2545_f491_4f6c_dd1d)
896        }
897
898        fn next(&mut self) -> u64 {
899            self.0 ^= self.0 << 13;
900            self.0 ^= self.0 >> 7;
901            self.0 ^= self.0 << 17;
902            self.0
903        }
904    }
905
906    fn sample<T: Packable>(width: usize) -> Vec<T> {
907        let mut random = Random::new();
908        (0..VALUES).map(|_| T::from_u64(random.next() & low_mask(width))).collect()
909    }
910
911    fn round_trip<T: Packable>(width: usize) {
912        let values = sample::<T>(width);
913        let mut packed = vec![T::from_u64(0); packed_len::<T>(width)];
914        pack(&values, width, &mut packed).unwrap();
915        let mut back = vec![T::from_u64(0); VALUES];
916        unpack(&packed, width, &mut back).unwrap();
917        assert_eq!(back, values, "{width} bits of a {} bit type", T::WIDTH);
918    }
919
920    #[test]
921    fn every_width_of_every_type_round_trips() {
922        for width in 0..=8 {
923            round_trip::<u8>(width);
924        }
925        for width in 0..=16 {
926            round_trip::<u16>(width);
927        }
928        for width in 0..=32 {
929            round_trip::<u32>(width);
930        }
931        for width in 0..=64 {
932            round_trip::<u64>(width);
933        }
934    }
935
936    #[test]
937    fn one_value_from_a_full_u64_unit_agrees_with_a_whole_unpack() {
938        for width in 0..=64 {
939            let values = sample::<u64>(width);
940            let mut packed = vec![0u64; packed_len::<u64>(width)];
941            pack(&values, width, &mut packed).unwrap();
942            let bytes = packed.iter().flat_map(|word| word.to_le_bytes()).collect::<Vec<_>>();
943            for (index, expected) in values.iter().enumerate() {
944                assert_eq!(
945                    unpack_u64_at(&bytes, width, index).unwrap(),
946                    *expected,
947                    "value {index} at {width} bits"
948                );
949            }
950        }
951    }
952
953    #[test]
954    fn a_unit_unpacked_from_bytes_gives_what_one_unpacked_from_words_gives() {
955        // Two spellings of the same loop, one reading aligned words and one reading them where the
956        // chunk left them, so every width is checked against the other rather than against a table.
957        // The mapping is not the identity, because the caller this exists for is frame of reference
958        // coding and a base that lands in the answer is the way a shift applied to the wrong word
959        // would show up.
960        for width in 0..=64 {
961            let values = sample::<u64>(width);
962            let mut packed = vec![0u64; packed_len::<u64>(width)];
963            pack(&values, width, &mut packed).unwrap();
964            let bytes = packed.iter().flat_map(|word| word.to_le_bytes()).collect::<Vec<_>>();
965            assert_eq!(bytes.len(), unit_len(width), "at {width} bits");
966            let map = |offset: u64| offset.wrapping_add(0x1234_5678) as i64;
967            let mut from_words = vec![0i64; VALUES];
968            unpack_mapped(&packed, width, &mut from_words, map).unwrap();
969            let mut from_bytes = vec![0i64; VALUES];
970            unpack_unit_into(&bytes, width, &mut from_bytes, map).unwrap();
971            assert_eq!(from_bytes, from_words, "at {width} bits");
972            // And the same again with the bytes handed over at an odd offset, which is where a chunk
973            // puts them and which is the whole reason this form reads them a word at a time.
974            let mut moved = vec![0u8; bytes.len() + 3];
975            moved[3..].copy_from_slice(&bytes);
976            let mut from_moved = vec![0i64; VALUES];
977            unpack_unit_into(&moved[3..], width, &mut from_moved, map).unwrap();
978            assert_eq!(from_moved, from_words, "at {width} bits, three bytes along");
979        }
980    }
981
982    #[test]
983    fn a_unit_of_the_wrong_length_is_refused() {
984        let mut out = vec![0i64; VALUES];
985        let bytes = vec![0u8; unit_len(9) - 1];
986        assert!(unpack_unit_into(&bytes, 9, &mut out, |bits| bits as i64).is_err());
987        let bytes = vec![0u8; unit_len(9) + 1];
988        assert!(unpack_unit_into(&bytes, 9, &mut out, |bits| bits as i64).is_err());
989        let bytes = vec![0u8; unit_len(65)];
990        assert!(unpack_unit_into(&bytes, 65, &mut out, |bits| bits as i64).is_err());
991        let bytes = vec![0u8; unit_len(9)];
992        assert!(unpack_unit_into(&bytes, 9, &mut out[..VALUES - 1], |bits| bits as i64).is_err());
993    }
994
995    #[test]
996    fn a_reused_scratch_gives_what_a_fresh_one_gives() {
997        // The buffer a unit transposes through is handed in so it is not zeroed per call, which is
998        // only sound if every element of it is written every time. If some were not, a narrow unit
999        // following a wide one would read whatever the wide one left behind, so the widths here go
1000        // up and down rather than in order and each answer is checked against the same unit packed
1001        // through a buffer nothing has touched.
1002        let mut scratch = Scratch::<u64>::new();
1003        for width in [64, 1, 33, 7, 64, 0, 17, 60, 3] {
1004            let values = sample::<u64>(width);
1005            let mut reused = vec![0u64; packed_len::<u64>(width)];
1006            pack_with(&values, width, &mut reused, &mut scratch).unwrap();
1007            let mut fresh = vec![0u64; packed_len::<u64>(width)];
1008            pack(&values, width, &mut fresh).unwrap();
1009            assert_eq!(reused, fresh, "at {width} bits after a wider unit");
1010            let mut back = vec![0u64; VALUES];
1011            unpack(&reused, width, &mut back).unwrap();
1012            assert_eq!(back, values, "at {width} bits");
1013        }
1014    }
1015
1016    #[test]
1017    fn the_one_pass_unpack_gives_what_the_two_passes_give() {
1018        // `unpack` is the fused form of `unpack_transposed` followed by `untranspose`, and those two
1019        // are the definition of the layout. So this checks the fast one against the slow one at
1020        // every width of every type rather than against a remembered answer, which is the check that
1021        // would catch the fused one getting a shift or a row base wrong at one width out of sixty
1022        // five.
1023        fn agree<T: Packable>() {
1024            for width in 0..=T::WIDTH {
1025                let values = sample::<T>(width);
1026                let mut transposed = vec![T::from_u64(0); VALUES];
1027                transpose(&values, &mut transposed).unwrap();
1028                let mut packed = vec![T::from_u64(0); packed_len::<T>(width)];
1029                pack_transposed(&transposed, width, &mut packed).unwrap();
1030
1031                let mut middle = vec![T::from_u64(0); VALUES];
1032                unpack_transposed(&packed, width, &mut middle).unwrap();
1033                let mut slow = vec![T::from_u64(0); VALUES];
1034                untranspose(&middle, &mut slow).unwrap();
1035
1036                let mut fast = vec![T::from_u64(0); VALUES];
1037                unpack(&packed, width, &mut fast).unwrap();
1038
1039                assert_eq!(fast, slow, "{} bit type at {width} bits", T::WIDTH);
1040                assert_eq!(fast, values, "{} bit type at {width} bits round trip", T::WIDTH);
1041            }
1042        }
1043        agree::<u8>();
1044        agree::<u16>();
1045        agree::<u32>();
1046        agree::<u64>();
1047    }
1048
1049    #[test]
1050    fn a_mapped_unpack_gives_what_unpacking_and_then_mapping_gives() {
1051        // The frame of reference decode is the caller, so the mapping under test is the one it
1052        // uses: a signed base added to an unsigned offset, into an output of a different type from
1053        // the packed words. Checked at every width because the two inner loops of `unpack_mapped`
1054        // split on whether a value straddles two words, and which one runs depends on the width.
1055        for width in 0..=64 {
1056            let values = sample::<u64>(width);
1057            let mut packed = vec![0u64; packed_len::<u64>(width)];
1058            pack(&values, width, &mut packed).unwrap();
1059
1060            let base = -7i64;
1061            let mut mapped = vec![0i64; VALUES];
1062            unpack_mapped(&packed, width, &mut mapped, |offset| {
1063                (i128::from(base) + i128::from(offset)) as i64
1064            })
1065            .unwrap();
1066
1067            let mut plain = vec![0u64; VALUES];
1068            unpack(&packed, width, &mut plain).unwrap();
1069            let expected: Vec<i64> = plain
1070                .iter()
1071                .map(|offset| (i128::from(base) + i128::from(*offset)) as i64)
1072                .collect();
1073            assert_eq!(mapped, expected, "at {width} bits");
1074        }
1075    }
1076
1077    #[test]
1078    fn a_mapped_tail_gives_what_unpacking_the_tail_and_then_mapping_gives() {
1079        // Every length, because `unpack_tail_into` has three paths through it and which one a call
1080        // takes depends on how many bytes the run came to: everything inside one window, a walk
1081        // that stops a window short of the end, and the leftovers after that walk.
1082        for width in [0usize, 1, 7, 17, 32, 57, 58, 64] {
1083            for count in [1usize, 2, 63, 64, 300, 1023] {
1084                let values: Vec<u64> = sample::<u64>(width).into_iter().take(count).collect();
1085                let mut packed = Vec::new();
1086                pack_tail(&values, width, &mut packed).unwrap();
1087
1088                let base = 11i64;
1089                let mut mapped = vec![0i64; count];
1090                unpack_tail_into(&packed, width, &mut mapped, |offset| {
1091                    (i128::from(base) + i128::from(offset)) as i64
1092                })
1093                .unwrap();
1094
1095                let plain = unpack_tail(&packed, width, count).unwrap();
1096                let expected: Vec<i64> = plain
1097                    .iter()
1098                    .map(|offset| (i128::from(base) + i128::from(*offset)) as i64)
1099                    .collect();
1100                assert_eq!(mapped, expected, "{count} values at {width} bits");
1101            }
1102        }
1103    }
1104
1105    #[test]
1106    fn the_transposed_form_also_round_trips_without_being_reordered() {
1107        // What the engine actually does: transpose once, then pack and unpack any number of times
1108        // without ever going back to row order.
1109        let values = sample::<u32>(19);
1110        let mut transposed = vec![0u32; VALUES];
1111        transpose(&values, &mut transposed).unwrap();
1112        let mut packed = vec![0u32; packed_len::<u32>(19)];
1113        pack_transposed(&transposed, 19, &mut packed).unwrap();
1114        let mut back = vec![0u32; VALUES];
1115        unpack_transposed(&packed, 19, &mut back).unwrap();
1116        assert_eq!(back, transposed);
1117    }
1118
1119    #[test]
1120    fn the_permutation_is_a_bijection() {
1121        // Every value has to land somewhere and no two may land in the same place, or a round trip
1122        // would silently drop rows. Checked for all four widths because the group size changes.
1123        fn check<T: Packable>() {
1124            let mut seen = vec![false; VALUES];
1125            for row in 0..T::WIDTH {
1126                for lane in 0..T::LANES {
1127                    let index = source_index::<T>(row, lane);
1128                    assert!(!seen[index], "{index} is written twice for {} bits", T::WIDTH);
1129                    seen[index] = true;
1130                }
1131            }
1132            assert!(seen.into_iter().all(|hit| hit));
1133        }
1134        check::<u8>();
1135        check::<u16>();
1136        check::<u32>();
1137        check::<u64>();
1138    }
1139
1140    #[test]
1141    fn transposing_is_not_the_identity() {
1142        // If it were, the test above would be passing on a layout that is not the FastLanes one.
1143        let values: Vec<u32> = (0..VALUES).map(|index| index as u32).collect();
1144        let mut transposed = vec![0u32; VALUES];
1145        transpose(&values, &mut transposed).unwrap();
1146        assert_ne!(transposed, values);
1147        let mut back = vec![0u32; VALUES];
1148        untranspose(&transposed, &mut back).unwrap();
1149        assert_eq!(back, values);
1150    }
1151
1152    #[test]
1153    fn a_full_width_pack_is_the_data_itself() {
1154        // 64 bits of a 64 bit type has no packing to do, and the loop that handles the general case
1155        // has to get the degenerate one right rather than shifting by 64 and wrapping.
1156        let values = sample::<u64>(64);
1157        let mut transposed = vec![0u64; VALUES];
1158        transpose(&values, &mut transposed).unwrap();
1159        let mut packed = vec![0u64; packed_len::<u64>(64)];
1160        pack_transposed(&transposed, 64, &mut packed).unwrap();
1161        assert_eq!(packed, transposed);
1162    }
1163
1164    #[test]
1165    fn a_zero_width_vector_stores_nothing_and_reads_back_as_zeros() {
1166        let values = vec![0u32; VALUES];
1167        assert_eq!(required_width(&values), 0);
1168        let mut packed = Vec::new();
1169        pack(&values, 0, &mut packed).unwrap();
1170        let mut back = vec![7u32; VALUES];
1171        unpack(&packed, 0, &mut back).unwrap();
1172        assert_eq!(back, values);
1173    }
1174
1175    #[test]
1176    fn required_width_is_the_bits_of_the_largest_value() {
1177        assert_eq!(required_width::<u32>(&[]), 0);
1178        assert_eq!(required_width::<u32>(&[0, 0]), 0);
1179        assert_eq!(required_width::<u32>(&[1]), 1);
1180        assert_eq!(required_width::<u32>(&[255, 3]), 8);
1181        assert_eq!(required_width::<u32>(&[256]), 9);
1182        assert_eq!(required_width::<u64>(&[u64::MAX]), 64);
1183    }
1184
1185    #[test]
1186    fn a_value_too_wide_for_the_width_is_an_error_rather_than_silent_truncation() {
1187        let mut values = vec![0u32; VALUES];
1188        values[500] = 8;
1189        let mut transposed = vec![0u32; VALUES];
1190        transpose(&values, &mut transposed).unwrap();
1191        let mut packed = vec![0u32; packed_len::<u32>(3)];
1192        let error = pack_transposed(&transposed, 3, &mut packed).unwrap_err();
1193        assert!(error.message().contains("does not fit in 3 bits"), "{error}");
1194    }
1195
1196    #[test]
1197    fn a_wrong_sized_buffer_is_an_error() {
1198        let values = vec![0u32; VALUES];
1199        let mut packed = vec![0u32; 3];
1200        let error = pack(&values, 5, &mut packed).unwrap_err();
1201        assert!(error.message().contains("words"), "{error}");
1202
1203        let short = vec![0u32; 7];
1204        let mut output = vec![0u32; VALUES];
1205        let error = unpack(&short, 5, &mut output).unwrap_err();
1206        assert!(error.message().contains("words"), "{error}");
1207    }
1208
1209    #[test]
1210    fn a_nonzero_value_at_zero_width_is_an_error() {
1211        let mut values = vec![0u32; VALUES];
1212        values[9] = 1;
1213        let mut packed = Vec::new();
1214        let error = pack(&values, 0, &mut packed).unwrap_err();
1215        assert!(error.message().contains("zero bit vector"), "{error}");
1216    }
1217
1218    #[test]
1219    fn packing_at_a_width_the_type_cannot_hold_is_an_error() {
1220        let values = vec![0u16; VALUES];
1221        let mut packed = vec![0u16; 17 * 64];
1222        let error = pack(&values, 17, &mut packed).unwrap_err();
1223        assert!(error.message().contains("16 bit type"), "{error}");
1224    }
1225
1226    #[test]
1227    fn a_tail_round_trips_at_every_width_and_every_length() {
1228        let mut random = Random::new();
1229        for width in 0..=64usize {
1230            for count in [0usize, 1, 2, 7, 8, 9, 100, 1023] {
1231                let values: Vec<u64> =
1232                    (0..count).map(|_| random.next() & low_mask(width)).collect();
1233                let mut bytes = Vec::new();
1234                pack_tail(&values, width, &mut bytes).unwrap();
1235                assert_eq!(bytes.len(), tail_len(count, width), "{count} at {width}");
1236                assert_eq!(
1237                    unpack_tail(&bytes, width, count).unwrap(),
1238                    values,
1239                    "{count} at {width}"
1240                );
1241            }
1242        }
1243    }
1244
1245    /// Reading one value where it lies agrees with decoding the whole run.
1246    ///
1247    /// Every width and every position, since the point of it is the arithmetic that finds the bytes
1248    /// a value straddles, and that is what is off by one.
1249    ///
1250    /// A value that runs off the buffer is an error. The buffer stops on a byte boundary and a value
1251    /// does not, so an index a little past the count can still lie inside the padding of the last
1252    /// byte and that reads rather than complains. It is the caller that knows how many values it
1253    /// wrote, the same way it does for `unpack_tail`.
1254    #[test]
1255    fn one_value_of_a_tail_reads_the_same_as_the_whole_of_it() {
1256        let mut random = Random::new();
1257        for width in 0..=64usize {
1258            let count = 37;
1259            let values: Vec<u64> = (0..count).map(|_| random.next() & low_mask(width)).collect();
1260            let mut bytes = Vec::new();
1261            pack_tail(&values, width, &mut bytes).unwrap();
1262            for (index, value) in values.iter().enumerate() {
1263                assert_eq!(tail_at(&bytes, width, index).unwrap(), *value, "{index} at {width}");
1264            }
1265            let Some(fits) = (bytes.len() * 8).checked_div(width) else { continue };
1266            assert!(tail_at(&bytes, width, fits + 1).is_err(), "past the end at {width}");
1267        }
1268    }
1269
1270    /// The two halves of the reader agree with each other.
1271    ///
1272    /// A value is read with one sixteen byte load, which the values near the end of the buffer
1273    /// cannot have because the buffer stops on the byte holding the top bits of the last one. Those
1274    /// go through a zero padded copy instead, and the split between the two is arithmetic on
1275    /// lengths, which is the kind of thing that is off by one. Handing the same bytes to the reader
1276    /// twice, once exactly sized so the last values take the padded path and once with slack on the
1277    /// end so every value takes the fast one, makes the two paths check each other at every width.
1278    #[test]
1279    fn the_padded_end_of_a_tail_reads_the_same_as_the_windowed_start() {
1280        let mut random = Random::new();
1281        for width in 1..=64usize {
1282            for count in [1usize, 2, 3, 17, 129, 1023] {
1283                let values: Vec<u64> =
1284                    (0..count).map(|_| random.next() & low_mask(width)).collect();
1285                let mut exact = Vec::new();
1286                pack_tail(&values, width, &mut exact).unwrap();
1287                let mut slack = exact.clone();
1288                slack.extend_from_slice(&[0u8; WINDOW]);
1289                assert_eq!(
1290                    unpack_tail(&exact, width, count).unwrap(),
1291                    values,
1292                    "{count} at {width}"
1293                );
1294                assert_eq!(
1295                    unpack_tail(&slack, width, count).unwrap(),
1296                    values,
1297                    "{count} at {width}"
1298                );
1299            }
1300        }
1301    }
1302
1303    /// The pair read agrees with two single reads, at every width and every position.
1304    ///
1305    /// The pair has its own arithmetic for the case where both values fit one load, so the thing to
1306    /// check is that it falls back to the same answer everywhere that does not hold, which is every
1307    /// width past thirty two and every value near the end of the buffer.
1308    #[test]
1309    fn a_pair_of_tail_values_reads_the_same_as_the_two_of_them_apart() {
1310        let mut random = Random::new();
1311        for width in 0..=64usize {
1312            let count = 37;
1313            let values: Vec<u64> = (0..count).map(|_| random.next() & low_mask(width)).collect();
1314            let mut bytes = Vec::new();
1315            pack_tail(&values, width, &mut bytes).unwrap();
1316            for index in 1..count {
1317                assert_eq!(
1318                    tail_pair(&bytes, width, index).unwrap(),
1319                    (values[index - 1], values[index]),
1320                    "{index} at {width}"
1321                );
1322            }
1323            assert!(tail_pair(&bytes, width, 0).is_err(), "nothing before the first at {width}");
1324        }
1325    }
1326
1327    #[test]
1328    fn a_tail_costs_its_own_values_and_not_a_whole_unit() {
1329        // The reason it exists. Three 40 bit values in the transposed layout is a 5 KB buffer.
1330        let values = vec![(1u64 << 39) + 1; 3];
1331        let mut bytes = Vec::new();
1332        pack_tail(&values, 40, &mut bytes).unwrap();
1333        assert_eq!(bytes.len(), 15);
1334        assert_eq!(packed_len::<u64>(40) * 8, 5120);
1335    }
1336
1337    #[test]
1338    fn a_whole_unit_is_refused_by_the_tail_packer() {
1339        let values = vec![0u64; VALUES];
1340        let error = pack_tail(&values, 4, &mut Vec::new()).unwrap_err();
1341        assert!(error.message().contains("whole unit"), "{error}");
1342    }
1343
1344    #[test]
1345    fn a_short_tail_buffer_is_an_error() {
1346        let error = unpack_tail(&[0, 0], 8, 5).unwrap_err();
1347        assert!(error.message().contains("need 5 bytes"), "{error}");
1348    }
1349
1350    #[test]
1351    fn the_packed_size_is_the_same_as_the_naive_layout() {
1352        for width in 0..=32 {
1353            assert_eq!(packed_len::<u32>(width) * 32, width * VALUES);
1354        }
1355    }
1356}