compressed_intvec/variable/
traits.rs

1//! Core traits for the `variable` module.
2//!
3//! This module defines the [`Storable`] trait, which provides a generic
4//! abstraction over the integer types that can be stored in an [`IntVec`].
5//!
6//! [`IntVec`]: crate::variable::IntVec
7
8use dsi_bitstream::prelude::{ToInt, ToNat};
9
10/// A trait for types that can be stored in a variable-length compressed vector.
11///
12/// This trait provides a bidirectional, lossless conversion between a user-facing
13/// element type (e.g., `i32`, `u16`) and the `u64` representation required by the
14/// underlying compression codecs.
15///
16/// # Zig-Zag Encoding for Signed Integers
17///
18/// For signed integer types, this trait's implementation automatically applies
19/// **Zig-Zag encoding**. This is a transformation that maps signed
20/// integers to unsigned integers in a way that is efficient for variable-length
21/// compression.
22///
23/// It works by mapping small positive and negative numbers to small unsigned
24/// numbers, as shown below:
25///
26/// | Original Signed | Zig-Zag Unsigned |
27/// |-----------------|------------------|
28/// | 0               | 0                |
29/// | -1              | 1                |
30/// | 1               | 2                |
31/// | -2              | 3                |
32/// | 2               | 4                |
33/// | ...             | ...              |
34///
35/// This ensures that values close to zero, whether positive or negative, are
36/// represented by small unsigned integers, which can then be compressed into
37/// very few bits by the variable-length codecs. 
38pub trait Storable: Sized + Copy {
39    /// Converts the element into its `u64` storage representation.
40    ///
41    /// For unsigned types, this is a simple cast. For signed types, this
42    /// applies Zig-Zag encoding.
43    fn to_word(self) -> u64;
44    /// Converts a `u64` storage word back into the element type.
45    ///
46    /// For unsigned types, this is a simple cast. For signed types, this
47    /// decodes the Zig-Zag encoded value.
48    fn from_word(word: u64) -> Self;
49}
50
51macro_rules! impl_storable_for_unsigned {
52    ($($T:ty),*) => {$(
53        impl Storable for $T {
54            #[inline(always)]
55            fn to_word(self) -> u64 {
56                self as u64
57            }
58
59            #[inline(always)]
60            fn from_word(word: u64) -> Self {
61                word as Self
62            }
63        }
64    )*};
65}
66
67macro_rules! impl_storable_for_signed {
68    ($($T:ty),*) => {$(
69        impl Storable for $T {
70            #[inline(always)]
71            fn to_word(self) -> u64 {
72                self.to_nat().into()
73            }
74
75            #[inline(always)]
76            fn from_word(word: u64) -> Self {
77                ToInt::to_int(word)
78                    .try_into()
79                    .unwrap_or_else(|_| panic!("Value out of range for type"))
80            }
81        }
82    )*};
83}
84
85// Implement `Storable` for all primitive integer types.
86impl_storable_for_unsigned!(u8, u16, u32, u64);
87impl_storable_for_signed!(i8, i16, i32, i64);