use crate::CborValue;
const MT_UINT: u8 = 0;
const MT_NEGINT: u8 = 1;
const MT_BYTES: u8 = 2;
const MT_STRING: u8 = 3;
const MT_ARRAY: u8 = 4;
const MT_MAP: u8 = 5;
pub fn encode_dag_cbor(value: &CborValue) -> Vec<u8> {
let mut out = Vec::new();
encode_value(value, &mut out);
out
}
fn encode_value(v: &CborValue, out: &mut Vec<u8>) {
match v {
CborValue::Null => out.push(0xf6),
CborValue::Bool(false) => out.push(0xf4),
CborValue::Bool(true) => out.push(0xf5),
CborValue::Int(n) => {
if *n >= 0 {
write_header(MT_UINT, n.unsigned_abs(), out);
} else {
write_header(MT_NEGINT, n.unsigned_abs() - 1, out);
}
}
CborValue::Bytes(b) => {
write_header(MT_BYTES, len_as_u64(b.len()), out);
out.extend_from_slice(b);
}
CborValue::String(s) => {
let bytes = s.as_bytes();
write_header(MT_STRING, len_as_u64(bytes.len()), out);
out.extend_from_slice(bytes);
}
CborValue::Array(arr) => {
write_header(MT_ARRAY, len_as_u64(arr.len()), out);
for v in arr {
encode_value(v, out);
}
}
CborValue::Map(entries) => {
let mut sorted = entries.clone();
sorted.sort_by(|(ka, _), (kb, _)| {
ka.len()
.cmp(&kb.len())
.then_with(|| ka.as_bytes().cmp(kb.as_bytes()))
});
write_header(MT_MAP, len_as_u64(sorted.len()), out);
for (k, v) in sorted {
let kb = k.as_bytes();
write_header(MT_STRING, len_as_u64(kb.len()), out);
out.extend_from_slice(kb);
encode_value(&v, out);
}
}
}
}
fn len_as_u64(len: usize) -> u64 {
u64::try_from(len).unwrap_or(u64::MAX)
}
fn write_header(mt: u8, value: u64, out: &mut Vec<u8>) {
let be = value.to_be_bytes();
let head = mt << 5;
if value < 24 {
out.push(head | be[7]);
} else if value < 0x100 {
out.push(head | 24);
out.extend_from_slice(&be[7..8]);
} else if value < 0x1_0000 {
out.push(head | 25);
out.extend_from_slice(&be[6..8]);
} else if value < 0x1_0000_0000 {
out.push(head | 26);
out.extend_from_slice(&be[4..8]);
} else {
out.push(head | 27);
out.extend_from_slice(&be);
}
}