Skip to main content

qudit_core/utils/
storage.rs

1use serde::{Serialize, de::DeserializeOwned};
2
3/// A trait for types that can be stored compactly by attempting to fit them into smaller storage types.
4///
5/// This trait is designed for scenarios where values are often, but not always, small enough to fit in a more compact
6/// representation (e.g., storing i32 values that typically fit in i8 to save memory).
7pub trait CompactStorage: Copy + 'static {
8    /// The compact storage type used for inline representation
9    type InlineType: Copy + Default + 'static + Serialize + DeserializeOwned;
10    /// Whether conversion to inline type is guaranteed to never fail
11    const CONVERSION_INFALLIBLE: bool;
12
13    /// Attempts to convert the value to its compact inline representation.
14    /// Returns `Err(value)` if the value cannot fit in the inline type.
15    fn to_inline(value: Self) -> Result<Self::InlineType, Self>;
16    /// Converts from the compact inline representation back to the original type.
17    /// This conversion is always infallible as we're expanding to a larger type.
18    fn from_inline(value: Self::InlineType) -> Self;
19
20    /// Converts to inline representation without bounds checking.
21    ///
22    /// # Safety
23    /// The caller must guarantee that the value fits within the bounds of `InlineType`.
24    /// Violating this contract may result in data truncation or undefined behavior.
25    fn to_inline_unchecked(value: Self) -> Self::InlineType;
26}
27
28// Trait implementations for signed integer types (use i8 storage)
29impl CompactStorage for i8 {
30    type InlineType = i8;
31    const CONVERSION_INFALLIBLE: bool = true;
32
33    #[inline(always)]
34    fn to_inline(value: Self) -> Result<Self::InlineType, Self> {
35        Ok(value)
36    }
37
38    #[inline(always)]
39    fn from_inline(value: Self::InlineType) -> Self {
40        value
41    }
42
43    #[inline(always)]
44    fn to_inline_unchecked(value: Self) -> Self::InlineType {
45        value
46    }
47}
48
49impl CompactStorage for i16 {
50    type InlineType = i8;
51    const CONVERSION_INFALLIBLE: bool = false;
52
53    #[inline]
54    fn to_inline(value: Self) -> Result<Self::InlineType, Self> {
55        if value >= i8::MIN as i16 && value <= i8::MAX as i16 {
56            Ok(value as i8)
57        } else {
58            Err(value)
59        }
60    }
61
62    #[inline(always)]
63    fn from_inline(value: Self::InlineType) -> Self {
64        value as i16
65    }
66
67    #[inline(always)]
68    fn to_inline_unchecked(value: Self) -> Self::InlineType {
69        value as i8
70    }
71}
72
73impl CompactStorage for i32 {
74    type InlineType = i8;
75    const CONVERSION_INFALLIBLE: bool = false;
76
77    #[inline]
78    fn to_inline(value: Self) -> Result<Self::InlineType, Self> {
79        if value >= i8::MIN as i32 && value <= i8::MAX as i32 {
80            Ok(value as i8)
81        } else {
82            Err(value)
83        }
84    }
85    #[inline(always)]
86    fn from_inline(value: Self::InlineType) -> Self {
87        value as i32
88    }
89
90    #[inline(always)]
91    fn to_inline_unchecked(value: Self) -> Self::InlineType {
92        value as i8
93    }
94}
95
96impl CompactStorage for i64 {
97    type InlineType = i8;
98    const CONVERSION_INFALLIBLE: bool = false;
99
100    #[inline]
101    fn to_inline(value: Self) -> Result<Self::InlineType, Self> {
102        if value >= i8::MIN as i64 && value <= i8::MAX as i64 {
103            Ok(value as i8)
104        } else {
105            Err(value)
106        }
107    }
108
109    #[inline(always)]
110    fn from_inline(value: Self::InlineType) -> Self {
111        value as i64
112    }
113
114    #[inline(always)]
115    fn to_inline_unchecked(value: Self) -> Self::InlineType {
116        value as i8
117    }
118}
119
120impl CompactStorage for i128 {
121    type InlineType = i8;
122    const CONVERSION_INFALLIBLE: bool = false;
123
124    #[inline]
125    fn to_inline(value: Self) -> Result<Self::InlineType, Self> {
126        if value >= i8::MIN as i128 && value <= i8::MAX as i128 {
127            Ok(value as i8)
128        } else {
129            Err(value)
130        }
131    }
132
133    #[inline(always)]
134    fn from_inline(value: Self::InlineType) -> Self {
135        value as i128
136    }
137
138    #[inline(always)]
139    fn to_inline_unchecked(value: Self) -> Self::InlineType {
140        value as i8
141    }
142}
143
144impl CompactStorage for isize {
145    type InlineType = i8;
146    const CONVERSION_INFALLIBLE: bool = false;
147
148    #[inline]
149    fn to_inline(value: Self) -> Result<Self::InlineType, Self> {
150        if value >= i8::MIN as isize && value <= i8::MAX as isize {
151            Ok(value as i8)
152        } else {
153            Err(value)
154        }
155    }
156
157    #[inline(always)]
158    fn from_inline(value: Self::InlineType) -> Self {
159        value as isize
160    }
161
162    #[inline(always)]
163    fn to_inline_unchecked(value: Self) -> Self::InlineType {
164        value as i8
165    }
166}
167
168// Trait implementations for unsigned integer types (use u8 storage)
169impl CompactStorage for u8 {
170    type InlineType = u8;
171    const CONVERSION_INFALLIBLE: bool = true;
172
173    #[inline(always)]
174    fn to_inline(value: Self) -> Result<Self::InlineType, Self> {
175        Ok(value)
176    }
177
178    #[inline(always)]
179    fn from_inline(value: Self::InlineType) -> Self {
180        value
181    }
182
183    #[inline(always)]
184    fn to_inline_unchecked(value: Self) -> Self::InlineType {
185        value
186    }
187}
188
189impl CompactStorage for u16 {
190    type InlineType = u8;
191    const CONVERSION_INFALLIBLE: bool = false;
192
193    #[inline]
194    fn to_inline(value: Self) -> Result<Self::InlineType, Self> {
195        if value <= u8::MAX as u16 {
196            Ok(value as u8)
197        } else {
198            Err(value)
199        }
200    }
201
202    #[inline(always)]
203    fn from_inline(value: Self::InlineType) -> Self {
204        value as u16
205    }
206
207    #[inline(always)]
208    fn to_inline_unchecked(value: Self) -> Self::InlineType {
209        value as u8
210    }
211}
212
213impl CompactStorage for u32 {
214    type InlineType = u8;
215    const CONVERSION_INFALLIBLE: bool = false;
216
217    #[inline]
218    fn to_inline(value: Self) -> Result<Self::InlineType, Self> {
219        if value <= u8::MAX as u32 {
220            Ok(value as u8)
221        } else {
222            Err(value)
223        }
224    }
225
226    #[inline(always)]
227    fn from_inline(value: Self::InlineType) -> Self {
228        value as u32
229    }
230
231    #[inline(always)]
232    fn to_inline_unchecked(value: Self) -> Self::InlineType {
233        value as u8
234    }
235}
236
237impl CompactStorage for u64 {
238    type InlineType = u8;
239    const CONVERSION_INFALLIBLE: bool = false;
240
241    #[inline]
242    fn to_inline(value: Self) -> Result<Self::InlineType, Self> {
243        if value <= u8::MAX as u64 {
244            Ok(value as u8)
245        } else {
246            Err(value)
247        }
248    }
249
250    #[inline(always)]
251    fn from_inline(value: Self::InlineType) -> Self {
252        value as u64
253    }
254
255    #[inline(always)]
256    fn to_inline_unchecked(value: Self) -> Self::InlineType {
257        value as u8
258    }
259}
260
261impl CompactStorage for u128 {
262    type InlineType = u8;
263    const CONVERSION_INFALLIBLE: bool = false;
264
265    #[inline]
266    fn to_inline(value: Self) -> Result<Self::InlineType, Self> {
267        if value <= u8::MAX as u128 {
268            Ok(value as u8)
269        } else {
270            Err(value)
271        }
272    }
273
274    #[inline(always)]
275    fn from_inline(value: Self::InlineType) -> Self {
276        value as u128
277    }
278
279    #[inline(always)]
280    fn to_inline_unchecked(value: Self) -> Self::InlineType {
281        value as u8
282    }
283}
284
285impl CompactStorage for usize {
286    type InlineType = u8;
287    const CONVERSION_INFALLIBLE: bool = false;
288
289    #[inline]
290    fn to_inline(value: Self) -> Result<Self::InlineType, Self> {
291        if value <= u8::MAX as usize {
292            Ok(value as u8)
293        } else {
294            Err(value)
295        }
296    }
297
298    #[inline(always)]
299    fn from_inline(value: Self::InlineType) -> Self {
300        value as usize
301    }
302
303    #[inline(always)]
304    fn to_inline_unchecked(value: Self) -> Self::InlineType {
305        value as u8
306    }
307}
308
309#[cfg(test)]
310mod tests {
311    use super::*;
312
313    // Test infallible types (i8, u8)
314    #[test]
315    fn test_infallible_conversions() {
316        #![allow(clippy::assertions_on_constants)]
317        assert!(i8::CONVERSION_INFALLIBLE);
318        assert!(u8::CONVERSION_INFALLIBLE);
319        assert_eq!(i8::to_inline(42), Ok(42));
320        assert_eq!(u8::to_inline(42), Ok(42));
321    }
322
323    // Test fallible signed types
324    #[test]
325    fn test_signed_conversions() {
326        // Test within range
327        assert_eq!(i16::to_inline(42), Ok(42));
328        assert_eq!(i32::to_inline(-42), Ok(-42));
329        assert_eq!(i64::to_inline(127), Ok(127));
330        assert_eq!(i128::to_inline(-128), Ok(-128));
331
332        // Test out of range
333        assert!(i16::to_inline(200).is_err());
334        assert!(i32::to_inline(1000).is_err());
335        assert!(i64::to_inline(i64::MAX).is_err());
336        assert!(i128::to_inline(i128::MAX).is_err());
337    }
338
339    // Test fallible unsigned types
340    #[test]
341    fn test_unsigned_conversions() {
342        // Test within range
343        assert_eq!(u16::to_inline(42), Ok(42));
344        assert_eq!(u32::to_inline(255), Ok(255));
345        assert_eq!(u64::to_inline(100), Ok(100));
346        assert_eq!(u128::to_inline(200), Ok(200));
347
348        // Test out of range
349        assert!(u16::to_inline(300).is_err());
350        assert!(u32::to_inline(1000).is_err());
351        assert!(u64::to_inline(u64::MAX).is_err());
352        assert!(u128::to_inline(u128::MAX).is_err());
353    }
354
355    // Test round-trip conversions
356    #[test]
357    fn test_round_trip() {
358        assert_eq!(i128::from_inline(42), 42i128);
359        assert_eq!(u128::from_inline(255), 255u128);
360    }
361}