From 007ad11281a43fff1be2acc6ccf7d2786d30a74e Mon Sep 17 00:00:00 2001 From: Shautvast Date: Fri, 22 Dec 2023 17:26:00 +0100 Subject: [PATCH] support for ints, bytes, strings and arrays --- .gitignore | 4 ++ Cargo.toml | 11 ++++ README.md | 5 ++ src/lib.rs | 184 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 3 + 5 files changed, 207 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 README.md create mode 100644 src/lib.rs create mode 100644 src/main.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..082ddd2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +*.iml +target/ +.idea/ +Cargo.lock \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..8beee34 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "cbor_debug" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] + +[dev-dependencies] +minicbor = { version = "0.20", features = ["alloc", "derive"] } \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..6ffbd81 --- /dev/null +++ b/README.md @@ -0,0 +1,5 @@ +**CBOR DEBUG** + +Util that I needed while writing CBOR schema files (CDDL) + + diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..b5bccbe --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,184 @@ +use std::collections::HashMap; +use crate::MajorType::*; + + +pub fn decode(bytes: &[u8]) -> String { + format!("{:?}", decode_at(bytes, 0)) +} + +fn decode_at(bytes: &[u8], mut idx: usize) -> Vec { + let mut output = Vec::new(); + + while idx < bytes.len() { + output.push(decode_one_at(bytes, &mut idx)); + idx += 1; + } + output +} + +fn decode_one_at(bytes: &[u8], mut idx: &mut usize) -> MajorType { + let major_type = (bytes[*idx] & 0b11100000) >> 5; + + match major_type { + 0 => get_intval(&bytes, idx).map(|v| U(v)).unwrap_or(Invalid), + 1 => get_intval(&bytes, idx).map(|v| N(-1 - (v as i128))).unwrap_or(Invalid), + 2 => { + let len = get_intval(&bytes, &mut idx).unwrap() as usize; + let byte_string = bytes[*idx..*idx + len].to_vec(); + *idx += len; + BStr(byte_string) + } + 3 => { + let len = get_intval(&bytes, idx).unwrap() as usize; + let utf = bytes[*idx..*idx + len].to_vec(); + *idx += len; + Str(String::from_utf8(utf).unwrap()) + } + 4 => { + let len = get_intval(&bytes, idx).unwrap() as usize; + let mut array: Vec = Vec::new(); + for _ in 0..len { + array.push(decode_one_at(bytes, idx)); + } + Arr(array) + } + 5 => Map(HashMap::new()), + 6 => Tag, + 7 => { + *idx += 1; + Div + } + _ => { + Invalid + } + } +} + +fn get_intval(bytes: &[u8], i: &mut usize) -> Option { + let next = bytes[*i] & 0b00011111; + if next < 24 { + *i += 1; + Some(next as u64) + } else { + if next < 28 { + let nbytes = 1 << (next - 24); + + let int_val = u64::from_be_bytes(to_b8(&bytes[*i + 1..=*i + nbytes])); + *i += nbytes + 1; + Some(int_val) + } else { + None + } + } +} + +#[derive(Debug)] +#[repr(u8)] +enum MajorType { + U(u64) = 0, + N(i128) = 1, + BStr(Vec) = 2, + Str(String) = 3, + Arr(Vec) = 4, + Map(HashMap) = 5, + Tag = 6, + Div = 7, + Invalid, +} + +fn to_b8(bytes: &[u8]) -> [u8; 8] { + let mut out = [0_u8; 8]; + for (i, b) in bytes.iter().enumerate() { + out[8 - bytes.len() + i] = *b; + } + out +} + +#[cfg(test)] +mod test { + use minicbor::{Decode, Encode, to_vec}; + use super::*; + + #[test] + fn int_0() { + assert_eq!("[U(0)]", decode(&to_vec(0).unwrap())); + } + + #[test] + fn int_24() { + assert_eq!("[U(24)]", decode(&to_vec(24).unwrap())); + } + + #[test] + fn int_u64_max() { + assert_eq!(format!("[U({})]", u64::MAX - 1), decode(&to_vec(u64::MAX - 1).unwrap())); + } + + #[test] + fn neg_int_23() { + assert_eq!("[N(-23)]", decode(&to_vec(-23).unwrap())); + } + + #[test] + fn neg_int_i64_max() { + // i64::MIN = -2^63 + //can't encode -2^64 in minicbor...? cbor spec allows it! + assert_eq!(format!("[N({})]", i64::MIN), decode(&to_vec(i64::MIN).unwrap())); + } + + #[test] + fn bytestring() { + assert_eq!(format!("[BStr([1, 2, 3, 4, 5])]"), decode(&[0b01000101, 1, 2, 3, 4, 5])); + } + + #[test] + fn string() { + assert_eq!(format!("[Str(\"Hello World\")]"), decode(&to_vec("Hello World").unwrap())); + } + + #[test] + fn array() { + assert_eq!(format!("[Arr([U(1), U(2), U(3), U(4), U(5)])]"), decode(&to_vec([1, 2, 3, 4, 5]).unwrap())); + } + + #[test] + fn struct_n0() { + #[derive(Decode, Encode)] + struct Simple { + #[n(0)] name: String, + } + assert_eq!(format!("[Arr([Str(\"foobar\")])]"), decode(&to_vec(Simple { name: "foobar".into() }).unwrap())); + } + + #[test] + fn struct_n1() { + #[derive(Decode, Encode)] + struct Simple { + #[n(1)] name: String, + } + assert_eq!(format!("[Arr([Div, Str(\"foobar\")])]"), decode(&to_vec(Simple { name: "foobar".into() }).unwrap())); + } + + #[test] + fn enum_1() { + assert_eq!(format!("[Arr([U(1), Arr([Div, Str(\"foo\")])])]"), decode( + &to_vec( + Simple::Left("foo".into()) + ).unwrap())); + } + + #[test] + fn enum_vec() { + assert_eq!(format!("[Arr([Arr([U(1), Arr([Div, Str(\"foo\")])]), Arr([U(2), Arr([Div, Str(\"bar\")])])])]"), decode( + &to_vec( + vec![Simple::Left("foo".into()), + Simple::Right("bar".into())], + ).unwrap())); + } + + #[derive(Decode, Encode)] + enum Simple { + #[n(1)] Left(#[n(1)] String), + #[n(2)] Right(#[n(1)] String), + } +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..c5ef4f2 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("to be implemented soon"); +}