ciborium-0.2.0/.cargo_vcs_info.json0000644000000001120000000000100126360ustar { "git": { "sha1": "e8512abee2f126ae60923be4362c175703550894" } } ciborium-0.2.0/Cargo.toml0000644000000031100000000000100106350ustar # 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" name = "ciborium" version = "0.2.0" authors = ["Nathaniel McCallum "] description = "serde implementation of CBOR using ciborium-basic" homepage = "https://github.com/enarx/ciborium" readme = "README.md" keywords = ["cbor", "serde"] categories = ["data-structures", "embedded", "encoding", "no-std", "parsing"] license = "Apache-2.0" repository = "https://github.com/enarx/ciborium" [dependencies.ciborium-io] version = "0.2.0" features = ["alloc"] [dependencies.ciborium-ll] version = "0.2.0" [dependencies.serde] version = "1.0" features = ["alloc", "derive"] default-features = false [dev-dependencies.hex] version = "0.4" [dev-dependencies.rand] version = "0.8" [dev-dependencies.rstest] version = "0.11" [dev-dependencies.serde_bytes] version = "0.11" [features] default = ["std"] std = ["ciborium-io/std", "serde/std"] [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" ciborium-0.2.0/Cargo.toml.orig000064400000000000000000000023160072674642500143550ustar 00000000000000[package] name = "ciborium" version = "0.2.0" authors = ["Nathaniel McCallum "] license = "Apache-2.0" edition = "2021" homepage = "https://github.com/enarx/ciborium" repository = "https://github.com/enarx/ciborium" description = "serde implementation of CBOR using ciborium-basic" readme = "README.md" keywords = ["cbor", "serde"] categories = ["data-structures", "embedded", "encoding", "no-std", "parsing"] [badges] # See https://doc.rust-lang.org/cargo/reference/manifest.html#the-badges-section github = { repository = "enarx/ciborium", workflow = "test" } #github = { repository = "enarx/ciborium", workflow = "lint" } maintenance = { status = "actively-developed" } is-it-maintained-issue-resolution = { repository = "enarx/ciborium" } is-it-maintained-open-issues = { repository = "enarx/ciborium" } [dependencies] ciborium-ll = { path = "../ciborium-ll", version = "0.2.0" } ciborium-io = { path = "../ciborium-io", version = "0.2.0", features = ["alloc"] } serde = { version = "1.0", default-features = false, features = ["alloc", "derive"] } [dev-dependencies] serde_bytes = "0.11" rstest = "0.11" rand = "0.8" hex = "0.4" [features] default = ["std"] std = ["ciborium-io/std", "serde/std"] ciborium-0.2.0/LICENSE000064400000000000000000000261350072674642500125000ustar 00000000000000 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. ciborium-0.2.0/README.md000064400000000000000000000106040072674642500127440ustar 00000000000000[![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 Welcome to Ciborium! Ciborium contains CBOR serialization and deserialization implementations for serde. ## Quick Start You're probably looking for [`de::from_reader()`](crate::de::from_reader) and [`ser::into_writer()`](crate::ser::into_writer), which are the main functions. Note that byte slices are also readers and writers and can be passed to these functions just as streams can. For dynamic CBOR value creation/inspection, see [`value::Value`](crate::value::Value). ## Design Decisions ### Always Serialize Numeric Values to the Smallest Size Although the CBOR specification has differing numeric widths, this is only a form of compression on the wire and is not intended to directly represent an "integer width" or "float width." Therefore, ciborium always serializes numbers to the smallest possible lossless encoding. For example, we serialize `1u128` as a single byte (`01`). Likewise, we will also freely decode that single byte into a `u128`. While there is some minor performance cost for this, there are several reasons for this choice. First, the specification seems to imply it by using a separate bit for the sign. Second, the specification requires that implementations handle leading zeroes; a liberal reading of which implies a requirement for lossless coercion. Third, dynamic languages like Python have no notion of "integer width," making this is a practical choice for maximizing wire compatibility with those languages. This coercion is **always** lossless. For floats, this implies that we only coerce to a smaller size if coercion back to the original size has the same raw bits as the original. ### Compatibility with Other Implementations The ciborium project follows the [Robustness Principle](https://en.wikipedia.org/wiki/Robustness_principle). Therefore, we aim to be liberal in what we accept. This implies that we aim to be wire-compatible with other implementations in decoding, but not necessarily encoding. One notable example of this is that `serde_cbor` uses fixed-width encoding of numbers and doesn't losslessly coerce. This implies that `ciborium` will successfully decode `serde_cbor` encodings, but the opposite may not be the case. ### Representing Map as a Sequence of Values Other serde parsers have generally taken the route of using `BTreeMap` or `HashMap` to implement their encoding's underlying `Map` type. This crate chooses to represent the `Map` type using `Vec<(Value, Value)>` instead. This decision was made because this type preserves the order of the pairs on the wire. Further, for those that need the properties of `BTreeMap` or `HashMap`, you can simply `collect()` the values into the respective type. This provides maximum flexibility. ### Low-level Library The ciborium crate has the beginnings of a low-level library in the (private) `basic` module. We may extend this to be more robust and expose it for application consumption once we have it in a good state. If you'd like to collaborate with us on that, please contact us. Alternatively, we might fork this code into a separate crate with no serde dependency. ### Internal Types The ciborium crate contains a number of internal types that implement useful serde traits. While these are not currently exposed, we might choose to expose them in the future if there is demand. Generally, this crate takes a conservative approach to exposing APIs to avoid breakage. ### Packed Encoding? Packed encoding uses numerical offsets to represent structure field names and enum variant names. This can save significant space on the wire. While the authors of this crate like packed encoding, it should generally be avoided because it can be fragile as it exposes invariants of your Rust code to remote actors. We might consider adding this in the future. If you are interested in this, please contact us. License: Apache-2.0 ciborium-0.2.0/src/de/error.rs000064400000000000000000000036110072674642500143430ustar 00000000000000// SPDX-License-Identifier: Apache-2.0 use alloc::string::{String, ToString}; use core::fmt::{Debug, Display, Formatter, Result}; use serde::de::{Error as DeError, StdError}; /// An error occurred during deserialization #[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), /// An error occurred while processing a parsed value /// /// Contains a description of the error that occurred and (optionally) /// the offset into the stream indicating the start of the item being /// processed when the error occurred. Semantic(Option, String), /// The input caused serde to recurse too much /// /// This error prevents a stack overflow. RecursionLimitExceeded, } impl Error { /// A helper method for composing a semantic error #[inline] pub fn semantic(offset: impl Into>, msg: impl Into) -> Self { Self::Semantic(offset.into(), msg.into()) } } impl From for Error { #[inline] fn from(value: T) -> Self { Error::Io(value) } } impl From> for Error { #[inline] fn from(value: ciborium_ll::Error) -> Self { match value { ciborium_ll::Error::Io(x) => Self::Io(x), ciborium_ll::Error::Syntax(x) => Self::Syntax(x), } } } impl Display for Error { #[inline] fn fmt(&self, f: &mut Formatter<'_>) -> Result { write!(f, "{:?}", self) } } impl StdError for Error {} impl DeError for Error { #[inline] fn custom(msg: U) -> Self { Self::Semantic(None, msg.to_string()) } } ciborium-0.2.0/src/de/mod.rs000064400000000000000000000560260072674642500140010ustar 00000000000000// SPDX-License-Identifier: Apache-2.0 //! Serde deserialization support for CBOR mod error; pub use error::Error; use alloc::{string::String, vec::Vec}; use ciborium_io::Read; use ciborium_ll::*; use serde::{de, de::Deserializer as _, forward_to_deserialize_any}; trait Expected { fn expected(self, kind: &'static str) -> E; } impl Expected for Header { #[inline] fn expected(self, kind: &'static str) -> E { de::Error::invalid_type( match self { Header::Positive(x) => de::Unexpected::Unsigned(x), Header::Negative(x) => de::Unexpected::Signed(x as i64 ^ !0), Header::Bytes(..) => de::Unexpected::Other("bytes"), Header::Text(..) => de::Unexpected::Other("string"), Header::Array(..) => de::Unexpected::Seq, Header::Map(..) => de::Unexpected::Map, Header::Tag(..) => de::Unexpected::Other("tag"), Header::Simple(simple::FALSE) => de::Unexpected::Bool(false), Header::Simple(simple::TRUE) => de::Unexpected::Bool(true), Header::Simple(simple::NULL) => de::Unexpected::Other("null"), Header::Simple(simple::UNDEFINED) => de::Unexpected::Other("undefined"), Header::Simple(..) => de::Unexpected::Other("simple"), Header::Float(x) => de::Unexpected::Float(x), Header::Break => de::Unexpected::Other("break"), }, &kind, ) } } struct Deserializer<'b, R: Read> { decoder: Decoder, scratch: &'b mut [u8], recurse: usize, } impl<'de, 'a, 'b, R: Read> Deserializer<'b, R> where R::Error: core::fmt::Debug, { #[inline] fn recurse Result>>( &mut self, func: F, ) -> Result> { if self.recurse == 0 { return Err(Error::RecursionLimitExceeded); } self.recurse -= 1; let result = func(self); self.recurse += 1; result } #[inline] fn integer(&mut self, mut header: Option
) -> Result<(bool, u128), Error> { loop { let header = match header.take() { Some(h) => h, None => self.decoder.pull()?, }; let neg = match header { Header::Positive(x) => return Ok((false, x.into())), Header::Negative(x) => return Ok((true, x.into())), Header::Tag(tag::BIGPOS) => false, Header::Tag(tag::BIGNEG) => true, Header::Tag(..) => continue, header => return Err(header.expected("integer")), }; let mut buffer = [0u8; 16]; let mut value = [0u8; 16]; let mut index = 0usize; return match self.decoder.pull()? { Header::Bytes(len) => { let mut segments = self.decoder.bytes(len); while let Some(mut segment) = segments.pull()? { while let Some(chunk) = segment.pull(&mut buffer)? { for b in chunk { match index { 16 => return Err(de::Error::custom("bigint too large")), 0 if *b == 0 => continue, // Skip leading zeros _ => value[index] = *b, } index += 1; } } } value[..index].reverse(); Ok((neg, u128::from_le_bytes(value))) } h => Err(h.expected("bytes")), }; } } } impl<'de, 'a, 'b, R: Read> de::Deserializer<'de> for &'a mut Deserializer<'b, R> where R::Error: core::fmt::Debug, { type Error = Error; #[inline] fn deserialize_any>(self, visitor: V) -> Result { let header = self.decoder.pull()?; self.decoder.push(header); match header { Header::Positive(..) => self.deserialize_u64(visitor), Header::Negative(x) => match i64::try_from(x) { Ok(..) => self.deserialize_i64(visitor), Err(..) => self.deserialize_i128(visitor), }, Header::Bytes(len) => match len { Some(len) if len <= self.scratch.len() => self.deserialize_bytes(visitor), _ => self.deserialize_byte_buf(visitor), }, Header::Text(len) => match len { Some(len) if len <= self.scratch.len() => self.deserialize_str(visitor), _ => self.deserialize_string(visitor), }, Header::Array(..) => self.deserialize_seq(visitor), Header::Map(..) => self.deserialize_map(visitor), Header::Tag(tag) => { let _: Header = self.decoder.pull()?; // Peek at the next item. let header = self.decoder.pull()?; self.decoder.push(header); // If it is bytes, capture the length. let len = match header { Header::Bytes(x) => x, _ => None, }; match (tag, len) { (tag::BIGPOS, Some(len)) | (tag::BIGNEG, Some(len)) if len <= 16 => { let result = match self.integer(Some(Header::Tag(tag)))? { (false, raw) => return visitor.visit_u128(raw), (true, raw) => i128::try_from(raw).map(|x| x ^ !0), }; match result { Ok(x) => visitor.visit_i128(x), Err(..) => Err(de::Error::custom("integer too large")), } } _ => self.recurse(|me| { let access = crate::tag::TagAccess::new(me, Some(tag)); visitor.visit_enum(access) }), } } Header::Float(..) => self.deserialize_f64(visitor), Header::Simple(simple::FALSE) => self.deserialize_bool(visitor), Header::Simple(simple::TRUE) => self.deserialize_bool(visitor), Header::Simple(simple::NULL) => self.deserialize_option(visitor), Header::Simple(simple::UNDEFINED) => self.deserialize_option(visitor), h @ Header::Simple(..) => Err(h.expected("known simple value")), h @ Header::Break => Err(h.expected("non-break")), } } #[inline] fn deserialize_bool>(self, visitor: V) -> Result { loop { let offset = self.decoder.offset(); return match self.decoder.pull()? { Header::Tag(..) => continue, Header::Simple(simple::FALSE) => visitor.visit_bool(false), Header::Simple(simple::TRUE) => visitor.visit_bool(true), _ => Err(Error::semantic(offset, "expected bool")), }; } } #[inline] fn deserialize_f32>(self, visitor: V) -> Result { self.deserialize_f64(visitor) } #[inline] fn deserialize_f64>(self, visitor: V) -> Result { loop { return match self.decoder.pull()? { Header::Tag(..) => continue, Header::Float(x) => visitor.visit_f64(x), h => Err(h.expected("float")), }; } } fn deserialize_i8>(self, visitor: V) -> Result { self.deserialize_i64(visitor) } fn deserialize_i16>(self, visitor: V) -> Result { self.deserialize_i64(visitor) } fn deserialize_i32>(self, visitor: V) -> Result { self.deserialize_i64(visitor) } fn deserialize_i64>(self, visitor: V) -> Result { let result = match self.integer(None)? { (false, raw) => i64::try_from(raw), (true, raw) => i64::try_from(raw).map(|x| x ^ !0), }; match result { Ok(x) => visitor.visit_i64(x), Err(..) => Err(de::Error::custom("integer too large")), } } fn deserialize_i128>(self, visitor: V) -> Result { let result = match self.integer(None)? { (false, raw) => i128::try_from(raw), (true, raw) => i128::try_from(raw).map(|x| x ^ !0), }; match result { Ok(x) => visitor.visit_i128(x), Err(..) => Err(de::Error::custom("integer too large")), } } fn deserialize_u8>(self, visitor: V) -> Result { self.deserialize_u64(visitor) } fn deserialize_u16>(self, visitor: V) -> Result { self.deserialize_u64(visitor) } fn deserialize_u32>(self, visitor: V) -> Result { self.deserialize_u64(visitor) } fn deserialize_u64>(self, visitor: V) -> Result { let result = match self.integer(None)? { (false, raw) => u64::try_from(raw), (true, ..) => return Err(de::Error::custom("unexpected negative integer")), }; match result { Ok(x) => visitor.visit_u64(x), Err(..) => Err(de::Error::custom("integer too large")), } } fn deserialize_u128>(self, visitor: V) -> Result { match self.integer(None)? { (false, raw) => visitor.visit_u128(raw), (true, ..) => Err(de::Error::custom("unexpected negative integer")), } } fn deserialize_char>(self, visitor: V) -> Result { loop { let offset = self.decoder.offset(); let header = self.decoder.pull()?; return match header { Header::Tag(..) => continue, Header::Text(Some(len)) if len <= 4 => { let mut buf = [0u8; 4]; self.decoder.read_exact(&mut buf[..len])?; match core::str::from_utf8(&buf[..len]) { Ok(s) => match s.chars().count() { 1 => visitor.visit_char(s.chars().next().unwrap()), _ => Err(header.expected("char")), }, Err(..) => Err(Error::Syntax(offset)), } } _ => Err(header.expected("char")), }; } } fn deserialize_str>(self, visitor: V) -> Result { loop { let offset = self.decoder.offset(); return match self.decoder.pull()? { Header::Tag(..) => continue, Header::Text(Some(len)) if len <= self.scratch.len() => { self.decoder.read_exact(&mut self.scratch[..len])?; match core::str::from_utf8(&self.scratch[..len]) { Ok(s) => visitor.visit_str(s), Err(..) => Err(Error::Syntax(offset)), } } header => Err(header.expected("str")), }; } } fn deserialize_string>(self, visitor: V) -> Result { loop { return match self.decoder.pull()? { Header::Tag(..) => continue, Header::Text(len) => { let mut buffer = String::new(); let mut segments = self.decoder.text(len); while let Some(mut segment) = segments.pull()? { while let Some(chunk) = segment.pull(&mut self.scratch)? { buffer.push_str(chunk); } } visitor.visit_string(buffer) } header => Err(header.expected("string")), }; } } fn deserialize_bytes>(self, visitor: V) -> Result { loop { return match self.decoder.pull()? { Header::Tag(..) => continue, Header::Bytes(Some(len)) if len <= self.scratch.len() => { self.decoder.read_exact(&mut self.scratch[..len])?; visitor.visit_bytes(&self.scratch[..len]) } header => Err(header.expected("bytes")), }; } } fn deserialize_byte_buf>( self, visitor: V, ) -> Result { loop { return match self.decoder.pull()? { Header::Tag(..) => continue, Header::Bytes(len) => { let mut buffer = Vec::new(); let mut segments = self.decoder.bytes(len); while let Some(mut segment) = segments.pull()? { while let Some(chunk) = segment.pull(&mut self.scratch)? { buffer.extend_from_slice(chunk); } } visitor.visit_byte_buf(buffer) } header => Err(header.expected("expected byte buffer")), }; } } fn deserialize_seq>(self, visitor: V) -> Result { loop { return match self.decoder.pull()? { Header::Tag(..) => continue, Header::Array(len) => self.recurse(|me| { let access = Access(me, len); visitor.visit_seq(access) }), header => Err(header.expected("array")), }; } } fn deserialize_map>(self, visitor: V) -> Result { loop { return match self.decoder.pull()? { Header::Tag(..) => continue, Header::Map(len) => self.recurse(|me| { let access = Access(me, len); visitor.visit_map(access) }), header => Err(header.expected("map")), }; } } fn deserialize_struct>( self, _name: &'static str, _fields: &'static [&'static str], visitor: V, ) -> Result { self.deserialize_map(visitor) } fn deserialize_tuple>( self, _len: usize, visitor: V, ) -> Result { self.deserialize_seq(visitor) } fn deserialize_tuple_struct>( self, _name: &'static str, _len: usize, visitor: V, ) -> Result { self.deserialize_seq(visitor) } fn deserialize_identifier>( self, visitor: V, ) -> Result { self.deserialize_str(visitor) } fn deserialize_ignored_any>( self, visitor: V, ) -> Result { self.deserialize_any(visitor) } #[inline] fn deserialize_option>(self, visitor: V) -> Result { loop { return match self.decoder.pull()? { Header::Simple(simple::UNDEFINED) => visitor.visit_none(), Header::Simple(simple::NULL) => visitor.visit_none(), Header::Tag(..) => continue, header => { self.decoder.push(header); visitor.visit_some(self) } }; } } #[inline] fn deserialize_unit>(self, visitor: V) -> Result { loop { return match self.decoder.pull()? { Header::Simple(simple::UNDEFINED) => visitor.visit_unit(), Header::Simple(simple::NULL) => visitor.visit_unit(), Header::Tag(..) => continue, header => Err(header.expected("unit")), }; } } #[inline] fn deserialize_unit_struct>( self, _name: &'static str, visitor: V, ) -> Result { self.deserialize_unit(visitor) } #[inline] fn deserialize_newtype_struct>( self, _name: &'static str, visitor: V, ) -> Result { visitor.visit_newtype_struct(self) } #[inline] fn deserialize_enum>( self, name: &'static str, _variants: &'static [&'static str], visitor: V, ) -> Result { if name == "@@TAG@@" { let tag = match self.decoder.pull()? { Header::Tag(x) => Some(x), header => { self.decoder.push(header); None } }; return self.recurse(|me| { let access = crate::tag::TagAccess::new(me, tag); visitor.visit_enum(access) }); } loop { match self.decoder.pull()? { Header::Tag(..) => continue, Header::Map(Some(1)) => (), header @ Header::Text(..) => self.decoder.push(header), header => return Err(header.expected("enum")), } return self.recurse(|me| { let access = Access(me, Some(0)); visitor.visit_enum(access) }); } } #[inline] fn is_human_readable(&self) -> bool { false } } struct Access<'a, 'b, R: Read>(&'a mut Deserializer<'b, R>, Option); impl<'de, 'a, 'b, R: Read> de::SeqAccess<'de> for Access<'a, 'b, R> where R::Error: core::fmt::Debug, { type Error = Error; #[inline] fn next_element_seed>( &mut self, seed: U, ) -> Result, Self::Error> { match self.1 { Some(0) => return Ok(None), Some(x) => self.1 = Some(x - 1), None => match self.0.decoder.pull()? { Header::Break => return Ok(None), header => self.0.decoder.push(header), }, } seed.deserialize(&mut *self.0).map(Some) } #[inline] fn size_hint(&self) -> Option { self.1 } } impl<'de, 'a, 'b, R: Read> de::MapAccess<'de> for Access<'a, 'b, R> where R::Error: core::fmt::Debug, { type Error = Error; #[inline] fn next_key_seed>( &mut self, seed: K, ) -> Result, Self::Error> { match self.1 { Some(0) => return Ok(None), Some(x) => self.1 = Some(x - 1), None => match self.0.decoder.pull()? { Header::Break => return Ok(None), header => self.0.decoder.push(header), }, } seed.deserialize(&mut *self.0).map(Some) } #[inline] fn next_value_seed>( &mut self, seed: V, ) -> Result { seed.deserialize(&mut *self.0) } #[inline] fn size_hint(&self) -> Option { self.1 } } impl<'de, 'a, 'b, R: Read> de::EnumAccess<'de> for Access<'a, 'b, R> where R::Error: core::fmt::Debug, { type Error = Error; type Variant = Self; #[inline] fn variant_seed>( self, seed: V, ) -> Result<(V::Value, Self::Variant), Self::Error> { let variant = seed.deserialize(&mut *self.0)?; Ok((variant, self)) } } impl<'de, 'a, 'b, R: Read> de::VariantAccess<'de> for Access<'a, 'b, R> where R::Error: core::fmt::Debug, { type Error = Error; #[inline] fn unit_variant(self) -> Result<(), Self::Error> { Ok(()) } #[inline] fn newtype_variant_seed>( self, seed: U, ) -> Result { seed.deserialize(&mut *self.0) } #[inline] fn tuple_variant>( self, _len: usize, visitor: V, ) -> Result { self.0.deserialize_any(visitor) } #[inline] fn struct_variant>( self, _fields: &'static [&'static str], visitor: V, ) -> Result { self.0.deserialize_any(visitor) } } struct TagAccess<'a, 'b, R: Read>(&'a mut Deserializer<'b, R>, usize); impl<'de, 'a, 'b, R: Read> de::Deserializer<'de> for &mut TagAccess<'a, 'b, R> where R::Error: core::fmt::Debug, { type Error = Error; #[inline] fn deserialize_any>(self, visitor: V) -> Result { let offset = self.0.decoder.offset(); match self.0.decoder.pull()? { Header::Tag(x) => visitor.visit_u64(x), _ => Err(Error::semantic(offset, "expected tag")), } } forward_to_deserialize_any! { i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 bool f32 f64 char str string bytes byte_buf seq map struct tuple tuple_struct identifier ignored_any option unit unit_struct newtype_struct enum } } impl<'de, 'a, 'b, R: Read> de::SeqAccess<'de> for TagAccess<'a, 'b, R> where R::Error: core::fmt::Debug, { type Error = Error; #[inline] fn next_element_seed>( &mut self, seed: U, ) -> Result, Self::Error> { self.1 += 1; match self.1 { 1 => seed.deserialize(self).map(Some), 2 => seed.deserialize(&mut *self.0).map(Some), _ => Ok(None), } } #[inline] fn size_hint(&self) -> Option { Some(match self.1 { 0 => 2, 1 => 1, _ => 0, }) } } /// Deserializes as CBOR from a type with [`impl ciborium_io::Read`](ciborium_io::Read) #[inline] pub fn from_reader<'de, T: de::Deserialize<'de>, R: Read>(reader: R) -> Result> where R::Error: core::fmt::Debug, { let mut scratch = [0; 4096]; let mut reader = Deserializer { decoder: reader.into(), scratch: &mut scratch, recurse: 256, }; T::deserialize(&mut reader) } ciborium-0.2.0/src/lib.rs000064400000000000000000000163350072674642500133770ustar 00000000000000// SPDX-License-Identifier: Apache-2.0 //! Welcome to Ciborium! //! //! Ciborium contains CBOR serialization and deserialization implementations for serde. //! //! # Quick Start //! //! You're probably looking for [`de::from_reader()`](crate::de::from_reader) //! and [`ser::into_writer()`](crate::ser::into_writer), which are //! the main functions. Note that byte slices are also readers and writers and can be //! passed to these functions just as streams can. //! //! For dynamic CBOR value creation/inspection, see [`value::Value`](crate::value::Value). //! //! # Design Decisions //! //! ## Always Serialize Numeric Values to the Smallest Size //! //! Although the CBOR specification has differing numeric widths, this is only //! a form of compression on the wire and is not intended to directly //! represent an "integer width" or "float width." Therefore, ciborium always //! serializes numbers to the smallest possible lossless encoding. For example, //! we serialize `1u128` as a single byte (`01`). Likewise, we will also freely //! decode that single byte into a `u128`. //! //! While there is some minor performance cost for this, there are several //! reasons for this choice. First, the specification seems to imply it by //! using a separate bit for the sign. Second, the specification requires //! that implementations handle leading zeroes; a liberal reading of which //! implies a requirement for lossless coercion. Third, dynamic languages like //! Python have no notion of "integer width," making this is a practical //! choice for maximizing wire compatibility with those languages. //! //! This coercion is **always** lossless. For floats, this implies that we //! only coerce to a smaller size if coercion back to the original size has //! the same raw bits as the original. //! //! ## Compatibility with Other Implementations //! //! The ciborium project follows the [Robustness Principle](https://en.wikipedia.org/wiki/Robustness_principle). //! Therefore, we aim to be liberal in what we accept. This implies that we //! aim to be wire-compatible with other implementations in decoding, but //! not necessarily encoding. //! //! One notable example of this is that `serde_cbor` uses fixed-width encoding //! of numbers and doesn't losslessly coerce. This implies that `ciborium` will //! successfully decode `serde_cbor` encodings, but the opposite may not be the //! case. //! //! ## Representing Map as a Sequence of Values //! //! Other serde parsers have generally taken the route of using `BTreeMap` or //! `HashMap` to implement their encoding's underlying `Map` type. This crate //! chooses to represent the `Map` type using `Vec<(Value, Value)>` instead. //! //! This decision was made because this type preserves the order of the pairs //! on the wire. Further, for those that need the properties of `BTreeMap` or //! `HashMap`, you can simply `collect()` the values into the respective type. //! This provides maximum flexibility. //! //! ## Low-level Library //! //! The ciborium crate has the beginnings of a low-level library in the //! (private) `basic` module. We may extend this to be more robust and expose //! it for application consumption once we have it in a good state. If you'd //! like to collaborate with us on that, please contact us. Alternatively, //! we might fork this code into a separate crate with no serde dependency. //! //! ## Internal Types //! //! The ciborium crate contains a number of internal types that implement //! useful serde traits. While these are not currently exposed, we might //! choose to expose them in the future if there is demand. Generally, this //! crate takes a conservative approach to exposing APIs to avoid breakage. //! //! ## Packed Encoding? //! //! Packed encoding uses numerical offsets to represent structure field names //! and enum variant names. This can save significant space on the wire. //! //! While the authors of this crate like packed encoding, it should generally //! be avoided because it can be fragile as it exposes invariants of your Rust //! code to remote actors. We might consider adding this in the future. If you //! are interested in this, please contact us. #![cfg_attr(not(feature = "std"), no_std)] #![deny(missing_docs)] #![deny(clippy::all)] #![deny(clippy::cargo)] #![allow(clippy::unit_arg)] extern crate alloc; pub mod de; pub mod ser; pub mod tag; pub mod value; /// Build a `Value` conveniently. /// /// The syntax should be intuitive if you are familiar with JSON. You can also /// inline simple Rust expressions, including custom values that implement /// `serde::Serialize`. Note that this macro returns `Result`, /// so you should handle the error appropriately. /// /// ``` /// use ciborium::cbor; /// /// let value = cbor!({ /// "code" => 415, /// "message" => null, /// "continue" => false, /// "extra" => { "numbers" => [8.2341e+4, 0.251425] }, /// }).unwrap(); /// ``` #[macro_export] macro_rules! cbor { (@map {$($key:expr => $val:expr),*} $(,)?) => {{ $crate::value::Value::Map(vec![ $( (cbor!( $key )?, cbor!( $val )?) ),* ]) }}; (@map {$($key:expr => $val:expr),*} { $($nkey:tt)* } => $($next:tt)*) => { cbor!( @map { $($key => $val),* } cbor!({ $($nkey)* })? => $($next)* ) }; (@map {$($key:expr => $val:expr),*} [ $($nkey:tt)* ] => $($next:tt)*) => { cbor!( @map { $($key => $val),* } cbor!([ $($nkey)* ])? => $($next)* ) }; (@map {$($key:expr => $val:expr),*} $nkey:expr => { $($nval:tt)* }, $($next:tt)*) => { cbor!( @map { $($key => $val,)* $nkey => cbor!({ $($nval)* })? } $($next)* ) }; (@map {$($key:expr => $val:expr),*} $nkey:expr => [ $($nval:tt)* ], $($next:tt)*) => { cbor!( @map { $($key => $val,)* $nkey => cbor!([ $($nval)* ])? } $($next)* ) }; (@map {$($key:expr => $val:expr),*} $nkey:expr => $nval:expr, $($next:tt)*) => { cbor!( @map { $($key => $val,)* $nkey => cbor!($nval)? } $($next)* ) }; (@seq [$($val:expr),*] $(,)?) => { $crate::value::Value::Array( vec![$( cbor!($val)? ),*] ) }; (@seq [$($val:expr),*] { $($item:tt)* }, $($next:tt)*) => { cbor!( @seq [ $($val,)* cbor!({ $($item)* })? ] $($next)* ) }; (@seq [$($val:expr),*] [ $($item:tt)* ], $($next:tt)*) => { cbor!( @seq [ $($val,)* cbor!([ $($item)* ])? ] $($next)* ) }; (@seq [$($val:expr),*] $item:expr, $($next:tt)*) => { cbor!( @seq [ $($val,)* $item ] $($next)* ) }; ({ $($next:tt)* }) => {(||{ ::core::result::Result::<_, $crate::value::Error>::from(Ok(cbor!(@map {} $($next)* ,))) })()}; ([ $($next:tt)* ]) => {(||{ ::core::result::Result::<_, $crate::value::Error>::from(Ok(cbor!(@seq [] $($next)* ,))) })()}; ($val:expr) => {{ #[allow(unused_imports)] use $crate::value::Value::Null as null; $crate::value::Value::serialized(&$val) }}; } ciborium-0.2.0/src/ser/error.rs000064400000000000000000000016730072674642500145520ustar 00000000000000// SPDX-License-Identifier: Apache-2.0 use alloc::string::{String, ToString}; use core::fmt::{Debug, Display, Formatter, Result}; use serde::ser::{Error as SerError, StdError}; /// An error occurred during serialization #[derive(Debug)] pub enum Error { /// An error occurred while writing bytes /// /// Contains the underlying error reaturned while writing. Io(T), /// An error indicating a value that cannot be serialized /// /// Contains a description of the problem. Value(String), } impl From for Error { #[inline] fn from(value: T) -> Self { Error::Io(value) } } impl Display for Error { #[inline] fn fmt(&self, f: &mut Formatter<'_>) -> Result { write!(f, "{:?}", self) } } impl StdError for Error {} impl SerError for Error { fn custom(msg: U) -> Self { Error::Value(msg.to_string()) } } ciborium-0.2.0/src/ser/mod.rs000064400000000000000000000303030072674642500141700ustar 00000000000000// SPDX-License-Identifier: Apache-2.0 //! Serde serialization support for CBOR mod error; pub use error::Error; use alloc::string::ToString; use ciborium_io::Write; use ciborium_ll::*; use serde::{ser, Serialize as _}; struct Serializer(Encoder); impl From for Serializer { #[inline] fn from(writer: W) -> Self { Self(writer.into()) } } impl From> for Serializer { #[inline] fn from(writer: Encoder) -> Self { Self(writer) } } impl<'a, W: Write> ser::Serializer for &'a mut Serializer where W::Error: core::fmt::Debug, { type Ok = (); type Error = Error; type SerializeSeq = CollectionSerializer<'a, W>; type SerializeTuple = CollectionSerializer<'a, W>; type SerializeTupleStruct = CollectionSerializer<'a, W>; type SerializeTupleVariant = CollectionSerializer<'a, W>; type SerializeMap = CollectionSerializer<'a, W>; type SerializeStruct = CollectionSerializer<'a, W>; type SerializeStructVariant = CollectionSerializer<'a, W>; #[inline] fn serialize_bool(self, v: bool) -> Result<(), Self::Error> { Ok(self.0.push(match v { false => Header::Simple(simple::FALSE), true => Header::Simple(simple::TRUE), })?) } #[inline] fn serialize_i8(self, v: i8) -> Result<(), Self::Error> { self.serialize_i64(v.into()) } #[inline] fn serialize_i16(self, v: i16) -> Result<(), Self::Error> { self.serialize_i64(v.into()) } #[inline] fn serialize_i32(self, v: i32) -> Result<(), Self::Error> { self.serialize_i64(v.into()) } #[inline] fn serialize_i64(self, v: i64) -> Result<(), Self::Error> { Ok(self.0.push(match v.is_negative() { false => Header::Positive(v as u64), true => Header::Negative(v as u64 ^ !0), })?) } #[inline] fn serialize_i128(self, v: i128) -> Result<(), Self::Error> { let (tag, raw) = match v.is_negative() { false => (tag::BIGPOS, v as u128), true => (tag::BIGNEG, v as u128 ^ !0), }; match (tag, u64::try_from(raw)) { (tag::BIGPOS, Ok(x)) => return Ok(self.0.push(Header::Positive(x))?), (tag::BIGNEG, Ok(x)) => return Ok(self.0.push(Header::Negative(x))?), _ => {} } let bytes = raw.to_be_bytes(); // Skip leading zeros. let mut slice = &bytes[..]; while !slice.is_empty() && slice[0] == 0 { slice = &slice[1..]; } self.0.push(Header::Tag(tag))?; self.0.push(Header::Bytes(Some(slice.len())))?; Ok(self.0.write_all(slice)?) } #[inline] fn serialize_u8(self, v: u8) -> Result<(), Self::Error> { self.serialize_u64(v.into()) } #[inline] fn serialize_u16(self, v: u16) -> Result<(), Self::Error> { self.serialize_u64(v.into()) } #[inline] fn serialize_u32(self, v: u32) -> Result<(), Self::Error> { self.serialize_u64(v.into()) } #[inline] fn serialize_u64(self, v: u64) -> Result<(), Self::Error> { Ok(self.0.push(Header::Positive(v))?) } #[inline] fn serialize_u128(self, v: u128) -> Result<(), Self::Error> { if let Ok(x) = u64::try_from(v) { return self.serialize_u64(x); } let bytes = v.to_be_bytes(); // Skip leading zeros. let mut slice = &bytes[..]; while !slice.is_empty() && slice[0] == 0 { slice = &slice[1..]; } self.0.push(Header::Tag(tag::BIGPOS))?; self.0.push(Header::Bytes(Some(slice.len())))?; Ok(self.0.write_all(slice)?) } #[inline] fn serialize_f32(self, v: f32) -> Result<(), Self::Error> { self.serialize_f64(v.into()) } #[inline] fn serialize_f64(self, v: f64) -> Result<(), Self::Error> { Ok(self.0.push(Header::Float(v))?) } #[inline] fn serialize_char(self, v: char) -> Result<(), Self::Error> { self.serialize_str(&v.to_string()) } #[inline] fn serialize_str(self, v: &str) -> Result<(), Self::Error> { let bytes = v.as_bytes(); self.0.push(Header::Text(bytes.len().into()))?; Ok(self.0.write_all(bytes)?) } #[inline] fn serialize_bytes(self, v: &[u8]) -> Result<(), Self::Error> { self.0.push(Header::Bytes(v.len().into()))?; Ok(self.0.write_all(v)?) } #[inline] fn serialize_none(self) -> Result<(), Self::Error> { Ok(self.0.push(Header::Simple(simple::NULL))?) } #[inline] fn serialize_some(self, value: &U) -> Result<(), Self::Error> { value.serialize(self) } #[inline] fn serialize_unit(self) -> Result<(), Self::Error> { self.serialize_none() } #[inline] fn serialize_unit_struct(self, _name: &'static str) -> Result<(), Self::Error> { self.serialize_unit() } #[inline] fn serialize_unit_variant( self, _name: &'static str, _index: u32, variant: &'static str, ) -> Result<(), Self::Error> { self.serialize_str(variant) } #[inline] fn serialize_newtype_struct( self, _name: &'static str, value: &U, ) -> Result<(), Self::Error> { value.serialize(self) } #[inline] fn serialize_newtype_variant( self, name: &'static str, _index: u32, variant: &'static str, value: &U, ) -> Result<(), Self::Error> { if name != "@@TAG@@" || variant != "@@UNTAGGED@@" { self.0.push(Header::Map(Some(1)))?; self.serialize_str(variant)?; } value.serialize(self) } #[inline] fn serialize_seq(self, length: Option) -> Result { self.0.push(Header::Array(length))?; Ok(CollectionSerializer { encoder: self, ending: length.is_none(), tag: false, }) } #[inline] fn serialize_tuple(self, length: usize) -> Result { self.serialize_seq(Some(length)) } #[inline] fn serialize_tuple_struct( self, _name: &'static str, length: usize, ) -> Result { self.serialize_seq(Some(length)) } #[inline] fn serialize_tuple_variant( self, name: &'static str, _index: u32, variant: &'static str, length: usize, ) -> Result { match (name, variant) { ("@@TAG@@", "@@TAGGED@@") => Ok(CollectionSerializer { encoder: self, ending: false, tag: true, }), _ => { self.0.push(Header::Map(Some(1)))?; self.serialize_str(variant)?; self.0.push(Header::Array(Some(length)))?; Ok(CollectionSerializer { encoder: self, ending: false, tag: false, }) } } } #[inline] fn serialize_map(self, length: Option) -> Result { self.0.push(Header::Map(length))?; Ok(CollectionSerializer { encoder: self, ending: length.is_none(), tag: false, }) } #[inline] fn serialize_struct( self, _name: &'static str, length: usize, ) -> Result { self.0.push(Header::Map(Some(length)))?; Ok(CollectionSerializer { encoder: self, ending: false, tag: false, }) } #[inline] fn serialize_struct_variant( self, _name: &'static str, _index: u32, variant: &'static str, length: usize, ) -> Result { self.0.push(Header::Map(Some(1)))?; self.serialize_str(variant)?; self.0.push(Header::Map(Some(length)))?; Ok(CollectionSerializer { encoder: self, ending: false, tag: false, }) } #[inline] fn is_human_readable(&self) -> bool { false } } macro_rules! end { () => { #[inline] fn end(self) -> Result<(), Self::Error> { if self.ending { self.encoder.0.push(Header::Break)?; } Ok(()) } }; } struct CollectionSerializer<'a, W: Write> { encoder: &'a mut Serializer, ending: bool, tag: bool, } impl<'a, W: Write> ser::SerializeSeq for CollectionSerializer<'a, W> where W::Error: core::fmt::Debug, { type Ok = (); type Error = Error; #[inline] fn serialize_element( &mut self, value: &U, ) -> Result<(), Self::Error> { value.serialize(&mut *self.encoder) } end!(); } impl<'a, W: Write> ser::SerializeTuple for CollectionSerializer<'a, W> where W::Error: core::fmt::Debug, { type Ok = (); type Error = Error; #[inline] fn serialize_element( &mut self, value: &U, ) -> Result<(), Self::Error> { value.serialize(&mut *self.encoder) } end!(); } impl<'a, W: Write> ser::SerializeTupleStruct for CollectionSerializer<'a, W> where W::Error: core::fmt::Debug, { type Ok = (); type Error = Error; #[inline] fn serialize_field( &mut self, value: &U, ) -> Result<(), Self::Error> { value.serialize(&mut *self.encoder) } end!(); } impl<'a, W: Write> ser::SerializeTupleVariant for CollectionSerializer<'a, W> where W::Error: core::fmt::Debug, { type Ok = (); type Error = Error; #[inline] fn serialize_field( &mut self, value: &U, ) -> Result<(), Self::Error> { if !self.tag { return value.serialize(&mut *self.encoder); } self.tag = false; match value.serialize(crate::tag::Serializer) { Ok(x) => Ok(self.encoder.0.push(Header::Tag(x))?), _ => Err(Error::Value("expected tag".into())), } } end!(); } impl<'a, W: Write> ser::SerializeMap for CollectionSerializer<'a, W> where W::Error: core::fmt::Debug, { type Ok = (); type Error = Error; #[inline] fn serialize_key(&mut self, key: &U) -> Result<(), Self::Error> { key.serialize(&mut *self.encoder) } #[inline] fn serialize_value( &mut self, value: &U, ) -> Result<(), Self::Error> { value.serialize(&mut *self.encoder) } end!(); } impl<'a, W: Write> ser::SerializeStruct for CollectionSerializer<'a, W> where W::Error: core::fmt::Debug, { type Ok = (); type Error = Error; #[inline] fn serialize_field( &mut self, key: &'static str, value: &U, ) -> Result<(), Self::Error> { key.serialize(&mut *self.encoder)?; value.serialize(&mut *self.encoder)?; Ok(()) } end!(); } impl<'a, W: Write> ser::SerializeStructVariant for CollectionSerializer<'a, W> where W::Error: core::fmt::Debug, { type Ok = (); type Error = Error; #[inline] fn serialize_field( &mut self, key: &'static str, value: &U, ) -> Result<(), Self::Error> { key.serialize(&mut *self.encoder)?; value.serialize(&mut *self.encoder) } end!(); } /// Serializes as CBOR into a type with [`impl ciborium_io::Write`](ciborium_io::Write) #[inline] pub fn into_writer( value: &T, writer: W, ) -> Result<(), Error> where W::Error: core::fmt::Debug, { let mut encoder = Serializer::from(writer); value.serialize(&mut encoder)?; Ok(encoder.0.flush()?) } ciborium-0.2.0/src/tag.rs000064400000000000000000000322540072674642500134020ustar 00000000000000//! Contains helper types for dealing with CBOR tags use serde::{de, de::Error as _, forward_to_deserialize_any, ser, Deserialize, Serialize}; #[derive(Deserialize, Serialize)] #[serde(rename = "@@TAG@@")] enum Internal { #[serde(rename = "@@UNTAGGED@@")] Untagged(T), #[serde(rename = "@@TAGGED@@")] Tagged(u64, T), } /// An optional CBOR tag and its data item /// /// No semantic evaluation of the tag is made. #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Captured(pub Option, pub V); impl<'de, V: Deserialize<'de>> Deserialize<'de> for Captured { #[inline] fn deserialize>(deserializer: D) -> Result { match Internal::deserialize(deserializer)? { Internal::Tagged(t, v) => Ok(Captured(Some(t), v)), Internal::Untagged(v) => Ok(Captured(None, v)), } } } impl Serialize for Captured { #[inline] fn serialize(&self, serializer: S) -> Result { match self.0 { Some(tag) => Internal::Tagged(tag, &self.1).serialize(serializer), None => Internal::Untagged(&self.1).serialize(serializer), } } } /// A required CBOR tag /// /// This data type indicates that the specified tag, and **only** that tag, /// is required during deserialization. If the tag is missing, deserialization /// will fail. The tag will always be emitted during serialization. #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Required(pub V); impl<'de, V: Deserialize<'de>, const TAG: u64> Deserialize<'de> for Required { #[inline] fn deserialize>(deserializer: D) -> Result { match Internal::deserialize(deserializer)? { Internal::Tagged(t, v) if t == TAG => Ok(Required(v)), _ => Err(de::Error::custom("required tag not found")), } } } impl Serialize for Required { #[inline] fn serialize(&self, serializer: S) -> Result { Internal::Tagged(TAG, &self.0).serialize(serializer) } } /// An optional CBOR tag /// /// This data type indicates that the specified tag, and **only** that tag, /// is accepted, but not required, during deserialization. The tag will always /// be emitted during serialization. #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Accepted(pub V); impl<'de, V: Deserialize<'de>, const TAG: u64> Deserialize<'de> for Accepted { #[inline] fn deserialize>(deserializer: D) -> Result { match Internal::deserialize(deserializer)? { Internal::Tagged(t, v) if t == TAG => Ok(Accepted(v)), Internal::Untagged(v) => Ok(Accepted(v)), _ => Err(de::Error::custom("required tag not found")), } } } impl Serialize for Accepted { #[inline] fn serialize(&self, serializer: S) -> Result { Internal::Tagged(TAG, &self.0).serialize(serializer) } } pub(crate) struct TagAccess { parent: Option, state: usize, tag: Option, } impl TagAccess { pub fn new(parent: D, tag: Option) -> Self { Self { parent: Some(parent), state: 0, tag, } } } impl<'de, D: de::Deserializer<'de>> de::Deserializer<'de> for &mut TagAccess { type Error = D::Error; #[inline] fn deserialize_any>(self, visitor: V) -> Result { self.state += 1; match self.state { 1 => visitor.visit_str(match self.tag { Some(..) => "@@TAGGED@@", None => "@@UNTAGGED@@", }), _ => visitor.visit_u64(self.tag.unwrap()), } } forward_to_deserialize_any! { i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 bool f32 f64 char str string bytes byte_buf seq map struct tuple tuple_struct identifier ignored_any option unit unit_struct newtype_struct enum } } impl<'de, D: de::Deserializer<'de>> de::EnumAccess<'de> for TagAccess { type Error = D::Error; type Variant = Self; #[inline] fn variant_seed>( mut self, seed: V, ) -> Result<(V::Value, Self::Variant), Self::Error> { let variant = seed.deserialize(&mut self)?; Ok((variant, self)) } } impl<'de, D: de::Deserializer<'de>> de::VariantAccess<'de> for TagAccess { type Error = D::Error; #[inline] fn unit_variant(self) -> Result<(), Self::Error> { Err(Self::Error::custom("expected tag")) } #[inline] fn newtype_variant_seed>( mut self, seed: U, ) -> Result { seed.deserialize(self.parent.take().unwrap()) } #[inline] fn tuple_variant>( self, _len: usize, visitor: V, ) -> Result { visitor.visit_seq(self) } #[inline] fn struct_variant>( self, _fields: &'static [&'static str], _visitor: V, ) -> Result { Err(Self::Error::custom("expected tag")) } } impl<'de, D: de::Deserializer<'de>> de::SeqAccess<'de> for TagAccess { type Error = D::Error; #[inline] fn next_element_seed>( &mut self, seed: T, ) -> Result, Self::Error> { if self.state < 2 { return Ok(Some(seed.deserialize(self)?)); } Ok(match self.parent.take() { Some(x) => Some(seed.deserialize(x)?), None => None, }) } } #[derive(Debug)] pub(crate) struct Error; impl core::fmt::Display for Error { #[inline] fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { write!(f, "{:?}", self) } } impl ser::StdError for Error {} impl ser::Error for Error { fn custom(_msg: U) -> Self { Error } } pub(crate) struct Serializer; impl ser::Serializer for Serializer { type Ok = u64; type Error = Error; type SerializeSeq = Self; type SerializeTuple = Self; type SerializeTupleStruct = Self; type SerializeTupleVariant = Self; type SerializeMap = Self; type SerializeStruct = Self; type SerializeStructVariant = Self; #[inline] fn serialize_bool(self, _: bool) -> Result { Err(Error) } #[inline] fn serialize_i8(self, _: i8) -> Result { Err(Error) } #[inline] fn serialize_i16(self, _: i16) -> Result { Err(Error) } #[inline] fn serialize_i32(self, _: i32) -> Result { Err(Error) } #[inline] fn serialize_i64(self, _: i64) -> Result { Err(Error) } #[inline] fn serialize_i128(self, _: i128) -> Result { Err(Error) } #[inline] fn serialize_u8(self, v: u8) -> Result { Ok(v.into()) } #[inline] fn serialize_u16(self, v: u16) -> Result { Ok(v.into()) } #[inline] fn serialize_u32(self, v: u32) -> Result { Ok(v.into()) } #[inline] fn serialize_u64(self, v: u64) -> Result { Ok(v) } #[inline] fn serialize_u128(self, _: u128) -> Result { Err(Error) } #[inline] fn serialize_f32(self, _: f32) -> Result { Err(Error) } #[inline] fn serialize_f64(self, _: f64) -> Result { Err(Error) } #[inline] fn serialize_char(self, _: char) -> Result { Err(Error) } #[inline] fn serialize_str(self, _: &str) -> Result { Err(Error) } #[inline] fn serialize_bytes(self, _: &[u8]) -> Result { Err(Error) } #[inline] fn serialize_none(self) -> Result { Err(Error) } #[inline] fn serialize_some(self, _: &U) -> Result { Err(Error) } #[inline] fn serialize_unit(self) -> Result { Err(Error) } #[inline] fn serialize_unit_struct(self, _name: &'static str) -> Result { Err(Error) } #[inline] fn serialize_unit_variant( self, _name: &'static str, _index: u32, _variant: &'static str, ) -> Result { Err(Error) } #[inline] fn serialize_newtype_struct( self, _name: &'static str, _value: &U, ) -> Result { Err(Error) } #[inline] fn serialize_newtype_variant( self, _name: &'static str, _index: u32, _variant: &'static str, _value: &U, ) -> Result { Err(Error) } #[inline] fn serialize_seq(self, _length: Option) -> Result { Err(Error) } #[inline] fn serialize_tuple(self, _length: usize) -> Result { Err(Error) } #[inline] fn serialize_tuple_struct( self, _name: &'static str, _length: usize, ) -> Result { Err(Error) } #[inline] fn serialize_tuple_variant( self, _name: &'static str, _index: u32, _variant: &'static str, _length: usize, ) -> Result { Err(Error) } #[inline] fn serialize_map(self, _length: Option) -> Result { Err(Error) } #[inline] fn serialize_struct( self, _name: &'static str, _length: usize, ) -> Result { Err(Error) } #[inline] fn serialize_struct_variant( self, _name: &'static str, _index: u32, _variant: &'static str, _length: usize, ) -> Result { Err(Error) } #[inline] fn is_human_readable(&self) -> bool { false } } impl<'a> ser::SerializeSeq for Serializer { type Ok = u64; type Error = Error; #[inline] fn serialize_element(&mut self, _value: &U) -> Result<(), Error> { Err(Error) } #[inline] fn end(self) -> Result { Err(Error) } } impl<'a> ser::SerializeTuple for Serializer { type Ok = u64; type Error = Error; #[inline] fn serialize_element(&mut self, _value: &U) -> Result<(), Error> { Err(Error) } #[inline] fn end(self) -> Result { Err(Error) } } impl<'a> ser::SerializeTupleStruct for Serializer { type Ok = u64; type Error = Error; #[inline] fn serialize_field(&mut self, _value: &U) -> Result<(), Error> { Err(Error) } #[inline] fn end(self) -> Result { Err(Error) } } impl<'a> ser::SerializeTupleVariant for Serializer { type Ok = u64; type Error = Error; #[inline] fn serialize_field(&mut self, _value: &U) -> Result<(), Error> { Err(Error) } #[inline] fn end(self) -> Result { Err(Error) } } impl<'a> ser::SerializeMap for Serializer { type Ok = u64; type Error = Error; #[inline] fn serialize_key(&mut self, _key: &U) -> Result<(), Error> { Err(Error) } #[inline] fn serialize_value(&mut self, _value: &U) -> Result<(), Error> { Err(Error) } #[inline] fn end(self) -> Result { Err(Error) } } impl<'a> ser::SerializeStruct for Serializer { type Ok = u64; type Error = Error; #[inline] fn serialize_field( &mut self, _key: &'static str, _value: &U, ) -> Result<(), Error> { Err(Error) } #[inline] fn end(self) -> Result { Err(Error) } } impl<'a> ser::SerializeStructVariant for Serializer { type Ok = u64; type Error = Error; #[inline] fn serialize_field( &mut self, _key: &'static str, _value: &U, ) -> Result<(), Self::Error> { Err(Error) } #[inline] fn end(self) -> Result { Err(Error) } } ciborium-0.2.0/src/value/de.rs000064400000000000000000000436100072674642500143310ustar 00000000000000// SPDX-License-Identifier: Apache-2.0 use super::{Error, Integer, Value}; use alloc::{boxed::Box, string::String, vec::Vec}; use core::iter::Peekable; use ciborium_ll::tag; use serde::de::{self, Deserializer as _}; impl<'a> From for de::Unexpected<'a> { #[inline] fn from(value: Integer) -> Self { u64::try_from(value) .map(de::Unexpected::Unsigned) .unwrap_or_else(|_| { i64::try_from(value) .map(de::Unexpected::Signed) .unwrap_or_else(|_| de::Unexpected::Other("large integer")) }) } } impl<'a> From<&'a Value> for de::Unexpected<'a> { #[inline] fn from(value: &'a Value) -> Self { match value { Value::Bool(x) => Self::Bool(*x), Value::Integer(x) => Self::from(*x), Value::Float(x) => Self::Float(*x), Value::Bytes(x) => Self::Bytes(x), Value::Text(x) => Self::Str(x), Value::Array(..) => Self::Seq, Value::Map(..) => Self::Map, Value::Null => Self::Other("null"), Value::Tag(..) => Self::Other("tag"), } } } macro_rules! mkvisit { ($($f:ident($v:ty)),+ $(,)?) => { $( #[inline] fn $f(self, v: $v) -> Result { Ok(v.into()) } )+ }; } struct Visitor; impl<'de> serde::de::Visitor<'de> for Visitor { type Value = Value; fn expecting(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { write!(formatter, "a valid CBOR item") } mkvisit! { visit_bool(bool), visit_f32(f32), visit_f64(f64), visit_i8(i8), visit_i16(i16), visit_i32(i32), visit_i64(i64), visit_i128(i128), visit_u8(u8), visit_u16(u16), visit_u32(u32), visit_u64(u64), visit_u128(u128), visit_char(char), visit_str(&str), visit_borrowed_str(&'de str), visit_string(String), visit_bytes(&[u8]), visit_borrowed_bytes(&'de [u8]), visit_byte_buf(Vec), } #[inline] fn visit_none(self) -> Result { Ok(Value::Null) } #[inline] fn visit_some>( self, deserializer: D, ) -> Result { deserializer.deserialize_any(self) } #[inline] fn visit_unit(self) -> Result { Ok(Value::Null) } #[inline] fn visit_newtype_struct>( self, deserializer: D, ) -> Result { deserializer.deserialize_any(self) } #[inline] fn visit_seq>(self, mut acc: A) -> Result { let mut seq = Vec::new(); while let Some(elem) = acc.next_element()? { seq.push(elem); } Ok(Value::Array(seq)) } #[inline] fn visit_map>(self, mut acc: A) -> Result { let mut map = Vec::<(Value, Value)>::new(); while let Some(kv) = acc.next_entry()? { map.push(kv); } Ok(Value::Map(map)) } #[inline] fn visit_enum>(self, acc: A) -> Result { use serde::de::VariantAccess; struct Inner; impl<'de> serde::de::Visitor<'de> for Inner { type Value = Value; fn expecting(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { write!(formatter, "a valid CBOR item") } #[inline] fn visit_seq>(self, mut acc: A) -> Result { let tag: u64 = acc .next_element()? .ok_or_else(|| de::Error::custom("expected tag"))?; let val = acc .next_element()? .ok_or_else(|| de::Error::custom("expected val"))?; Ok(Value::Tag(tag, Box::new(val))) } } let (name, data): (String, _) = acc.variant()?; assert_eq!("@@TAGGED@@", name); data.tuple_variant(2, Inner) } } impl<'de> de::Deserialize<'de> for Value { #[inline] fn deserialize>(deserializer: D) -> Result { deserializer.deserialize_any(Visitor) } } struct Deserializer(T); impl<'a, 'de> Deserializer<&'a Value> { fn integer(&self, kind: &'static str) -> Result where N: TryFrom, N: TryFrom, { fn raw(value: &Value) -> Result { let mut buffer = 0u128.to_ne_bytes(); let length = buffer.len(); let bytes = match value { Value::Bytes(bytes) => { // Skip leading zeros... let mut bytes: &[u8] = bytes.as_ref(); while bytes.len() > buffer.len() && bytes[0] == 0 { bytes = &bytes[1..]; } if bytes.len() > buffer.len() { return Err(de::Error::custom("bigint too large")); } bytes } _ => return Err(de::Error::invalid_type(value.into(), &"bytes")), }; buffer[length - bytes.len()..].copy_from_slice(bytes); Ok(u128::from_be_bytes(buffer)) } let err = || de::Error::invalid_type(self.0.into(), &kind); Ok(match self.0 { Value::Integer(x) => i128::from(*x).try_into().map_err(|_| err())?, Value::Tag(t, v) if *t == tag::BIGPOS => raw(v)?.try_into().map_err(|_| err())?, Value::Tag(t, v) if *t == tag::BIGNEG => i128::try_from(raw(v)?) .map(|x| x ^ !0) .map_err(|_| err()) .and_then(|x| x.try_into().map_err(|_| err()))?, _ => return Err(de::Error::invalid_type(self.0.into(), &"(big)int")), }) } } impl<'a, 'de> de::Deserializer<'de> for Deserializer<&'a Value> { type Error = Error; #[inline] fn deserialize_any>(self, visitor: V) -> Result { match self.0 { Value::Bytes(x) => visitor.visit_bytes(x), Value::Text(x) => visitor.visit_str(x), Value::Array(x) => visitor.visit_seq(Deserializer(x.iter())), Value::Map(x) => visitor.visit_map(Deserializer(x.iter().peekable())), Value::Bool(x) => visitor.visit_bool(*x), Value::Null => visitor.visit_none(), Value::Tag(t, v) => { let parent: Deserializer<&Value> = Deserializer(&*v); let access = crate::tag::TagAccess::new(parent, Some(*t)); visitor.visit_enum(access) } Value::Integer(x) => { if let Ok(x) = u64::try_from(*x) { visitor.visit_u64(x) } else if let Ok(x) = i64::try_from(*x) { visitor.visit_i64(x) } else if let Ok(x) = i128::try_from(*x) { visitor.visit_i128(x) } else { unreachable!() } } Value::Float(x) => visitor.visit_f64(*x), } } #[inline] fn deserialize_bool>(self, visitor: V) -> Result { let mut value = self.0; while let Value::Tag(.., v) = value { value = v; } match value { Value::Bool(x) => visitor.visit_bool(*x), _ => Err(de::Error::invalid_type(value.into(), &"bool")), } } #[inline] fn deserialize_f32>(self, visitor: V) -> Result { self.deserialize_f64(visitor) } #[inline] fn deserialize_f64>(self, visitor: V) -> Result { let mut value = self.0; while let Value::Tag(.., v) = value { value = v; } match value { Value::Float(x) => visitor.visit_f64(*x), _ => Err(de::Error::invalid_type(value.into(), &"f64")), } } fn deserialize_i8>(self, visitor: V) -> Result { visitor.visit_i8(self.integer("i8")?) } fn deserialize_i16>(self, visitor: V) -> Result { visitor.visit_i16(self.integer("i16")?) } fn deserialize_i32>(self, visitor: V) -> Result { visitor.visit_i32(self.integer("i32")?) } fn deserialize_i64>(self, visitor: V) -> Result { visitor.visit_i64(self.integer("i64")?) } fn deserialize_i128>(self, visitor: V) -> Result { visitor.visit_i128(self.integer("i128")?) } fn deserialize_u8>(self, visitor: V) -> Result { visitor.visit_u8(self.integer("u8")?) } fn deserialize_u16>(self, visitor: V) -> Result { visitor.visit_u16(self.integer("u16")?) } fn deserialize_u32>(self, visitor: V) -> Result { visitor.visit_u32(self.integer("u32")?) } fn deserialize_u64>(self, visitor: V) -> Result { visitor.visit_u64(self.integer("u64")?) } fn deserialize_u128>(self, visitor: V) -> Result { visitor.visit_u128(self.integer("u128")?) } fn deserialize_char>(self, visitor: V) -> Result { let mut value = self.0; while let Value::Tag(.., v) = value { value = v; } match value { Value::Text(x) => match x.chars().count() { 1 => visitor.visit_char(x.chars().next().unwrap()), _ => Err(de::Error::invalid_type(value.into(), &"char")), }, _ => Err(de::Error::invalid_type(value.into(), &"char")), } } fn deserialize_str>(self, visitor: V) -> Result { let mut value = self.0; while let Value::Tag(.., v) = value { value = v; } match value { Value::Text(x) => visitor.visit_str(x), _ => Err(de::Error::invalid_type(value.into(), &"str")), } } fn deserialize_string>(self, visitor: V) -> Result { self.deserialize_str(visitor) } fn deserialize_bytes>(self, visitor: V) -> Result { let mut value = self.0; while let Value::Tag(.., v) = value { value = v; } match value { Value::Bytes(x) => visitor.visit_bytes(x), _ => Err(de::Error::invalid_type(value.into(), &"bytes")), } } fn deserialize_byte_buf>( self, visitor: V, ) -> Result { self.deserialize_bytes(visitor) } fn deserialize_seq>(self, visitor: V) -> Result { let mut value = self.0; while let Value::Tag(.., v) = value { value = v; } match value { Value::Array(x) => visitor.visit_seq(Deserializer(x.iter())), _ => Err(de::Error::invalid_type(value.into(), &"array")), } } fn deserialize_map>(self, visitor: V) -> Result { let mut value = self.0; while let Value::Tag(.., v) = value { value = v; } match value { Value::Map(x) => visitor.visit_map(Deserializer(x.iter().peekable())), _ => Err(de::Error::invalid_type(value.into(), &"map")), } } fn deserialize_struct>( self, _name: &'static str, _fields: &'static [&'static str], visitor: V, ) -> Result { self.deserialize_map(visitor) } fn deserialize_tuple>( self, _len: usize, visitor: V, ) -> Result { self.deserialize_seq(visitor) } fn deserialize_tuple_struct>( self, _name: &'static str, _len: usize, visitor: V, ) -> Result { self.deserialize_seq(visitor) } fn deserialize_identifier>( self, visitor: V, ) -> Result { self.deserialize_str(visitor) } fn deserialize_ignored_any>( self, visitor: V, ) -> Result { self.deserialize_any(visitor) } #[inline] fn deserialize_option>(self, visitor: V) -> Result { match self.0 { Value::Null => visitor.visit_none(), x => visitor.visit_some(Self(x)), } } #[inline] fn deserialize_unit>(self, visitor: V) -> Result { match self.0 { Value::Null => visitor.visit_unit(), _ => Err(de::Error::invalid_type(self.0.into(), &"null")), } } #[inline] fn deserialize_unit_struct>( self, _name: &'static str, visitor: V, ) -> Result { self.deserialize_unit(visitor) } #[inline] fn deserialize_newtype_struct>( self, _name: &'static str, visitor: V, ) -> Result { visitor.visit_newtype_struct(self) } #[inline] fn deserialize_enum>( self, name: &'static str, variants: &'static [&'static str], visitor: V, ) -> Result { if name == "@@TAG@@" { let (tag, val) = match self.0 { Value::Tag(t, v) => (Some(*t), v.as_ref()), v => (None, v), }; let parent: Deserializer<&Value> = Deserializer(&*val); let access = crate::tag::TagAccess::new(parent, tag); return visitor.visit_enum(access); } match self.0 { Value::Tag(.., v) => Deserializer(v.as_ref()).deserialize_enum(name, variants, visitor), Value::Map(x) if x.len() == 1 => visitor.visit_enum(Deserializer(&x[0])), x @ Value::Text(..) => visitor.visit_enum(Deserializer(x)), _ => Err(de::Error::invalid_type(self.0.into(), &"map")), } } } impl<'a, 'de, T: Iterator> de::SeqAccess<'de> for Deserializer { type Error = Error; #[inline] fn next_element_seed>( &mut self, seed: U, ) -> Result, Self::Error> { match self.0.next() { None => Ok(None), Some(v) => seed.deserialize(Deserializer(v)).map(Some), } } } impl<'a, 'de, T: Iterator> de::MapAccess<'de> for Deserializer> { type Error = Error; #[inline] fn next_key_seed>( &mut self, seed: K, ) -> Result, Self::Error> { match self.0.peek() { None => Ok(None), Some(x) => Ok(Some(seed.deserialize(Deserializer(&x.0))?)), } } #[inline] fn next_value_seed>( &mut self, seed: V, ) -> Result { seed.deserialize(Deserializer(&self.0.next().unwrap().1)) } } impl<'a, 'de> de::EnumAccess<'de> for Deserializer<&'a (Value, Value)> { type Error = Error; type Variant = Deserializer<&'a Value>; #[inline] fn variant_seed>( self, seed: V, ) -> Result<(V::Value, Self::Variant), Self::Error> { let k = seed.deserialize(Deserializer(&self.0 .0))?; Ok((k, Deserializer(&self.0 .1))) } } impl<'a, 'de> de::EnumAccess<'de> for Deserializer<&'a Value> { type Error = Error; type Variant = Deserializer<&'a Value>; #[inline] fn variant_seed>( self, seed: V, ) -> Result<(V::Value, Self::Variant), Self::Error> { let k = seed.deserialize(self)?; Ok((k, Deserializer(&Value::Null))) } } impl<'a, 'de> de::VariantAccess<'de> for Deserializer<&'a Value> { type Error = Error; #[inline] fn unit_variant(self) -> Result<(), Self::Error> { match self.0 { Value::Null => Ok(()), _ => Err(de::Error::invalid_type(self.0.into(), &"unit")), } } #[inline] fn newtype_variant_seed>( self, seed: U, ) -> Result { seed.deserialize(self) } #[inline] fn tuple_variant>( self, _len: usize, visitor: V, ) -> Result { self.deserialize_seq(visitor) } #[inline] fn struct_variant>( self, _fields: &'static [&'static str], visitor: V, ) -> Result { self.deserialize_map(visitor) } } impl Value { /// Deserializes the `Value` into an object #[inline] pub fn deserialized<'de, T: de::Deserialize<'de>>(&self) -> Result { T::deserialize(Deserializer(self)) } } ciborium-0.2.0/src/value/error.rs000064400000000000000000000013250072674642500150670ustar 00000000000000// SPDX-License-Identifier: Apache-2.0 use alloc::string::{String, ToString}; /// The error when serializing to/from a `Value` #[derive(Debug)] pub enum Error { /// A custom error string produced by serde Custom(String), } impl core::fmt::Display for Error { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { write!(f, "{:?}", self) } } impl serde::de::StdError for Error {} impl serde::de::Error for Error { #[inline] fn custom(msg: T) -> Self { Self::Custom(msg.to_string()) } } impl serde::ser::Error for Error { #[inline] fn custom(msg: T) -> Self { Self::Custom(msg.to_string()) } } ciborium-0.2.0/src/value/integer.rs000064400000000000000000000041060072674642500153730ustar 00000000000000// SPDX-License-Identifier: Apache-2.0 macro_rules! implfrom { ($( $(#[$($attr:meta)+])? $t:ident)+) => { $( $(#[$($attr)+])? impl From<$t> for Integer { #[inline] fn from(value: $t) -> Self { Self(value as _) } } impl TryFrom for $t { type Error = core::num::TryFromIntError; #[inline] fn try_from(value: Integer) -> Result { $t::try_from(value.0) } } )+ }; } /// An abstract integer value /// /// This opaque type represents an integer value which can be encoded in CBOR /// without resulting to big integer encoding. Larger values may be encoded /// using the big integer encoding as described in the CBOR RFC. See the /// implementations for 128-bit integer conversions on `Value` for more /// details. #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] pub struct Integer(i128); implfrom! { u8 u16 u32 u64 i8 i16 i32 i64 #[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))] usize #[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))] isize } impl TryFrom for Integer { type Error = core::num::TryFromIntError; #[inline] fn try_from(value: i128) -> Result { u64::try_from(match value.is_negative() { false => value, true => value ^ !0, })?; Ok(Integer(value)) } } impl TryFrom for Integer { type Error = core::num::TryFromIntError; #[inline] fn try_from(value: u128) -> Result { Ok(Self(u64::try_from(value)?.into())) } } impl From for i128 { #[inline] fn from(value: Integer) -> Self { value.0 } } impl TryFrom for u128 { type Error = core::num::TryFromIntError; #[inline] fn try_from(value: Integer) -> Result { u128::try_from(value.0) } } ciborium-0.2.0/src/value/mod.rs000064400000000000000000000323570072674642500145260ustar 00000000000000// SPDX-License-Identifier: Apache-2.0 //! A dynamic CBOR value mod integer; mod de; mod error; mod ser; pub use error::Error; pub use integer::Integer; use alloc::{boxed::Box, string::String, vec::Vec}; /// A representation of a dynamic CBOR value that can handled dynamically #[non_exhaustive] #[derive(Clone, Debug, PartialEq, PartialOrd)] pub enum Value { /// An integer Integer(Integer), /// Bytes Bytes(Vec), /// A float Float(f64), /// A string Text(String), /// A boolean Bool(bool), /// Null Null, /// Tag Tag(u64, Box), /// An array Array(Vec), /// A map Map(Vec<(Value, Value)>), } impl Value { /// Returns true if the `Value` is an `Integer`. Returns false otherwise. /// /// ``` /// # use ciborium::value::Value; /// # /// let value = Value::Integer(17.into()); /// /// assert!(value.is_integer()); /// ``` pub fn is_integer(&self) -> bool { self.as_integer().is_some() } /// If the `Value` is a `Integer`, returns a reference to the associated `Integer` data. /// Returns None otherwise. /// /// ``` /// # use ciborium::value::Value; /// # /// let value = Value::Integer(17.into()); /// /// // We can read the number /// assert_eq!(17, value.as_integer().unwrap().try_into().unwrap()); /// ``` pub fn as_integer(&self) -> Option { match self { Value::Integer(int) => Some(*int), _ => None, } } /// Returns true if the `Value` is a `Bytes`. Returns false otherwise. /// /// ``` /// # use ciborium::value::Value; /// # /// let value = Value::Bytes(vec![104, 101, 108, 108, 111]); /// /// assert!(value.is_bytes()); /// ``` pub fn is_bytes(&self) -> bool { self.as_bytes().is_some() } /// If the `Value` is a `Bytes`, returns a reference to the associated bytes vector. /// Returns None otherwise. /// /// ``` /// # use ciborium::value::Value; /// # /// let value = Value::Bytes(vec![104, 101, 108, 108, 111]); /// /// assert_eq!(std::str::from_utf8(value.as_bytes().unwrap()).unwrap(), "hello"); /// ``` pub fn as_bytes(&self) -> Option<&Vec> { match *self { Value::Bytes(ref bytes) => Some(bytes), _ => None, } } /// If the `Value` is a `Bytes`, returns a mutable reference to the associated bytes vector. /// Returns None otherwise. /// /// ``` /// # use ciborium::value::Value; /// # /// let mut value = Value::Bytes(vec![104, 101, 108, 108, 111]); /// value.as_bytes_mut().unwrap().clear(); /// /// assert_eq!(value, Value::Bytes(vec![])); /// ``` pub fn as_bytes_mut(&mut self) -> Option<&mut Vec> { match *self { Value::Bytes(ref mut bytes) => Some(bytes), _ => None, } } /// Returns true if the `Value` is a `Float`. Returns false otherwise. /// /// ``` /// # use ciborium::value::Value; /// # /// let value = Value::Float(17.0.into()); /// /// assert!(value.is_float()); /// ``` pub fn is_float(&self) -> bool { self.as_float().is_some() } /// If the `Value` is a `Float`, returns a reference to the associated float data. /// Returns None otherwise. /// /// ``` /// # use ciborium::value::Value; /// # /// let value = Value::Float(17.0.into()); /// /// // We can read the float number /// assert_eq!(value.as_float().unwrap(), 17.0_f64); /// ``` pub fn as_float(&self) -> Option { match *self { Value::Float(f) => Some(f), _ => None, } } /// Returns true if the `Value` is a `Text`. Returns false otherwise. /// /// ``` /// # use ciborium::value::Value; /// # /// let value = Value::Text(String::from("hello")); /// /// assert!(value.is_text()); /// ``` pub fn is_text(&self) -> bool { self.as_text().is_some() } /// If the `Value` is a `Text`, returns a reference to the associated `String` data. /// Returns None otherwise. /// /// ``` /// # use ciborium::value::Value; /// # /// let value = Value::Text(String::from("hello")); /// /// // We can read the String /// assert_eq!(value.as_text().unwrap(), "hello"); /// ``` pub fn as_text(&self) -> Option<&str> { match *self { Value::Text(ref s) => Some(s), _ => None, } } /// If the `Value` is a `Text`, returns a mutable reference to the associated `String` data. /// Returns None otherwise. /// /// ``` /// # use ciborium::value::Value; /// # /// let mut value = Value::Text(String::from("hello")); /// value.as_text_mut().unwrap().clear(); /// /// assert_eq!(value.as_text().unwrap(), &String::from("")); /// ``` pub fn as_text_mut(&mut self) -> Option<&mut String> { match *self { Value::Text(ref mut s) => Some(s), _ => None, } } /// Returns true if the `Value` is a `Bool`. Returns false otherwise. /// /// ``` /// # use ciborium::value::Value; /// # /// let value = Value::Bool(false); /// /// assert!(value.is_bool()); /// ``` pub fn is_bool(&self) -> bool { self.as_bool().is_some() } /// If the `Value` is a `Bool`, returns a copy of the associated boolean value. Returns None /// otherwise. /// /// ``` /// # use ciborium::value::Value; /// # /// let value = Value::Bool(false); /// /// assert_eq!(value.as_bool().unwrap(), false); /// ``` pub fn as_bool(&self) -> Option { match *self { Value::Bool(b) => Some(b), _ => None, } } /// Returns true if the `Value` is a `Null`. Returns false otherwise. /// /// ``` /// # use ciborium::value::Value; /// # /// let value = Value::Null; /// /// assert!(value.is_null()); /// ``` pub fn is_null(&self) -> bool { matches!(self, Value::Null) } /// Returns true if the `Value` is a `Tag`. Returns false otherwise. /// /// ``` /// # use ciborium::value::Value; /// # /// let value = Value::Tag(61, Box::from(Value::Null)); /// /// assert!(value.is_tag()); /// ``` pub fn is_tag(&self) -> bool { self.as_tag().is_some() } /// If the `Value` is a `Tag`, returns the associated tag value and a reference to the tag `Value`. /// Returns None otherwise. /// /// ``` /// # use ciborium::value::Value; /// # /// let value = Value::Tag(61, Box::from(Value::Bytes(vec![104, 101, 108, 108, 111]))); /// /// let (tag, data) = value.as_tag().unwrap(); /// assert_eq!(tag, 61); /// assert_eq!(data, &Value::Bytes(vec![104, 101, 108, 108, 111])); /// ``` pub fn as_tag(&self) -> Option<(u64, &Value)> { match self { Value::Tag(tag, data) => Some((*tag, data)), _ => None, } } /// If the `Value` is a `Tag`, returns the associated tag value and a mutable reference /// to the tag `Value`. Returns None otherwise. /// /// ``` /// # use ciborium::value::Value; /// # /// let mut value = Value::Tag(61, Box::from(Value::Bytes(vec![104, 101, 108, 108, 111]))); /// /// let (tag, mut data) = value.as_tag_mut().unwrap(); /// data.as_bytes_mut().unwrap().clear(); /// assert_eq!(tag, &61); /// assert_eq!(data, &Value::Bytes(vec![])); /// ``` pub fn as_tag_mut(&mut self) -> Option<(&mut u64, &mut Value)> { match self { Value::Tag(tag, data) => Some((tag, data.as_mut())), _ => None, } } /// Returns true if the `Value` is an Array. Returns false otherwise. /// /// ``` /// # use ciborium::value::Value; /// # /// let value = Value::Array( /// vec![ /// Value::Text(String::from("foo")), /// Value::Text(String::from("bar")) /// ] /// ); /// /// assert!(value.is_array()); /// ``` pub fn is_array(&self) -> bool { self.as_array().is_some() } /// If the `Value` is an Array, returns a reference to the associated vector. Returns None /// otherwise. /// /// ``` /// # use ciborium::value::Value; /// # /// let value = Value::Array( /// vec![ /// Value::Text(String::from("foo")), /// Value::Text(String::from("bar")) /// ] /// ); /// /// // The length of `value` is 2 elements. /// assert_eq!(value.as_array().unwrap().len(), 2); /// ``` pub fn as_array(&self) -> Option<&Vec> { match *self { Value::Array(ref array) => Some(&*array), _ => None, } } /// If the `Value` is an Array, returns a mutable reference to the associated vector. /// Returns None otherwise. /// /// ``` /// # use ciborium::value::Value; /// # /// let mut value = Value::Array( /// vec![ /// Value::Text(String::from("foo")), /// Value::Text(String::from("bar")) /// ] /// ); /// /// value.as_array_mut().unwrap().clear(); /// assert_eq!(value, Value::Array(vec![])); /// ``` pub fn as_array_mut(&mut self) -> Option<&mut Vec> { match *self { Value::Array(ref mut list) => Some(list), _ => None, } } /// Returns true if the `Value` is a Map. Returns false otherwise. /// /// ``` /// # use ciborium::value::Value; /// # /// let value = Value::Map( /// vec![ /// (Value::Text(String::from("foo")), Value::Text(String::from("bar"))) /// ] /// ); /// /// assert!(value.is_map()); /// ``` pub fn is_map(&self) -> bool { self.as_map().is_some() } /// If the `Value` is a Map, returns a reference to the associated Map data. Returns None /// otherwise. /// /// ``` /// # use ciborium::value::Value; /// # /// let value = Value::Map( /// vec![ /// (Value::Text(String::from("foo")), Value::Text(String::from("bar"))) /// ] /// ); /// /// // The length of data is 1 entry (1 key/value pair). /// assert_eq!(value.as_map().unwrap().len(), 1); /// /// // The content of the first element is what we expect /// assert_eq!( /// value.as_map().unwrap().get(0).unwrap(), /// &(Value::Text(String::from("foo")), Value::Text(String::from("bar"))) /// ); /// ``` pub fn as_map(&self) -> Option<&Vec<(Value, Value)>> { match *self { Value::Map(ref map) => Some(map), _ => None, } } /// If the `Value` is a Map, returns a mutable reference to the associated Map Data. /// Returns None otherwise. /// /// ``` /// # use ciborium::value::Value; /// # /// let mut value = Value::Map( /// vec![ /// (Value::Text(String::from("foo")), Value::Text(String::from("bar"))) /// ] /// ); /// /// value.as_map_mut().unwrap().clear(); /// assert_eq!(value, Value::Map(vec![])); /// assert_eq!(value.as_map().unwrap().len(), 0); /// ``` pub fn as_map_mut(&mut self) -> Option<&mut Vec<(Value, Value)>> { match *self { Value::Map(ref mut map) => Some(map), _ => None, } } } macro_rules! implfrom { ($($v:ident($t:ty)),+ $(,)?) => { $( impl From<$t> for Value { #[inline] fn from(value: $t) -> Self { Self::$v(value.into()) } } )+ }; } implfrom! { Integer(Integer), Integer(u64), Integer(i64), Integer(u32), Integer(i32), Integer(u16), Integer(i16), Integer(u8), Integer(i8), Bytes(Vec), Bytes(&[u8]), Float(f64), Float(f32), Text(String), Text(&str), Bool(bool), Array(&[Value]), Array(Vec), Map(&[(Value, Value)]), Map(Vec<(Value, Value)>), } impl From for Value { #[inline] fn from(value: u128) -> Self { if let Ok(x) = Integer::try_from(value) { return Value::Integer(x); } let mut bytes = &value.to_be_bytes()[..]; while let Some(0) = bytes.get(0) { bytes = &bytes[1..]; } Value::Tag(ciborium_ll::tag::BIGPOS, Value::Bytes(bytes.into()).into()) } } impl From for Value { #[inline] fn from(value: i128) -> Self { if let Ok(x) = Integer::try_from(value) { return Value::Integer(x); } let (tag, raw) = match value.is_negative() { true => (ciborium_ll::tag::BIGNEG, value as u128 ^ !0), false => (ciborium_ll::tag::BIGPOS, value as u128), }; let mut bytes = &raw.to_be_bytes()[..]; while let Some(0) = bytes.get(0) { bytes = &bytes[1..]; } Value::Tag(tag, Value::Bytes(bytes.into()).into()) } } impl From for Value { #[inline] fn from(value: char) -> Self { let mut v = String::with_capacity(1); v.push(value); Value::Text(v) } } ciborium-0.2.0/src/value/ser.rs000064400000000000000000000267340072674642500145420ustar 00000000000000// SPDX-License-Identifier: Apache-2.0 use super::{Error, Value}; use alloc::{vec, vec::Vec}; use ::serde::ser::{self, SerializeMap as _, SerializeSeq as _, SerializeTupleVariant as _}; impl ser::Serialize for Value { #[inline] fn serialize(&self, serializer: S) -> Result { match self { Value::Bytes(x) => serializer.serialize_bytes(x), Value::Bool(x) => serializer.serialize_bool(*x), Value::Text(x) => serializer.serialize_str(x), Value::Null => serializer.serialize_unit(), Value::Tag(t, v) => { let mut acc = serializer.serialize_tuple_variant("@@TAG@@", 0, "@@TAGGED@@", 2)?; acc.serialize_field(t)?; acc.serialize_field(v)?; acc.end() } Value::Float(x) => { let y = *x as f32; if (y as f64).to_bits() == x.to_bits() { serializer.serialize_f32(y) } else { serializer.serialize_f64(*x) } } Value::Integer(x) => { if let Ok(x) = u8::try_from(*x) { serializer.serialize_u8(x) } else if let Ok(x) = i8::try_from(*x) { serializer.serialize_i8(x) } else if let Ok(x) = u16::try_from(*x) { serializer.serialize_u16(x) } else if let Ok(x) = i16::try_from(*x) { serializer.serialize_i16(x) } else if let Ok(x) = u32::try_from(*x) { serializer.serialize_u32(x) } else if let Ok(x) = i32::try_from(*x) { serializer.serialize_i32(x) } else if let Ok(x) = u64::try_from(*x) { serializer.serialize_u64(x) } else if let Ok(x) = i64::try_from(*x) { serializer.serialize_i64(x) } else if let Ok(x) = u128::try_from(*x) { serializer.serialize_u128(x) } else if let Ok(x) = i128::try_from(*x) { serializer.serialize_i128(x) } else { unreachable!() } } Value::Array(x) => { let mut map = serializer.serialize_seq(Some(x.len()))?; for v in x { map.serialize_element(v)?; } map.end() } Value::Map(x) => { let mut map = serializer.serialize_map(Some(x.len()))?; for (k, v) in x { map.serialize_entry(k, v)?; } map.end() } } } } macro_rules! mkserialize { ($($f:ident($v:ty)),+ $(,)?) => { $( #[inline] fn $f(self, v: $v) -> Result { Ok(v.into()) } )+ }; } struct Tagged { tag: Option, val: Option, } struct Named { name: &'static str, data: T, tag: Option, } struct Map { data: Vec<(Value, Value)>, temp: Option, } struct Serializer(T); impl ser::Serializer for Serializer<()> { type Ok = Value; type Error = Error; type SerializeSeq = Serializer>; type SerializeTuple = Serializer>; type SerializeTupleStruct = Serializer>; type SerializeTupleVariant = Serializer>>; type SerializeMap = Serializer; type SerializeStruct = Serializer>; type SerializeStructVariant = Serializer>>; mkserialize! { serialize_bool(bool), serialize_f32(f32), serialize_f64(f64), serialize_i8(i8), serialize_i16(i16), serialize_i32(i32), serialize_i64(i64), serialize_i128(i128), serialize_u8(u8), serialize_u16(u16), serialize_u32(u32), serialize_u64(u64), serialize_u128(u128), serialize_char(char), serialize_str(&str), serialize_bytes(&[u8]), } #[inline] fn serialize_none(self) -> Result { Ok(Value::Null) } #[inline] fn serialize_some(self, value: &U) -> Result { value.serialize(self) } #[inline] fn serialize_unit(self) -> Result { self.serialize_none() } #[inline] fn serialize_unit_struct(self, _name: &'static str) -> Result { self.serialize_unit() } #[inline] fn serialize_unit_variant( self, _name: &'static str, _index: u32, variant: &'static str, ) -> Result { Ok(variant.into()) } #[inline] fn serialize_newtype_struct( self, _name: &'static str, value: &U, ) -> Result { value.serialize(self) } #[inline] fn serialize_newtype_variant( self, name: &'static str, _index: u32, variant: &'static str, value: &U, ) -> Result { Ok(match (name, variant) { ("@@TAG@@", "@@UNTAGGED@@") => Value::serialized(value)?, _ => vec![(variant.into(), Value::serialized(value)?)].into(), }) } #[inline] fn serialize_seq(self, length: Option) -> Result { Ok(Serializer(Vec::with_capacity(length.unwrap_or(0)))) } #[inline] fn serialize_tuple(self, length: usize) -> Result { self.serialize_seq(Some(length)) } #[inline] fn serialize_tuple_struct( self, _name: &'static str, length: usize, ) -> Result { self.serialize_seq(Some(length)) } #[inline] fn serialize_tuple_variant( self, name: &'static str, _index: u32, variant: &'static str, length: usize, ) -> Result { Ok(Serializer(Named { name: variant, data: Vec::with_capacity(length), tag: match (name, variant) { ("@@TAG@@", "@@TAGGED@@") => Some(Tagged { tag: None, val: None, }), _ => None, }, })) } #[inline] fn serialize_map(self, length: Option) -> Result { Ok(Serializer(Map { data: Vec::with_capacity(length.unwrap_or(0)), temp: None, })) } #[inline] fn serialize_struct( self, _name: &'static str, length: usize, ) -> Result { Ok(Serializer(Vec::with_capacity(length))) } #[inline] fn serialize_struct_variant( self, _name: &'static str, _index: u32, variant: &'static str, length: usize, ) -> Result { Ok(Serializer(Named { name: variant, data: Vec::with_capacity(length), tag: None, })) } } impl<'a> ser::SerializeSeq for Serializer> { type Ok = Value; type Error = Error; #[inline] fn serialize_element(&mut self, value: &U) -> Result<(), Error> { self.0.push(Value::serialized(&value)?); Ok(()) } #[inline] fn end(self) -> Result { Ok(self.0.into()) } } impl<'a> ser::SerializeTuple for Serializer> { type Ok = Value; type Error = Error; #[inline] fn serialize_element(&mut self, value: &U) -> Result<(), Error> { self.0.push(Value::serialized(&value)?); Ok(()) } #[inline] fn end(self) -> Result { Ok(self.0.into()) } } impl<'a> ser::SerializeTupleStruct for Serializer> { type Ok = Value; type Error = Error; #[inline] fn serialize_field(&mut self, value: &U) -> Result<(), Error> { self.0.push(Value::serialized(&value)?); Ok(()) } #[inline] fn end(self) -> Result { Ok(self.0.into()) } } impl<'a> ser::SerializeTupleVariant for Serializer>> { type Ok = Value; type Error = Error; #[inline] fn serialize_field(&mut self, value: &U) -> Result<(), Error> { match self.0.tag.as_mut() { Some(tag) => match tag.tag { None => match value.serialize(crate::tag::Serializer) { Ok(t) => tag.tag = Some(t), Err(..) => return Err(ser::Error::custom("expected tag")), }, Some(..) => tag.val = Some(Value::serialized(value)?), }, None => self.0.data.push(Value::serialized(value)?), } Ok(()) } #[inline] fn end(self) -> Result { Ok(match self.0.tag { Some(tag) => match tag { Tagged { tag: Some(t), val: Some(v), } => Value::Tag(t, v.into()), _ => return Err(ser::Error::custom("invalid tag input")), }, None => vec![(self.0.name.into(), self.0.data.into())].into(), }) } } impl<'a> ser::SerializeMap for Serializer { type Ok = Value; type Error = Error; #[inline] fn serialize_key(&mut self, key: &U) -> Result<(), Error> { self.0.temp = Some(Value::serialized(key)?); Ok(()) } #[inline] fn serialize_value(&mut self, value: &U) -> Result<(), Error> { let key = self.0.temp.take().unwrap(); let val = Value::serialized(&value)?; self.0.data.push((key, val)); Ok(()) } #[inline] fn end(self) -> Result { Ok(self.0.data.into()) } } impl<'a> ser::SerializeStruct for Serializer> { type Ok = Value; type Error = Error; #[inline] fn serialize_field( &mut self, key: &'static str, value: &U, ) -> Result<(), Error> { let k = Value::serialized(&key)?; let v = Value::serialized(&value)?; self.0.push((k, v)); Ok(()) } #[inline] fn end(self) -> Result { Ok(self.0.into()) } } impl<'a> ser::SerializeStructVariant for Serializer>> { type Ok = Value; type Error = Error; #[inline] fn serialize_field( &mut self, key: &'static str, value: &U, ) -> Result<(), Self::Error> { let k = Value::serialized(&key)?; let v = Value::serialized(&value)?; self.0.data.push((k, v)); Ok(()) } #[inline] fn end(self) -> Result { Ok(vec![(self.0.name.into(), self.0.data.into())].into()) } } impl Value { /// Serializes an object into a `Value` #[inline] pub fn serialized(value: &T) -> Result { value.serialize(Serializer(())) } } ciborium-0.2.0/tests/codec.rs000064400000000000000000000504110072674642500142520ustar 00000000000000// SPDX-License-Identifier: Apache-2.0 extern crate std; use std::collections::{BTreeMap, HashMap}; use std::convert::TryFrom; use std::fmt::Debug; use ciborium::value::Value; use ciborium::{cbor, de::from_reader, ser::into_writer}; use rstest::rstest; use serde::{Deserialize, Serialize}; macro_rules! val { ($x:expr) => { Value::try_from($x).unwrap() }; } macro_rules! hex { ($x:expr) => { serde_bytes::ByteBuf::from(hex::decode($x).unwrap()) }; } macro_rules! map { ($($k:expr => $v:expr),*) => {{ let mut map = BTreeMap::new(); $( map.insert($k, $v); )* map }} } // Keep the first "case" aligned to a line number ending in 1 for ease in finding tests. #[allow(clippy::excessive_precision)] #[rstest(input, value, bytes, alternate, equality, case(0u8, val!(0u8), "00", false, same), case(0u16, val!(0u16), "00", false, same), case(0u32, val!(0u32), "00", false, same), case(0u64, val!(0u64), "00", false, same), case(0u128, val!(0u128), "00", false, same), case(0i8, val!(0i8), "00", false, same), case(0i16, val!(0i16), "00", false, same), case(0i32, val!(0i32), "00", false, same), case(0i64, val!(0i64), "00", false, same), case(0i128, val!(0i128), "00", false, same), case(1u8, val!(1u8), "01", false, same), case(1u16, val!(1u16), "01", false, same), case(1u32, val!(1u32), "01", false, same), case(1u64, val!(1u64), "01", false, same), case(1u128, val!(1u128), "01", false, same), case(1i8, val!(1i8), "01", false, same), case(1i16, val!(1i16), "01", false, same), case(1i32, val!(1i32), "01", false, same), case(1i64, val!(1i64), "01", false, same), case(1i128, val!(1i128), "01", false, same), case(1u8, val!(1u8), "1b0000000000000001", true, same), case(1u16, val!(1u16), "1b0000000000000001", true, same), case(1u32, val!(1u32), "1b0000000000000001", true, same), case(1u64, val!(1u64), "1b0000000000000001", true, same), case(1u128, val!(1u128), "1b0000000000000001", true, same), case(1i8, val!(1i8), "1b0000000000000001", true, same), case(1i16, val!(1i16), "1b0000000000000001", true, same), case(1i32, val!(1i32), "1b0000000000000001", true, same), case(1i64, val!(1i64), "1b0000000000000001", true, same), case(1i128, val!(1i128), "1b0000000000000001", true, same), case(1u8, bigint(), "c2540000000000000000000000000000000000000001", true, same), // Not In RFC case(1u16, bigint(), "c2540000000000000000000000000000000000000001", true, same), // Not In RFC case(1u32, bigint(), "c2540000000000000000000000000000000000000001", true, same), // Not In RFC case(1u64, bigint(), "c2540000000000000000000000000000000000000001", true, same), // Not In RFC case(1u128, bigint(), "c2540000000000000000000000000000000000000001", true, same), // Not In RFC case(1i8, bigint(), "c2540000000000000000000000000000000000000001", true, same), // Not In RFC case(1i16, bigint(), "c2540000000000000000000000000000000000000001", true, same), // Not In RFC case(1i32, bigint(), "c2540000000000000000000000000000000000000001", true, same), // Not In RFC case(1i64, bigint(), "c2540000000000000000000000000000000000000001", true, same), // Not In RFC case(1i128, bigint(), "c2540000000000000000000000000000000000000001", true, same), // Not In RFC case(10u8, val!(10u8), "0a", false, same), case(10u16, val!(10u16), "0a", false, same), case(10u32, val!(10u32), "0a", false, same), case(10u64, val!(10u64), "0a", false, same), case(10u128, val!(10u128), "0a", false, same), case(10i8, val!(10i8), "0a", false, same), case(10i16, val!(10i16), "0a", false, same), case(10i32, val!(10i32), "0a", false, same), case(10i64, val!(10i64), "0a", false, same), case(10i128, val!(10i128), "0a", false, same), case(23u8, val!(23u8), "17", false, same), case(23u16, val!(23u16), "17", false, same), case(23u32, val!(23u32), "17", false, same), case(23u64, val!(23u64), "17", false, same), case(23u128, val!(23u128), "17", false, same), case(23i8, val!(23i8), "17", false, same), case(23i16, val!(23i16), "17", false, same), case(23i32, val!(23i32), "17", false, same), case(23i64, val!(23i64), "17", false, same), case(23i128, val!(23i128), "17", false, same), case(24u8, val!(24u8), "1818", false, same), case(24u16, val!(24u16), "1818", false, same), case(24u32, val!(24u32), "1818", false, same), case(24u64, val!(24u64), "1818", false, same), case(24u128, val!(24u128), "1818", false, same), case(24i8, val!(24i8), "1818", false, same), case(24i16, val!(24i16), "1818", false, same), case(24i32, val!(24i32), "1818", false, same), case(24i64, val!(24i64), "1818", false, same), case(24i128, val!(24i128), "1818", false, same), case(25u8, val!(25u8), "1819", false, same), case(25u16, val!(25u16), "1819", false, same), case(25u32, val!(25u32), "1819", false, same), case(25u64, val!(25u64), "1819", false, same), case(25u128, val!(25u128), "1819", false, same), case(25i8, val!(25i8), "1819", false, same), case(25i16, val!(25i16), "1819", false, same), case(25i32, val!(25i32), "1819", false, same), case(25i64, val!(25i64), "1819", false, same), case(25i128, val!(25i128), "1819", false, same), case(100u8, val!(100u8), "1864", false, same), case(100u16, val!(100u16), "1864", false, same), case(100u32, val!(100u32), "1864", false, same), case(100u64, val!(100u64), "1864", false, same), case(100u128, val!(100u128), "1864", false, same), case(100i8, val!(100i8), "1864", false, same), case(100i16, val!(100i16), "1864", false, same), case(100i32, val!(100i32), "1864", false, same), case(100i64, val!(100i64), "1864", false, same), case(100i128, val!(100i128), "1864", false, same), case(1000u16, val!(1000u16), "1903e8", false, same), case(1000u32, val!(1000u32), "1903e8", false, same), case(1000u64, val!(1000u64), "1903e8", false, same), case(1000u128, val!(1000u128), "1903e8", false, same), case(1000i16, val!(1000i16), "1903e8", false, same), case(1000i32, val!(1000i32), "1903e8", false, same), case(1000i64, val!(1000i64), "1903e8", false, same), case(1000i128, val!(1000i128), "1903e8", false, same), case(1000000u32, val!(1000000u32), "1a000f4240", false, same), case(1000000u64, val!(1000000u64), "1a000f4240", false, same), case(1000000u128, val!(1000000u128), "1a000f4240", false, same), case(1000000i32, val!(1000000i32), "1a000f4240", false, same), case(1000000i64, val!(1000000i64), "1a000f4240", false, same), case(1000000i128, val!(1000000i128), "1a000f4240", false, same), case(1000000000000u64, val!(1000000000000u64), "1b000000e8d4a51000", false, same), case(1000000000000u128, val!(1000000000000u128), "1b000000e8d4a51000", false, same), case(1000000000000i64, val!(1000000000000i64), "1b000000e8d4a51000", false, same), case(1000000000000i128, val!(1000000000000i128), "1b000000e8d4a51000", false, same), case(18446744073709551615u64, val!(18446744073709551615u64), "1bffffffffffffffff", false, same), case(18446744073709551615u128, val!(18446744073709551615u128), "1bffffffffffffffff", false, same), case(18446744073709551615i128, val!(18446744073709551615i128), "1bffffffffffffffff", false, same), case(18446744073709551616u128, val!(18446744073709551616u128), "c249010000000000000000", false, same), case(18446744073709551616i128, val!(18446744073709551616i128), "c249010000000000000000", false, same), case(-18446744073709551617i128, val!(-18446744073709551617i128), "c349010000000000000000", false, same), case(-18446744073709551616i128, val!(-18446744073709551616i128), "3bffffffffffffffff", false, same), case(-1000i16, val!(-1000i16), "3903e7", false, same), case(-1000i32, val!(-1000i32), "3903e7", false, same), case(-1000i64, val!(-1000i64), "3903e7", false, same), case(-1000i128, val!(-1000i128), "3903e7", false, same), case(-100i8, val!(-100i8), "3863", false, same), case(-100i16, val!(-100i16), "3863", false, same), case(-100i32, val!(-100i32), "3863", false, same), case(-100i64, val!(-100i64), "3863", false, same), case(-100i128, val!(-100i128), "3863", false, same), case(-10i8, val!(-10i8), "29", false, same), case(-10i16, val!(-10i16), "29", false, same), case(-10i32, val!(-10i32), "29", false, same), case(-10i64, val!(-10i64), "29", false, same), case(-10i128, val!(-10i128), "29", false, same), case(-1i8, val!(-1i8), "20", false, same), case(-1i16, val!(-1i16), "20", false, same), case(-1i32, val!(-1i32), "20", false, same), case(-1i64, val!(-1i64), "20", false, same), case(-1i128, val!(-1i128), "20", false, same), case(-1i8, val!(-1i8), "3b0000000000000000", true, same), case(-1i16, val!(-1i16), "3b0000000000000000", true, same), case(-1i32, val!(-1i32), "3b0000000000000000", true, same), case(-1i64, val!(-1i64), "3b0000000000000000", true, same), case(-1i128, val!(-1i128), "3b0000000000000000", true, same), case(0.0f32, val!(0.0f32), "f90000", false, Float), case(0.0f64, val!(0.0f64), "f90000", false, Float), case(-0.0f32, val!(-0.0f32), "f98000", false, Float), case(-0.0f64, val!(-0.0f64), "f98000", false, Float), case(1.0f32, val!(1.0f32), "f93c00", false, Float), case(1.0f64, val!(1.0f64), "f93c00", false, Float), case(1.1f32, val!(1.1f32), "fa3f8ccccd", false, Float), // Not In RFC case(1.1f64, val!(1.1f64), "fb3ff199999999999a", false, Float), case(1.5f32, val!(1.5f32), "f93e00", false, Float), case(1.5f64, val!(1.5f64), "f93e00", false, Float), case(65504.0f32, val!(65504.0f32), "f97bff", false, Float), case(65504.0f64, val!(65504.0f64), "f97bff", false, Float), case(100000.0f32, val!(100000.0f32), "fa47c35000", false, Float), case(100000.0f64, val!(100000.0f64), "fa47c35000", false, Float), case(3.4028234663852886e+38f32, val!(3.4028234663852886e+38f32), "fa7f7fffff", false, Float), case(3.4028234663852886e+38f64, val!(3.4028234663852886e+38f64), "fa7f7fffff", false, Float), case(1.0e+300f64, val!(1.0e+300f64), "fb7e37e43c8800759c", false, Float), case(5.960464477539063e-8f32, val!(5.960464477539063e-8f32), "f90001", false, Float), case(5.960464477539063e-8f64, val!(5.960464477539063e-8f64), "f90001", false, Float), case(0.00006103515625f32, val!(0.00006103515625f32), "f90400", false, Float), case(0.00006103515625f64, val!(0.00006103515625f64), "f90400", false, Float), case(-4.0f32, val!(-4.0f32), "f9c400", false, Float), case(-4.0f64, val!(-4.0f64), "f9c400", false, Float), case(-4.1f32, val!(-4.1f32), "fac0833333", false, Float), // Not In RFC case(-4.1f64, val!(-4.1f64), "fbc010666666666666", false, Float), case(core::f32::INFINITY, val!(core::f32::INFINITY), "f97c00", false, Float), case(core::f64::INFINITY, val!(core::f64::INFINITY), "f97c00", false, Float), case(core::f32::INFINITY, val!(core::f32::INFINITY), "fa7f800000", true, Float), case(core::f64::INFINITY, val!(core::f64::INFINITY), "fa7f800000", true, Float), case(core::f32::INFINITY, val!(core::f32::INFINITY), "fb7ff0000000000000", true, Float), case(core::f64::INFINITY, val!(core::f64::INFINITY), "fb7ff0000000000000", true, Float), case(-core::f32::INFINITY, val!(-core::f32::INFINITY), "f9fc00", false, Float), case(-core::f64::INFINITY, val!(-core::f64::INFINITY), "f9fc00", false, Float), case(-core::f32::INFINITY, val!(-core::f32::INFINITY), "faff800000", true, Float), case(-core::f64::INFINITY, val!(-core::f64::INFINITY), "faff800000", true, Float), case(-core::f32::INFINITY, val!(-core::f32::INFINITY), "fbfff0000000000000", true, Float), case(-core::f64::INFINITY, val!(-core::f64::INFINITY), "fbfff0000000000000", true, Float), case(core::f32::NAN, val!(core::f32::NAN), "f97e00", false, Float), case(core::f64::NAN, val!(core::f64::NAN), "f97e00", false, Float), case(core::f32::NAN, val!(core::f32::NAN), "fa7fc00000", true, Float), case(core::f64::NAN, val!(core::f64::NAN), "fa7fc00000", true, Float), case(core::f32::NAN, val!(core::f32::NAN), "fb7ff8000000000000", true, Float), case(core::f64::NAN, val!(core::f64::NAN), "fb7ff8000000000000", true, Float), case(-core::f32::NAN, val!(-core::f32::NAN), "f9fe00", false, Float), // Not In RFC case(-core::f64::NAN, val!(-core::f64::NAN), "f9fe00", false, Float), // Not In RFC case(-core::f32::NAN, val!(-core::f32::NAN), "faffc00000", true, Float), // Not In RFC case(-core::f64::NAN, val!(-core::f64::NAN), "faffc00000", true, Float), // Not In RFC case(-core::f32::NAN, val!(-core::f32::NAN), "fbfff8000000000000", true, Float), // Not In RFC case(-core::f64::NAN, val!(-core::f64::NAN), "fbfff8000000000000", true, Float), // Not In RFC case(false, val!(false), "f4", false, same), case(true, val!(true), "f5", false, same), case(Value::Null, Value::Null, "f6", false, same), case(hex!(""), val!(&b""[..]), "40", false, same), case(hex!("01020304"), val!(&b"\x01\x02\x03\x04"[..]), "4401020304", false, same), case(hex!("0102030405"), val!(&b"\x01\x02\x03\x04\x05"[..]), "5f42010243030405ff", true, same), case("", val!(""), "60", false, ToOwned::to_owned), case("a", val!("a"), "6161", false, ToOwned::to_owned), case('a', val!('a'), "6161", false, same), case("IETF", val!("IETF"), "6449455446", false, ToOwned::to_owned), case("\"\\", val!("\"\\"), "62225c", false, ToOwned::to_owned), case("ü", val!("ü"), "62c3bc", false, ToOwned::to_owned), case('ü', val!('ü'), "62c3bc", false, same), case("水", val!("水"), "63e6b0b4", false, ToOwned::to_owned), case('水', val!('水'), "63e6b0b4", false, same), case("𐅑", val!("𐅑"), "64f0908591", false, ToOwned::to_owned), case('𐅑', val!('𐅑'), "64f0908591", false, same), case("streaming", val!("streaming"), "7f657374726561646d696e67ff", true, ToOwned::to_owned), case(cbor!([]).unwrap(), Vec::::new().into(), "80", false, same), case(cbor!([]).unwrap(), Vec::::new().into(), "9fff", true, same), case(cbor!([1, 2, 3]).unwrap(), cbor!([1, 2, 3]).unwrap(), "83010203", false, same), case(cbor!([1, [2, 3], [4, 5]]).unwrap(), cbor!([1, [2, 3], [4, 5]]).unwrap(), "8301820203820405", false, same), case(cbor!([1, [2, 3], [4, 5]]).unwrap(), cbor!([1, [2, 3], [4, 5]]).unwrap(), "9f018202039f0405ffff", true, same), case(cbor!([1, [2, 3], [4, 5]]).unwrap(), cbor!([1, [2, 3], [4, 5]]).unwrap(), "9f01820203820405ff", true, same), case(cbor!([1, [2, 3], [4, 5]]).unwrap(), cbor!([1, [2, 3], [4, 5]]).unwrap(), "83018202039f0405ff", true, same), case(cbor!([1, [2, 3], [4, 5]]).unwrap(), cbor!([1, [2, 3], [4, 5]]).unwrap(), "83019f0203ff820405", true, same), case((1..=25).collect::>(), (1..=25).map(|x| x.into()).collect::>().into(), "98190102030405060708090a0b0c0d0e0f101112131415161718181819", false, same), case((1..=25).collect::>(), (1..=25).map(|x| x.into()).collect::>().into(), "9f0102030405060708090a0b0c0d0e0f101112131415161718181819ff", true, same), case(HashMap::::new(), Value::Map(vec![]), "a0", false, same), case(BTreeMap::::new(), Value::Map(vec![]), "a0", false, same), case(map!{1 => 2, 3 => 4}, cbor!({1 => 2, 3 => 4}).unwrap(), "a201020304", false, same), case(cbor!({"a" => 1, "b" => [2, 3]}).unwrap(), cbor!({"a" => 1, "b" => [2, 3]}).unwrap(), "a26161016162820203", false, same), case(cbor!({"a" => 1, "b" => [2, 3]}).unwrap(), cbor!({"a" => 1, "b" => [2, 3]}).unwrap(), "bf61610161629f0203ffff", true, same), case(cbor!(["a", {"b" => "c"}]).unwrap(), cbor!(["a", {"b" => "c"}]).unwrap(), "826161a161626163", false, same), case(cbor!(["a", {"b" => "c"}]).unwrap(), cbor!(["a", {"b" => "c"}]).unwrap(), "826161bf61626163ff", true, same), case(cbor!({"Fun" => true, "Amt" => -2}).unwrap(), cbor!({"Fun" => true, "Amt" => -2}).unwrap(), "bf6346756ef563416d7421ff", true, same), case(map_big(), vmap_big(), "a56161614161626142616361436164614461656145", false, same), case(Option::::None, Value::Null, "f6", false, same), // Not In RFC case(Option::Some(7u8), val!(7u8), "07", false, same), // Not In RFC case((), Value::Null, "f6", false, same), // Not In RFC case(UnitStruct, Value::Null, "f6", false, same), // Not In RFC case(Newtype(123), val!(123u8), "187b", false, same), // Not In RFC case((22u8, 23u16), cbor!([22, 23]).unwrap(), "821617", false, same), // Not In RFC case(TupleStruct(33, 34), cbor!([33, 34]).unwrap(), "8218211822", false, same), // Not In RFC case(Enum::Unit, cbor!("Unit").unwrap(), "64556e6974", false, same), // Not In RFC case(Enum::Newtype(45), cbor!({"Newtype" => 45}).unwrap(), "a1674e657774797065182d", false, same), // Not In RFC case(Enum::Tuple(56, 67), cbor!({"Tuple" => [56, 67]}).unwrap(), "a1655475706c658218381843", false, same), // Not In RFC case(Enum::Struct { first: 78, second: 89 }, cbor!({ "Struct" => { "first" => 78, "second" => 89 }}).unwrap(), "a166537472756374a2656669727374184e667365636f6e641859", false, same), // Not In RFC )] fn codec<'de, T: Serialize + Clone, V: Debug + PartialEq + Deserialize<'de>, F: Fn(T) -> V>( input: T, value: Value, bytes: &str, alternate: bool, equality: F, ) { let bytes = hex::decode(bytes).unwrap(); if !alternate { let mut encoded = Vec::new(); into_writer(&input, &mut encoded).unwrap(); eprintln!("{:x?} == {:x?}", bytes, encoded); assert_eq!(bytes, encoded); let mut encoded = Vec::new(); into_writer(&value, &mut encoded).unwrap(); eprintln!("{:x?} == {:x?}", bytes, encoded); assert_eq!(bytes, encoded); let encoded = Value::serialized(&input).unwrap(); eprintln!("{:x?} == {:x?}", &value, &encoded); assert!(veq(&value, &encoded)); } let decoded: V = from_reader(&bytes[..]).unwrap(); let answer = equality(input.clone()); eprintln!("{:x?} == {:x?}", answer, decoded); assert_eq!(answer, decoded); let decoded: Value = from_reader(&bytes[..]).unwrap(); eprintln!("{:x?} == {:x?}", &value, &decoded); assert!(veq(&value, &decoded)); let decoded: V = value.deserialized().unwrap(); let answer = equality(input); eprintln!("{:x?} == {:x?}", answer, decoded); assert_eq!(answer, decoded); } #[inline] fn veq(lhs: &Value, rhs: &Value) -> bool { if let Value::Float(l) = lhs { if let Value::Float(r) = rhs { return Float(*l) == Float(*r); } } lhs == rhs } #[inline] fn same(x: T) -> T { x } #[derive(Debug, Deserialize)] struct Float(T); impl PartialEq for Float { fn eq(&self, other: &Float) -> bool { if self.0.is_nan() && other.0.is_nan() { return true; } self.0 == other.0 } } impl PartialEq for Float { fn eq(&self, other: &Float) -> bool { if self.0.is_nan() && other.0.is_nan() { return true; } self.0 == other.0 } } #[inline] fn map_big() -> BTreeMap { let mut map = BTreeMap::new(); map.insert("a".into(), "A".into()); map.insert("b".into(), "B".into()); map.insert("c".into(), "C".into()); map.insert("d".into(), "D".into()); map.insert("e".into(), "E".into()); map } #[inline] fn vmap_big() -> Value { Value::Map( map_big() .into_iter() .map(|x| (x.0.into(), x.1.into())) .collect(), ) } #[inline] fn bigint() -> Value { let bytes = hex::decode("0000000000000000000000000000000000000001").unwrap(); Value::Tag(2, Value::Bytes(bytes).into()) } #[derive(Deserialize, Serialize, Copy, Clone, Debug, PartialEq, Eq)] struct UnitStruct; #[derive(Deserialize, Serialize, Copy, Clone, Debug, PartialEq, Eq)] struct TupleStruct(u8, u16); #[derive(Deserialize, Serialize, Copy, Clone, Debug, PartialEq, Eq)] struct Newtype(u8); #[derive(Deserialize, Serialize, Copy, Clone, Debug, PartialEq, Eq)] enum Enum { Unit, Newtype(u8), Tuple(u8, u16), Struct { first: u8, second: u16 }, } ciborium-0.2.0/tests/error.rs000064400000000000000000000025210072674642500143250ustar 00000000000000// SPDX-License-Identifier: Apache-2.0 use ciborium::{de::from_reader, de::Error, value::Value}; use rstest::rstest; #[rstest(bytes, error, // Invalid value case("1e", Error::Syntax(0)), // Indeterminate integers are invalid case("1f", Error::Syntax(0)), // Indeterminate integer in an array case("83011f03", Error::Syntax(2)), // Integer in a string continuation case("7F616101FF", Error::Syntax(3)), // Bytes in a string continuation case("7F61614101FF", Error::Syntax(3)), // Invalid UTF-8 case("62C328", Error::Syntax(0)), // Invalid UTF-8 in a string continuation case("7F62C328FF", Error::Syntax(1)), )] fn test(bytes: &str, error: Error) { let bytes = hex::decode(bytes).unwrap(); let correct = match error { Error::Io(..) => panic!(), Error::Syntax(x) => ("syntax", Some(x), None), Error::Semantic(x, y) => ("semantic", x, Some(y)), Error::RecursionLimitExceeded => panic!(), }; let result: Result = from_reader(&bytes[..]); let actual = match result.unwrap_err() { Error::Io(..) => panic!(), Error::Syntax(x) => ("syntax", Some(x), None), Error::Semantic(x, y) => ("semantic", x, Some(y)), Error::RecursionLimitExceeded => panic!(), }; assert_eq!(correct, actual); } ciborium-0.2.0/tests/fuzz.rs000064400000000000000000000032460072674642500141770ustar 00000000000000// SPDX-License-Identifier: Apache-2.0 use std::fs::File; use std::io::Read; use std::io::Write; use std::os::raw::c_int; use std::os::unix::io::{FromRawFd, RawFd}; use ciborium::{de::from_reader, value::Value}; use rand::Rng; const ITERATIONS: usize = 128 * 1024; #[allow(non_camel_case_types)] type pid_t = i32; extern "C" { fn close(fd: RawFd) -> c_int; fn fork() -> pid_t; fn pipe(pipefd: &mut [RawFd; 2]) -> c_int; fn waitpid(pid: pid_t, wstatus: *mut c_int, options: c_int) -> pid_t; } #[test] fn fuzz() { let mut fds: [RawFd; 2] = [0; 2]; assert_eq!(unsafe { pipe(&mut fds) }, 0); let pid = unsafe { fork() }; assert!(pid >= 0); match pid { 0 => { let mut child = unsafe { File::from_raw_fd(fds[1]) }; unsafe { close(fds[0]) }; let mut rng = rand::thread_rng(); let mut buffer = [0u8; 32]; for _ in 0..ITERATIONS { let len = rng.gen_range(0..buffer.len()); rng.fill(&mut buffer[..len]); writeln!(child, "{}", hex::encode(&buffer[..len])).unwrap(); writeln!(child, "{:?}", from_reader::(&buffer[..len])).unwrap(); } } pid => { let mut parent = unsafe { File::from_raw_fd(fds[0]) }; unsafe { close(fds[1]) }; let mut string = String::new(); parent.read_to_string(&mut string).unwrap(); eprint!("{}", string); let mut status = 0; assert_eq!(pid, unsafe { waitpid(pid, &mut status, 0) }); eprintln!("exit status: {:?}", status); assert_eq!(0, status); } } } ciborium-0.2.0/tests/macro.rs000064400000000000000000000520370072674642500143040ustar 00000000000000// SPDX-License-Identifier: Apache-2.0 extern crate alloc; use ciborium::{ cbor, value::{Integer, Value, Value::Null}, }; use rstest::rstest; use serde_bytes::Bytes; macro_rules! map { ($($key:expr => $val:expr),* $(,)*) => { Value::Map(vec![$( ( Value::serialized(&$key).unwrap(), Value::serialized(&$val).unwrap() ) ),*]) }; } macro_rules! arr { ($($val:expr),*) => { Value::Array(vec![$( Value::serialized(&$val).unwrap() ),*]) }; } #[rstest(answer, question, // Non-numeric simple types case(Value::Null, cbor!(null).unwrap()), case(Value::Bool(true), cbor!(true).unwrap()), case(Value::Bool(false), cbor!(false).unwrap()), case(Value::Text("foo".into()), cbor!("foo").unwrap()), case(Value::Bytes(vec![0, 1, 2]), cbor!(Bytes::new(b"\x00\x01\x02")).unwrap()), // Numeric simple types case(Value::Integer(Integer::from(123)), cbor!(123).unwrap()), case(Value::Integer(Integer::from(-123)), cbor!(-123).unwrap()), case(Value::Float(1.23), cbor!(1.23).unwrap()), case(Value::Float(-1.23), cbor!(-1.23).unwrap()), case(Value::Float(2.5e+1), cbor!(2.5e+1).unwrap()), case(Value::Float(-2.5e+1), cbor!(-2.5e+1).unwrap()), // Simple array formulations case(arr![], cbor!([]).unwrap()), case(arr![Null], cbor!([null]).unwrap()), case(arr![true], cbor!([true]).unwrap()), case(arr![false], cbor!([false]).unwrap()), case(arr!["foo"], cbor!(["foo"]).unwrap()), case(arr![123], cbor!([123]).unwrap()), case(arr![-123], cbor!([-123]).unwrap()), case(arr![1.23], cbor!([1.23]).unwrap()), case(arr![-1.23], cbor!([-1.23]).unwrap()), case(arr![2.5e+1], cbor!([2.5e+1]).unwrap()), case(arr![2.5e+1], cbor!([2.5e+1]).unwrap()), case(arr![[1, 2]], cbor!([[1, 2]]).unwrap()), case(arr![map! {1=>2,3=>4}], cbor!([{1=>2,3=>4}]).unwrap()), // Two-item array formluations case(arr![Null, Null], cbor!([null, null]).unwrap()), case(arr![Null, true], cbor!([null, true]).unwrap()), case(arr![Null, false], cbor!([null, false]).unwrap()), case(arr![Null, "foo"], cbor!([null, "foo"]).unwrap()), case(arr![Null, 123], cbor!([null, 123]).unwrap()), case(arr![Null, -123], cbor!([null, -123]).unwrap()), case(arr![Null, 1.23], cbor!([null, 1.23]).unwrap()), case(arr![Null, -1.23], cbor!([null, -1.23]).unwrap()), case(arr![Null, 2.5e+1], cbor!([null, 2.5e+1]).unwrap()), case(arr![Null, 2.5e+1], cbor!([null, 2.5e+1]).unwrap()), case(arr![Null, [1, 2]], cbor!([null, [1, 2]]).unwrap()), case(arr![Null, map! {1=>2,3=>4}], cbor!([null, {1=>2,3=>4}]).unwrap()), case(arr![true, Null], cbor!([true, null]).unwrap()), case(arr![true, true], cbor!([true, true]).unwrap()), case(arr![true, false], cbor!([true, false]).unwrap()), case(arr![true, "foo"], cbor!([true, "foo"]).unwrap()), case(arr![true, 123], cbor!([true, 123]).unwrap()), case(arr![true, -123], cbor!([true, -123]).unwrap()), case(arr![true, 1.23], cbor!([true, 1.23]).unwrap()), case(arr![true, -1.23], cbor!([true, -1.23]).unwrap()), case(arr![true, 2.5e+1], cbor!([true, 2.5e+1]).unwrap()), case(arr![true, 2.5e+1], cbor!([true, 2.5e+1]).unwrap()), case(arr![true, [1, 2]], cbor!([true, [1, 2]]).unwrap()), case(arr![true, map! {1=>2,3=>4}], cbor!([true, {1=>2,3=>4}]).unwrap()), case(arr![false, Null], cbor!([false, null]).unwrap()), case(arr![false, true], cbor!([false, true]).unwrap()), case(arr![false, false], cbor!([false, false]).unwrap()), case(arr![false, "foo"], cbor!([false, "foo"]).unwrap()), case(arr![false, 123], cbor!([false, 123]).unwrap()), case(arr![false, -123], cbor!([false, -123]).unwrap()), case(arr![false, 1.23], cbor!([false, 1.23]).unwrap()), case(arr![false, -1.23], cbor!([false, -1.23]).unwrap()), case(arr![false, 2.5e+1], cbor!([false, 2.5e+1]).unwrap()), case(arr![false, 2.5e+1], cbor!([false, 2.5e+1]).unwrap()), case(arr![false, [1, 2]], cbor!([false, [1, 2]]).unwrap()), case(arr![false, map! {1=>2,3=>4}], cbor!([false, {1=>2,3=>4}]).unwrap()), case(arr!["foo", Null], cbor!(["foo", null]).unwrap()), case(arr!["foo", true], cbor!(["foo", true]).unwrap()), case(arr!["foo", false], cbor!(["foo", false]).unwrap()), case(arr!["foo", "foo"], cbor!(["foo", "foo"]).unwrap()), case(arr!["foo", 123], cbor!(["foo", 123]).unwrap()), case(arr!["foo", -123], cbor!(["foo", -123]).unwrap()), case(arr!["foo", 1.23], cbor!(["foo", 1.23]).unwrap()), case(arr!["foo", -1.23], cbor!(["foo", -1.23]).unwrap()), case(arr!["foo", 2.5e+1], cbor!(["foo", 2.5e+1]).unwrap()), case(arr!["foo", 2.5e+1], cbor!(["foo", 2.5e+1]).unwrap()), case(arr!["foo", [1, 2]], cbor!(["foo", [1, 2]]).unwrap()), case(arr!["foo", map! {1=>2,3=>4}], cbor!(["foo", {1=>2,3=>4}]).unwrap()), case(arr![123, Null], cbor!([123, null]).unwrap()), case(arr![123, true], cbor!([123, true]).unwrap()), case(arr![123, false], cbor!([123, false]).unwrap()), case(arr![123, "foo"], cbor!([123, "foo"]).unwrap()), case(arr![123, 123], cbor!([123, 123]).unwrap()), case(arr![123, -123], cbor!([123, -123]).unwrap()), case(arr![123, 1.23], cbor!([123, 1.23]).unwrap()), case(arr![123, -1.23], cbor!([123, -1.23]).unwrap()), case(arr![123, 2.5e+1], cbor!([123, 2.5e+1]).unwrap()), case(arr![123, 2.5e+1], cbor!([123, 2.5e+1]).unwrap()), case(arr![123, [1, 2]], cbor!([123, [1, 2]]).unwrap()), case(arr![123, map! {1=>2,3=>4}], cbor!([123, {1=>2,3=>4}]).unwrap()), case(arr![-123, Null], cbor!([-123, null]).unwrap()), case(arr![-123, true], cbor!([-123, true]).unwrap()), case(arr![-123, false], cbor!([-123, false]).unwrap()), case(arr![-123, "foo"], cbor!([-123, "foo"]).unwrap()), case(arr![-123, 123], cbor!([-123, 123]).unwrap()), case(arr![-123, -123], cbor!([-123, -123]).unwrap()), case(arr![-123, 1.23], cbor!([-123, 1.23]).unwrap()), case(arr![-123, -1.23], cbor!([-123, -1.23]).unwrap()), case(arr![-123, 2.5e+1], cbor!([-123, 2.5e+1]).unwrap()), case(arr![-123, 2.5e+1], cbor!([-123, 2.5e+1]).unwrap()), case(arr![-123, [1, 2]], cbor!([-123, [1, 2]]).unwrap()), case(arr![-123, map! {1=>2,3=>4}], cbor!([-123, {1=>2,3=>4}]).unwrap()), case(arr![1.23, Null], cbor!([1.23, null]).unwrap()), case(arr![1.23, true], cbor!([1.23, true]).unwrap()), case(arr![1.23, false], cbor!([1.23, false]).unwrap()), case(arr![1.23, "foo"], cbor!([1.23, "foo"]).unwrap()), case(arr![1.23, 123], cbor!([1.23, 123]).unwrap()), case(arr![1.23, -123], cbor!([1.23, -123]).unwrap()), case(arr![1.23, 1.23], cbor!([1.23, 1.23]).unwrap()), case(arr![1.23, -1.23], cbor!([1.23, -1.23]).unwrap()), case(arr![1.23, 2.5e+1], cbor!([1.23, 2.5e+1]).unwrap()), case(arr![1.23, 2.5e+1], cbor!([1.23, 2.5e+1]).unwrap()), case(arr![1.23, [1, 2]], cbor!([1.23, [1, 2]]).unwrap()), case(arr![1.23, map! {1=>2,3=>4}], cbor!([1.23, {1=>2,3=>4}]).unwrap()), case(arr![-1.23, Null], cbor!([-1.23, null]).unwrap()), case(arr![-1.23, true], cbor!([-1.23, true]).unwrap()), case(arr![-1.23, false], cbor!([-1.23, false]).unwrap()), case(arr![-1.23, "foo"], cbor!([-1.23, "foo"]).unwrap()), case(arr![-1.23, 123], cbor!([-1.23, 123]).unwrap()), case(arr![-1.23, -123], cbor!([-1.23, -123]).unwrap()), case(arr![-1.23, 1.23], cbor!([-1.23, 1.23]).unwrap()), case(arr![-1.23, -1.23], cbor!([-1.23, -1.23]).unwrap()), case(arr![-1.23, 2.5e+1], cbor!([-1.23, 2.5e+1]).unwrap()), case(arr![-1.23, 2.5e+1], cbor!([-1.23, 2.5e+1]).unwrap()), case(arr![-1.23, [1, 2]], cbor!([-1.23, [1, 2]]).unwrap()), case(arr![-1.23, map! {1=>2,3=>4}], cbor!([-1.23, {1=>2,3=>4}]).unwrap()), case(arr![2.5e+1, Null], cbor!([2.5e+1, null]).unwrap()), case(arr![2.5e+1, true], cbor!([2.5e+1, true]).unwrap()), case(arr![2.5e+1, false], cbor!([2.5e+1, false]).unwrap()), case(arr![2.5e+1, "foo"], cbor!([2.5e+1, "foo"]).unwrap()), case(arr![2.5e+1, 123], cbor!([2.5e+1, 123]).unwrap()), case(arr![2.5e+1, -123], cbor!([2.5e+1, -123]).unwrap()), case(arr![2.5e+1, 1.23], cbor!([2.5e+1, 1.23]).unwrap()), case(arr![2.5e+1, -1.23], cbor!([2.5e+1, -1.23]).unwrap()), case(arr![2.5e+1, 2.5e+1], cbor!([2.5e+1, 2.5e+1]).unwrap()), case(arr![2.5e+1, 2.5e+1], cbor!([2.5e+1, 2.5e+1]).unwrap()), case(arr![2.5e+1, [1, 2]], cbor!([2.5e+1, [1, 2]]).unwrap()), case(arr![2.5e+1, map! {1=>2,3=>4}], cbor!([2.5e+1, {1=>2,3=>4}]).unwrap()), case(arr![2.5e+1, Null], cbor!([2.5e+1, null]).unwrap()), case(arr![2.5e+1, true], cbor!([2.5e+1, true]).unwrap()), case(arr![2.5e+1, false], cbor!([2.5e+1, false]).unwrap()), case(arr![2.5e+1, "foo"], cbor!([2.5e+1, "foo"]).unwrap()), case(arr![2.5e+1, 123], cbor!([2.5e+1, 123]).unwrap()), case(arr![2.5e+1, -123], cbor!([2.5e+1, -123]).unwrap()), case(arr![2.5e+1, 1.23], cbor!([2.5e+1, 1.23]).unwrap()), case(arr![2.5e+1, -1.23], cbor!([2.5e+1, -1.23]).unwrap()), case(arr![2.5e+1, 2.5e+1], cbor!([2.5e+1, 2.5e+1]).unwrap()), case(arr![2.5e+1, 2.5e+1], cbor!([2.5e+1, 2.5e+1]).unwrap()), case(arr![2.5e+1, [1, 2]], cbor!([2.5e+1, [1, 2]]).unwrap()), case(arr![2.5e+1, map! {1=>2,3=>4}], cbor!([2.5e+1, {1=>2,3=>4}]).unwrap()), case(arr![[1, 2], Null], cbor!([[1, 2], null]).unwrap()), case(arr![[1, 2], true], cbor!([[1, 2], true]).unwrap()), case(arr![[1, 2], false], cbor!([[1, 2], false]).unwrap()), case(arr![[1, 2], "foo"], cbor!([[1, 2], "foo"]).unwrap()), case(arr![[1, 2], 123], cbor!([[1, 2], 123]).unwrap()), case(arr![[1, 2], -123], cbor!([[1, 2], -123]).unwrap()), case(arr![[1, 2], 1.23], cbor!([[1, 2], 1.23]).unwrap()), case(arr![[1, 2], -1.23], cbor!([[1, 2], -1.23]).unwrap()), case(arr![[1, 2], 2.5e+1], cbor!([[1, 2], 2.5e+1]).unwrap()), case(arr![[1, 2], 2.5e+1], cbor!([[1, 2], 2.5e+1]).unwrap()), case(arr![[1, 2], [1, 2]], cbor!([[1, 2], [1, 2]]).unwrap()), case(arr![[1, 2], map! {1=>2,3=>4}], cbor!([[1, 2], {1=>2,3=>4}]).unwrap()), case(arr![map! {1=>2,3=>4}, Null], cbor!([{1=>2,3=>4}, null]).unwrap()), case(arr![map! {1=>2,3=>4}, true], cbor!([{1=>2,3=>4}, true]).unwrap()), case(arr![map! {1=>2,3=>4}, false], cbor!([{1=>2,3=>4}, false]).unwrap()), case(arr![map! {1=>2,3=>4}, "foo"], cbor!([{1=>2,3=>4}, "foo"]).unwrap()), case(arr![map! {1=>2,3=>4}, 123], cbor!([{1=>2,3=>4}, 123]).unwrap()), case(arr![map! {1=>2,3=>4}, -123], cbor!([{1=>2,3=>4}, -123]).unwrap()), case(arr![map! {1=>2,3=>4}, 1.23], cbor!([{1=>2,3=>4}, 1.23]).unwrap()), case(arr![map! {1=>2,3=>4}, -1.23], cbor!([{1=>2,3=>4}, -1.23]).unwrap()), case(arr![map! {1=>2,3=>4}, 2.5e+1], cbor!([{1=>2,3=>4}, 2.5e+1]).unwrap()), case(arr![map! {1=>2,3=>4}, 2.5e+1], cbor!([{1=>2,3=>4}, 2.5e+1]).unwrap()), case(arr![map! {1=>2,3=>4}, [1, 2]], cbor!([{1=>2,3=>4}, [1, 2]]).unwrap()), case(arr![map! {1=>2,3=>4}, map! {1=>2,3=>4}], cbor!([{1=>2,3=>4}, {1=>2,3=>4}]).unwrap()), // Map formulations case(map! {}, cbor!({}).unwrap()), case(map! {Null => Null}, cbor!({ null => null }).unwrap()), case(map! {Null => true}, cbor!({ null => true }).unwrap()), case(map! {Null => false}, cbor!({ null => false }).unwrap()), case(map! {Null => "foo"}, cbor!({ null => "foo" }).unwrap()), case(map! {Null => 123}, cbor!({ null => 123 }).unwrap()), case(map! {Null => -123}, cbor!({ null => -123 }).unwrap()), case(map! {Null => 1.23}, cbor!({ null => 1.23 }).unwrap()), case(map! {Null => -1.23}, cbor!({ null => -1.23 }).unwrap()), case(map! {Null => 2.5e+1}, cbor!({ null => 2.5e+1 }).unwrap()), case(map! {Null => 2.5e+1}, cbor!({ null => 2.5e+1 }).unwrap()), case(map! {Null => [1, 2]}, cbor!({ null => [1, 2] }).unwrap()), case(map! {Null => map! {1=>2,3=>4}}, cbor!({ null => {1=>2,3=>4} }).unwrap()), case(map! {true => Null}, cbor!({ true => null }).unwrap()), case(map! {true => true}, cbor!({ true => true }).unwrap()), case(map! {true => false}, cbor!({ true => false }).unwrap()), case(map! {true => "foo"}, cbor!({ true => "foo" }).unwrap()), case(map! {true => 123}, cbor!({ true => 123 }).unwrap()), case(map! {true => -123}, cbor!({ true => -123 }).unwrap()), case(map! {true => 1.23}, cbor!({ true => 1.23 }).unwrap()), case(map! {true => -1.23}, cbor!({ true => -1.23 }).unwrap()), case(map! {true => 2.5e+1}, cbor!({ true => 2.5e+1 }).unwrap()), case(map! {true => 2.5e+1}, cbor!({ true => 2.5e+1 }).unwrap()), case(map! {true => [1, 2]}, cbor!({ true => [1, 2] }).unwrap()), case(map! {true => map! {1=>2,3=>4}}, cbor!({ true => {1=>2,3=>4} }).unwrap()), case(map! {false => Null}, cbor!({ false => null }).unwrap()), case(map! {false => true}, cbor!({ false => true }).unwrap()), case(map! {false => false}, cbor!({ false => false }).unwrap()), case(map! {false => "foo"}, cbor!({ false => "foo" }).unwrap()), case(map! {false => 123}, cbor!({ false => 123 }).unwrap()), case(map! {false => -123}, cbor!({ false => -123 }).unwrap()), case(map! {false => 1.23}, cbor!({ false => 1.23 }).unwrap()), case(map! {false => -1.23}, cbor!({ false => -1.23 }).unwrap()), case(map! {false => 2.5e+1}, cbor!({ false => 2.5e+1 }).unwrap()), case(map! {false => 2.5e+1}, cbor!({ false => 2.5e+1 }).unwrap()), case(map! {false => [1, 2]}, cbor!({ false => [1, 2] }).unwrap()), case(map! {false => map! {1=>2,3=>4}}, cbor!({ false => {1=>2,3=>4} }).unwrap()), case(map! {"foo" => Null}, cbor!({ "foo" => null }).unwrap()), case(map! {"foo" => true}, cbor!({ "foo" => true }).unwrap()), case(map! {"foo" => false}, cbor!({ "foo" => false }).unwrap()), case(map! {"foo" => "foo"}, cbor!({ "foo" => "foo" }).unwrap()), case(map! {"foo" => 123}, cbor!({ "foo" => 123 }).unwrap()), case(map! {"foo" => -123}, cbor!({ "foo" => -123 }).unwrap()), case(map! {"foo" => 1.23}, cbor!({ "foo" => 1.23 }).unwrap()), case(map! {"foo" => -1.23}, cbor!({ "foo" => -1.23 }).unwrap()), case(map! {"foo" => 2.5e+1}, cbor!({ "foo" => 2.5e+1 }).unwrap()), case(map! {"foo" => 2.5e+1}, cbor!({ "foo" => 2.5e+1 }).unwrap()), case(map! {"foo" => [1, 2]}, cbor!({ "foo" => [1, 2] }).unwrap()), case(map! {"foo" => map! {1=>2,3=>4}}, cbor!({ "foo" => {1=>2,3=>4} }).unwrap()), case(map! {123 => Null}, cbor!({ 123 => null }).unwrap()), case(map! {123 => true}, cbor!({ 123 => true }).unwrap()), case(map! {123 => false}, cbor!({ 123 => false }).unwrap()), case(map! {123 => "foo"}, cbor!({ 123 => "foo" }).unwrap()), case(map! {123 => 123}, cbor!({ 123 => 123 }).unwrap()), case(map! {123 => -123}, cbor!({ 123 => -123 }).unwrap()), case(map! {123 => 1.23}, cbor!({ 123 => 1.23 }).unwrap()), case(map! {123 => -1.23}, cbor!({ 123 => -1.23 }).unwrap()), case(map! {123 => 2.5e+1}, cbor!({ 123 => 2.5e+1 }).unwrap()), case(map! {123 => 2.5e+1}, cbor!({ 123 => 2.5e+1 }).unwrap()), case(map! {123 => [1, 2]}, cbor!({ 123 => [1, 2] }).unwrap()), case(map! {123 => map! {1=>2,3=>4}}, cbor!({ 123 => {1=>2,3=>4} }).unwrap()), case(map! {-123 => Null}, cbor!({ -123 => null }).unwrap()), case(map! {-123 => true}, cbor!({ -123 => true }).unwrap()), case(map! {-123 => false}, cbor!({ -123 => false }).unwrap()), case(map! {-123 => "foo"}, cbor!({ -123 => "foo" }).unwrap()), case(map! {-123 => 123}, cbor!({ -123 => 123 }).unwrap()), case(map! {-123 => -123}, cbor!({ -123 => -123 }).unwrap()), case(map! {-123 => 1.23}, cbor!({ -123 => 1.23 }).unwrap()), case(map! {-123 => -1.23}, cbor!({ -123 => -1.23 }).unwrap()), case(map! {-123 => 2.5e+1}, cbor!({ -123 => 2.5e+1 }).unwrap()), case(map! {-123 => 2.5e+1}, cbor!({ -123 => 2.5e+1 }).unwrap()), case(map! {-123 => [1, 2]}, cbor!({ -123 => [1, 2] }).unwrap()), case(map! {-123 => map! {1=>2,3=>4}}, cbor!({ -123 => {1=>2,3=>4} }).unwrap()), case(map! {1.23 => Null}, cbor!({ 1.23 => null }).unwrap()), case(map! {1.23 => true}, cbor!({ 1.23 => true }).unwrap()), case(map! {1.23 => false}, cbor!({ 1.23 => false }).unwrap()), case(map! {1.23 => "foo"}, cbor!({ 1.23 => "foo" }).unwrap()), case(map! {1.23 => 123}, cbor!({ 1.23 => 123 }).unwrap()), case(map! {1.23 => -123}, cbor!({ 1.23 => -123 }).unwrap()), case(map! {1.23 => 1.23}, cbor!({ 1.23 => 1.23 }).unwrap()), case(map! {1.23 => -1.23}, cbor!({ 1.23 => -1.23 }).unwrap()), case(map! {1.23 => 2.5e+1}, cbor!({ 1.23 => 2.5e+1 }).unwrap()), case(map! {1.23 => 2.5e+1}, cbor!({ 1.23 => 2.5e+1 }).unwrap()), case(map! {1.23 => [1, 2]}, cbor!({ 1.23 => [1, 2] }).unwrap()), case(map! {1.23 => map! {1=>2,3=>4}}, cbor!({ 1.23 => {1=>2,3=>4} }).unwrap()), case(map! {-1.23 => Null}, cbor!({ -1.23 => null }).unwrap()), case(map! {-1.23 => true}, cbor!({ -1.23 => true }).unwrap()), case(map! {-1.23 => false}, cbor!({ -1.23 => false }).unwrap()), case(map! {-1.23 => "foo"}, cbor!({ -1.23 => "foo" }).unwrap()), case(map! {-1.23 => 123}, cbor!({ -1.23 => 123 }).unwrap()), case(map! {-1.23 => -123}, cbor!({ -1.23 => -123 }).unwrap()), case(map! {-1.23 => 1.23}, cbor!({ -1.23 => 1.23 }).unwrap()), case(map! {-1.23 => -1.23}, cbor!({ -1.23 => -1.23 }).unwrap()), case(map! {-1.23 => 2.5e+1}, cbor!({ -1.23 => 2.5e+1 }).unwrap()), case(map! {-1.23 => 2.5e+1}, cbor!({ -1.23 => 2.5e+1 }).unwrap()), case(map! {-1.23 => [1, 2]}, cbor!({ -1.23 => [1, 2] }).unwrap()), case(map! {-1.23 => map! {1=>2,3=>4}}, cbor!({ -1.23 => {1=>2,3=>4} }).unwrap()), case(map! {2.5e+1 => Null}, cbor!({ 2.5e+1 => null }).unwrap()), case(map! {2.5e+1 => true}, cbor!({ 2.5e+1 => true }).unwrap()), case(map! {2.5e+1 => false}, cbor!({ 2.5e+1 => false }).unwrap()), case(map! {2.5e+1 => "foo"}, cbor!({ 2.5e+1 => "foo" }).unwrap()), case(map! {2.5e+1 => 123}, cbor!({ 2.5e+1 => 123 }).unwrap()), case(map! {2.5e+1 => -123}, cbor!({ 2.5e+1 => -123 }).unwrap()), case(map! {2.5e+1 => 1.23}, cbor!({ 2.5e+1 => 1.23 }).unwrap()), case(map! {2.5e+1 => -1.23}, cbor!({ 2.5e+1 => -1.23 }).unwrap()), case(map! {2.5e+1 => 2.5e+1}, cbor!({ 2.5e+1 => 2.5e+1 }).unwrap()), case(map! {2.5e+1 => 2.5e+1}, cbor!({ 2.5e+1 => 2.5e+1 }).unwrap()), case(map! {2.5e+1 => [1, 2]}, cbor!({ 2.5e+1 => [1, 2] }).unwrap()), case(map! {2.5e+1 => map! {1=>2,3=>4}}, cbor!({ 2.5e+1 => {1=>2,3=>4} }).unwrap()), case(map! {2.5e+1 => Null}, cbor!({ 2.5e+1 => null }).unwrap()), case(map! {2.5e+1 => true}, cbor!({ 2.5e+1 => true }).unwrap()), case(map! {2.5e+1 => false}, cbor!({ 2.5e+1 => false }).unwrap()), case(map! {2.5e+1 => "foo"}, cbor!({ 2.5e+1 => "foo" }).unwrap()), case(map! {2.5e+1 => 123}, cbor!({ 2.5e+1 => 123 }).unwrap()), case(map! {2.5e+1 => -123}, cbor!({ 2.5e+1 => -123 }).unwrap()), case(map! {2.5e+1 => 1.23}, cbor!({ 2.5e+1 => 1.23 }).unwrap()), case(map! {2.5e+1 => -1.23}, cbor!({ 2.5e+1 => -1.23 }).unwrap()), case(map! {2.5e+1 => 2.5e+1}, cbor!({ 2.5e+1 => 2.5e+1 }).unwrap()), case(map! {2.5e+1 => 2.5e+1}, cbor!({ 2.5e+1 => 2.5e+1 }).unwrap()), case(map! {2.5e+1 => [1, 2]}, cbor!({ 2.5e+1 => [1, 2] }).unwrap()), case(map! {2.5e+1 => map! {1=>2,3=>4}}, cbor!({ 2.5e+1 => {1=>2,3=>4} }).unwrap()), case(map! {[1, 2] => Null}, cbor!({ [1, 2] => null }).unwrap()), case(map! {[1, 2] => true}, cbor!({ [1, 2] => true }).unwrap()), case(map! {[1, 2] => false}, cbor!({ [1, 2] => false }).unwrap()), case(map! {[1, 2] => "foo"}, cbor!({ [1, 2] => "foo" }).unwrap()), case(map! {[1, 2] => 123}, cbor!({ [1, 2] => 123 }).unwrap()), case(map! {[1, 2] => -123}, cbor!({ [1, 2] => -123 }).unwrap()), case(map! {[1, 2] => 1.23}, cbor!({ [1, 2] => 1.23 }).unwrap()), case(map! {[1, 2] => -1.23}, cbor!({ [1, 2] => -1.23 }).unwrap()), case(map! {[1, 2] => 2.5e+1}, cbor!({ [1, 2] => 2.5e+1 }).unwrap()), case(map! {[1, 2] => 2.5e+1}, cbor!({ [1, 2] => 2.5e+1 }).unwrap()), case(map! {[1, 2] => [1, 2]}, cbor!({ [1, 2] => [1, 2] }).unwrap()), case(map! {[1, 2] => map! {1=>2,3=>4}}, cbor!({ [1, 2] => {1=>2,3=>4} }).unwrap()), case(map! {map! {1=>2,3=>4} => Null}, cbor!({ {1=>2,3=>4} => null }).unwrap()), case(map! {map! {1=>2,3=>4} => true}, cbor!({ {1=>2,3=>4} => true }).unwrap()), case(map! {map! {1=>2,3=>4} => false}, cbor!({ {1=>2,3=>4} => false }).unwrap()), case(map! {map! {1=>2,3=>4} => "foo"}, cbor!({ {1=>2,3=>4} => "foo" }).unwrap()), case(map! {map! {1=>2,3=>4} => 123}, cbor!({ {1=>2,3=>4} => 123 }).unwrap()), case(map! {map! {1=>2,3=>4} => -123}, cbor!({ {1=>2,3=>4} => -123 }).unwrap()), case(map! {map! {1=>2,3=>4} => 1.23}, cbor!({ {1=>2,3=>4} => 1.23 }).unwrap()), case(map! {map! {1=>2,3=>4} => -1.23}, cbor!({ {1=>2,3=>4} => -1.23 }).unwrap()), case(map! {map! {1=>2,3=>4} => 2.5e+1}, cbor!({ {1=>2,3=>4} => 2.5e+1 }).unwrap()), case(map! {map! {1=>2,3=>4} => 2.5e+1}, cbor!({ {1=>2,3=>4} => 2.5e+1 }).unwrap()), case(map! {map! {1=>2,3=>4} => [1, 2]}, cbor!({ {1=>2,3=>4} => [1, 2] }).unwrap()), case(map! {map! {1=>2,3=>4} => map! {1=>2,3=>4}}, cbor!({ {1=>2,3=>4} => {1=>2,3=>4} }).unwrap()), )] fn test(answer: Value, question: Value) { assert_eq!(answer, question); } ciborium-0.2.0/tests/no_std.rs000064400000000000000000000013210072674642500144570ustar 00000000000000// SPDX-License-Identifier: Apache-2.0 #![cfg(all(feature = "serde", not(feature = "std")))] #![no_std] extern crate alloc; use alloc::vec::Vec; use ciborium::{de::from_reader, ser::into_writer}; #[test] fn decode() { assert_eq!(from_reader::(&[7u8][..]).unwrap(), 7); } #[test] fn eof() { from_reader::(&[]).unwrap_err(); } #[test] fn encode_slice() { let mut buffer = [0u8; 1]; into_writer(&3u8, &mut buffer[..]).unwrap(); assert_eq!(buffer[0], 3); } #[test] fn encode_vec() { let mut buffer = Vec::with_capacity(1); into_writer(&3u8, &mut buffer).unwrap(); assert_eq!(buffer[0], 3); } #[test] fn oos() { into_writer(&3u8, &mut [][..]).unwrap_err(); } ciborium-0.2.0/tests/recursion.rs000064400000000000000000000023310072674642500152040ustar 00000000000000// SPDX-License-Identifier: Apache-2.0 //! This test validates that we don't get stack overflows. //! //! If container types cause recursion, then a long list of prefixes which //! indicate nested container types could cause the stack to overflow. We //! test each of these types here to ensure there is no stack overflow. use ciborium::{ de::{from_reader, Error}, value::Value, }; #[test] fn array() { let bytes = [0x9f; 128 * 1024]; match from_reader::(&bytes[..]).unwrap_err() { Error::RecursionLimitExceeded => (), e => panic!("incorrect error: {:?}", e), } } #[test] fn map() { let bytes = [0xbf; 128 * 1024]; match from_reader::(&bytes[..]).unwrap_err() { Error::RecursionLimitExceeded => (), e => panic!("incorrect error: {:?}", e), } } #[test] fn bytes() { let bytes = [0x5f; 128 * 1024]; match from_reader::(&bytes[..]).unwrap_err() { Error::Io(..) => (), e => panic!("incorrect error: {:?}", e), } } #[test] fn text() { let bytes = [0x7f; 128 * 1024]; match from_reader::(&bytes[..]).unwrap_err() { Error::Io(..) => (), e => panic!("incorrect error: {:?}", e), } } ciborium-0.2.0/tests/tag.rs000064400000000000000000000035420072674642500137530ustar 00000000000000// SPDX-License-Identifier: Apache-2.0 extern crate alloc; use ciborium::{de::from_reader, ser::into_writer, tag::*, value::Value}; use rstest::rstest; use serde::{Deserialize, Serialize}; use core::fmt::Debug; #[rstest(item, bytes, value, encode, success, case(Captured(Some(6), true), "c6f5", Value::Tag(6, Value::Bool(true).into()), true, true), case(Captured(None, true), "f5", Value::Bool(true), true, true), case(Required::<_, 6>(true), "c6f5", Value::Tag(6, Value::Bool(true).into()), true, true), case(Required::<_, 6>(true), "c7f5", Value::Tag(7, Value::Bool(true).into()), false, false), case(Required::<_, 6>(true), "f5", Value::Bool(true), false, false), case(Accepted::<_, 6>(true), "c6f5", Value::Tag(6, Value::Bool(true).into()), true, true), case(Accepted::<_, 6>(true), "c7f5", Value::Tag(7, Value::Bool(true).into()), false, false), case(Accepted::<_, 6>(true), "f5", Value::Bool(true), false, true), )] fn test<'de, T: Serialize + Deserialize<'de> + Debug + Eq>( item: T, bytes: &str, value: Value, encode: bool, success: bool, ) { let bytes = hex::decode(bytes).unwrap(); if encode { // Encode into bytes let mut encoded = Vec::new(); into_writer(&item, &mut encoded).unwrap(); assert_eq!(bytes, encoded); // Encode into value assert_eq!(value, Value::serialized(&item).unwrap()); } // Decode from bytes match from_reader(&bytes[..]) { Ok(x) if success => assert_eq!(item, x), Ok(..) => panic!("unexpected success"), Err(e) if success => Err(e).unwrap(), Err(..) => (), } // Decode from value match value.deserialized() { Ok(x) if success => assert_eq!(item, x), Ok(..) => panic!("unexpected success"), Err(e) if success => Err(e).unwrap(), Err(..) => (), } }