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