sk_cbor/
macros.rs

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
17use crate::values::Value;
18use alloc::vec;
19use core::cmp::Ordering;
20use 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]
54macro_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.
91pub fn destructure_cbor_map_peek_value(
92    it: &mut Peekable<vec::IntoIter<(Value, Value)>>,
93    needle: Value,
94) -> Option<Value> {
95    loop {
96        match it.peek() {
97            None => return None,
98            Some(item) => {
99                let key: &Value = &item.0;
100                match key.cmp(&needle) {
101                    Ordering::Less => {
102                        it.next();
103                    }
104                    Ordering::Equal => {
105                        let value: Value = it.next().unwrap().1;
106                        return Some(value);
107                    }
108                    Ordering::Greater => return None,
109                }
110            }
111        }
112    }
113}
114
115/// Assert that the keys in a vector of key-value pairs are in canonical order.
116#[macro_export]
117macro_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/// Duplicate keys will lead to invalid CBOR, i.e. writing these values fails.
143/// Keys do not have to be sorted.
144///
145/// Example usage:
146///
147/// ```rust
148/// # extern crate alloc;
149/// # use sk_cbor::cbor_map;
150/// let map = cbor_map! {
151///   0x01 => false,
152///   "02" => -3,
153/// };
154/// ```
155#[macro_export]
156macro_rules! cbor_map {
157    // trailing comma case
158    ( $( $key:expr => $value:expr, )+ ) => {
159        cbor_map! ( $($key => $value),+ )
160    };
161
162    ( $( $key:expr => $value:expr ),* ) => {
163        {
164            // The import is unused if the list is empty.
165            #[allow(unused_imports)]
166            use $crate::values::IntoCborValue;
167            let mut _map = ::alloc::vec::Vec::new();
168            $(
169                _map.push(($key.into_cbor_value(), $value.into_cbor_value()));
170            )*
171            $crate::values::Value::Map(_map)
172        }
173    };
174}
175
176/// Creates a CBOR Value of type Map with key-value pairs where values can be Options.
177///
178/// Keys and values are expressions and converted into CBOR Keys and Value Options.
179/// The map entry is included iff the Value is not an Option or Option is Some.
180/// The syntax for these pairs is `key_expression => value_expression,`.
181/// Duplicate keys will lead to invalid CBOR, i.e. writing these values fails.
182/// Keys do not have to be sorted.
183///
184/// Example usage:
185///
186/// ```rust
187/// # extern crate alloc;
188/// # use sk_cbor::cbor_map_options;
189/// let missing_value: Option<bool> = None;
190/// let map = cbor_map_options! {
191///   0x01 => Some(false),
192///   "02" => -3,
193///   "not in map" => missing_value,
194/// };
195/// ```
196#[macro_export]
197macro_rules! cbor_map_options {
198    // trailing comma case
199    ( $( $key:expr => $value:expr, )+ ) => {
200        cbor_map_options! ( $($key => $value),+ )
201    };
202
203    ( $( $key:expr => $value:expr ),* ) => {
204        {
205            // The import is unused if the list is empty.
206            #[allow(unused_imports)]
207            use $crate::values::{IntoCborValue, IntoCborValueOption};
208            let mut _map = ::alloc::vec::Vec::<(_, $crate::values::Value)>::new();
209            $(
210            {
211                let opt: Option<$crate::values::Value> = $value.into_cbor_value_option();
212                if let Some(val) = opt {
213                    _map.push(($key.into_cbor_value(), val));
214                }
215            }
216            )*
217            $crate::values::Value::Map(_map)
218        }
219    };
220}
221
222/// Creates a CBOR Value of type Map from a Vec<(Value, Value)>.
223#[macro_export]
224macro_rules! cbor_map_collection {
225    ( $tree:expr ) => {{
226        $crate::values::Value::from($tree)
227    }};
228}
229
230/// Creates a CBOR Value of type Array with the given elements.
231///
232/// Elements are expressions and converted into CBOR Values. Elements are comma-separated.
233///
234/// Example usage:
235///
236/// ```rust
237/// # extern crate alloc;
238/// # use sk_cbor::cbor_array;
239/// let array = cbor_array![1, "2"];
240/// ```
241#[macro_export]
242macro_rules! cbor_array {
243    // trailing comma case
244    ( $( $value:expr, )+ ) => {
245        cbor_array! ( $($value),+ )
246    };
247
248    ( $( $value:expr ),* ) => {
249        {
250            // The import is unused if the list is empty.
251            #[allow(unused_imports)]
252            use $crate::values::IntoCborValue;
253            $crate::values::Value::Array(vec![ $( $value.into_cbor_value(), )* ])
254        }
255    };
256}
257
258/// Creates a CBOR Value of type Array from a Vec<Value>.
259#[macro_export]
260macro_rules! cbor_array_vec {
261    ( $vec:expr ) => {{
262        use $crate::values::IntoCborValue;
263        $crate::values::Value::Array($vec.into_iter().map(|x| x.into_cbor_value()).collect())
264    }};
265}
266
267/// Creates a CBOR Value of type Simple with value true.
268#[macro_export]
269macro_rules! cbor_true {
270    ( ) => {
271        $crate::values::Value::Simple($crate::values::SimpleValue::TrueValue)
272    };
273}
274
275/// Creates a CBOR Value of type Simple with value false.
276#[macro_export]
277macro_rules! cbor_false {
278    ( ) => {
279        $crate::values::Value::Simple($crate::values::SimpleValue::FalseValue)
280    };
281}
282
283/// Creates a CBOR Value of type Simple with value null.
284#[macro_export]
285macro_rules! cbor_null {
286    ( ) => {
287        $crate::values::Value::Simple($crate::values::SimpleValue::NullValue)
288    };
289}
290
291/// Creates a CBOR Value of type Simple with the undefined value.
292#[macro_export]
293macro_rules! cbor_undefined {
294    ( ) => {
295        $crate::values::Value::Simple($crate::values::SimpleValue::Undefined)
296    };
297}
298
299/// Creates a CBOR Value of type Simple with the given bool value.
300#[macro_export]
301macro_rules! cbor_bool {
302    ( $x:expr ) => {
303        $crate::values::Value::bool_value($x)
304    };
305}
306
307/// Creates a CBOR Value of type Unsigned with the given numeric value.
308#[macro_export]
309macro_rules! cbor_unsigned {
310    ( $x:expr ) => {
311        $crate::values::Value::Unsigned($x)
312    };
313}
314
315/// Creates a CBOR Value of type Unsigned or Negative with the given numeric value.
316#[macro_export]
317macro_rules! cbor_int {
318    ( $x:expr ) => {
319        $crate::values::Value::integer($x)
320    };
321}
322
323/// Creates a CBOR Value of type Text String with the given string.
324#[macro_export]
325macro_rules! cbor_text {
326    ( $x:expr ) => {
327        $crate::values::Value::TextString($x.into())
328    };
329}
330
331/// Creates a CBOR Value of type Byte String with the given slice or vector.
332#[macro_export]
333macro_rules! cbor_bytes {
334    ( $x:expr ) => {
335        $crate::values::Value::ByteString($x)
336    };
337}
338
339/// Creates a CBOR Value of type Tag with the given tag and object.
340#[macro_export]
341macro_rules! cbor_tagged {
342    ( $t:expr, $x: expr ) => {
343        $crate::values::Value::Tag($t, ::alloc::boxed::Box::new($x))
344    };
345}
346
347/// Creates a CBOR Value of type Byte String with the given byte string literal.
348///
349/// Example usage:
350///
351/// ```rust
352/// # extern crate alloc;
353/// # use sk_cbor::cbor_bytes_lit;
354/// let byte_array = cbor_bytes_lit!(b"foo");
355/// ```
356#[macro_export]
357macro_rules! cbor_bytes_lit {
358    ( $x:expr ) => {
359        $crate::cbor_bytes!(($x as &[u8]).to_vec())
360    };
361}
362
363#[cfg(test)]
364mod test {
365    use super::super::values::{SimpleValue, Value};
366    use alloc::{string::String, vec, vec::Vec};
367
368    #[test]
369    fn test_cbor_simple_values() {
370        assert_eq!(cbor_true!(), Value::Simple(SimpleValue::TrueValue));
371        assert_eq!(cbor_false!(), Value::Simple(SimpleValue::FalseValue));
372        assert_eq!(cbor_null!(), Value::Simple(SimpleValue::NullValue));
373        assert_eq!(cbor_undefined!(), Value::Simple(SimpleValue::Undefined));
374    }
375
376    #[test]
377    fn test_cbor_bool() {
378        assert_eq!(cbor_bool!(true), Value::Simple(SimpleValue::TrueValue));
379        assert_eq!(cbor_bool!(false), Value::Simple(SimpleValue::FalseValue));
380    }
381
382    #[test]
383    fn test_cbor_int_unsigned() {
384        assert_eq!(cbor_int!(0), Value::Unsigned(0));
385        assert_eq!(cbor_int!(1), Value::Unsigned(1));
386        assert_eq!(cbor_int!(123456), Value::Unsigned(123456));
387        assert_eq!(
388            cbor_int!(core::i64::MAX),
389            Value::Unsigned(core::i64::MAX as u64)
390        );
391    }
392
393    #[test]
394    fn test_cbor_int_negative() {
395        assert_eq!(cbor_int!(-1), Value::Negative(-1));
396        assert_eq!(cbor_int!(-123456), Value::Negative(-123456));
397        assert_eq!(cbor_int!(core::i64::MIN), Value::Negative(core::i64::MIN));
398    }
399
400    #[test]
401    fn test_cbor_int_literals() {
402        let a = cbor_array![
403            core::i64::MIN,
404            core::i32::MIN,
405            -123456,
406            -1,
407            0,
408            1,
409            123456,
410            core::i32::MAX,
411            core::i64::MAX,
412            core::u64::MAX,
413        ];
414        let b = Value::Array(vec![
415            Value::Negative(core::i64::MIN),
416            Value::Negative(core::i32::MIN as i64),
417            Value::Negative(-123456),
418            Value::Negative(-1),
419            Value::Unsigned(0),
420            Value::Unsigned(1),
421            Value::Unsigned(123456),
422            Value::Unsigned(core::i32::MAX as u64),
423            Value::Unsigned(core::i64::MAX as u64),
424            Value::Unsigned(core::u64::MAX),
425        ]);
426        assert_eq!(a, b);
427    }
428
429    #[test]
430    fn test_cbor_array() {
431        let a = cbor_array![
432            -123,
433            456,
434            true,
435            cbor_null!(),
436            "foo",
437            b"bar",
438            cbor_array![],
439            cbor_array![0, 1],
440            cbor_map! {},
441            cbor_map! {2 => 3},
442        ];
443        let b = Value::Array(vec![
444            Value::Negative(-123),
445            Value::Unsigned(456),
446            Value::Simple(SimpleValue::TrueValue),
447            Value::Simple(SimpleValue::NullValue),
448            Value::TextString(String::from("foo")),
449            Value::ByteString(b"bar".to_vec()),
450            Value::Array(Vec::new()),
451            Value::Array(vec![Value::Unsigned(0), Value::Unsigned(1)]),
452            Value::Map(Vec::new()),
453            Value::Map(
454                [(Value::Unsigned(2), Value::Unsigned(3))]
455                    .iter()
456                    .cloned()
457                    .collect(),
458            ),
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::Unsigned(1),
475            Value::Unsigned(2),
476            Value::Unsigned(3),
477            Value::Unsigned(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::TextString(String::from("a")),
487            Value::TextString(String::from("b")),
488            Value::TextString(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::ByteString(b"a".to_vec()),
498            Value::ByteString(b"b".to_vec()),
499            Value::ByteString(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            -1 => -23,
508            4 => 56,
509            "foo" => true,
510            b"bar" => cbor_null!(),
511            5 => "foo",
512            6 => b"bar",
513            7 => cbor_array![],
514            8 => cbor_array![0, 1],
515            9 => cbor_map!{},
516            10 => cbor_map!{2 => 3},
517        };
518        let b = Value::Map(
519            [
520                (Value::Negative(-1), Value::Negative(-23)),
521                (Value::Unsigned(4), Value::Unsigned(56)),
522                (
523                    Value::TextString(String::from("foo")),
524                    Value::Simple(SimpleValue::TrueValue),
525                ),
526                (
527                    Value::ByteString(b"bar".to_vec()),
528                    Value::Simple(SimpleValue::NullValue),
529                ),
530                (Value::Unsigned(5), Value::TextString(String::from("foo"))),
531                (Value::Unsigned(6), Value::ByteString(b"bar".to_vec())),
532                (Value::Unsigned(7), Value::Array(Vec::new())),
533                (
534                    Value::Unsigned(8),
535                    Value::Array(vec![Value::Unsigned(0), Value::Unsigned(1)]),
536                ),
537                (Value::Unsigned(9), Value::Map(Vec::new())),
538                (
539                    Value::Unsigned(10),
540                    Value::Map(
541                        [(Value::Unsigned(2), Value::Unsigned(3))]
542                            .iter()
543                            .cloned()
544                            .collect(),
545                    ),
546                ),
547            ]
548            .iter()
549            .cloned()
550            .collect(),
551        );
552        assert_eq!(a, b);
553    }
554
555    #[test]
556    fn test_cbor_map_options() {
557        let a = cbor_map_options! {
558            -1 => -23,
559            4 => Some(56),
560            11 => None::<String>,
561            "foo" => true,
562            12 => None::<&str>,
563            b"bar" => Some(cbor_null!()),
564            13 => None::<Vec<u8>>,
565            5 => "foo",
566            14 => None::<&[u8]>,
567            6 => Some(b"bar" as &[u8]),
568            15 => None::<bool>,
569            7 => cbor_array![],
570            16 => None::<i32>,
571            8 => Some(cbor_array![0, 1]),
572            17 => None::<i64>,
573            9 => cbor_map!{},
574            18 => None::<u64>,
575            10 => Some(cbor_map!{2 => 3}),
576        };
577        let b = Value::Map(
578            [
579                (Value::Negative(-1), Value::Negative(-23)),
580                (Value::Unsigned(4), Value::Unsigned(56)),
581                (
582                    Value::TextString(String::from("foo")),
583                    Value::Simple(SimpleValue::TrueValue),
584                ),
585                (
586                    Value::ByteString(b"bar".to_vec()),
587                    Value::Simple(SimpleValue::NullValue),
588                ),
589                (Value::Unsigned(5), Value::TextString(String::from("foo"))),
590                (Value::Unsigned(6), Value::ByteString(b"bar".to_vec())),
591                (Value::Unsigned(7), Value::Array(Vec::new())),
592                (
593                    Value::Unsigned(8),
594                    Value::Array(vec![Value::Unsigned(0), Value::Unsigned(1)]),
595                ),
596                (Value::Unsigned(9), Value::Map(Vec::new())),
597                (
598                    Value::Unsigned(10),
599                    Value::Map(
600                        [(Value::Unsigned(2), Value::Unsigned(3))]
601                            .iter()
602                            .cloned()
603                            .collect(),
604                    ),
605                ),
606            ]
607            .iter()
608            .cloned()
609            .collect(),
610        );
611        assert_eq!(a, b);
612    }
613
614    #[test]
615    fn test_cbor_map_collection_empty() {
616        let a = cbor_map_collection!(Vec::<(_, _)>::new());
617        let b = Value::Map(Vec::new());
618        assert_eq!(a, b);
619    }
620
621    #[test]
622    fn test_cbor_map_collection_foo() {
623        let a = cbor_map_collection!(vec![(Value::Unsigned(2), Value::Unsigned(3))]);
624        let b = Value::Map(vec![(Value::Unsigned(2), Value::Unsigned(3))]);
625        assert_eq!(a, b);
626    }
627
628    fn extract_map(cbor_value: Value) -> Vec<(Value, Value)> {
629        match cbor_value {
630            Value::Map(map) => map,
631            _ => panic!("Expected CBOR map."),
632        }
633    }
634
635    #[test]
636    fn test_destructure_cbor_map_simple() {
637        let map = cbor_map! {
638            1 => 10,
639            2 => 20,
640        };
641
642        destructure_cbor_map! {
643            let {
644                1 => x1,
645                2 => x2,
646            } = extract_map(map);
647        }
648
649        assert_eq!(x1, Some(cbor_unsigned!(10)));
650        assert_eq!(x2, Some(cbor_unsigned!(20)));
651    }
652
653    #[test]
654    #[should_panic]
655    fn test_destructure_cbor_map_unsorted() {
656        let map = cbor_map! {
657            1 => 10,
658            2 => 20,
659        };
660
661        destructure_cbor_map! {
662            // The keys are not sorted here, which violates the precondition of
663            // destructure_cbor_map. An assertion should catch that and make the test panic.
664            let {
665                2 => _x2,
666                1 => _x1,
667            } = extract_map(map);
668        }
669    }
670
671    #[test]
672    fn test_destructure_cbor_map_partial() {
673        let map = cbor_map! {
674            1 => 10,
675            2 => 20,
676            3 => 30,
677            4 => 40,
678            5 => 50,
679            6 => 60,
680            7 => 70,
681            8 => 80,
682            9 => 90,
683        };
684
685        destructure_cbor_map! {
686            let {
687                3 => x3,
688                7 => x7,
689            } = extract_map(map);
690        }
691
692        assert_eq!(x3, Some(cbor_unsigned!(30)));
693        assert_eq!(x7, Some(cbor_unsigned!(70)));
694    }
695
696    #[test]
697    fn test_destructure_cbor_map_missing() {
698        let map = cbor_map! {
699            1 => 10,
700            3 => 30,
701            4 => 40,
702        };
703
704        destructure_cbor_map! {
705            let {
706                0 => x0,
707                1 => x1,
708                2 => x2,
709                3 => x3,
710                4 => x4,
711                5 => x5,
712            } = extract_map(map);
713        }
714
715        assert_eq!(x0, None);
716        assert_eq!(x1, Some(cbor_unsigned!(10)));
717        assert_eq!(x2, None);
718        assert_eq!(x3, Some(cbor_unsigned!(30)));
719        assert_eq!(x4, Some(cbor_unsigned!(40)));
720        assert_eq!(x5, None);
721    }
722}
OSZAR »