Coverage Report

Created: 2026-08-14 06:55

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/OpenSK/libraries/cbor/src/writer.rs
Line
Count
Source
1
// Copyright 2019 Google LLC
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//      http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
15
//! Functionality for serializing CBOR values into bytes.
16
17
use super::values::{Constants, Value, ValueImpl};
18
use alloc::vec::Vec;
19
20
/// Possible errors from a serialization operation.
21
#[derive(Debug, PartialEq)]
22
pub enum EncoderError {
23
    TooMuchNesting,
24
    DuplicateMapKey,
25
}
26
27
/// Convert a [`Value`] to serialized CBOR data, consuming it along the way and appending to the provided vector.
28
/// Maximum level of nesting supported is 127; more deeply nested structures will fail with
29
/// [`EncoderError::TooMuchNesting`].
30
1.60k
pub fn write(value: Value, encoded_cbor: &mut Vec<u8>) -> Result<(), EncoderError> {
31
1.60k
    write_nested(value, encoded_cbor, Some(i8::MAX))
32
1.60k
}
33
34
/// Convert a [`Value`] to serialized CBOR data, consuming it along the way and appending to the provided vector.  If
35
/// `max_nest` is `Some(max)`, then nested structures are only supported up to the given limit (returning
36
/// [`DecoderError::TooMuchNesting`] if the limit is hit).
37
11.6k
pub fn write_nested(
38
11.6k
    value: Value,
39
11.6k
    encoded_cbor: &mut Vec<u8>,
40
11.6k
    max_nest: Option<i8>,
41
11.6k
) -> Result<(), EncoderError> {
42
11.6k
    let mut writer = Writer::new(encoded_cbor);
43
11.6k
    writer.encode_cbor(value, max_nest)
44
11.6k
}
45
46
struct Writer<'a> {
47
    encoded_cbor: &'a mut Vec<u8>,
48
}
49
50
impl<'a> Writer<'a> {
51
11.6k
    pub fn new(encoded_cbor: &mut Vec<u8>) -> Writer<'_> {
52
11.6k
        Writer { encoded_cbor }
53
11.6k
    }
54
55
6.47M
    fn encode_cbor(
56
6.47M
        &mut self,
57
6.47M
        value: Value,
58
6.47M
        remaining_depth: Option<i8>,
59
6.47M
    ) -> Result<(), EncoderError> {
60
6.47M
        if remaining_depth.is_some_and(|d| d < 0) {
61
0
            return Err(EncoderError::TooMuchNesting);
62
6.47M
        }
63
6.47M
        let type_label = value.0.type_label();
64
6.47M
        match value.0 {
65
1.18M
            ValueImpl::Unsigned(unsigned) => self.start_item(type_label, unsigned),
66
2.11M
            ValueImpl::Negative(negative) => self.start_item(type_label, -(negative + 1) as u64),
67
812k
            ValueImpl::ByteString(byte_string) => {
68
812k
                self.start_item(type_label, byte_string.len() as u64);
69
812k
                self.encoded_cbor.extend(byte_string);
70
812k
            }
71
52.7k
            ValueImpl::TextString(text_string) => {
72
52.7k
                self.start_item(type_label, text_string.len() as u64);
73
52.7k
                self.encoded_cbor.extend(text_string.into_bytes());
74
52.7k
            }
75
497k
            ValueImpl::Array(array) => {
76
497k
                self.start_item(type_label, array.len() as u64);
77
4.33M
                for el in array {
78
3.83M
                    self.encode_cbor(el, remaining_depth.map(|d| d - 1))?;
79
                }
80
            }
81
325k
            ValueImpl::Map(map) => {
82
325k
                self.start_item(type_label, map.len() as u64);
83
909k
                for (k, v) in map {
84
584k
                    self.encode_cbor(k, remaining_depth.map(|d| d - 1))?;
85
584k
                    self.encode_cbor(v, remaining_depth.map(|d| d - 1))?;
86
                }
87
            }
88
1.45M
            ValueImpl::Tag(tag, inner_value) => {
89
1.45M
                self.start_item(type_label, tag);
90
1.45M
                self.encode_cbor(*inner_value, remaining_depth.map(|d| d - 1))?;
91
            }
92
29.0k
            ValueImpl::Simple(simple_value) => self.start_item(type_label, simple_value as u64),
93
        }
94
6.47M
        Ok(())
95
6.47M
    }
96
97
6.47M
    fn start_item(&mut self, type_label: u8, size: u64) {
98
6.47M
        let (mut first_byte, shift) = match size {
99
6.47M
            0..=23 => (size as u8, 0),
100
1.22M
            24..=0xFF => (Constants::ADDITIONAL_INFORMATION_1_BYTE, 1),
101
382k
            0x100..=0xFFFF => (Constants::ADDITIONAL_INFORMATION_2_BYTES, 2),
102
213k
            0x10000..=0xFFFF_FFFF => (Constants::ADDITIONAL_INFORMATION_4_BYTES, 4),
103
30.6k
            _ => (Constants::ADDITIONAL_INFORMATION_8_BYTES, 8),
104
        };
105
6.47M
        first_byte |= type_label << Constants::MAJOR_TYPE_BIT_SHIFT;
106
6.47M
        self.encoded_cbor.push(first_byte);
107
108
6.47M
        for i in (0..shift).rev() {
109
2.15M
            self.encoded_cbor.push((size >> (i * 8)) as u8);
110
2.15M
        }
111
6.47M
    }
112
}
113
114
#[cfg(test)]
115
mod test {
116
    use super::*;
117
    use crate::{
118
        cbor_array, cbor_array_vec, cbor_bytes, cbor_false, cbor_int, cbor_map, cbor_null,
119
        cbor_tagged, cbor_text, cbor_true, cbor_undefined,
120
    };
121
    use alloc::vec;
122
123
    fn write_return(value: Value) -> Option<Vec<u8>> {
124
        let mut encoded_cbor = Vec::new();
125
        if write(value, &mut encoded_cbor).is_ok() {
126
            Some(encoded_cbor)
127
        } else {
128
            None
129
        }
130
    }
131
132
    #[test]
133
    fn test_write_unsigned() {
134
        let cases = vec![
135
            (0, vec![0x00]),
136
            (1, vec![0x01]),
137
            (10, vec![0x0A]),
138
            (23, vec![0x17]),
139
            (24, vec![0x18, 0x18]),
140
            (25, vec![0x18, 0x19]),
141
            (100, vec![0x18, 0x64]),
142
            (1000, vec![0x19, 0x03, 0xE8]),
143
            (1000000, vec![0x1A, 0x00, 0x0F, 0x42, 0x40]),
144
            (0xFFFFFFFF, vec![0x1A, 0xFF, 0xFF, 0xFF, 0xFF]),
145
            (
146
                0x100000000,
147
                vec![0x1B, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00],
148
            ),
149
            (
150
                core::i64::MAX,
151
                vec![0x1B, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF],
152
            ),
153
        ];
154
        for (unsigned, correct_cbor) in cases {
155
            assert_eq!(write_return(cbor_int!(unsigned)), Some(correct_cbor));
156
        }
157
    }
158
159
    #[test]
160
    fn test_write_negative() {
161
        let cases = vec![
162
            (-1, vec![0x20]),
163
            (-10, vec![0x29]),
164
            (-23, vec![0x36]),
165
            (-24, vec![0x37]),
166
            (-25, vec![0x38, 0x18]),
167
            (-100, vec![0x38, 0x63]),
168
            (-1000, vec![0x39, 0x03, 0xE7]),
169
            (-4294967296, vec![0x3A, 0xFF, 0xFF, 0xFF, 0xFF]),
170
            (
171
                -4294967297,
172
                vec![0x3B, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00],
173
            ),
174
            (
175
                core::i64::MIN,
176
                vec![0x3B, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF],
177
            ),
178
        ];
179
        for (negative, correct_cbor) in cases {
180
            assert_eq!(write_return(cbor_int!(negative)), Some(correct_cbor));
181
        }
182
    }
183
184
    #[test]
185
    fn test_write_byte_string() {
186
        let cases = vec![
187
            (vec![], vec![0x40]),
188
            (
189
                vec![0x01, 0x02, 0x03, 0x04],
190
                vec![0x44, 0x01, 0x02, 0x03, 0x04],
191
            ),
192
        ];
193
        for (byte_string, correct_cbor) in cases {
194
            assert_eq!(write_return(cbor_bytes!(byte_string)), Some(correct_cbor));
195
        }
196
    }
197
198
    #[test]
199
    fn test_write_text_string() {
200
        let unicode_3byte = vec![0xE6, 0xB0, 0xB4];
201
        let cases = vec![
202
            ("", vec![0x60]),
203
            ("a", vec![0x61, 0x61]),
204
            ("IETF", vec![0x64, 0x49, 0x45, 0x54, 0x46]),
205
            ("\"\\", vec![0x62, 0x22, 0x5C]),
206
            ("ü", vec![0x62, 0xC3, 0xBC]),
207
            (
208
                core::str::from_utf8(&unicode_3byte).unwrap(),
209
                vec![0x63, 0xE6, 0xB0, 0xB4],
210
            ),
211
            ("𐅑", vec![0x64, 0xF0, 0x90, 0x85, 0x91]),
212
        ];
213
        for (text_string, correct_cbor) in cases {
214
            assert_eq!(write_return(cbor_text!(text_string)), Some(correct_cbor));
215
        }
216
    }
217
218
    #[test]
219
    fn test_write_array() {
220
        let value_vec: Vec<_> = (1..26).collect();
221
        let expected_cbor = vec![
222
            0x98, 0x19, // array of 25 elements
223
            0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
224
            0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x18, 0x18, 0x19,
225
        ];
226
        assert_eq!(
227
            write_return(cbor_array_vec!(value_vec)),
228
            Some(expected_cbor)
229
        );
230
    }
231
232
    #[test]
233
    fn test_write_map() {
234
        let value_map = cbor_map! {
235
            0 => "a",
236
            23 => "b",
237
            24 => "c",
238
            core::u8::MAX as i64 => "d",
239
            256 => "e",
240
            core::u16::MAX as i64 => "f",
241
            65536 => "g",
242
            core::u32::MAX as i64 => "h",
243
            4294967296_i64 => "i",
244
            core::i64::MAX => "j",
245
            -1 => "k",
246
            -24 => "l",
247
            -25 => "m",
248
            -256 => "n",
249
            -257 => "o",
250
            -65537 => "p",
251
            -4294967296_i64 => "q",
252
            -4294967297_i64 => "r",
253
            core::i64::MIN => "s",
254
            b"a" => 2,
255
            b"bar" => 3,
256
            b"foo" => 4,
257
            "" => ".",
258
            "e" => "E",
259
            "aa" => "AA",
260
        };
261
        let expected_cbor = vec![
262
            0xb8, 0x19, // map of 25 pairs:
263
            0x00, // key 0
264
            0x61, 0x61, // value "a"
265
            0x17, // key 23
266
            0x61, 0x62, // value "b"
267
            0x18, 0x18, // key 24
268
            0x61, 0x63, // value "c"
269
            0x18, 0xFF, // key 255
270
            0x61, 0x64, // value "d"
271
            0x19, 0x01, 0x00, // key 256
272
            0x61, 0x65, // value "e"
273
            0x19, 0xFF, 0xFF, // key 65535
274
            0x61, 0x66, // value "f"
275
            0x1A, 0x00, 0x01, 0x00, 0x00, // key 65536
276
            0x61, 0x67, // value "g"
277
            0x1A, 0xFF, 0xFF, 0xFF, 0xFF, // key 4294967295
278
            0x61, 0x68, // value "h"
279
            // key 4294967296
280
            0x1B, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x61, 0x69, //  value "i"
281
            // key INT64_MAX
282
            0x1b, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x61, 0x6a, //  value "j"
283
            0x20, // key -1
284
            0x61, 0x6b, // value "k"
285
            0x37, // key -24
286
            0x61, 0x6c, // value "l"
287
            0x38, 0x18, // key -25
288
            0x61, 0x6d, // value "m"
289
            0x38, 0xFF, // key -256
290
            0x61, 0x6e, // value "n"
291
            0x39, 0x01, 0x00, // key -257
292
            0x61, 0x6f, // value "o"
293
            0x3A, 0x00, 0x01, 0x00, 0x00, // key -65537
294
            0x61, 0x70, // value "p"
295
            0x3A, 0xFF, 0xFF, 0xFF, 0xFF, // key -4294967296
296
            0x61, 0x71, // value "q"
297
            // key -4294967297
298
            0x3B, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x61, 0x72, //  value "r"
299
            // key INT64_MIN
300
            0x3b, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x61, 0x73, //  value "s"
301
            0x41, b'a', // byte string "a"
302
            0x02, 0x43, b'b', b'a', b'r', // byte string "bar"
303
            0x03, 0x43, b'f', b'o', b'o', // byte string "foo"
304
            0x04, 0x60, // key ""
305
            0x61, 0x2e, // value "."
306
            0x61, 0x65, // key "e"
307
            0x61, 0x45, // value "E"
308
            0x62, 0x61, 0x61, // key "aa"
309
            0x62, 0x41, 0x41, // value "AA"
310
        ];
311
        assert_eq!(write_return(value_map), Some(expected_cbor));
312
    }
313
314
    #[test]
315
    fn test_write_map_sorted() {
316
        let sorted_map = cbor_map! {
317
            0 => "a",
318
            1 => "b",
319
            -1 => "c",
320
            -2 => "d",
321
            b"a" => "e",
322
            b"b" => "f",
323
            "" => "g",
324
            "c" => "h",
325
        };
326
        let unsorted_map = cbor_map! {
327
            1 => "b",
328
            -2 => "d",
329
            b"b" => "f",
330
            "c" => "h",
331
            "" => "g",
332
            b"a" => "e",
333
            -1 => "c",
334
            0 => "a",
335
        };
336
        assert_eq!(write_return(sorted_map), write_return(unsorted_map));
337
    }
338
339
    #[test]
340
    fn test_write_map_with_array() {
341
        let value_map = cbor_map! {
342
            "a" => 1,
343
            "b" => cbor_array![2, 3],
344
        };
345
        let expected_cbor = vec![
346
            0xa2, // map of 2 pairs
347
            0x61, 0x61, // "a"
348
            0x01, 0x61, 0x62, // "b"
349
            0x82, // array with 2 elements
350
            0x02, 0x03,
351
        ];
352
        assert_eq!(write_return(value_map), Some(expected_cbor));
353
    }
354
355
    #[test]
356
    fn test_write_nested_map() {
357
        let value_map = cbor_map! {
358
            "a" => 1,
359
            "b" => cbor_map! {
360
                "c" => 2,
361
                "d" => 3,
362
            },
363
        };
364
        let expected_cbor = vec![
365
            0xa2, // map of 2 pairs
366
            0x61, 0x61, // "a"
367
            0x01, 0x61, 0x62, // "b"
368
            0xa2, // map of 2 pairs
369
            0x61, 0x63, // "c"
370
            0x02, 0x61, 0x64, // "d"
371
            0x03,
372
        ];
373
        assert_eq!(write_return(value_map), Some(expected_cbor));
374
    }
375
376
    #[test]
377
    fn test_write_tagged() {
378
        let cases = vec![
379
            (cbor_tagged!(6, cbor_int!(0x42)), vec![0xc6, 0x18, 0x42]),
380
            (cbor_tagged!(1, cbor_true!()), vec![0xc1, 0xf5]),
381
            (
382
                cbor_tagged!(
383
                    1000,
384
                    cbor_map! {
385
                        "a" => 1,
386
                        "b" => cbor_array![2, 3],
387
                    }
388
                ),
389
                vec![
390
                    0xd9, 0x03, 0xe8, 0xa2, // map of 2 pairs
391
                    0x61, 0x61, // "a"
392
                    0x01, 0x61, 0x62, // "b"
393
                    0x82, // array with 2 elements
394
                    0x02, 0x03,
395
                ],
396
            ),
397
        ];
398
        for (value, correct_cbor) in cases {
399
            assert_eq!(write_return(value), Some(correct_cbor));
400
        }
401
    }
402
403
    #[test]
404
    fn test_write_simple() {
405
        let cases = vec![
406
            (cbor_false!(), vec![0xF4]),
407
            (cbor_true!(), vec![0xF5]),
408
            (cbor_null!(), vec![0xF6]),
409
            (cbor_undefined!(), vec![0xF7]),
410
        ];
411
        for (value, correct_cbor) in cases {
412
            assert_eq!(write_return(value), Some(correct_cbor));
413
        }
414
    }
415
416
    #[test]
417
    fn test_write_single_levels() {
418
        let simple_array: Value = cbor_array![2];
419
        let simple_map: Value = cbor_map! {"b" => 3};
420
        let positive_cases = vec![
421
            (cbor_int!(1), 0),
422
            (cbor_bytes!(vec![0x01, 0x02, 0x03, 0x04]), 0),
423
            (cbor_text!("a"), 0),
424
            (cbor_array![], 0),
425
            (cbor_map! {}, 0),
426
            (simple_array.clone(), 1),
427
            (simple_map.clone(), 1),
428
        ];
429
        let negative_cases = vec![(simple_array.clone(), 0), (simple_map.clone(), 0)];
430
        for (value, level) in positive_cases {
431
            let mut buf = Vec::new();
432
            let mut writer = Writer::new(&mut buf);
433
            assert!(writer.encode_cbor(value, Some(level)).is_ok());
434
        }
435
        for (value, level) in negative_cases {
436
            let mut buf = Vec::new();
437
            let mut writer = Writer::new(&mut buf);
438
            assert!(!writer.encode_cbor(value, Some(level)).is_ok());
439
        }
440
    }
441
442
    #[test]
443
    fn test_write_nested_map_levels() {
444
        let cbor_map: Value = cbor_map! {
445
            "a" => 1,
446
            "b" => cbor_map! {
447
                "c" => 2,
448
                "d" => 3,
449
            },
450
        };
451
452
        let mut buf = Vec::new();
453
        let mut writer = Writer::new(&mut buf);
454
        assert!(writer.encode_cbor(cbor_map.clone(), Some(2)).is_ok());
455
        assert!(writer.encode_cbor(cbor_map.clone(), None).is_ok());
456
        writer = Writer::new(&mut buf);
457
        assert!(writer.encode_cbor(cbor_map, Some(1)).is_err());
458
    }
459
460
    #[test]
461
    fn test_write_unbalanced_nested_containers() {
462
        let cbor_array: Value = cbor_array![
463
            1,
464
            2,
465
            3,
466
            cbor_map! {
467
                "a" => 1,
468
                "b" => cbor_map! {
469
                    "c" => 2,
470
                    "d" => 3,
471
                },
472
            },
473
        ];
474
475
        let mut buf = Vec::new();
476
        let mut writer = Writer::new(&mut buf);
477
        assert!(writer.encode_cbor(cbor_array.clone(), Some(3)).is_ok());
478
        writer = Writer::new(&mut buf);
479
        assert!(writer.encode_cbor(cbor_array, Some(2)).is_err());
480
    }
481
482
    #[test]
483
    fn test_write_overly_nested() {
484
        let cbor_map: Value = cbor_map! {
485
            "a" => 1,
486
            "b" => cbor_map! {
487
                "c" => 2,
488
                "d" => 3,
489
                "h" => cbor_map! {
490
                    "e" => 4,
491
                    "f" => 5,
492
                    "g" => cbor_array![
493
                        6,
494
                        7,
495
                        cbor_array![
496
                            8
497
                        ]
498
                    ],
499
                },
500
            },
501
        };
502
503
        let mut buf = Vec::new();
504
        let mut writer = Writer::new(&mut buf);
505
        assert!(writer.encode_cbor(cbor_map.clone(), Some(5)).is_ok());
506
        assert!(writer.encode_cbor(cbor_map.clone(), None).is_ok());
507
        writer = Writer::new(&mut buf);
508
        assert!(writer.encode_cbor(cbor_map, Some(4)).is_err());
509
    }
510
}