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 | |
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 { |
119 | 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 | |