1 | // SPDX-License-Identifier: Apache-2.0 |
2 | |
3 | //! Low level CBOR parsing tools |
4 | //! |
5 | //! This crate contains low-level types for encoding and decoding items in |
6 | //! CBOR. This crate is usable in both `no_std` and `no_alloc` environments. |
7 | //! To understand how this crate works, first we will look at the structure |
8 | //! of a CBOR item on the wire. |
9 | //! |
10 | //! # Anatomy of a CBOR Item |
11 | //! |
12 | //! This is a brief anatomy of a CBOR item on the wire. |
13 | //! |
14 | //! ```text |
15 | //! +------------+-----------+ |
16 | //! | | | |
17 | //! | Major | Minor | |
18 | //! | (3bits) | (5bits) | |
19 | //! | | | |
20 | //! +------------+-----------+ |
21 | //! ^ ^ |
22 | //! | | |
23 | //! +-----+ +-----+ |
24 | //! | | |
25 | //! | | |
26 | //! +----------------------------+--------------+ |
27 | //! | | | | |
28 | //! | Prefix | Affix | Suffix | |
29 | //! | (1 byte) | (0-8 bytes) | (0+ bytes) | |
30 | //! | | | | |
31 | //! +------------+---------------+--------------+ |
32 | //! |
33 | //! | | | |
34 | //! +------------+---------------+--------------+ |
35 | //! | | |
36 | //! v v |
37 | //! |
38 | //! Header Body |
39 | //! ``` |
40 | //! |
41 | //! The `ciborium` crate works by providing the `Decoder` and `Encoder` types |
42 | //! which provide input and output for a CBOR header (see: `Header`). From |
43 | //! there, you can either handle the body yourself or use the provided utility |
44 | //! functions. |
45 | //! |
46 | //! For more information on the CBOR format, see |
47 | //! [RFC 7049](https://tools.ietf.org/html/rfc7049). |
48 | //! |
49 | //! # Decoding |
50 | //! |
51 | //! In order to decode CBOR, you will create a `Decoder` from a reader. The |
52 | //! decoder instance will allow you to `Decoder::pull()` `Header` instances |
53 | //! from the input. |
54 | //! |
55 | //! Most CBOR items are fully contained in their headers and therefore have no |
56 | //! body. These items can be evaluated directly from the `Header` instance. |
57 | //! |
58 | //! Bytes and text items have a body but do not contain child items. Since |
59 | //! both bytes and text values may be segmented, parsing them can be a bit |
60 | //! tricky. Therefore, we provide helper functions to parse these types. See |
61 | //! `Decoder::bytes()` and `Decoder::text()` for more details. |
62 | //! |
63 | //! Array and map items have a body which contains child items. These can be |
64 | //! parsed by simply doing `Decoder::pull()` to parse the child items. |
65 | //! |
66 | //! ## Example |
67 | //! |
68 | //! ```rust |
69 | //! use ciborium_ll::{Decoder, Header}; |
70 | //! use ciborium_io::Read as _; |
71 | //! |
72 | //! let input = b" \x6dHello, World!" ; |
73 | //! let mut decoder = Decoder::from(&input[..]); |
74 | //! let mut chunks = 0; |
75 | //! |
76 | //! match decoder.pull().unwrap() { |
77 | //! Header::Text(len) => { |
78 | //! let mut segments = decoder.text(len); |
79 | //! while let Some(mut segment) = segments.pull().unwrap() { |
80 | //! let mut buffer = [0u8; 7]; |
81 | //! while let Some(chunk) = segment.pull(&mut buffer[..]).unwrap() { |
82 | //! match chunk { |
83 | //! "Hello, " if chunks == 0 => chunks = 1, |
84 | //! "World!" if chunks == 1 => chunks = 2, |
85 | //! _ => panic!("received unexpected chunk" ), |
86 | //! } |
87 | //! } |
88 | //! } |
89 | //! } |
90 | //! |
91 | //! _ => panic!("received unexpected value" ), |
92 | //! } |
93 | //! |
94 | //! assert_eq!(chunks, 2); |
95 | //! ``` |
96 | //! |
97 | //! # Encoding |
98 | //! |
99 | //! To encode values to CBOR, create an `Encoder` from a writer. The encoder |
100 | //! instance provides the `Encoder::push()` method to write a `Header` value |
101 | //! to the wire. CBOR item bodies can be written directly. |
102 | //! |
103 | //! For bytes and text, there are the `Encoder::bytes()` and `Encoder::text()` |
104 | //! utility functions, respectively, which will properly segment the output |
105 | //! on the wire for you. |
106 | //! |
107 | //! ## Example |
108 | //! |
109 | //! ```rust |
110 | //! use ciborium_ll::{Encoder, Header}; |
111 | //! use ciborium_io::Write as _; |
112 | //! |
113 | //! let mut buffer = [0u8; 19]; |
114 | //! let mut encoder = Encoder::from(&mut buffer[..]); |
115 | //! |
116 | //! // Write the structure |
117 | //! encoder.push(Header::Map(Some(1))).unwrap(); |
118 | //! encoder.push(Header::Positive(7)).unwrap(); |
119 | //! encoder.text("Hello, World!" , 7).unwrap(); |
120 | //! |
121 | //! // Validate our output |
122 | //! encoder.flush().unwrap(); |
123 | //! assert_eq!(b" \xa1\x07\x7f\x67Hello, \x66World! \xff" , &buffer[..]); |
124 | //! ``` |
125 | |
126 | #![cfg_attr (not(feature = "std" ), no_std)] |
127 | #![deny (missing_docs)] |
128 | #![deny (clippy::all)] |
129 | #![deny (clippy::cargo)] |
130 | |
131 | #[cfg (feature = "alloc" )] |
132 | extern crate alloc; |
133 | |
134 | mod dec; |
135 | mod enc; |
136 | mod hdr; |
137 | mod seg; |
138 | |
139 | pub use dec::*; |
140 | pub use enc::*; |
141 | pub use hdr::*; |
142 | pub use seg::{Segment, Segments}; |
143 | |
144 | /// Simple value constants |
145 | pub mod simple { |
146 | #![allow (missing_docs)] |
147 | |
148 | pub const FALSE: u8 = 20; |
149 | pub const TRUE: u8 = 21; |
150 | pub const NULL: u8 = 22; |
151 | pub const UNDEFINED: u8 = 23; |
152 | } |
153 | |
154 | /// Tag constants |
155 | pub mod tag { |
156 | #![allow (missing_docs)] |
157 | |
158 | pub const BIGPOS: u64 = 2; |
159 | pub const BIGNEG: u64 = 3; |
160 | } |
161 | |
162 | #[derive(Debug)] |
163 | struct InvalidError(()); |
164 | |
165 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] |
166 | enum Major { |
167 | Positive, |
168 | Negative, |
169 | Bytes, |
170 | Text, |
171 | Array, |
172 | Map, |
173 | Tag, |
174 | Other, |
175 | } |
176 | |
177 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] |
178 | enum Minor { |
179 | This(u8), |
180 | Next1([u8; 1]), |
181 | Next2([u8; 2]), |
182 | Next4([u8; 4]), |
183 | Next8([u8; 8]), |
184 | More, |
185 | } |
186 | |
187 | impl AsRef<[u8]> for Minor { |
188 | #[inline ] |
189 | fn as_ref(&self) -> &[u8] { |
190 | match self { |
191 | Self::More => &[], |
192 | Self::This(..) => &[], |
193 | Self::Next1(x) => x.as_ref(), |
194 | Self::Next2(x) => x.as_ref(), |
195 | Self::Next4(x) => x.as_ref(), |
196 | Self::Next8(x) => x.as_ref(), |
197 | } |
198 | } |
199 | } |
200 | |
201 | impl AsMut<[u8]> for Minor { |
202 | #[inline ] |
203 | fn as_mut(&mut self) -> &mut [u8] { |
204 | match self { |
205 | Self::More => &mut [], |
206 | Self::This(..) => &mut [], |
207 | Self::Next1(x) => x.as_mut(), |
208 | Self::Next2(x) => x.as_mut(), |
209 | Self::Next4(x) => x.as_mut(), |
210 | Self::Next8(x) => x.as_mut(), |
211 | } |
212 | } |
213 | } |
214 | |
215 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] |
216 | struct Title(pub Major, pub Minor); |
217 | |
218 | #[cfg (test)] |
219 | mod tests { |
220 | use super::*; |
221 | |
222 | macro_rules! neg { |
223 | ($i:expr) => { |
224 | Header::Negative((($i as i128) ^ !0) as u64) |
225 | }; |
226 | } |
227 | |
228 | #[allow (clippy::excessive_precision)] |
229 | #[test] |
230 | fn leaf() { |
231 | use core::f64::{INFINITY, NAN}; |
232 | |
233 | let data = &[ |
234 | (Header::Positive(0), "00" , true), |
235 | (Header::Positive(1), "01" , true), |
236 | (Header::Positive(10), "0a" , true), |
237 | (Header::Positive(23), "17" , true), |
238 | (Header::Positive(24), "1818" , true), |
239 | (Header::Positive(25), "1819" , true), |
240 | (Header::Positive(100), "1864" , true), |
241 | (Header::Positive(1000), "1903e8" , true), |
242 | (Header::Positive(1000000), "1a000f4240" , true), |
243 | (Header::Positive(1000000000000), "1b000000e8d4a51000" , true), |
244 | ( |
245 | Header::Positive(18446744073709551615), |
246 | "1bffffffffffffffff" , |
247 | true, |
248 | ), |
249 | (neg!(-18446744073709551616), "3bffffffffffffffff" , true), |
250 | (neg!(-1), "20" , true), |
251 | (neg!(-10), "29" , true), |
252 | (neg!(-100), "3863" , true), |
253 | (neg!(-1000), "3903e7" , true), |
254 | (Header::Float(0.0), "f90000" , true), |
255 | (Header::Float(-0.0), "f98000" , true), |
256 | (Header::Float(1.0), "f93c00" , true), |
257 | (Header::Float(1.1), "fb3ff199999999999a" , true), |
258 | (Header::Float(1.5), "f93e00" , true), |
259 | (Header::Float(65504.0), "f97bff" , true), |
260 | (Header::Float(100000.0), "fa47c35000" , true), |
261 | (Header::Float(3.4028234663852886e+38), "fa7f7fffff" , true), |
262 | (Header::Float(1.0e+300), "fb7e37e43c8800759c" , true), |
263 | (Header::Float(5.960464477539063e-8), "f90001" , true), |
264 | (Header::Float(0.00006103515625), "f90400" , true), |
265 | (Header::Float(-4.0), "f9c400" , true), |
266 | (Header::Float(-4.1), "fbc010666666666666" , true), |
267 | (Header::Float(INFINITY), "f97c00" , true), |
268 | (Header::Float(NAN), "f97e00" , true), |
269 | (Header::Float(-INFINITY), "f9fc00" , true), |
270 | (Header::Float(INFINITY), "fa7f800000" , false), |
271 | (Header::Float(NAN), "fa7fc00000" , false), |
272 | (Header::Float(-INFINITY), "faff800000" , false), |
273 | (Header::Float(INFINITY), "fb7ff0000000000000" , false), |
274 | (Header::Float(NAN), "fb7ff8000000000000" , false), |
275 | (Header::Float(-INFINITY), "fbfff0000000000000" , false), |
276 | (Header::Simple(simple::FALSE), "f4" , true), |
277 | (Header::Simple(simple::TRUE), "f5" , true), |
278 | (Header::Simple(simple::NULL), "f6" , true), |
279 | (Header::Simple(simple::UNDEFINED), "f7" , true), |
280 | (Header::Simple(16), "f0" , true), |
281 | (Header::Simple(24), "f818" , true), |
282 | (Header::Simple(255), "f8ff" , true), |
283 | (Header::Tag(0), "c0" , true), |
284 | (Header::Tag(1), "c1" , true), |
285 | (Header::Tag(23), "d7" , true), |
286 | (Header::Tag(24), "d818" , true), |
287 | (Header::Tag(32), "d820" , true), |
288 | (Header::Bytes(Some(0)), "40" , true), |
289 | (Header::Bytes(Some(4)), "44" , true), |
290 | (Header::Text(Some(0)), "60" , true), |
291 | (Header::Text(Some(4)), "64" , true), |
292 | ]; |
293 | |
294 | for (header, bytes, encode) in data.iter().cloned() { |
295 | let bytes = hex::decode(bytes).unwrap(); |
296 | |
297 | let mut decoder = Decoder::from(&bytes[..]); |
298 | match (header, decoder.pull().unwrap()) { |
299 | // NaN equality... |
300 | (Header::Float(l), Header::Float(r)) if l.is_nan() && r.is_nan() => (), |
301 | |
302 | // Everything else... |
303 | (l, r) => assert_eq!(l, r), |
304 | } |
305 | |
306 | if encode { |
307 | let mut buffer = [0u8; 1024]; |
308 | let mut writer = &mut buffer[..]; |
309 | let mut encoder = Encoder::from(&mut writer); |
310 | encoder.push(header).unwrap(); |
311 | |
312 | let len = writer.len(); |
313 | assert_eq!(&bytes[..], &buffer[..1024 - len]); |
314 | } |
315 | } |
316 | } |
317 | |
318 | #[test] |
319 | fn node() { |
320 | let data: &[(&str, &[Header])] = &[ |
321 | ("80" , &[Header::Array(Some(0))]), |
322 | ( |
323 | "83010203" , |
324 | &[ |
325 | Header::Array(Some(3)), |
326 | Header::Positive(1), |
327 | Header::Positive(2), |
328 | Header::Positive(3), |
329 | ], |
330 | ), |
331 | ( |
332 | "98190102030405060708090a0b0c0d0e0f101112131415161718181819" , |
333 | &[ |
334 | Header::Array(Some(25)), |
335 | Header::Positive(1), |
336 | Header::Positive(2), |
337 | Header::Positive(3), |
338 | Header::Positive(4), |
339 | Header::Positive(5), |
340 | Header::Positive(6), |
341 | Header::Positive(7), |
342 | Header::Positive(8), |
343 | Header::Positive(9), |
344 | Header::Positive(10), |
345 | Header::Positive(11), |
346 | Header::Positive(12), |
347 | Header::Positive(13), |
348 | Header::Positive(14), |
349 | Header::Positive(15), |
350 | Header::Positive(16), |
351 | Header::Positive(17), |
352 | Header::Positive(18), |
353 | Header::Positive(19), |
354 | Header::Positive(20), |
355 | Header::Positive(21), |
356 | Header::Positive(22), |
357 | Header::Positive(23), |
358 | Header::Positive(24), |
359 | Header::Positive(25), |
360 | ], |
361 | ), |
362 | ("a0" , &[Header::Map(Some(0))]), |
363 | ( |
364 | "a201020304" , |
365 | &[ |
366 | Header::Map(Some(2)), |
367 | Header::Positive(1), |
368 | Header::Positive(2), |
369 | Header::Positive(3), |
370 | Header::Positive(4), |
371 | ], |
372 | ), |
373 | ("9fff" , &[Header::Array(None), Header::Break]), |
374 | ( |
375 | "9f018202039f0405ffff" , |
376 | &[ |
377 | Header::Array(None), |
378 | Header::Positive(1), |
379 | Header::Array(Some(2)), |
380 | Header::Positive(2), |
381 | Header::Positive(3), |
382 | Header::Array(None), |
383 | Header::Positive(4), |
384 | Header::Positive(5), |
385 | Header::Break, |
386 | Header::Break, |
387 | ], |
388 | ), |
389 | ( |
390 | "9f01820203820405ff" , |
391 | &[ |
392 | Header::Array(None), |
393 | Header::Positive(1), |
394 | Header::Array(Some(2)), |
395 | Header::Positive(2), |
396 | Header::Positive(3), |
397 | Header::Array(Some(2)), |
398 | Header::Positive(4), |
399 | Header::Positive(5), |
400 | Header::Break, |
401 | ], |
402 | ), |
403 | ( |
404 | "83018202039f0405ff" , |
405 | &[ |
406 | Header::Array(Some(3)), |
407 | Header::Positive(1), |
408 | Header::Array(Some(2)), |
409 | Header::Positive(2), |
410 | Header::Positive(3), |
411 | Header::Array(None), |
412 | Header::Positive(4), |
413 | Header::Positive(5), |
414 | Header::Break, |
415 | ], |
416 | ), |
417 | ( |
418 | "83019f0203ff820405" , |
419 | &[ |
420 | Header::Array(Some(3)), |
421 | Header::Positive(1), |
422 | Header::Array(None), |
423 | Header::Positive(2), |
424 | Header::Positive(3), |
425 | Header::Break, |
426 | Header::Array(Some(2)), |
427 | Header::Positive(4), |
428 | Header::Positive(5), |
429 | ], |
430 | ), |
431 | ( |
432 | "9f0102030405060708090a0b0c0d0e0f101112131415161718181819ff" , |
433 | &[ |
434 | Header::Array(None), |
435 | Header::Positive(1), |
436 | Header::Positive(2), |
437 | Header::Positive(3), |
438 | Header::Positive(4), |
439 | Header::Positive(5), |
440 | Header::Positive(6), |
441 | Header::Positive(7), |
442 | Header::Positive(8), |
443 | Header::Positive(9), |
444 | Header::Positive(10), |
445 | Header::Positive(11), |
446 | Header::Positive(12), |
447 | Header::Positive(13), |
448 | Header::Positive(14), |
449 | Header::Positive(15), |
450 | Header::Positive(16), |
451 | Header::Positive(17), |
452 | Header::Positive(18), |
453 | Header::Positive(19), |
454 | Header::Positive(20), |
455 | Header::Positive(21), |
456 | Header::Positive(22), |
457 | Header::Positive(23), |
458 | Header::Positive(24), |
459 | Header::Positive(25), |
460 | Header::Break, |
461 | ], |
462 | ), |
463 | ]; |
464 | |
465 | for (bytes, headers) in data { |
466 | let bytes = hex::decode(bytes).unwrap(); |
467 | |
468 | // Test decoding |
469 | let mut decoder = Decoder::from(&bytes[..]); |
470 | for header in headers.iter().cloned() { |
471 | assert_eq!(header, decoder.pull().unwrap()); |
472 | } |
473 | |
474 | // Test encoding |
475 | let mut buffer = [0u8; 1024]; |
476 | let mut writer = &mut buffer[..]; |
477 | let mut encoder = Encoder::from(&mut writer); |
478 | |
479 | for header in headers.iter().cloned() { |
480 | encoder.push(header).unwrap(); |
481 | } |
482 | |
483 | let len = writer.len(); |
484 | assert_eq!(&bytes[..], &buffer[..1024 - len]); |
485 | } |
486 | } |
487 | } |
488 | |