Skip to main content

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};
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    Bounded
24    + ToPrimitive
25    + dsi_bitstream::traits::Word
26    + NumCast
27    + Copy
28    + Send
29    + Sync
30    + Debug
31    + IntoAtomic
32    + 'static
33{
34    /// The number of bits in this word type (e.g., 64 for `u64`).
35    const BITS: usize = std::mem::size_of::<Self>() * 8;
36}
37
38/// A macro to implement the [`Word`] trait for a given list of unsigned integer types.
39macro_rules! impl_word_for {
40    ($($t:ty),*) => {$(
41        impl Word for $t {}
42    )*};
43}
44
45// Implement [`Word`] for all standard unsigned integer types.
46impl_word_for!(u8, u16, u32, u64, usize);
47
48/// A trait that defines a bidirectional, lossless conversion between a user-facing
49/// element type `T` and its storage representation `W`.
50///
51/// This trait is central to [`FixedVec`](crate::fixed::FixedVec)'s ability to store various integer types
52/// in a generic bit buffer.
53pub trait Storable<W: Word>: Sized + Copy {
54    /// Converts the element into its storage word representation.
55    ///
56    /// For signed integers, this conversion uses ZigZag encoding to map negative
57    /// and positive values to a compact unsigned representation.
58    fn into_word(self) -> W;
59    /// Converts a storage word representation back into an element.
60    ///
61    /// For signed integers, this reverses the ZigZag encoding.
62    fn from_word(word: W) -> Self;
63}
64
65/// Macro to implement [`Storable`] for unsigned integer types.
66///
67/// This implementation is a direct, lossless cast between the unsigned
68/// element type and the storage word type `W`.
69macro_rules! impl_storable_for_unsigned {
70    ($($T:ty),*) => {$(
71        impl<W> Storable<W> for $T
72        where
73            W: Word + TryFrom<$T>,
74            W: TryInto<$T>,
75        {
76            #[inline(always)]
77            fn into_word(self) -> W {
78                self.try_into().unwrap_or_else(|_| panic!("BUG: T -> W conversion failed."))
79            }
80
81            #[inline(always)]
82            fn from_word(word: W) -> Self {
83                word.try_into().unwrap_or_else(|_| {
84                    panic!("BUG: W -> T conversion failed. Logic error in FixedVec's bit manipulation.")
85                })
86            }
87        }
88    )*};
89}
90
91/// Macro to implement [`Storable`] for signed integer types using ZigZag encoding.
92///
93/// ZigZag encoding maps signed integers to unsigned integers in a way that is
94/// efficient for variable-length encoding but is also used here to ensure that
95/// small signed values (positive or negative) map to small unsigned values.
96macro_rules! impl_storable_for_signed {
97    ($($T:ty),*) => {$(
98        impl<W> Storable<W> for $T
99        where
100            W: Word,
101            <$T as SignedInt>::UnsignedInt: TryInto<W>,
102            W: TryInto<<$T as SignedInt>::UnsignedInt>,
103        {
104            #[inline(always)]
105            fn into_word(self) -> W {
106                self.to_nat().try_into().unwrap_or_else(|_| panic!("BUG: Signed -> Unsigned -> W conversion failed."))
107            }
108
109            #[inline(always)]
110            fn from_word(word: W) -> Self {
111                let unsigned_val: <$T as SignedInt>::UnsignedInt =
112                    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);