Skip to main content

laser_wire/
framing.rs

1// The shared CBOR entry points and the `[len: u32 LE][bytes]` socket framing,
2// sans-io: pure functions over byte slices, so the same code serves the
3// server's socket loop, LaserData Cloud's listener, and any future transport
4// without dragging a runtime into the contract. Every frame in the LaserData
5// band is named-field CBOR (RFC 8949, serde maps in declaration order via
6// `ciborium`). This module is the one encoding entry point so no consumer can
7// drift to a different encoding. Machine ids ride as fixed-width 16-byte CBOR
8// byte strings (the `wire_id!` serde uses `serialize_bytes`), not bignums.
9
10use crate::error::DecodeError;
11use crate::limits::MAX_FRAME_BYTES;
12use serde::Serialize;
13use serde::de::DeserializeOwned;
14
15/// Nesting a field scan follows before it refuses the value. Deeper input is
16/// never a named frame this band produces.
17const MAX_SCAN_DEPTH: u32 = 64;
18
19const MAJOR_BYTES: u8 = 2;
20const MAJOR_TEXT: u8 = 3;
21const MAJOR_ARRAY: u8 = 4;
22const MAJOR_MAP: u8 = 5;
23const MAJOR_TAG: u8 = 6;
24const MAJOR_SIMPLE: u8 = 7;
25const BREAK: u8 = 0xff;
26const NULL: u8 = 0xf6;
27
28/// Encode `value` as named-field CBOR (the band's canonical encoding).
29pub fn encode_named<T: Serialize + ?Sized>(value: &T) -> Result<Vec<u8>, DecodeError> {
30    let mut buffer = Vec::new();
31    encode_named_into(value, &mut buffer)?;
32    Ok(buffer)
33}
34
35/// The in-place form of [`encode_named`]: clear and fill the caller's buffer,
36/// so a hot loop reuses one allocation across encodes. Bytes identical to
37/// [`encode_named`] by construction (it delegates here), so there is still
38/// exactly one encoder.
39pub fn encode_named_into<T: Serialize + ?Sized>(
40    value: &T,
41    buffer: &mut Vec<u8>,
42) -> Result<(), DecodeError> {
43    buffer.clear();
44    ciborium::into_writer(value, &mut *buffer)
45        .map_err(|error| DecodeError::Encode(error.to_string()))
46}
47
48/// Decode named-field CBOR produced by [`encode_named`].
49///
50/// Rejects trailing bytes after the value. A band payload is exactly one CBOR
51/// item, so leftover bytes are a decode failure, never a silent skip.
52pub fn decode_named<T: DeserializeOwned>(payload: &[u8]) -> Result<T, DecodeError> {
53    let mut reader = payload;
54    let value = ciborium::from_reader(&mut reader)
55        .map_err(|error| DecodeError::Decode(error.to_string()))?;
56    if !reader.is_empty() {
57        return Err(DecodeError::Decode(format!(
58            "{} trailing byte(s) after the CBOR value",
59            reader.len()
60        )));
61    }
62    Ok(value)
63}
64
65/// Encode `value` as named-field CBOR behind its `[len: u32 LE]` prefix in one
66/// buffer sized for `capacity_hint` payload bytes, so a transport writes the
67/// frame without copying the encoded bytes again. Errors when the encoded
68/// value exceeds `max_bytes` or [`MAX_FRAME_BYTES`].
69pub fn encode_named_framed<T: Serialize + ?Sized>(
70    value: &T,
71    capacity_hint: usize,
72    max_bytes: usize,
73) -> Result<Vec<u8>, DecodeError> {
74    let mut framed = Vec::with_capacity(4 + capacity_hint);
75    framed.extend_from_slice(&[0; 4]);
76    ciborium::into_writer(value, &mut framed)
77        .map_err(|error| DecodeError::Encode(error.to_string()))?;
78    let length = framed.len() - 4;
79    if length > max_bytes.min(MAX_FRAME_BYTES) {
80        return Err(DecodeError::Frame(format!(
81            "payload is {length}B, exceeds frame cap {}B",
82            max_bytes.min(MAX_FRAME_BYTES)
83        )));
84    }
85    framed[..4].copy_from_slice(&(length as u32).to_le_bytes());
86    Ok(framed)
87}
88
89/// The top-level fields of one named-field CBOR map, each value left encoded
90/// in place.
91///
92/// One pass over the map finds every key and the bytes of its value and
93/// copies nothing: a byte string is stepped over by its length. A reader then
94/// decodes only the small values it needs and leaves a large one alone. Keys
95/// must be definite text and unique, nesting is bounded, and nothing may
96/// follow the map, so a malformed request is refused rather than read in
97/// part.
98#[derive(Debug, Default)]
99pub struct NamedFields<'a> {
100    fields: Vec<(&'a str, &'a [u8])>,
101}
102
103impl<'a> NamedFields<'a> {
104    /// Scan the fields of `payload`.
105    pub fn decode(payload: &'a [u8]) -> Result<Self, DecodeError> {
106        Self::decode_within(payload, usize::MAX)
107    }
108
109    /// [`Self::decode`], refusing a map of more than `max_fields` fields
110    /// before it scans past them, so a server can bound the work one request
111    /// costs before it authorizes it.
112    pub fn decode_within(payload: &'a [u8], max_fields: usize) -> Result<Self, DecodeError> {
113        let too_many = || {
114            DecodeError::Decode(format!(
115                "a named-field map holds more than {max_fields} fields"
116            ))
117        };
118        let mut cursor = Cursor {
119            bytes: payload,
120            at: 0,
121        };
122        let (major, argument) = cursor.head()?;
123        if major != MAJOR_MAP {
124            return Err(DecodeError::Decode(
125                "a named-field value is a map".to_owned(),
126            ));
127        }
128        let mut fields: Vec<(&'a str, &'a [u8])> = Vec::new();
129        match argument {
130            Argument::Value(count) => {
131                if usize::try_from(count)
132                    .ok()
133                    .is_none_or(|count| count > max_fields)
134                {
135                    return Err(too_many());
136                }
137                for _ in 0..count {
138                    fields.push(cursor.entry()?);
139                }
140            }
141            Argument::Indefinite => {
142                while !cursor.at_break()? {
143                    if fields.len() == max_fields {
144                        return Err(too_many());
145                    }
146                    fields.push(cursor.entry()?);
147                }
148                cursor.at += 1;
149            }
150        }
151        if cursor.at != payload.len() {
152            return Err(DecodeError::Decode(format!(
153                "{} trailing byte(s) after the CBOR value",
154                payload.len() - cursor.at
155            )));
156        }
157        // Sorted, so a map of many fields costs one sort, not a pass per field.
158        let mut keys: Vec<&str> = fields.iter().map(|(key, _)| *key).collect();
159        keys.sort_unstable();
160        if let Some(pair) = keys.windows(2).find(|pair| pair[0] == pair[1]) {
161            return Err(DecodeError::Decode(format!(
162                "duplicate field `{}`",
163                pair[0]
164            )));
165        }
166        Ok(Self { fields })
167    }
168
169    /// The encoded value of `key`, when the map holds it.
170    pub fn get(&self, key: &str) -> Option<&'a [u8]> {
171        self.fields
172            .iter()
173            .find(|(name, _)| *name == key)
174            .map(|(_, value)| *value)
175    }
176
177    /// The text value of `key`, borrowed from the payload. `Ok(None)` when the
178    /// field is absent or null, an error when it holds anything but text.
179    pub fn text(&self, key: &str) -> Result<Option<&'a str>, DecodeError> {
180        let Some(value) = self.get(key) else {
181            return Ok(None);
182        };
183        if value == [NULL] {
184            return Ok(None);
185        }
186        let mut cursor = Cursor {
187            bytes: value,
188            at: 0,
189        };
190        let (major, argument) = cursor.head()?;
191        let (MAJOR_TEXT, Argument::Value(length)) = (major, argument) else {
192            return Err(DecodeError::Decode(format!("field `{key}` is not text")));
193        };
194        let text = cursor.take(length)?;
195        std::str::from_utf8(text)
196            .map(Some)
197            .map_err(|_| DecodeError::Decode(format!("field `{key}` is not UTF-8")))
198    }
199
200    /// The value of `key` decoded on its own. `Ok(None)` when the field is
201    /// absent.
202    pub fn value<T: DeserializeOwned>(&self, key: &str) -> Result<Option<T>, DecodeError> {
203        self.get(key).map(decode_named).transpose()
204    }
205}
206
207/// The argument of one CBOR head: a count, a length, a value, or the
208/// indefinite marker.
209enum Argument {
210    Value(u64),
211    Indefinite,
212}
213
214struct Cursor<'a> {
215    bytes: &'a [u8],
216    at: usize,
217}
218
219impl<'a> Cursor<'a> {
220    fn entry(&mut self) -> Result<(&'a str, &'a [u8]), DecodeError> {
221        let (major, argument) = self.head()?;
222        let (MAJOR_TEXT, Argument::Value(length)) = (major, argument) else {
223            return Err(DecodeError::Decode(
224                "a named-field key is definite text".to_owned(),
225            ));
226        };
227        let key = std::str::from_utf8(self.take(length)?)
228            .map_err(|_| DecodeError::Decode("a named-field key is UTF-8".to_owned()))?;
229        let start = self.at;
230        self.skip(0)?;
231        Ok((key, &self.bytes[start..self.at]))
232    }
233
234    fn head(&mut self) -> Result<(u8, Argument), DecodeError> {
235        let initial = *self.bytes.get(self.at).ok_or_else(truncated)?;
236        self.at += 1;
237        let major = initial >> 5;
238        let argument = match initial & 0x1f {
239            info @ 0..=23 => Argument::Value(u64::from(info)),
240            24 => Argument::Value(u64::from(self.take(1)?[0])),
241            25 => Argument::Value(u64::from(u16::from_be_bytes(
242                self.take(2)?.try_into().map_err(|_| truncated())?,
243            ))),
244            26 => Argument::Value(u64::from(u32::from_be_bytes(
245                self.take(4)?.try_into().map_err(|_| truncated())?,
246            ))),
247            27 => Argument::Value(u64::from_be_bytes(
248                self.take(8)?.try_into().map_err(|_| truncated())?,
249            )),
250            31 => Argument::Indefinite,
251            _ => {
252                return Err(DecodeError::Decode(
253                    "reserved CBOR additional information".to_owned(),
254                ));
255            }
256        };
257        Ok((major, argument))
258    }
259
260    fn skip(&mut self, depth: u32) -> Result<(), DecodeError> {
261        if depth > MAX_SCAN_DEPTH {
262            return Err(DecodeError::Decode(format!(
263                "CBOR nesting exceeds {MAX_SCAN_DEPTH}"
264            )));
265        }
266        let (major, argument) = self.head()?;
267        match (major, argument) {
268            (MAJOR_BYTES | MAJOR_TEXT, Argument::Value(length)) => {
269                self.take(length)?;
270            }
271            (MAJOR_BYTES | MAJOR_TEXT, Argument::Indefinite) => {
272                while !self.at_break()? {
273                    let (chunk_major, chunk) = self.head()?;
274                    let Argument::Value(length) = chunk else {
275                        return Err(DecodeError::Decode(
276                            "an indefinite string chunk is definite".to_owned(),
277                        ));
278                    };
279                    if chunk_major != major {
280                        return Err(DecodeError::Decode(
281                            "an indefinite string chunk has its string's type".to_owned(),
282                        ));
283                    }
284                    self.take(length)?;
285                }
286                self.at += 1;
287            }
288            (MAJOR_ARRAY, Argument::Value(count)) => {
289                for _ in 0..count {
290                    self.skip(depth + 1)?;
291                }
292            }
293            (MAJOR_MAP, Argument::Value(count)) => {
294                for _ in 0..count {
295                    self.skip(depth + 1)?;
296                    self.skip(depth + 1)?;
297                }
298            }
299            (MAJOR_ARRAY | MAJOR_MAP, Argument::Indefinite) => {
300                let mut items = 0_u64;
301                while !self.at_break()? {
302                    self.skip(depth + 1)?;
303                    items += 1;
304                }
305                if major == MAJOR_MAP && items % 2 == 1 {
306                    return Err(DecodeError::Decode(
307                        "an indefinite map holds whole pairs".to_owned(),
308                    ));
309                }
310                self.at += 1;
311            }
312            (MAJOR_TAG, Argument::Value(_)) => self.skip(depth + 1)?,
313            (MAJOR_SIMPLE, Argument::Value(_)) => {}
314            (_, Argument::Value(_)) => {}
315            (_, Argument::Indefinite) => {
316                return Err(DecodeError::Decode(
317                    "only strings, arrays and maps are indefinite".to_owned(),
318                ));
319            }
320        }
321        Ok(())
322    }
323
324    fn at_break(&self) -> Result<bool, DecodeError> {
325        self.bytes
326            .get(self.at)
327            .map(|byte| *byte == BREAK)
328            .ok_or_else(truncated)
329    }
330
331    fn take(&mut self, length: u64) -> Result<&'a [u8], DecodeError> {
332        let end = usize::try_from(length)
333            .ok()
334            .and_then(|length| self.at.checked_add(length))
335            .filter(|end| *end <= self.bytes.len())
336            .ok_or_else(truncated)?;
337        let taken = &self.bytes[self.at..end];
338        self.at = end;
339        Ok(taken)
340    }
341}
342
343fn truncated() -> DecodeError {
344    DecodeError::Decode("the CBOR value is truncated".to_owned())
345}
346
347/// Frame `payload` as `[len: u32 LE][payload]`. Errors when the payload
348/// exceeds [`MAX_FRAME_BYTES`].
349pub fn frame_encode(payload: &[u8]) -> Result<Vec<u8>, DecodeError> {
350    if payload.len() > MAX_FRAME_BYTES {
351        return Err(DecodeError::Frame(format!(
352            "payload is {}B, exceeds frame cap {MAX_FRAME_BYTES}B",
353            payload.len()
354        )));
355    }
356    let mut framed = Vec::with_capacity(4 + payload.len());
357    framed.extend_from_slice(&(payload.len() as u32).to_le_bytes());
358    framed.extend_from_slice(payload);
359    Ok(framed)
360}
361
362/// Parse one frame from the front of `buf`. `Ok(None)` means the buffer does
363/// not yet hold a whole frame, so read more bytes. `Ok(Some((payload, consumed)))`
364/// is one payload and how many bytes of `buf` it spanned (`4 + len`). Errors
365/// when the length prefix exceeds [`MAX_FRAME_BYTES`].
366pub fn frame_decode(buf: &[u8]) -> Result<Option<(&[u8], usize)>, DecodeError> {
367    let Some(prefix) = buf.get(..4) else {
368        return Ok(None);
369    };
370    let len = u32::from_le_bytes(prefix.try_into().expect("4-byte slice")) as usize;
371    if len > MAX_FRAME_BYTES {
372        return Err(DecodeError::Frame(format!(
373            "frame length {len}B exceeds cap {MAX_FRAME_BYTES}B"
374        )));
375    }
376    match buf.get(4..4 + len) {
377        Some(payload) => Ok(Some((payload, 4 + len))),
378        None => Ok(None),
379    }
380}
381
382#[cfg(test)]
383mod tests {
384    use super::*;
385    use std::time::{Duration, Instant};
386
387    #[test]
388    fn given_a_payload_when_framed_then_should_round_trip() {
389        let framed = frame_encode(b"hello").expect("frames");
390        assert_eq!(&framed[..4], &5u32.to_le_bytes());
391        let (payload, consumed) = frame_decode(&framed)
392            .expect("decodes")
393            .expect("whole frame present");
394        assert_eq!(payload, b"hello");
395        assert_eq!(consumed, framed.len());
396    }
397
398    #[test]
399    fn given_a_partial_frame_when_decoded_then_should_ask_for_more() {
400        let framed = frame_encode(b"hello").expect("frames");
401        assert!(frame_decode(&framed[..3]).expect("no error").is_none());
402        assert!(frame_decode(&framed[..6]).expect("no error").is_none());
403    }
404
405    #[test]
406    fn given_an_oversized_length_prefix_when_decoded_then_should_error() {
407        let mut bad = Vec::new();
408        bad.extend_from_slice(&((MAX_FRAME_BYTES as u32) + 1).to_le_bytes());
409        assert!(matches!(frame_decode(&bad), Err(DecodeError::Frame(_))));
410    }
411
412    #[test]
413    fn given_named_encoding_when_round_tripped_then_should_preserve_fields() {
414        #[derive(serde::Serialize, serde::Deserialize, PartialEq, Debug)]
415        struct Body {
416            id: u32,
417            name: String,
418        }
419        let body = Body {
420            id: 7,
421            name: "alice".to_owned(),
422        };
423        let bytes = encode_named(&body).expect("encodes");
424        let back: Body = decode_named(&bytes).expect("decodes");
425        assert_eq!(back, body);
426    }
427
428    #[test]
429    fn given_trailing_bytes_when_decoded_then_should_error() {
430        let mut bytes = encode_named(&7u32).expect("encodes");
431        bytes.push(0x00);
432        assert!(matches!(
433            decode_named::<u32>(&bytes),
434            Err(DecodeError::Decode(_))
435        ));
436    }
437    #[test]
438    fn given_the_in_place_encoder_when_reused_then_should_match_encode_named_bytes() {
439        let value = serde_json::json!({"a": 1, "b": "two"});
440        let mut buffer = vec![0xff; 64]; // dirty, to prove the clear
441        super::encode_named_into(&value, &mut buffer).expect("encodes");
442        assert_eq!(buffer, super::encode_named(&value).expect("encodes"));
443    }
444
445    #[test]
446    fn given_the_framed_encoder_when_used_then_should_prefix_the_named_bytes_and_cap_them() {
447        let value = serde_json::json!({"a": 1, "b": "two"});
448        let framed = encode_named_framed(&value, 16, 1024).expect("frames");
449        let encoded = encode_named(&value).expect("encodes");
450        assert_eq!(&framed[..4], &(encoded.len() as u32).to_le_bytes());
451        assert_eq!(&framed[4..], &encoded);
452        assert_eq!(
453            encode_named_framed(&value, 16, encoded.len()).expect("exact cap frames"),
454            framed
455        );
456        assert!(matches!(
457            encode_named_framed(&value, 16, encoded.len() - 1),
458            Err(DecodeError::Frame(_))
459        ));
460    }
461
462    #[test]
463    fn given_a_payload_at_the_frame_cap_when_framed_then_should_round_trip_and_refuse_one_byte_more()
464     {
465        let over = vec![7u8; MAX_FRAME_BYTES + 1];
466        let payload = &over[..MAX_FRAME_BYTES];
467        let framed = frame_encode(payload).expect("a payload at the cap frames");
468        assert_eq!(&framed[..4], &(MAX_FRAME_BYTES as u32).to_le_bytes());
469        let (decoded, consumed) = frame_decode(&framed)
470            .expect("a frame at the cap decodes")
471            .expect("whole frame present");
472        assert!(decoded == payload, "the payload round trips");
473        assert_eq!(consumed, framed.len());
474        assert!(matches!(frame_encode(&over), Err(DecodeError::Frame(_))));
475    }
476
477    #[derive(serde::Serialize)]
478    struct Keyed {
479        v: u32,
480        namespace: String,
481        #[serde(default, skip_serializing_if = "Option::is_none")]
482        session: Option<String>,
483        #[serde(with = "crate::encoding::bin_bytes")]
484        value: Vec<u8>,
485    }
486
487    fn distinct_fields(count: u32) -> Vec<u8> {
488        let mut payload = vec![0xba];
489        payload.extend_from_slice(&count.to_be_bytes());
490        for index in 0..count {
491            payload.extend(text(&format!("f{index}")));
492            payload.push(0xf6);
493        }
494        payload
495    }
496
497    fn fastest_scan(payload: &[u8]) -> Duration {
498        (0..3)
499            .map(|_| {
500                let started = Instant::now();
501                NamedFields::decode(payload).expect("distinct fields scan");
502                started.elapsed()
503            })
504            .min()
505            .unwrap_or_default()
506    }
507
508    fn text(value: &str) -> Vec<u8> {
509        let mut bytes = vec![0x60 | value.len() as u8];
510        bytes.extend_from_slice(value.as_bytes());
511        bytes
512    }
513
514    // One pass finds every field, and the large byte string is a window into
515    // the payload rather than a copy of it.
516    #[test]
517    fn given_a_named_map_when_scanned_then_should_find_each_value_in_place() {
518        let keyed = Keyed {
519            v: 1,
520            namespace: "notes".to_owned(),
521            session: None,
522            value: vec![7; 8 << 20],
523        };
524        let payload = encode_named(&keyed).expect("encodes");
525        let fields = NamedFields::decode(&payload).expect("scans");
526        assert_eq!(fields.text("namespace").expect("text"), Some("notes"));
527        assert_eq!(fields.value::<u32>("v").expect("value"), Some(1));
528        assert_eq!(fields.text("session").expect("absent"), None);
529        assert_eq!(fields.value::<u32>("session").expect("absent"), None);
530        let value = fields.get("value").expect("present");
531        assert!(value.len() > 8 << 20);
532        let range = payload.as_ptr_range();
533        assert!(
534            range.contains(&value.as_ptr()) && value.as_ptr_range().end <= range.end,
535            "the value is borrowed from the payload"
536        );
537        assert!(
538            matches!(fields.text("value"), Err(DecodeError::Decode(_))),
539            "a byte string is not text"
540        );
541        assert!(
542            matches!(
543                fields.value::<u32>("namespace"),
544                Err(DecodeError::Decode(_))
545            ),
546            "text is not a number"
547        );
548    }
549
550    #[test]
551    fn given_indefinite_and_nested_values_when_scanned_then_should_step_over_them() {
552        // {"a": [1, {"x": "y"}], "b": (_ "ab" "cd"), "c": 1(2), "d": null}
553        let mut payload = vec![0xbf];
554        payload.extend(text("a"));
555        payload.extend([0x9f, 0x01, 0xa1]);
556        payload.extend(text("x"));
557        payload.extend(text("y"));
558        payload.push(0xff);
559        payload.extend(text("b"));
560        payload.extend([0x7f, 0x62, b'a', b'b', 0x62, b'c', b'd', 0xff]);
561        payload.extend(text("c"));
562        payload.extend([0xc1, 0x02]);
563        payload.extend(text("d"));
564        payload.push(0xf6);
565        payload.push(0xff);
566        let fields = NamedFields::decode(&payload).expect("scans");
567        assert_eq!(fields.get("a"), Some(&payload[3..11]));
568        assert_eq!(
569            fields.value::<String>("b").expect("joined"),
570            Some("abcd".to_owned())
571        );
572        assert!(
573            matches!(fields.text("b"), Err(DecodeError::Decode(_))),
574            "a borrowed text is definite"
575        );
576        assert_eq!(fields.value::<u32>("c").expect("tagged"), Some(2));
577        assert_eq!(fields.text("d").expect("null reads as absent"), None);
578        assert_eq!(fields.value::<Option<u32>>("d").expect("null"), Some(None));
579    }
580
581    #[test]
582    fn given_heads_with_one_two_and_eight_byte_arguments_when_scanned_then_should_find_each_value()
583    {
584        // {"a": 24 bytes, "b": 256 bytes, "c": 1 as a 64-bit integer}
585        let mut payload = vec![0xa3];
586        payload.extend(text("a"));
587        let one = payload.len();
588        payload.extend([0x58, 24]);
589        payload.extend([7; 24]);
590        let one_end = payload.len();
591        payload.extend(text("b"));
592        let two = payload.len();
593        payload.extend([0x59, 0x01, 0x00]);
594        payload.extend([7; 256]);
595        let two_end = payload.len();
596        payload.extend(text("c"));
597        let eight = payload.len();
598        payload.extend([0x1b, 0, 0, 0, 0, 0, 0, 0, 1]);
599        let fields = NamedFields::decode(&payload).expect("scans");
600        assert_eq!(fields.get("a"), Some(&payload[one..one_end]));
601        assert_eq!(fields.get("b"), Some(&payload[two..two_end]));
602        assert_eq!(fields.get("c"), Some(&payload[eight..]));
603    }
604
605    #[test]
606    fn given_arrays_nested_to_the_scan_bound_when_scanned_then_should_accept_them_and_refuse_one_more()
607     {
608        let bound = MAX_SCAN_DEPTH as usize;
609        for (levels, accepted) in [(bound, true), (bound + 1, false)] {
610            let mut payload = vec![0xa1];
611            payload.extend(text("a"));
612            payload.extend(std::iter::repeat_n(0x81, levels));
613            payload.push(0x01);
614            assert_eq!(
615                NamedFields::decode(&payload).is_ok(),
616                accepted,
617                "levels: {levels}"
618            );
619        }
620    }
621
622    #[test]
623    fn given_nesting_past_the_scan_bound_in_any_container_when_scanned_then_should_refuse_it() {
624        let levels = MAX_SCAN_DEPTH as usize + 1;
625        let nested = |open: &[u8], close: &[u8]| {
626            let mut payload = vec![0xa1];
627            payload.extend(text("a"));
628            for _ in 0..levels {
629                payload.extend_from_slice(open);
630            }
631            payload.push(0x01);
632            for _ in 0..levels {
633                payload.extend_from_slice(close);
634            }
635            payload
636        };
637        for (case, payload) in [
638            ("map keys", nested(&[0xa1], &[0x01])),
639            ("map values", nested(&[0xa1, 0x01], &[])),
640            ("indefinite arrays", nested(&[0x9f], &[0xff])),
641            ("tags", nested(&[0xc1], &[])),
642        ] {
643            assert!(
644                matches!(NamedFields::decode(&payload), Err(DecodeError::Decode(_))),
645                "{case} must be refused"
646            );
647        }
648    }
649
650    #[test]
651    fn given_a_nested_indefinite_map_when_scanned_then_should_accept_only_whole_pairs() {
652        let field = |value: &[u8]| {
653            let mut payload = vec![0xa1];
654            payload.extend(text("a"));
655            payload.extend_from_slice(value);
656            payload
657        };
658        assert!(
659            matches!(
660                NamedFields::decode(&field(&[0xbf, 0x01, 0xff])),
661                Err(DecodeError::Decode(_))
662            ),
663            "a key without a value must be refused"
664        );
665        for (case, value) in [
666            ("one pair", &[0xbf, 0x01, 0x02, 0xff][..]),
667            ("an odd array", &[0x9f, 0x01, 0xff][..]),
668        ] {
669            let payload = field(value);
670            let fields = NamedFields::decode(&payload).expect(case);
671            assert_eq!(fields.get("a"), Some(value), "{case}");
672        }
673    }
674
675    #[test]
676    fn given_a_named_map_with_many_distinct_fields_when_scanned_then_should_check_them_in_linearithmic_time()
677     {
678        // Eight times the fields cost about ten times the work when linearithmic and 64 when quadratic.
679        let small = fastest_scan(&distinct_fields(10_000));
680        let large_payload = distinct_fields(80_000);
681        let large = fastest_scan(&large_payload);
682
683        assert!(
684            large < small.max(Duration::from_micros(100)) * 32,
685            "80,000 fields took {large:?}, 10,000 took {small:?}"
686        );
687        let fields = NamedFields::decode(&large_payload).expect("distinct fields scan");
688        assert_eq!(fields.get("f79999"), Some(&[0xf6_u8][..]));
689        let mut repeated = large_payload;
690        repeated.extend(text("f7"));
691        repeated.push(0xf6);
692        repeated[1..5].copy_from_slice(&80_001_u32.to_be_bytes());
693        assert!(matches!(
694            NamedFields::decode(&repeated),
695            Err(DecodeError::Decode(_))
696        ));
697    }
698
699    #[test]
700    fn given_a_named_map_over_the_field_bound_when_scanned_within_it_then_should_refuse_it() {
701        for indefinite in [false, true] {
702            let mut payload = if indefinite { vec![0xbf] } else { vec![0xa3] };
703            for key in ["a", "b", "c"] {
704                payload.extend(text(key));
705                payload.push(0x01);
706            }
707            if indefinite {
708                payload.push(0xff);
709            }
710
711            assert!(
712                matches!(
713                    NamedFields::decode_within(&payload, 2),
714                    Err(DecodeError::Decode(_))
715                ),
716                "indefinite: {indefinite}"
717            );
718            let within = NamedFields::decode_within(&payload, 3).expect("three fields fit");
719            assert_eq!(within.get("c"), Some(&[0x01_u8][..]));
720        }
721    }
722
723    #[test]
724    fn given_malformed_named_maps_when_scanned_then_should_refuse_them() {
725        let mut duplicate = vec![0xa2];
726        duplicate.extend(text("a"));
727        duplicate.push(0x01);
728        duplicate.extend(text("a"));
729        duplicate.push(0x02);
730        let mut trailing = vec![0xa1];
731        trailing.extend(text("a"));
732        trailing.extend([0x01, 0x00]);
733        let mut numeric_key = vec![0xa1, 0x01, 0x01];
734        let mut truncated = vec![0xa1];
735        truncated.extend(text("a"));
736        truncated.extend([0x45, 0x01, 0x02]);
737        let mut odd = vec![0xbf];
738        odd.extend(text("a"));
739        odd.extend([0x01, 0xbf, 0x01, 0xff, 0xff]);
740        let mut deep = vec![0xa1];
741        deep.extend(text("a"));
742        deep.extend(std::iter::repeat_n(0x81, MAX_SCAN_DEPTH as usize + 2));
743        deep.push(0x01);
744        let mut reserved = vec![0xa1];
745        reserved.extend(text("a"));
746        reserved.push(0x1c);
747        let mut indefinite_number = vec![0xa1];
748        indefinite_number.extend(text("a"));
749        indefinite_number.push(0x1f);
750        numeric_key.push(0x00);
751        numeric_key.truncate(3);
752        for (case, payload) in [
753            ("duplicate", duplicate),
754            ("trailing", trailing),
755            ("numeric key", numeric_key),
756            ("truncated", truncated),
757            ("odd indefinite map", odd),
758            ("deep", deep),
759            ("reserved info", reserved),
760            ("indefinite number", indefinite_number),
761            ("not a map", vec![0x01]),
762            ("empty", Vec::new()),
763        ] {
764            assert!(
765                matches!(NamedFields::decode(&payload), Err(DecodeError::Decode(_))),
766                "{case} must be refused"
767            );
768        }
769        assert!(
770            NamedFields::decode(&[0xa0])
771                .expect("empty map")
772                .get("a")
773                .is_none()
774        );
775    }
776}