From d1b2d29528b7794b41e66fc2136e395a02f8529b Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Thu, 30 May 2024 05:59:35 +0200 Subject: Merging upstream version 1.73.0+dfsg1. Signed-off-by: Daniel Baumann --- vendor/ciborium-ll/.cargo-checksum.json | 1 + vendor/ciborium-ll/Cargo.toml | 49 ++++ vendor/ciborium-ll/LICENSE | 201 +++++++++++++ vendor/ciborium-ll/README.md | 131 +++++++++ vendor/ciborium-ll/src/dec.rs | 174 ++++++++++++ vendor/ciborium-ll/src/enc.rs | 127 +++++++++ vendor/ciborium-ll/src/hdr.rs | 163 +++++++++++ vendor/ciborium-ll/src/lib.rs | 487 ++++++++++++++++++++++++++++++++ vendor/ciborium-ll/src/seg.rs | 216 ++++++++++++++ 9 files changed, 1549 insertions(+) create mode 100644 vendor/ciborium-ll/.cargo-checksum.json create mode 100644 vendor/ciborium-ll/Cargo.toml create mode 100644 vendor/ciborium-ll/LICENSE create mode 100644 vendor/ciborium-ll/README.md create mode 100644 vendor/ciborium-ll/src/dec.rs create mode 100644 vendor/ciborium-ll/src/enc.rs create mode 100644 vendor/ciborium-ll/src/hdr.rs create mode 100644 vendor/ciborium-ll/src/lib.rs create mode 100644 vendor/ciborium-ll/src/seg.rs (limited to 'vendor/ciborium-ll') diff --git a/vendor/ciborium-ll/.cargo-checksum.json b/vendor/ciborium-ll/.cargo-checksum.json new file mode 100644 index 000000000..26a540019 --- /dev/null +++ b/vendor/ciborium-ll/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"Cargo.toml":"278cfaa9b5844046a047f8c9799de6888ab057de74462dec1761ff5d8fc5bb80","LICENSE":"c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4","README.md":"b79803cccd55d24fc81e47aa3fac77461caa78c80799b9115e283ac0bc805c3a","src/dec.rs":"48b2b15a181cc76153be58eb14762c854b83f696150946f3b74fe6b20c717912","src/enc.rs":"90388589b9798382c1abcae21d91e9f3d4a35415c3ad534f41312e94a0e43635","src/hdr.rs":"6cdbdf3c9ea430d804c1c64ab70c7a2a6e38e49f9cbd6f157c6542d8c9b2e952","src/lib.rs":"da6178728228ebfc50b13fc32ce707f2cc539f0212a919645ec64a50e21a0977","src/seg.rs":"cb3e552aad59bdbd0023c5a27b9c04bc61609175e03e6400d38767d964152288"},"package":"defaa24ecc093c77630e6c15e17c51f5e187bf35ee514f4e2d67baaa96dae22b"} \ No newline at end of file diff --git a/vendor/ciborium-ll/Cargo.toml b/vendor/ciborium-ll/Cargo.toml new file mode 100644 index 000000000..29675d1b4 --- /dev/null +++ b/vendor/ciborium-ll/Cargo.toml @@ -0,0 +1,49 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2021" +rust-version = "1.56" +name = "ciborium-ll" +version = "0.2.1" +authors = ["Nathaniel McCallum "] +description = "Low-level CBOR codec primitives" +homepage = "https://github.com/enarx/ciborium" +readme = "README.md" +keywords = ["cbor"] +categories = ["data-structures", "embedded", "encoding", "no-std", "parsing"] +license = "Apache-2.0" +repository = "https://github.com/enarx/ciborium" +[package.metadata.docs.rs] +all-features = true +[dependencies.ciborium-io] +version = "0.2.1" + +[dependencies.half] +version = "1.6" +[dev-dependencies.hex] +version = "0.4" + +[features] +alloc = [] +std = ["alloc"] +[badges.github] +repository = "enarx/ciborium" +workflow = "test" + +[badges.is-it-maintained-issue-resolution] +repository = "enarx/ciborium" + +[badges.is-it-maintained-open-issues] +repository = "enarx/ciborium" + +[badges.maintenance] +status = "actively-developed" diff --git a/vendor/ciborium-ll/LICENSE b/vendor/ciborium-ll/LICENSE new file mode 100644 index 000000000..261eeb9e9 --- /dev/null +++ b/vendor/ciborium-ll/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/ciborium-ll/README.md b/vendor/ciborium-ll/README.md new file mode 100644 index 000000000..2ca6306a7 --- /dev/null +++ b/vendor/ciborium-ll/README.md @@ -0,0 +1,131 @@ +[![Workflow Status](https://github.com/enarx/ciborium/workflows/test/badge.svg)](https://github.com/enarx/ciborium/actions?query=workflow%3A%22test%22) +[![Average time to resolve an issue](https://isitmaintained.com/badge/resolution/enarx/ciborium.svg)](https://isitmaintained.com/project/enarx/ciborium "Average time to resolve an issue") +[![Percentage of issues still open](https://isitmaintained.com/badge/open/enarx/ciborium.svg)](https://isitmaintained.com/project/enarx/ciborium "Percentage of issues still open") +![Maintenance](https://img.shields.io/badge/maintenance-activly--developed-brightgreen.svg) + +# ciborium-ll + +Low level CBOR parsing tools + +This crate contains low-level types for encoding and decoding items in +CBOR. This crate is usable in both `no_std` and `no_alloc` environments. +To understand how this crate works, first we will look at the structure +of a CBOR item on the wire. + +## Anatomy of a CBOR Item + +This is a brief anatomy of a CBOR item on the wire. + +``` ++------------+-----------+ +| | | +| Major | Minor | +| (3bits) | (5bits) | +| | | ++------------+-----------+ +^ ^ +| | ++-----+ +-----+ + | | + | | + +----------------------------+--------------+ + | | | | + | Prefix | Affix | Suffix | + | (1 byte) | (0-8 bytes) | (0+ bytes) | + | | | | + +------------+---------------+--------------+ + + | | | + +------------+---------------+--------------+ + | | + v v + + Header Body +``` + +The `ciborium` crate works by providing the `Decoder` and `Encoder` types +which provide input and output for a CBOR header (see: `Header`). From +there, you can either handle the body yourself or use the provided utility +functions. + +For more information on the CBOR format, see +[RFC 7049](https://tools.ietf.org/html/rfc7049). + +## Decoding + +In order to decode CBOR, you will create a `Decoder` from a reader. The +decoder instance will allow you to `Decoder::pull()` `Header` instances +from the input. + +Most CBOR items are fully contained in their headers and therefore have no +body. These items can be evaluated directly from the `Header` instance. + +Bytes and text items have a body but do not contain child items. Since +both bytes and text values may be segmented, parsing them can be a bit +tricky. Therefore, we provide helper functions to parse these types. See +`Decoder::bytes()` and `Decoder::text()` for more details. + +Array and map items have a body which contains child items. These can be +parsed by simply doing `Decoder::pull()` to parse the child items. + +### Example + +```rust +use ciborium_ll::{Decoder, Header}; +use ciborium_io::Read as _; + +let input = b"\x6dHello, World!"; +let mut decoder = Decoder::from(&input[..]); +let mut chunks = 0; + +match decoder.pull().unwrap() { + Header::Text(len) => { + let mut segments = decoder.text(len); + while let Some(mut segment) = segments.pull().unwrap() { + let mut buffer = [0u8; 7]; + while let Some(chunk) = segment.pull(&mut buffer[..]).unwrap() { + match chunk { + "Hello, " if chunks == 0 => chunks = 1, + "World!" if chunks == 1 => chunks = 2, + _ => panic!("received unexpected chunk"), + } + } + } + } + + _ => panic!("received unexpected value"), +} + +assert_eq!(chunks, 2); +``` + +## Encoding + +To encode values to CBOR, create an `Encoder` from a writer. The encoder +instance provides the `Encoder::push()` method to write a `Header` value +to the wire. CBOR item bodies can be written directly. + +For bytes and text, there are the `Encoder::bytes()` and `Encoder::text()` +utility functions, respectively, which will properly segment the output +on the wire for you. + +### Example + +```rust +use ciborium_ll::{Encoder, Header}; +use ciborium_io::Write as _; + +let mut buffer = [0u8; 19]; +let mut encoder = Encoder::from(&mut buffer[..]); + +// Write the structure +encoder.push(Header::Map(Some(1))).unwrap(); +encoder.push(Header::Positive(7)).unwrap(); +encoder.text("Hello, World!", 7).unwrap(); + +// Validate our output +encoder.flush().unwrap(); +assert_eq!(b"\xa1\x07\x7f\x67Hello, \x66World!\xff", &buffer[..]); +``` + +License: Apache-2.0 diff --git a/vendor/ciborium-ll/src/dec.rs b/vendor/ciborium-ll/src/dec.rs new file mode 100644 index 000000000..832963451 --- /dev/null +++ b/vendor/ciborium-ll/src/dec.rs @@ -0,0 +1,174 @@ +use super::*; + +use ciborium_io::Read; + +/// An error that occurred while decoding +#[derive(Debug)] +pub enum Error { + /// An error occurred while reading bytes + /// + /// Contains the underlying error reaturned while reading. + Io(T), + + /// An error occurred while parsing bytes + /// + /// Contains the offset into the stream where the syntax error occurred. + Syntax(usize), +} + +impl From for Error { + #[inline] + fn from(value: T) -> Self { + Self::Io(value) + } +} + +/// A decoder for deserializing CBOR items +/// +/// This decoder manages the low-level decoding of CBOR items into `Header` +/// objects. It also contains utility functions for parsing segmented bytes +/// and text inputs. +pub struct Decoder { + reader: R, + offset: usize, + buffer: Option, +} + +impl<R: Read> From<R> for Decoder<R> { + #[inline] + fn from(value: R) -> Self { + Self { + reader: value, + offset: 0, + buffer: None, + } + } +} + +impl<R: Read> Read for Decoder<R> { + type Error = R::Error; + + #[inline] + fn read_exact(&mut self, data: &mut [u8]) -> Result<(), Self::Error> { + assert!(self.buffer.is_none()); + self.reader.read_exact(data)?; + self.offset += data.len(); + Ok(()) + } +} + +impl<R: Read> Decoder<R> { + #[inline] + fn pull_title(&mut self) -> Result<Title, Error<R::Error>> { + if let Some(title) = self.buffer.take() { + self.offset += title.1.as_ref().len() + 1; + return Ok(title); + } + + let mut prefix = [0u8; 1]; + self.read_exact(&mut prefix[..])?; + + let major = match prefix[0] >> 5 { + 0 => Major::Positive, + 1 => Major::Negative, + 2 => Major::Bytes, + 3 => Major::Text, + 4 => Major::Array, + 5 => Major::Map, + 6 => Major::Tag, + 7 => Major::Other, + _ => unreachable!(), + }; + + let mut minor = match prefix[0] & 0b00011111 { + x if x < 24 => Minor::This(x), + 24 => Minor::Next1([0; 1]), + 25 => Minor::Next2([0; 2]), + 26 => Minor::Next4([0; 4]), + 27 => Minor::Next8([0; 8]), + 31 => Minor::More, + _ => return Err(Error::Syntax(self.offset - 1)), + }; + + self.read_exact(minor.as_mut())?; + Ok(Title(major, minor)) + } + + #[inline] + fn push_title(&mut self, item: Title) { + assert!(self.buffer.is_none()); + self.buffer = Some(item); + self.offset -= item.1.as_ref().len() + 1; + } + + /// Pulls the next header from the input + #[inline] + pub fn pull(&mut self) -> Result<Header, Error<R::Error>> { + let offset = self.offset; + self.pull_title()? + .try_into() + .map_err(|_| Error::Syntax(offset)) + } + + /// Push a single header into the input buffer + /// + /// # Panics + /// + /// This function panics if called while there is already a header in the + /// input buffer. You should take care to call this function only after + /// pulling a header to ensure there is nothing in the input buffer. + #[inline] + pub fn push(&mut self, item: Header) { + self.push_title(Title::from(item)) + } + + /// Gets the current byte offset into the stream + /// + /// The offset starts at zero when the decoder is created. Therefore, if + /// bytes were already read from the reader before the decoder was created, + /// you must account for this. + #[inline] + pub fn offset(&mut self) -> usize { + self.offset + } + + /// Process an incoming bytes item + /// + /// In CBOR, bytes can be segmented. The logic for this can be a bit tricky, + /// so we encapsulate that logic using this function. This function **MUST** + /// be called immediately after first pulling a `Header::Bytes(len)` from + /// the wire and `len` must be provided to this function from that value. + /// + /// The `buf` parameter provides a buffer used when reading in the segmented + /// bytes. A large buffer will result in fewer calls to read incoming bytes + /// at the cost of memory usage. You should consider this trade off when + /// deciding the size of your buffer. + #[inline] + pub fn bytes(&mut self, len: Option<usize>) -> Segments<R, crate::seg::Bytes> { + self.push(Header::Bytes(len)); + Segments::new(self, |header| match header { + Header::Bytes(len) => Ok(len), + _ => Err(()), + }) + } + + /// Process an incoming text item + /// + /// In CBOR, text can be segmented. The logic for this can be a bit tricky, + /// so we encapsulate that logic using this function. This function **MUST** + /// be called immediately after first pulling a `Header::Text(len)` from + /// the wire and `len` must be provided to this function from that value. + /// + /// The `buf` parameter provides a buffer used when reading in the segmented + /// text. A large buffer will result in fewer calls to read incoming bytes + /// at the cost of memory usage. You should consider this trade off when + /// deciding the size of your buffer. + #[inline] + pub fn text(&mut self, len: Option<usize>) -> Segments<R, crate::seg::Text> { + self.push(Header::Text(len)); + Segments::new(self, |header| match header { + Header::Text(len) => Ok(len), + _ => Err(()), + }) + } +} diff --git a/vendor/ciborium-ll/src/enc.rs b/vendor/ciborium-ll/src/enc.rs new file mode 100644 index 000000000..b8b8a20d6 --- /dev/null +++ b/vendor/ciborium-ll/src/enc.rs @@ -0,0 +1,127 @@ +use super::*; + +use ciborium_io::Write; + +/// An encoder for serializing CBOR items +/// +/// This structure wraps a writer and provides convenience functions for +/// writing `Header` objects to the wire. +pub struct Encoder<W: Write>(W); + +impl<W: Write> From<W> for Encoder<W> { + #[inline] + fn from(value: W) -> Self { + Self(value) + } +} + +impl<W: Write> Write for Encoder<W> { + type Error = W::Error; + + fn write_all(&mut self, data: &[u8]) -> Result<(), Self::Error> { + self.0.write_all(data) + } + + fn flush(&mut self) -> Result<(), Self::Error> { + self.0.flush() + } +} + +impl<W: Write> Encoder<W> { + /// Push a `Header` to the wire + #[inline] + pub fn push(&mut self, header: Header) -> Result<(), W::Error> { + let title = Title::from(header); + + let major = match title.0 { + Major::Positive => 0, + Major::Negative => 1, + Major::Bytes => 2, + Major::Text => 3, + Major::Array => 4, + Major::Map => 5, + Major::Tag => 6, + Major::Other => 7, + }; + + let minor = match title.1 { + Minor::This(x) => x, + Minor::Next1(..) => 24, + Minor::Next2(..) => 25, + Minor::Next4(..) => 26, + Minor::Next8(..) => 27, + Minor::More => 31, + }; + + self.0.write_all(&[major << 5 | minor])?; + self.0.write_all(title.1.as_ref()) + } + + /// Serialize a byte slice as CBOR + /// + /// Optionally, segment the output into `segment` size segments. Note that + /// if `segment == Some(0)` it will be silently upgraded to `Some(1)`. This + /// minimum value is highly inefficient and should not be relied upon. + #[inline] + pub fn bytes( + &mut self, + value: &[u8], + segment: impl Into<Option<usize>>, + ) -> Result<(), W::Error> { + let max = segment.into().unwrap_or(value.len()); + let max = core::cmp::max(max, 1); + + if max >= value.len() { + self.push(Header::Bytes(Some(value.len())))?; + self.write_all(value)?; + } else { + self.push(Header::Bytes(None))?; + + for chunk in value.chunks(max) { + self.push(Header::Bytes(Some(chunk.len())))?; + self.write_all(chunk)?; + } + + self.push(Header::Break)?; + } + + Ok(()) + } + + /// Serialize a string slice as CBOR + /// + /// Optionally, segment the output into `segment` size segments. Note that + /// since care is taken to ensure that each segment is itself a valid UTF-8 + /// string, if `segment` contains a value of less than 4, it will be + /// silently upgraded to 4. This minimum value is highly inefficient and + /// should not be relied upon. + #[inline] + pub fn text(&mut self, value: &str, segment: impl Into<Option<usize>>) -> Result<(), W::Error> { + let max = segment.into().unwrap_or(value.len()); + let max = core::cmp::max(max, 4); + + if max >= value.len() { + self.push(Header::Text(Some(value.len())))?; + self.write_all(value.as_bytes())?; + } else { + self.push(Header::Text(None))?; + + let mut bytes = value.as_bytes(); + while !bytes.is_empty() { + let mut len = core::cmp::min(bytes.len(), max); + while len > 0 && core::str::from_utf8(&bytes[..len]).is_err() { + len -= 1 + } + + let (prefix, suffix) = bytes.split_at(len); + self.push(Header::Text(Some(prefix.len())))?; + self.write_all(prefix)?; + bytes = suffix; + } + + self.push(Header::Break)?; + } + + Ok(()) + } +} diff --git a/vendor/ciborium-ll/src/hdr.rs b/vendor/ciborium-ll/src/hdr.rs new file mode 100644 index 000000000..dec178815 --- /dev/null +++ b/vendor/ciborium-ll/src/hdr.rs @@ -0,0 +1,163 @@ +use super::*; + +use half::f16; + +/// A semantic representation of a CBOR item header +/// +/// This structure represents the valid values of a CBOR item header and is +/// used extensively when serializing or deserializing CBOR items. Note well +/// that this structure **DOES NOT** represent the body (i.e. suffix) of the +/// CBOR item. You must parse the body yourself based on the contents of the +/// `Header`. However, utility functions are provided for this (see: +/// `Decoder::bytes()` and `Decoder::text()`). +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum Header { + /// A positive integer + Positive(u64), + + /// A negative integer + /// + /// Note well that this value has all bits inverted from a normal signed + /// integer. For example, to convert the `u64` to a `i128` you would do + /// this: `neg as i128 ^ !0`. + Negative(u64), + + /// A floating point value + Float(f64), + + /// A "simple" value + Simple(u8), + + /// A tag + Tag(u64), + + /// The "break" value + /// + /// This value is used to terminate indefinite length arrays and maps, + /// as well as segmented byte or text items. + Break, + + /// A bytes item + /// + /// The value contained in this variant indicates the length of the bytes + /// which follow or, if `None`, segmented bytes input. + /// + /// A best practice is to call `Decoder::bytes()` immediately after + /// first pulling a bytes item header since this utility function + /// encapsulates all the logic needed to handle segmentation. + Bytes(Option<usize>), + + /// A text item + /// + /// The value contained in this variant indicates the length of the text + /// which follows (in bytes) or, if `None`, segmented text input. + /// + /// A best practice is to call `Decoder::text()` immediately after + /// first pulling a text item header since this utility function + /// encapsulates all the logic needed to handle segmentation. + Text(Option<usize>), + + /// An array item + /// + /// The value contained in this variant indicates the length of the array + /// which follows (in items) or, if `None`, an indefinite length array + /// terminated by a "break" value. + Array(Option<usize>), + + /// An map item + /// + /// The value contained in this variant indicates the length of the map + /// which follows (in item pairs) or, if `None`, an indefinite length map + /// terminated by a "break" value. + Map(Option<usize>), +} + +impl TryFrom<Title> for Header { + type Error = InvalidError; + + fn try_from(title: Title) -> Result<Self, Self::Error> { + let opt = |minor| { + Some(match minor { + Minor::This(x) => x.into(), + Minor::Next1(x) => u8::from_be_bytes(x).into(), + Minor::Next2(x) => u16::from_be_bytes(x).into(), + Minor::Next4(x) => u32::from_be_bytes(x).into(), + Minor::Next8(x) => u64::from_be_bytes(x), + Minor::More => return None, + }) + }; + + let int = |m| opt(m).ok_or(InvalidError(())); + + let len = |m| { + opt(m) + .map(usize::try_from) + .transpose() + .or(Err(InvalidError(()))) + }; + + Ok(match title { + Title(Major::Positive, minor) => Self::Positive(int(minor)?), + Title(Major::Negative, minor) => Self::Negative(int(minor)?), + Title(Major::Bytes, minor) => Self::Bytes(len(minor)?), + Title(Major::Text, minor) => Self::Text(len(minor)?), + Title(Major::Array, minor) => Self::Array(len(minor)?), + Title(Major::Map, minor) => Self::Map(len(minor)?), + Title(Major::Tag, minor) => Self::Tag(int(minor)?), + + Title(Major::Other, Minor::More) => Self::Break, + Title(Major::Other, Minor::This(x)) => Self::Simple(x), + Title(Major::Other, Minor::Next1(x)) => Self::Simple(x[0]), + Title(Major::Other, Minor::Next2(x)) => Self::Float(f16::from_be_bytes(x).into()), + Title(Major::Other, Minor::Next4(x)) => Self::Float(f32::from_be_bytes(x).into()), + Title(Major::Other, Minor::Next8(x)) => Self::Float(f64::from_be_bytes(x)), + }) + } +} + +impl From<Header> for Title { + fn from(header: Header) -> Self { + let int = |i: u64| match i { + x if x <= 23 => Minor::This(i as u8), + x if x <= core::u8::MAX as u64 => Minor::Next1([i as u8]), + x if x <= core::u16::MAX as u64 => Minor::Next2((i as u16).to_be_bytes()), + x if x <= core::u32::MAX as u64 => Minor::Next4((i as u32).to_be_bytes()), + x => Minor::Next8(x.to_be_bytes()), + }; + + let len = |l: Option<usize>| l.map(|x| int(x as u64)).unwrap_or(Minor::More); + + match header { + Header::Positive(x) => Title(Major::Positive, int(x)), + Header::Negative(x) => Title(Major::Negative, int(x)), + Header::Bytes(x) => Title(Major::Bytes, len(x)), + Header::Text(x) => Title(Major::Text, len(x)), + Header::Array(x) => Title(Major::Array, len(x)), + Header::Map(x) => Title(Major::Map, len(x)), + Header::Tag(x) => Title(Major::Tag, int(x)), + + Header::Break => Title(Major::Other, Minor::More), + + Header::Simple(x) => match x { + x @ 0..=23 => Title(Major::Other, Minor::This(x)), + x => Title(Major::Other, Minor::Next1([x])), + }, + + Header::Float(n64) => { + let n16 = f16::from_f64(n64); + let n32 = n64 as f32; + + Title( + Major::Other, + if f64::from(n16).to_bits() == n64.to_bits() { + Minor::Next2(n16.to_be_bytes()) + } else if f64::from(n32).to_bits() == n64.to_bits() { + Minor::Next4(n32.to_be_bytes()) + } else { + Minor::Next8(n64.to_be_bytes()) + }, + ) + } + } + } +} diff --git a/vendor/ciborium-ll/src/lib.rs b/vendor/ciborium-ll/src/lib.rs new file mode 100644 index 000000000..8a1fe90ca --- /dev/null +++ b/vendor/ciborium-ll/src/lib.rs @@ -0,0 +1,487 @@ +// SPDX-License-Identifier: Apache-2.0 + +//! Low level CBOR parsing tools +//! +//! This crate contains low-level types for encoding and decoding items in +//! CBOR. This crate is usable in both `no_std` and `no_alloc` environments. +//! To understand how this crate works, first we will look at the structure +//! of a CBOR item on the wire. +//! +//! # Anatomy of a CBOR Item +//! +//! This is a brief anatomy of a CBOR item on the wire. +//! +//! ```text +//! +------------+-----------+ +//! | | | +//! | Major | Minor | +//! | (3bits) | (5bits) | +//! | | | +//! +------------+-----------+ +//! ^ ^ +//! | | +//! +-----+ +-----+ +//! | | +//! | | +//! +----------------------------+--------------+ +//! | | | | +//! | Prefix | Affix | Suffix | +//! | (1 byte) | (0-8 bytes) | (0+ bytes) | +//! | | | | +//! +------------+---------------+--------------+ +//! +//! | | | +//! +------------+---------------+--------------+ +//! | | +//! v v +//! +//! Header Body +//! ``` +//! +//! The `ciborium` crate works by providing the `Decoder` and `Encoder` types +//! which provide input and output for a CBOR header (see: `Header`). From +//! there, you can either handle the body yourself or use the provided utility +//! functions. +//! +//! For more information on the CBOR format, see +//! [RFC 7049](https://tools.ietf.org/html/rfc7049). +//! +//! # Decoding +//! +//! In order to decode CBOR, you will create a `Decoder` from a reader. The +//! decoder instance will allow you to `Decoder::pull()` `Header` instances +//! from the input. +//! +//! Most CBOR items are fully contained in their headers and therefore have no +//! body. These items can be evaluated directly from the `Header` instance. +//! +//! Bytes and text items have a body but do not contain child items. Since +//! both bytes and text values may be segmented, parsing them can be a bit +//! tricky. Therefore, we provide helper functions to parse these types. See +//! `Decoder::bytes()` and `Decoder::text()` for more details. +//! +//! Array and map items have a body which contains child items. These can be +//! parsed by simply doing `Decoder::pull()` to parse the child items. +//! +//! ## Example +//! +//! ```rust +//! use ciborium_ll::{Decoder, Header}; +//! use ciborium_io::Read as _; +//! +//! let input = b"\x6dHello, World!"; +//! let mut decoder = Decoder::from(&input[..]); +//! let mut chunks = 0; +//! +//! match decoder.pull().unwrap() { +//! Header::Text(len) => { +//! let mut segments = decoder.text(len); +//! while let Some(mut segment) = segments.pull().unwrap() { +//! let mut buffer = [0u8; 7]; +//! while let Some(chunk) = segment.pull(&mut buffer[..]).unwrap() { +//! match chunk { +//! "Hello, " if chunks == 0 => chunks = 1, +//! "World!" if chunks == 1 => chunks = 2, +//! _ => panic!("received unexpected chunk"), +//! } +//! } +//! } +//! } +//! +//! _ => panic!("received unexpected value"), +//! } +//! +//! assert_eq!(chunks, 2); +//! ``` +//! +//! # Encoding +//! +//! To encode values to CBOR, create an `Encoder` from a writer. The encoder +//! instance provides the `Encoder::push()` method to write a `Header` value +//! to the wire. CBOR item bodies can be written directly. +//! +//! For bytes and text, there are the `Encoder::bytes()` and `Encoder::text()` +//! utility functions, respectively, which will properly segment the output +//! on the wire for you. +//! +//! ## Example +//! +//! ```rust +//! use ciborium_ll::{Encoder, Header}; +//! use ciborium_io::Write as _; +//! +//! let mut buffer = [0u8; 19]; +//! let mut encoder = Encoder::from(&mut buffer[..]); +//! +//! // Write the structure +//! encoder.push(Header::Map(Some(1))).unwrap(); +//! encoder.push(Header::Positive(7)).unwrap(); +//! encoder.text("Hello, World!", 7).unwrap(); +//! +//! // Validate our output +//! encoder.flush().unwrap(); +//! assert_eq!(b"\xa1\x07\x7f\x67Hello, \x66World!\xff", &buffer[..]); +//! ``` + +#![cfg_attr(not(feature = "std"), no_std)] +#![deny(missing_docs)] +#![deny(clippy::all)] +#![deny(clippy::cargo)] + +#[cfg(feature = "alloc")] +extern crate alloc; + +mod dec; +mod enc; +mod hdr; +mod seg; + +pub use dec::*; +pub use enc::*; +pub use hdr::*; +pub use seg::{Segment, Segments}; + +/// Simple value constants +pub mod simple { + #![allow(missing_docs)] + + pub const FALSE: u8 = 20; + pub const TRUE: u8 = 21; + pub const NULL: u8 = 22; + pub const UNDEFINED: u8 = 23; +} + +/// Tag constants +pub mod tag { + #![allow(missing_docs)] + + pub const BIGPOS: u64 = 2; + pub const BIGNEG: u64 = 3; +} + +#[derive(Debug)] +struct InvalidError(()); + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +enum Major { + Positive, + Negative, + Bytes, + Text, + Array, + Map, + Tag, + Other, +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +enum Minor { + This(u8), + Next1([u8; 1]), + Next2([u8; 2]), + Next4([u8; 4]), + Next8([u8; 8]), + More, +} + +impl AsRef<[u8]> for Minor { + #[inline] + fn as_ref(&self) -> &[u8] { + match self { + Self::More => &[], + Self::This(..) => &[], + Self::Next1(x) => x.as_ref(), + Self::Next2(x) => x.as_ref(), + Self::Next4(x) => x.as_ref(), + Self::Next8(x) => x.as_ref(), + } + } +} + +impl AsMut<[u8]> for Minor { + #[inline] + fn as_mut(&mut self) -> &mut [u8] { + match self { + Self::More => &mut [], + Self::This(..) => &mut [], + Self::Next1(x) => x.as_mut(), + Self::Next2(x) => x.as_mut(), + Self::Next4(x) => x.as_mut(), + Self::Next8(x) => x.as_mut(), + } + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +struct Title(pub Major, pub Minor); + +#[cfg(test)] +mod tests { + use super::*; + + macro_rules! neg { + ($i:expr) => { + Header::Negative((($i as i128) ^ !0) as u64) + }; + } + + #[allow(clippy::excessive_precision)] + #[test] + fn leaf() { + use core::f64::{INFINITY, NAN}; + + let data = &[ + (Header::Positive(0), "00", true), + (Header::Positive(1), "01", true), + (Header::Positive(10), "0a", true), + (Header::Positive(23), "17", true), + (Header::Positive(24), "1818", true), + (Header::Positive(25), "1819", true), + (Header::Positive(100), "1864", true), + (Header::Positive(1000), "1903e8", true), + (Header::Positive(1000000), "1a000f4240", true), + (Header::Positive(1000000000000), "1b000000e8d4a51000", true), + ( + Header::Positive(18446744073709551615), + "1bffffffffffffffff", + true, + ), + (neg!(-18446744073709551616), "3bffffffffffffffff", true), + (neg!(-1), "20", true), + (neg!(-10), "29", true), + (neg!(-100), "3863", true), + (neg!(-1000), "3903e7", true), + (Header::Float(0.0), "f90000", true), + (Header::Float(-0.0), "f98000", true), + (Header::Float(1.0), "f93c00", true), + (Header::Float(1.1), "fb3ff199999999999a", true), + (Header::Float(1.5), "f93e00", true), + (Header::Float(65504.0), "f97bff", true), + (Header::Float(100000.0), "fa47c35000", true), + (Header::Float(3.4028234663852886e+38), "fa7f7fffff", true), + (Header::Float(1.0e+300), "fb7e37e43c8800759c", true), + (Header::Float(5.960464477539063e-8), "f90001", true), + (Header::Float(0.00006103515625), "f90400", true), + (Header::Float(-4.0), "f9c400", true), + (Header::Float(-4.1), "fbc010666666666666", true), + (Header::Float(INFINITY), "f97c00", true), + (Header::Float(NAN), "f97e00", true), + (Header::Float(-INFINITY), "f9fc00", true), + (Header::Float(INFINITY), "fa7f800000", false), + (Header::Float(NAN), "fa7fc00000", false), + (Header::Float(-INFINITY), "faff800000", false), + (Header::Float(INFINITY), "fb7ff0000000000000", false), + (Header::Float(NAN), "fb7ff8000000000000", false), + (Header::Float(-INFINITY), "fbfff0000000000000", false), + (Header::Simple(simple::FALSE), "f4", true), + (Header::Simple(simple::TRUE), "f5", true), + (Header::Simple(simple::NULL), "f6", true), + (Header::Simple(simple::UNDEFINED), "f7", true), + (Header::Simple(16), "f0", true), + (Header::Simple(24), "f818", true), + (Header::Simple(255), "f8ff", true), + (Header::Tag(0), "c0", true), + (Header::Tag(1), "c1", true), + (Header::Tag(23), "d7", true), + (Header::Tag(24), "d818", true), + (Header::Tag(32), "d820", true), + (Header::Bytes(Some(0)), "40", true), + (Header::Bytes(Some(4)), "44", true), + (Header::Text(Some(0)), "60", true), + (Header::Text(Some(4)), "64", true), + ]; + + for (header, bytes, encode) in data.iter().cloned() { + let bytes = hex::decode(bytes).unwrap(); + + let mut decoder = Decoder::from(&bytes[..]); + match (header, decoder.pull().unwrap()) { + // NaN equality... + (Header::Float(l), Header::Float(r)) if l.is_nan() && r.is_nan() => (), + + // Everything else... + (l, r) => assert_eq!(l, r), + } + + if encode { + let mut buffer = [0u8; 1024]; + let mut writer = &mut buffer[..]; + let mut encoder = Encoder::from(&mut writer); + encoder.push(header).unwrap(); + + let len = writer.len(); + assert_eq!(&bytes[..], &buffer[..1024 - len]); + } + } + } + + #[test] + fn node() { + let data: &[(&str, &[Header])] = &[ + ("80", &[Header::Array(Some(0))]), + ( + "83010203", + &[ + Header::Array(Some(3)), + Header::Positive(1), + Header::Positive(2), + Header::Positive(3), + ], + ), + ( + "98190102030405060708090a0b0c0d0e0f101112131415161718181819", + &[ + Header::Array(Some(25)), + Header::Positive(1), + Header::Positive(2), + Header::Positive(3), + Header::Positive(4), + Header::Positive(5), + Header::Positive(6), + Header::Positive(7), + Header::Positive(8), + Header::Positive(9), + Header::Positive(10), + Header::Positive(11), + Header::Positive(12), + Header::Positive(13), + Header::Positive(14), + Header::Positive(15), + Header::Positive(16), + Header::Positive(17), + Header::Positive(18), + Header::Positive(19), + Header::Positive(20), + Header::Positive(21), + Header::Positive(22), + Header::Positive(23), + Header::Positive(24), + Header::Positive(25), + ], + ), + ("a0", &[Header::Map(Some(0))]), + ( + "a201020304", + &[ + Header::Map(Some(2)), + Header::Positive(1), + Header::Positive(2), + Header::Positive(3), + Header::Positive(4), + ], + ), + ("9fff", &[Header::Array(None), Header::Break]), + ( + "9f018202039f0405ffff", + &[ + Header::Array(None), + Header::Positive(1), + Header::Array(Some(2)), + Header::Positive(2), + Header::Positive(3), + Header::Array(None), + Header::Positive(4), + Header::Positive(5), + Header::Break, + Header::Break, + ], + ), + ( + "9f01820203820405ff", + &[ + Header::Array(None), + Header::Positive(1), + Header::Array(Some(2)), + Header::Positive(2), + Header::Positive(3), + Header::Array(Some(2)), + Header::Positive(4), + Header::Positive(5), + Header::Break, + ], + ), + ( + "83018202039f0405ff", + &[ + Header::Array(Some(3)), + Header::Positive(1), + Header::Array(Some(2)), + Header::Positive(2), + Header::Positive(3), + Header::Array(None), + Header::Positive(4), + Header::Positive(5), + Header::Break, + ], + ), + ( + "83019f0203ff820405", + &[ + Header::Array(Some(3)), + Header::Positive(1), + Header::Array(None), + Header::Positive(2), + Header::Positive(3), + Header::Break, + Header::Array(Some(2)), + Header::Positive(4), + Header::Positive(5), + ], + ), + ( + "9f0102030405060708090a0b0c0d0e0f101112131415161718181819ff", + &[ + Header::Array(None), + Header::Positive(1), + Header::Positive(2), + Header::Positive(3), + Header::Positive(4), + Header::Positive(5), + Header::Positive(6), + Header::Positive(7), + Header::Positive(8), + Header::Positive(9), + Header::Positive(10), + Header::Positive(11), + Header::Positive(12), + Header::Positive(13), + Header::Positive(14), + Header::Positive(15), + Header::Positive(16), + Header::Positive(17), + Header::Positive(18), + Header::Positive(19), + Header::Positive(20), + Header::Positive(21), + Header::Positive(22), + Header::Positive(23), + Header::Positive(24), + Header::Positive(25), + Header::Break, + ], + ), + ]; + + for (bytes, headers) in data { + let bytes = hex::decode(bytes).unwrap(); + + // Test decoding + let mut decoder = Decoder::from(&bytes[..]); + for header in headers.iter().cloned() { + assert_eq!(header, decoder.pull().unwrap()); + } + + // Test encoding + let mut buffer = [0u8; 1024]; + let mut writer = &mut buffer[..]; + let mut encoder = Encoder::from(&mut writer); + + for header in headers.iter().cloned() { + encoder.push(header).unwrap(); + } + + let len = writer.len(); + assert_eq!(&bytes[..], &buffer[..1024 - len]); + } + } +} diff --git a/vendor/ciborium-ll/src/seg.rs b/vendor/ciborium-ll/src/seg.rs new file mode 100644 index 000000000..95eaac9b0 --- /dev/null +++ b/vendor/ciborium-ll/src/seg.rs @@ -0,0 +1,216 @@ +use super::*; + +use ciborium_io::Read; + +use core::marker::PhantomData; + +/// A parser for incoming segments +pub trait Parser: Default { + /// The type of item that is parsed + type Item: ?Sized; + + /// The parsing error that may occur + type Error; + + /// The main parsing function + /// + /// This function processes the incoming bytes and returns the item. + /// + /// One important detail that **MUST NOT** be overlooked is that the + /// parser may save data from a previous parsing attempt. The number of + /// bytes saved is indicated by the `Parser::saved()` function. The saved + /// bytes will be copied into the beginning of the `bytes` array before + /// processing. Therefore, two requirements should be met. + /// + /// First, the incoming byte slice should be larger than the saved bytes. + /// + /// Second, the incoming byte slice should contain new bytes only after + /// the saved byte prefix. + /// + /// If both criteria are met, this allows the parser to prepend its saved + /// bytes without any additional allocation. + fn parse<'a>(&mut self, bytes: &'a mut [u8]) -> Result<&'a Self::Item, Self::Error>; + + /// Indicates the number of saved bytes in the parser + fn saved(&self) -> usize { + 0 + } +} + +/// A bytes parser +/// +/// No actual processing is performed and the input bytes are directly +/// returned. This implies that this parser never saves any bytes internally. +#[derive(Default)] +pub struct Bytes(()); + +impl Parser for Bytes { + type Item = [u8]; + type Error = core::convert::Infallible; + + fn parse<'a>(&mut self, bytes: &'a mut [u8]) -> Result<&'a [u8], Self::Error> { + Ok(bytes) + } +} + +/// A text parser +/// +/// This parser converts the input bytes to a `str`. This parser preserves +/// trailing invalid UTF-8 sequences in the case that chunking fell in the +/// middle of a valid UTF-8 character. +#[derive(Default)] +pub struct Text { + stored: usize, + buffer: [u8; 3], +} + +impl Parser for Text { + type Item = str; + type Error = core::str::Utf8Error; + + fn parse<'a>(&mut self, bytes: &'a mut [u8]) -> Result<&'a str, Self::Error> { + // If we cannot advance, return nothing. + if bytes.len() <= self.stored { + return Ok(""); + } + + // Copy previously invalid data into place. + bytes[..self.stored].clone_from_slice(&self.buffer[..self.stored]); + + Ok(match core::str::from_utf8(bytes) { + Ok(s) => { + self.stored = 0; + s + } + Err(e) => { + let valid_len = e.valid_up_to(); + let invalid_len = bytes.len() - valid_len; + + // If the size of the invalid UTF-8 is large enough to hold + // all valid UTF-8 characters, we have a syntax error. + if invalid_len > self.buffer.len() { + return Err(e); + } + + // Otherwise, store the invalid bytes for the next read cycle. + self.buffer[..invalid_len].clone_from_slice(&bytes[valid_len..]); + self.stored = invalid_len; + + // Decode the valid part of the string. + core::str::from_utf8(&bytes[..valid_len]).unwrap() + } + }) + } + + fn saved(&self) -> usize { + self.stored + } +} + +/// A CBOR segment +/// +/// This type represents a single bytes or text segment on the wire. It can be +/// read out in parsed chunks based on the size of the input scratch buffer. +pub struct Segment<'r, R: Read, P: Parser> { + reader: &'r mut Decoder<R>, + unread: usize, + offset: usize, + parser: P, +} + +impl<'r, R: Read, P: Parser> Segment<'r, R, P> { + /// Gets the number of unprocessed bytes + #[inline] + pub fn left(&self) -> usize { + self.unread + self.parser.saved() + } + + /// Gets the next parsed chunk within the segment + /// + /// Returns `Ok(None)` when all chunks have been read. + #[inline] + pub fn pull<'a>( + &mut self, + buffer: &'a mut [u8], + ) -> Result<Option<&'a P::Item>, Error<R::Error>> { + use core::cmp::min; + + let prev = self.parser.saved(); + match self.unread { + 0 if prev == 0 => return Ok(None), + 0 => return Err(Error::Syntax(self.offset)), + _ => (), + } + + // Determine how many bytes to read. + let size = min(buffer.len(), prev + self.unread); + let full = &mut buffer[..size]; + let next = &mut full[min(size, prev)..]; + + // Read additional bytes. + self.reader.read_exact(next)?; + self.unread -= next.len(); + + self.parser + .parse(full) + .or(Err(Error::Syntax(self.offset))) + .map(Some) + } +} + +/// A sequence of CBOR segments +/// +/// CBOR allows for bytes or text items to be segmented. This type represents +/// the state of that segmented input stream. +pub struct Segments<'r, R: Read, P: Parser> { + reader: &'r mut Decoder<R>, + finish: bool, + nested: usize, + parser: PhantomData<P>, + unwrap: fn(Header) -> Result<Option<usize>, ()>, +} + +impl<'r, R: Read, P: Parser> Segments<'r, R, P> { + #[inline] + pub(crate) fn new( + decoder: &'r mut Decoder<R>, + unwrap: fn(Header) -> Result<Option<usize>, ()>, + ) -> Self { + Self { + reader: decoder, + finish: false, + nested: 0, + parser: PhantomData, + unwrap, + } + } + + /// Gets the next segment in the stream + /// + /// Returns `Ok(None)` at the conclusion of the stream. + #[inline] + pub fn pull(&mut self) -> Result<Option<Segment<R, P>>, Error<R::Error>> { + while !self.finish { + let offset = self.reader.offset(); + match self.reader.pull()? { + Header::Break if self.nested == 1 => return Ok(None), + Header::Break if self.nested > 1 => self.nested -= 1, + header => match (self.unwrap)(header) { + Err(..) => return Err(Error::Syntax(offset)), + Ok(None) => self.nested += 1, + Ok(Some(len)) => { + self.finish = self.nested == 0; + return Ok(Some(Segment { + reader: self.reader, + unread: len, + offset, + parser: P::default(), + })); + } + }, + } + } + + Ok(None) + } +} -- cgit v1.2.3