1use aws_smithy_types::{BigInteger, Blob, DateTime};
7
8macro_rules! delegate_method {
24 ($($(#[$meta:meta])* $wrapper_name:ident => $encoder_name:ident($($param_name:ident : $param_type:ty),*);)+) => {
25 $(
26 pub fn $wrapper_name(&mut self, $($param_name: $param_type),*) -> &mut Self {
27 self.encoder.$encoder_name($($param_name)*).expect(INFALLIBLE_WRITE);
28 self
29 }
30 )+
31 };
32}
33
34#[derive(Debug, Clone)]
35pub struct Encoder {
36 encoder: minicbor::Encoder<Vec<u8>>,
37}
38
39const INFALLIBLE_WRITE: &str = "write failed";
42
43impl Encoder {
44 pub fn new(writer: Vec<u8>) -> Self {
45 Self {
46 encoder: minicbor::Encoder::new(writer),
47 }
48 }
49
50 delegate_method! {
51 begin_map => begin_map();
54 begin_array => begin_array();
56 boolean => bool(x: bool);
58 byte => i8(x: i8);
60 short => i16(x: i16);
62 integer => i32(x: i32);
64 long => i64(x: i64);
66 float => f32(x: f32);
68 double => f64(x: f64);
70 null => null();
72 end => end();
74 }
75
76 const MAX_HEADER_LEN: usize = 9;
78
79 #[inline]
88 fn write_type_len(writer: &mut Vec<u8>, major: u8, len: usize) {
89 let mut buf = [0u8; Self::MAX_HEADER_LEN];
90 let n = match len {
91 0..=23 => {
92 buf[0] = major | len as u8;
93 1
94 }
95 24..=0xff => {
96 buf[0] = major | 24;
97 buf[1] = len as u8;
98 2
99 }
100 0x100..=0xffff => {
101 buf[0] = major | 25;
102 buf[1..3].copy_from_slice(&(len as u16).to_be_bytes());
103 3
104 }
105 0x1_0000..=0xffff_ffff => {
106 buf[0] = major | 26;
107 buf[1..5].copy_from_slice(&(len as u32).to_be_bytes());
108 5
109 }
110 _ => {
111 buf[0] = major | 27;
112 buf[1..9].copy_from_slice(&(len as u64).to_be_bytes());
113 9
114 }
115 };
116 writer.extend_from_slice(&buf[..n]);
117 }
118
119 pub fn str(&mut self, x: &str) -> &mut Self {
121 let writer = self.encoder.writer_mut();
122 let len = x.len();
123 writer.reserve(Self::MAX_HEADER_LEN + len);
124 Self::write_type_len(writer, 0x60, len);
125 writer.extend_from_slice(x.as_bytes());
126 self
127 }
128
129 pub fn big_integer(&mut self, value: &BigInteger) -> &mut Self {
136 use num_bigint::{BigInt, Sign};
137
138 let n: BigInt = value
139 .as_ref()
140 .parse()
141 .expect("BigInteger contains invalid value");
142 let (sign, magnitude) = n.to_bytes_be();
143
144 match sign {
145 Sign::Plus | Sign::NoSign => {
146 if magnitude.len() <= 8 {
148 let mut buf = [0u8; 8];
149 buf[8 - magnitude.len()..].copy_from_slice(&magnitude);
150 let val = u64::from_be_bytes(buf);
151 self.encoder.u64(val).expect(INFALLIBLE_WRITE);
152 } else {
153 self.encoder
154 .tag(minicbor::data::Tag::new(2))
155 .expect(INFALLIBLE_WRITE);
156 let stripped = strip_leading_zeroes(&magnitude);
158 self.encoder.bytes(stripped).expect(INFALLIBLE_WRITE);
159 }
160 }
161 Sign::Minus => {
162 let one = BigInt::from(1u8);
164 let n = (-n) - one;
165 let (_, n_bytes) = n.to_bytes_be();
166
167 if n_bytes.len() <= 8 {
169 let mut buf = [0u8; 8];
170 buf[8 - n_bytes.len()..].copy_from_slice(&n_bytes);
171 let val = u64::from_be_bytes(buf);
172 let neg = -1i128 - (val as i128);
176 let int_val = minicbor::data::Int::try_from(neg)
177 .expect("value fits in CBOR integer range");
178 self.encoder.int(int_val).expect(INFALLIBLE_WRITE);
179 } else {
180 self.encoder
181 .tag(minicbor::data::Tag::new(3))
182 .expect(INFALLIBLE_WRITE);
183 let stripped = strip_leading_zeroes(&n_bytes);
184 self.encoder.bytes(stripped).expect(INFALLIBLE_WRITE);
185 }
186 }
187 }
188 self
189 }
190
191 pub fn blob_bytes(&mut self, data: &[u8]) -> &mut Self {
198 let writer = self.encoder.writer_mut();
199 let len = data.len();
200 writer.reserve(Self::MAX_HEADER_LEN + len);
201 Self::write_type_len(writer, 0x40, len);
202 writer.extend_from_slice(data);
203 self
204 }
205
206 pub fn blob(&mut self, x: &Blob) -> &mut Self {
208 self.blob_bytes(x.as_ref())
209 }
210
211 pub fn array(&mut self, len: usize) -> &mut Self {
213 Self::write_type_len(self.encoder.writer_mut(), 0x80, len);
214 self
215 }
216
217 pub fn map(&mut self, len: usize) -> &mut Self {
223 Self::write_type_len(self.encoder.writer_mut(), 0xa0, len);
224 self
225 }
226
227 pub fn timestamp(&mut self, x: &DateTime) -> &mut Self {
228 self.encoder
229 .tag(minicbor::data::Tag::from(
230 minicbor::data::IanaTag::Timestamp,
231 ))
232 .expect(INFALLIBLE_WRITE);
233 self.encoder.f64(x.as_secs_f64()).expect(INFALLIBLE_WRITE);
234 self
235 }
236
237 pub fn into_writer(self) -> Vec<u8> {
238 self.encoder.into_writer()
239 }
240}
241
242fn strip_leading_zeroes(bytes: &[u8]) -> &[u8] {
244 let start = bytes.iter().position(|&b| b != 0).unwrap_or(bytes.len());
245 &bytes[start..]
246}
247
248#[cfg(test)]
249mod tests {
250 use super::*;
251 use aws_smithy_types::Blob;
252
253 #[test]
255 fn str_matches_minicbor() {
256 let cases = [
257 "", "a", "hello world!! test str", "this is exactly 24 char", &"x".repeat(0xff), &"y".repeat(0x100), &"z".repeat(0x1_0000), ];
265 for input in &cases {
266 let mut ours = Encoder::new(Vec::new());
267 ours.str(input);
268
269 let mut theirs = minicbor::Encoder::new(Vec::new());
270 theirs.str(input).unwrap();
271
272 assert_eq!(
273 ours.into_writer(),
274 theirs.into_writer(),
275 "str mismatch for input len={}",
276 input.len()
277 );
278 }
279 }
280
281 #[test]
283 fn blob_matches_minicbor() {
284 let cases: Vec<Vec<u8>> = vec![
285 vec![], vec![0x42], vec![0xAB; 23], vec![0xCD; 24], vec![0xEF; 0xff], vec![0x01; 0x100], vec![0x02; 0x1_0000], ];
293 for input in &cases {
294 let mut ours = Encoder::new(Vec::new());
295 ours.blob(&Blob::new(input.clone()));
296
297 let mut theirs = minicbor::Encoder::new(Vec::new());
298 theirs.bytes(input).unwrap();
299
300 assert_eq!(
301 ours.into_writer(),
302 theirs.into_writer(),
303 "blob mismatch for input len={}",
304 input.len()
305 );
306 }
307 }
308
309 #[test]
311 fn str_chained_matches_minicbor() {
312 let mut ours = Encoder::new(Vec::new());
313 ours.str("key1").str("value1").str("key2").str("value2");
314
315 let mut theirs = minicbor::Encoder::new(Vec::new());
316 theirs
317 .str("key1")
318 .unwrap()
319 .str("value1")
320 .unwrap()
321 .str("key2")
322 .unwrap()
323 .str("value2")
324 .unwrap();
325
326 assert_eq!(ours.into_writer(), theirs.into_writer());
327 }
328
329 #[test]
331 fn str_inside_map_matches_minicbor() {
332 let mut ours = Encoder::new(Vec::new());
333 ours.begin_map().str("TableName").str("my-table").end();
334
335 let mut theirs = minicbor::Encoder::new(Vec::new());
336 theirs
337 .begin_map()
338 .unwrap()
339 .str("TableName")
340 .unwrap()
341 .str("my-table")
342 .unwrap()
343 .end()
344 .unwrap();
345
346 assert_eq!(ours.into_writer(), theirs.into_writer());
347 }
348
349 #[test]
351 fn str_utf8_matches_minicbor() {
352 let cases = [
353 "café", "日本語", "🦀🔥", "mixed: aé日🦀", ];
358 for input in &cases {
359 let mut ours = Encoder::new(Vec::new());
360 ours.str(input);
361
362 let mut theirs = minicbor::Encoder::new(Vec::new());
363 theirs.str(input).unwrap();
364
365 assert_eq!(
366 ours.into_writer(),
367 theirs.into_writer(),
368 "str UTF-8 mismatch for {:?}",
369 input
370 );
371 }
372 }
373
374 #[test]
375 fn preferred_serialization_small_positive() {
376 let mut encoder = Encoder::new(Vec::new());
378 encoder.big_integer(&"0".parse().unwrap());
379 assert_eq!(encoder.into_writer(), vec![0x00]); let mut encoder = Encoder::new(Vec::new());
382 encoder.big_integer(&"23".parse().unwrap());
383 assert_eq!(encoder.into_writer(), vec![0x17]); let mut encoder = Encoder::new(Vec::new());
386 encoder.big_integer(&"256".parse().unwrap());
387 assert_eq!(encoder.into_writer(), vec![0x19, 0x01, 0x00]);
389 }
390
391 #[test]
392 fn preferred_serialization_small_negative() {
393 let mut encoder = Encoder::new(Vec::new());
396 encoder.big_integer(&"-1".parse().unwrap());
397 assert_eq!(encoder.into_writer(), vec![0x20]); let mut encoder = Encoder::new(Vec::new());
400 encoder.big_integer(&"-42".parse().unwrap());
401 assert_eq!(encoder.into_writer(), vec![0x38, 0x29]);
403 }
404
405 #[test]
406 fn preferred_serialization_u64_max() {
407 let mut encoder = Encoder::new(Vec::new());
409 encoder.big_integer(&"18446744073709551615".parse().unwrap());
410 let bytes = encoder.into_writer();
411 assert_eq!(bytes[0], 0x1b); assert_eq!(
413 &bytes[1..],
414 &[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]
415 );
416 }
417
418 #[test]
419 fn tag2_for_values_exceeding_u64() {
420 let mut encoder = Encoder::new(Vec::new());
423 encoder.big_integer(&"18446744073709551616".parse().unwrap());
424 let bytes = encoder.into_writer();
425 assert_eq!(
426 bytes,
427 vec![0xc2, 0x49, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
428 );
429 }
430
431 #[test]
432 fn tag3_negative_bignum_rfc8949_example() {
433 let mut encoder = Encoder::new(Vec::new());
436 encoder.big_integer(&"-18446744073709551617".parse().unwrap());
437 let bytes = encoder.into_writer();
438 assert_eq!(
439 bytes,
440 vec![0xc3, 0x49, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
441 );
442 }
443
444 #[test]
445 fn negative_at_major_type_1_boundary() {
446 let mut encoder = Encoder::new(Vec::new());
449 encoder.big_integer(&"-18446744073709551616".parse().unwrap());
450 let bytes = encoder.into_writer();
451 assert_eq!(bytes[0], 0x3b); assert_eq!(
453 &bytes[1..],
454 &[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]
455 );
456 }
457
458 #[test]
459 fn tag2_strips_leading_zeroes() {
460 let mut encoder = Encoder::new(Vec::new());
462 let large = "123456789012345678901234567890";
463 encoder.big_integer(&large.parse().unwrap());
464 let bytes = encoder.into_writer();
465 assert_eq!(bytes[0], 0xc2); let payload_start = if bytes[1] < 0x58 { 2 } else { 3 };
469 assert_ne!(bytes[payload_start], 0x00);
470 }
471}