From aeef21c5a48e38af9972fad72325002b6b1fae6d Mon Sep 17 00:00:00 2001 From: Sebastien Guillemot Date: Thu, 9 Jul 2026 00:32:01 +0900 Subject: [PATCH] Use Vec for Object and IObject, replacing ObjectKey --- src/lib.rs | 2 +- src/value.rs | 132 ++++++++++++++++++-------------------- tests/indefinite_check.rs | 46 +++++++------ 3 files changed, 91 insertions(+), 89 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 038e52f..0ef5781 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -78,7 +78,7 @@ pub use len::*; pub use result::Result; pub use se::Serialize; pub use types::*; -pub use value::{ObjectKey, Value}; +pub use value::Value; const MAX_INLINE_ENCODING: u64 = 23; diff --git a/src/value.rs b/src/value.rs index aa43c11..a354b96 100644 --- a/src/value.rs +++ b/src/value.rs @@ -10,8 +10,6 @@ //! This is why all the objects here are marked as deprecated use alloc::boxed::Box; -use alloc::collections::BTreeMap; -use alloc::format; use alloc::string::String; use alloc::vec::Vec; #[cfg(test)] @@ -22,53 +20,13 @@ use quickcheck::{Arbitrary, Gen}; use core::convert::TryFrom; use de::*; +#[cfg(test)] use error::Error; use len::Len; use result::Result; use se::*; use types::{Special, SpecialValue, Type}; -/// CBOR Object key, represents the possible supported values for -/// a CBOR key in a CBOR Map. -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] -pub enum ObjectKey { - Integer(u64), - Bytes(Vec), - Text(String), -} -impl ObjectKey { - /// convert the given `ObjectKey` into a CBOR [`Value`](./struct.Value.html) - pub fn value(self) -> Value { - match self { - ObjectKey::Integer(v) => Value::U64(v), - ObjectKey::Bytes(v) => Value::Bytes(v), - ObjectKey::Text(v) => Value::Text(v), - } - } -} -impl Serialize for ObjectKey { - fn serialize<'se>(&self, serializer: &'se mut Serializer) -> Result<&'se mut Serializer> { - match self { - ObjectKey::Integer(ref v) => serializer.write_unsigned_integer(*v), - ObjectKey::Bytes(ref v) => serializer.write_bytes(v), - ObjectKey::Text(ref v) => serializer.write_text(v), - } - } -} -impl Deserialize for ObjectKey { - fn deserialize(raw: &mut Deserializer) -> Result { - match raw.cbor_type()? { - Type::UnsignedInteger => Ok(ObjectKey::Integer(raw.unsigned_integer()?)), - Type::Bytes => Ok(ObjectKey::Bytes(raw.bytes()?)), - Type::Text => Ok(ObjectKey::Text(raw.text()?)), - t => Err(Error::CustomError(format!( - "Type `{:?}' is not a support type for CBOR Map's key", - t - ))), - } - } -} - /// All possible CBOR supported values. /// /// We advise not to use these objects as an intermediary representation before @@ -83,8 +41,12 @@ pub enum Value { Text(String), Array(Vec), IArray(Vec), - Object(BTreeMap), - IObject(BTreeMap), + /// maps are key-order-preserving pairs of arbitrary `Value`s: RFC 8949 + /// §3.1 allows any type as a map key, and §5.6 leaves duplicate-key + /// handling to the protocol, so the decoder passes every entry through + /// in wire order rather than deduplicating or sorting + Object(Vec<(Value, Value)>), + IObject(Vec<(Value, Value)>), Tag(u64, Box), /// holds [`SpecialValue`], not [`Special`]: /// Break is a wire-level terminator, not a data item, so a `Value` cannot contain one @@ -117,15 +79,15 @@ impl Serialize for Value { } Value::Object(ref v) => { serializer.write_map(Len::Len(v.len() as u64))?; - for element in v { - serializer.serialize(element.0)?.serialize(element.1)?; + for (key, value) in v { + serializer.serialize(key)?.serialize(value)?; } Ok(serializer) } Value::IObject(ref v) => { serializer.write_map(Len::Indefinite)?; - for element in v { - serializer.serialize(element.0)?.serialize(element.1)?; + for (key, value) in v { + serializer.serialize(key)?.serialize(value)?; } serializer.write_special(Special::Break) } @@ -161,13 +123,13 @@ impl Deserialize for Value { } Type::Map => { let len = raw.map()?; - let mut vec = BTreeMap::new(); + let mut vec = Vec::new(); match len { Len::Indefinite => { while raw.cbor_type()? != Type::Special || !raw.special_break()? { let k = Deserialize::deserialize(raw)?; let v = Deserialize::deserialize(raw)?; - vec.insert(k, v); + vec.push((k, v)); } Ok(Value::IObject(vec)) } @@ -175,7 +137,7 @@ impl Deserialize for Value { for _ in 0..len { let k = Deserialize::deserialize(raw)?; let v = Deserialize::deserialize(raw)?; - vec.insert(k, v); + vec.push((k, v)); } Ok(Value::Object(vec)) } @@ -193,18 +155,6 @@ impl Deserialize for Value { } } -#[cfg(test)] -impl Arbitrary for ObjectKey { - fn arbitrary(g: &mut G) -> Self { - match u8::arbitrary(g) % 3 { - 0 => ObjectKey::Integer(Arbitrary::arbitrary(g)), - 1 => ObjectKey::Bytes(Arbitrary::arbitrary(g)), - 2 => ObjectKey::Text(Arbitrary::arbitrary(g)), - _ => unreachable!(), - } - } -} - // canonical Value integers: non-negative integers are U64 (major type 0), // so generated I64 must be negative or the round-trip property would // compare I64(n) against the U64(n) it decodes to @@ -261,7 +211,7 @@ fn arbitrary_value_indefinite(counter: usize, g: &mut G) -> Value { Value::Object( repeat_with(|| { ( - ObjectKey::arbitrary(g), + arbitrary_value_indefinite(counter - 1, g), arbitrary_value_indefinite(counter - 1, g), ) }) @@ -274,7 +224,7 @@ fn arbitrary_value_indefinite(counter: usize, g: &mut G) -> Value { Value::IObject( repeat_with(|| { ( - ObjectKey::arbitrary(g), + arbitrary_value_indefinite(counter - 1, g), arbitrary_value_indefinite(counter - 1, g), ) }) @@ -345,13 +295,13 @@ mod test { #[test] fn text_invalid_utf8_rejected() { - // regression guard: Value/ObjectKey text decoding must stay strict + // regression guard: Value text decoding must stay strict // even if text_sz() internals change let mut raw = Deserializer::from(vec![0x62, 0xff, 0xfe]); let decoded: Result = Deserialize::deserialize(&mut raw); assert!(matches!(decoded, Err(Error::InvalidTextError(_)))); - // map with an invalid-UTF-8 text key (exercises ObjectKey) + // map with an invalid-UTF-8 text key (exercises key decoding) let mut raw = Deserializer::from(vec![0xa1, 0x62, 0xff, 0xfe, 0x01]); let decoded: Result = Deserialize::deserialize(&mut raw); assert!(matches!(decoded, Err(Error::InvalidTextError(_)))); @@ -403,6 +353,52 @@ mod test { ); } + // RFC 8949 §3.1 allows any type as a map key + #[test] + fn map_with_arbitrary_key_types() { + // a1 20 01 = {-1: 1} (e.g. COSE negative integer labels) + let mut raw = Deserializer::from(vec![0xa1, 0x20, 0x01]); + let decoded: Value = raw.deserialize().unwrap(); + assert_eq!( + decoded, + Value::Object(vec![(Value::I64(-1), Value::U64(1))]) + ); + + // a1 81 01 02 = {[1]: 2}, a container as key + let mut raw = Deserializer::from(vec![0xa1, 0x81, 0x01, 0x02]); + let decoded: Value = raw.deserialize().unwrap(); + assert_eq!( + decoded, + Value::Object(vec![(Value::Array(vec![Value::U64(1)]), Value::U64(2))]) + ); + + assert!(test_encode_decode(&decoded).unwrap()); + } + + // RFC 8949 §5.6: duplicate-key handling belongs to the protocol, so the + // decoder passes all entries through; wire order must survive re-encoding + #[test] + fn map_preserves_duplicate_keys_and_order() { + // a3 02 02 01 01 02 03 = {2: 2, 1: 1, 2: 3} + let mut raw = Deserializer::from(vec![0xa3, 0x02, 0x02, 0x01, 0x01, 0x02, 0x03]); + let decoded: Value = raw.deserialize().unwrap(); + assert_eq!( + decoded, + Value::Object(vec![ + (Value::U64(2), Value::U64(2)), + (Value::U64(1), Value::U64(1)), + (Value::U64(2), Value::U64(3)), + ]) + ); + + let mut se = Serializer::new_vec(); + decoded.serialize(&mut se).unwrap(); + assert_eq!( + se.finalize(), + vec![0xa3, 0x02, 0x02, 0x01, 0x01, 0x02, 0x03] + ); + } + quickcheck! { fn property_encode_decode(value: Value) -> bool { test_encode_decode(&value).unwrap() diff --git a/tests/indefinite_check.rs b/tests/indefinite_check.rs index cfd1307..f732803 100644 --- a/tests/indefinite_check.rs +++ b/tests/indefinite_check.rs @@ -25,15 +25,15 @@ extern crate cbor_event; use cbor_event::de::Deserializer; -use cbor_event::{ObjectKey, SpecialValue, Value}; +use cbor_event::{SpecialValue, Value}; use std::collections::BTreeMap; fn de_value(bytes: &[u8]) -> cbor_event::Result { Deserializer::from(bytes.to_vec()).deserialize::() } -fn text_key(s: &str) -> ObjectKey { - ObjectKey::Text(s.to_owned()) +fn text_key(s: &str) -> Value { + Value::Text(s.to_owned()) } // class 1 (Break as first item): 9fff = [_ ], RFC 8949 @@ -45,10 +45,7 @@ fn value_empty_indefinite_array() { // class 1, map position: bfff = {_ } #[test] fn value_empty_indefinite_map() { - assert_eq!( - de_value(&[0xbf, 0xff]).unwrap(), - Value::IObject(BTreeMap::new()) - ); + assert_eq!(de_value(&[0xbf, 0xff]).unwrap(), Value::IObject(vec![])); } // class 2: 9ff4ff = [_ false] -- used to panic on the Break assertion @@ -63,9 +60,11 @@ fn value_indefinite_array_with_bool() { // classes 2+3 in map position: bf6346756ef563416d7421ff = {_ "Fun": true, "Amt": -2}, RFC 8949 #[test] fn value_indefinite_map_rfc_vector() { - let mut expected = BTreeMap::new(); - expected.insert(text_key("Fun"), Value::Special(SpecialValue::Bool(true))); - expected.insert(text_key("Amt"), Value::I64(-2)); + // entries appear in wire order + let expected = vec![ + (text_key("Fun"), Value::Special(SpecialValue::Bool(true))), + (text_key("Amt"), Value::I64(-2)), + ]; assert_eq!( de_value(&[0xbf, 0x63, 0x46, 0x75, 0x6e, 0xf5, 0x63, 0x41, 0x6d, 0x74, 0x21, 0xff]) .unwrap(), @@ -91,23 +90,30 @@ fn value_nested_mixed_arrays_rfc_vector() { // bf61610161629f0203ffff = {_ "a": 1, "b": [_ 2, 3]}, RFC 8949 #[test] fn value_indefinite_map_nested_rfc_vector() { - let mut expected = BTreeMap::new(); - expected.insert(text_key("a"), Value::U64(1)); - expected.insert( - text_key("b"), - Value::IArray(vec![Value::U64(2), Value::U64(3)]), - ); + let expected = vec![ + (text_key("a"), Value::U64(1)), + ( + text_key("b"), + Value::IArray(vec![Value::U64(2), Value::U64(3)]), + ), + ]; assert_eq!( de_value(&[0xbf, 0x61, 0x61, 0x01, 0x61, 0x62, 0x9f, 0x02, 0x03, 0xff, 0xff]).unwrap(), Value::IObject(expected) ); } -// bff401ff = {_ false: 1} -- valid CBOR, but bool is not representable as an -// ObjectKey in this crate: must be an error, never a panic +// bff401ff = {_ false: 1} -- RFC 8949 §3.1 allows any type as a map key; +// used to error because map keys were the restricted ObjectKey enum #[test] -fn value_indefinite_map_with_special_key_errors() { - assert!(de_value(&[0xbf, 0xf4, 0x01, 0xff]).is_err()); +fn value_indefinite_map_with_special_key() { + assert_eq!( + de_value(&[0xbf, 0xf4, 0x01, 0xff]).unwrap(), + Value::IObject(vec![( + Value::Special(SpecialValue::Bool(false)), + Value::U64(1) + )]) + ); } // class 4: 9ff4 = [_ false