Skip to main content

neco_cbor/
encode.rs

1use alloc::vec::Vec;
2
3use crate::{CborValue, EncodeError};
4
5pub fn encode(value: &CborValue) -> Result<Vec<u8>, EncodeError> {
6    let mut output = Vec::new();
7    encode_value(value, &mut output, false)?;
8    Ok(output)
9}
10
11pub fn encode_dag(value: &CborValue) -> Result<Vec<u8>, EncodeError> {
12    let mut output = Vec::new();
13    encode_value(value, &mut output, true)?;
14    Ok(output)
15}
16
17fn encode_value(
18    value: &CborValue,
19    output: &mut Vec<u8>,
20    dag_mode: bool,
21) -> Result<(), EncodeError> {
22    match value {
23        CborValue::Unsigned(number) => encode_head(0, *number, output),
24        CborValue::Negative(number) => encode_negative(*number, output)?,
25        CborValue::Bytes(bytes) => {
26            encode_head(2, bytes.len() as u64, output);
27            output.extend_from_slice(bytes);
28        }
29        CborValue::Text(text) => {
30            encode_head(3, text.len() as u64, output);
31            output.extend_from_slice(text.as_bytes());
32        }
33        CborValue::Array(items) => {
34            encode_head(4, items.len() as u64, output);
35            for item in items {
36                encode_value(item, output, dag_mode)?;
37            }
38        }
39        CborValue::Map(entries) => encode_map(entries, output, dag_mode)?,
40        CborValue::Tag(tag, inner) => encode_tag(*tag, inner.as_ref(), output)?,
41        CborValue::Bool(false) => output.push(0xF4),
42        CborValue::Bool(true) => output.push(0xF5),
43        CborValue::Null => output.push(0xF6),
44    }
45    Ok(())
46}
47
48fn encode_negative(value: i64, output: &mut Vec<u8>) -> Result<(), EncodeError> {
49    if value >= 0 {
50        return Err(EncodeError::InvalidNegativeValue(value));
51    }
52
53    let encoded = (-1_i128 - i128::from(value)) as u64;
54    encode_head(1, encoded, output);
55    Ok(())
56}
57
58fn encode_map(
59    entries: &[(CborValue, CborValue)],
60    output: &mut Vec<u8>,
61    dag_mode: bool,
62) -> Result<(), EncodeError> {
63    encode_head(5, entries.len() as u64, output);
64
65    if !dag_mode {
66        for (key, value) in entries {
67            encode_value(key, output, false)?;
68            encode_value(value, output, false)?;
69        }
70        return Ok(());
71    }
72
73    let mut ordered = Vec::with_capacity(entries.len());
74    for (key, value) in entries {
75        let CborValue::Text(text) = key else {
76            return Err(EncodeError::NonTextKeyInDagMode);
77        };
78
79        let mut encoded_key = Vec::new();
80        encode_head(3, text.len() as u64, &mut encoded_key);
81        encoded_key.extend_from_slice(text.as_bytes());
82        ordered.push((encoded_key, value));
83    }
84
85    ordered.sort_by(|left, right| left.0.cmp(&right.0));
86    for i in 1..ordered.len() {
87        if ordered[i - 1].0 == ordered[i].0 {
88            return Err(EncodeError::DuplicateKeyInDagMode);
89        }
90    }
91    for (encoded_key, value) in ordered {
92        output.extend_from_slice(&encoded_key);
93        encode_value(value, output, true)?;
94    }
95    Ok(())
96}
97
98fn encode_tag(tag: u64, inner: &CborValue, output: &mut Vec<u8>) -> Result<(), EncodeError> {
99    if tag != 42 {
100        return Err(EncodeError::UnsupportedTag(tag));
101    }
102
103    let CborValue::Bytes(bytes) = inner else {
104        return Err(EncodeError::InvalidTag42Payload);
105    };
106    if bytes.first().copied() != Some(0x00) {
107        return Err(EncodeError::InvalidTag42Payload);
108    }
109
110    encode_head(6, tag, output);
111    encode_head(2, bytes.len() as u64, output);
112    output.extend_from_slice(bytes);
113    Ok(())
114}
115
116fn encode_head(major: u8, value: u64, output: &mut Vec<u8>) {
117    match value {
118        0..=23 => output.push((major << 5) | value as u8),
119        24..=0xFF => {
120            output.push((major << 5) | 24);
121            output.push(value as u8);
122        }
123        0x100..=0xFFFF => {
124            output.push((major << 5) | 25);
125            output.extend_from_slice(&(value as u16).to_be_bytes());
126        }
127        0x1_0000..=0xFFFF_FFFF => {
128            output.push((major << 5) | 26);
129            output.extend_from_slice(&(value as u32).to_be_bytes());
130        }
131        _ => {
132            output.push((major << 5) | 27);
133            output.extend_from_slice(&value.to_be_bytes());
134        }
135    }
136}