use crate::c2pa_cbor::{canonical_sort, value::Value, Profile};
use thiserror::Error;
#[derive(Debug, Error)]
pub enum EncodeError {
#[error("non-finite float not supported in canonical profile")]
NonFiniteFloat,
}
const MT_UINT: u8 = 0 << 5;
const MT_NINT: u8 = 1 << 5;
const MT_BYTES: u8 = 2 << 5;
const MT_TEXT: u8 = 3 << 5;
const MT_ARRAY: u8 = 4 << 5;
const MT_MAP: u8 = 5 << 5;
const MT_TAG: u8 = 6 << 5;
const MT_SIMPLE: u8 = 7 << 5;
pub fn encode_into(out: &mut Vec<u8>, value: &Value, profile: Profile) -> Result<(), EncodeError> {
match value {
Value::Integer(n) => {
encode_int(out, *n);
Ok(())
}
Value::Bytes(b) => {
write_head(out, MT_BYTES, b.len() as u64);
out.extend_from_slice(b);
Ok(())
}
Value::Text(s) => {
write_head(out, MT_TEXT, s.len() as u64);
out.extend_from_slice(s.as_bytes());
Ok(())
}
Value::Array(items) => {
if profile.indefinite() {
out.push(MT_ARRAY | 31); for item in items {
encode_into(out, item, profile)?;
}
out.push(0xff);
} else {
write_head(out, MT_ARRAY, items.len() as u64);
for item in items {
encode_into(out, item, profile)?;
}
}
Ok(())
}
Value::Map(entries) => {
let mut entries = entries.clone();
if profile.sort_keys() {
canonical_sort(&mut entries);
}
if profile.indefinite() {
out.push(MT_MAP | 31); for (k, v) in &entries {
encode_into(out, k, profile)?;
encode_into(out, v, profile)?;
}
out.push(0xff);
} else {
write_head(out, MT_MAP, entries.len() as u64);
for (k, v) in &entries {
encode_into(out, k, profile)?;
encode_into(out, v, profile)?;
}
}
Ok(())
}
Value::Tag(tag, inner) => {
write_head(out, MT_TAG, *tag);
encode_into(out, inner, profile)
}
Value::Bool(b) => {
out.push(MT_SIMPLE | if *b { 21 } else { 20 });
Ok(())
}
Value::Null => {
out.push(MT_SIMPLE | 22);
Ok(())
}
Value::Float(f) => {
if profile.sort_keys() && !f.is_finite() {
return Err(EncodeError::NonFiniteFloat);
}
out.push(MT_SIMPLE | 27); out.extend_from_slice(&f.to_bits().to_be_bytes());
Ok(())
}
}
}
fn encode_int(out: &mut Vec<u8>, n: i128) {
if n >= 0 {
write_head(out, MT_UINT, n as u64);
} else {
let m = (-1 - n) as u64;
write_head(out, MT_NINT, m);
}
}
fn write_head(out: &mut Vec<u8>, mt: u8, arg: u64) {
if arg < 24 {
out.push(mt | arg as u8);
} else if arg <= u8::MAX as u64 {
out.push(mt | 24);
out.push(arg as u8);
} else if arg <= u16::MAX as u64 {
out.push(mt | 25);
out.extend_from_slice(&(arg as u16).to_be_bytes());
} else if arg <= u32::MAX as u64 {
out.push(mt | 26);
out.extend_from_slice(&(arg as u32).to_be_bytes());
} else {
out.push(mt | 27);
out.extend_from_slice(&arg.to_be_bytes());
}
}