musli_descriptive/
tag.rs

1//! Type flags available for `musli-wire`.
2
3#![allow(clippy::unusual_byte_groupings)]
4
5use core::fmt;
6use core::mem;
7
8use musli::{Decode, Decoder};
9
10/// Variant corresponding to marks.
11#[derive(Debug)]
12#[repr(u8)]
13pub enum Mark {
14    /// The marker indicating an absent value.
15    None = 0b000,
16    /// The marker indicating a value that is present.
17    Some = 0b001,
18    /// The marker indicating the value true.
19    True = 0b011,
20    /// The marker indicating the value false.
21    False = 0b010,
22    /// The marker indicating that the value is a variant.
23    Variant = 0b100,
24    /// A single character.
25    Char = 0b101,
26    /// A unit type.
27    Unit = 0b110,
28    /// A reserved mark.
29    Reserved0 = 0b111,
30}
31
32/// The kind of a number.
33///
34/// Not that this enum occupies all possible low 2-bit patterns, which allows it
35/// to be transmuted from a byte masked over `0b11`.
36#[derive(Debug, Clone, Copy)]
37#[repr(u8)]
38pub(crate) enum NumberKind {
39    /// The numerical type is a signed value.
40    Signed = 0b00,
41    /// The numerical type is an unsigned value.
42    Unsigned = 0b01,
43    /// The numerical type is a float.
44    Float = 0b10,
45    /// Reserved number kind.
46    #[allow(unused)]
47    Reserved0 = 0b11,
48}
49
50/// The width of the field.
51///
52/// Note that this representation directly corresponds to 2 to the power of the
53/// specified width.
54#[repr(u8)]
55pub(crate) enum Width {
56    #[allow(unused)]
57    Reserved0 = 0b000,
58    #[allow(unused)]
59    Reserved1 = 0b001,
60    #[allow(unused)]
61    Reserved2 = 0b010,
62    /// 8-bit width.
63    U8 = 0b011,
64    /// 16-bit width.
65    U16 = 0b100,
66    /// 32-bit width.
67    U32 = 0b101,
68    /// 64-bit width.
69    U64 = 0b110,
70    /// 128-bit width.
71    U128 = 0b111,
72}
73
74#[test]
75fn ensure_width() {
76    assert_eq!(2u32.pow(Width::U8 as u32), 8u32);
77    assert_eq!(1u32 << Width::U8 as u32, 8u32);
78    assert_eq!(2u32.pow(Width::U16 as u32), 16u32);
79    assert_eq!(1u32 << Width::U16 as u32, 16u32);
80    assert_eq!(2u32.pow(Width::U32 as u32), 32u32);
81    assert_eq!(1u32 << Width::U32 as u32, 32u32);
82    assert_eq!(2u32.pow(Width::U64 as u32), 64u32);
83    assert_eq!(1u32 << Width::U64 as u32, 64u32);
84    assert_eq!(2u32.pow(Width::U128 as u32), 128u32);
85    assert_eq!(1u32 << Width::U128 as u32, 128u32);
86}
87
88/// 8-bit unsigned number.
89pub const U8: u8 = (Width::U8 as u8) << 2 | NumberKind::Unsigned as u8;
90/// 16-bit unsigned number.
91pub const U16: u8 = (Width::U16 as u8) << 2 | NumberKind::Unsigned as u8;
92/// 32-bit unsigned number.
93pub const U32: u8 = (Width::U32 as u8) << 2 | NumberKind::Unsigned as u8;
94/// 64-bit unsigned number.
95pub const U64: u8 = (Width::U64 as u8) << 2 | NumberKind::Unsigned as u8;
96/// 128-bit number hint.
97pub const U128: u8 = (Width::U128 as u8) << 2 | NumberKind::Unsigned as u8;
98/// 8-bit signed number.
99pub const I8: u8 = (Width::U8 as u8) << 2 | NumberKind::Signed as u8;
100/// 16-bit signed number.
101pub const I16: u8 = (Width::U16 as u8) << 2 | NumberKind::Signed as u8;
102/// 32-bit signed number.
103pub const I32: u8 = (Width::U32 as u8) << 2 | NumberKind::Signed as u8;
104/// 64-bit signed number.
105pub const I64: u8 = (Width::U64 as u8) << 2 | NumberKind::Signed as u8;
106/// 128-bit signed number.
107pub const I128: u8 = (Width::U128 as u8) << 2 | NumberKind::Signed as u8;
108/// 32-bit float hint.
109pub const F32: u8 = (Width::U32 as u8) << 2 | NumberKind::Float as u8;
110/// 64-bit float hint.
111pub const F64: u8 = (Width::U64 as u8) << 2 | NumberKind::Float as u8;
112/// The marker for a usize.
113#[cfg(target_pointer_width = "32")]
114pub const USIZE: u8 = U32;
115/// The marker for a usize.
116#[cfg(target_pointer_width = "64")]
117pub const USIZE: u8 = U64;
118/// The marker for a isize.
119#[cfg(target_pointer_width = "32")]
120pub const ISIZE: u8 = I32;
121/// The marker for a isize.
122#[cfg(target_pointer_width = "64")]
123pub const ISIZE: u8 = I64;
124
125/// Data masked into the data type.
126pub(crate) const DATA_MASK: u8 = 0b000_11111;
127pub(crate) const MARK_MASK: u8 = 0b00000_111;
128pub(crate) const NUMBER_KIND_MASK: u8 = 0b000000_11;
129/// The maximum length that can be inlined in the tag without adding additional
130/// data to the wire format.
131pub const MAX_INLINE_LEN: usize = (DATA_MASK - 1) as usize;
132
133/// The structure of a type tag.
134#[derive(Debug, Clone, Copy, PartialEq, Eq)]
135#[repr(u8)]
136pub enum Kind {
137    /// Reserved 0.
138    Reserved0 = 0b000_00000,
139    /// Reserved 1.
140    Reserved1 = 0b001_00000,
141    /// A continuation-encoded numerical value.
142    Number = 0b010_00000,
143    /// A length-prefixed sequence of value.
144    Sequence = 0b011_00000,
145    /// A length-prefixed map.
146    Map = 0b100_00000,
147    /// A sequence of raw bytes.
148    Bytes = 0b101_00000,
149    /// A string.
150    String = 0b110_00000,
151    /// A distinct mark.
152    Mark = 0b111_00000,
153}
154
155/// A type tag.
156///
157/// The [Kind] of the element is indicates by its 2 MSBs, and remaining 6 bits
158/// is the data field. The exact use of the data field depends on the [Kind] in
159/// question. It is primarily used to smuggle extra data for the kind in
160/// question.
161#[derive(Clone, Copy, PartialEq, Eq, Hash)]
162#[repr(transparent)]
163pub struct Tag {
164    /// The internal representation of the tag.
165    repr: u8,
166}
167
168impl Tag {
169    /// Construct a new tag through an unchecked constructor.
170    ///
171    /// `data` must not be equal to or larger than [MAX_INLINE_LEN], or else it
172    /// could corrupt the payload.
173    #[inline]
174    pub const fn new(kind: Kind, data: u8) -> Self {
175        Self {
176            repr: kind as u8 | data,
177        }
178    }
179
180    /// Construct a tag corresponding to a mark.
181    #[inline]
182    pub const fn from_mark(mark: Mark) -> Self {
183        Self {
184            repr: Kind::Mark as u8 | mark as u8,
185        }
186    }
187
188    /// Access the mark this byte corresponds to.
189    #[inline]
190    pub const fn mark(&self) -> Mark {
191        // SAFETY: The representation used by `Mark` is exhaustive over all
192        // emitted bit-patterns.
193        unsafe { mem::transmute(self.repr & MARK_MASK) }
194    }
195
196    /// Access the number kind this tag corresponds to.
197    #[inline]
198    pub(crate) const fn number_kind(&self) -> NumberKind {
199        // SAFETY: The representation used by `Mark` is exhaustive over all
200        // emitted bit-patterns.
201        unsafe { mem::transmute(self.repr & NUMBER_KIND_MASK) }
202    }
203
204    /// Construct a new empty tag of the given [Kind].
205    #[inline]
206    pub const fn empty(kind: Kind) -> Self {
207        Self {
208            repr: kind as u8 | DATA_MASK,
209        }
210    }
211
212    /// Construct from a byte.
213    #[inline]
214    pub const fn from_byte(repr: u8) -> Self {
215        Self { repr }
216    }
217
218    /// Coerce type flag into a byte.
219    #[inline]
220    pub const fn byte(self) -> u8 {
221        self.repr
222    }
223
224    /// Access the kind of the tag.
225    #[inline]
226    pub const fn kind(self) -> Kind {
227        // SAFETY: this is safe because we've ensured that all available Kind
228        // variants occupy all available bit patterns.
229        unsafe { mem::transmute(self.repr & !DATA_MASK) }
230    }
231
232    /// Perform raw access over the data payload. Will return [DATA_MASK] if
233    /// data is empty.
234    #[inline]
235    pub(crate) const fn data_raw(self) -> u8 {
236        self.repr & DATA_MASK
237    }
238
239    /// Perform checked access over the internal data. Returns [None] if data is
240    /// empty.
241    #[inline]
242    pub const fn data(self) -> Option<u8> {
243        let data = self.data_raw();
244
245        if data == DATA_MASK {
246            None
247        } else {
248            Some(data)
249        }
250    }
251
252    /// Attempt to construct a type tag with the given length embedded.
253    ///
254    /// Returns a tuple where the boolean indicates if the value was embedded or
255    /// not.
256    #[inline]
257    pub const fn with_len(kind: Kind, len: usize) -> (Self, bool) {
258        if len < DATA_MASK as usize {
259            (Self::new(kind, len as u8), true)
260        } else {
261            (Self::new(kind, DATA_MASK), false)
262        }
263    }
264
265    /// Attempt to construct a type tag with the given length embedded.
266    ///
267    /// Returns a tuple where the boolean indicates if the value was embedded or
268    /// not.
269    #[inline]
270    pub const fn with_byte(kind: Kind, len: u8) -> (Self, bool) {
271        if len < DATA_MASK {
272            (Self::new(kind, len), true)
273        } else {
274            (Self::new(kind, DATA_MASK), false)
275        }
276    }
277}
278
279impl fmt::Debug for Tag {
280    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
281        f.debug_struct("Tag")
282            .field("kind", &self.kind())
283            .field("data", &self.data())
284            .finish()
285    }
286}
287
288impl<'de, M> Decode<'de, M> for Tag {
289    #[inline]
290    fn decode<D>(_: &D::Cx, decoder: D) -> Result<Self, D::Error>
291    where
292        D: Decoder<'de, Mode = M>,
293    {
294        Ok(Self::from_byte(decoder.decode_u8()?))
295    }
296}