use alloc::vec::Vec;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Value {
Uint(u64),
Bytes(Vec<u8>),
Text(Vec<u8>),
Array(Vec<Value>),
IntMap(Vec<(u64, Value)>),
}
impl Value {
pub fn encode(&self, out: &mut Vec<u8>) {
match self {
Value::Uint(n) => encode_head(out, 0, *n),
Value::Bytes(b) => {
encode_head(out, 2, b.len() as u64);
out.extend_from_slice(b);
}
Value::Text(t) => {
encode_head(out, 3, t.len() as u64);
out.extend_from_slice(t);
}
Value::Array(items) => {
encode_head(out, 4, items.len() as u64);
for it in items {
it.encode(out);
}
}
Value::IntMap(entries) => {
let mut sorted: Vec<&(u64, Value)> = entries.iter().collect();
sorted.sort_by_key(|(k, _)| *k);
debug_assert!(
sorted.windows(2).all(|w| w[0].0 != w[1].0),
"IntMap has duplicate keys; CTAP2 canonical CBOR forbids duplicate map keys"
);
encode_head(out, 5, sorted.len() as u64);
for (k, v) in sorted {
encode_head(out, 0, *k); v.encode(out);
}
}
}
}
pub fn to_vec(&self) -> Vec<u8> {
let mut out = Vec::new();
self.encode(&mut out);
out
}
}
pub fn int_map(pairs: impl IntoIterator<Item = (u64, Option<Value>)>) -> Value {
Value::IntMap(
pairs
.into_iter()
.filter_map(|(k, v)| v.map(|v| (k, v)))
.collect(),
)
}
fn encode_head(out: &mut Vec<u8>, major: u8, n: u64) {
let mt = major << 5;
if n < 24 {
out.push(mt | (n as u8));
} else if n <= u8::MAX as u64 {
out.push(mt | 24);
out.push(n as u8);
} else if n <= u16::MAX as u64 {
out.push(mt | 25);
out.extend_from_slice(&(n as u16).to_be_bytes());
} else if n <= u32::MAX as u64 {
out.push(mt | 26);
out.extend_from_slice(&(n as u32).to_be_bytes());
} else {
out.push(mt | 27);
out.extend_from_slice(&n.to_be_bytes());
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn uint_minimal_encoding() {
assert_eq!(Value::Uint(0).to_vec(), vec![0x00]);
assert_eq!(Value::Uint(23).to_vec(), vec![0x17]);
assert_eq!(Value::Uint(24).to_vec(), vec![0x18, 24]);
assert_eq!(Value::Uint(255).to_vec(), vec![0x18, 0xff]);
assert_eq!(Value::Uint(256).to_vec(), vec![0x19, 0x01, 0x00]);
assert_eq!(
Value::Uint(65536).to_vec(),
vec![0x1a, 0x00, 0x01, 0x00, 0x00]
);
}
#[test]
fn bytes_encoding() {
assert_eq!(Value::Bytes(vec![1, 2, 3]).to_vec(), vec![0x43, 1, 2, 3]);
assert_eq!(Value::Bytes(vec![]).to_vec(), vec![0x40]);
}
#[test]
fn int_map_sorts_keys_ascending() {
let m = Value::IntMap(vec![
(3, Value::Uint(30)),
(1, Value::Uint(10)),
(2, Value::Uint(20)),
]);
assert_eq!(
m.to_vec(),
vec![0xa3, 0x01, 0x0a, 0x02, 0x14, 0x03, 0x18, 30]
);
}
#[test]
fn int_map_omitempty_drops_none() {
let m = int_map([
(1, Some(Value::Uint(1))),
(2, None),
(3, Some(Value::Uint(3))),
]);
assert_eq!(m.to_vec(), vec![0xa2, 0x01, 0x01, 0x03, 0x03]);
}
}