Skip to main content

planck_pack_core/
radix.rs

1use crate::error::DecodeError;
2use crate::traits::Packable;
3
4// --- bool ---
5
6impl Packable for bool {
7    const RADIX: u128 = 2;
8
9    fn to_ordinal(&self) -> u128 {
10        *self as u128
11    }
12
13    fn from_ordinal(ord: u128) -> Result<Self, DecodeError> {
14        match ord {
15            0 => Ok(false),
16            1 => Ok(true),
17            _ => Err(DecodeError::OrdinalOutOfRange {
18                ordinal: ord,
19                radix: 2,
20            }),
21        }
22    }
23}
24
25// --- Unit type ---
26
27impl Packable for () {
28    const RADIX: u128 = 1;
29
30    fn to_ordinal(&self) -> u128 {
31        0
32    }
33
34    fn from_ordinal(ord: u128) -> Result<Self, DecodeError> {
35        if ord == 0 {
36            Ok(())
37        } else {
38            Err(DecodeError::OrdinalOutOfRange {
39                ordinal: ord,
40                radix: 1,
41            })
42        }
43    }
44}
45
46// --- Unsigned integers ---
47
48macro_rules! impl_packable_unsigned {
49    ($ty:ty, $radix:expr) => {
50        impl Packable for $ty {
51            const RADIX: u128 = $radix;
52
53            fn to_ordinal(&self) -> u128 {
54                *self as u128
55            }
56
57            fn from_ordinal(ord: u128) -> Result<Self, DecodeError> {
58                if ord < $radix {
59                    Ok(ord as $ty)
60                } else {
61                    Err(DecodeError::OrdinalOutOfRange {
62                        ordinal: ord,
63                        radix: $radix,
64                    })
65                }
66            }
67        }
68    };
69}
70
71impl_packable_unsigned!(u8, 256);
72impl_packable_unsigned!(u16, 65536);
73impl_packable_unsigned!(u32, 1 << 32);
74impl_packable_unsigned!(u64, 1 << 64);
75
76// --- Signed integers ---
77
78macro_rules! impl_packable_signed {
79    ($ty:ty, $unsigned:ty, $radix:expr, $offset:expr) => {
80        impl Packable for $ty {
81            const RADIX: u128 = $radix;
82
83            fn to_ordinal(&self) -> u128 {
84                (*self as $unsigned) as u128
85            }
86
87            fn from_ordinal(ord: u128) -> Result<Self, DecodeError> {
88                if ord < $radix {
89                    Ok((ord as $unsigned) as $ty)
90                } else {
91                    Err(DecodeError::OrdinalOutOfRange {
92                        ordinal: ord,
93                        radix: $radix,
94                    })
95                }
96            }
97        }
98    };
99}
100
101impl_packable_signed!(i8, u8, 256, 128);
102impl_packable_signed!(i16, u16, 65536, 32768);
103impl_packable_signed!(i32, u32, 1 << 32, 1 << 31);
104impl_packable_signed!(i64, u64, 1 << 64, 1 << 63);
105
106// --- Option<T> ---
107
108impl<T: Packable> Packable for Option<T> {
109    const RADIX: u128 = T::RADIX + 1;
110
111    fn to_ordinal(&self) -> u128 {
112        match self {
113            None => 0,
114            Some(v) => v.to_ordinal() + 1,
115        }
116    }
117
118    fn from_ordinal(ord: u128) -> Result<Self, DecodeError> {
119        if ord == 0 {
120            Ok(None)
121        } else if ord <= T::RADIX {
122            Ok(Some(T::from_ordinal(ord - 1)?))
123        } else {
124            Err(DecodeError::OrdinalOutOfRange {
125                ordinal: ord,
126                radix: Self::RADIX,
127            })
128        }
129    }
130}
131
132#[cfg(test)]
133mod tests {
134    use super::*;
135
136    #[test]
137    fn bool_round_trip() {
138        assert_eq!(bool::from_ordinal(false.to_ordinal()).unwrap(), false);
139        assert_eq!(bool::from_ordinal(true.to_ordinal()).unwrap(), true);
140        assert!(bool::from_ordinal(2).is_err());
141    }
142
143    #[test]
144    fn u8_round_trip() {
145        for v in 0..=255u8 {
146            assert_eq!(u8::from_ordinal(v.to_ordinal()).unwrap(), v);
147        }
148        assert!(u8::from_ordinal(256).is_err());
149    }
150
151    #[test]
152    fn i8_round_trip() {
153        for v in i8::MIN..=i8::MAX {
154            assert_eq!(i8::from_ordinal(v.to_ordinal()).unwrap(), v);
155        }
156    }
157
158    #[test]
159    fn option_round_trip() {
160        let none: Option<bool> = None;
161        assert_eq!(Option::<bool>::from_ordinal(none.to_ordinal()).unwrap(), None);
162        assert_eq!(
163            Option::<bool>::from_ordinal(Some(true).to_ordinal()).unwrap(),
164            Some(true)
165        );
166        assert_eq!(Option::<bool>::RADIX, 3);
167    }
168
169    #[test]
170    fn unit_round_trip() {
171        assert_eq!(<()>::from_ordinal(().to_ordinal()).unwrap(), ());
172        assert!(<()>::from_ordinal(1).is_err());
173    }
174}