• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use super::*;
2 
3 use half::f16;
4 
5 /// A semantic representation of a CBOR item header
6 ///
7 /// This structure represents the valid values of a CBOR item header and is
8 /// used extensively when serializing or deserializing CBOR items. Note well
9 /// that this structure **DOES NOT** represent the body (i.e. suffix) of the
10 /// CBOR item. You must parse the body yourself based on the contents of the
11 /// `Header`. However, utility functions are provided for this (see:
12 /// `Decoder::bytes()` and `Decoder::text()`).
13 #[derive(Copy, Clone, Debug, PartialEq)]
14 pub enum Header {
15     /// A positive integer
16     Positive(u64),
17 
18     /// A negative integer
19     ///
20     /// Note well that this value has all bits inverted from a normal signed
21     /// integer. For example, to convert the `u64` to a `i128` you would do
22     /// this: `neg as i128 ^ !0`.
23     Negative(u64),
24 
25     /// A floating point value
26     Float(f64),
27 
28     /// A "simple" value
29     Simple(u8),
30 
31     /// A tag
32     Tag(u64),
33 
34     /// The "break" value
35     ///
36     /// This value is used to terminate indefinite length arrays and maps,
37     /// as well as segmented byte or text items.
38     Break,
39 
40     /// A bytes item
41     ///
42     /// The value contained in this variant indicates the length of the bytes
43     /// which follow or, if `None`, segmented bytes input.
44     ///
45     /// A best practice is to call `Decoder::bytes()` immediately after
46     /// first pulling a bytes item header since this utility function
47     /// encapsulates all the logic needed to handle segmentation.
48     Bytes(Option<usize>),
49 
50     /// A text item
51     ///
52     /// The value contained in this variant indicates the length of the text
53     /// which follows (in bytes) or, if `None`, segmented text input.
54     ///
55     /// A best practice is to call `Decoder::text()` immediately after
56     /// first pulling a text item header since this utility function
57     /// encapsulates all the logic needed to handle segmentation.
58     Text(Option<usize>),
59 
60     /// An array item
61     ///
62     /// The value contained in this variant indicates the length of the array
63     /// which follows (in items) or, if `None`, an indefinite length array
64     /// terminated by a "break" value.
65     Array(Option<usize>),
66 
67     /// An map item
68     ///
69     /// The value contained in this variant indicates the length of the map
70     /// which follows (in item pairs) or, if `None`, an indefinite length map
71     /// terminated by a "break" value.
72     Map(Option<usize>),
73 }
74 
75 impl TryFrom<Title> for Header {
76     type Error = InvalidError;
77 
try_from(title: Title) -> Result<Self, Self::Error>78     fn try_from(title: Title) -> Result<Self, Self::Error> {
79         let opt = |minor| {
80             Some(match minor {
81                 Minor::This(x) => x.into(),
82                 Minor::Next1(x) => u8::from_be_bytes(x).into(),
83                 Minor::Next2(x) => u16::from_be_bytes(x).into(),
84                 Minor::Next4(x) => u32::from_be_bytes(x).into(),
85                 Minor::Next8(x) => u64::from_be_bytes(x),
86                 Minor::More => return None,
87             })
88         };
89 
90         let int = |m| opt(m).ok_or(InvalidError(()));
91 
92         let len = |m| {
93             opt(m)
94                 .map(usize::try_from)
95                 .transpose()
96                 .or(Err(InvalidError(())))
97         };
98 
99         Ok(match title {
100             Title(Major::Positive, minor) => Self::Positive(int(minor)?),
101             Title(Major::Negative, minor) => Self::Negative(int(minor)?),
102             Title(Major::Bytes, minor) => Self::Bytes(len(minor)?),
103             Title(Major::Text, minor) => Self::Text(len(minor)?),
104             Title(Major::Array, minor) => Self::Array(len(minor)?),
105             Title(Major::Map, minor) => Self::Map(len(minor)?),
106             Title(Major::Tag, minor) => Self::Tag(int(minor)?),
107 
108             Title(Major::Other, Minor::More) => Self::Break,
109             Title(Major::Other, Minor::This(x)) => Self::Simple(x),
110             Title(Major::Other, Minor::Next1(x)) => Self::Simple(x[0]),
111             Title(Major::Other, Minor::Next2(x)) => Self::Float(f16::from_be_bytes(x).into()),
112             Title(Major::Other, Minor::Next4(x)) => Self::Float(f32::from_be_bytes(x).into()),
113             Title(Major::Other, Minor::Next8(x)) => Self::Float(f64::from_be_bytes(x)),
114         })
115     }
116 }
117 
118 impl From<Header> for Title {
from(header: Header) -> Self119     fn from(header: Header) -> Self {
120         let int = |i: u64| match i {
121             x if x <= 23 => Minor::This(i as u8),
122             x if x <= core::u8::MAX as u64 => Minor::Next1([i as u8]),
123             x if x <= core::u16::MAX as u64 => Minor::Next2((i as u16).to_be_bytes()),
124             x if x <= core::u32::MAX as u64 => Minor::Next4((i as u32).to_be_bytes()),
125             x => Minor::Next8(x.to_be_bytes()),
126         };
127 
128         let len = |l: Option<usize>| l.map(|x| int(x as u64)).unwrap_or(Minor::More);
129 
130         match header {
131             Header::Positive(x) => Title(Major::Positive, int(x)),
132             Header::Negative(x) => Title(Major::Negative, int(x)),
133             Header::Bytes(x) => Title(Major::Bytes, len(x)),
134             Header::Text(x) => Title(Major::Text, len(x)),
135             Header::Array(x) => Title(Major::Array, len(x)),
136             Header::Map(x) => Title(Major::Map, len(x)),
137             Header::Tag(x) => Title(Major::Tag, int(x)),
138 
139             Header::Break => Title(Major::Other, Minor::More),
140 
141             Header::Simple(x) => match x {
142                 x @ 0..=23 => Title(Major::Other, Minor::This(x)),
143                 x => Title(Major::Other, Minor::Next1([x])),
144             },
145 
146             Header::Float(n64) => {
147                 let n16 = f16::from_f64(n64);
148                 let n32 = n64 as f32;
149 
150                 Title(
151                     Major::Other,
152                     if f64::from(n16).to_bits() == n64.to_bits() {
153                         Minor::Next2(n16.to_be_bytes())
154                     } else if f64::from(n32).to_bits() == n64.to_bits() {
155                         Minor::Next4(n32.to_be_bytes())
156                     } else {
157                         Minor::Next8(n64.to_be_bytes())
158                     },
159                 )
160             }
161         }
162     }
163 }
164