Skip to main content

sacp_cbor/
query.rs

1//! Query support for canonical CBOR messages.
2//!
3//! This module provides a lightweight, allocation-free query engine for
4//! [`CanonicalCborRef`](crate::CanonicalCborRef). Queries return borrowed views
5//! ([`CborValueRef`]) pointing into the original message bytes.
6//!
7//! The query layer assumes the input bytes are already validated as canonical via
8//! [`validate_canonical`](crate::validate_canonical). If invariants are violated,
9//! APIs may return [`ErrorCode::MalformedCanonical`].
10
11use core::cmp::Ordering;
12
13use crate::canonical::CanonicalCborRef;
14use crate::profile::{checked_text_len, cmp_text_keys_canonical};
15use crate::utf8;
16use crate::wire;
17use crate::{CborError, ErrorCode};
18
19#[cfg(feature = "alloc")]
20use crate::canonical::CanonicalCbor;
21#[cfg(feature = "alloc")]
22use crate::canonical::EncodedTextKey;
23
24#[cfg(feature = "alloc")]
25use alloc::vec::Vec;
26
27/// The CBOR data model supported by this crate.
28#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29pub enum CborKind {
30    /// Major type 0/1 (safe-range) or tag 2/3 bignum.
31    Integer,
32    /// Major type 2.
33    Bytes,
34    /// Major type 3.
35    Text,
36    /// Major type 4.
37    Array,
38    /// Major type 5 (text keys only).
39    Map,
40    /// Simple value true/false.
41    Bool,
42    /// Simple value null.
43    Null,
44    /// IEEE-754 float64 (major 7, ai 27).
45    Float,
46}
47
48const fn err(code: ErrorCode, offset: usize) -> CborError {
49    CborError::new(code, offset)
50}
51
52const fn malformed(offset: usize) -> CborError {
53    err(ErrorCode::MalformedCanonical, offset)
54}
55
56const fn expected_map(offset: usize) -> CborError {
57    err(ErrorCode::ExpectedMap, offset)
58}
59
60const fn expected_array(offset: usize) -> CborError {
61    err(ErrorCode::ExpectedArray, offset)
62}
63
64const fn expected_integer(offset: usize) -> CborError {
65    err(ErrorCode::ExpectedInteger, offset)
66}
67
68const fn expected_text(offset: usize) -> CborError {
69    err(ErrorCode::ExpectedText, offset)
70}
71
72const fn expected_bytes(offset: usize) -> CborError {
73    err(ErrorCode::ExpectedBytes, offset)
74}
75
76const fn expected_bool(offset: usize) -> CborError {
77    err(ErrorCode::ExpectedBool, offset)
78}
79
80const fn expected_float(offset: usize) -> CborError {
81    err(ErrorCode::ExpectedFloat, offset)
82}
83
84const fn missing_key(offset: usize) -> CborError {
85    err(ErrorCode::MissingKey, offset)
86}
87
88/// A path element for navigating inside a CBOR value.
89///
90/// The query engine supports map keys (text) and array indices.
91#[derive(Debug, Clone, Copy, PartialEq, Eq)]
92pub enum PathElem<'p> {
93    /// Select a text key from a CBOR map.
94    Key(&'p str),
95    /// Select an index from a CBOR array.
96    Index(usize),
97}
98
99impl<'p> From<&'p str> for PathElem<'p> {
100    fn from(key: &'p str) -> Self {
101        Self::Key(key)
102    }
103}
104
105impl From<usize> for PathElem<'_> {
106    fn from(index: usize) -> Self {
107        Self::Index(index)
108    }
109}
110
111/// A borrowed view of a CBOR bignum (tag 2 / tag 3).
112#[derive(Debug, Clone, Copy, PartialEq, Eq)]
113pub struct BigIntRef<'a> {
114    negative: bool,
115    magnitude: &'a [u8],
116}
117
118impl<'a> BigIntRef<'a> {
119    /// Returns whether the bignum is negative (tag 3).
120    #[must_use]
121    pub const fn is_negative(self) -> bool {
122        self.negative
123    }
124
125    /// Returns the big-endian magnitude bytes.
126    #[must_use]
127    pub const fn magnitude(self) -> &'a [u8] {
128        self.magnitude
129    }
130}
131
132/// A borrowed view of an integer (safe or bignum).
133#[derive(Debug, Clone, Copy, PartialEq, Eq)]
134pub enum CborIntegerRef<'a> {
135    /// Safe-range integer (major 0/1).
136    Safe(i64),
137    /// Bignum integer (tag 2/3).
138    Big(BigIntRef<'a>),
139}
140
141impl<'a> CborIntegerRef<'a> {
142    /// Return the safe integer value, if present.
143    #[must_use]
144    pub const fn as_i64(self) -> Option<i64> {
145        match self {
146            Self::Safe(v) => Some(v),
147            Self::Big(_) => None,
148        }
149    }
150
151    /// Return the bignum reference, if present.
152    #[must_use]
153    pub const fn as_bigint(self) -> Option<BigIntRef<'a>> {
154        match self {
155            Self::Safe(_) => None,
156            Self::Big(b) => Some(b),
157        }
158    }
159}
160
161/// A borrowed view into a canonical CBOR message.
162///
163/// The view carries the full message bytes plus a `(start, end)` range for the
164/// current value. All nested values returned from queries keep referencing the
165/// original message bytes.
166#[derive(Debug, Clone, Copy)]
167pub struct CborValueRef<'a> {
168    data: &'a [u8],
169    start: usize,
170    end: usize,
171}
172
173#[allow(clippy::elidable_lifetime_names)]
174impl<'a> CborValueRef<'a> {
175    #[inline]
176    pub(crate) const fn new(data: &'a [u8], start: usize, end: usize) -> Self {
177        Self { data, start, end }
178    }
179
180    /// Construct a value reference from a canonical byte slice and range.
181    ///
182    /// # Safety
183    ///
184    /// The caller must ensure `data` is canonical CBOR and the range bounds a full CBOR item.
185    #[cfg(feature = "unsafe")]
186    #[cfg_attr(docsrs, doc(cfg(feature = "unsafe")))]
187    #[inline]
188    #[must_use]
189    pub unsafe fn from_canonical_range(data: &'a [u8], start: usize, end: usize) -> Self {
190        debug_assert!(start <= end && end <= data.len());
191        Self { data, start, end }
192    }
193
194    /// Returns the raw bytes (canonical CBOR encoding) for this value.
195    #[must_use]
196    pub fn as_bytes(self) -> &'a [u8] {
197        // Invariants are guaranteed by construction from validated canonical bytes.
198        &self.data[self.start..self.end]
199    }
200
201    /// Returns the starting offset (in bytes) of this value within the message.
202    #[must_use]
203    pub const fn offset(self) -> usize {
204        self.start
205    }
206
207    /// Returns the byte length of this value's canonical encoding.
208    #[must_use]
209    pub const fn len(self) -> usize {
210        self.end.saturating_sub(self.start)
211    }
212
213    /// Returns whether this value's canonical encoding is empty.
214    #[must_use]
215    pub const fn is_empty(self) -> bool {
216        self.start >= self.end
217    }
218
219    /// Returns the kind of this value.
220    ///
221    /// # Errors
222    ///
223    /// Returns `CborError` if the underlying bytes are malformed.
224    pub fn kind(self) -> Result<CborKind, CborError> {
225        let mut pos = self.start;
226        let off = self.start;
227        let ib = read_u8_trusted(self.data, &mut pos)?;
228        let major = ib >> 5;
229        let ai = ib & 0x1f;
230
231        match major {
232            0 | 1 => Ok(CborKind::Integer),
233            2 => Ok(CborKind::Bytes),
234            3 => Ok(CborKind::Text),
235            4 => Ok(CborKind::Array),
236            5 => Ok(CborKind::Map),
237            6 => {
238                let tag = read_uint_trusted(self.data, &mut pos, ai, off)?;
239                match tag {
240                    2 | 3 => Ok(CborKind::Integer),
241                    _ => Err(malformed(off)),
242                }
243            }
244            7 => match ai {
245                20 | 21 => Ok(CborKind::Bool),
246                22 => Ok(CborKind::Null),
247                27 => Ok(CborKind::Float),
248                _ => Err(malformed(off)),
249            },
250            _ => Err(malformed(off)),
251        }
252    }
253
254    /// Returns `true` if this value is CBOR `null`.
255    #[must_use]
256    pub fn is_null(self) -> bool {
257        self.data.get(self.start) == Some(&0xf6)
258    }
259
260    /// Interprets this value as a CBOR map and returns a borrowed map view.
261    ///
262    /// # Errors
263    ///
264    /// Returns `CborError::ExpectedMap` if the value is not a map.
265    pub fn map(self) -> Result<MapRef<'a>, CborError> {
266        let (len, entries_start) = parse_map_header(self.data, self.start)?;
267        Ok(MapRef {
268            data: self.data,
269            map_off: self.start,
270            entries_start,
271            len,
272        })
273    }
274
275    /// Interprets this value as a CBOR array and returns a borrowed array view.
276    ///
277    /// # Errors
278    ///
279    /// Returns `CborError::ExpectedArray` if the value is not an array.
280    pub fn array(self) -> Result<ArrayRef<'a>, CborError> {
281        let (len, items_start) = parse_array_header(self.data, self.start)?;
282        Ok(ArrayRef {
283            data: self.data,
284            array_off: self.start,
285            items_start,
286            len,
287        })
288    }
289
290    /// Retrieves a value by map key from this value (which must be a map).
291    ///
292    /// # Errors
293    ///
294    /// Returns `CborError::ExpectedMap` if the value is not a map.
295    pub fn get_key(self, key: &str) -> Result<Option<Self>, CborError> {
296        self.map()?.get(key)
297    }
298
299    /// Retrieves a value by array index from this value (which must be an array).
300    ///
301    /// # Errors
302    ///
303    /// Returns `CborError::ExpectedArray` if the value is not an array.
304    pub fn get_index(self, index: usize) -> Result<Option<Self>, CborError> {
305        self.array()?.get(index)
306    }
307
308    /// Traverses a nested path starting from this value.
309    ///
310    /// Returns `Ok(None)` if any map key is missing or any array index is out of
311    /// bounds. Returns `Err(_)` on type mismatches or malformed canonical input.
312    ///
313    /// # Errors
314    ///
315    /// Returns `CborError` for type mismatches or malformed canonical input.
316    pub fn at(self, path: &[PathElem<'_>]) -> Result<Option<Self>, CborError> {
317        let mut cur = self;
318        for pe in path {
319            match *pe {
320                PathElem::Key(k) => match cur.get_key(k)? {
321                    Some(v) => cur = v,
322                    None => return Ok(None),
323                },
324                PathElem::Index(i) => match cur.get_index(i)? {
325                    Some(v) => cur = v,
326                    None => return Ok(None),
327                },
328            }
329        }
330        Ok(Some(cur))
331    }
332
333    /// Decodes this value as a CBOR integer (safe or bignum).
334    ///
335    /// # Errors
336    ///
337    /// Returns `CborError::ExpectedInteger` if the value is not an integer or is malformed.
338    pub fn integer(self) -> Result<CborIntegerRef<'a>, CborError> {
339        let mut pos = self.start;
340        let off = self.start;
341        let ib = read_u8_trusted(self.data, &mut pos)?;
342        let major = ib >> 5;
343        let ai = ib & 0x1f;
344
345        match major {
346            0 => {
347                let u = read_uint_trusted(self.data, &mut pos, ai, off)?;
348                let i = i64::try_from(u).map_err(|_| malformed(off))?;
349                Ok(CborIntegerRef::Safe(i))
350            }
351            1 => {
352                let n = read_uint_trusted(self.data, &mut pos, ai, off)?;
353                let n_i = i64::try_from(n).map_err(|_| malformed(off))?;
354                Ok(CborIntegerRef::Safe(-1 - n_i))
355            }
356            6 => {
357                let tag = read_uint_trusted(self.data, &mut pos, ai, off)?;
358                let negative = match tag {
359                    2 => false,
360                    3 => true,
361                    _ => return Err(expected_integer(off)),
362                };
363
364                let m_off = pos;
365                let first = read_u8_trusted(self.data, &mut pos)?;
366                let m_major = first >> 5;
367                let m_ai = first & 0x1f;
368                if m_major != 2 {
369                    return Err(malformed(m_off));
370                }
371
372                let m_len = read_len_trusted(self.data, &mut pos, m_ai, m_off)?;
373                let mag = read_exact_trusted(self.data, &mut pos, m_len)?;
374
375                Ok(CborIntegerRef::Big(BigIntRef {
376                    negative,
377                    magnitude: mag,
378                }))
379            }
380            _ => Err(expected_integer(off)),
381        }
382    }
383
384    /// Decodes this value as a CBOR text string.
385    ///
386    /// # Errors
387    ///
388    /// Returns `CborError::ExpectedText` if the value is not a text string or is malformed.
389    pub fn text(self) -> Result<&'a str, CborError> {
390        let mut pos = self.start;
391        let off = self.start;
392        let ib = read_u8_trusted(self.data, &mut pos)?;
393        let major = ib >> 5;
394        let ai = ib & 0x1f;
395
396        if major != 3 {
397            return Err(expected_text(off));
398        }
399
400        let len = read_len_trusted(self.data, &mut pos, ai, off)?;
401        let bytes = read_exact_trusted(self.data, &mut pos, len)?;
402        let s = utf8::trusted(bytes).map_err(|()| malformed(off))?;
403        Ok(s)
404    }
405
406    /// Decodes this value as a CBOR byte string.
407    ///
408    /// # Errors
409    ///
410    /// Returns `CborError::ExpectedBytes` if the value is not a byte string or is malformed.
411    pub fn bytes(self) -> Result<&'a [u8], CborError> {
412        let mut pos = self.start;
413        let off = self.start;
414        let ib = read_u8_trusted(self.data, &mut pos)?;
415        let major = ib >> 5;
416        let ai = ib & 0x1f;
417
418        if major != 2 {
419            return Err(expected_bytes(off));
420        }
421
422        let len = read_len_trusted(self.data, &mut pos, ai, off)?;
423        let bytes = read_exact_trusted(self.data, &mut pos, len)?;
424        Ok(bytes)
425    }
426
427    /// Decodes this value as a CBOR boolean.
428    ///
429    /// # Errors
430    ///
431    /// Returns `CborError::ExpectedBool` if the value is not a boolean or is malformed.
432    pub fn bool(self) -> Result<bool, CborError> {
433        let off = self.start;
434        let b = *self.data.get(off).ok_or_else(|| malformed(off))?;
435
436        match b {
437            0xf4 => Ok(false),
438            0xf5 => Ok(true),
439            _ => Err(expected_bool(off)),
440        }
441    }
442
443    /// Decodes this value as a CBOR float64.
444    ///
445    /// # Errors
446    ///
447    /// Returns `CborError::ExpectedFloat` if the value is not a float64 or is malformed.
448    pub fn float64(self) -> Result<f64, CborError> {
449        let mut pos = self.start;
450        let off = self.start;
451        let ib = read_u8_trusted(self.data, &mut pos)?;
452        let major = ib >> 5;
453        let ai = ib & 0x1f;
454
455        if major != 7 || ai != 27 {
456            return Err(expected_float(off));
457        }
458
459        let b = read_exact_trusted(self.data, &mut pos, 8)?;
460        let bits = u64::from_be_bytes([b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]]);
461        Ok(f64::from_bits(bits))
462    }
463}
464
465impl PartialEq for CborValueRef<'_> {
466    fn eq(&self, other: &Self) -> bool {
467        self.as_bytes() == other.as_bytes()
468    }
469}
470
471impl Eq for CborValueRef<'_> {}
472
473/// A borrowed view into a canonical CBOR map.
474///
475/// Map keys are text strings and appear in canonical order (encoded length then
476/// lexicographic byte order).
477#[derive(Debug, Clone, Copy)]
478pub struct MapRef<'a> {
479    data: &'a [u8],
480    map_off: usize,
481    entries_start: usize,
482    len: usize,
483}
484
485impl<'a> MapRef<'a> {
486    /// Returns the number of entries in the map.
487    #[must_use]
488    pub const fn len(self) -> usize {
489        self.len
490    }
491
492    /// Returns whether the map is empty.
493    #[must_use]
494    pub const fn is_empty(self) -> bool {
495        self.len == 0
496    }
497
498    /// Looks up a single key in the map.
499    ///
500    /// This is efficient for canonical maps: it scans entries once and can stop early.
501    ///
502    /// # Errors
503    ///
504    /// Returns `CborError` if the map is malformed.
505    pub fn get(self, key: &str) -> Result<Option<CborValueRef<'a>>, CborError> {
506        checked_text_len(key.len()).map_err(|code| CborError::new(code, self.map_off))?;
507        let mut pos = self.entries_start;
508        let mut scratch = wire::SkipScratch::new();
509
510        for _ in 0..self.len {
511            let key_off = pos;
512            let mut key_pos = pos;
513            let key_bytes = read_text_bytes(self.data, &mut key_pos)?;
514            let value_start = key_pos;
515
516            let cmp = cmp_text_key_bytes_to_query(key_bytes, key);
517            match cmp {
518                Ordering::Less => {
519                    pos = value_end_with_scratch(self.data, value_start, &mut scratch)?;
520                }
521                Ordering::Equal => {
522                    let end = value_end_with_scratch(self.data, value_start, &mut scratch)?;
523                    return Ok(Some(CborValueRef::new(self.data, value_start, end)));
524                }
525                Ordering::Greater => return Ok(None),
526            }
527
528            // Ensure the scan continues from the end of the value we just skipped.
529            if pos <= key_off {
530                return Err(malformed(key_off));
531            }
532        }
533
534        Ok(None)
535    }
536
537    /// Looks up a required key in the map.
538    ///
539    /// # Errors
540    ///
541    /// Returns `CborError::MissingKey` if the key is not present.
542    pub fn require(self, key: &str) -> Result<CborValueRef<'a>, CborError> {
543        self.get(key)?.ok_or_else(|| missing_key(self.map_off))
544    }
545
546    /// Looks up multiple required keys in a single pass.
547    ///
548    /// # Errors
549    ///
550    /// Returns `CborError::MissingKey` if any key is not present.
551    pub fn require_many_sorted<const N: usize>(
552        self,
553        keys: [&str; N],
554    ) -> Result<[CborValueRef<'a>; N], CborError> {
555        let got = self.get_many_sorted(keys)?;
556        for v in &got {
557            if v.is_none() {
558                return Err(missing_key(self.map_off));
559            }
560        }
561
562        let mut err: Option<CborError> = None;
563        let data = self.data;
564        let off = self.map_off;
565        let out = core::array::from_fn(|i| {
566            got[i].unwrap_or_else(|| {
567                err = Some(missing_key(off));
568                CborValueRef::new(data, off, off)
569            })
570        });
571
572        if let Some(e) = err {
573            return Err(e);
574        }
575
576        Ok(out)
577    }
578
579    /// Looks up multiple keys in a single pass.
580    ///
581    /// Keys may be in any order; results preserve the input key order. Missing keys yield `None`.
582    ///
583    /// # Errors
584    ///
585    /// Returns `CborError` for invalid query inputs or malformed canonical data.
586    pub fn get_many_sorted<const N: usize>(
587        self,
588        keys: [&str; N],
589    ) -> Result<[Option<CborValueRef<'a>>; N], CborError> {
590        let mut out: [Option<CborValueRef<'a>>; N] = [None; N];
591
592        validate_query_keys(&keys, self.map_off)?;
593
594        if keys.is_empty() || self.len == 0 {
595            return Ok(out);
596        }
597
598        let mut idxs: [usize; N] = core::array::from_fn(|i| i);
599        idxs[..].sort_unstable_by(|&i, &j| cmp_text_keys_canonical(keys[i], keys[j]));
600
601        for w in idxs.windows(2) {
602            if keys[w[0]] == keys[w[1]] {
603                return Err(CborError::new(ErrorCode::InvalidQuery, self.map_off));
604            }
605        }
606
607        let mut state = MapScanState::new(self.data, self.entries_start, self.len);
608        state.scan_sorted(&keys, &idxs, |out_idx, value_start, end| {
609            out[out_idx] = Some(CborValueRef::new(self.data, value_start, end));
610        })?;
611
612        Ok(out)
613    }
614
615    /// The slice-based form of [`MapRef::get_many_sorted`].
616    ///
617    /// `out` is cleared to `None` for all entries before results are written.
618    ///
619    /// # Errors
620    ///
621    /// Returns `CborError` for invalid query inputs or malformed canonical data.
622    #[cfg(feature = "alloc")]
623    pub fn get_many_sorted_into(
624        self,
625        keys: &[&str],
626        out: &mut [Option<CborValueRef<'a>>],
627    ) -> Result<(), CborError> {
628        self.get_many_into(keys, out)
629    }
630
631    /// Iterates over `(key, value)` pairs in canonical order.
632    ///
633    /// The iterator yields `Result` to remain robust if canonical invariants are violated.
634    pub fn iter(self) -> impl Iterator<Item = Result<(&'a str, CborValueRef<'a>), CborError>> + 'a {
635        MapIter {
636            data: self.data,
637            pos: self.entries_start,
638            remaining: self.len,
639            scratch: wire::SkipScratch::new(),
640        }
641    }
642
643    /// Iterates over `(key, encoded_key, value)` in canonical order.
644    ///
645    /// The encoded key is the canonical CBOR encoding of the text key.
646    #[cfg(feature = "alloc")]
647    pub(crate) fn iter_encoded(
648        self,
649    ) -> impl Iterator<Item = Result<EncodedMapEntry<'a>, CborError>> + 'a {
650        MapIterEncoded {
651            data: self.data,
652            pos: self.entries_start,
653            remaining: self.len,
654            scratch: wire::SkipScratch::new(),
655        }
656    }
657
658    /// Iterates over map entries excluding `used_keys` (keys must be sorted canonically).
659    ///
660    /// # Errors
661    ///
662    /// Returns `CborError` if `used_keys` are not strictly increasing or if the map is malformed.
663    pub fn extras_sorted<'k>(
664        self,
665        used_keys: &'k [&'k str],
666    ) -> Result<impl Iterator<Item = Result<(&'a str, CborValueRef<'a>), CborError>> + 'k, CborError>
667    where
668        'a: 'k,
669    {
670        validate_query_keys(used_keys, self.map_off)?;
671        ensure_strictly_increasing_keys(used_keys, self.map_off)?;
672
673        let it = MapIter {
674            data: self.data,
675            pos: self.entries_start,
676            remaining: self.len,
677            scratch: wire::SkipScratch::new(),
678        };
679
680        Ok(ExtrasIter {
681            iter: it,
682            used: used_keys,
683            idx: 0,
684        })
685    }
686
687    /// Collects extras for sorted `used_keys` into a Vec.
688    ///
689    /// # Errors
690    ///
691    /// Returns `CborError` if `used_keys` are not strictly increasing or if the map is malformed.
692    #[cfg(feature = "alloc")]
693    pub fn extras_sorted_vec<'k>(
694        self,
695        used_keys: &'k [&'k str],
696    ) -> Result<Vec<(&'a str, CborValueRef<'a>)>, CborError>
697    where
698        'a: 'k,
699    {
700        use crate::alloc_util::try_vec_with_capacity;
701
702        let mut out = try_vec_with_capacity(self.len(), self.map_off)?;
703        for entry in self.extras_sorted(used_keys)? {
704            out.push(entry?);
705        }
706        Ok(out)
707    }
708
709    /// Accepts `used_keys` in any order (allocates to sort them), then returns extras.
710    ///
711    /// # Errors
712    ///
713    /// Returns `CborError` if `used_keys` contain duplicates or if the map is malformed.
714    #[cfg(feature = "alloc")]
715    pub fn extras_vec<'k>(
716        self,
717        used_keys: &'k [&'k str],
718    ) -> Result<Vec<(&'a str, CborValueRef<'a>)>, CborError>
719    where
720        'a: 'k,
721    {
722        use crate::alloc_util::try_vec_with_capacity;
723
724        validate_query_keys(used_keys, self.map_off)?;
725        if used_keys.is_empty() {
726            return self.extras_sorted_vec(&[]);
727        }
728
729        let mut idxs = try_vec_with_capacity(used_keys.len(), self.map_off)?;
730        for idx in 0..used_keys.len() {
731            idxs.push(idx);
732        }
733        idxs.sort_by(|&i, &j| cmp_text_keys_canonical(used_keys[i], used_keys[j]));
734        for w in idxs.windows(2) {
735            if used_keys[w[0]] == used_keys[w[1]] {
736                return Err(CborError::new(ErrorCode::InvalidQuery, self.map_off));
737            }
738        }
739
740        let mut sorted = try_vec_with_capacity(used_keys.len(), self.map_off)?;
741        for idx in idxs {
742            sorted.push(used_keys[idx]);
743        }
744        self.extras_sorted_vec(&sorted)
745    }
746
747    /// Looks up multiple keys in one pass (keys may be in any order).
748    ///
749    /// This API is available with the `alloc` feature. Results preserve the input key order.
750    ///
751    /// # Errors
752    ///
753    /// Returns `CborError` for invalid query inputs or malformed canonical data.
754    #[cfg(feature = "alloc")]
755    pub fn get_many(self, keys: &[&str]) -> Result<Vec<Option<CborValueRef<'a>>>, CborError> {
756        let mut out = crate::alloc_util::try_vec_repeat_copy(keys.len(), None, self.map_off)?;
757        self.get_many_into(keys, &mut out)?;
758        Ok(out)
759    }
760
761    /// Looks up multiple required keys in one pass (keys may be in any order).
762    ///
763    /// This API is available with the `alloc` feature. Results preserve the input key order.
764    ///
765    /// # Errors
766    ///
767    /// Returns `CborError::MissingKey` if any key is not present.
768    #[cfg(feature = "alloc")]
769    pub fn require_many(self, keys: &[&str]) -> Result<Vec<CborValueRef<'a>>, CborError> {
770        let mut out = crate::alloc_util::try_vec_repeat_copy(keys.len(), None, self.map_off)?;
771        self.get_many_into(keys, &mut out)?;
772
773        let mut req = crate::alloc_util::try_vec_with_capacity(out.len(), self.map_off)?;
774        for slot in out {
775            match slot {
776                Some(v) => req.push(v),
777                None => return Err(missing_key(self.map_off)),
778            }
779        }
780        Ok(req)
781    }
782
783    /// The slice-based form of [`MapRef::get_many`].
784    ///
785    /// `out` is cleared to `None` for all entries before results are written.
786    ///
787    /// # Errors
788    ///
789    /// Returns `CborError` for invalid query inputs or malformed canonical data.
790    #[cfg(feature = "alloc")]
791    pub fn get_many_into(
792        self,
793        keys: &[&str],
794        out: &mut [Option<CborValueRef<'a>>],
795    ) -> Result<(), CborError> {
796        if keys.len() != out.len() {
797            return Err(CborError::new(ErrorCode::InvalidQuery, self.map_off));
798        }
799
800        validate_query_keys(keys, self.map_off)?;
801
802        for slot in out.iter_mut() {
803            *slot = None;
804        }
805
806        if keys.is_empty() || self.len == 0 {
807            return Ok(());
808        }
809
810        // Sort indices by canonical ordering of the corresponding keys.
811        let mut idxs = crate::alloc_util::try_vec_with_capacity(keys.len(), self.map_off)?;
812        for i in 0..keys.len() {
813            idxs.push(i);
814        }
815        idxs.sort_by(|&i, &j| cmp_text_keys_canonical(keys[i], keys[j]));
816
817        // Detect duplicate query keys.
818        for w in idxs.windows(2) {
819            if keys[w[0]] == keys[w[1]] {
820                return Err(CborError::new(ErrorCode::InvalidQuery, self.map_off));
821            }
822        }
823
824        // Merge-join scan over the map and the sorted query list.
825        let mut state = MapScanState::new(self.data, self.entries_start, self.len);
826        state.scan_sorted(keys, &idxs, |out_idx, value_start, end| {
827            out[out_idx] = Some(CborValueRef::new(self.data, value_start, end));
828        })?;
829
830        Ok(())
831    }
832}
833
834/// A borrowed view into a canonical CBOR array.
835#[derive(Debug, Clone, Copy)]
836pub struct ArrayRef<'a> {
837    data: &'a [u8],
838    array_off: usize,
839    items_start: usize,
840    len: usize,
841}
842
843impl<'a> ArrayRef<'a> {
844    /// Returns the number of items in the array.
845    #[must_use]
846    pub const fn len(self) -> usize {
847        self.len
848    }
849
850    /// Returns whether the array is empty.
851    #[must_use]
852    pub const fn is_empty(self) -> bool {
853        self.len == 0
854    }
855
856    /// Returns the array item at `index`, or `None` if out of bounds.
857    ///
858    /// # Errors
859    ///
860    /// Returns `CborError` if the array is malformed.
861    pub fn get(self, index: usize) -> Result<Option<CborValueRef<'a>>, CborError> {
862        if index >= self.len {
863            return Ok(None);
864        }
865
866        let mut pos = self.items_start;
867        let mut scratch = wire::SkipScratch::new();
868        for i in 0..self.len {
869            let start = pos;
870            let end = value_end_with_scratch(self.data, start, &mut scratch)?;
871            if i == index {
872                return Ok(Some(CborValueRef::new(self.data, start, end)));
873            }
874            pos = end;
875        }
876
877        Err(malformed(self.array_off))
878    }
879
880    /// Iterates over array items in order.
881    ///
882    /// The iterator yields `Result` to remain robust if canonical invariants are violated.
883    pub fn iter(self) -> impl Iterator<Item = Result<CborValueRef<'a>, CborError>> + 'a {
884        ArrayIter {
885            data: self.data,
886            pos: self.items_start,
887            remaining: self.len,
888            scratch: wire::SkipScratch::new(),
889        }
890    }
891}
892
893/// Adds query methods to `CanonicalCborRef`.
894impl<'a> CanonicalCborRef<'a> {
895    /// Returns a borrowed view of the root CBOR item.
896    ///
897    /// Canonical validation guarantees the message is exactly one CBOR item, so the
898    /// root value spans the full byte slice.
899    #[must_use]
900    pub const fn root(self) -> CborValueRef<'a> {
901        CborValueRef::new(self.as_bytes(), 0, self.len())
902    }
903
904    /// Convenience wrapper around `self.root().at(path)`.
905    ///
906    /// # Errors
907    ///
908    /// Returns `CborError` for type mismatches or malformed canonical input.
909    pub fn at(self, path: &[PathElem<'_>]) -> Result<Option<CborValueRef<'a>>, CborError> {
910        self.root().at(path)
911    }
912}
913
914#[cfg(feature = "alloc")]
915impl CanonicalCbor {
916    /// Returns a borrowed view of the root CBOR item.
917    #[must_use]
918    pub fn root(&self) -> CborValueRef<'_> {
919        let b = self.as_bytes();
920        CborValueRef::new(b, 0, b.len())
921    }
922
923    /// Convenience wrapper around `self.root().at(path)`.
924    ///
925    /// # Errors
926    ///
927    /// Returns `CborError` for type mismatches or malformed canonical input.
928    pub fn at(&self, path: &[PathElem<'_>]) -> Result<Option<CborValueRef<'_>>, CborError> {
929        self.root().at(path)
930    }
931}
932
933/* =========================
934 * Internal parsing helpers
935 * ========================= */
936
937#[inline]
938const fn map_trusted_err(cause: CborError) -> CborError {
939    err(ErrorCode::MalformedCanonical, cause.offset)
940}
941
942#[inline]
943fn read_u8_trusted(data: &[u8], pos: &mut usize) -> Result<u8, CborError> {
944    wire::read_u8(data, pos).map_err(map_trusted_err)
945}
946
947#[inline]
948fn read_exact_trusted<'a>(
949    data: &'a [u8],
950    pos: &mut usize,
951    n: usize,
952) -> Result<&'a [u8], CborError> {
953    wire::read_exact(data, pos, n).map_err(map_trusted_err)
954}
955
956#[inline]
957fn read_uint_trusted(data: &[u8], pos: &mut usize, ai: u8, off: usize) -> Result<u64, CborError> {
958    wire::read_uint_trusted(data, pos, ai, off).map_err(map_trusted_err)
959}
960
961#[inline]
962fn read_len_trusted(data: &[u8], pos: &mut usize, ai: u8, off: usize) -> Result<usize, CborError> {
963    wire::read_len_trusted(data, pos, ai, off).map_err(map_trusted_err)
964}
965
966#[derive(Clone, Copy)]
967struct CachedKey<'a> {
968    key_bytes: &'a [u8],
969    value_start: usize,
970}
971
972struct MapScanState<'a> {
973    data: &'a [u8],
974    pos: usize,
975    cached: Option<CachedKey<'a>>,
976    map_remaining: usize,
977    scratch: wire::SkipScratch,
978}
979
980impl<'a> MapScanState<'a> {
981    const fn new(data: &'a [u8], pos: usize, map_remaining: usize) -> Self {
982        Self {
983            data,
984            pos,
985            cached: None,
986            map_remaining,
987            scratch: wire::SkipScratch::new(),
988        }
989    }
990
991    fn fill_cache(&mut self) -> Result<(), CborError> {
992        if self.cached.is_none() {
993            let mut key_pos = self.pos;
994            let key_bytes = read_text_bytes(self.data, &mut key_pos)?;
995            let value_start = key_pos;
996            self.pos = value_start;
997            self.cached = Some(CachedKey {
998                key_bytes,
999                value_start,
1000            });
1001        }
1002        Ok(())
1003    }
1004
1005    fn consume_cached_entry(&mut self, ck: CachedKey<'a>) -> Result<usize, CborError> {
1006        let end = value_end_with_scratch(self.data, ck.value_start, &mut self.scratch)?;
1007        self.pos = end;
1008        self.cached = None;
1009        self.map_remaining -= 1;
1010        Ok(end)
1011    }
1012
1013    fn handle_query_match<F>(
1014        &mut self,
1015        query: &str,
1016        q_idx: usize,
1017        on_match: F,
1018    ) -> Result<usize, CborError>
1019    where
1020        F: FnOnce(usize),
1021    {
1022        let Some(ck) = self.cached else {
1023            return Ok(q_idx);
1024        };
1025
1026        match cmp_text_key_bytes_to_query(ck.key_bytes, query) {
1027            Ordering::Less => {
1028                let _ = self.consume_cached_entry(ck)?;
1029                Ok(q_idx)
1030            }
1031            Ordering::Equal => {
1032                let end = self.consume_cached_entry(ck)?;
1033                on_match(end);
1034                Ok(q_idx + 1)
1035            }
1036            Ordering::Greater => Ok(q_idx + 1),
1037        }
1038    }
1039
1040    fn scan_sorted<F>(
1041        &mut self,
1042        keys: &[&str],
1043        idxs: &[usize],
1044        mut on_match: F,
1045    ) -> Result<(), CborError>
1046    where
1047        F: FnMut(usize, usize, usize),
1048    {
1049        let mut q_pos = 0usize;
1050
1051        while q_pos < idxs.len() {
1052            if self.map_remaining == 0 {
1053                break;
1054            }
1055
1056            self.fill_cache()?;
1057
1058            let Some(ck) = self.cached else {
1059                continue;
1060            };
1061
1062            let out_idx = idxs[q_pos];
1063            let value_start = ck.value_start;
1064            q_pos = self.handle_query_match(keys[out_idx], q_pos, |end| {
1065                on_match(out_idx, value_start, end);
1066            })?;
1067        }
1068
1069        Ok(())
1070    }
1071}
1072
1073fn read_text<'a>(data: &'a [u8], pos: &mut usize) -> Result<&'a str, CborError> {
1074    let off = *pos;
1075    let ib = read_u8_trusted(data, pos)?;
1076    let major = ib >> 5;
1077    let ai = ib & 0x1f;
1078
1079    if major != 3 {
1080        return Err(malformed(off));
1081    }
1082
1083    let len = read_len_trusted(data, pos, ai, off)?;
1084    let bytes = read_exact_trusted(data, pos, len)?;
1085    let text = utf8::trusted(bytes).map_err(|()| malformed(off))?;
1086    Ok(text)
1087}
1088
1089fn read_text_bytes<'a>(data: &'a [u8], pos: &mut usize) -> Result<&'a [u8], CborError> {
1090    let off = *pos;
1091    let ib = read_u8_trusted(data, pos)?;
1092    let major = ib >> 5;
1093    let ai = ib & 0x1f;
1094
1095    if major != 3 {
1096        return Err(malformed(off));
1097    }
1098
1099    let len = read_len_trusted(data, pos, ai, off)?;
1100    read_exact_trusted(data, pos, len)
1101}
1102
1103fn value_end_with_scratch(
1104    data: &[u8],
1105    start: usize,
1106    scratch: &mut wire::SkipScratch,
1107) -> Result<usize, CborError> {
1108    let mut cursor = wire::Cursor::<CborError>::with_pos(data, start);
1109    let mut items_seen = 0;
1110    wire::skip_one_value_with_scratch::<false, CborError>(
1111        &mut cursor,
1112        None,
1113        &mut items_seen,
1114        0,
1115        scratch,
1116    )?;
1117    Ok(cursor.position())
1118}
1119
1120fn parse_map_header(data: &[u8], start: usize) -> Result<(usize, usize), CborError> {
1121    let mut pos = start;
1122    let off = start;
1123    let ib = read_u8_trusted(data, &mut pos)?;
1124    let major = ib >> 5;
1125    let ai = ib & 0x1f;
1126
1127    if major != 5 {
1128        return Err(expected_map(off));
1129    }
1130
1131    let len = read_len_trusted(data, &mut pos, ai, off)?;
1132    Ok((len, pos))
1133}
1134
1135fn parse_array_header(data: &[u8], start: usize) -> Result<(usize, usize), CborError> {
1136    let mut pos = start;
1137    let off = start;
1138    let ib = read_u8_trusted(data, &mut pos)?;
1139    let major = ib >> 5;
1140    let ai = ib & 0x1f;
1141
1142    if major != 4 {
1143        return Err(expected_array(off));
1144    }
1145
1146    let len = read_len_trusted(data, &mut pos, ai, off)?;
1147    Ok((len, pos))
1148}
1149
1150fn cmp_text_key_bytes_to_query(key_payload: &[u8], query: &str) -> Ordering {
1151    let q_bytes = query.as_bytes();
1152    match key_payload.len().cmp(&q_bytes.len()) {
1153        Ordering::Equal => key_payload.cmp(q_bytes),
1154        other => other,
1155    }
1156}
1157
1158fn validate_query_keys(keys: &[&str], err_off: usize) -> Result<(), CborError> {
1159    for &k in keys {
1160        checked_text_len(k.len()).map_err(|code| CborError::new(code, err_off))?;
1161    }
1162    Ok(())
1163}
1164
1165fn ensure_strictly_increasing_keys(keys: &[&str], err_off: usize) -> Result<(), CborError> {
1166    let mut prev: Option<&str> = None;
1167
1168    for &k in keys {
1169        if let Some(p) = prev {
1170            match cmp_text_keys_canonical(p, k) {
1171                Ordering::Less => {}
1172                Ordering::Equal | Ordering::Greater => {
1173                    return Err(CborError::new(ErrorCode::InvalidQuery, err_off));
1174                }
1175            }
1176        }
1177        prev = Some(k);
1178    }
1179
1180    Ok(())
1181}
1182
1183struct ExtrasIter<'a, 'k> {
1184    iter: MapIter<'a>,
1185    used: &'k [&'k str],
1186    idx: usize,
1187}
1188
1189impl<'a> Iterator for ExtrasIter<'a, '_> {
1190    type Item = Result<(&'a str, CborValueRef<'a>), CborError>;
1191
1192    fn next(&mut self) -> Option<Self::Item> {
1193        loop {
1194            let next = self.iter.next()?;
1195            return match next {
1196                Err(e) => Some(Err(e)),
1197                Ok((k, v)) => {
1198                    while self.idx < self.used.len()
1199                        && cmp_text_keys_canonical(self.used[self.idx], k) == Ordering::Less
1200                    {
1201                        self.idx += 1;
1202                    }
1203                    if self.idx < self.used.len()
1204                        && cmp_text_keys_canonical(self.used[self.idx], k) == Ordering::Equal
1205                    {
1206                        self.idx += 1;
1207                        continue;
1208                    }
1209                    Some(Ok((k, v)))
1210                }
1211            };
1212        }
1213    }
1214}
1215
1216struct MapIter<'a> {
1217    data: &'a [u8],
1218    pos: usize,
1219    remaining: usize,
1220    scratch: wire::SkipScratch,
1221}
1222
1223impl<'a> Iterator for MapIter<'a> {
1224    type Item = Result<(&'a str, CborValueRef<'a>), CborError>;
1225
1226    fn next(&mut self) -> Option<Self::Item> {
1227        if self.remaining == 0 {
1228            return None;
1229        }
1230
1231        let mut key_pos = self.pos;
1232        let parsed = match read_text(self.data, &mut key_pos) {
1233            Ok(v) => v,
1234            Err(e) => {
1235                self.remaining = 0;
1236                return Some(Err(e));
1237            }
1238        };
1239
1240        let value_start = key_pos;
1241        let end = match value_end_with_scratch(self.data, value_start, &mut self.scratch) {
1242            Ok(e) => e,
1243            Err(e) => {
1244                self.remaining = 0;
1245                return Some(Err(e));
1246            }
1247        };
1248
1249        self.pos = end;
1250        self.remaining -= 1;
1251
1252        Some(Ok((parsed, CborValueRef::new(self.data, value_start, end))))
1253    }
1254}
1255
1256#[cfg(feature = "alloc")]
1257type EncodedMapEntry<'a> = (&'a str, EncodedTextKey<'a>, CborValueRef<'a>);
1258
1259#[cfg(feature = "alloc")]
1260struct MapIterEncoded<'a> {
1261    data: &'a [u8],
1262    pos: usize,
1263    remaining: usize,
1264    scratch: wire::SkipScratch,
1265}
1266
1267#[cfg(feature = "alloc")]
1268impl<'a> Iterator for MapIterEncoded<'a> {
1269    type Item = Result<EncodedMapEntry<'a>, CborError>;
1270
1271    fn next(&mut self) -> Option<Self::Item> {
1272        if self.remaining == 0 {
1273            return None;
1274        }
1275
1276        let key_start = self.pos;
1277        let mut key_pos = self.pos;
1278        let parsed = match read_text(self.data, &mut key_pos) {
1279            Ok(v) => v,
1280            Err(e) => {
1281                self.remaining = 0;
1282                return Some(Err(e));
1283            }
1284        };
1285        let key_end = key_pos;
1286
1287        let value_start = key_end;
1288        let end = match value_end_with_scratch(self.data, value_start, &mut self.scratch) {
1289            Ok(e) => e,
1290            Err(e) => {
1291                self.remaining = 0;
1292                return Some(Err(e));
1293            }
1294        };
1295
1296        self.pos = end;
1297        self.remaining -= 1;
1298
1299        Some(Ok((
1300            parsed,
1301            EncodedTextKey::new_unchecked(&self.data[key_start..key_end]),
1302            CborValueRef::new(self.data, value_start, end),
1303        )))
1304    }
1305}
1306
1307struct ArrayIter<'a> {
1308    data: &'a [u8],
1309    pos: usize,
1310    remaining: usize,
1311    scratch: wire::SkipScratch,
1312}
1313
1314impl<'a> Iterator for ArrayIter<'a> {
1315    type Item = Result<CborValueRef<'a>, CborError>;
1316
1317    fn next(&mut self) -> Option<Self::Item> {
1318        if self.remaining == 0 {
1319            return None;
1320        }
1321
1322        let start = self.pos;
1323        let end = match value_end_with_scratch(self.data, start, &mut self.scratch) {
1324            Ok(e) => e,
1325            Err(e) => {
1326                self.remaining = 0;
1327                return Some(Err(e));
1328            }
1329        };
1330
1331        self.pos = end;
1332        self.remaining -= 1;
1333
1334        Some(Ok(CborValueRef::new(self.data, start, end)))
1335    }
1336}