musli_utils/
options.rs

1//! Serialization options.
2
3/// Type encapsulating a static flavor of an encoding.
4pub struct OptionsBuilder(Options);
5
6const DEFAULT: Options = (ByteOrder::NATIVE as Options) << BYTEORDER_BIT;
7
8/// Start building new options.
9///
10/// Call [`OptionsBuilder::build`] to construct them.
11pub const fn new() -> OptionsBuilder {
12    OptionsBuilder(DEFAULT)
13}
14
15/// Type encapsulating a static flavor of an encoding.
16///
17/// Note: despite being made up of a primitive type, this cannot be serialized
18/// and correctly re-used.
19///
20/// Making assumptions about its layout might lead to unspecified behavior
21/// during encoding. Only use this type through the provided API.
22pub type Options = u128;
23
24const BYTEORDER_BIT: Options = 0;
25const INTEGER_BIT: Options = 1;
26const LENGTH_BIT: Options = 2;
27const MAP_KEYS_AS_NUMBERS_BIT: Options = 3;
28const FLOAT_BIT: Options = 8;
29const LENGTH_WIDTH_BIT: Options = 16;
30
31impl OptionsBuilder {
32    /// Indicates if an integer serialization should be variable.
33    #[inline(always)]
34    pub const fn with_integer(self, integer: Integer) -> Self {
35        const MASK: Options = 0b11 << INTEGER_BIT;
36        Self((self.0 & !MASK) | ((integer as Options) << INTEGER_BIT))
37    }
38
39    /// Indicates the configuration of float serialization.
40    #[inline(always)]
41    pub const fn with_float(self, float: Float) -> Self {
42        const MASK: Options = 0b11 << FLOAT_BIT;
43        Self((self.0 & !MASK) | ((float as Options) << FLOAT_BIT))
44    }
45
46    /// Specify which byte order to use, if that's relevant.
47    #[inline(always)]
48    pub const fn with_byte_order(self, byte_order: ByteOrder) -> Self {
49        const MASK: Options = 0b1 << BYTEORDER_BIT;
50        Self((self.0 & !MASK) | ((byte_order as Options) << BYTEORDER_BIT))
51    }
52
53    /// Specify how lengths should be serialized.
54    #[inline(always)]
55    pub const fn with_length(self, length: Integer) -> Self {
56        const MASK: Options = 0b1 << LENGTH_BIT;
57        Self((self.0 & !MASK) | ((length as Options) << LENGTH_BIT))
58    }
59
60    /// Allows for treating string keys as numbers.
61    #[inline(always)]
62    pub const fn with_map_keys_as_numbers(self, value: bool) -> Self {
63        const MASK: Options = 0b1 << MAP_KEYS_AS_NUMBERS_BIT;
64        let value = if value { 1 } else { 0 };
65        Self((self.0 & !MASK) | (value << MAP_KEYS_AS_NUMBERS_BIT))
66    }
67
68    /// If length is set to [`Integer::Fixed`], specify the width of the length.
69    #[inline(always)]
70    pub const fn with_length_width(self, width: Width) -> Self {
71        const MASK: Options = 0b11 << LENGTH_WIDTH_BIT;
72        let this = self.with_length(Integer::Fixed);
73        Self((this.0 & !MASK) | ((width as Options) << LENGTH_WIDTH_BIT))
74    }
75
76    /// Build a flavor.
77    #[inline(always)]
78    pub const fn build(self) -> Options {
79        self.0
80    }
81}
82
83#[doc(hidden)]
84pub const fn integer<const OPT: Options>() -> Integer {
85    match (OPT >> INTEGER_BIT) & 0b1 {
86        0 => Integer::Variable,
87        _ => Integer::Fixed,
88    }
89}
90
91#[doc(hidden)]
92pub const fn float<const OPT: Options>() -> Float {
93    match (OPT >> FLOAT_BIT) & 0b11 {
94        0 => Float::Integer,
95        1 => Float::Variable,
96        _ => Float::Fixed,
97    }
98}
99
100#[doc(hidden)]
101pub const fn length<const OPT: Options>() -> Integer {
102    match (OPT >> LENGTH_BIT) & 0b1 {
103        0 => Integer::Variable,
104        _ => Integer::Fixed,
105    }
106}
107
108#[doc(hidden)]
109pub const fn length_width<const OPT: Options>() -> Width {
110    match (OPT >> LENGTH_WIDTH_BIT) & 0b11 {
111        0 => Width::U8,
112        1 => Width::U16,
113        2 => Width::U32,
114        _ => Width::U64,
115    }
116}
117
118#[doc(hidden)]
119pub const fn byteorder<const OPT: Options>() -> ByteOrder {
120    match (OPT >> BYTEORDER_BIT) & 0b1 {
121        0 => ByteOrder::LittleEndian,
122        _ => ByteOrder::BigEndian,
123    }
124}
125
126#[doc(hidden)]
127pub const fn is_map_keys_as_numbers<const OPT: Options>() -> bool {
128    ((OPT >> MAP_KEYS_AS_NUMBERS_BIT) & 0b1) == 1
129}
130
131/// Integer serialization mode.
132#[cfg_attr(test, derive(Debug, PartialEq))]
133#[repr(u8)]
134#[non_exhaustive]
135pub enum Integer {
136    /// Variable number encoding.
137    Variable = 0,
138    /// Fixed number encoding.
139    Fixed = 1,
140}
141
142/// Float serialization mode.
143#[cfg_attr(test, derive(Debug, PartialEq))]
144#[repr(u8)]
145#[non_exhaustive]
146pub enum Float {
147    /// Use the same serialization as integers, after coercing the bits of a
148    /// float into an unsigned integer.
149    Integer = 0,
150    /// Use variable float encoding.
151    Variable = 1,
152    /// Use fixed float encoding.
153    Fixed = 2,
154}
155
156/// Byte order.
157#[derive(PartialEq, Eq)]
158#[cfg_attr(test, derive(Debug))]
159#[repr(u8)]
160#[non_exhaustive]
161pub enum ByteOrder {
162    /// Little endian byte order.
163    LittleEndian = 0,
164    /// Big endian byte order.
165    BigEndian = 1,
166}
167
168impl ByteOrder {
169    /// The native byte order.
170    pub const NATIVE: Self = if cfg!(target_endian = "little") {
171        Self::LittleEndian
172    } else {
173        Self::BigEndian
174    };
175
176    /// The network byte order.
177    pub const NETWORK: Self = Self::BigEndian;
178}
179
180#[doc(hidden)]
181#[macro_export]
182macro_rules! width_arm {
183    ($width:expr, $macro:path) => {
184        match $width {
185            $crate::options::Width::U8 => {
186                $macro!(u8)
187            }
188            $crate::options::Width::U16 => {
189                $macro!(u16)
190            }
191            $crate::options::Width::U32 => {
192                $macro!(u32)
193            }
194            _ => {
195                $macro!(u64)
196            }
197        }
198    };
199}
200
201/// The width of a numerical type.
202#[derive(Clone, Copy)]
203#[cfg_attr(test, derive(Debug, PartialEq))]
204#[repr(u8)]
205#[non_exhaustive]
206pub enum Width {
207    /// 8 bit width.
208    U8 = 0,
209    /// 16 bit width.
210    U16 = 1,
211    /// 32 bit width.
212    U32 = 2,
213    /// 64 bit width.
214    U64 = 3,
215}
216
217#[test]
218fn test_builds() {
219    macro_rules! assert_or_default {
220        ($expr:expr, $test:expr, $default:expr, ()) => {
221            assert_eq!(
222                $test,
223                $default,
224                "{}: Expected default value for {}",
225                stringify!($expr),
226                stringify!($test)
227            );
228        };
229
230        ($expr:expr, $test:expr, $_default:expr, ($expected:expr)) => {
231            assert_eq!(
232                $test,
233                $expected,
234                "{}: Expected custom value for {}",
235                stringify!($expr),
236                stringify!($test)
237            );
238        };
239    }
240
241    macro_rules! test_case {
242        ($expr:expr => {
243            $(byteorder = $byteorder:expr,)?
244            $(integer = $integer:expr,)?
245            $(float = $float:expr,)?
246            $(length = $length:expr,)?
247            $(length_width = $length_width:expr,)?
248            $(is_map_keys_as_numbers = $is_map_keys_as_numbers:expr,)?
249        }) => {{
250            const O: Options = $expr.build();
251            assert_or_default!($expr, byteorder::<O>(), ByteOrder::NATIVE, ($($byteorder)?));
252            assert_or_default!($expr, integer::<O>(), Integer::Variable, ($($integer)?));
253            assert_or_default!($expr, float::<O>(), Float::Integer, ($($float)?));
254            assert_or_default!($expr, length::<O>(), Integer::Variable, ($($length)?));
255            assert_or_default!($expr, is_map_keys_as_numbers::<O>(), false, ($($is_map_keys_as_numbers)?));
256        }}
257    }
258
259    test_case! {
260        self::new() => {}
261    }
262
263    test_case! {
264        self::new().with_map_keys_as_numbers(true) => {
265            is_map_keys_as_numbers = true,
266        }
267    }
268
269    test_case! {
270        self::new().with_integer(Integer::Fixed) => {
271            integer = Integer::Fixed,
272        }
273    }
274
275    test_case! {
276        self::new().with_float(Float::Fixed) => {
277            float = Float::Fixed,
278        }
279    }
280
281    test_case! {
282        self::new().with_float(Float::Variable) => {
283            float = Float::Variable,
284        }
285    }
286
287    test_case! {
288        self::new().with_float(Float::Variable) => {
289            float = Float::Variable,
290        }
291    }
292
293    test_case! {
294        self::new().with_byte_order(ByteOrder::BigEndian) => {
295            byteorder = ByteOrder::BigEndian,
296        }
297    }
298
299    test_case! {
300        self::new().with_byte_order(ByteOrder::LittleEndian) => {
301            byteorder = ByteOrder::LittleEndian,
302        }
303    }
304
305    test_case! {
306        self::new().with_length_width(Width::U16) => {
307            length = Integer::Fixed,
308            length_width = Width::U16,
309        }
310    }
311
312    test_case! {
313        self::new().with_length_width(Width::U32) => {
314            length = Integer::Fixed,
315            length_width = Width::U32,
316        }
317    }
318
319    test_case! {
320        self::new().with_length_width(Width::U64) => {
321            length = Integer::Fixed,
322            length_width = Width::U64,
323        }
324    }
325}