codec_cbor/
encode_dag_cbor.rs1use crate::{CborError, CborValue, MAX_DAG_CBOR_INPUT_LEN, MAX_NESTING_DEPTH};
6use zeroize::Zeroizing;
7
8const MT_UINT: u8 = 0;
9const MT_NEGINT: u8 = 1;
10const MT_BYTES: u8 = 2;
11const MT_STRING: u8 = 3;
12const MT_ARRAY: u8 = 4;
13const MT_MAP: u8 = 5;
14
15pub fn encode_dag_cbor(value: &CborValue) -> Result<Vec<u8>, CborError> {
25 let mut length = 0_usize;
29 encode_value(value, &mut length, 0)?;
30 let mut out = Zeroizing::new(Vec::with_capacity(length));
31 encode_value(value, &mut *out, 0)?;
32 Ok(core::mem::take(&mut *out))
33}
34
35fn encode_value(v: &CborValue, out: &mut impl EncodingSink, depth: usize) -> Result<(), CborError> {
36 match v {
37 CborValue::Null => push_byte(out, 0xf6)?,
38 CborValue::Bool(false) => push_byte(out, 0xf4)?,
39 CborValue::Bool(true) => push_byte(out, 0xf5)?,
40
41 CborValue::Int(n) => {
42 if *n >= 0 {
43 write_header(MT_UINT, n.unsigned_abs(), out)?;
44 } else {
45 write_header(MT_NEGINT, n.unsigned_abs() - 1, out)?;
46 }
47 }
48
49 CborValue::Bytes(b) => {
50 write_header(MT_BYTES, len_as_u64(b.len())?, out)?;
51 extend_bytes(out, b)?;
52 }
53
54 CborValue::String(s) => {
55 let bytes = s.as_bytes();
56 write_header(MT_STRING, len_as_u64(bytes.len())?, out)?;
57 extend_bytes(out, bytes)?;
58 }
59
60 CborValue::Array(arr) => {
61 let child_depth = descend(depth)?;
62 ensure_minimum_encoded_len(arr.len(), 1)?;
63 write_header(MT_ARRAY, len_as_u64(arr.len())?, out)?;
64 for v in arr {
65 encode_value(v, out, child_depth)?;
66 }
67 }
68
69 CborValue::Map(entries) => {
70 let child_depth = descend(depth)?;
71 ensure_minimum_encoded_len(entries.len(), 2)?;
72 let mut sorted: Vec<(&String, &CborValue)> =
76 entries.iter().map(|(key, value)| (key, value)).collect();
77 sorted.sort_by(|(ka, _), (kb, _)| {
78 ka.len()
79 .cmp(&kb.len())
80 .then_with(|| ka.as_bytes().cmp(kb.as_bytes()))
81 });
82
83 if sorted
84 .windows(2)
85 .any(|pair| pair[0].0.as_bytes() == pair[1].0.as_bytes())
86 {
87 return Err(CborError::DuplicateMapKey);
88 }
89
90 write_header(MT_MAP, len_as_u64(sorted.len())?, out)?;
91
92 for (k, v) in sorted {
93 let kb = k.as_bytes();
94 write_header(MT_STRING, len_as_u64(kb.len())?, out)?;
95 extend_bytes(out, kb)?;
96 encode_value(v, out, child_depth)?;
97 }
98 }
99 }
100 Ok(())
101}
102
103fn len_as_u64(len: usize) -> Result<u64, CborError> {
109 u64::try_from(len).map_err(|_| CborError::LengthTooLarge)
110}
111
112fn write_header(mt: u8, value: u64, out: &mut impl EncodingSink) -> Result<(), CborError> {
119 let be = value.to_be_bytes();
120 let head = mt << 5;
121 if value < 24 {
122 push_byte(out, head | be[7])?;
124 } else if value < 0x100 {
125 push_byte(out, head | 24)?;
126 extend_bytes(out, &be[7..8])?;
127 } else if value < 0x1_0000 {
128 push_byte(out, head | 25)?;
129 extend_bytes(out, &be[6..8])?;
130 } else if value < 0x1_0000_0000 {
131 push_byte(out, head | 26)?;
132 extend_bytes(out, &be[4..8])?;
133 } else {
134 push_byte(out, head | 27)?;
135 extend_bytes(out, &be)?;
136 }
137 Ok(())
138}
139
140trait EncodingSink {
143 fn append(&mut self, bytes: &[u8]) -> Result<(), CborError>;
144}
145
146impl EncodingSink for usize {
147 fn append(&mut self, bytes: &[u8]) -> Result<(), CborError> {
148 *self = checked_output_length(*self, bytes.len())?;
149 Ok(())
150 }
151}
152
153impl EncodingSink for Vec<u8> {
154 fn append(&mut self, bytes: &[u8]) -> Result<(), CborError> {
155 checked_output_length(self.len(), bytes.len())?;
156 self.extend_from_slice(bytes);
157 Ok(())
158 }
159}
160
161fn checked_output_length(length: usize, additional: usize) -> Result<usize, CborError> {
162 let next = length
163 .checked_add(additional)
164 .ok_or(CborError::OffsetOverflow)?;
165 if next > MAX_DAG_CBOR_INPUT_LEN {
166 return Err(CborError::OutputTooLarge);
167 }
168 Ok(next)
169}
170
171fn push_byte(out: &mut impl EncodingSink, byte: u8) -> Result<(), CborError> {
172 out.append(&[byte])
173}
174
175fn extend_bytes(out: &mut impl EncodingSink, bytes: &[u8]) -> Result<(), CborError> {
176 out.append(bytes)
177}
178
179fn descend(depth: usize) -> Result<usize, CborError> {
180 let next = depth.checked_add(1).ok_or(CborError::OffsetOverflow)?;
181 if next > MAX_NESTING_DEPTH {
182 return Err(CborError::DepthExceeded);
183 }
184 Ok(next)
185}
186
187fn ensure_minimum_encoded_len(count: usize, min_element_len: usize) -> Result<(), CborError> {
188 let minimum = count
189 .checked_mul(min_element_len)
190 .ok_or(CborError::OffsetOverflow)?;
191 if minimum > MAX_DAG_CBOR_INPUT_LEN {
192 return Err(CborError::OutputTooLarge);
193 }
194 Ok(())
195}