/rust/registry/src/index.crates.io-6f17d22bba15001f/ciborium-ll-0.2.2/src/lib.rs
Line | Count | Source (jump to first uncovered line) |
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 | 0 | fn as_ref(&self) -> &[u8] { |
190 | 0 | match self { |
191 | 0 | Self::More => &[], |
192 | 0 | Self::This(..) => &[], |
193 | 0 | Self::Next1(x) => x.as_ref(), |
194 | 0 | Self::Next2(x) => x.as_ref(), |
195 | 0 | Self::Next4(x) => x.as_ref(), |
196 | 0 | Self::Next8(x) => x.as_ref(), |
197 | | } |
198 | 0 | } Unexecuted instantiation: <ciborium_ll::Minor as core::convert::AsRef<[u8]>>::as_ref Unexecuted instantiation: <ciborium_ll::Minor as core::convert::AsRef<[u8]>>::as_ref Unexecuted instantiation: <ciborium_ll::Minor as core::convert::AsRef<[u8]>>::as_ref |
199 | | } |
200 | | |
201 | | impl AsMut<[u8]> for Minor { |
202 | | #[inline] |
203 | 0 | fn as_mut(&mut self) -> &mut [u8] { |
204 | 0 | match self { |
205 | 0 | Self::More => &mut [], |
206 | 0 | Self::This(..) => &mut [], |
207 | 0 | Self::Next1(x) => x.as_mut(), |
208 | 0 | Self::Next2(x) => x.as_mut(), |
209 | 0 | Self::Next4(x) => x.as_mut(), |
210 | 0 | Self::Next8(x) => x.as_mut(), |
211 | | } |
212 | 0 | } Unexecuted instantiation: <ciborium_ll::Minor as core::convert::AsMut<[u8]>>::as_mut Unexecuted instantiation: <ciborium_ll::Minor as core::convert::AsMut<[u8]>>::as_mut |
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 | | } |