qudit_core/utils/
storage.rs

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