Skip to main content

diskann_quantization/bits/
slice.rs

1/*
2 * Copyright (c) Microsoft Corporation.
3 * Licensed under the MIT license.
4 */
5
6use std::{marker::PhantomData, ops::RangeInclusive, ptr::NonNull};
7
8use diskann_utils::{Reborrow, ReborrowMut};
9use thiserror::Error;
10
11use super::{
12    length::{Dynamic, Length},
13    packing,
14    ptr::{AsMutPtr, AsPtr, MutSlicePtr, Precursor, SlicePtr},
15};
16use crate::{
17    alloc::{AllocatorCore, AllocatorError, GlobalAllocator, Poly},
18    utils,
19};
20
21//////////////////////
22// Retrieval Traits //
23//////////////////////
24
25/// Representation of `NBITS` bit numbers in the associated domain.
26pub trait Representation<const NBITS: usize> {
27    /// The type of the domain accepted by this representation.
28    type Domain: Iterator<Item = i64>;
29
30    /// Encode `value` into the lower order bits of a byte. Returns the encoded value on
31    /// success, or an `EncodingError` if the value is unencodable.
32    fn encode(value: i64) -> Result<u8, EncodingError>;
33
34    /// Encode `value` into the lower order bits of a byte without checking if `value`
35    /// is encodable. This function is not marked as unsafe because in-and-of itself, it
36    /// won't cause memory safety issues.
37    ///
38    /// This may panic in debug mode when `value` is outside of this representation's
39    /// domain.
40    fn encode_unchecked(value: i64) -> u8;
41
42    /// Decode a previously encoded value. The result will be in the range
43    /// `[Self::MIN, Self::MAX]`.
44    ///
45    /// # Panics
46    ///
47    /// May panic in debug builds if `raw` is not a valid pattern emitted by `encode`.
48    fn decode(raw: u8) -> i64;
49
50    /// Check whether or not the argument is in the domain.
51    fn check(value: i64) -> bool;
52
53    /// Return an iterator over the domain of representable values.
54    fn domain() -> Self::Domain;
55}
56
57#[derive(Debug, Error, Clone, Copy)]
58#[error("value {} is not in the encodable range of {}", got, domain)]
59pub struct EncodingError {
60    got: i64,
61    // Question: Why is this a ref-ref??
62    //
63    // Answer: I have a personal vendetta to keep this struct within 16-bytes with a
64    // niche-optimization. A `&'static src` is 16 bytes in and of itself. But a
65    // `&'static &'static str`, now *that's* just 8 bytes.
66    domain: &'static &'static str,
67}
68
69impl EncodingError {
70    fn new(got: i64, domain: &'static &'static str) -> Self {
71        Self { got, domain }
72    }
73}
74
75//////////////
76// Unsigned //
77//////////////
78
79/// Storage unsigned integers in slices.
80///
81/// For a bit count of `NBITS`, the `Unsigned` type can store unsigned integers in
82/// the range `[0, 2^NBITS - 1]`.
83#[derive(Debug, Clone, Copy)]
84pub struct Unsigned;
85
86impl Unsigned {
87    /// Return the dynamic range of an `Unsigned` encoding for `NBITS`.
88    pub const fn domain_const<const NBITS: usize>() -> std::ops::RangeInclusive<i64> {
89        0..=2i64.pow(NBITS as u32) - 1
90    }
91
92    #[expect(clippy::panic)]
93    const fn domain_str(nbits: usize) -> &'static &'static str {
94        match nbits {
95            8 => &"[0, 255]",
96            7 => &"[0, 127]",
97            6 => &"[0, 63]",
98            5 => &"[0, 31]",
99            4 => &"[0, 15]",
100            3 => &"[0, 7]",
101            2 => &"[0, 3]",
102            1 => &"[0, 1]",
103            _ => panic!("unimplemented"),
104        }
105    }
106}
107
108macro_rules! repr_unsigned {
109    ($N:literal) => {
110        impl Representation<$N> for Unsigned {
111            type Domain = RangeInclusive<i64>;
112
113            fn encode(value: i64) -> Result<u8, EncodingError> {
114                if !<Self as Representation<$N>>::check(value) {
115                    // Even with the macro gymnastics - we still have to manually inline
116                    // this computation :(
117                    let domain = Self::domain_str($N);
118                    Err(EncodingError::new(value, domain))
119                } else {
120                    Ok(<Self as Representation<$N>>::encode_unchecked(value))
121                }
122            }
123
124            fn encode_unchecked(value: i64) -> u8 {
125                debug_assert!(<Self as Representation<$N>>::check(value));
126                value as u8
127            }
128
129            fn decode(raw: u8) -> i64 {
130                // Feed through the value un-modified.
131                let raw: i64 = raw.into();
132                debug_assert!(<Self as Representation<$N>>::check(raw));
133                raw
134            }
135
136            fn check(value: i64) -> bool {
137                <Self as Representation<$N>>::domain().contains(&value)
138            }
139
140            fn domain() -> Self::Domain {
141                Self::domain_const::<$N>()
142            }
143        }
144    };
145    ($N:literal, $($Ns:literal),+) => {
146        repr_unsigned!($N);
147        $(repr_unsigned!($Ns);)+
148    };
149}
150
151repr_unsigned!(1, 2, 3, 4, 5, 6, 7, 8);
152
153////////////
154// Binary //
155////////////
156
157/// A 1-bit binary quantization mapping `-1` to `0` and `1` to `1`.
158#[derive(Debug, Clone, Copy)]
159pub struct Binary;
160
161impl Representation<1> for Binary {
162    type Domain = std::array::IntoIter<i64, 2>;
163
164    fn encode(value: i64) -> Result<u8, EncodingError> {
165        if !Self::check(value) {
166            const DOMAIN: &str = "{-1, 1}";
167            Err(EncodingError::new(value, &DOMAIN))
168        } else {
169            Ok(Self::encode_unchecked(value))
170        }
171    }
172
173    fn encode_unchecked(value: i64) -> u8 {
174        debug_assert!(Self::check(value));
175        // The use of `clamp` here is a quick way of sending `-1` to `0` and `1` to `1`.
176        value.clamp(0, 1) as u8
177    }
178
179    fn decode(raw: u8) -> i64 {
180        // Raw is either 0 or 1. We want to map it to -1 or 1.
181        // We can do this by multiplying by 2 and subtracting 1.
182        let raw: i64 = raw.into();
183        (raw << 1) - 1
184    }
185
186    fn check(value: i64) -> bool {
187        value == -1 || value == 1
188    }
189
190    /// Return the domain of the encoding.
191    ///
192    /// The domain is the set `{-1, 1}`.
193    fn domain() -> Self::Domain {
194        [-1, 1].into_iter()
195    }
196}
197
198////////////////////////
199// Permutation Traits //
200////////////////////////
201
202/// A enable the dimensions within a BitSlice to be permuted in an arbitrary way.
203///
204/// # Safety
205///
206/// This provides the computation for the number of bytes required to store a given number
207/// of `NBITS` bit-packed values. Improper implementation will result in out-of-bounds
208/// accesses being made.
209///
210/// The following must hold:
211///
212/// For all counts values `c`, let `b = Self::bytes(c)` be requested number of bytes for `c`
213/// for this permutation strategy and `s` be a slice of bytes with length `c`. Then, for all
214/// `i < c`, `Self::pack(s, i, _)` and `Self::unpack(s, i)` must only access `s` in-bounds.
215///
216/// This implementation must be such that unsafe code can rely on this property holding.
217pub unsafe trait PermutationStrategy<const NBITS: usize> {
218    /// Return the number of bytes required to store `count` values of with `NBITS`.
219    fn bytes(count: usize) -> usize;
220
221    /// Pack the lower `NBITS` bits of `value` into `s` at logical index `i`.
222    ///
223    /// # Safety
224    ///
225    /// This is a tricky function to call with several subtle requirements.
226    ///
227    /// * Let `s` be a slice of length `c` where `c = Self::bytes(b)` for some `b`. Then
228    ///   this function is safe to call if `i` is in `[0, b)`.
229    unsafe fn pack(s: &mut [u8], i: usize, value: u8);
230
231    /// Unpack the value stored at logical index `i` and return it as the lower `NBITS` bits
232    /// in the return value.
233    ///
234    /// # Safety
235    ///
236    /// This is a tricky function to call with several subtle requirements.
237    ///
238    /// * Let `s` be a slice of length `c` where `c = Self::bytes(b)` for some `b`. Then
239    ///   this function is safe to call if `i` is in `[0, b)`.
240    unsafe fn unpack(s: &[u8], i: usize) -> u8;
241}
242
243/// The identity permutation strategy.
244///
245/// All values are densly packed.
246#[derive(Debug, Clone, Copy)]
247pub struct Dense;
248
249impl Dense {
250    fn bytes<const NBITS: usize>(count: usize) -> usize {
251        utils::div_round_up(NBITS * count, 8)
252    }
253}
254
255/// Safety: For all `0 <= i < count`, `NBITS * i <= 8 * ceil((NBITS * count) / 8)`.
256unsafe impl<const NBITS: usize> PermutationStrategy<NBITS> for Dense {
257    fn bytes(count: usize) -> usize {
258        Self::bytes::<NBITS>(count)
259    }
260
261    unsafe fn pack(data: &mut [u8], i: usize, encoded: u8) {
262        let bitaddress = NBITS * i;
263
264        let bytestart = bitaddress / 8;
265        let bytestop = (bitaddress + NBITS - 1) / 8;
266        let bitstart = bitaddress - 8 * bytestart;
267        debug_assert!(bytestop < data.len());
268
269        if bytestart == bytestop {
270            // SAFETY: This is safe for the following:
271            // ```
272            // data.len() >= ceil(NBITS * i / 8)        from `pack`'s safety requirements.
273            //            >= floor(NBITS * i / 8)
274            //            = bytestart
275            //
276            // Since we are only reading one byte - this is in-bounds.
277            // ```
278            let raw = unsafe { data.as_ptr().add(bytestart).read() };
279            let packed = packing::pack_u8::<NBITS>(raw, encoded, bitstart);
280
281            // SAFETY: See previous argument for in-bounds access.
282            // For writing, we are the only writers in this function and we have a mutable
283            // reference to `data`.
284            unsafe { data.as_mut_ptr().add(bytestart).write(packed) };
285        } else {
286            // SAFETY: This is safe for the following reason:
287            // ```
288            // data.len() >= ceil(NBITS * i / 8)        from `pack`'s safety requirements.
289            //            = bytestop
290            //            = bytestart + 1
291            // ```
292            // Therefore, it is safe to read 2-bytes starting at `bytestart`.
293            let raw = unsafe { data.as_ptr().add(bytestart).cast::<u16>().read_unaligned() };
294            let packed = packing::pack_u16::<NBITS>(raw, encoded, bitstart);
295
296            // SAFETY: See previous argument for in-bounds access.
297            // For writing, we are the only writers in this function and we have a mutable
298            // reference to `data`.
299            unsafe {
300                data.as_mut_ptr()
301                    .add(bytestart)
302                    .cast::<u16>()
303                    .write_unaligned(packed)
304            };
305        }
306    }
307
308    unsafe fn unpack(data: &[u8], i: usize) -> u8 {
309        let bitaddress = NBITS * i;
310
311        let bytestart = bitaddress / 8;
312        let bytestop = (bitaddress + NBITS - 1) / 8;
313        debug_assert!(bytestop < data.len());
314        if bytestart == bytestop {
315            // SAFETY: See the safety argument in `pack` for in-bounds.
316            let raw = unsafe { data.as_ptr().add(bytestart).read() };
317            packing::unpack_u8::<NBITS>(raw, bitaddress - 8 * bytestart)
318        } else {
319            // SAFETY: See the safety argument in `pack` for in-bounds.
320            let raw = unsafe { data.as_ptr().add(bytestart).cast::<u16>().read_unaligned() };
321            packing::unpack_u16::<NBITS>(raw, bitaddress - 8 * bytestart)
322        }
323    }
324}
325
326/// A layout specialized for performing multi-bit operations with 1-bit scalar quantization.
327///
328/// The layout provided by this struct is as follows. Assume we are compressing `N` bit data.
329/// Then, the store the data in blocks of `64 * N` bits (where 64 comes from the native CPU
330/// word size).
331///
332/// Each block can contain 64 values, stored in `N` 64-bit words. The 0th bit of each value
333/// is stored in word 0, the 1st bit is stored in word 1, etc.
334///
335/// # Partially Filled Blocks
336///
337/// This strategy always requests data in blocks. For partially filled blocks, the lower
338/// bits in the last block will be used.
339#[derive(Debug, Clone, Copy)]
340pub struct BitTranspose;
341
342/// Safety: We ask for bytes in multiples of 32. Furthermore, the accesses to the packed
343/// data in `pack` and `unpack` use checked accesses, so out-of-bounds reads will panic.
344unsafe impl PermutationStrategy<4> for BitTranspose {
345    fn bytes(count: usize) -> usize {
346        32 * utils::div_round_up(count, 64)
347    }
348
349    unsafe fn pack(data: &mut [u8], i: usize, encoded: u8) {
350        // Compute the byte-address of the block containing `i`.
351        let block_start = 32 * (i / 64);
352        // Compute the offset within the block to find the first byte containing `i`.
353        let byte_start = block_start + (i % 64) / 8;
354        // Finally, compute the bit within the byte that we are interested in.
355        let bit = i % 8;
356
357        let mask: u8 = 0x1 << bit;
358        for p in 0..4 {
359            let mut v = data[byte_start + 8 * p];
360            v = (v & !mask) | (((encoded >> p) & 0x1) << bit);
361            data[byte_start + 8 * p] = v;
362        }
363    }
364
365    unsafe fn unpack(data: &[u8], i: usize) -> u8 {
366        // Compute the byte-address of the block containing `i`.
367        let block_start = 32 * (i / 64);
368        // Compute the offset within the block to find the first byte containing `i`.
369        let byte_start = block_start + (i % 64) / 8;
370        // Finally, compute the bit within the byte that we are interested in.
371        let bit = i % 8;
372
373        let mut output: u8 = 0;
374        for p in 0..4 {
375            let v = data[byte_start + 8 * p];
376            output |= ((v >> bit) & 0x1) << p
377        }
378        output
379    }
380}
381
382////////////
383// Errors //
384////////////
385
386#[derive(Debug, Clone, Copy, PartialEq, Eq, Error)]
387#[error("decoded output has {output} elements, expected {input}")]
388pub(crate) struct DecodeLengthMismatch {
389    input: usize,
390    output: usize,
391}
392
393#[derive(Debug, Error, Clone, Copy)]
394#[error("input span has length {got} bytes but expected {expected}")]
395pub struct ConstructionError {
396    got: usize,
397    expected: usize,
398}
399
400#[derive(Debug, Error, Clone, Copy)]
401#[error("index {index} exceeds the maximum length of {len}")]
402pub struct IndexOutOfBounds {
403    index: usize,
404    len: usize,
405}
406
407impl IndexOutOfBounds {
408    fn new(index: usize, len: usize) -> Self {
409        Self { index, len }
410    }
411}
412
413#[derive(Debug, Error, Clone, Copy)]
414#[error("error setting index in bitslice")]
415#[non_exhaustive]
416pub enum SetError {
417    IndexError(#[from] IndexOutOfBounds),
418    EncodingError(#[from] EncodingError),
419}
420
421#[derive(Debug, Error, Clone, Copy)]
422#[error("error getting index in bitslice")]
423pub enum GetError {
424    IndexError(#[from] IndexOutOfBounds),
425}
426
427//////////////
428// BitSlice //
429//////////////
430
431/// A generalized representation for packed small bit integer encodings over a contiguous
432/// span of memory.
433///
434/// Think of this as a Rust slice, but supporting integer elements with fewer than 8-bits.
435/// The borrowed representations [`BitSlice`] and [`MutBitSlice`] consist of just a pointer
436/// and a length and are therefore just 16-bytes in size and amenable to the niche
437/// optimization.
438///
439/// # Parameters
440///
441/// * `NBITS`: The number of bits occupied by each entry in the vector.
442///
443/// * `Repr`: The storage representation for each collection of 8-bits. This representation
444///   defines the domain of the encoding (i.e., range of realized values) as well as how
445///   this domain is mapped into `NBITS` bits.
446///
447/// * `Ptr`: The storage type for the contiguous memory. Possible representations are:
448///   - `diskann_quantization::bits::SlicePtr<'_, u8>`: For immutable views.
449///   - `diskann_quantization::bits::MutSlicePtr<'_, u8>`: For mutable views.
450///   - `Box<[u8]>`: For standalone vectors.
451///
452/// * `Perm`: By default, this type uses a dense storage strategy where the least significant
453///   bit of the value at index `i` occurs directly after the most significant bit of
454///   the value at index `i-1`.
455///
456///   Different permutations can be used to enable faster distance computations between
457///   compressed vectors and full-precision vectors by enabling faster SIMD unpacking.
458///
459/// * `Len`: The representation for the length of the vector. This may only be one of the
460///   two families of types:
461///   - `diskann_quantization::bits::Dynamic`: For instances with a run-time length.
462///   - `diskann_quantization::bits::Static<N>`: For instances with a compile-time known length
463///     of `N`.
464///
465/// # Examples
466///
467/// ## Canonical Bit Slice
468///
469/// The canonical `BitSlice` stores unsigned integers of `NBITS` densely in memory.
470/// That is, for a type `BitSliceBase<3, Unsigned, _>`, the layout is as follows:
471/// ```text
472/// |<--LSB-- byte 0 --MSB--->|<--LSB-- byte 1 --MSB--->|
473/// | a0 a1 a2 b0 b1 b2 c0 c1 | c2 d0 d1 d2 e0 e1 e2 f0 |
474/// |<-- A -->|<-- B ->|<--- C -->|<-- D ->|<-- E ->|<- F
475/// ```
476/// An example is shown below:
477///
478/// ```rust
479/// use diskann_quantization::bits::{BoxedBitSlice, Unsigned};
480/// // Create a new boxed bit-slice with capacity for 10 dimensions.
481/// let mut x = BoxedBitSlice::<3, Unsigned>::new_boxed(10);
482/// assert_eq!(x.len(), 10);
483/// // The number of bytes in the canonical representation is computed by
484/// // ceil((len * NBITS) / 8);
485/// assert_eq!(x.bytes(), 4);
486///
487/// // Assign values.
488/// x.set(0, 1).unwrap(); // assign the value 1 to index 0
489/// x.set(1, 5).unwrap(); // assign the value 5 to index 1
490/// assert_eq!(x.get(0).unwrap(), 1); // retrieve the value at index 0
491/// assert_eq!(x.get(1).unwrap(), 5); // retrieve the value at index 1
492///
493/// // Assigning out-of-bounds will result in an error.
494/// let err = x.set(1, 10).unwrap_err();
495/// assert!(matches!(diskann_quantization::bits::SetError::EncodingError, err));
496/// // The old value is left untouched.
497/// assert_eq!(x.get(1).unwrap(), 5);
498///
499/// // `BoxedBitSlice` allows itself to be consumed, returning the underlying storage.
500/// let y = x.into_inner();
501/// assert_eq!(y.len(), BoxedBitSlice::<3, Unsigned>::bytes_for(10));
502/// ```
503///
504/// The above example demonstrates a boxed bit slice - a type that owns its underlying
505/// memory. However, this is not always ergonomic when interfacing with data stores.
506/// For this, the viewing interface can be used.
507/// ```rust
508/// use diskann_quantization::bits::{MutBitSlice, Unsigned};
509///
510/// let mut x: Vec<u8> = vec![0; 4];
511/// let mut slice = MutBitSlice::<3, Unsigned>::new(x.as_mut_slice(), 10).unwrap();
512/// assert_eq!(slice.len(), 10);
513/// assert_eq!(slice.bytes(), 4);
514///
515/// // The slice reference behaves just like boxed slice.
516/// slice.set(0, 5).unwrap();
517/// assert_eq!(slice.get(0).unwrap(), 5);
518///
519/// // Note - if the number of bytes required for the provided dimensions does not match
520/// // the length of the provided span, than slice construction will return an error.
521/// let err = MutBitSlice::<3, Unsigned>::new(x.as_mut_slice(), 11).unwrap_err();
522/// assert_eq!(err.to_string(), "input span has length 4 bytes but expected 5");
523/// ```
524#[derive(Debug, Clone, Copy)]
525pub struct BitSliceBase<const NBITS: usize, Repr, Ptr, Perm = Dense, Len = Dynamic>
526where
527    Repr: Representation<NBITS>,
528    Ptr: AsPtr<Type = u8>,
529    Perm: PermutationStrategy<NBITS>,
530    Len: Length,
531{
532    ptr: Ptr,
533    len: Len,
534    repr: PhantomData<Repr>,
535    packing: PhantomData<Perm>,
536}
537
538impl<const NBITS: usize, Repr, Ptr, Perm, Len> BitSliceBase<NBITS, Repr, Ptr, Perm, Len>
539where
540    Repr: Representation<NBITS>,
541    Ptr: AsPtr<Type = u8>,
542    Perm: PermutationStrategy<NBITS>,
543    Len: Length,
544{
545    /// Check that NBITS is in the interval [1, 8].
546    const _CHECK: () = assert!(NBITS > 0 && NBITS <= 8);
547
548    /// Return the exact number of bytes required to store `count` values.
549    pub fn bytes_for(count: usize) -> usize {
550        Perm::bytes(count)
551    }
552
553    /// Return a new `BitSlice` over the data behind `ptr`.
554    ///
555    /// # Safety
556    ///
557    /// It's the callers responsibility to ensure that all the invariants required for
558    /// `std::slice::from_raw_parts(ptr.as_ptr(), len.value)` hold.
559    unsafe fn new_unchecked_internal(ptr: Ptr, len: Len) -> Self {
560        Self {
561            ptr,
562            len,
563            repr: PhantomData,
564            packing: PhantomData,
565        }
566    }
567
568    /// Construct a new `BitSlice` without checking preconditions.
569    ///
570    /// # Safety
571    ///
572    /// Requires the following to avoid undefined behavior:
573    ///
574    /// * `precursor.precursor_len() == Self::bytes_for(<Count as Into<Len>>::into(count).value())`.
575    ///
576    /// This is checked in debug builds.
577    pub unsafe fn new_unchecked<Pre, Count>(precursor: Pre, count: Count) -> Self
578    where
579        Count: Into<Len>,
580        Pre: Precursor<Ptr>,
581    {
582        let count: Len = count.into();
583        debug_assert_eq!(precursor.precursor_len(), Self::bytes_for(count.value()));
584
585        // SAFETY: Inherited from the caller.
586        unsafe { Self::new_unchecked_internal(precursor.precursor_into(), count) }
587    }
588
589    /// Construct a new `BitSlice` from the `precursor` capable of holding `count` encoded
590    /// elements of size `NBITS`.
591    ///
592    /// # Requirements
593    ///
594    /// The number of bytes pointed to by the precursor must be equal to the number of bytes
595    /// required by the layout. That is:
596    ///
597    /// * `precursor.precursor_len() == Self::bytes_for(<Count as Into<Len>>::into(count).value())`.
598    pub fn new<Pre, Count>(precursor: Pre, count: Count) -> Result<Self, ConstructionError>
599    where
600        Count: Into<Len>,
601        Pre: Precursor<Ptr>,
602    {
603        // Allow callers to pass in `usize` as the count when using dynamic
604        let count: Len = count.into();
605
606        // Make sure that the slice has the correct length.
607        if precursor.precursor_len() != Self::bytes_for(count.value()) {
608            Err(ConstructionError {
609                got: precursor.precursor_len(),
610                expected: Self::bytes_for(count.value()),
611            })
612        } else {
613            // SAFETY: We have checked that `precursor` has the correct number of bytes.
614            // The only implementations of `Precursor` are those we defined for slices, so we
615            // don't have to worry about downstream users inserting their own, incorrectly
616            // implemented implementation.
617            Ok(unsafe { Self::new_unchecked(precursor, count) })
618        }
619    }
620
621    /// Return the number of elements contained in the slice.
622    pub fn len(&self) -> usize {
623        self.len.value()
624    }
625
626    /// Return whether or not the slice is empty.
627    pub fn is_empty(&self) -> bool {
628        self.len() == 0
629    }
630
631    /// Return the number of bytes occupied by this slice.
632    pub fn bytes(&self) -> usize {
633        Self::bytes_for(self.len())
634    }
635
636    /// Return the value at logical index `i`.
637    pub fn get(&self, i: usize) -> Result<i64, GetError> {
638        if i >= self.len() {
639            Err(IndexOutOfBounds::new(i, self.len()).into())
640        } else {
641            // SAFETY: We've performed the bounds check.
642            Ok(unsafe { self.get_unchecked(i) })
643        }
644    }
645
646    /// Return the value at logical index `i`.
647    ///
648    /// # Safety
649    ///
650    /// Argument `i` must be in bounds: `0 <= i < self.len()`.
651    pub unsafe fn get_unchecked(&self, i: usize) -> i64 {
652        debug_assert!(i < self.len());
653        debug_assert_eq!(self.as_slice().len(), Perm::bytes(self.len()));
654
655        // SAFETY: We maintain the invariant that
656        // `self.as_slice().len() == Perm::bytes(self.len())`.
657        //
658        // So, `i < self.len()` implies we uphold the safety requirements of `unpack`.
659        Repr::decode(unsafe { Perm::unpack(self.as_slice(), i) })
660    }
661
662    /// Encode and assign `value` to logical index `i`.
663    pub fn set(&mut self, i: usize, value: i64) -> Result<(), SetError>
664    where
665        Ptr: AsMutPtr<Type = u8>,
666    {
667        if i >= self.len() {
668            return Err(IndexOutOfBounds::new(i, self.len()).into());
669        }
670
671        let encoded = Repr::encode(value)?;
672
673        // SAFETY: We've performed the bounds check.
674        unsafe { self.set_unchecked(i, encoded) }
675        Ok(())
676    }
677
678    /// Assign `value` to logical index `i`.
679    ///
680    /// # Safety
681    ///
682    /// Argument `i` must be in bounds: `0 <= i < self.len()`.
683    pub unsafe fn set_unchecked(&mut self, i: usize, encoded: u8)
684    where
685        Ptr: AsMutPtr<Type = u8>,
686    {
687        debug_assert!(i < self.len());
688        debug_assert_eq!(self.as_slice().len(), Perm::bytes(self.len()));
689
690        // SAFETY: We maintain the invariant that
691        // `self.as_slice().len() == Perm::bytes(self.len())`.
692        //
693        // So, `i < self.len()` implies we uphold the safety requirements of `unpack`.
694        unsafe { Perm::pack(self.as_mut_slice(), i, encoded) }
695    }
696
697    /// Return the domain of acceptable values.
698    pub fn domain(&self) -> Repr::Domain {
699        Repr::domain()
700    }
701
702    pub(crate) fn as_slice(&self) -> &'_ [u8] {
703        // SAFETY: This class has the invariant that the backing storage must be initialized
704        // and exist in a single allocation containing at least
705        // `[self.ptr.as_ptr(), self.ptr_ptr() + self.bytes())`.
706        unsafe { std::slice::from_raw_parts(self.ptr.as_ptr(), self.bytes()) }
707    }
708
709    /// Return a pointer to the beginning of the memory associated with this slice.
710    ///
711    /// # NOTE
712    ///
713    /// The memory span underlying this instances is valid for `self.bytes()`, not
714    /// necessarily `self.len()`.
715    pub fn as_ptr(&self) -> *const u8 {
716        self.ptr.as_ptr()
717    }
718
719    /// This function is very easy to use incorrectly and hence is crate-local.
720    pub(super) fn as_mut_slice(&mut self) -> &'_ mut [u8]
721    where
722        Ptr: AsMutPtr,
723    {
724        // SAFETY: This class has the invariant that the backing storage must be initialized
725        // and exist in a single allocation containing at least
726        // `[self.ptr.as_ptr(), self.ptr_ptr() + self.bytes())`.
727        //
728        // A mutable reference to self with `Ptr: AsMutPtr` attests to the fact that we
729        // have an exclusive borrow over the underlying memory.
730        unsafe { std::slice::from_raw_parts_mut(self.ptr.as_mut_ptr(), self.bytes()) }
731    }
732
733    /// This function is very easy to use incorrectly and hence is private.
734    fn as_mut_ptr(&mut self) -> *mut u8
735    where
736        Ptr: AsMutPtr,
737    {
738        self.ptr.as_mut_ptr()
739    }
740}
741
742impl<const NBITS: usize, Repr, Perm, Len>
743    BitSliceBase<NBITS, Repr, Poly<[u8], GlobalAllocator>, Perm, Len>
744where
745    Repr: Representation<NBITS>,
746    Perm: PermutationStrategy<NBITS>,
747    Len: Length,
748{
749    /// Construct a new owning `BitSlice` capable of holding `Count` logical values.
750    /// The slice is initialized in a valid but undefined state.
751    ///
752    /// # Example
753    ///
754    /// ```
755    /// use diskann_quantization::bits::{BoxedBitSlice, Unsigned};
756    /// let mut x = BoxedBitSlice::<3, Unsigned>::new_boxed(4);
757    /// x.set(0, 0).unwrap();
758    /// x.set(1, 2).unwrap();
759    /// x.set(2, 4).unwrap();
760    /// x.set(3, 6).unwrap();
761    ///
762    /// assert_eq!(x.get(0).unwrap(), 0);
763    /// assert_eq!(x.get(1).unwrap(), 2);
764    /// assert_eq!(x.get(2).unwrap(), 4);
765    /// assert_eq!(x.get(3).unwrap(), 6);
766    /// ```
767    pub fn new_boxed<Count>(count: Count) -> Self
768    where
769        Count: Into<Len>,
770    {
771        let count: Len = count.into();
772        let bytes = Self::bytes_for(count.value());
773        let storage: Box<[u8]> = (0..bytes).map(|_| 0).collect();
774
775        // SAFETY: We've ensured that the backing storage has the correct number of bytes
776        // as required by the count and PermutationStrategy.
777        //
778        // Since this is owned storage, we do not need to worry about capturing lifetimes.
779        unsafe { Self::new_unchecked(Poly::from(storage), count) }
780    }
781}
782
783impl<const NBITS: usize, Repr, Perm, Len, A> BitSliceBase<NBITS, Repr, Poly<[u8], A>, Perm, Len>
784where
785    Repr: Representation<NBITS>,
786    Perm: PermutationStrategy<NBITS>,
787    Len: Length,
788    A: AllocatorCore,
789{
790    /// Construct a new owning `BitSlice` capable of holding `Count` logical values using
791    /// the provided allocator.
792    ///
793    /// The slice is initialized in a valid but undefined state.
794    ///
795    /// # Example
796    ///
797    /// ```
798    /// use diskann_quantization::{
799    ///     alloc::GlobalAllocator,
800    ///     bits::{BoxedBitSlice, Unsigned}
801    /// };
802    /// let mut x = BoxedBitSlice::<3, Unsigned>::new_in(4, GlobalAllocator).unwrap();
803    /// x.set(0, 0).unwrap();
804    /// x.set(1, 2).unwrap();
805    /// x.set(2, 4).unwrap();
806    /// x.set(3, 6).unwrap();
807    ///
808    /// assert_eq!(x.get(0).unwrap(), 0);
809    /// assert_eq!(x.get(1).unwrap(), 2);
810    /// assert_eq!(x.get(2).unwrap(), 4);
811    /// assert_eq!(x.get(3).unwrap(), 6);
812    /// ```
813    pub fn new_in<Count>(count: Count, allocator: A) -> Result<Self, AllocatorError>
814    where
815        Count: Into<Len>,
816    {
817        let count: Len = count.into();
818        let bytes = Self::bytes_for(count.value());
819        let storage = Poly::broadcast(0, bytes, allocator)?;
820
821        // SAFETY: We've ensured that the backing storage has the correct number of bytes
822        // as required by the count and PermutationStrategy.
823        //
824        // Since this is owned storage, we do not need to worry about capturing lifetimes.
825        Ok(unsafe { Self::new_unchecked(storage, count) })
826    }
827
828    /// Consume `self` and return the boxed allocation.
829    pub fn into_inner(self) -> Poly<[u8], A> {
830        self.ptr
831    }
832}
833
834/// The layout for `N`-bit integers that references a raw underlying slice.
835pub type BitSlice<'a, const N: usize, Repr, Perm = Dense, Len = Dynamic> =
836    BitSliceBase<N, Repr, SlicePtr<'a, u8>, Perm, Len>;
837
838/// The layout for `N`-bit integers that mutable references a raw underlying slice.
839pub type MutBitSlice<'a, const N: usize, Repr, Perm = Dense, Len = Dynamic> =
840    BitSliceBase<N, Repr, MutSlicePtr<'a, u8>, Perm, Len>;
841
842/// The layout for `N`-bit integers that own the underlying slice.
843pub type PolyBitSlice<const N: usize, Repr, A, Perm = Dense, Len = Dynamic> =
844    BitSliceBase<N, Repr, Poly<[u8], A>, Perm, Len>;
845
846/// The layout for `N`-bit integers that own the underlying slice.
847pub type BoxedBitSlice<const N: usize, Repr, Perm = Dense, Len = Dynamic> =
848    PolyBitSlice<N, Repr, GlobalAllocator, Perm, Len>;
849
850impl<const NBITS: usize> BitSlice<'_, NBITS, Unsigned, Dense>
851where
852    Unsigned: Representation<NBITS>,
853{
854    /// Decode and convert directly into the destination, ignoring unused trailing bits.
855    ///
856    /// # Errors
857    ///
858    /// Returns [`DecodeLengthMismatch`] without writing or calling `convert` if the
859    /// output length differs from the logical input length.
860    #[inline]
861    pub(crate) fn decode_into<T>(
862        &self,
863        output: &mut [T],
864        mut convert: impl FnMut(u8) -> T,
865    ) -> Result<(), DecodeLengthMismatch> {
866        if output.len() != self.len() {
867            return Err(DecodeLengthMismatch {
868                input: self.len(),
869                output: output.len(),
870            });
871        }
872        let packed = self.as_slice();
873        let divisor = 1usize << NBITS.trailing_zeros();
874        let codes_per_block = 8 / divisor;
875        let bytes_per_block = NBITS / divisor;
876        let blocks = output.len() / codes_per_block;
877        let (output, tail) = output.split_at_mut(blocks * codes_per_block);
878        let (packed, rest) = packed.split_at(blocks * bytes_per_block);
879        let mask = (1u64 << NBITS) - 1;
880        // Use the smallest byte-aligned group: 8 / gcd(NBITS, 8) codes.
881        for (block, bytes) in output
882            .chunks_exact_mut(codes_per_block)
883            .zip(packed.chunks_exact(bytes_per_block))
884        {
885            let mut word = [0u8; 8];
886            word[..bytes_per_block].copy_from_slice(bytes);
887            let word = u64::from_le_bytes(word);
888            for (i, out) in block.iter_mut().enumerate() {
889                *out = convert(((word >> (i * NBITS)) & mask) as u8);
890            }
891        }
892        let word = rest
893            .iter()
894            .enumerate()
895            .fold(0u64, |word, (i, &byte)| word | (u64::from(byte) << (8 * i)));
896        for (i, out) in tail.iter_mut().enumerate() {
897            *out = convert(((word >> (i * NBITS)) & mask) as u8);
898        }
899        Ok(())
900    }
901}
902
903///////////////////////////////
904// Special Cased Conversions //
905///////////////////////////////
906
907impl<'a, Ptr> From<&'a BitSliceBase<8, Unsigned, Ptr>> for &'a [u8]
908where
909    Ptr: AsPtr<Type = u8>,
910{
911    fn from(slice: &'a BitSliceBase<8, Unsigned, Ptr>) -> Self {
912        // SAFETY: The original pointer must have been obtained from a slice of the
913        // appropriate length.
914        //
915        // Furthermore, the layout of this type of slice is guaranteed to be identical
916        // to the layout of a `[u8]`.
917        unsafe { std::slice::from_raw_parts(slice.as_ptr(), slice.len()) }
918    }
919}
920
921impl<'this, const NBITS: usize, Repr, Ptr, Perm, Len> Reborrow<'this>
922    for BitSliceBase<NBITS, Repr, Ptr, Perm, Len>
923where
924    Repr: Representation<NBITS>,
925    Ptr: AsPtr<Type = u8>,
926    Perm: PermutationStrategy<NBITS>,
927    Len: Length,
928{
929    type Target = BitSlice<'this, NBITS, Repr, Perm, Len>;
930
931    fn reborrow(&'this self) -> Self::Target {
932        let ptr: *const u8 = self.as_ptr();
933        debug_assert!(!ptr.is_null());
934
935        // Safety: `AsPtr` may never return null pointers.
936        // The `cast_mut()` is safe because `SlicePtr` does not provide a way of retrieving
937        // a mutable pointer.
938        let nonnull = unsafe { NonNull::new_unchecked(ptr.cast_mut()) };
939
940        // Safety: By struct invariant,
941        // `[self.ptr(), self.ptr() + Self::bytes_for(self.len()))` is a valid slice, so
942        // the returned object will also uphold these invariants.
943        //
944        // The returned struct will not outlive `&'this self`, so we've attached the
945        // proper lifetime.
946        let ptr = unsafe { SlicePtr::new_unchecked(nonnull) };
947
948        Self::Target {
949            ptr,
950            len: self.len,
951            repr: PhantomData,
952            packing: PhantomData,
953        }
954    }
955}
956
957impl<'this, const NBITS: usize, Repr, Ptr, Perm, Len> ReborrowMut<'this>
958    for BitSliceBase<NBITS, Repr, Ptr, Perm, Len>
959where
960    Repr: Representation<NBITS>,
961    Ptr: AsMutPtr<Type = u8>,
962    Perm: PermutationStrategy<NBITS>,
963    Len: Length,
964{
965    type Target = MutBitSlice<'this, NBITS, Repr, Perm, Len>;
966
967    fn reborrow_mut(&'this mut self) -> Self::Target {
968        let ptr: *mut u8 = self.as_mut_ptr();
969        debug_assert!(!ptr.is_null());
970
971        // Safety: `AsMutPtr` may never return null pointers.
972        let nonnull = unsafe { NonNull::new_unchecked(ptr) };
973
974        // Safety: By struct invariant,
975        // `[self.ptr(), self.ptr() + Self::bytes_for(self.len()))` is a valid slice, so
976        // the returned object will also uphold these invariants.
977        //
978        // The returned struct will not outlive `&'this mut self`, so we've attached the
979        // proper lifetime.
980        //
981        // Exclusive ownership is attested by both `AsMutPtr` and the mutable refernce
982        // to self.
983        let ptr = unsafe { MutSlicePtr::new_unchecked(nonnull) };
984
985        Self::Target {
986            ptr,
987            len: self.len,
988            repr: PhantomData,
989            packing: PhantomData,
990        }
991    }
992}
993
994///////////
995// Tests //
996///////////
997
998#[cfg(test)]
999mod tests {
1000    use rand::{
1001        Rng, SeedableRng,
1002        distr::{Distribution, Uniform},
1003        rngs::StdRng,
1004        seq::{IndexedRandom, SliceRandom},
1005    };
1006
1007    use super::*;
1008    use crate::{bits::Static, test_util::AlwaysFails};
1009
1010    fn check_bulk_decoding<const NBITS: usize>()
1011    where
1012        Unsigned: Representation<NBITS>,
1013    {
1014        for len in [0, 1, 2, 3, 7, 8, 9, 15, 16, 17, 255, 256, 257, 513] {
1015            for offset in [0, 1, 7] {
1016                let byte_count = BitSlice::<NBITS, Unsigned>::bytes_for(len);
1017                let mut bytes = vec![0xff; offset + byte_count];
1018                let bytes = &mut bytes[offset..];
1019                let mut codes = MutBitSlice::<NBITS, Unsigned>::new(bytes, len).unwrap();
1020                for i in 0..len {
1021                    codes
1022                        .set(i, ((i * 37 + i / 2) & ((1 << NBITS) - 1)) as i64)
1023                        .unwrap();
1024                }
1025                let expected: Vec<_> = (0..len).map(|i| codes.get(i).unwrap()).collect();
1026                let mut guarded = vec![i64::MIN; len + 2];
1027                let mut calls = 0;
1028                codes
1029                    .reborrow()
1030                    .decode_into(&mut guarded[1..=len], |value| {
1031                        assert_eq!(i64::from(value), expected[calls]);
1032                        calls += 1;
1033                        i64::from(value)
1034                    })
1035                    .unwrap();
1036                assert_eq!(calls, len);
1037                assert_eq!(&guarded[1..=len], expected);
1038                assert_eq!(guarded[0], i64::MIN);
1039                assert_eq!(guarded[len + 1], i64::MIN);
1040            }
1041        }
1042    }
1043
1044    #[test]
1045    fn test_decode_into_preserves_order_tails_and_bounds() {
1046        check_bulk_decoding::<1>();
1047        check_bulk_decoding::<2>();
1048        check_bulk_decoding::<3>();
1049        check_bulk_decoding::<4>();
1050        check_bulk_decoding::<5>();
1051        check_bulk_decoding::<6>();
1052        check_bulk_decoding::<7>();
1053        check_bulk_decoding::<8>();
1054    }
1055
1056    #[test]
1057    fn test_decode_into_applies_conversion() {
1058        let codes = BitSlice::<8, Unsigned>::new(&[0, 128, 255][..], 3).unwrap();
1059        let mut output = [0i16; 3];
1060        codes
1061            .decode_into(&mut output, |value| i16::from(value) * 3)
1062            .unwrap();
1063        assert_eq!(output, [0, 384, 765]);
1064        let codes = BoxedBitSlice::<4, Unsigned>::new_boxed(2);
1065        let mut output = [1u8; 2];
1066        codes
1067            .reborrow()
1068            .decode_into(&mut output, |value| value)
1069            .unwrap();
1070        assert_eq!(output, [0, 0]);
1071    }
1072
1073    #[test]
1074    fn test_decode_into_length_mismatch_does_not_write_or_convert() {
1075        let codes = BitSlice::<4, Unsigned>::new(&[0x21u8][..], 2).unwrap();
1076        for len in [0, 1, 3] {
1077            let mut output = vec![99; len];
1078            let mut calls = 0;
1079            let error = codes
1080                .decode_into(&mut output, |value| {
1081                    calls += 1;
1082                    value
1083                })
1084                .unwrap_err();
1085            assert_eq!(
1086                error,
1087                DecodeLengthMismatch {
1088                    input: 2,
1089                    output: len
1090                }
1091            );
1092            assert_eq!(calls, 0);
1093            assert_eq!(output, vec![99; len]);
1094        }
1095    }
1096
1097    ////////////
1098    // Errors //
1099    ////////////
1100
1101    const BOUNDS: &str = "special bounds";
1102
1103    #[test]
1104    fn test_encoding_error() {
1105        assert_eq!(std::mem::size_of::<EncodingError>(), 16);
1106        assert_eq!(
1107            std::mem::size_of::<Option<EncodingError>>(),
1108            16,
1109            "expected EncodingError to have the niche optimization"
1110        );
1111        let err = EncodingError::new(7, &BOUNDS);
1112        assert_eq!(
1113            err.to_string(),
1114            "value 7 is not in the encodable range of special bounds"
1115        );
1116    }
1117
1118    // Check that a type is `Send` and `Sync`.
1119    fn assert_send_and_sync<T: Send + Sync>(_x: &T) {}
1120
1121    ////////////
1122    // Binary //
1123    ////////////
1124
1125    #[test]
1126    fn test_binary_repr() {
1127        assert_eq!(Binary::encode(-1).unwrap(), 0);
1128        assert_eq!(Binary::encode(1).unwrap(), 1);
1129        assert_eq!(Binary::decode(0), -1);
1130        assert_eq!(Binary::decode(1), 1);
1131
1132        assert!(Binary::check(-1));
1133        assert!(Binary::check(1));
1134        assert!(!Binary::check(0));
1135        assert!(!Binary::check(-2));
1136        assert!(!Binary::check(2));
1137
1138        let domain: Vec<_> = Binary::domain().collect();
1139        assert_eq!(domain, &[-1, 1]);
1140    }
1141
1142    ///////////
1143    // Sizes //
1144    ///////////
1145
1146    #[test]
1147    fn test_sizes() {
1148        assert_eq!(std::mem::size_of::<BitSlice<'static, 8, Unsigned>>(), 16);
1149        assert_eq!(std::mem::size_of::<MutBitSlice<'static, 8, Unsigned>>(), 16);
1150
1151        // Ensure the borrowed slices are eligible for niche optimization.
1152        assert_eq!(
1153            std::mem::size_of::<Option<BitSlice<'static, 8, Unsigned>>>(),
1154            16
1155        );
1156        assert_eq!(
1157            std::mem::size_of::<Option<MutBitSlice<'static, 8, Unsigned>>>(),
1158            16
1159        );
1160
1161        assert_eq!(
1162            std::mem::size_of::<BitSlice<'static, 8, Unsigned, Dense, Static<128>>>(),
1163            8
1164        );
1165    }
1166
1167    ///////////////////
1168    // General Tests //
1169    ///////////////////
1170
1171    cfg_if::cfg_if! {
1172        if #[cfg(miri)] {
1173            const MAX_DIM: usize = 160;
1174            const FUZZ_ITERATIONS: usize = 1;
1175        } else if #[cfg(debug_assertions)] {
1176            const MAX_DIM: usize = 128;
1177            const FUZZ_ITERATIONS: usize = 10;
1178        } else {
1179            const MAX_DIM: usize = 256;
1180            const FUZZ_ITERATIONS: usize = 100;
1181        }
1182    }
1183
1184    fn test_send_and_sync<const NBITS: usize, Repr, Perm>()
1185    where
1186        Repr: Representation<NBITS> + Send + Sync,
1187        Perm: PermutationStrategy<NBITS> + Send + Sync,
1188    {
1189        let mut x = BoxedBitSlice::<NBITS, Repr, Perm>::new_boxed(1);
1190        assert_send_and_sync(&x);
1191        assert_send_and_sync(&x.reborrow());
1192        assert_send_and_sync(&x.reborrow_mut());
1193    }
1194
1195    fn test_empty<const NBITS: usize, Repr, Perm>()
1196    where
1197        Repr: Representation<NBITS>,
1198        Perm: PermutationStrategy<NBITS>,
1199    {
1200        let base: &mut [u8] = &mut [];
1201        let mut slice = MutBitSlice::<NBITS, Repr, Perm>::new(base, 0).unwrap();
1202        assert_eq!(slice.len(), 0);
1203        assert!(slice.is_empty());
1204
1205        {
1206            let reborrow = slice.reborrow();
1207            assert_eq!(reborrow.len(), 0);
1208            assert!(reborrow.is_empty());
1209        }
1210
1211        {
1212            let reborrow = slice.reborrow_mut();
1213            assert_eq!(reborrow.len(), 0);
1214            assert!(reborrow.is_empty());
1215        }
1216    }
1217
1218    // times, ensuring that values are preserved.
1219    fn test_construction_errors<const NBITS: usize, Repr, Perm>()
1220    where
1221        Repr: Representation<NBITS>,
1222        Perm: PermutationStrategy<NBITS>,
1223    {
1224        let len: usize = 10;
1225        let bytes = Perm::bytes(len);
1226
1227        // Construction errors for Boxes
1228        let box_big = Poly::broadcast(0u8, bytes + 1, GlobalAllocator).unwrap();
1229        let box_small = Poly::broadcast(0u8, bytes - 1, GlobalAllocator).unwrap();
1230        let box_right = Poly::broadcast(0u8, bytes, GlobalAllocator).unwrap();
1231
1232        let result = BoxedBitSlice::<NBITS, Repr, Perm>::new(box_big, len);
1233        match result {
1234            Err(ConstructionError { got, expected }) => {
1235                assert_eq!(got, bytes + 1);
1236                assert_eq!(expected, bytes);
1237            }
1238            _ => panic!("shouldn't have reached here!"),
1239        };
1240
1241        let result = BoxedBitSlice::<NBITS, Repr, Perm>::new(box_small, len);
1242        match result {
1243            Err(ConstructionError { got, expected }) => {
1244                assert_eq!(got, bytes - 1);
1245                assert_eq!(expected, bytes);
1246            }
1247            _ => panic!("shouldn't have reached here!"),
1248        };
1249
1250        let mut base = BoxedBitSlice::<NBITS, Repr, Perm>::new(box_right, len).unwrap();
1251        let ptr = base.as_ptr();
1252        assert_eq!(base.len(), len);
1253
1254        // Successful mutable reborrow and borrow.
1255        {
1256            // Use reborrow
1257            let borrowed = base.reborrow_mut();
1258            assert_eq!(borrowed.as_ptr(), ptr);
1259            assert_eq!(borrowed.len(), len);
1260
1261            // Go through a slice.
1262            let borrowed = MutBitSlice::<NBITS, Repr, Perm>::new(base.as_mut_slice(), len).unwrap();
1263            assert_eq!(borrowed.as_ptr(), ptr);
1264            assert_eq!(borrowed.len(), len);
1265        }
1266
1267        // Successful mutable borrow.
1268        {
1269            // Try constructing from an oversized slice.
1270            let mut oversized = vec![0; bytes + 1];
1271            let result = MutBitSlice::<NBITS, Repr, Perm>::new(oversized.as_mut_slice(), len);
1272            match result {
1273                Err(ConstructionError { got, expected }) => {
1274                    assert_eq!(got, bytes + 1);
1275                    assert_eq!(expected, bytes);
1276                }
1277                _ => panic!("shouldn't have reached here!"),
1278            };
1279
1280            let mut undersized = vec![0; bytes - 1];
1281            let result = MutBitSlice::<NBITS, Repr, Perm>::new(undersized.as_mut_slice(), len);
1282            match result {
1283                Err(ConstructionError { got, expected }) => {
1284                    assert_eq!(got, bytes - 1);
1285                    assert_eq!(expected, bytes);
1286                }
1287                _ => panic!("shouldn't have reached here!"),
1288            };
1289        }
1290
1291        // Successful const borrow and reborrow.
1292        {
1293            // Use reborrow
1294            let borrowed = base.reborrow();
1295            assert_eq!(borrowed.as_ptr(), ptr);
1296            assert_eq!(borrowed.len(), len);
1297
1298            // Go through a slice.
1299            let borrowed = BitSlice::<NBITS, Repr, Perm>::new(base.as_slice(), len).unwrap();
1300            assert_eq!(borrowed.as_ptr(), ptr);
1301            assert_eq!(borrowed.len(), len);
1302
1303            // Go through a mutable slice.
1304            let borrowed = BitSlice::<NBITS, Repr, Perm>::new(base.as_mut_slice(), len).unwrap();
1305            assert_eq!(borrowed.as_ptr(), ptr);
1306            assert_eq!(borrowed.len(), len);
1307        }
1308
1309        // Successful mutable borrow.
1310        {
1311            // Try constructing from an oversized slice.
1312            let mut oversized = vec![0; bytes + 1];
1313            let result = BitSlice::<NBITS, Repr, Perm>::new(oversized.as_mut_slice(), len);
1314            match result {
1315                Err(ConstructionError { got, expected }) => {
1316                    assert_eq!(got, bytes + 1);
1317                    assert_eq!(expected, bytes);
1318                }
1319                _ => panic!("shouldn't have reached here!"),
1320            };
1321
1322            let result = BitSlice::<NBITS, Repr, Perm>::new(oversized.as_slice(), len);
1323            match result {
1324                Err(ConstructionError { got, expected }) => {
1325                    assert_eq!(got, bytes + 1);
1326                    assert_eq!(expected, bytes);
1327                }
1328                _ => panic!("shouldn't have reached here!"),
1329            };
1330
1331            // Try constructing from an undersized slice.
1332            let mut undersized = vec![0; bytes - 1];
1333            let result = BitSlice::<NBITS, Repr, Perm>::new(undersized.as_mut_slice(), len);
1334            match result {
1335                Err(ConstructionError { got, expected }) => {
1336                    assert_eq!(got, bytes - 1);
1337                    assert_eq!(expected, bytes);
1338                }
1339                _ => panic!("shouldn't have reached here!"),
1340            };
1341
1342            let result = BitSlice::<NBITS, Repr, Perm>::new(undersized.as_slice(), len);
1343            match result {
1344                Err(ConstructionError { got, expected }) => {
1345                    assert_eq!(got, bytes - 1);
1346                    assert_eq!(expected, bytes);
1347                }
1348                _ => panic!("shouldn't have reached here!"),
1349            };
1350        }
1351    }
1352
1353    // This series of tests writes to all indices in the vector in random orders multiple
1354    // times, ensuring that values are preserved.
1355    fn run_overwrite_test<const NBITS: usize, Perm, Len, R>(
1356        base: &mut BoxedBitSlice<NBITS, Unsigned, Perm, Len>,
1357        num_iterations: usize,
1358        rng: &mut R,
1359    ) where
1360        Unsigned: Representation<NBITS, Domain = RangeInclusive<i64>>,
1361        Len: Length,
1362        Perm: PermutationStrategy<NBITS>,
1363        R: Rng,
1364    {
1365        let mut expected: Vec<i64> = vec![0; base.len()];
1366        let mut indices: Vec<usize> = (0..base.len()).collect();
1367        for i in 0..base.len() {
1368            base.set(i, 0).unwrap();
1369        }
1370
1371        for i in 0..base.len() {
1372            assert_eq!(base.get(i).unwrap(), 0, "failed to initialize bit vector");
1373        }
1374
1375        let domain = base.domain();
1376        assert_eq!(domain, 0..=2i64.pow(NBITS as u32) - 1);
1377        let distribution = Uniform::new_inclusive(*domain.start(), *domain.end()).unwrap();
1378
1379        for iter in 0..num_iterations {
1380            // Shuffle insertion order.
1381            indices.shuffle(rng);
1382
1383            // Insert random values.
1384            for &i in indices.iter() {
1385                let value = distribution.sample(rng);
1386                expected[i] = value;
1387                base.set(i, value).unwrap();
1388            }
1389
1390            // Make sure values are preserved.
1391            for (i, &expect) in expected.iter().enumerate() {
1392                let value = base.get(i).unwrap();
1393                assert_eq!(
1394                    value, expect,
1395                    "retrieval failed on iteration {iter} at index {i}"
1396                );
1397            }
1398
1399            // Make sure the reborrowed version matches.
1400            let borrowed = base.reborrow();
1401            for (i, &expect) in expected.iter().enumerate() {
1402                let value = borrowed.get(i).unwrap();
1403                assert_eq!(
1404                    value, expect,
1405                    "reborrow retrieval failed on iteration {iter} at index {i}"
1406                );
1407            }
1408        }
1409    }
1410
1411    fn run_overwrite_binary_test<Perm, Len, R>(
1412        base: &mut BoxedBitSlice<1, Binary, Perm, Len>,
1413        num_iterations: usize,
1414        rng: &mut R,
1415    ) where
1416        Len: Length,
1417        Perm: PermutationStrategy<1>,
1418        R: Rng,
1419    {
1420        let mut expected: Vec<i64> = vec![0; base.len()];
1421        let mut indices: Vec<usize> = (0..base.len()).collect();
1422        for i in 0..base.len() {
1423            base.set(i, -1).unwrap();
1424        }
1425
1426        for i in 0..base.len() {
1427            assert_eq!(base.get(i).unwrap(), -1, "failed to initialize bit vector");
1428        }
1429
1430        let distribution: [i64; 2] = [-1, 1];
1431
1432        for iter in 0..num_iterations {
1433            // Shuffle insertion order.
1434            indices.shuffle(rng);
1435
1436            // Insert random values.
1437            for &i in indices.iter() {
1438                let value = distribution.choose(rng).unwrap();
1439                expected[i] = *value;
1440                base.set(i, *value).unwrap();
1441            }
1442
1443            // Make sure values are preserved.
1444            for (i, &expect) in expected.iter().enumerate() {
1445                let value = base.get(i).unwrap();
1446                assert_eq!(
1447                    value, expect,
1448                    "retrieval failed on iteration {iter} at index {i}"
1449                );
1450            }
1451
1452            // Make sure the reborrowed version matches.
1453            let borrowed = base.reborrow();
1454            for (i, &expect) in expected.iter().enumerate() {
1455                let value = borrowed.get(i).unwrap();
1456                assert_eq!(
1457                    value, expect,
1458                    "reborrow retrieval failed on iteration {iter} at index {i}"
1459                );
1460            }
1461        }
1462    }
1463
1464    //////////////////////
1465    // Unsigned - Dense //
1466    //////////////////////
1467
1468    fn test_unsigned_dense<const NBITS: usize, Len, R>(
1469        len: Len,
1470        minimum: i64,
1471        maximum: i64,
1472        rng: &mut R,
1473    ) where
1474        Unsigned: Representation<NBITS, Domain = RangeInclusive<i64>>,
1475        Dense: PermutationStrategy<NBITS>,
1476        Len: Length,
1477        R: Rng,
1478    {
1479        test_send_and_sync::<NBITS, Unsigned, Dense>();
1480        test_empty::<NBITS, Unsigned, Dense>();
1481        test_construction_errors::<NBITS, Unsigned, Dense>();
1482        assert_eq!(Unsigned::domain_const::<NBITS>(), Unsigned::domain(),);
1483
1484        match PolyBitSlice::<NBITS, Unsigned, _, Dense, Len>::new_in(len, AlwaysFails) {
1485            Ok(_) => {
1486                if len.value() != 0 {
1487                    panic!("zero sized allocations don't require an allocator");
1488                }
1489            }
1490            Err(AllocatorError) => {
1491                if len.value() == 0 {
1492                    panic!("allocation should have failed");
1493                }
1494            }
1495        }
1496
1497        let mut base =
1498            PolyBitSlice::<NBITS, Unsigned, _, Dense, Len>::new_in(len, GlobalAllocator).unwrap();
1499        assert_eq!(
1500            base.len(),
1501            len.value(),
1502            "BoxedBitSlice returned the incorrect length"
1503        );
1504
1505        let expected_bytes = BitSlice::<'static, NBITS, Unsigned>::bytes_for(len.value());
1506        assert_eq!(
1507            base.bytes(),
1508            expected_bytes,
1509            "BoxedBitSlice has the incorrect number of bytes"
1510        );
1511
1512        // Check that the minimum and maximum values reported by the struct are correct.
1513        assert_eq!(base.domain(), minimum..=maximum);
1514
1515        if len.value() == 0 {
1516            return;
1517        }
1518
1519        let ptr = base.as_ptr();
1520
1521        // Now that we know the length is non-zero, we can try testing the interface.
1522        // Setting the lowest index should always work.
1523        {
1524            let mut borrowed = base.reborrow_mut();
1525
1526            // Make sure the pointer is preserved.
1527            assert_eq!(
1528                borrowed.as_ptr(),
1529                ptr,
1530                "pointer was not preserved during borrowing!"
1531            );
1532            assert_eq!(
1533                borrowed.len(),
1534                len.value(),
1535                "borrowing did not preserve length!"
1536            );
1537
1538            borrowed.set(0, 0).unwrap();
1539            assert_eq!(borrowed.get(0).unwrap(), 0);
1540
1541            borrowed.set(0, 1).unwrap();
1542            assert_eq!(borrowed.get(0).unwrap(), 1);
1543
1544            borrowed.set(0, 0).unwrap();
1545            assert_eq!(borrowed.get(0).unwrap(), 0);
1546
1547            // Setting to an invalid value should yield an error.
1548            let result = borrowed.set(0, minimum - 1);
1549            assert!(matches!(result, Err(SetError::EncodingError { .. })));
1550
1551            let result = borrowed.set(0, maximum + 1);
1552            assert!(matches!(result, Err(SetError::EncodingError { .. })));
1553
1554            // Make sure an out-of-bounds access is caught.
1555            let result = borrowed.set(borrowed.len(), 0);
1556            assert!(matches!(result, Err(SetError::IndexError { .. })));
1557
1558            // Ensure that getting out-of-bounds is an error.
1559            let result = borrowed.get(borrowed.len());
1560            assert!(matches!(result, Err(GetError::IndexError { .. })));
1561        }
1562
1563        {
1564            // Reconsturct the mutable borrow directly through a slice.
1565            let borrowed =
1566                MutBitSlice::<NBITS, Unsigned, Dense, Len>::new(base.as_mut_slice(), len).unwrap();
1567
1568            assert_eq!(
1569                borrowed.as_ptr(),
1570                ptr,
1571                "pointer was not preserved during borrowing!"
1572            );
1573            assert_eq!(
1574                borrowed.len(),
1575                len.value(),
1576                "borrowing did not preserve length!"
1577            );
1578        }
1579
1580        {
1581            let borrowed = base.reborrow();
1582
1583            // Make sure the pointer is preserved.
1584            assert_eq!(
1585                borrowed.as_ptr(),
1586                ptr,
1587                "pointer was not preserved during borrowing!"
1588            );
1589
1590            assert_eq!(
1591                borrowed.len(),
1592                len.value(),
1593                "borrowing did not preserve length!"
1594            );
1595
1596            // Ensure that getting out-of-bounds is an error.
1597            let result = borrowed.get(borrowed.len());
1598            assert!(matches!(result, Err(GetError::IndexError { .. })));
1599        }
1600
1601        {
1602            // Reconsturct the mutable borrow directly through a slice.
1603            let borrowed =
1604                BitSlice::<NBITS, Unsigned, Dense, Len>::new(base.as_slice(), len).unwrap();
1605
1606            assert_eq!(
1607                borrowed.as_ptr(),
1608                ptr,
1609                "pointer was not preserved during borrowing!"
1610            );
1611            assert_eq!(
1612                borrowed.len(),
1613                len.value(),
1614                "borrowing did not preserve length!"
1615            );
1616        }
1617
1618        {
1619            // Reconsturct the mutable borrow directly through a slice.
1620            let borrowed =
1621                BitSlice::<NBITS, Unsigned, Dense, Len>::new(base.as_mut_slice(), len).unwrap();
1622
1623            assert_eq!(
1624                borrowed.as_ptr(),
1625                ptr,
1626                "pointer was not preserved during borrowing!"
1627            );
1628            assert_eq!(
1629                borrowed.len(),
1630                len.value(),
1631                "borrowing did not preserve length!"
1632            );
1633        }
1634
1635        // Now we begin the testing loop.
1636        run_overwrite_test(&mut base, FUZZ_ITERATIONS, rng);
1637    }
1638
1639    macro_rules! generate_unsigned_test {
1640        ($name:ident, $NBITS:literal, $MIN:literal, $MAX:literal, $SEED:literal) => {
1641            #[test]
1642            fn $name() {
1643                let mut rng = StdRng::seed_from_u64($SEED);
1644                for dim in 0..MAX_DIM {
1645                    test_unsigned_dense::<$NBITS, Dynamic, _>(dim.into(), $MIN, $MAX, &mut rng);
1646                }
1647            }
1648        };
1649    }
1650
1651    generate_unsigned_test!(test_unsigned_8bit, 8, 0, 0xff, 0xc652f2a1018f442b);
1652    generate_unsigned_test!(test_unsigned_7bit, 7, 0, 0x7f, 0xb732e59fec6d6c9c);
1653    generate_unsigned_test!(test_unsigned_6bit, 6, 0, 0x3f, 0x35d9380d0a318f21);
1654    generate_unsigned_test!(test_unsigned_5bit, 5, 0, 0x1f, 0xfb09895183334304);
1655    generate_unsigned_test!(test_unsigned_4bit, 4, 0, 0x0f, 0x38dfcf9e82c33f48);
1656    generate_unsigned_test!(test_unsigned_3bit, 3, 0, 0x07, 0xf9a94c8c749ee26c);
1657    generate_unsigned_test!(test_unsigned_2bit, 2, 0, 0x03, 0xbba03db62cecf4cf);
1658    generate_unsigned_test!(test_unsigned_1bit, 1, 0, 0x01, 0x54ea2a07d7c67f37);
1659
1660    #[test]
1661    fn test_binary_dense() {
1662        let mut rng = StdRng::seed_from_u64(0xb3c95e8e19d3842e);
1663        for len in 0..MAX_DIM {
1664            test_send_and_sync::<1, Binary, Dense>();
1665            test_empty::<1, Binary, Dense>();
1666            test_construction_errors::<1, Binary, Dense>();
1667
1668            // Create a boxed base.
1669            let mut base = BoxedBitSlice::<1, Binary>::new_boxed(len);
1670            assert_eq!(
1671                base.len(),
1672                len,
1673                "BoxedBitSlice returned the incorrect length"
1674            );
1675
1676            assert_eq!(base.bytes(), len.div_ceil(8));
1677
1678            let bytes = BitSlice::<'static, 1, Binary>::bytes_for(len);
1679            assert_eq!(
1680                bytes,
1681                len.div_ceil(8),
1682                "BoxedBitSlice has the incorrect number of bytes"
1683            );
1684
1685            if len == 0 {
1686                continue;
1687            }
1688
1689            // Setting to an invalid value should yield an error.
1690            let result = base.set(0, 0);
1691            assert!(matches!(result, Err(SetError::EncodingError { .. })));
1692
1693            // Make sure an out-of-bounds access is caught.
1694            let result = base.set(base.len(), -1);
1695            assert!(matches!(result, Err(SetError::IndexError { .. })));
1696
1697            // Ensure that getting out-of-bounds is an error.
1698            let result = base.get(base.len());
1699            assert!(matches!(result, Err(GetError::IndexError { .. })));
1700
1701            // Now we begin the testing loop.
1702            run_overwrite_binary_test(&mut base, FUZZ_ITERATIONS, &mut rng);
1703        }
1704    }
1705
1706    #[test]
1707    fn test_4bit_bit_transpose() {
1708        let mut rng = StdRng::seed_from_u64(0xb3c95e8e19d3842e);
1709        for len in 0..MAX_DIM {
1710            test_send_and_sync::<4, Unsigned, BitTranspose>();
1711            test_empty::<4, Unsigned, BitTranspose>();
1712            test_construction_errors::<4, Unsigned, BitTranspose>();
1713
1714            // Create a boxed base.
1715            let mut base = BoxedBitSlice::<4, Unsigned, BitTranspose>::new_boxed(len);
1716            assert_eq!(
1717                base.len(),
1718                len,
1719                "BoxedBitSlice returned the incorrect length"
1720            );
1721
1722            assert_eq!(base.bytes(), 32 * len.div_ceil(64));
1723
1724            let bytes = BitSlice::<'static, 4, Unsigned, BitTranspose>::bytes_for(len);
1725            assert_eq!(
1726                bytes,
1727                32 * len.div_ceil(64),
1728                "BoxedBitSlice has the incorrect number of bytes"
1729            );
1730
1731            if len == 0 {
1732                continue;
1733            }
1734
1735            // Setting to an invalid value should yield an error.
1736            let result = base.set(0, -1);
1737            assert!(matches!(result, Err(SetError::EncodingError { .. })));
1738
1739            // Make sure an out-of-bounds access is caught.
1740            let result = base.set(base.len(), -1);
1741            assert!(matches!(result, Err(SetError::IndexError { .. })));
1742
1743            // Ensure that getting out-of-bounds is an error.
1744            let result = base.get(base.len());
1745            assert!(matches!(result, Err(GetError::IndexError { .. })));
1746
1747            // Now we begin the testing loop.
1748            run_overwrite_test(&mut base, FUZZ_ITERATIONS, &mut rng);
1749        }
1750    }
1751}