#![no_std]
#![forbid(unsafe_code)]
extern crate alloc;
extern crate bytes;
use alloc::collections::BTreeMap;
use alloc::string::String;
use alloc::vec::Vec;
use bytes::{Buf, Bytes};
use core::borrow::Borrow;
use core::cmp::Ordering;
use core::fmt;
use core::ops::Deref;
const _: () = assert!(
core::mem::size_of::<usize>() <= core::mem::size_of::<u64>(),
"usize too large"
);
const MAX_DEPTH: usize = 16;
#[derive(Debug, PartialEq)]
pub enum Error {
DepthLimitExceeded(usize, usize),
InputTruncated,
InvalidUTF8(usize),
MapKeysOutOfOrder(usize, Value),
NegativeOutOfRange(u64),
NonMinimalAdditionalData(usize),
TrailingData(usize),
UnsignedOutOfRange(u64),
UnsupportedAdditionalInformation(usize, u8),
UnsupportedMajorType(usize, u8),
UnsupportedMapKeyType(usize, Value),
UnsupportedSimpleValue(u64),
}
fn get_u8(bytes: &mut Bytes) -> Result<u8, Error> {
if bytes.is_empty() {
return Err(Error::InputTruncated);
}
Ok(bytes.get_u8())
}
fn get(bytes: &mut Bytes, num_bytes: usize) -> Result<Bytes, Error> {
if bytes.len() < num_bytes {
return Err(Error::InputTruncated);
}
Ok(bytes.split_to(num_bytes))
}
#[derive(PartialEq, Clone)]
pub enum Value {
Int(i64),
Bytestring(Bytes),
String(String),
Array(Vec<Value>),
Map(BTreeMap<MapKey, Value>),
Boolean(bool),
}
mod debug {
use super::{MapKey, Value};
use alloc::string::String;
use core::fmt;
const HEX_CHARS: [char; 16] = [
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f',
];
fn hex_encode(bytes: &[u8]) -> String {
let mut ret = String::with_capacity(bytes.len() * 2);
for &byte in bytes {
ret.push(HEX_CHARS[(byte >> 4) as usize]);
ret.push(HEX_CHARS[(byte & 15) as usize]);
}
ret
}
pub fn map_key(f: &mut fmt::Formatter, key: &MapKey) -> fmt::Result {
match key {
MapKey::Int(i) => write!(f, "{}", i),
MapKey::Bytestring(bytes) => {
f.write_str("h\"")?;
f.write_str(&hex_encode(bytes))?;
f.write_str("\"")
}
MapKey::String(s) => {
f.write_str("\"")?;
f.write_str(s)?;
f.write_str("\"")
}
}
}
pub fn value(f: &mut fmt::Formatter, value: &Value) -> fmt::Result {
match value {
Value::Int(i) => write!(f, "{}", i),
Value::Bytestring(bytes) => {
f.write_str("h\"")?;
f.write_str(&hex_encode(bytes))?;
f.write_str("\"")
}
Value::String(s) => {
f.write_str("\"")?;
f.write_str(s)?;
f.write_str("\"")
}
Value::Boolean(b) => f.write_str(if *b { "true" } else { "false" }),
Value::Array(array) => f.debug_list().entries(array.iter()).finish(),
Value::Map(map) => f.debug_map().entries(map.iter()).finish(),
}
}
}
impl fmt::Debug for MapKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
debug::map_key(f, self)
}
}
impl fmt::Debug for Value {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
debug::value(f, self)
}
}
fn low_bits_and_length(arg: u64) -> (u8, usize) {
if arg < 24 {
(arg as u8, 0)
} else if arg < 0x100 {
(24, 1)
} else if arg < 0x10000 {
(25, 2)
} else if arg < 0x100000000 {
(26, 4)
} else {
(27, 8)
}
}
fn write_header(out: &mut Vec<u8>, major_type: u8, arg: u64) {
let (low_bits, num_bytes) = low_bits_and_length(arg);
out.push(major_type << 5 | low_bits);
let value_bytes = arg.to_be_bytes();
out.extend_from_slice(&value_bytes[8 - num_bytes..]);
}
impl Value {
pub fn to_bytes(&self) -> Vec<u8> {
let mut ret = Vec::new();
self.append_bytes(&mut ret);
ret
}
pub fn append_bytes(&self, out: &mut Vec<u8>) {
match self {
Value::Int(v) if v >= &0 => write_header(out, 0, *v as u64),
Value::Int(v) => write_header(out, 1, !v as u64),
Value::Bytestring(s) => {
write_header(out, 2, s.len() as u64);
out.extend_from_slice(s.deref());
}
Value::String(s) => {
write_header(out, 3, s.len() as u64);
out.extend_from_slice(s.as_bytes());
}
Value::Array(a) => {
write_header(out, 4, a.len() as u64);
for elem in a {
elem.append_bytes(out);
}
}
Value::Map(m) => {
write_header(out, 5, m.len() as u64);
for (key, value) in m {
key.append_bytes(out);
value.append_bytes(out);
}
}
Value::Boolean(b) => write_header(out, 7, 20u64 + *b as u64),
}
}
}
impl From<&str> for Value {
fn from(s: &str) -> Self {
Value::String(String::from(s))
}
}
impl From<i64> for Value {
fn from(i: i64) -> Self {
Value::Int(i)
}
}
impl From<bool> for Value {
fn from(b: bool) -> Self {
Value::Boolean(b)
}
}
impl From<&[u8]> for Value {
fn from(bytes: &[u8]) -> Self {
Value::Bytestring(Bytes::from(bytes.to_vec()))
}
}
impl<const N: usize> From<&[u8; N]> for Value {
fn from(bytes: &[u8; N]) -> Self {
Value::Bytestring(Bytes::from(bytes.to_vec()))
}
}
impl From<Vec<u8>> for Value {
fn from(bytes: Vec<u8>) -> Self {
Value::Bytestring(Bytes::from(bytes))
}
}
impl From<Vec<Value>> for Value {
fn from(values: Vec<Value>) -> Self {
Value::Array(values)
}
}
impl From<BTreeMap<MapKey, Value>> for Value {
fn from(map: BTreeMap<MapKey, Value>) -> Self {
Value::Map(map)
}
}
#[macro_export]
macro_rules! cbor {
([ $( $elem:tt ),* ]) => {
$crate::Value::Array(vec![ $( $crate::cbor!($elem) ),* ])
};
([ $( $elem:tt ),* ,]) => {
$crate::cbor!([ $( $elem ),* ])
};
({ $( $key:tt : $value:tt ),* }) => {{
let mut map = alloc::collections::BTreeMap::new();
$( map.insert($crate::MapKey::from($key), $crate::cbor!($value)); )*
$crate::Value::Map(map)
}};
({ $( $key:tt : $value:tt ),* ,}) => {
$crate::cbor!({ $( $key : $value ),* })
};
( $value:tt ) => { $crate::Value::from($value) };
}
#[derive(PartialEq, Eq, Clone)]
pub enum MapKey {
Int(i64),
Bytestring(Vec<u8>),
String(String),
}
impl MapKey {
fn as_ref(&self) -> MapKeyRef<'_> {
match self {
MapKey::Int(v) => MapKeyRef::Int(*v),
MapKey::Bytestring(b) => MapKeyRef::Slice(b),
MapKey::String(s) => MapKeyRef::Str(s),
}
}
fn append_bytes(&self, out: &mut Vec<u8>) {
self.as_ref().append_bytes(out)
}
}
impl PartialOrd for MapKey {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for MapKey {
fn cmp(&self, other: &Self) -> Ordering {
self.as_ref().cmp(&other.as_ref())
}
}
impl From<&str> for MapKey {
fn from(s: &str) -> Self {
MapKey::String(String::from(s))
}
}
impl From<i64> for MapKey {
fn from(i: i64) -> Self {
MapKey::Int(i)
}
}
impl From<&[u8]> for MapKey {
fn from(bytes: &[u8]) -> Self {
MapKey::Bytestring(bytes.to_vec())
}
}
impl<const N: usize> From<&[u8; N]> for MapKey {
fn from(bytes: &[u8; N]) -> Self {
MapKey::Bytestring(bytes.to_vec())
}
}
impl From<Vec<u8>> for MapKey {
fn from(bytes: Vec<u8>) -> Self {
MapKey::Bytestring(bytes)
}
}
#[derive(PartialEq, Eq, Clone)]
pub enum MapKeyRef<'a> {
Int(i64),
Slice(&'a [u8]),
Str(&'a str),
}
impl MapKeyRef<'_> {
fn type_arg_and_payload(&self) -> (u8, u64, Option<&[u8]>) {
match self {
MapKeyRef::Int(v) if v >= &0 => (0, *v as u64, None),
MapKeyRef::Int(v) => (1, !v as u64, None),
MapKeyRef::Slice(b) => (2, b.len() as u64, Some(b)),
MapKeyRef::Str(s) => (3, s.len() as u64, Some(s.as_bytes())),
}
}
fn append_bytes(&self, out: &mut Vec<u8>) {
let (major_type, arg, payload) = self.type_arg_and_payload();
write_header(out, major_type, arg);
if let Some(payload) = payload {
out.extend_from_slice(payload);
}
}
}
impl PartialOrd for MapKeyRef<'_> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for MapKeyRef<'_> {
fn cmp(&self, other: &Self) -> Ordering {
self.type_arg_and_payload()
.cmp(&other.type_arg_and_payload())
}
}
pub trait MapLookupKey {
fn to_key(&self) -> MapKeyRef<'_>;
}
impl PartialEq for dyn MapLookupKey + '_ {
fn eq(&self, other: &Self) -> bool {
self.to_key().eq(&other.to_key())
}
}
impl Eq for dyn MapLookupKey + '_ {}
impl PartialOrd for dyn MapLookupKey + '_ {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.to_key().cmp(&other.to_key()))
}
}
impl Ord for dyn MapLookupKey + '_ {
fn cmp(&self, other: &Self) -> Ordering {
self.to_key().cmp(&other.to_key())
}
}
impl MapLookupKey for MapKey {
fn to_key(&self) -> MapKeyRef<'_> {
self.as_ref()
}
}
impl<'a> MapLookupKey for MapKeyRef<'a> {
fn to_key(&self) -> MapKeyRef<'_> {
self.clone()
}
}
impl<'a> Borrow<dyn MapLookupKey + 'a> for MapKey {
fn borrow(&self) -> &(dyn MapLookupKey + 'a) {
self
}
}
pub fn parse(input_vec: Vec<u8>) -> Result<Value, Error> {
parse_bytes(input_vec.into())
}
pub fn parse_bytes(mut input: Bytes) -> Result<Value, Error> {
let ret = parse_value(&mut input, 0)?;
if !input.is_empty() {
Err(Error::TrailingData(input.len()))
} else {
Ok(ret)
}
}
fn parse_value(input: &mut Bytes, depth: usize) -> Result<Value, Error> {
if depth > MAX_DEPTH {
return Err(Error::DepthLimitExceeded(input.len(), MAX_DEPTH));
}
let (major_type, arg) = parse_header(input)?;
match major_type {
0 => to_int(arg, false),
1 => to_int(arg, true),
2 => to_bytestring(input, arg),
3 => to_string(input, arg),
4 => to_array(input, arg, depth + 1),
5 => to_map(input, arg, depth + 1),
7 => to_boolean(arg),
_ => Result::Err(Error::UnsupportedMajorType(input.len(), major_type)),
}
}
fn parse_header(input: &mut Bytes) -> Result<(u8, u64), Error> {
let b = get_u8(input)?;
let major_type = b >> 5;
let info = b & 0x1f;
let arg = (match info {
0..=23 => Ok(info as u64),
24 => get_argument(input, 1),
25 => get_argument(input, 2),
26 => get_argument(input, 4),
27 => get_argument(input, 8),
_ => Err(Error::UnsupportedAdditionalInformation(input.len(), info)),
})?;
Ok((major_type, arg))
}
fn get_argument(input: &mut Bytes, num_bytes: u8) -> Result<u64, Error> {
let mut v: u64 = 0;
for _ in 0..num_bytes {
v <<= 8;
let b = get_u8(input)?;
v |= b as u64;
}
let (_, expected_num_bytes) = low_bits_and_length(v);
if num_bytes as usize != expected_num_bytes {
Err(Error::NonMinimalAdditionalData(input.len()))
} else {
Ok(v)
}
}
fn to_int(arg: u64, is_negative: bool) -> Result<Value, Error> {
if is_negative {
if arg > i64::MAX as u64 {
Err(Error::NegativeOutOfRange(arg))
} else {
Ok(Value::Int(!arg as i64))
}
} else if arg > i64::MAX as u64 {
Err(Error::UnsignedOutOfRange(arg))
} else {
Ok(Value::Int(arg as i64))
}
}
fn to_bytestring(input: &mut Bytes, len64: u64) -> Result<Value, Error> {
let Some(len): Option<usize> = len64.try_into().ok() else {
return Err(Error::InputTruncated);
};
let bytes = get(input, len)?;
Ok(Value::Bytestring(bytes))
}
fn to_string(input: &mut Bytes, len64: u64) -> Result<Value, Error> {
let Some(len): Option<usize> = len64.try_into().ok() else {
return Err(Error::InputTruncated);
};
let orig_len = input.len();
let bytes = get(input, len)?;
let vec = Vec::from(bytes.deref());
let Some(string) = String::from_utf8(vec).ok() else {
return Err(Error::InvalidUTF8(orig_len));
};
Ok(Value::String(string))
}
fn to_array(input: &mut Bytes, num_elements: u64, depth: usize) -> Result<Value, Error> {
let mut ret = Vec::new();
for _ in 0..num_elements {
ret.push(parse_value(input, depth)?);
}
Ok(Value::Array(ret))
}
fn to_map(input: &mut Bytes, num_elements: u64, depth: usize) -> Result<Value, Error> {
let mut ret = BTreeMap::new();
let mut previous_key: Option<Bytes> = Option::None;
for _ in 0..num_elements {
let mut key_slice = input.clone();
let key_value = parse_value(input, depth)?;
key_slice.truncate(key_slice.len() - input.len());
if let Some(previous_key_slice) = previous_key {
if *key_slice <= *previous_key_slice {
return Err(Error::MapKeysOutOfOrder(input.len(), key_value));
}
}
previous_key = Some(key_slice);
let key = match key_value {
Value::Int(i) => MapKey::Int(i),
Value::Bytestring(b) => MapKey::Bytestring(Vec::from(b.deref())),
Value::String(s) => MapKey::String(s),
_ => return Err(Error::UnsupportedMapKeyType(input.len(), key_value)),
};
let value = parse_value(input, depth)?;
ret.insert(key, value);
}
Ok(Value::Map(ret))
}
fn to_boolean(arg: u64) -> Result<Value, Error> {
if arg == 20 {
Ok(Value::Boolean(false))
} else if arg == 21 {
Ok(Value::Boolean(true))
} else {
Err(Error::UnsupportedSimpleValue(arg))
}
}
#[cfg(test)]
mod tests {
extern crate hex;
use super::*;
use alloc::{format, vec};
#[test]
fn test_inputs() {
let test_cases = [
("", Err(Error::InputTruncated)),
("d0", Err(Error::UnsupportedMajorType(0, 6))),
("1f", Err(Error::UnsupportedAdditionalInformation(0, 31))),
("01", Ok(Value::Int(1))),
("0100", Err(Error::TrailingData(1))),
("20", Ok(Value::Int(-1))),
("182a", Ok(Value::Int(42))),
("191388", Ok(Value::Int(5000))),
("1a02faf080", Ok(Value::Int(50000000))),
("1b000000746a528800", Ok(Value::Int(500000000000))),
("1b000000746a528800", Ok(Value::Int(500000000000))),
("1bffffffffffffffff", Err(Error::UnsignedOutOfRange(0xffffffffffffffff))),
("3bffffffffffffffff", Err(Error::NegativeOutOfRange(0xffffffffffffffff))),
("1818", Ok(Value::Int(24))),
("1817", Err(Error::NonMinimalAdditionalData(0))),
("190080", Err(Error::NonMinimalAdditionalData(0))),
("60", Ok(Value::String(String::from("")))),
("6161", Ok(Value::String(String::from("a")))),
("40", Ok(Value::Bytestring(Bytes::new()))),
("4100", Ok(Value::Bytestring(Bytes::from(b"\x00".as_slice())))),
("61ff", Err(Error::InvalidUTF8(1))),
("80", Ok(Value::Array(Vec::new()))),
("8101", Ok(Value::Array(vec![Value::Int(1)]))),
("818101", Ok(Value::Array(vec![Value::Array(vec![Value::Int(1)])]))),
(
"8181818181818181818181818181818181818180",
Err(Error::DepthLimitExceeded(3, MAX_DEPTH)),
),
("a0", Ok(Value::Map(BTreeMap::new()))),
("a10101", Ok(Value::Map(BTreeMap::from([(MapKey::Int(1), Value::Int(1))])))),
("a12101", Ok(Value::Map(BTreeMap::from([(MapKey::Int(-2), Value::Int(1))])))),
(
"a201010202",
Ok(Value::Map(BTreeMap::from([
(MapKey::Int(1), Value::Int(1)),
(MapKey::Int(2), Value::Int(2)),
]))),
),
(
"a1410a01",
Ok(Value::Map(BTreeMap::from([(
MapKey::Bytestring(hex::decode("0a").unwrap()),
Value::Int(1),
)]))),
),
(
"a501020326200121582009ac4af6a4646b5bfe81c37f751769c768c5c41ffea633dad0f48e6e3bc3e9a0225820269fbe132c40bf11f4de4a92bec527901906fdce98bbed52df9b175b6a4f3808",
Ok(Value::Map(BTreeMap::from([
(MapKey::Int(1), Value::Int(2)),
(MapKey::Int(3), Value::Int(-7)),
(MapKey::Int(-1), Value::Int(1)),
(
MapKey::Int(-2),
Value::Bytestring(Bytes::from(
hex::decode(
"09ac4af6a4646b5bfe81c37f751769c768c5c41ffea633dad0f48e6e3bc3e9a0",
)
.unwrap(),
)),
),
(
MapKey::Int(-3),
Value::Bytestring(Bytes::from(
hex::decode(
"269fbe132c40bf11f4de4a92bec527901906fdce98bbed52df9b175b6a4f3808",
)
.unwrap(),
)),
),
]))),
),
(
"a101a101a101a101a101a101a101a101a101a101a101a101a101a101a101a101a101a101a101a0",
Err(Error::DepthLimitExceeded(6, MAX_DEPTH)),
),
("a202010102", Err(Error::MapKeysOutOfOrder(1, Value::Int(1)))),
("a1810101", Err(Error::UnsupportedMapKeyType(1, Value::Array(vec![Value::Int(1)])))),
("f4", Ok(Value::Boolean(false))),
("f5", Ok(Value::Boolean(true))),
("f6", Err(Error::UnsupportedSimpleValue(22))),
];
for test in test_cases {
let bytes = Bytes::from(hex::decode(test.0).unwrap());
let result = parse_bytes(bytes.clone());
assert_eq!(result, test.1, "{}", test.0);
if let Ok(value) = result {
let bytes2 = value.to_bytes();
assert_eq!(bytes, bytes2, "{}", test.0);
}
if bytes.len() > 0 {
for i in 0..bytes.len() - 1 {
let mut bytes2 = bytes.clone();
bytes2.truncate(i);
let result = parse_bytes(bytes2.clone());
assert!(matches!(result, Result::Err(_)));
}
}
}
}
const EXAMPLE: &str = "example";
const EXAMPLE_KEY: &dyn MapLookupKey = &MapKeyRef::Str(EXAMPLE) as &dyn MapLookupKey;
#[test]
fn test_lookup() {
let map = BTreeMap::from([(MapKey::String(String::from(EXAMPLE)), Value::Int(1))]);
assert_eq!(map.get(EXAMPLE_KEY), Some(&Value::Int(1)));
}
#[test]
fn test_macro() {
assert_eq!(cbor!(1), Value::Int(1));
assert_eq!(cbor!("test"), Value::String(String::from("test")));
assert_eq!(
cbor!(b"123"),
Value::Bytestring(Bytes::from(b"\x31\x32\x33".as_slice()))
);
assert_eq!(
cbor!([1, 2]),
Value::Array(vec![Value::Int(1), Value::Int(2)])
);
assert_eq!(
cbor!([1, true, "str"]),
Value::Array(vec![
Value::Int(1),
Value::Boolean(true),
Value::String(String::from("str"))
])
);
assert_eq!(
cbor!({1: 2, 3: 4}),
Value::Map(BTreeMap::from([
(MapKey::Int(1), Value::Int(2)),
(MapKey::Int(3), Value::Int(4))
]))
);
assert_eq!(
cbor!({1: 2, 3: [1]}),
Value::Map(BTreeMap::from([
(MapKey::Int(1), Value::Int(2)),
(MapKey::Int(3), Value::Array(vec![Value::Int(1)]))
]))
);
assert_eq!(
cbor!({1: 2, 3: {"s":2}}),
Value::Map(BTreeMap::from([
(MapKey::Int(1), Value::Int(2)),
(
MapKey::Int(3),
Value::Map(BTreeMap::from([(
MapKey::String(String::from("s")),
Value::Int(2)
)]))
)
]))
);
assert_eq!(
cbor!({b"0": 1}),
Value::Map(BTreeMap::from([(
MapKey::Bytestring(vec![0x30u8]),
Value::Int(1)
),]))
);
}
#[test]
fn test_debug() {
let value = cbor!({
1: 2,
"three": "four",
"five": [6, 7, "eight"],
"nine": {
"ten": [11],
"twelve": {
b"13": 14,
},
},
});
let debug = format!("{:?}", value);
assert_eq!(
debug,
"{1: 2, \"five\": [6, 7, \"eight\"], \"nine\": {\"ten\": [11], \"twelve\": {h\"3133\": 14}}, \"three\": \"four\"}"
);
}
}