musli_wire/tag.rs
1//! Type flags available for `musli-wire`.
2
3#![allow(clippy::unusual_byte_groupings)]
4
5use core::fmt;
6use core::mem;
7
8#[cfg(feature = "test")]
9use musli::{Decode, Encode};
10
11/// Data masked into the data type.
12pub(crate) const DATA_MASK: u8 = 0b00_111111;
13/// The maximum length that can be inlined in the tag without adding additional
14/// data to the wire format.
15pub const MAX_INLINE_LEN: usize = (DATA_MASK - 1) as usize;
16
17/// The structure of a type tag.
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19#[repr(u8)]
20pub enum Kind {
21 /// A reserved value.
22 Reserve = 0b00_000000,
23 /// A fixed element where data indicates how many bytes it consists of. Data
24 /// contains the prefix length unless it's set to all 1s after which a
25 /// continuation sequence indicating the length should be decoded.
26 Prefix = 0b01_000000,
27 /// A length-prefixed sequence of values. Data contains the length of the
28 /// sequence if it's short enough to fit in 6 bits. All bits as 1s is
29 /// reserved to indicate when it's empty.
30 Sequence = 0b10_000000,
31 /// A continuation-encoded value. Data is the immediate value embedded if
32 /// it's small enough to fit in 6 bits. All bits as 1s is reserved to
33 /// indicate when a continuation sequence is used.
34 Continuation = 0b11_000000,
35}
36
37/// A type tag.
38///
39/// The [Kind] of the element is indicates by its 2 MSBs, and remaining 6 bits
40/// is the data field. The exact use of the data field depends on the [Kind] in
41/// question. It is primarily used to smuggle extra data for the kind in
42/// question.
43#[derive(Clone, Copy, PartialEq, Eq, Hash)]
44#[cfg_attr(feature = "test", derive(Encode, Decode))]
45#[repr(transparent)]
46#[cfg_attr(feature = "test", musli(transparent))]
47pub struct Tag {
48 /// The internal representation of the tag.
49 repr: u8,
50}
51
52impl Tag {
53 /// Construct a new tag through an unchecked constructor.
54 ///
55 /// `data` must not be equal to or larger than [MAX_INLINE_LEN], or else it
56 /// could corrupt the payload.
57 #[inline]
58 pub const fn new(kind: Kind, data: u8) -> Self {
59 Self {
60 repr: kind as u8 | data,
61 }
62 }
63
64 /// Construct a new empty tag of the given [Kind].
65 #[inline]
66 pub const fn empty(kind: Kind) -> Self {
67 Self {
68 repr: kind as u8 | DATA_MASK,
69 }
70 }
71
72 /// Construct from a byte.
73 #[inline]
74 pub const fn from_byte(repr: u8) -> Self {
75 Self { repr }
76 }
77
78 /// Coerce type flag into a byte.
79 #[inline]
80 pub const fn byte(self) -> u8 {
81 self.repr
82 }
83
84 /// Access the kind of the tag.
85 #[inline]
86 pub const fn kind(self) -> Kind {
87 // SAFETY: this is safe because we've ensured that all available Kind
88 // variants occupy all available bit patterns.
89 unsafe { mem::transmute(self.repr & !DATA_MASK) }
90 }
91
92 /// Perform raw access over the data payload. Will return [DATA_MASK] if
93 /// data is empty.
94 #[inline]
95 pub(crate) const fn data_raw(self) -> u8 {
96 self.repr & DATA_MASK
97 }
98
99 /// Perform checked access over the internal data. Returns [None] if data is
100 /// empty.
101 #[inline]
102 pub const fn data(self) -> Option<u8> {
103 let data = self.data_raw();
104
105 if data == DATA_MASK {
106 None
107 } else {
108 Some(data)
109 }
110 }
111
112 /// Attempt to construct a type tag with the given length embedded.
113 ///
114 /// Returns a tuple where the boolean indicates if the value was embedded or
115 /// not.
116 #[inline]
117 pub const fn with_len(kind: Kind, len: usize) -> (Self, bool) {
118 if len < DATA_MASK as usize {
119 (Self::new(kind, len as u8), true)
120 } else {
121 (Self::new(kind, DATA_MASK), false)
122 }
123 }
124
125 /// Attempt to construct a type tag with the given length embedded.
126 ///
127 /// Returns a tuple where the boolean indicates if the value was embedded or
128 /// not.
129 #[inline]
130 pub const fn with_byte(kind: Kind, len: u8) -> (Self, bool) {
131 if len < DATA_MASK {
132 (Self::new(kind, len), true)
133 } else {
134 (Self::new(kind, DATA_MASK), false)
135 }
136 }
137}
138
139impl fmt::Debug for Tag {
140 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
141 f.debug_struct("Tag")
142 .field("kind", &self.kind())
143 .field("data", &self.data())
144 .finish()
145 }
146}