use super::values::{Constants, Value};
use alloc::vec::Vec;
#[derive(Debug, PartialEq)]
pub enum EncoderError {
TooMuchNesting,
DuplicateMapKey,
}
pub fn write(value: Value, encoded_cbor: &mut Vec<u8>) -> Result<(), EncoderError> {
write_nested(value, encoded_cbor, None)
}
pub fn write_nested(
value: Value,
encoded_cbor: &mut Vec<u8>,
max_nest: Option<i8>,
) -> Result<(), EncoderError> {
let mut writer = Writer::new(encoded_cbor);
writer.encode_cbor(value, max_nest)
}
struct Writer<'a> {
encoded_cbor: &'a mut Vec<u8>,
}
impl<'a> Writer<'a> {
pub fn new(encoded_cbor: &mut Vec<u8>) -> Writer {
Writer { encoded_cbor }
}
fn encode_cbor(
&mut self,
value: Value,
remaining_depth: Option<i8>,
) -> Result<(), EncoderError> {
if remaining_depth.map_or(false, |d| d < 0) {
return Err(EncoderError::TooMuchNesting);
}
let type_label = value.type_label();
match value {
Value::Unsigned(unsigned) => self.start_item(type_label, unsigned),
Value::Negative(negative) => self.start_item(type_label, -(negative + 1) as u64),
Value::ByteString(byte_string) => {
self.start_item(type_label, byte_string.len() as u64);
self.encoded_cbor.extend(byte_string);
}
Value::TextString(text_string) => {
self.start_item(type_label, text_string.len() as u64);
self.encoded_cbor.extend(text_string.into_bytes());
}
Value::Array(array) => {
self.start_item(type_label, array.len() as u64);
for el in array {
self.encode_cbor(el, remaining_depth.map(|d| d - 1))?;
}
}
Value::Map(mut map) => {
map.sort_by(|a, b| a.0.cmp(&b.0));
let map_len = map.len();
map.dedup_by(|a, b| a.0.eq(&b.0));
if map_len != map.len() {
return Err(EncoderError::DuplicateMapKey);
}
self.start_item(type_label, map_len as u64);
for (k, v) in map {
self.encode_cbor(k, remaining_depth.map(|d| d - 1))?;
self.encode_cbor(v, remaining_depth.map(|d| d - 1))?;
}
}
Value::Tag(tag, inner_value) => {
self.start_item(type_label, tag);
self.encode_cbor(*inner_value, remaining_depth.map(|d| d - 1))?;
}
Value::Simple(simple_value) => self.start_item(type_label, simple_value as u64),
}
Ok(())
}
fn start_item(&mut self, type_label: u8, size: u64) {
let (mut first_byte, shift) = match size {
0..=23 => (size as u8, 0),
24..=0xFF => (Constants::ADDITIONAL_INFORMATION_1_BYTE, 1),
0x100..=0xFFFF => (Constants::ADDITIONAL_INFORMATION_2_BYTES, 2),
0x10000..=0xFFFF_FFFF => (Constants::ADDITIONAL_INFORMATION_4_BYTES, 4),
_ => (Constants::ADDITIONAL_INFORMATION_8_BYTES, 8),
};
first_byte |= type_label << Constants::MAJOR_TYPE_BIT_SHIFT;
self.encoded_cbor.push(first_byte);
for i in (0..shift).rev() {
self.encoded_cbor.push((size >> (i * 8)) as u8);
}
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::{
cbor_array, cbor_array_vec, cbor_bytes, cbor_false, cbor_int, cbor_map, cbor_null,
cbor_tagged, cbor_text, cbor_true, cbor_undefined,
};
use alloc::vec;
fn write_return(value: Value) -> Option<Vec<u8>> {
let mut encoded_cbor = Vec::new();
if write(value, &mut encoded_cbor).is_ok() {
Some(encoded_cbor)
} else {
None
}
}
#[test]
fn test_write_unsigned() {
let cases = vec![
(0, vec![0x00]),
(1, vec![0x01]),
(10, vec![0x0A]),
(23, vec![0x17]),
(24, vec![0x18, 0x18]),
(25, vec![0x18, 0x19]),
(100, vec![0x18, 0x64]),
(1000, vec![0x19, 0x03, 0xE8]),
(1000000, vec![0x1A, 0x00, 0x0F, 0x42, 0x40]),
(0xFFFFFFFF, vec![0x1A, 0xFF, 0xFF, 0xFF, 0xFF]),
(
0x100000000,
vec![0x1B, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00],
),
(
core::i64::MAX,
vec![0x1B, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF],
),
];
for (unsigned, correct_cbor) in cases {
assert_eq!(write_return(cbor_int!(unsigned)), Some(correct_cbor));
}
}
#[test]
fn test_write_negative() {
let cases = vec![
(-1, vec![0x20]),
(-10, vec![0x29]),
(-23, vec![0x36]),
(-24, vec![0x37]),
(-25, vec![0x38, 0x18]),
(-100, vec![0x38, 0x63]),
(-1000, vec![0x39, 0x03, 0xE7]),
(-4294967296, vec![0x3A, 0xFF, 0xFF, 0xFF, 0xFF]),
(
-4294967297,
vec![0x3B, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00],
),
(
core::i64::MIN,
vec![0x3B, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF],
),
];
for (negative, correct_cbor) in cases {
assert_eq!(write_return(cbor_int!(negative)), Some(correct_cbor));
}
}
#[test]
fn test_write_byte_string() {
let cases = vec![
(vec![], vec![0x40]),
(
vec![0x01, 0x02, 0x03, 0x04],
vec![0x44, 0x01, 0x02, 0x03, 0x04],
),
];
for (byte_string, correct_cbor) in cases {
assert_eq!(write_return(cbor_bytes!(byte_string)), Some(correct_cbor));
}
}
#[test]
fn test_write_text_string() {
let unicode_3byte = vec![0xE6, 0xB0, 0xB4];
let cases = vec![
("", vec![0x60]),
("a", vec![0x61, 0x61]),
("IETF", vec![0x64, 0x49, 0x45, 0x54, 0x46]),
("\"\\", vec![0x62, 0x22, 0x5C]),
("ü", vec![0x62, 0xC3, 0xBC]),
(
core::str::from_utf8(&unicode_3byte).unwrap(),
vec![0x63, 0xE6, 0xB0, 0xB4],
),
("𐅑", vec![0x64, 0xF0, 0x90, 0x85, 0x91]),
];
for (text_string, correct_cbor) in cases {
assert_eq!(write_return(cbor_text!(text_string)), Some(correct_cbor));
}
}
#[test]
fn test_write_array() {
let value_vec: Vec<_> = (1..26).collect();
let expected_cbor = vec![
0x98, 0x19, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x18, 0x18, 0x19,
];
assert_eq!(
write_return(cbor_array_vec!(value_vec)),
Some(expected_cbor)
);
}
#[test]
fn test_write_map() {
let value_map = cbor_map! {
0 => "a",
23 => "b",
24 => "c",
core::u8::MAX as i64 => "d",
256 => "e",
core::u16::MAX as i64 => "f",
65536 => "g",
core::u32::MAX as i64 => "h",
4294967296_i64 => "i",
core::i64::MAX => "j",
-1 => "k",
-24 => "l",
-25 => "m",
-256 => "n",
-257 => "o",
-65537 => "p",
-4294967296_i64 => "q",
-4294967297_i64 => "r",
core::i64::MIN => "s",
b"a" => 2,
b"bar" => 3,
b"foo" => 4,
"" => ".",
"e" => "E",
"aa" => "AA",
};
let expected_cbor = vec![
0xb8, 0x19, 0x00, 0x61, 0x61, 0x17, 0x61, 0x62, 0x18, 0x18, 0x61, 0x63, 0x18, 0xFF, 0x61, 0x64, 0x19, 0x01, 0x00, 0x61, 0x65, 0x19, 0xFF, 0xFF, 0x61, 0x66, 0x1A, 0x00, 0x01, 0x00, 0x00, 0x61, 0x67, 0x1A, 0xFF, 0xFF, 0xFF, 0xFF, 0x61, 0x68, 0x1B, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x61, 0x69, 0x1b, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x61, 0x6a, 0x20, 0x61, 0x6b, 0x37, 0x61, 0x6c, 0x38, 0x18, 0x61, 0x6d, 0x38, 0xFF, 0x61, 0x6e, 0x39, 0x01, 0x00, 0x61, 0x6f, 0x3A, 0x00, 0x01, 0x00, 0x00, 0x61, 0x70, 0x3A, 0xFF, 0xFF, 0xFF, 0xFF, 0x61, 0x71, 0x3B, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x61, 0x72, 0x3b, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x61, 0x73, 0x41, b'a', 0x02, 0x43, b'b', b'a', b'r', 0x03, 0x43, b'f', b'o', b'o', 0x04, 0x60, 0x61, 0x2e, 0x61, 0x65, 0x61, 0x45, 0x62, 0x61, 0x61, 0x62, 0x41, 0x41, ];
assert_eq!(write_return(value_map), Some(expected_cbor));
}
#[test]
fn test_write_map_sorted() {
let sorted_map = cbor_map! {
0 => "a",
1 => "b",
-1 => "c",
-2 => "d",
b"a" => "e",
b"b" => "f",
"" => "g",
"c" => "h",
};
let unsorted_map = cbor_map! {
1 => "b",
-2 => "d",
b"b" => "f",
"c" => "h",
"" => "g",
b"a" => "e",
-1 => "c",
0 => "a",
};
assert_eq!(write_return(sorted_map), write_return(unsorted_map));
}
#[test]
fn test_write_map_duplicates() {
let duplicate0 = cbor_map! {
0 => "a",
-1 => "c",
b"a" => "e",
"c" => "g",
0 => "b",
};
assert_eq!(write_return(duplicate0), None);
let duplicate1 = cbor_map! {
0 => "a",
-1 => "c",
b"a" => "e",
"c" => "g",
-1 => "d",
};
assert_eq!(write_return(duplicate1), None);
let duplicate2 = cbor_map! {
0 => "a",
-1 => "c",
b"a" => "e",
"c" => "g",
b"a" => "f",
};
assert_eq!(write_return(duplicate2), None);
let duplicate3 = cbor_map! {
0 => "a",
-1 => "c",
b"a" => "e",
"c" => "g",
"c" => "h",
};
assert_eq!(write_return(duplicate3), None);
}
#[test]
fn test_write_map_with_array() {
let value_map = cbor_map! {
"a" => 1,
"b" => cbor_array![2, 3],
};
let expected_cbor = vec![
0xa2, 0x61, 0x61, 0x01, 0x61, 0x62, 0x82, 0x02, 0x03,
];
assert_eq!(write_return(value_map), Some(expected_cbor));
}
#[test]
fn test_write_nested_map() {
let value_map = cbor_map! {
"a" => 1,
"b" => cbor_map! {
"c" => 2,
"d" => 3,
},
};
let expected_cbor = vec![
0xa2, 0x61, 0x61, 0x01, 0x61, 0x62, 0xa2, 0x61, 0x63, 0x02, 0x61, 0x64, 0x03,
];
assert_eq!(write_return(value_map), Some(expected_cbor));
}
#[test]
fn test_write_tagged() {
let cases = vec![
(cbor_tagged!(6, cbor_int!(0x42)), vec![0xc6, 0x18, 0x42]),
(cbor_tagged!(1, cbor_true!()), vec![0xc1, 0xf5]),
(
cbor_tagged!(
1000,
cbor_map! {
"a" => 1,
"b" => cbor_array![2, 3],
}
),
vec![
0xd9, 0x03, 0xe8, 0xa2, 0x61, 0x61, 0x01, 0x61, 0x62, 0x82, 0x02, 0x03,
],
),
];
for (value, correct_cbor) in cases {
assert_eq!(write_return(value), Some(correct_cbor));
}
}
#[test]
fn test_write_simple() {
let cases = vec![
(cbor_false!(), vec![0xF4]),
(cbor_true!(), vec![0xF5]),
(cbor_null!(), vec![0xF6]),
(cbor_undefined!(), vec![0xF7]),
];
for (value, correct_cbor) in cases {
assert_eq!(write_return(value), Some(correct_cbor));
}
}
#[test]
fn test_write_single_levels() {
let simple_array: Value = cbor_array![2];
let simple_map: Value = cbor_map! {"b" => 3};
let positive_cases = vec![
(cbor_int!(1), 0),
(cbor_bytes!(vec![0x01, 0x02, 0x03, 0x04]), 0),
(cbor_text!("a"), 0),
(cbor_array![], 0),
(cbor_map! {}, 0),
(simple_array.clone(), 1),
(simple_map.clone(), 1),
];
let negative_cases = vec![(simple_array.clone(), 0), (simple_map.clone(), 0)];
for (value, level) in positive_cases {
let mut buf = Vec::new();
let mut writer = Writer::new(&mut buf);
assert!(writer.encode_cbor(value, Some(level)).is_ok());
}
for (value, level) in negative_cases {
let mut buf = Vec::new();
let mut writer = Writer::new(&mut buf);
assert!(!writer.encode_cbor(value, Some(level)).is_ok());
}
}
#[test]
fn test_write_nested_map_levels() {
let cbor_map: Value = cbor_map! {
"a" => 1,
"b" => cbor_map! {
"c" => 2,
"d" => 3,
},
};
let mut buf = Vec::new();
let mut writer = Writer::new(&mut buf);
assert!(writer.encode_cbor(cbor_map.clone(), Some(2)).is_ok());
assert!(writer.encode_cbor(cbor_map.clone(), None).is_ok());
writer = Writer::new(&mut buf);
assert!(writer.encode_cbor(cbor_map, Some(1)).is_err());
}
#[test]
fn test_write_unbalanced_nested_containers() {
let cbor_array: Value = cbor_array![
1,
2,
3,
cbor_map! {
"a" => 1,
"b" => cbor_map! {
"c" => 2,
"d" => 3,
},
},
];
let mut buf = Vec::new();
let mut writer = Writer::new(&mut buf);
assert!(writer.encode_cbor(cbor_array.clone(), Some(3)).is_ok());
writer = Writer::new(&mut buf);
assert!(writer.encode_cbor(cbor_array, Some(2)).is_err());
}
#[test]
fn test_write_overly_nested() {
let cbor_map: Value = cbor_map! {
"a" => 1,
"b" => cbor_map! {
"c" => 2,
"d" => 3,
"h" => cbor_map! {
"e" => 4,
"f" => 5,
"g" => cbor_array![
6,
7,
cbor_array![
8
]
],
},
},
};
let mut buf = Vec::new();
let mut writer = Writer::new(&mut buf);
assert!(writer.encode_cbor(cbor_map.clone(), Some(5)).is_ok());
assert!(writer.encode_cbor(cbor_map.clone(), None).is_ok());
writer = Writer::new(&mut buf);
assert!(writer.encode_cbor(cbor_map, Some(4)).is_err());
}
}