use crate::ConstVec;
#[repr(u8)]
#[derive(PartialEq)]
enum MajorType {
UnsignedInteger = 0,
NegativeInteger = 1,
Bytes = 2,
Text = 3,
Array = 4,
Map = 5,
Tagged = 6,
Float = 7,
}
impl MajorType {
const MASK: u8 = 0b0001_1111;
const fn from_byte(byte: u8) -> Self {
match byte >> 5 {
0 => MajorType::UnsignedInteger,
1 => MajorType::NegativeInteger,
2 => MajorType::Bytes,
3 => MajorType::Text,
4 => MajorType::Array,
5 => MajorType::Map,
6 => MajorType::Tagged,
7 => MajorType::Float,
_ => panic!("Invalid major type"),
}
}
}
const fn item_length(bytes: &[u8]) -> Result<usize, ()> {
let [head, rest @ ..] = bytes else {
return Err(());
};
let major = MajorType::from_byte(*head);
let additional_information = *head & MajorType::MASK;
let length_of_item = match major {
MajorType::UnsignedInteger | MajorType::NegativeInteger => {
get_length_of_number(additional_information) as usize
}
MajorType::Text | MajorType::Bytes => {
let length_of_number = get_length_of_number(additional_information);
let Ok((length_of_bytes, _)) =
grab_u64_with_byte_length(rest, length_of_number, additional_information)
else {
return Err(());
};
length_of_number as usize + length_of_bytes as usize
}
MajorType::Array | MajorType::Map => {
let length_of_number = get_length_of_number(additional_information);
let Ok((length_of_items, _)) =
grab_u64_with_byte_length(rest, length_of_number, additional_information)
else {
return Err(());
};
let mut total_length = length_of_number as usize;
let mut items_left = length_of_items * if let MajorType::Map = major { 2 } else { 1 };
while items_left > 0 {
let Some((_, after)) = rest.split_at_checked(total_length) else {
return Err(());
};
let Ok(item_length) = item_length(after) else {
return Err(());
};
total_length += item_length;
items_left -= 1;
}
total_length
}
_ => return Err(()),
};
let length_of_head = 1;
Ok(length_of_head + length_of_item)
}
pub(crate) const fn take_number(bytes: &[u8]) -> Result<(i64, &[u8]), ()> {
let [head, rest @ ..] = bytes else {
return Err(());
};
let major = MajorType::from_byte(*head);
let additional_information = *head & MajorType::MASK;
match major {
MajorType::UnsignedInteger => {
let Ok((number, rest)) = grab_u64(rest, additional_information) else {
return Err(());
};
Ok((number as i64, rest))
}
MajorType::NegativeInteger => {
let Ok((number, rest)) = grab_u64(rest, additional_information) else {
return Err(());
};
Ok((-(1 + number as i64), rest))
}
_ => Err(()),
}
}
pub(crate) const fn write_number<const MAX_SIZE: usize>(
vec: ConstVec<u8, MAX_SIZE>,
number: i64,
) -> ConstVec<u8, MAX_SIZE> {
match number {
0.. => write_major_type_and_u64(vec, MajorType::UnsignedInteger, number as u64),
..0 => write_major_type_and_u64(vec, MajorType::NegativeInteger, (-(number + 1)) as u64),
}
}
const fn write_major_type_and_u64<const MAX_SIZE: usize>(
vec: ConstVec<u8, MAX_SIZE>,
major: MajorType,
number: u64,
) -> ConstVec<u8, MAX_SIZE> {
let major = (major as u8) << 5;
match number {
0..24 => {
let additional_information = number as u8;
let byte = major | additional_information;
vec.push(byte)
}
24.. => {
let log2_additional_bytes = log2_bytes_for_number(number);
let additional_bytes = 1 << log2_additional_bytes;
let additional_information = log2_additional_bytes + 24;
let byte = major | additional_information;
let mut vec = vec.push(byte);
let mut byte = 0;
while byte < additional_bytes {
vec = vec.push((number >> ((additional_bytes - byte - 1) * 8)) as u8);
byte += 1;
}
vec
}
}
}
const fn log2_bytes_for_number(number: u64) -> u8 {
let required_bytes = ((64 - number.leading_zeros()).div_ceil(8)) as u8;
#[allow(clippy::match_overlapping_arm)]
match required_bytes {
..=1 => 0,
..=2 => 1,
..=4 => 2,
_ => 3,
}
}
pub(crate) const fn take_bytes(bytes: &[u8]) -> Result<(&[u8], &[u8]), ()> {
let [head, rest @ ..] = bytes else {
return Err(());
};
let major = MajorType::from_byte(*head);
let additional_information = *head & MajorType::MASK;
if let MajorType::Bytes = major {
take_bytes_from(rest, additional_information)
} else {
Err(())
}
}
pub(crate) const fn write_bytes<const MAX_SIZE: usize>(
vec: ConstVec<u8, MAX_SIZE>,
bytes: &[u8],
) -> ConstVec<u8, MAX_SIZE> {
let vec = write_major_type_and_u64(vec, MajorType::Bytes, bytes.len() as u64);
vec.extend(bytes)
}
pub(crate) const fn take_str(bytes: &[u8]) -> Result<(&str, &[u8]), ()> {
let [head, rest @ ..] = bytes else {
return Err(());
};
let major = MajorType::from_byte(*head);
let additional_information = *head & MajorType::MASK;
if let MajorType::Text = major {
let Ok((bytes, rest)) = take_bytes_from(rest, additional_information) else {
return Err(());
};
let Ok(string) = std::str::from_utf8(bytes) else {
return Err(());
};
Ok((string, rest))
} else {
Err(())
}
}
pub(crate) const fn write_str<const MAX_SIZE: usize>(
vec: ConstVec<u8, MAX_SIZE>,
string: &str,
) -> ConstVec<u8, MAX_SIZE> {
let vec = write_major_type_and_u64(vec, MajorType::Text, string.len() as u64);
vec.extend(string.as_bytes())
}
pub(crate) const fn take_array(bytes: &[u8]) -> Result<(usize, &[u8]), ()> {
let [head, rest @ ..] = bytes else {
return Err(());
};
let major = MajorType::from_byte(*head);
let additional_information = *head & MajorType::MASK;
if let MajorType::Array = major {
let Ok((length, rest)) = take_len_from(rest, additional_information) else {
return Err(());
};
Ok((length as usize, rest))
} else {
Err(())
}
}
pub(crate) const fn write_array<const MAX_SIZE: usize>(
vec: ConstVec<u8, MAX_SIZE>,
len: usize,
) -> ConstVec<u8, MAX_SIZE> {
write_major_type_and_u64(vec, MajorType::Array, len as u64)
}
pub(crate) const fn write_map<const MAX_SIZE: usize>(
vec: ConstVec<u8, MAX_SIZE>,
len: usize,
) -> ConstVec<u8, MAX_SIZE> {
write_major_type_and_u64(vec, MajorType::Map, len as u64)
}
pub(crate) const fn write_map_key<const MAX_SIZE: usize>(
value: ConstVec<u8, MAX_SIZE>,
key: &str,
) -> ConstVec<u8, MAX_SIZE> {
write_str(value, key)
}
pub(crate) const fn take_map<'a>(bytes: &'a [u8]) -> Result<(MapRef<'a>, &'a [u8]), ()> {
let [head, rest @ ..] = bytes else {
return Err(());
};
let major = MajorType::from_byte(*head);
let additional_information = *head & MajorType::MASK;
if let MajorType::Map = major {
let Ok((length, rest)) = take_len_from(rest, additional_information) else {
return Err(());
};
let mut after_map = rest;
let mut items_left = length * 2;
while items_left > 0 {
let Ok(len) = item_length(after_map) else {
return Err(());
};
let Some((_, rest)) = after_map.split_at_checked(len) else {
return Err(());
};
after_map = rest;
items_left -= 1;
}
Ok((MapRef::new(rest, length as usize), after_map))
} else {
Err(())
}
}
pub(crate) struct MapRef<'a> {
pub(crate) bytes: &'a [u8],
pub(crate) len: usize,
}
impl<'a> MapRef<'a> {
const fn new(bytes: &'a [u8], len: usize) -> Self {
Self { bytes, len }
}
pub(crate) const fn find(&self, key: &str) -> Result<Option<&[u8]>, ()> {
let mut bytes = self.bytes;
let mut items_left = self.len;
while items_left > 0 {
let Ok((str, rest)) = take_str(bytes) else {
return Err(());
};
if str_eq(key, str) {
return Ok(Some(rest));
}
let Ok(len) = item_length(rest) else {
return Err(());
};
let Some((_, rest)) = rest.split_at_checked(len) else {
return Err(());
};
bytes = rest;
items_left -= 1;
}
Ok(None)
}
}
pub(crate) const fn str_eq(a: &str, b: &str) -> bool {
let a_bytes = a.as_bytes();
let b_bytes = b.as_bytes();
let a_len = a_bytes.len();
let b_len = b_bytes.len();
if a_len != b_len {
return false;
}
let mut index = 0;
while index < a_len {
if a_bytes[index] != b_bytes[index] {
return false;
}
index += 1;
}
true
}
const fn take_len_from(rest: &[u8], additional_information: u8) -> Result<(u64, &[u8]), ()> {
match additional_information {
0..24 => Ok((additional_information as u64, rest)),
24..28 => {
let Ok((number, rest)) = grab_u64(rest, additional_information) else {
return Err(());
};
Ok((number, rest))
}
_ => Err(()),
}
}
pub(crate) const fn take_bytes_from(
rest: &[u8],
additional_information: u8,
) -> Result<(&[u8], &[u8]), ()> {
let Ok((number, rest)) = grab_u64(rest, additional_information) else {
return Err(());
};
let Some((bytes, rest)) = rest.split_at_checked(number as usize) else {
return Err(());
};
Ok((bytes, rest))
}
const fn get_length_of_number(additional_information: u8) -> u8 {
match additional_information {
0..24 => 0,
24..28 => 1 << (additional_information - 24),
_ => 0,
}
}
const fn grab_u64(rest: &[u8], additional_information: u8) -> Result<(u64, &[u8]), ()> {
grab_u64_with_byte_length(
rest,
get_length_of_number(additional_information),
additional_information,
)
}
const fn grab_u64_with_byte_length(
mut rest: &[u8],
byte_length: u8,
additional_information: u8,
) -> Result<(u64, &[u8]), ()> {
match byte_length {
0 => Ok((additional_information as u64, rest)),
n => {
let mut value = 0;
let mut count = 0;
while count < n {
let [next, remaining @ ..] = rest else {
return Err(());
};
value = (value << 8) | *next as u64;
rest = remaining;
count += 1;
}
Ok((value, rest))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_byte() {
for byte in 0..=255 {
let bytes = if byte < 24 { [byte, 0] } else { [24, byte] };
let (item, _) = take_number(&bytes).unwrap();
assert_eq!(item, byte as _);
}
for byte in 1..=255 {
let bytes = if byte < 24 {
[(byte - 1) | 0b0010_0000, 0]
} else {
[0b0010_0000 | 24, byte - 1]
};
let (item, _) = take_number(&bytes).unwrap();
assert_eq!(item, -(byte as i64));
}
}
#[test]
fn test_byte_roundtrip() {
for byte in 0..=255 {
let vec = write_number(ConstVec::new(), byte as _);
println!("{vec:?}");
let (item, _) = take_number(vec.as_ref()).unwrap();
assert_eq!(item, byte as _);
}
for byte in 0..=255 {
let vec = write_number(ConstVec::new(), -(byte as i64));
let (item, _) = take_number(vec.as_ref()).unwrap();
assert_eq!(item, -(byte as i64));
}
}
#[test]
fn test_number_roundtrip() {
for _ in 0..100 {
let value = rand::random::<i64>();
let vec = write_number(ConstVec::new(), value);
let (item, _) = take_number(vec.as_ref()).unwrap();
assert_eq!(item, value);
}
}
#[test]
fn test_bytes_roundtrip() {
for _ in 0..100 {
let len = (rand::random::<u8>() % 100) as usize;
let bytes = rand::random::<[u8; 100]>();
let vec = write_bytes(ConstVec::new(), &bytes[..len]);
let (item, _) = take_bytes(vec.as_ref()).unwrap();
assert_eq!(item, &bytes[..len]);
}
}
#[test]
fn test_array_roundtrip() {
for _ in 0..100 {
let len = (rand::random::<u8>() % 100) as usize;
let mut vec = write_array(ConstVec::new(), len);
for i in 0..len {
vec = write_number(vec, i as _);
}
let (len, mut remaining) = take_array(vec.as_ref()).unwrap();
for i in 0..len {
let (item, rest) = take_number(remaining).unwrap();
remaining = rest;
assert_eq!(item, i as i64);
}
}
}
#[test]
fn test_map_roundtrip() {
use rand::prelude::SliceRandom;
for _ in 0..100 {
let len = (rand::random::<u8>() % 10) as usize;
let mut vec = write_map(ConstVec::new(), len);
let mut random_order_indexes = (0..len).collect::<Vec<_>>();
random_order_indexes.shuffle(&mut rand::rng());
for &i in &random_order_indexes {
vec = write_map_key(vec, &i.to_string());
vec = write_number(vec, i as _);
}
println!("len: {}", len);
println!("Map: {:?}", vec);
let (map, remaining) = take_map(vec.as_ref()).unwrap();
println!("remaining: {:?}", remaining);
assert!(remaining.is_empty());
for i in 0..len {
let key = i.to_string();
let key_location = map
.find(&key)
.expect("encoding is valid")
.expect("key exists");
let (value, _) = take_number(key_location).unwrap();
assert_eq!(value, i as i64);
}
}
}
#[test]
fn test_item_length_str() {
#[rustfmt::skip]
let input = [
0x61,
0x31,
0x61,
0x31,
];
let Ok(length) = item_length(&input) else {
panic!("Failed to calculate length");
};
assert_eq!(length, 2);
}
#[test]
fn test_item_length_map() {
#[rustfmt::skip]
let input = [
0xA1,
0x61,
0x41,
0xA2,
0x63,
0x6F, 0x6E, 0x65,
0x1A, 0x11, 0x11, 0x11, 0x11,
0x63,
0x74, 0x77, 0x6F,
0x18, 0x22,
];
let Ok(length) = item_length(&input) else {
panic!("Failed to calculate length");
};
assert_eq!(length, input.len());
}
}