Coverage Report

Created: 2024-06-18 06:18

/src/OpenSK/libraries/cbor/src/macros.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
//! Convenience macros for working with CBOR values.
16
17
use crate::values::Value;
18
use alloc::vec;
19
use core::cmp::Ordering;
20
use core::iter::Peekable;
21
22
/// This macro generates code to extract multiple values from a `Vec<(Value, Value)>` at once
23
/// in an optimized manner, consuming the input vector.
24
///
25
/// It takes as input a `Vec` as well as a list of identifiers and keys, and generates code
26
/// that assigns the corresponding values to new variables using the given identifiers. Each of
27
/// these variables has type `Option<Value>`, to account for the case where keys aren't found.
28
///
29
/// **Important:** Keys passed to the `destructure_cbor_map!` macro **must be sorted** in increasing
30
/// order. If not, the algorithm can yield incorrect results, such a assigning `None` to a variable
31
/// even if the corresponding key existed in the map. **No runtime checks** are made for this in the
32
/// `destructure_cbor_map!` macro, in order to avoid overhead at runtime. However, assertions that
33
/// keys are sorted are added in `cfg(test)` mode, so that unit tests can verify ahead of time that
34
/// the keys are indeed sorted. This macro is therefore **not suitable for dynamic keys** that can
35
/// change at runtime.
36
///
37
/// Example usage:
38
///
39
/// ```rust
40
/// # extern crate alloc;
41
/// # use sk_cbor::destructure_cbor_map;
42
/// #
43
/// # fn main() {
44
/// #     let map = alloc::vec::Vec::new();
45
/// destructure_cbor_map! {
46
///     let {
47
///         1 => x,
48
///         "key" => y,
49
///     } = map;
50
/// }
51
/// # }
52
/// ```
53
#[macro_export]
54
macro_rules! destructure_cbor_map {
55
    ( let { $( $key:expr => $variable:ident, )+ } = $map:expr; ) => {
56
        // A pre-requisite for this algorithm to work is that the keys to extract from the map are
57
        // sorted - the behavior is unspecified if the keys are not sorted.
58
        // Therefore, in test mode we add assertions that the keys are indeed sorted.
59
        #[cfg(test)]
60
        $crate::assert_sorted_keys!($( $key, )+);
61
62
        use $crate::values::{IntoCborValue, Value};
63
        use $crate::macros::destructure_cbor_map_peek_value;
64
65
        // This algorithm first converts the map into a peekable iterator - whose items are sorted
66
        // in strictly increasing order of keys. Then, the repeated calls to the "peek value"
67
        // helper function will consume this iterator and yield values (or `None`) when reaching
68
        // the keys to extract.
69
        //
70
        // This is where the pre-requisite that keys to extract are sorted is important: the
71
        // algorithm does a single linear scan over the iterator and therefore keys to extract have
72
        // to come in the same order (i.e. sorted).
73
        let mut it = $map.into_iter().peekable();
74
        $(
75
        let $variable: Option<Value> = destructure_cbor_map_peek_value(&mut it, $key.into_cbor_value());
76
        )+
77
    };
78
}
79
80
/// This function is an internal detail of the `destructure_cbor_map!` macro, but has public
81
/// visibility so that users of the macro can use it.
82
///
83
/// Given a peekable iterator of key-value pairs sorted in strictly increasing key order and a
84
/// needle key, this function consumes all items whose key compares less than or equal to the
85
/// needle, and returns `Some(value)` if the needle was present as the key in the iterator and
86
/// `None` otherwise.
87
///
88
/// The logic is separated into its own function to reduce binary size, as otherwise the logic
89
/// would be inlined for every use case. As of June 2020, this saves ~40KB of binary size for the
90
/// CTAP2 application of OpenSK.
91
146k
pub fn destructure_cbor_map_peek_value(
92
146k
    it: &mut Peekable<vec::IntoIter<(Value, Value)>>,
93
146k
    needle: Value,
94
146k
) -> Option<Value> {
95
149k
    loop {
96
149k
        match it.peek() {
97
53.0k
            None => return None,
98
96.1k
            Some(item) => {
99
96.1k
                let key: &Value = &item.0;
100
96.1k
                match key.cmp(&needle) {
101
2.87k
                    Ordering::Less => {
102
2.87k
                        it.next();
103
2.87k
                    }
104
                    Ordering::Equal => {
105
52.8k
                        let value: Value = it.next().unwrap().1;
106
52.8k
                        return Some(value);
107
                    }
108
40.3k
                    Ordering::Greater => return None,
109
                }
110
            }
111
        }
112
    }
113
146k
}
114
115
/// Assert that the keys in a vector of key-value pairs are in canonical order.
116
#[macro_export]
117
macro_rules! assert_sorted_keys {
118
    // Last key
119
    ( $key:expr, ) => {
120
    };
121
122
    ( $key1:expr, $key2:expr, $( $keys:expr, )* ) => {
123
        {
124
            use $crate::values::{IntoCborValue, Value};
125
            let k1: Value = $key1.into_cbor_value();
126
            let k2: Value = $key2.into_cbor_value();
127
            assert!(
128
                k1 < k2,
129
                "{:?} < {:?} failed. The destructure_cbor_map! macro requires keys in sorted order.",
130
                k1,
131
                k2,
132
            );
133
        }
134
        $crate::assert_sorted_keys!($key2, $( $keys, )*);
135
    };
136
}
137
138
/// Creates a CBOR Value of type Map with the specified key-value pairs.
139
///
140
/// Keys and values are expressions and converted into CBOR Keys and Values.
141
/// The syntax for these pairs is `key_expression => value_expression,`.
142
/// Keys do not have to be sorted.
143
///
144
/// # Panics
145
///
146
/// You may not call this function with identical keys in its argument.
147
///
148
/// Example usage:
149
///
150
/// ```rust
151
/// # extern crate alloc;
152
/// # use sk_cbor::cbor_map;
153
/// let map = cbor_map! {
154
///   0x01 => false,
155
///   "02" => -3,
156
/// };
157
/// ```
158
#[macro_export]
159
macro_rules! cbor_map {
160
    // trailing comma case
161
    ( $( $key:expr => $value:expr, )+ ) => {
162
        cbor_map! ( $($key => $value),+ )
163
    };
164
165
    ( $( $key:expr => $value:expr ),* ) => {
166
        {
167
            // The import is unused if the list is empty.
168
            #[allow(unused_imports)]
169
            use $crate::values::IntoCborValue;
170
            let mut _map = ::alloc::vec::Vec::new();
171
            $(
172
                _map.push(($key.into_cbor_value(), $value.into_cbor_value()));
173
            )*
174
            $crate::values::Value::map(_map)
175
        }
176
    };
177
}
178
179
/// Creates a CBOR Value of type Map with key-value pairs where values can be Options.
180
///
181
/// Keys and values are expressions and converted into CBOR Keys and Value Options.
182
/// The map entry is included iff the Value is not an Option or Option is Some.
183
/// The syntax for these pairs is `key_expression => value_expression,`.
184
/// Duplicate keys will lead to invalid CBOR, i.e. writing these values fails.
185
/// Keys do not have to be sorted.
186
///
187
/// Example usage:
188
///
189
/// ```rust
190
/// # extern crate alloc;
191
/// # use sk_cbor::cbor_map_options;
192
/// let missing_value: Option<bool> = None;
193
/// let map = cbor_map_options! {
194
///   0x01 => Some(false),
195
///   "02" => -3,
196
///   "not in map" => missing_value,
197
/// };
198
/// ```
199
#[macro_export]
200
macro_rules! cbor_map_options {
201
    // trailing comma case
202
    ( $( $key:expr => $value:expr, )+ ) => {
203
        cbor_map_options! ( $($key => $value),+ )
204
    };
205
206
    ( $( $key:expr => $value:expr ),* ) => {
207
        {
208
            // The import is unused if the list is empty.
209
            #[allow(unused_imports)]
210
            use $crate::values::{IntoCborValue, IntoCborValueOption};
211
            let mut _map = ::alloc::vec::Vec::<(_, $crate::values::Value)>::new();
212
            $(
213
            {
214
                let opt: Option<$crate::values::Value> = $value.into_cbor_value_option();
215
                if let Some(val) = opt {
216
                    _map.push(($key.into_cbor_value(), val));
217
                }
218
            }
219
            )*
220
            $crate::values::Value::map(_map)
221
        }
222
    };
223
}
224
225
/// Creates a CBOR Value of type Map from a Vec<(Value, Value)>.
226
#[macro_export]
227
macro_rules! cbor_map_collection {
228
    ( $tree:expr ) => {{
229
        $crate::values::Value::map($tree)
230
    }};
231
}
232
233
/// Creates a CBOR Value of type Array with the given elements.
234
///
235
/// Elements are expressions and converted into CBOR Values. Elements are comma-separated.
236
///
237
/// Example usage:
238
///
239
/// ```rust
240
/// # extern crate alloc;
241
/// # use sk_cbor::cbor_array;
242
/// let array = cbor_array![1, "2"];
243
/// ```
244
#[macro_export]
245
macro_rules! cbor_array {
246
    // trailing comma case
247
    ( $( $value:expr, )+ ) => {
248
        cbor_array! ( $($value),+ )
249
    };
250
251
    ( $( $value:expr ),* ) => {
252
        {
253
            // The import is unused if the list is empty.
254
            #[allow(unused_imports)]
255
            use $crate::values::IntoCborValue;
256
            $crate::values::Value::array(vec![ $( $value.into_cbor_value(), )* ])
257
        }
258
    };
259
}
260
261
/// Creates a CBOR Value of type Array from a Vec<Value>.
262
#[macro_export]
263
macro_rules! cbor_array_vec {
264
    ( $vec:expr ) => {{
265
        use $crate::values::IntoCborValue;
266
        $crate::values::Value::array($vec.into_iter().map(|x| x.into_cbor_value()).collect())
267
    }};
268
}
269
270
/// Creates a CBOR Value of type Simple with value true.
271
#[macro_export]
272
macro_rules! cbor_true {
273
    ( ) => {
274
        $crate::values::Value::bool_value(true)
275
    };
276
}
277
278
/// Creates a CBOR Value of type Simple with value false.
279
#[macro_export]
280
macro_rules! cbor_false {
281
    ( ) => {
282
        $crate::values::Value::bool_value(false)
283
    };
284
}
285
286
/// Creates a CBOR Value of type Simple with value null.
287
#[macro_export]
288
macro_rules! cbor_null {
289
    ( ) => {
290
        $crate::values::Value::null_value()
291
    };
292
}
293
294
/// Creates a CBOR Value of type Simple with the undefined value.
295
#[macro_export]
296
macro_rules! cbor_undefined {
297
    ( ) => {
298
        $crate::values::Value::undefined()
299
    };
300
}
301
302
/// Creates a CBOR Value of type Simple with the given bool value.
303
#[macro_export]
304
macro_rules! cbor_bool {
305
    ( $x:expr ) => {
306
        $crate::values::Value::bool_value($x)
307
    };
308
}
309
310
/// Creates a CBOR Value of type Unsigned with the given numeric value.
311
#[macro_export]
312
macro_rules! cbor_unsigned {
313
    ( $x:expr ) => {
314
        $crate::values::Value::unsigned($x as u64)
315
    };
316
}
317
318
/// Creates a CBOR Value of type Unsigned or Negative with the given numeric value.
319
#[macro_export]
320
macro_rules! cbor_int {
321
    ( $x:expr ) => {
322
        $crate::values::Value::integer($x as i64)
323
    };
324
}
325
326
/// Creates a CBOR Value of type Text String with the given string.
327
#[macro_export]
328
macro_rules! cbor_text {
329
    ( $x:expr ) => {
330
        $crate::values::Value::text_string($x.into())
331
    };
332
}
333
334
/// Creates a CBOR Value of type Byte String with the given slice or vector.
335
#[macro_export]
336
macro_rules! cbor_bytes {
337
    ( $x:expr ) => {
338
        $crate::values::Value::byte_string($x)
339
    };
340
}
341
342
/// Creates a CBOR Value of type Tag with the given tag and object.
343
#[macro_export]
344
macro_rules! cbor_tagged {
345
    ( $t:expr, $x: expr ) => {
346
        $crate::values::Value::tag($t, $x)
347
    };
348
}
349
350
/// Creates a CBOR Value of type Byte String with the given byte string literal.
351
///
352
/// Example usage:
353
///
354
/// ```rust
355
/// # extern crate alloc;
356
/// # use sk_cbor::cbor_bytes_lit;
357
/// let byte_array = cbor_bytes_lit!(b"foo");
358
/// ```
359
#[macro_export]
360
macro_rules! cbor_bytes_lit {
361
    ( $x:expr ) => {
362
        $crate::cbor_bytes!(($x as &[u8]).to_vec())
363
    };
364
}
365
366
#[cfg(test)]
367
mod test {
368
    use super::super::values::Value;
369
    use alloc::string::String;
370
    use alloc::vec;
371
    use alloc::vec::Vec;
372
373
    #[test]
374
    fn test_cbor_simple_values() {
375
        assert_eq!(cbor_true!(), Value::bool_value(true));
376
        assert_eq!(cbor_false!(), Value::bool_value(false));
377
        assert_eq!(cbor_null!(), Value::null_value());
378
        assert_eq!(cbor_undefined!(), Value::undefined());
379
    }
380
381
    #[test]
382
    fn test_cbor_bool() {
383
        assert_eq!(cbor_bool!(true), Value::bool_value(true));
384
        assert_eq!(cbor_bool!(false), Value::bool_value(false));
385
    }
386
387
    #[test]
388
    fn test_cbor_int_unsigned() {
389
        assert_eq!(cbor_int!(0), Value::from(0));
390
        assert_eq!(cbor_int!(1), Value::from(1));
391
        assert_eq!(cbor_int!(123456), Value::from(123456));
392
        assert_eq!(
393
            cbor_int!(core::i64::MAX),
394
            Value::from(core::i64::MAX as u64)
395
        );
396
    }
397
398
    #[test]
399
    fn test_cbor_int_negative() {
400
        assert_eq!(cbor_int!(-1), Value::from(-1));
401
        assert_eq!(cbor_int!(-123456), Value::from(-123456));
402
        assert_eq!(cbor_int!(core::i64::MIN), Value::from(core::i64::MIN));
403
    }
404
405
    #[test]
406
    fn test_cbor_int_literals() {
407
        let a = cbor_array![
408
            core::i64::MIN,
409
            core::i32::MIN,
410
            -123456,
411
            -1,
412
            0,
413
            1,
414
            123456,
415
            core::i32::MAX,
416
            core::i64::MAX,
417
            core::u64::MAX,
418
        ];
419
        let b = Value::array(vec![
420
            Value::from(core::i64::MIN),
421
            Value::from(core::i32::MIN as i64),
422
            Value::from(-123456),
423
            Value::from(-1),
424
            Value::from(0),
425
            Value::from(1),
426
            Value::from(123456),
427
            Value::from(core::i32::MAX as u64),
428
            Value::from(core::i64::MAX as u64),
429
            Value::from(core::u64::MAX),
430
        ]);
431
        assert_eq!(a, b);
432
    }
433
434
    #[test]
435
    fn test_cbor_array() {
436
        let a = cbor_array![
437
            -123,
438
            456,
439
            true,
440
            cbor_null!(),
441
            "foo",
442
            b"bar",
443
            cbor_array![],
444
            cbor_array![0, 1],
445
            cbor_map! {},
446
            cbor_map! {2 => 3},
447
        ];
448
        let b = Value::array(vec![
449
            Value::from(-123),
450
            Value::from(456),
451
            Value::bool_value(true),
452
            Value::null_value(),
453
            Value::from(String::from("foo")),
454
            Value::from(b"bar".to_vec()),
455
            Value::array(Vec::new()),
456
            Value::array(vec![Value::from(0), Value::from(1)]),
457
            Value::map(Vec::new()),
458
            Value::map([(Value::from(2), Value::from(3))].iter().cloned().collect()),
459
        ]);
460
        assert_eq!(a, b);
461
    }
462
463
    #[test]
464
    fn test_cbor_array_vec_empty() {
465
        let a = cbor_array_vec!(Vec::<bool>::new());
466
        let b = Value::array(Vec::new());
467
        assert_eq!(a, b);
468
    }
469
470
    #[test]
471
    fn test_cbor_array_vec_int() {
472
        let a = cbor_array_vec!(vec![1, 2, 3, 4]);
473
        let b = Value::array(vec![
474
            Value::from(1),
475
            Value::from(2),
476
            Value::from(3),
477
            Value::from(4),
478
        ]);
479
        assert_eq!(a, b);
480
    }
481
482
    #[test]
483
    fn test_cbor_array_vec_text() {
484
        let a = cbor_array_vec!(vec!["a", "b", "c"]);
485
        let b = Value::array(vec![
486
            Value::from(String::from("a")),
487
            Value::from(String::from("b")),
488
            Value::from(String::from("c")),
489
        ]);
490
        assert_eq!(a, b);
491
    }
492
493
    #[test]
494
    fn test_cbor_array_vec_bytes() {
495
        let a = cbor_array_vec!(vec![b"a", b"b", b"c"]);
496
        let b = Value::array(vec![
497
            Value::from(b"a".to_vec()),
498
            Value::from(b"b".to_vec()),
499
            Value::from(b"c".to_vec()),
500
        ]);
501
        assert_eq!(a, b);
502
    }
503
504
    #[test]
505
    fn test_cbor_map() {
506
        let a = cbor_map! {
507
            4 => 56,
508
            5 => "foo",
509
            6 => b"bar",
510
            7 => cbor_array![],
511
            8 => cbor_array![0, 1],
512
            9 => cbor_map!{},
513
            10 => cbor_map!{2 => 3},
514
            -1 => -23,
515
            b"bar" => cbor_null!(),
516
            "foo" => true,
517
        };
518
        let b = Value::map(
519
            [
520
                (Value::from(4), Value::from(56)),
521
                (Value::from(5), Value::from(String::from("foo"))),
522
                (Value::from(6), Value::from(b"bar".to_vec())),
523
                (Value::from(7), Value::array(Vec::new())),
524
                (
525
                    Value::from(8),
526
                    Value::array(vec![Value::from(0), Value::from(1)]),
527
                ),
528
                (Value::from(9), Value::map(Vec::new())),
529
                (
530
                    Value::from(10),
531
                    Value::map([(Value::from(2), Value::from(3))].iter().cloned().collect()),
532
                ),
533
                (Value::from(-1), Value::from(-23)),
534
                (Value::from(b"bar".to_vec()), Value::null_value()),
535
                (Value::from(String::from("foo")), Value::bool_value(true)),
536
            ]
537
            .iter()
538
            .cloned()
539
            .collect(),
540
        );
541
        assert_eq!(a, b);
542
    }
543
544
    #[test]
545
    fn test_cbor_map_options() {
546
        let a = cbor_map_options! {
547
            4 => Some(56),
548
            5 => "foo",
549
            6 => Some(b"bar" as &[u8]),
550
            7 => cbor_array![],
551
            8 => Some(cbor_array![0, 1]),
552
            9 => cbor_map!{},
553
            10 => Some(cbor_map!{2 => 3}),
554
            11 => None::<String>,
555
            12 => None::<&str>,
556
            13 => None::<Vec<u8>>,
557
            14 => None::<&[u8]>,
558
            15 => None::<bool>,
559
            16 => None::<i32>,
560
            17 => None::<i64>,
561
            18 => None::<u64>,
562
            -1 => -23,
563
            b"bar" => Some(cbor_null!()),
564
            "foo" => true,
565
        };
566
        let b = Value::map(
567
            [
568
                (Value::from(4), Value::from(56)),
569
                (Value::from(5), Value::from(String::from("foo"))),
570
                (Value::from(6), Value::from(b"bar".to_vec())),
571
                (Value::from(7), Value::array(Vec::new())),
572
                (
573
                    Value::from(8),
574
                    Value::array(vec![Value::from(0), Value::from(1)]),
575
                ),
576
                (Value::from(9), Value::map(Vec::new())),
577
                (
578
                    Value::from(10),
579
                    Value::map([(Value::from(2), Value::from(3))].iter().cloned().collect()),
580
                ),
581
                (Value::from(-1), Value::from(-23)),
582
                (Value::from(b"bar".to_vec()), Value::null_value()),
583
                (Value::from(String::from("foo")), Value::bool_value(true)),
584
            ]
585
            .iter()
586
            .cloned()
587
            .collect(),
588
        );
589
        assert_eq!(a, b);
590
    }
591
592
    #[test]
593
    fn test_cbor_map_collection_empty() {
594
        let a = cbor_map_collection!(Vec::<(_, _)>::new());
595
        let b = Value::map(Vec::new());
596
        assert_eq!(a, b);
597
    }
598
599
    #[test]
600
    fn test_cbor_map_collection_foo() {
601
        let a = cbor_map_collection!(vec![(Value::from(2), Value::from(3))]);
602
        let b = Value::map(vec![(Value::from(2), Value::from(3))]);
603
        assert_eq!(a, b);
604
    }
605
606
    fn extract_map(cbor_value: Value) -> Vec<(Value, Value)> {
607
        cbor_value.extract_map().unwrap()
608
    }
609
610
    #[test]
611
    fn test_destructure_cbor_map_simple() {
612
        let map = cbor_map! {
613
            1 => 10,
614
            2 => 20,
615
        };
616
617
        destructure_cbor_map! {
618
            let {
619
                1 => x1,
620
                2 => x2,
621
            } = extract_map(map);
622
        }
623
624
        assert_eq!(x1, Some(cbor_unsigned!(10)));
625
        assert_eq!(x2, Some(cbor_unsigned!(20)));
626
    }
627
628
    #[test]
629
    #[should_panic]
630
    fn test_destructure_cbor_map_unsorted() {
631
        let map = cbor_map! {
632
            1 => 10,
633
            2 => 20,
634
        };
635
636
        destructure_cbor_map! {
637
            // The keys are not sorted here, which violates the precondition of
638
            // destructure_cbor_map. An assertion should catch that and make the test panic.
639
            let {
640
                2 => _x2,
641
                1 => _x1,
642
            } = extract_map(map);
643
        }
644
    }
645
646
    #[test]
647
    fn test_destructure_cbor_map_partial() {
648
        let map = cbor_map! {
649
            1 => 10,
650
            2 => 20,
651
            3 => 30,
652
            4 => 40,
653
            5 => 50,
654
            6 => 60,
655
            7 => 70,
656
            8 => 80,
657
            9 => 90,
658
        };
659
660
        destructure_cbor_map! {
661
            let {
662
                3 => x3,
663
                7 => x7,
664
            } = extract_map(map);
665
        }
666
667
        assert_eq!(x3, Some(cbor_unsigned!(30)));
668
        assert_eq!(x7, Some(cbor_unsigned!(70)));
669
    }
670
671
    #[test]
672
    fn test_destructure_cbor_map_missing() {
673
        let map = cbor_map! {
674
            1 => 10,
675
            3 => 30,
676
            4 => 40,
677
        };
678
679
        destructure_cbor_map! {
680
            let {
681
                0 => x0,
682
                1 => x1,
683
                2 => x2,
684
                3 => x3,
685
                4 => x4,
686
                5 => x5,
687
            } = extract_map(map);
688
        }
689
690
        assert_eq!(x0, None);
691
        assert_eq!(x1, Some(cbor_unsigned!(10)));
692
        assert_eq!(x2, None);
693
        assert_eq!(x3, Some(cbor_unsigned!(30)));
694
        assert_eq!(x4, Some(cbor_unsigned!(40)));
695
        assert_eq!(x5, None);
696
    }
697
698
    #[test]
699
    fn test_destructure_unsorted_cbor_map() {
700
        let map = cbor_map! {
701
            2 => 20,
702
            1 => 10,
703
        };
704
705
        destructure_cbor_map! {
706
            let {
707
                1 => x1,
708
                2 => x2,
709
            } = extract_map(map);
710
        }
711
712
        assert_eq!(x1, Some(cbor_unsigned!(10)));
713
        assert_eq!(x2, Some(cbor_unsigned!(20)));
714
    }
715
}