Revision control
Copy as Markdown
Other Tools
//! CBOR values, keys and serialization routines.
mod de;
mod ser;
use std::cmp::{Ord, Ordering, PartialOrd};
use std::collections::BTreeMap;
#[doc(inline)]
pub use self::de::from_value;
#[doc(inline)]
pub use self::ser::to_value;
/// The `Value` enum, a loosely typed way of representing any valid CBOR value.
///
/// Maps are sorted according to the canonical ordering
/// described in [RFC 7049 bis].
/// Therefore values are unambiguously serialized
/// to a canonical form of CBOR from the same RFC.
///
#[derive(Clone, Debug)]
pub enum Value {
/// Represents the absence of a value or the value undefined.
Null,
/// Represents a boolean value.
Bool(bool),
/// Integer CBOR numbers.
///
/// The biggest value that can be represented is 2^64 - 1.
/// While the smallest value is -2^64.
/// Values outside this range can't be serialized
/// and will cause an error.
Integer(i128),
/// Represents a floating point value.
Float(f64),
/// Represents a byte string.
Bytes(Vec<u8>),
/// Represents an UTF-8 encoded string.
Text(String),
/// Represents an array of values.
Array(Vec<Value>),
/// Represents a map.
///
/// Maps are also called tables, dictionaries, hashes, or objects (in JSON).
/// While any value can be used as a CBOR key
/// it is better to use only one type of key in a map
/// to avoid ambiguity.
/// If floating point values are used as keys they are compared bit-by-bit for equality.
/// If arrays or maps are used as keys the comparisons
/// to establish canonical order may be slow and therefore insertion
/// and retrieval of values will be slow too.
Map(BTreeMap<Value, Value>),
/// Represents a tagged value
Tag(u64, Box<Value>),
// The hidden variant allows the enum to be extended
// with variants for tags and simple values.
#[doc(hidden)]
__Hidden,
}
impl PartialEq for Value {
fn eq(&self, other: &Value) -> bool {
self.cmp(other) == Ordering::Equal
}
}
impl Eq for Value {}
impl PartialOrd for Value {
fn partial_cmp(&self, other: &Value) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Value {
fn cmp(&self, other: &Value) -> Ordering {
// Determine the canonical order of two values:
// 1. Smaller major type sorts first.
// 2. Shorter sequence sorts first.
// 3. Compare integers by magnitude.
// 4. Compare byte and text sequences lexically.
// 5. Compare the serializations of both types. (expensive)
use self::Value::*;
if self.major_type() != other.major_type() {
return self.major_type().cmp(&other.major_type());
}
match (self, other) {
(Integer(a), Integer(b)) => a.abs().cmp(&b.abs()),
(Bytes(a), Bytes(b)) if a.len() != b.len() => a.len().cmp(&b.len()),
(Text(a), Text(b)) if a.len() != b.len() => a.len().cmp(&b.len()),
(Array(a), Array(b)) if a.len() != b.len() => a.len().cmp(&b.len()),
(Map(a), Map(b)) if a.len() != b.len() => a.len().cmp(&b.len()),
(Bytes(a), Bytes(b)) => a.cmp(b),
(Text(a), Text(b)) => a.cmp(b),
(a, b) => {
let a = crate::to_vec(a).expect("self is serializable");
let b = crate::to_vec(b).expect("other is serializable");
a.cmp(&b)
}
}
}
}
macro_rules! impl_from {
($variant:path, $for_type:ty) => {
impl From<$for_type> for Value {
fn from(v: $for_type) -> Value {
$variant(v.into())
}
}
};
}
impl_from!(Value::Bool, bool);
impl_from!(Value::Integer, i8);
impl_from!(Value::Integer, i16);
impl_from!(Value::Integer, i32);
impl_from!(Value::Integer, i64);
// i128 omitted because not all numbers fit in CBOR serialization
impl_from!(Value::Integer, u8);
impl_from!(Value::Integer, u16);
impl_from!(Value::Integer, u32);
impl_from!(Value::Integer, u64);
// u128 omitted because not all numbers fit in CBOR serialization
impl_from!(Value::Float, f32);
impl_from!(Value::Float, f64);
impl_from!(Value::Bytes, Vec<u8>);
impl_from!(Value::Text, String);
// TODO: figure out if these impls should be more generic or removed.
impl_from!(Value::Array, Vec<Value>);
impl_from!(Value::Map, BTreeMap<Value, Value>);
impl Value {
fn major_type(&self) -> u8 {
use self::Value::*;
match self {
Null => 7,
Bool(_) => 7,
Integer(v) => {
if *v >= 0 {
0
} else {
1
}
}
Tag(_, _) => 6,
Float(_) => 7,
Bytes(_) => 2,
Text(_) => 3,
Array(_) => 4,
Map(_) => 5,
__Hidden => unreachable!(),
}
}
}