diff options
author | Andrei Homescu <ahomescu@google.com> | 2023-10-17 05:18:36 +0000 |
---|---|---|
committer | Andrei Homescu <ahomescu@google.com> | 2023-10-17 05:18:36 +0000 |
commit | 02178b638951eeedf32f57cef31180a87f7cb448 (patch) | |
tree | e1ef9eee9dc3ce7c25b77e4420438e97fc6d777c | |
parent | 223e2a0e7fc00c1d6ff5e1a7cdc18f34868865dd (diff) | |
parent | 1377d4e18e9e36adc0c561944f9429dbc4b3b8a4 (diff) | |
download | ciborium-trusty-main.tar.gz |
Merge remote-tracking branch 'aosp/main' into trusty-maintrusty-main
Change-Id: I3929bf4d6c6364712b1ae1ba318c8f8b7230cb2d
-rw-r--r-- | .cargo_vcs_info.json | 5 | ||||
-rw-r--r-- | Android.bp | 39 | ||||
-rw-r--r-- | Cargo.toml | 9 | ||||
-rw-r--r-- | Cargo.toml.orig | 10 | ||||
-rw-r--r-- | METADATA | 19 | ||||
-rw-r--r-- | OWNERS | 2 | ||||
-rw-r--r-- | README.md | 6 | ||||
-rw-r--r-- | TEST_MAPPING | 13 | ||||
-rw-r--r-- | cargo2android.json | 18 | ||||
-rw-r--r-- | src/de/mod.rs | 127 | ||||
-rw-r--r-- | src/lib.rs | 16 | ||||
-rw-r--r-- | src/ser/mod.rs | 3 | ||||
-rw-r--r-- | src/tag.rs | 14 | ||||
-rw-r--r-- | src/value/canonical.rs | 124 | ||||
-rw-r--r-- | src/value/de.rs | 6 | ||||
-rw-r--r-- | src/value/integer.rs | 59 | ||||
-rw-r--r-- | src/value/mod.rs | 213 | ||||
-rw-r--r-- | src/value/ser.rs | 14 | ||||
-rw-r--r-- | tests/canonical.rs | 111 | ||||
-rw-r--r-- | tests/codec.rs | 38 | ||||
-rw-r--r-- | tests/error.rs | 14 | ||||
-rw-r--r-- | tests/recursion.rs | 46 | ||||
-rw-r--r-- | tests/tag.rs | 4 |
23 files changed, 814 insertions, 96 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json new file mode 100644 index 0000000..4212810 --- /dev/null +++ b/.cargo_vcs_info.json @@ -0,0 +1,5 @@ +{ + "git": { + "sha1": "7d8f6e499db51fe52f5d3c2ce1d0e0be61c7eaa2" + } +} @@ -1,8 +1,6 @@ -// This file is generated by cargo2android.py --run --device --dependencies. +// This file is generated by cargo2android.py --config cargo2android.json. // Do not modify this file as changes will be overridden on upgrade. - - package { default_applicable_licenses: ["external_rust_crates_ciborium_license"], } @@ -25,7 +23,7 @@ rust_library { host_supported: true, crate_name: "ciborium", cargo_env_compat: true, - cargo_pkg_version: "0.2.0", + cargo_pkg_version: "0.2.1", srcs: ["src/lib.rs"], edition: "2021", features: [ @@ -37,4 +35,37 @@ rust_library { "libciborium_ll", "libserde", ], + apex_available: [ + "//apex_available:platform", + "//apex_available:anyapex", + ], + product_available: true, + vendor_available: true, +} + +rust_library_rlib { + name: "libciborium_nostd", + crate_name: "ciborium", + cargo_env_compat: true, + cargo_pkg_version: "0.2.1", + srcs: ["src/lib.rs"], + edition: "2021", + rustlibs: [ + "libciborium_io_nostd", + "libciborium_ll_nostd", + "libserde_nostd", + ], + apex_available: [ + "//apex_available:platform", + "//apex_available:anyapex", + ], + prefer_rlib: true, + no_stdlibs: true, + stdlibs: [ + "liballoc.rust_sysroot", + "libcompiler_builtins.rust_sysroot", + "libcore.rust_sysroot", + ], + product_available: true, + vendor_available: true, } @@ -11,8 +11,9 @@ [package] edition = "2021" +rust-version = "1.56" name = "ciborium" -version = "0.2.0" +version = "0.2.1" authors = ["Nathaniel McCallum <npmccallum@profian.com>"] description = "serde implementation of CBOR using ciborium-basic" homepage = "https://github.com/enarx/ciborium" @@ -21,12 +22,14 @@ keywords = ["cbor", "serde"] categories = ["data-structures", "embedded", "encoding", "no-std", "parsing"] license = "Apache-2.0" repository = "https://github.com/enarx/ciborium" +[package.metadata.docs.rs] +all-features = true [dependencies.ciborium-io] -version = "0.2.0" +version = "0.2.1" features = ["alloc"] [dependencies.ciborium-ll] -version = "0.2.0" +version = "0.2.1" [dependencies.serde] version = "1.0" diff --git a/Cargo.toml.orig b/Cargo.toml.orig index 9e575ab..d1e14d3 100644 --- a/Cargo.toml.orig +++ b/Cargo.toml.orig @@ -1,9 +1,10 @@ [package] name = "ciborium" -version = "0.2.0" +version = "0.2.1" authors = ["Nathaniel McCallum <npmccallum@profian.com>"] license = "Apache-2.0" edition = "2021" +rust-version = "1.56" homepage = "https://github.com/enarx/ciborium" repository = "https://github.com/enarx/ciborium" description = "serde implementation of CBOR using ciborium-basic" @@ -20,8 +21,8 @@ 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"] } +ciborium-ll = { path = "../ciborium-ll", version = "0.2.1" } +ciborium-io = { path = "../ciborium-io", version = "0.2.1", features = ["alloc"] } serde = { version = "1.0", default-features = false, features = ["alloc", "derive"] } [dev-dependencies] @@ -33,3 +34,6 @@ hex = "0.4" [features] default = ["std"] std = ["ciborium-io/std", "serde/std"] + +[package.metadata.docs.rs] +all-features = true @@ -1,7 +1,9 @@ -name: "ciborium" -description: - "serde implementation of CBOR using ciborium-basic" +# This project was upgraded with external_updater. +# Usage: tools/external_updater/updater.sh update rust/crates/ciborium +# For more info, check https://cs.android.com/android/platform/superproject/+/master:tools/external_updater/README.md +name: "ciborium" +description: "serde implementation of CBOR using ciborium-basic" third_party { url { type: HOMEPAGE @@ -9,10 +11,13 @@ third_party { } url { type: ARCHIVE - value: "https://static.crates.io/crates/ciborium/ciborium-0.2.0.crate" + value: "https://static.crates.io/crates/ciborium/ciborium-0.2.1.crate" } - version: "0.2.0" - last_upgrade_date { year: 2022 month: 1 day: 17 } + version: "0.2.1" license_type: NOTICE + last_upgrade_date { + year: 2023 + month: 6 + day: 14 + } } - @@ -1,4 +1,4 @@ -include platform/prebuilts/rust:master:/OWNERS +include platform/prebuilts/rust:main:/OWNERS # Android Hardware Security asbel@google.com jbires@google.com @@ -11,12 +11,12 @@ Ciborium contains CBOR serialization and deserialization implementations for ser ## Quick Start -You're probably looking for [`de::from_reader()`](crate::de::from_reader) -and [`ser::into_writer()`](crate::ser::into_writer), which are +You're probably looking for [`from_reader()`](crate::de::from_reader) +and [`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). +For dynamic CBOR value creation/inspection, see [`Value`](crate::value::Value). ## Design Decisions diff --git a/TEST_MAPPING b/TEST_MAPPING index 2f7de78..2903e5f 100644 --- a/TEST_MAPPING +++ b/TEST_MAPPING @@ -1,13 +1,14 @@ // Generated by update_crate_tests.py for tests that depend on this crate. { - "presubmit": [ + "imports": [ { - "name": "libcert_request_validator_tests" - } - ], - "presubmit-rust": [ + "path": "external/rust/crates/coset" + }, + { + "path": "system/keymint/derive" + }, { - "name": "libcert_request_validator_tests" + "path": "system/keymint/hal" } ] } diff --git a/cargo2android.json b/cargo2android.json new file mode 100644 index 0000000..adac568 --- /dev/null +++ b/cargo2android.json @@ -0,0 +1,18 @@ +{ + "device": true, + "run": true, + "dependencies": true, + "variants": [ + { + }, + { + "alloc": true, + "dependency_suffix": "_nostd", + "features": "", + "force-rlib": true, + "no-host": true, + "suffix": "_nostd", + "no-std": true + } + ] +} diff --git a/src/de/mod.rs b/src/de/mod.rs index 63e5109..b68458d 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -51,7 +51,7 @@ struct Deserializer<'b, R: Read> { recurse: usize, } -impl<'de, 'a, 'b, R: Read> Deserializer<'b, R> +impl<'a, R: Read> Deserializer<'a, R> where R::Error: core::fmt::Debug, { @@ -348,7 +348,7 @@ where let mut segments = self.decoder.text(len); while let Some(mut segment) = segments.pull()? { - while let Some(chunk) = segment.pull(&mut self.scratch)? { + while let Some(chunk) = segment.pull(self.scratch)? { buffer.push_str(chunk); } } @@ -371,6 +371,11 @@ where visitor.visit_bytes(&self.scratch[..len]) } + Header::Array(len) => self.recurse(|me| { + let access = Access(me, len); + visitor.visit_seq(access) + }), + header => Err(header.expected("bytes")), }; } @@ -389,7 +394,7 @@ where let mut segments = self.decoder.bytes(len); while let Some(mut segment) = segments.pull()? { - while let Some(chunk) = segment.pull(&mut self.scratch)? { + while let Some(chunk) = segment.pull(self.scratch)? { buffer.extend_from_slice(chunk); } } @@ -397,7 +402,12 @@ where visitor.visit_byte_buf(buffer) } - header => Err(header.expected("expected byte buffer")), + Header::Array(len) => self.recurse(|me| { + let access = Access(me, len); + visitor.visit_seq(access) + }), + + header => Err(header.expected("byte buffer")), }; } } @@ -412,6 +422,19 @@ where visitor.visit_seq(access) }), + 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(self.scratch)? { + buffer.extend_from_slice(chunk); + } + } + + visitor.visit_seq(BytesAccess::<R>(0, buffer, core::marker::PhantomData)) + } + header => Err(header.expected("array")), }; } @@ -462,7 +485,28 @@ where self, visitor: V, ) -> Result<V::Value, Self::Error> { - self.deserialize_str(visitor) + 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::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("str or bytes")), + }; + } } fn deserialize_ignored_any<V: de::Visitor<'de>>( @@ -474,16 +518,13 @@ where #[inline] fn deserialize_option<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> { - 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) - } - }; + match self.decoder.pull()? { + Header::Simple(simple::UNDEFINED) => visitor.visit_none(), + Header::Simple(simple::NULL) => visitor.visit_none(), + header => { + self.decoder.push(header); + visitor.visit_some(self) + } } } @@ -683,6 +724,36 @@ where } } +struct BytesAccess<R: Read>(usize, Vec<u8>, core::marker::PhantomData<R>); + +impl<'de, R: Read> de::SeqAccess<'de> for BytesAccess<R> +where + R::Error: core::fmt::Debug, +{ + type Error = Error<R::Error>; + + #[inline] + fn next_element_seed<U: de::DeserializeSeed<'de>>( + &mut self, + seed: U, + ) -> Result<Option<U::Value>, Self::Error> { + use de::IntoDeserializer; + + if self.0 < self.1.len() { + let byte = self.1[self.0]; + self.0 += 1; + seed.deserialize(byte.into_deserializer()).map(Some) + } else { + Ok(None) + } + } + + #[inline] + fn size_hint(&self) -> Option<usize> { + Some(self.1.len() - self.0) + } +} + 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> @@ -746,7 +817,7 @@ where /// 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<T, Error<R::Error>> +pub fn from_reader<T: de::DeserializeOwned, R: Read>(reader: R) -> Result<T, Error<R::Error>> where R::Error: core::fmt::Debug, { @@ -760,3 +831,27 @@ where T::deserialize(&mut reader) } + +/// Deserializes as CBOR from a type with [`impl ciborium_io::Read`](ciborium_io::Read), with +/// a specified maximum recursion limit. Inputs that are nested beyond the specified limit +/// will result in [`Error::RecursionLimitExceeded`] . +/// +/// Set a high recursion limit at your own risk (of stack exhaustion)! +#[inline] +pub fn from_reader_with_recursion_limit<T: de::DeserializeOwned, R: Read>( + reader: R, + recurse_limit: usize, +) -> Result<T, Error<R::Error>> +where + R::Error: core::fmt::Debug, +{ + let mut scratch = [0; 4096]; + + let mut reader = Deserializer { + decoder: reader.into(), + scratch: &mut scratch, + recurse: recurse_limit, + }; + + T::deserialize(&mut reader) +} @@ -6,12 +6,12 @@ //! //! # Quick Start //! -//! You're probably looking for [`de::from_reader()`](crate::de::from_reader) -//! and [`ser::into_writer()`](crate::ser::into_writer), which are +//! You're probably looking for [`from_reader()`](crate::de::from_reader) +//! and [`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). +//! For dynamic CBOR value creation/inspection, see [`Value`](crate::value::Value). //! //! # Design Decisions //! @@ -97,6 +97,16 @@ pub mod ser; pub mod tag; pub mod value; +// Re-export the [items recommended by serde](https://serde.rs/conventions.html). +#[doc(inline)] +pub use crate::de::from_reader; + +#[doc(inline)] +pub use crate::ser::into_writer; + +#[doc(inline)] +pub use crate::value::Value; + /// Build a `Value` conveniently. /// /// The syntax should be intuitive if you are familiar with JSON. You can also diff --git a/src/ser/mod.rs b/src/ser/mod.rs index 0488d72..ff3d118 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -495,6 +495,5 @@ where W::Error: core::fmt::Debug, { let mut encoder = Serializer::from(writer); - value.serialize(&mut encoder)?; - Ok(encoder.0.flush()?) + value.serialize(&mut encoder) } @@ -426,7 +426,7 @@ impl ser::Serializer for Serializer { } } -impl<'a> ser::SerializeSeq for Serializer { +impl ser::SerializeSeq for Serializer { type Ok = u64; type Error = Error; @@ -441,7 +441,7 @@ impl<'a> ser::SerializeSeq for Serializer { } } -impl<'a> ser::SerializeTuple for Serializer { +impl ser::SerializeTuple for Serializer { type Ok = u64; type Error = Error; @@ -456,7 +456,7 @@ impl<'a> ser::SerializeTuple for Serializer { } } -impl<'a> ser::SerializeTupleStruct for Serializer { +impl ser::SerializeTupleStruct for Serializer { type Ok = u64; type Error = Error; @@ -471,7 +471,7 @@ impl<'a> ser::SerializeTupleStruct for Serializer { } } -impl<'a> ser::SerializeTupleVariant for Serializer { +impl ser::SerializeTupleVariant for Serializer { type Ok = u64; type Error = Error; @@ -486,7 +486,7 @@ impl<'a> ser::SerializeTupleVariant for Serializer { } } -impl<'a> ser::SerializeMap for Serializer { +impl ser::SerializeMap for Serializer { type Ok = u64; type Error = Error; @@ -506,7 +506,7 @@ impl<'a> ser::SerializeMap for Serializer { } } -impl<'a> ser::SerializeStruct for Serializer { +impl ser::SerializeStruct for Serializer { type Ok = u64; type Error = Error; @@ -525,7 +525,7 @@ impl<'a> ser::SerializeStruct for Serializer { } } -impl<'a> ser::SerializeStructVariant for Serializer { +impl ser::SerializeStructVariant for Serializer { type Ok = u64; type Error = Error; diff --git a/src/value/canonical.rs b/src/value/canonical.rs new file mode 100644 index 0000000..f1196f4 --- /dev/null +++ b/src/value/canonical.rs @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: Apache-2.0 + +use crate::value::Value; +use alloc::vec::Vec; +use core::cmp::Ordering; +use serde::{de, ser}; + +/// Manually serialize values to compare them. +fn serialized_canonical_cmp(v1: &Value, v2: &Value) -> Ordering { + // There is an optimization to be done here, but it would take a lot more code + // and using mixing keys, Arrays or Maps as CanonicalValue is probably not the + // best use of this type as it is meant mainly to be used as keys. + + let mut bytes1 = Vec::new(); + let _ = crate::ser::into_writer(v1, &mut bytes1); + let mut bytes2 = Vec::new(); + let _ = crate::ser::into_writer(v2, &mut bytes2); + + match bytes1.len().cmp(&bytes2.len()) { + Ordering::Equal => bytes1.cmp(&bytes2), + x => x, + } +} + +/// Compares two values uses canonical comparison, as defined in both +/// RFC 7049 Section 3.9 (regarding key sorting) and RFC 8949 4.2.3 (as errata). +/// +/// In short, the comparison follow the following rules: +/// - If two keys have different lengths, the shorter one sorts earlier; +/// - If two keys have the same length, the one with the lower value in +/// (byte-wise) lexical order sorts earlier. +/// +/// This specific comparison allows Maps and sorting that respect these two rules. +pub fn cmp_value(v1: &Value, v2: &Value) -> Ordering { + use Value::*; + + match (v1, v2) { + (Integer(i), Integer(o)) => { + // Because of the first rule above, two numbers might be in a different + // order than regular i128 comparison. For example, 10 < -1 in + // canonical ordering, since 10 serializes to `0x0a` and -1 to `0x20`, + // and -1 < -1000 because of their lengths. + i.canonical_cmp(o) + } + (Text(s), Text(o)) => match s.len().cmp(&o.len()) { + Ordering::Equal => s.cmp(o), + x => x, + }, + (Bool(s), Bool(o)) => s.cmp(o), + (Null, Null) => Ordering::Equal, + (Tag(t, v), Tag(ot, ov)) => match Value::from(*t).partial_cmp(&Value::from(*ot)) { + Some(Ordering::Equal) | None => match v.partial_cmp(ov) { + Some(x) => x, + None => serialized_canonical_cmp(v1, v2), + }, + Some(x) => x, + }, + (_, _) => serialized_canonical_cmp(v1, v2), + } +} + +/// A CBOR Value that impl Ord and Eq to allow sorting of values as defined in both +/// RFC 7049 Section 3.9 (regarding key sorting) and RFC 8949 4.2.3 (as errata). +/// +/// Since a regular [Value] can be +#[derive(Clone, Debug)] +pub struct CanonicalValue(Value); + +impl PartialEq for CanonicalValue { + fn eq(&self, other: &Self) -> bool { + self.cmp(other) == Ordering::Equal + } +} + +impl Eq for CanonicalValue {} + +impl From<Value> for CanonicalValue { + fn from(v: Value) -> Self { + Self(v) + } +} + +impl From<CanonicalValue> for Value { + fn from(v: CanonicalValue) -> Self { + v.0 + } +} + +impl ser::Serialize for CanonicalValue { + fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> + where + S: ser::Serializer, + { + self.0.serialize(serializer) + } +} + +impl<'de> de::Deserialize<'de> for CanonicalValue { + fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> + where + D: de::Deserializer<'de>, + { + Value::deserialize(deserializer).map(Into::into) + } + + fn deserialize_in_place<D>(deserializer: D, place: &mut Self) -> Result<(), D::Error> + where + D: de::Deserializer<'de>, + { + Value::deserialize_in_place(deserializer, &mut place.0) + } +} + +impl Ord for CanonicalValue { + fn cmp(&self, other: &Self) -> Ordering { + cmp_value(&self.0, &other.0) + } +} + +impl PartialOrd for CanonicalValue { + fn partial_cmp(&self, other: &Self) -> Option<Ordering> { + Some(cmp_value(&self.0, &other.0)) + } +} diff --git a/src/value/de.rs b/src/value/de.rs index 0d30a86..6323190 100644 --- a/src/value/de.rs +++ b/src/value/de.rs @@ -173,7 +173,7 @@ impl<'de> de::Deserialize<'de> for Value { struct Deserializer<T>(T); -impl<'a, 'de> Deserializer<&'a Value> { +impl<'a> Deserializer<&'a Value> { fn integer<N>(&self, kind: &'static str) -> Result<N, Error> where N: TryFrom<u128>, @@ -233,7 +233,7 @@ impl<'a, 'de> de::Deserializer<'de> for Deserializer<&'a Value> { Value::Null => visitor.visit_none(), Value::Tag(t, v) => { - let parent: Deserializer<&Value> = Deserializer(&*v); + let parent: Deserializer<&Value> = Deserializer(v); let access = crate::tag::TagAccess::new(parent, Some(*t)); visitor.visit_enum(access) } @@ -487,7 +487,7 @@ impl<'a, 'de> de::Deserializer<'de> for Deserializer<&'a Value> { v => (None, v), }; - let parent: Deserializer<&Value> = Deserializer(&*val); + let parent: Deserializer<&Value> = Deserializer(val); let access = crate::tag::TagAccess::new(parent, tag); return visitor.visit_enum(access); } diff --git a/src/value/integer.rs b/src/value/integer.rs index 4dd1f3a..ef6ea3d 100644 --- a/src/value/integer.rs +++ b/src/value/integer.rs @@ -1,4 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 +use core::cmp::Ordering; macro_rules! implfrom { ($( $(#[$($attr:meta)+])? $t:ident)+) => { @@ -33,6 +34,64 @@ macro_rules! implfrom { #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] pub struct Integer(i128); +impl Integer { + /// Returns the canonical length this integer will have when serialized to bytes. + /// This is called `canonical` as it is only used for canonically comparing two + /// values. It shouldn't be used in any other context. + fn canonical_len(&self) -> usize { + let x = self.0; + + if let Ok(x) = u8::try_from(x) { + if x < 24 { + 1 + } else { + 2 + } + } else if let Ok(x) = i8::try_from(x) { + if x >= -24i8 { + 1 + } else { + 2 + } + } else if u16::try_from(x).is_ok() || i16::try_from(x).is_ok() { + 3 + } else if u32::try_from(x).is_ok() || i32::try_from(x).is_ok() { + 5 + } else if u64::try_from(x).is_ok() || i64::try_from(x).is_ok() { + 9 + } else { + // Ciborium serializes u128/i128 as BigPos if they don't fit in 64 bits. + // In this special case we have to calculate the length. + // The Tag itself will always be 1 byte. + x.to_be_bytes().len() + 1 + } + } + + /// Compare two integers as if we were to serialize them, but more efficiently. + pub fn canonical_cmp(&self, other: &Self) -> Ordering { + match self.canonical_len().cmp(&other.canonical_len()) { + Ordering::Equal => { + // Negative numbers are higher in byte-order than positive numbers. + match (self.0.is_negative(), other.0.is_negative()) { + (false, true) => Ordering::Less, + (true, false) => Ordering::Greater, + (true, true) => { + // For negative numbers the byte order puts numbers closer to 0 which + // are lexically higher, lower. So -1 < -2 when sorting by be_bytes(). + match self.0.cmp(&other.0) { + Ordering::Less => Ordering::Greater, + Ordering::Equal => Ordering::Equal, + Ordering::Greater => Ordering::Less, + } + } + (_, _) => self.0.cmp(&other.0), + } + } + x => x, + } + } +} + implfrom! { u8 u16 u32 u64 i8 i16 i32 i64 diff --git a/src/value/mod.rs b/src/value/mod.rs index 40988b0..7233026 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -2,12 +2,14 @@ //! A dynamic CBOR value +mod canonical; mod integer; mod de; mod error; mod ser; +pub use canonical::CanonicalValue; pub use error::Error; pub use integer::Integer; @@ -49,7 +51,7 @@ impl Value { /// Returns true if the `Value` is an `Integer`. Returns false otherwise. /// /// ``` - /// # use ciborium::value::Value; + /// # use ciborium::Value; /// # /// let value = Value::Integer(17.into()); /// @@ -63,7 +65,7 @@ impl Value { /// Returns None otherwise. /// /// ``` - /// # use ciborium::value::Value; + /// # use ciborium::Value; /// # /// let value = Value::Integer(17.into()); /// @@ -77,10 +79,29 @@ impl Value { } } + /// If the `Value` is a `Integer`, returns a the associated `Integer` data as `Ok`. + /// Returns `Err(Self)` otherwise. + /// + /// ``` + /// # use ciborium::{Value, value::Integer}; + /// # + /// let value = Value::Integer(17.into()); + /// assert_eq!(value.into_integer(), Ok(Integer::from(17))); + /// + /// let value = Value::Bool(true); + /// assert_eq!(value.into_integer(), Err(Value::Bool(true))); + /// ``` + pub fn into_integer(self) -> Result<Integer, Self> { + match self { + Value::Integer(int) => Ok(int), + other => Err(other), + } + } + /// Returns true if the `Value` is a `Bytes`. Returns false otherwise. /// /// ``` - /// # use ciborium::value::Value; + /// # use ciborium::Value; /// # /// let value = Value::Bytes(vec![104, 101, 108, 108, 111]); /// @@ -94,7 +115,7 @@ impl Value { /// Returns None otherwise. /// /// ``` - /// # use ciborium::value::Value; + /// # use ciborium::Value; /// # /// let value = Value::Bytes(vec![104, 101, 108, 108, 111]); /// @@ -111,7 +132,7 @@ impl Value { /// Returns None otherwise. /// /// ``` - /// # use ciborium::value::Value; + /// # use ciborium::Value; /// # /// let mut value = Value::Bytes(vec![104, 101, 108, 108, 111]); /// value.as_bytes_mut().unwrap().clear(); @@ -125,10 +146,29 @@ impl Value { } } + /// If the `Value` is a `Bytes`, returns a the associated `Vec<u8>` data as `Ok`. + /// Returns `Err(Self)` otherwise. + /// + /// ``` + /// # use ciborium::Value; + /// # + /// let value = Value::Bytes(vec![104, 101, 108, 108, 111]); + /// assert_eq!(value.into_bytes(), Ok(vec![104, 101, 108, 108, 111])); + /// + /// let value = Value::Bool(true); + /// assert_eq!(value.into_bytes(), Err(Value::Bool(true))); + /// ``` + pub fn into_bytes(self) -> Result<Vec<u8>, Self> { + match self { + Value::Bytes(vec) => Ok(vec), + other => Err(other), + } + } + /// Returns true if the `Value` is a `Float`. Returns false otherwise. /// /// ``` - /// # use ciborium::value::Value; + /// # use ciborium::Value; /// # /// let value = Value::Float(17.0.into()); /// @@ -142,7 +182,7 @@ impl Value { /// Returns None otherwise. /// /// ``` - /// # use ciborium::value::Value; + /// # use ciborium::Value; /// # /// let value = Value::Float(17.0.into()); /// @@ -156,10 +196,29 @@ impl Value { } } + /// If the `Value` is a `Float`, returns a the associated `f64` data as `Ok`. + /// Returns `Err(Self)` otherwise. + /// + /// ``` + /// # use ciborium::Value; + /// # + /// let value = Value::Float(17.); + /// assert_eq!(value.into_float(), Ok(17.)); + /// + /// let value = Value::Bool(true); + /// assert_eq!(value.into_float(), Err(Value::Bool(true))); + /// ``` + pub fn into_float(self) -> Result<f64, Self> { + match self { + Value::Float(f) => Ok(f), + other => Err(other), + } + } + /// Returns true if the `Value` is a `Text`. Returns false otherwise. /// /// ``` - /// # use ciborium::value::Value; + /// # use ciborium::Value; /// # /// let value = Value::Text(String::from("hello")); /// @@ -173,7 +232,7 @@ impl Value { /// Returns None otherwise. /// /// ``` - /// # use ciborium::value::Value; + /// # use ciborium::Value; /// # /// let value = Value::Text(String::from("hello")); /// @@ -191,7 +250,7 @@ impl Value { /// Returns None otherwise. /// /// ``` - /// # use ciborium::value::Value; + /// # use ciborium::Value; /// # /// let mut value = Value::Text(String::from("hello")); /// value.as_text_mut().unwrap().clear(); @@ -205,10 +264,29 @@ impl Value { } } + /// If the `Value` is a `String`, returns a the associated `String` data as `Ok`. + /// Returns `Err(Self)` otherwise. + /// + /// ``` + /// # use ciborium::Value; + /// # + /// let value = Value::Text(String::from("hello")); + /// assert_eq!(value.into_text().as_deref(), Ok("hello")); + /// + /// let value = Value::Bool(true); + /// assert_eq!(value.into_text(), Err(Value::Bool(true))); + /// ``` + pub fn into_text(self) -> Result<String, Self> { + match self { + Value::Text(s) => Ok(s), + other => Err(other), + } + } + /// Returns true if the `Value` is a `Bool`. Returns false otherwise. /// /// ``` - /// # use ciborium::value::Value; + /// # use ciborium::Value; /// # /// let value = Value::Bool(false); /// @@ -222,7 +300,7 @@ impl Value { /// otherwise. /// /// ``` - /// # use ciborium::value::Value; + /// # use ciborium::Value; /// # /// let value = Value::Bool(false); /// @@ -235,10 +313,29 @@ impl Value { } } + /// If the `Value` is a `Bool`, returns a the associated `bool` data as `Ok`. + /// Returns `Err(Self)` otherwise. + /// + /// ``` + /// # use ciborium::Value; + /// # + /// let value = Value::Bool(false); + /// assert_eq!(value.into_bool(), Ok(false)); + /// + /// let value = Value::Float(17.); + /// assert_eq!(value.into_bool(), Err(Value::Float(17.))); + /// ``` + pub fn into_bool(self) -> Result<bool, Self> { + match self { + Value::Bool(b) => Ok(b), + other => Err(other), + } + } + /// Returns true if the `Value` is a `Null`. Returns false otherwise. /// /// ``` - /// # use ciborium::value::Value; + /// # use ciborium::Value; /// # /// let value = Value::Null; /// @@ -251,7 +348,7 @@ impl Value { /// Returns true if the `Value` is a `Tag`. Returns false otherwise. /// /// ``` - /// # use ciborium::value::Value; + /// # use ciborium::Value; /// # /// let value = Value::Tag(61, Box::from(Value::Null)); /// @@ -265,7 +362,7 @@ impl Value { /// Returns None otherwise. /// /// ``` - /// # use ciborium::value::Value; + /// # use ciborium::Value; /// # /// let value = Value::Tag(61, Box::from(Value::Bytes(vec![104, 101, 108, 108, 111]))); /// @@ -284,7 +381,7 @@ impl Value { /// to the tag `Value`. Returns None otherwise. /// /// ``` - /// # use ciborium::value::Value; + /// # use ciborium::Value; /// # /// let mut value = Value::Tag(61, Box::from(Value::Bytes(vec![104, 101, 108, 108, 111]))); /// @@ -300,10 +397,29 @@ impl Value { } } + /// If the `Value` is a `Tag`, returns a the associated pair of `u64` and `Box<value>` data as `Ok`. + /// Returns `Err(Self)` otherwise. + /// + /// ``` + /// # use ciborium::Value; + /// # + /// let value = Value::Tag(7, Box::new(Value::Float(12.))); + /// assert_eq!(value.into_tag(), Ok((7, Box::new(Value::Float(12.))))); + /// + /// let value = Value::Bool(true); + /// assert_eq!(value.into_tag(), Err(Value::Bool(true))); + /// ``` + pub fn into_tag(self) -> Result<(u64, Box<Value>), Self> { + match self { + Value::Tag(tag, value) => Ok((tag, value)), + other => Err(other), + } + } + /// Returns true if the `Value` is an Array. Returns false otherwise. /// /// ``` - /// # use ciborium::value::Value; + /// # use ciborium::Value; /// # /// let value = Value::Array( /// vec![ @@ -322,7 +438,7 @@ impl Value { /// otherwise. /// /// ``` - /// # use ciborium::value::Value; + /// # use ciborium::Value; /// # /// let value = Value::Array( /// vec![ @@ -336,7 +452,7 @@ impl Value { /// ``` pub fn as_array(&self) -> Option<&Vec<Value>> { match *self { - Value::Array(ref array) => Some(&*array), + Value::Array(ref array) => Some(array), _ => None, } } @@ -345,7 +461,7 @@ impl Value { /// Returns None otherwise. /// /// ``` - /// # use ciborium::value::Value; + /// # use ciborium::Value; /// # /// let mut value = Value::Array( /// vec![ @@ -364,10 +480,34 @@ impl Value { } } + /// If the `Value` is a `Array`, returns a the associated `Vec<Value>` data as `Ok`. + /// Returns `Err(Self)` otherwise. + /// + /// ``` + /// # use ciborium::{Value, value::Integer}; + /// # + /// let mut value = Value::Array( + /// vec![ + /// Value::Integer(17.into()), + /// Value::Float(18.), + /// ] + /// ); + /// assert_eq!(value.into_array(), Ok(vec![Value::Integer(17.into()), Value::Float(18.)])); + /// + /// let value = Value::Bool(true); + /// assert_eq!(value.into_array(), Err(Value::Bool(true))); + /// ``` + pub fn into_array(self) -> Result<Vec<Value>, Self> { + match self { + Value::Array(vec) => Ok(vec), + other => Err(other), + } + } + /// Returns true if the `Value` is a Map. Returns false otherwise. /// /// ``` - /// # use ciborium::value::Value; + /// # use ciborium::Value; /// # /// let value = Value::Map( /// vec![ @@ -385,7 +525,7 @@ impl Value { /// otherwise. /// /// ``` - /// # use ciborium::value::Value; + /// # use ciborium::Value; /// # /// let value = Value::Map( /// vec![ @@ -413,7 +553,7 @@ impl Value { /// Returns None otherwise. /// /// ``` - /// # use ciborium::value::Value; + /// # use ciborium::Value; /// # /// let mut value = Value::Map( /// vec![ @@ -431,6 +571,29 @@ impl Value { _ => None, } } + + /// If the `Value` is a `Map`, returns a the associated `Vec<(Value, Value)>` data as `Ok`. + /// Returns `Err(Self)` otherwise. + /// + /// ``` + /// # use ciborium::Value; + /// # + /// let mut value = Value::Map( + /// vec![ + /// (Value::Text(String::from("key")), Value::Float(18.)), + /// ] + /// ); + /// assert_eq!(value.into_map(), Ok(vec![(Value::Text(String::from("key")), Value::Float(18.))])); + /// + /// let value = Value::Bool(true); + /// assert_eq!(value.into_map(), Err(Value::Bool(true))); + /// ``` + pub fn into_map(self) -> Result<Vec<(Value, Value)>, Self> { + match self { + Value::Map(map) => Ok(map), + other => Err(other), + } + } } macro_rules! implfrom { @@ -483,7 +646,7 @@ impl From<u128> for Value { } let mut bytes = &value.to_be_bytes()[..]; - while let Some(0) = bytes.get(0) { + while let Some(0) = bytes.first() { bytes = &bytes[1..]; } @@ -504,7 +667,7 @@ impl From<i128> for Value { }; let mut bytes = &raw.to_be_bytes()[..]; - while let Some(0) = bytes.get(0) { + while let Some(0) = bytes.first() { bytes = &bytes[1..]; } diff --git a/src/value/ser.rs b/src/value/ser.rs index 6c739d3..a399719 100644 --- a/src/value/ser.rs +++ b/src/value/ser.rs @@ -270,7 +270,7 @@ impl ser::Serializer for Serializer<()> { } } -impl<'a> ser::SerializeSeq for Serializer<Vec<Value>> { +impl ser::SerializeSeq for Serializer<Vec<Value>> { type Ok = Value; type Error = Error; @@ -286,7 +286,7 @@ impl<'a> ser::SerializeSeq for Serializer<Vec<Value>> { } } -impl<'a> ser::SerializeTuple for Serializer<Vec<Value>> { +impl ser::SerializeTuple for Serializer<Vec<Value>> { type Ok = Value; type Error = Error; @@ -302,7 +302,7 @@ impl<'a> ser::SerializeTuple for Serializer<Vec<Value>> { } } -impl<'a> ser::SerializeTupleStruct for Serializer<Vec<Value>> { +impl ser::SerializeTupleStruct for Serializer<Vec<Value>> { type Ok = Value; type Error = Error; @@ -318,7 +318,7 @@ impl<'a> ser::SerializeTupleStruct for Serializer<Vec<Value>> { } } -impl<'a> ser::SerializeTupleVariant for Serializer<Named<Vec<Value>>> { +impl ser::SerializeTupleVariant for Serializer<Named<Vec<Value>>> { type Ok = Value; type Error = Error; @@ -356,7 +356,7 @@ impl<'a> ser::SerializeTupleVariant for Serializer<Named<Vec<Value>>> { } } -impl<'a> ser::SerializeMap for Serializer<Map> { +impl ser::SerializeMap for Serializer<Map> { type Ok = Value; type Error = Error; @@ -381,7 +381,7 @@ impl<'a> ser::SerializeMap for Serializer<Map> { } } -impl<'a> ser::SerializeStruct for Serializer<Vec<(Value, Value)>> { +impl ser::SerializeStruct for Serializer<Vec<(Value, Value)>> { type Ok = Value; type Error = Error; @@ -403,7 +403,7 @@ impl<'a> ser::SerializeStruct for Serializer<Vec<(Value, Value)>> { } } -impl<'a> ser::SerializeStructVariant for Serializer<Named<Vec<(Value, Value)>>> { +impl ser::SerializeStructVariant for Serializer<Named<Vec<(Value, Value)>>> { type Ok = Value; type Error = Error; diff --git a/tests/canonical.rs b/tests/canonical.rs new file mode 100644 index 0000000..d4aa33c --- /dev/null +++ b/tests/canonical.rs @@ -0,0 +1,111 @@ +// SPDX-License-Identifier: Apache-2.0 + +extern crate std; + +use ciborium::cbor; +use ciborium::tag::Required; +use ciborium::value::CanonicalValue; +use rand::prelude::*; +use std::collections::BTreeMap; + +macro_rules! cval { + ($x:expr) => { + CanonicalValue::from(val!($x)) + }; +} + +macro_rules! val { + ($x:expr) => { + cbor!($x).unwrap() + }; +} + +#[test] +fn rfc8949_example() { + let mut array: Vec<CanonicalValue> = vec![ + cval!(10), + cval!(-1), + cval!(false), + cval!(100), + cval!("z"), + cval!([-1]), + cval!("aa"), + cval!([100]), + ]; + let golden = array.clone(); + + // Shuffle the array. + array.shuffle(&mut rand::thread_rng()); + + array.sort(); + + assert_eq!(array, golden); +} + +#[test] +fn map() { + let mut map = BTreeMap::new(); + map.insert(cval!(false), val!(2)); + map.insert(cval!([-1]), val!(5)); + map.insert(cval!(-1), val!(1)); + map.insert(cval!(10), val!(0)); + map.insert(cval!(100), val!(3)); + map.insert(cval!([100]), val!(7)); + map.insert(cval!("z"), val!(4)); + map.insert(cval!("aa"), val!(6)); + + let mut bytes1 = Vec::new(); + ciborium::ser::into_writer(&map, &mut bytes1).unwrap(); + + assert_eq!( + hex::encode(&bytes1), + "a80a002001f402186403617a048120056261610681186407" + ); +} + +#[test] +fn negative_numbers() { + let mut array: Vec<CanonicalValue> = vec![ + cval!(10), + cval!(-1), + cval!(-2), + cval!(-3), + cval!(-4), + cval!(false), + cval!(100), + cval!(-100), + cval!(-200), + cval!("z"), + cval!([-1]), + cval!(-300), + cval!("aa"), + cval!([100]), + ]; + let golden = array.clone(); + + // Shuffle the array. + array.shuffle(&mut rand::thread_rng()); + + array.sort(); + + assert_eq!(array, golden); +} + +#[test] +fn tagged_option() { + let mut opt = Some(Required::<u64, 0xff>(2u32.into())); + + let mut bytes = Vec::new(); + ciborium::ser::into_writer(&opt, &mut bytes).unwrap(); + + let output = ciborium::de::from_reader(&bytes[..]).unwrap(); + assert_eq!(opt, output); + + opt = None; + + let mut bytes = Vec::new(); + ciborium::ser::into_writer(&opt, &mut bytes).unwrap(); + + let output = ciborium::de::from_reader(&bytes[..]).unwrap(); + assert_eq!(opt, output); +} diff --git a/tests/codec.rs b/tests/codec.rs index d1f6026..d5df9a7 100644 --- a/tests/codec.rs +++ b/tests/codec.rs @@ -10,7 +10,7 @@ use ciborium::value::Value; use ciborium::{cbor, de::from_reader, ser::into_writer}; use rstest::rstest; -use serde::{Deserialize, Serialize}; +use serde::{de::DeserializeOwned, Deserialize, Serialize}; macro_rules! val { ($x:expr) => { @@ -275,7 +275,7 @@ macro_rules! map { 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>( +fn codec<'de, T: Serialize + Clone, V: Debug + PartialEq + DeserializeOwned, F: Fn(T) -> V>( input: T, value: Value, bytes: &str, @@ -397,3 +397,37 @@ enum Enum { Tuple(u8, u16), Struct { first: u8, second: u16 }, } + +#[rstest( + input, + case(vec![]), + case(vec![0u8, 1, 2, 3]), +)] +fn byte_vec_serde_bytes_compatibility(input: Vec<u8>) { + use serde_bytes::ByteBuf; + + let mut buf = Vec::new(); + into_writer(&input, &mut buf).unwrap(); + let bytes: ByteBuf = from_reader(&buf[..]).unwrap(); + assert_eq!(input, bytes.to_vec()); + + let mut buf = Vec::new(); + into_writer(&ByteBuf::from(input.clone()), &mut buf).unwrap(); + let bytes: Vec<u8> = from_reader(&buf[..]).unwrap(); + assert_eq!(input, bytes); +} + +#[derive(Serialize, Deserialize, Eq, PartialEq, Debug)] +struct Foo { + bar: u8, +} + +#[rstest(input, expected, + case("a163626172182a", Foo { bar: 42 }), + case("a143626172182a", Foo { bar: 42 }), +)] +fn handle_struct_field_names(input: &str, expected: Foo) { + let buf = hex::decode(input).unwrap(); + let read = from_reader(&buf[..]).unwrap(); + assert_eq!(expected, read); +} diff --git a/tests/error.rs b/tests/error.rs index eb1711f..13bcd5a 100644 --- a/tests/error.rs +++ b/tests/error.rs @@ -1,6 +1,10 @@ // SPDX-License-Identifier: Apache-2.0 -use ciborium::{de::from_reader, de::Error, value::Value}; +use ciborium::{ + de::{from_reader, Error}, + ser::into_writer, + value::Value, +}; use rstest::rstest; #[rstest(bytes, error, @@ -45,3 +49,11 @@ fn test(bytes: &str, error: Error<std::io::Error>) { assert_eq!(correct, actual); } + +#[test] +fn test_long_utf8_deserialization() { + let s = (0..2000).map(|_| 'ボ').collect::<String>(); + let mut v = Vec::new(); + into_writer(&s, &mut v).unwrap(); + let _: String = from_reader(&*v).unwrap(); +} diff --git a/tests/recursion.rs b/tests/recursion.rs index a340b2d..cda1ce2 100644 --- a/tests/recursion.rs +++ b/tests/recursion.rs @@ -7,7 +7,7 @@ //! test each of these types here to ensure there is no stack overflow. use ciborium::{ - de::{from_reader, Error}, + de::{from_reader, from_reader_with_recursion_limit, Error}, value::Value, }; @@ -46,3 +46,47 @@ fn text() { e => panic!("incorrect error: {:?}", e), } } + +#[test] +fn array_limit() { + let bytes = [0x9f; 128 * 1024]; + for limit in 16..256 { + match from_reader_with_recursion_limit::<Value, _>(&bytes[..], limit).unwrap_err() { + Error::RecursionLimitExceeded => (), + e => panic!("incorrect error with limit {}: {:?}", limit, e), + } + // Data that is nested beyond the limit should fail with `RecursionLimitExceeded` + match from_reader_with_recursion_limit::<Value, _>(&bytes[..limit + 1], limit).unwrap_err() + { + Error::RecursionLimitExceeded => (), + e => panic!("incorrect error with limit {}: {:?}", limit, e), + } + // Data that is nested within the limit fails with a different error. + match from_reader_with_recursion_limit::<Value, _>(&bytes[..limit], limit).unwrap_err() { + Error::Io(..) => (), + e => panic!("incorrect error with limit {}: {:?}", limit, e), + } + } +} + +#[test] +fn map_limit() { + let bytes = [0xbf; 128 * 1024]; + for limit in 16..256 { + match from_reader_with_recursion_limit::<Value, _>(&bytes[..], limit).unwrap_err() { + Error::RecursionLimitExceeded => (), + e => panic!("incorrect error with limit {}: {:?}", limit, e), + } + // Data that is nested beyond the limit should fail with `RecursionLimitExceeded` + match from_reader_with_recursion_limit::<Value, _>(&bytes[..limit + 1], limit).unwrap_err() + { + Error::RecursionLimitExceeded => (), + e => panic!("incorrect error with limit {}: {:?}", limit, e), + } + // Data that is nested within the limit fails with a different error. + match from_reader_with_recursion_limit::<Value, _>(&bytes[..limit], limit).unwrap_err() { + Error::Io(..) => (), + e => panic!("incorrect error with limit {}: {:?}", limit, e), + } + } +} diff --git a/tests/tag.rs b/tests/tag.rs index 04b6dad..c19ee00 100644 --- a/tests/tag.rs +++ b/tests/tag.rs @@ -4,7 +4,7 @@ extern crate alloc; use ciborium::{de::from_reader, ser::into_writer, tag::*, value::Value}; use rstest::rstest; -use serde::{Deserialize, Serialize}; +use serde::{de::DeserializeOwned, Serialize}; use core::fmt::Debug; @@ -20,7 +20,7 @@ use core::fmt::Debug; 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>( +fn test<T: Serialize + DeserializeOwned + Debug + Eq>( item: T, bytes: &str, value: Value, |