tps_minicbor/
map.rs

1/***************************************************************************************************
2 * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
5 * and associated documentation files (the “Software”), to deal in the Software without
6 * restriction, including without limitation the rights to use, copy, modify, merge, publish,
7 * distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
8 * Software is furnished to do so, subject to the following conditions:
9 *
10 * The above copyright notice and this permission notice (including the next
11 * paragraph) shall be included in all copies or substantial portions of the
12 * Software.
13 *
14 * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
15 * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
16 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
17 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19 **************************************************************************************************/
20/***************************************************************************************************
21 * rs_minicbor CBOR Map deserialser API
22 *
23 * A fairly comprehensive, memory efficient, deserializer and serializer for CBOR (RFC7049).
24 * This implementation is designed for use in constrained systems and requires neither the Rust
25 * standard library nor an allocator.
26 **************************************************************************************************/
27use crate::ast::CBOR;
28use crate::decode::{DecodeBufIterator, DecodeBufIteratorSource};
29use crate::error::CBORError;
30
31use crate::encode::{EncodeBuffer, EncodeContext, EncodeItem};
32
33use std::convert::{From, Into, TryFrom};
34
35#[cfg(feature = "trace")]
36use func_trace::trace;
37
38#[cfg(feature = "trace")]
39func_trace::init_depth_var!();
40
41/***************************************************************************************************
42 * Decoding Maps
43 **************************************************************************************************/
44/// A buffer which contains a CBOR Map to be decoded. The buffer has lifetime `'buf`,
45/// which must be longer than any borrow from the buffer itself. This is generally used to represent
46/// a CBOR map with an exposed map-like API.
47///
48/// This CBOR buffer implementation does not support indefinite length items.
49#[derive(PartialEq, Debug, Copy, Clone)]
50pub struct MapBuf<'buf> {
51    bytes: &'buf [u8],
52    n_pairs: usize,
53}
54
55impl<'buf> MapBuf<'buf> {
56    /// Construct a new instance of `MapBuf` with all context initialized.
57    #[cfg_attr(feature = "trace", trace)]
58    pub fn new(init: &'buf [u8], n_pairs: usize) -> MapBuf<'buf> {
59        MapBuf {
60            bytes: init,
61            n_pairs,
62        }
63    }
64
65    /// Return the number of item pairs in the `MapBuf`.
66    #[cfg_attr(feature = "trace", trace)]
67    #[inline]
68    pub fn len(self) -> usize {
69        self.n_pairs
70    }
71
72    /// Return `true` if `MapBuf` is empty.
73    #[cfg_attr(feature = "trace", trace)]
74    #[inline]
75    pub fn is_empty(self) -> bool {
76        self.n_pairs == 0 && self.bytes.len() == 0
77    }
78
79    /// Look-up a value using a key.
80    ///
81    /// The key is any value that can be transformed into a CBOR items (although integers and
82    /// short strings are strongly preferred as other types over keys).
83    ///
84    /// Lookup is fallible - function will return an error if the requested key is not present
85    /// in the map.
86    pub fn lookup<K, V>(self, key: K) -> Result<V, CBORError>
87    where
88        K: Into<CBOR<'buf>>,
89        V: TryFrom<CBOR<'buf>> + Clone,
90    {
91        match self.get(&key.into()) {
92            Some(cbor) => match V::try_from(cbor) {
93                Ok(v) => {
94                    Ok(v)
95                }
96                Err(_) => Err(CBORError::IncompatibleType),
97            },
98            None => Err(CBORError::KeyNotPresent),
99        }
100    }
101
102    /// Return `true` if `MapBuf` contains the provided key
103    #[cfg_attr(feature = "trace", trace)]
104    #[inline]
105    pub fn contains_key(self, key: &CBOR) -> bool {
106        match self.find_key_with_value(key) {
107            Ok((_, _)) => true,
108            _ => false,
109        }
110    }
111
112    /// Return the value corresponding to key.
113    #[cfg_attr(feature = "trace", trace)]
114    #[inline]
115    pub fn get(self, key: &CBOR) -> Option<CBOR<'buf>> {
116        match self.find_key_with_value(key) {
117            Ok((_, value)) => value,
118            _ => None,
119        }
120    }
121
122    /// Return the value corresponding to an integer key.
123    ///
124    /// In general, integers and strings are the recommended types to be used for map keys, so it
125    /// makes sense to simplify this use-case.
126    #[cfg_attr(feature = "trace", trace)]
127    #[inline]
128    pub fn get_int(self, v: i64) -> Option<CBOR<'buf>> {
129        self.get(&CBOR::from(v))
130    }
131
132    /// Return the value corresponding to an integer key.
133    ///
134    /// In general, integers and strings are the recommended types to be used for map keys, so it
135    /// makes sense to simplify this use-case.
136    #[cfg_attr(feature = "trace", trace)]
137    #[inline]
138    pub fn get_tstr(self, v: &str) -> Option<CBOR<'buf>> {
139        self.get(&CBOR::from(v))
140    }
141
142    /// Return value corresponding to a map item that can have either an integer or a string
143    /// key. This is a common use-case in IETF standards where human readability vs compactness
144    /// tradeoff is supported.
145    ///
146    /// In general, integers and strings are the recommended types to be used for map keys, so it
147    /// makes sense to simplify this use-case.
148    #[cfg_attr(feature = "trace", trace)]
149    #[inline]
150    pub fn get_int_or_tstr(self, v: i64, s: &str) -> Option<CBOR<'buf>> {
151        if let Some(cbor) = self.get_int(v) {
152            Some(cbor)
153        } else {
154            self.get_tstr(s)
155        }
156    }
157
158    /// Return the key, value pair corresponding to key.
159    #[cfg_attr(feature = "trace", trace)]
160    #[inline]
161    pub fn get_key_value(self, key: &CBOR) -> Option<(CBOR<'buf>, CBOR<'buf>)> {
162        match self.find_key_with_value(key) {
163            Ok((found_key, Some(found_value))) => Some((found_key, found_value)),
164            _ => None,
165        }
166    }
167
168    /// Return the (key, value) pair corresponding to an integer used as a key
169    #[cfg_attr(feature = "trace", trace)]
170    #[inline]
171    pub fn get_int_key_value(self, key: i64) -> Option<(CBOR<'buf>, CBOR<'buf>)> {
172        self.get_key_value(&CBOR::from(key))
173    }
174
175    /// Return the (key, value) pair corresponding to an tstr used as a key
176    #[cfg_attr(feature = "trace", trace)]
177    #[inline]
178    pub fn get_tstr_key_value(self, key: &str) -> Option<(CBOR<'buf>, CBOR<'buf>)> {
179        self.get_key_value(&CBOR::from(key))
180    }
181
182    /// Return (key, value) pair  corresponding to a map item that can have either an integer or a
183    /// string key. This is a common use-case in IETF standards where human readability vs
184    /// compactness tradeoff is supported.
185    #[cfg_attr(feature = "trace", trace)]
186    #[inline]
187    pub fn get_int_or_tstr_key_value(self, v: i64, s: &str) -> Option<(CBOR<'buf>, CBOR<'buf>)> {
188        if let Some(pair) = self.get_int_key_value(v) {
189            Some(pair)
190        } else {
191            self.get_tstr_key_value(s)
192        }
193    }
194
195    /// (private) If there is a key matching `search_key`, return the
196    /// key and corresponding value, otherwise return a `KeyNotPresent` error.
197    #[cfg_attr(feature = "trace", trace)]
198    fn find_key_with_value(
199        self,
200        search_key: &CBOR,
201    ) -> Result<(CBOR<'buf>, Option<CBOR<'buf>>), CBORError> {
202        let mut it: DecodeBufIterator<'buf> = self.into_iter();
203        let mut current_key = it.next();
204        while current_key.is_some() {
205            if let Some(item_key) = current_key {
206                if item_key == *search_key {
207                    return Ok((item_key, it.next()));
208                }
209                let _ = it.next(); // skip the next value as it doesn't match key
210                current_key = it.next(); // This one is a key again
211            }
212        }
213        return Err(CBORError::KeyNotPresent);
214    }
215}
216
217impl<'buf> IntoIterator for MapBuf<'buf> {
218    type Item = CBOR<'buf>;
219    type IntoIter = DecodeBufIterator<'buf>;
220
221    /// Construct an Iterator adapter from a `DecodeBuf`.
222    #[cfg_attr(feature = "trace", trace)]
223    fn into_iter(self) -> Self::IntoIter {
224        DecodeBufIterator {
225            buf: self.bytes,
226            index: 0,
227            source: DecodeBufIteratorSource::Map,
228        }
229    }
230}
231
232/***************************************************************************************************
233 * Encoding Maps
234 **************************************************************************************************/
235
236/// A container structure for the closure used to manage encoding of CBOR maps, and in particular
237/// to ensure that the correct lifetime bounds are specified.
238///
239/// The user is able to encode members of the map within a closure, and the map length will
240/// automatically be correctly constructed. Arbitrary nesting of arrays and maps is supported.
241///
242/// Users should never need to directly instantiate `Map`. Instead, see [`map`].
243pub struct Map<F>
244where
245    F: for<'f, 'buf> Fn(
246        &'f mut EncodeBuffer<'buf>,
247    ) -> Result<&'f mut EncodeBuffer<'buf>, CBORError>,
248{
249    f: F,
250}
251
252/// `Map` provides a constructor to contain the closure that constructs it
253impl<F> Map<F>
254where
255    F: for<'f, 'buf> Fn(
256        &'f mut EncodeBuffer<'buf>,
257    ) -> Result<&'f mut EncodeBuffer<'buf>, CBORError>,
258{
259    pub fn new(f: F) -> Map<F> {
260        Map { f }
261    }
262}
263
264/// The [`EncodeItem`] instance for `Map` performs the required manipulations to correctly
265/// calculate the size of the map and ensure that the number of items inserted is a multiple of two.
266impl<F> EncodeItem for Map<F>
267where
268    F: for<'f, 'buf> Fn(
269        &'f mut EncodeBuffer<'buf>,
270    ) -> Result<&'f mut EncodeBuffer<'buf>, CBORError>,
271{
272    fn encode<'f, 'buf>(
273        &self,
274        buf: &'f mut EncodeBuffer<'buf>,
275    ) -> Result<&'f mut EncodeBuffer<'buf>, CBORError> {
276        let mut map_ctx = EncodeContext::new();
277        buf.map_start(&mut map_ctx)?;
278        let _ = (self.f)(buf)?;
279        buf.map_finalize(&map_ctx)?;
280        Ok(buf)
281    }
282}
283
284/// A convenience function for the user to create an instance of a CBOR map. The user provides a
285/// closure which constructs the map contents.
286///
287/// The user can insert the map keys and values separately, but the use of the convenience function
288/// [`EncodeBuffer::insert_key_value`] helps to avoid errors.
289///
290/// ```
291///# use tps_minicbor::encoder::CBORBuilder;
292///# use tps_minicbor::error::CBORError;
293///# use tps_minicbor::types::map;
294///# fn main() -> Result<(), CBORError> {
295///    let mut buffer = [0u8; 16];
296///    let expected : &[u8] = &[162, 1, 101, 72, 101, 108, 108, 111, 2, 101, 87, 111, 114, 108, 100];
297///
298///    let mut encoder = CBORBuilder::new(&mut buffer);
299///    encoder.insert(&map(|buff| {
300///        buff.insert_key_value(&1, &"Hello")?
301///            .insert_key_value(&2, &"World")
302///    }));
303///    assert_eq!(encoder.encoded()?, expected);
304///#    Ok(())
305///# }
306/// ```
307pub fn map<F>(f: F) -> Map<F>
308where
309    F: for<'f, 'buf> Fn(
310        &'f mut EncodeBuffer<'buf>,
311    ) -> Result<&'f mut EncodeBuffer<'buf>, CBORError>,
312{
313    Map::new(f)
314}