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}