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);