musli/
options.rs

1//! Serialization options.
2
3use core::fmt;
4
5/// [`Options`] builder.
6pub struct Builder(Options);
7
8const DEFAULT: Options = (ByteOrder::Little as Options) << BYTEORDER_BIT;
9
10/// Start building new options.
11///
12/// Call [`Builder::build`] to construct them.
13#[inline]
14pub const fn new() -> Builder {
15    Builder(DEFAULT)
16}
17
18/// Construct a [`Builder`] from the raw underlying value of an [`Options`].
19///
20/// This can be used to modify a value at compile time.
21#[inline]
22pub const fn from_raw(value: Options) -> Builder {
23    Builder(value)
24}
25
26/// Type encapsulating a static options for an encoding.
27///
28/// Note: despite being made up of a primitive type, this cannot be serialized
29/// and correctly re-used. This is simply the case because of restrictions in
30/// constant evaluation.
31///
32/// Making assumptions about its layout might lead to unspecified behavior
33/// during encoding. Only use this type through the provided [`options`] APIs.
34///
35/// [`options`]: crate::options
36pub type Options = u32;
37
38const BYTEORDER_BIT: Options = 0;
39const INTEGER_BIT: Options = 4;
40const FLOAT_BIT: Options = 8;
41const LENGTH_BIT: Options = 12;
42const MAP_KEYS_AS_NUMBERS_BIT: Options = 16;
43
44impl Builder {
45    /// Indicates if an integer serialization should be variable.
46    ///
47    /// # Examples
48    ///
49    /// ```
50    /// use musli::options::{self, Integer, Options};
51    ///
52    /// const OPTIONS: Options = options::new().integer(Integer::Fixed).build();
53    /// ```
54    #[inline]
55    pub const fn integer(self, integer: Integer) -> Self {
56        const MASK: Options = Integer::MASK << INTEGER_BIT;
57        Self((self.0 & !MASK) | ((integer as Options) << INTEGER_BIT))
58    }
59
60    /// Indicates the configuration of float serialization.
61    ///
62    /// # Examples
63    ///
64    /// ```
65    /// use musli::options::{self, Float, Options};
66    ///
67    /// const OPTIONS: Options = options::new().float(Float::Fixed).build();
68    /// ```
69    #[inline]
70    pub const fn float(self, float: Float) -> Self {
71        const MASK: Options = Float::MASK << FLOAT_BIT;
72        Self((self.0 & !MASK) | ((float as Options) << FLOAT_BIT))
73    }
74
75    /// Specify which byte order to use.
76    ///
77    /// # Examples
78    ///
79    /// ```
80    /// use musli::options::{self, ByteOrder, Options};
81    ///
82    /// const OPTIONS: Options = options::new().byte_order(ByteOrder::Little).build();
83    /// ```
84    #[inline]
85    pub const fn byte_order(self, byte_order: ByteOrder) -> Self {
86        const MASK: Options = ByteOrder::MASK << BYTEORDER_BIT;
87        Self((self.0 & !MASK) | ((byte_order as Options) << BYTEORDER_BIT))
88    }
89
90    /// Specify that the [`ByteOrder::NATIVE`] byte order should be used.
91    ///
92    /// # Examples
93    ///
94    /// ```
95    /// use musli::options::{self, Width, Options};
96    ///
97    /// const OPTIONS: Options = options::new().native_byte_order().build();
98    /// ```
99    #[inline]
100    pub const fn native_byte_order(self) -> Self {
101        self.byte_order(ByteOrder::NATIVE)
102    }
103
104    /// Sets the way in which pointer-like `usize` and `isize` types are
105    /// encoded.
106    ///
107    /// # Examples
108    ///
109    /// ```
110    /// use musli::options::{self, Width, Options};
111    ///
112    /// const OPTIONS: Options = options::new().pointer(Width::U32).build();
113    /// ```
114    #[inline]
115    pub const fn pointer(self, width: Width) -> Self {
116        const MASK: Options = Width::MASK << LENGTH_BIT;
117        Self((self.0 & !MASK) | ((width as Options) << LENGTH_BIT))
118    }
119
120    /// Configured a format to use numbers as map keys.
121    ///
122    /// This options is used for an encoding such as JSON to allow for storing
123    /// numbers as string keys, since this would otherwise not be possible and
124    /// cause an error.
125    ///
126    /// # Examples
127    ///
128    /// ```
129    /// use musli::options::{self, Options};
130    ///
131    /// const OPTIONS: Options = options::new().map_keys_as_numbers().build();
132    /// ```
133    #[inline]
134    pub const fn map_keys_as_numbers(self) -> Self {
135        const MASK: Options = 0b1 << MAP_KEYS_AS_NUMBERS_BIT;
136        Self((self.0 & !MASK) | (1 << MAP_KEYS_AS_NUMBERS_BIT))
137    }
138
139    /// Configure the options to use fixed serialization.
140    ///
141    /// This causes numerical types to use the default fixed-length
142    /// serialization which is typically more efficient than variable-length
143    /// through [`variable()`] but is less compact.
144    ///
145    /// This is the same as calling [`integer(Integer::Fixed)`],
146    /// [`float(Float::Fixed)`], and [`pointer(Width::NATIVE)`].
147    ///
148    /// [`variable()`]: Builder::variable
149    /// [`integer(Integer::Fixed)`]: Builder::integer
150    /// [`float(Float::Fixed)`]: Builder::float
151    /// [`pointer(Width::NATIVE)`]: Builder::pointer
152    ///
153    /// # Examples
154    ///
155    /// ```
156    /// use musli::options::{self, Options};
157    ///
158    /// const OPTIONS: Options = options::new().fixed().build();
159    /// ```
160    #[inline]
161    pub const fn fixed(self) -> Self {
162        self.integer(Integer::Fixed)
163            .float(Float::Fixed)
164            .pointer(Width::NATIVE)
165    }
166
167    /// Configure the options to use variable serialization.
168    ///
169    /// This causes numerical types to use the default variable-length
170    /// serialization which is typically less efficient than fixed-length
171    /// through [`fixed()`] but is more compact.
172    ///
173    /// This is the same as calling [`integer(Integer::Variable)`],
174    /// [`float(Float::Variable)`], and [`pointer(Width::Variable)`].
175    ///
176    /// [`fixed()`]: Builder::fixed
177    /// [`integer(Integer::Variable)`]: Builder::integer
178    /// [`float(Float::Variable)`]: Builder::float
179    /// [`pointer(Width::Variable)`]: Builder::pointer
180    ///
181    /// # Examples
182    ///
183    /// ```
184    /// use musli::options::{self, Options};
185    ///
186    /// const OPTIONS: Options = options::new().variable().build();
187    /// ```
188    #[inline]
189    pub const fn variable(self) -> Self {
190        self.integer(Integer::Variable)
191            .float(Float::Variable)
192            .pointer(Width::Variable)
193    }
194
195    /// Built an options builder into a constant.
196    ///
197    /// # Examples
198    ///
199    /// ```
200    /// use musli::options::{self, Options};
201    ///
202    /// const OPTIONS: Options = options::new().variable().build();
203    /// ```
204    #[inline]
205    pub const fn build(self) -> Options {
206        self.0
207    }
208}
209
210impl fmt::Debug for Builder {
211    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
212        f.debug_struct("Builder")
213            .field("byteorder", &byteorder_value(self.0))
214            .field("integer", &integer_value(self.0))
215            .field("float", &float_value(self.0))
216            .field("length", &length_value(self.0))
217            .field(
218                "is_map_keys_as_numbers",
219                &is_map_keys_as_numbers_value(self.0),
220            )
221            .finish()
222    }
223}
224
225#[cfg(any(
226    feature = "storage",
227    feature = "wire",
228    feature = "descriptive",
229    feature = "json",
230    feature = "value"
231))]
232#[inline]
233pub(crate) const fn integer<const OPT: Options>() -> Integer {
234    integer_value(OPT)
235}
236
237#[inline]
238const fn integer_value(opt: Options) -> Integer {
239    match (opt >> INTEGER_BIT) & 0b1 {
240        0 => Integer::Variable,
241        _ => Integer::Fixed,
242    }
243}
244
245#[cfg(any(
246    feature = "storage",
247    feature = "wire",
248    feature = "descriptive",
249    feature = "json",
250    feature = "value"
251))]
252#[inline]
253pub(crate) const fn float<const OPT: Options>() -> Float {
254    float_value(OPT)
255}
256
257#[inline]
258const fn float_value(opt: Options) -> Float {
259    match (opt >> FLOAT_BIT) & 0b11 {
260        0b00 => Float::Integer,
261        0b01 => Float::Variable,
262        0b10 => Float::Fixed,
263        _ => Float::Pad0,
264    }
265}
266
267#[cfg(any(
268    feature = "storage",
269    feature = "wire",
270    feature = "descriptive",
271    feature = "json",
272    feature = "value"
273))]
274#[inline]
275pub(crate) const fn length<const OPT: Options>() -> Width {
276    length_value(OPT)
277}
278
279#[inline]
280const fn length_value(opt: Options) -> Width {
281    match (opt >> LENGTH_BIT) & 0b111 {
282        0b000 => Width::Variable,
283        0b001 => Width::U8,
284        0b010 => Width::U16,
285        0b011 => Width::U32,
286        0b100 => Width::U64,
287        0b101 => Width::Pad0,
288        0b110 => Width::Pad1,
289        _ => Width::Pad2,
290    }
291}
292
293#[cfg(any(
294    feature = "storage",
295    feature = "wire",
296    feature = "descriptive",
297    feature = "json",
298    feature = "value"
299))]
300#[inline]
301pub(crate) const fn byteorder<const OPT: Options>() -> ByteOrder {
302    byteorder_value(OPT)
303}
304
305#[inline]
306pub(crate) const fn byteorder_value(opt: Options) -> ByteOrder {
307    match (opt >> BYTEORDER_BIT) & 0b1 {
308        0 => ByteOrder::Little,
309        _ => ByteOrder::Big,
310    }
311}
312
313#[cfg(feature = "value")]
314#[inline]
315pub(crate) const fn is_map_keys_as_numbers<const OPT: Options>() -> bool {
316    is_map_keys_as_numbers_value(OPT)
317}
318
319const fn is_map_keys_as_numbers_value(opt: Options) -> bool {
320    ((opt >> MAP_KEYS_AS_NUMBERS_BIT) & 0b1) == 1
321}
322
323#[cfg(any(
324    feature = "storage",
325    feature = "wire",
326    feature = "descriptive",
327    feature = "value"
328))]
329pub(crate) const fn is_native_fixed<const OPT: Options>() -> bool {
330    matches!(
331        (integer::<OPT>(), float::<OPT>(), length::<OPT>(),),
332        (Integer::Fixed, Float::Fixed, Width::NATIVE)
333    )
334}
335
336/// Integer serialization mode.
337#[derive(Debug, PartialEq, Eq)]
338#[repr(u8)]
339#[non_exhaustive]
340pub enum Integer {
341    /// Variable number encoding.
342    Variable = 0b0,
343    /// Fixed number encoding.
344    Fixed = 0b1,
345}
346
347impl Integer {
348    const MASK: Options = 0b1;
349}
350
351/// Float serialization mode.
352#[derive(Debug, PartialEq, Eq)]
353#[repr(u8)]
354#[non_exhaustive]
355pub enum Float {
356    /// Use the same serialization as integers, after coercing the bits of a
357    /// float into an unsigned integer.
358    Integer = 0b00,
359    /// Use variable float encoding.
360    Variable = 0b01,
361    /// Use fixed float encoding.
362    Fixed = 0b10,
363    /// Padding.
364    #[doc(hidden)]
365    Pad0 = 0b11,
366}
367
368impl Float {
369    const MASK: Options = 0b11;
370}
371
372/// Byte order to use when encoding numbers.
373///
374/// By default, this is the [`ByteOrder::NATIVE`] byte order of the target
375/// platform.
376#[derive(Debug, PartialEq, Eq)]
377#[repr(u8)]
378#[non_exhaustive]
379pub enum ByteOrder {
380    /// Little endian byte order.
381    Little = 0,
382    /// Big endian byte order.
383    Big = 1,
384}
385
386impl ByteOrder {
387    const MASK: Options = 0b1;
388
389    /// The native byte order.
390    ///
391    /// [`Little`] for little and [`Big`] for big endian platforms.
392    ///
393    /// [`Little`]: ByteOrder::Little
394    /// [`Big`]: ByteOrder::Big
395    pub const NATIVE: Self = if cfg!(target_endian = "little") {
396        Self::Little
397    } else {
398        Self::Big
399    };
400
401    /// The network byte order.
402    ///
403    /// This is the same as [`Big`].
404    ///
405    /// [`Big`]: ByteOrder::Big
406    pub const NETWORK: Self = Self::Big;
407}
408
409#[doc(hidden)]
410#[cfg(any(
411    feature = "storage",
412    feature = "wire",
413    feature = "descriptive",
414    feature = "value"
415))]
416macro_rules! width_arm {
417    ($width:expr, $macro:path) => {
418        match $width {
419            $crate::options::Width::U8 => {
420                $macro!(u8)
421            }
422            $crate::options::Width::U16 => {
423                $macro!(u16)
424            }
425            $crate::options::Width::U32 => {
426                $macro!(u32)
427            }
428            _ => {
429                $macro!(u64)
430            }
431        }
432    };
433}
434
435#[cfg(any(
436    feature = "storage",
437    feature = "wire",
438    feature = "descriptive",
439    feature = "value"
440))]
441pub(crate) use width_arm;
442
443/// The width of a numerical type.
444#[derive(Clone, Copy, Debug, PartialEq, Eq)]
445#[repr(u8)]
446#[non_exhaustive]
447pub enum Width {
448    /// Use a variable width encoding.
449    Variable = 0b000,
450    /// 8 bit width.
451    U8 = 0b001,
452    /// 16 bit width.
453    U16 = 0b010,
454    /// 32 bit width.
455    U32 = 0b011,
456    /// 64 bit width.
457    U64 = 0b100,
458    /// Padding.
459    #[doc(hidden)]
460    Pad0 = 0b101,
461    /// Padding.
462    #[doc(hidden)]
463    Pad1 = 0b110,
464    /// Padding.
465    #[doc(hidden)]
466    Pad2 = 0b111,
467}
468
469impl Width {
470    const MASK: Options = 0b111;
471
472    /// The native width.
473    ///
474    /// This is the width of the target platform's native integer type.
475    pub const NATIVE: Self = const {
476        if cfg!(target_pointer_width = "64") {
477            Self::U64
478        } else if cfg!(target_pointer_width = "32") {
479            Self::U32
480        } else if cfg!(target_pointer_width = "16") {
481            Self::U16
482        } else {
483            panic!("Unsupported target pointer width")
484        }
485    };
486}
487
488#[test]
489fn test_builds() {
490    macro_rules! assert_or_default {
491        ($expr:expr, $test:expr, $default:expr, ()) => {
492            assert_eq!(
493                $test,
494                $default,
495                "{}: Expected default value for {}",
496                stringify!($expr),
497                stringify!($test)
498            );
499        };
500
501        ($expr:expr, $test:expr, $_default:expr, ($expected:expr)) => {
502            assert_eq!(
503                $test,
504                $expected,
505                "{}: Expected custom value for {}",
506                stringify!($expr),
507                stringify!($test)
508            );
509        };
510    }
511
512    macro_rules! test_case {
513        ($expr:expr => {
514            $(byteorder = $byteorder:expr,)?
515            $(integer = $integer:expr,)?
516            $(float = $float:expr,)?
517            $(length = $length:expr,)?
518            $(is_map_keys_as_numbers = $is_map_keys_as_numbers:expr,)?
519        }) => {{
520            const O: Options = $expr.build();
521            assert_or_default!($expr, byteorder::<O>(), ByteOrder::Little, ($($byteorder)?));
522            assert_or_default!($expr, integer::<O>(), Integer::Variable, ($($integer)?));
523            assert_or_default!($expr, float::<O>(), Float::Integer, ($($float)?));
524            assert_or_default!($expr, length::<O>(), Width::Variable, ($($length)?));
525            assert_or_default!($expr, is_map_keys_as_numbers::<O>(), false, ($($is_map_keys_as_numbers)?));
526        }}
527    }
528
529    test_case! {
530        self::new() => {}
531    }
532
533    test_case! {
534        self::new().map_keys_as_numbers() => {
535            is_map_keys_as_numbers = true,
536        }
537    }
538
539    test_case! {
540        self::new().integer(Integer::Fixed) => {
541            integer = Integer::Fixed,
542        }
543    }
544
545    test_case! {
546        self::new().float(Float::Fixed) => {
547            float = Float::Fixed,
548        }
549    }
550
551    test_case! {
552        self::new().float(Float::Variable) => {
553            float = Float::Variable,
554        }
555    }
556
557    test_case! {
558        self::new().float(Float::Variable) => {
559            float = Float::Variable,
560        }
561    }
562
563    test_case! {
564        self::new().byte_order(ByteOrder::Big) => {
565            byteorder = ByteOrder::Big,
566        }
567    }
568
569    test_case! {
570        self::new().byte_order(ByteOrder::Little) => {
571            byteorder = ByteOrder::Little,
572        }
573    }
574
575    test_case! {
576        self::new().pointer(Width::Variable) => {
577            length = Width::Variable,
578        }
579    }
580
581    test_case! {
582        self::new().pointer(Width::U8) => {
583            length = Width::U8,
584        }
585    }
586
587    test_case! {
588        self::new().pointer(Width::U16) => {
589            length = Width::U16,
590        }
591    }
592
593    test_case! {
594        self::new().pointer(Width::U32) => {
595            length = Width::U32,
596        }
597    }
598
599    test_case! {
600        self::new().pointer(Width::U64) => {
601            length = Width::U64,
602        }
603    }
604}