compressed_intvec/fixed/
traits.rs

1//! # Core Traits for [`FixedVec`](crate::fixed::FixedVec)
2//!
3//! This module defines the core traits that enable the generic and unified
4//! [`FixedVec`](crate::fixed::FixedVec) architecture. These traits abstract away the details of the
5//! underlying storage words and the conversion logic for different element types,
6//! allowing [`FixedVec`](crate::fixed::FixedVec) to be highly configurable.
7
8use common_traits::{IntoAtomic, SignedInt, UnsignedInt};
9use dsi_bitstream::{
10    prelude::{ToInt, ToNat},
11    traits::Endianness,
12};
13use num_traits::{Bounded, NumCast, ToPrimitive};
14use std::fmt::Debug;
15
16/// A trait that abstracts over the primitive unsigned integer types that can
17/// serve as the storage words in a [`FixedVec`](crate::fixed::FixedVec).
18///
19/// This trait establishes a contract for what constitutes a "machine word"
20/// for storage, providing access to its size in bits and requiring the
21/// necessary traits for bit-level operations.
22pub trait Word:
23    UnsignedInt
24    + Bounded
25    + ToPrimitive
26    + dsi_bitstream::traits::Word
27    + NumCast
28    + Copy
29    + Send
30    + Sync
31    + Debug
32    + IntoAtomic
33    + 'static
34{
35    /// The number of bits in this word type (e.g., 64 for `u64`).
36    const BITS: usize = std::mem::size_of::<Self>() * 8;
37}
38
39/// A macro to implement the [`Word`] trait for a given list of unsigned integer types.
40macro_rules! impl_word_for {
41    ($($t:ty),*) => {$(
42        impl Word for $t {}
43    )*};
44}
45
46// Implement [`Word`] for all standard unsigned integer types.
47impl_word_for!(u8, u16, u32, u64, usize);
48
49/// A trait that defines a bidirectional, lossless conversion between a user-facing
50/// element type `T` and its storage representation `W`.
51///
52/// This trait is central to [`FixedVec`](crate::fixed::FixedVec)'s ability to store various integer types
53/// in a generic bit buffer.
54pub trait Storable<W: Word>: Sized + Copy {
55    /// Converts the element into its storage word representation.
56    ///
57    /// For signed integers, this conversion uses ZigZag encoding to map negative
58    /// and positive values to a compact unsigned representation.
59    fn into_word(self) -> W;
60    /// Converts a storage word representation back into an element.
61    ///
62    /// For signed integers, this reverses the ZigZag encoding.
63    fn from_word(word: W) -> Self;
64}
65
66/// Macro to implement [`Storable`] for unsigned integer types.
67///
68/// This implementation is a direct, lossless cast between the unsigned
69/// element type and the storage word type `W`.
70macro_rules! impl_storable_for_unsigned {
71    ($($T:ty),*) => {$(
72        impl<W> Storable<W> for $T
73        where
74            W: Word + TryFrom<$T>,
75            W: TryInto<$T>,
76        {
77            #[inline(always)]
78            fn into_word(self) -> W {
79                self.try_into().unwrap_or_else(|_| panic!("BUG: T -> W conversion failed."))
80            }
81
82            #[inline(always)]
83            fn from_word(word: W) -> Self {
84                word.try_into().unwrap_or_else(|_| {
85                    panic!("BUG: W -> T conversion failed. Logic error in FixedVec's bit manipulation.")
86                })
87            }
88        }
89    )*};
90}
91
92/// Macro to implement [`Storable`] for signed integer types using ZigZag encoding.
93///
94/// ZigZag encoding maps signed integers to unsigned integers in a way that is
95/// efficient for variable-length encoding but is also used here to ensure that
96/// small signed values (positive or negative) map to small unsigned values.
97macro_rules! impl_storable_for_signed {
98    ($($T:ty),*) => {$(
99        impl<W> Storable<W> for $T
100        where
101            W: Word,
102            <$T as SignedInt>::UnsignedInt: TryInto<W>,
103            W: TryInto<<$T as SignedInt>::UnsignedInt>,
104        {
105            #[inline(always)]
106            fn into_word(self) -> W {
107                self.to_nat().try_into().unwrap_or_else(|_| panic!("BUG: Signed -> Unsigned -> W conversion failed."))
108            }
109
110            #[inline(always)]
111            fn from_word(word: W) -> Self {
112                let unsigned_val = word.try_into().unwrap_or_else(|_| {
113                    panic!("BUG: W -> Unsigned conversion failed. Logic error in FixedVec.")
114                });
115                ToInt::to_int(unsigned_val)
116            }
117        }
118    )*};
119}
120
121// Implement `Storable` for all primitive integer types.
122impl_storable_for_unsigned!(u8, u16, u32, u64, u128, usize);
123impl_storable_for_signed!(i8, i16, i32, i64, i128, isize);
124
125/// A sealed trait to associate an element type `T` with its default storage
126/// word `W` and `Endianness` `E`.
127///
128/// This allows for the creation of convenient type aliases like `UFixedVec<T>`
129/// that do not require specifying all generic parameters.
130pub trait DefaultParams: Sized {
131    /// The default word type for storage (usually `usize`).
132    type W: Word;
133    /// The default endianness (usually `LittleEndian`).
134    type E: Endianness;
135}
136
137/// Macro to implement [`DefaultParams`] for unsigned integer types.
138macro_rules! impl_default_params_unsigned {
139    ($($T:ty),*) => {$(
140        impl DefaultParams for $T {
141            type W = usize;
142            type E = dsi_bitstream::prelude::LE;
143        }
144    )*};
145}
146
147/// Macro to implement [`DefaultParams`] for signed integer types.
148macro_rules! impl_default_params_signed {
149    ($($T:ty),*) => {$(
150        impl DefaultParams for $T {
151            type W = usize;
152            type E = dsi_bitstream::prelude::LE;
153        }
154    )*};
155}
156
157impl_default_params_unsigned!(u8, u16, u32, u64, u128, usize);
158impl_default_params_signed!(i8, i16, i32, i64, i128, isize);