diff options
Diffstat (limited to 'vendor/ciborium/src/value/canonical.rs')
-rw-r--r-- | vendor/ciborium/src/value/canonical.rs | 124 |
1 files changed, 124 insertions, 0 deletions
diff --git a/vendor/ciborium/src/value/canonical.rs b/vendor/ciborium/src/value/canonical.rs new file mode 100644 index 000000000..f1196f4d0 --- /dev/null +++ b/vendor/ciborium/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)) + } +} |