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}