Skip to main content

didbot_data/dag_cbor/
read.rs

1//! Reading a block back, which is a stricter job than writing one.
2//!
3//! An encoder has to produce the one canonical form. A decoder has to *refuse*
4//! every other form, and that asymmetry is the whole of this file. Plain CBOR
5//! has five widths for the same integer, two lengths forms for every string,
6//! any order for a map's keys and a tag space of thousands; all of it decodes
7//! to something. A decoder that accepted the lot would hand back values whose
8//! re-encoding is a different block with a different CID — which is to say it
9//! would let a peer hand this server two spellings of one record and get two
10//! identities for it.
11//!
12//! So the rule here is: a block that does not re-encode to itself is not a
13//! block. Every non-canonical form is an error rather than a value, and
14//! [`decode`]'s own tests assert the round trip rather than trusting the
15//! reading.
16//!
17//! What this refuses that CBOR allows, in the order a parser meets it:
18//!
19//! * an argument written wider than it needs — `0x18 0x05` for 5;
20//! * an indefinite length, on a string, a list or a map;
21//! * a float, a half float included, and the `undefined` simple value;
22//! * a simple value below 32 written in the one-byte form — `f8 14` is a
23//!   second spelling of `f4`, and a decoder that read it would re-encode the
24//!   block to a different CID;
25//! * a tag other than 42, and a tag 42 over anything but an
26//!   identity-prefixed CID;
27//! * a map key that is not a text string, and a map whose keys are not in
28//!   ascending DAG-CBOR order — which also rules out a duplicate key;
29//! * a CID this crate cannot re-encode: see [`Cid::from_bytes`].
30//!
31//! Nesting is bounded by [`MAX_DEPTH`]. A block arrives from a peer, and a
32//! recursive descent parser over a few kilobytes of nested arrays is a stack
33//! overflow — which aborts the process rather than unwinding, so it cannot be
34//! caught anywhere upstream.
35
36use std::collections::BTreeMap;
37
38use super::{
39    key_order, IDENTITY_MULTIBASE, MAJOR_BYTES, MAJOR_LIST, MAJOR_MAP, MAJOR_NEGATIVE,
40    MAJOR_SIMPLE, MAJOR_TAG, MAJOR_TEXT, MAJOR_UNSIGNED, TAG_LINK,
41};
42use crate::cid::{Cid, CidError};
43use crate::value::Value;
44
45/// How deep a value may nest before it is refused.
46///
47/// Records nest a handful of levels — an embed inside a post, images inside
48/// the embed — and a Merkle search tree node nests two. A hundred is far past
49/// anything the protocol produces and far below the depth that would trouble
50/// the stack.
51pub const MAX_DEPTH: usize = 100;
52
53/// Why a block is not a value in this data model.
54#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
55pub enum CborError {
56    /// The block ended in the middle of a value.
57    #[error("the block ends inside a value: {0} more bytes were needed")]
58    Truncated(usize),
59    /// A value was complete and bytes followed it. A block is one value.
60    #[error("{0} bytes follow the end of the value")]
61    Trailing(usize),
62    /// An argument written in a wider form than the shortest that holds it.
63    #[error("{argument} is written in a {width}-byte form, which is not the shortest")]
64    NonCanonicalArgument {
65        /// The number that was written.
66        argument: u64,
67        /// How many bytes it was written in.
68        width: usize,
69    },
70    /// One of the indefinite-length forms, which DAG-CBOR does not have.
71    #[error("indefinite lengths are not part of dag-cbor")]
72    IndefiniteLength,
73    /// A reserved additional-information value: 28, 29 or 30.
74    #[error("additional information {0} is reserved")]
75    ReservedArgument(u8),
76    /// A float, of any width. The data model has no float; see [`Value`].
77    #[error("the data model has no floats")]
78    Float,
79    /// A simple value other than `false`, `true` and `null`.
80    #[error("simple value {0} is not one of false, true and null")]
81    Simple(u8),
82    /// An integer past what an `i64` holds, which the data model's one number
83    /// type cannot carry.
84    #[error("{0} is outside the integers the data model holds")]
85    IntegerRange(u64),
86    /// A text string, or a map key, that is not UTF-8.
87    #[error("a text string is not valid utf-8")]
88    NotUtf8,
89    /// A tag other than 42.
90    #[error("tag {0} is not the link tag, which is the only tag dag-cbor has")]
91    Tag(u64),
92    /// Tag 42 over something that is not a byte string.
93    #[error("a link tag must carry a byte string")]
94    LinkNotBytes,
95    /// A link's byte string without the identity multibase prefix.
96    #[error("a link's bytes must begin with the identity multibase prefix")]
97    LinkPrefix,
98    /// A link whose bytes are not a CID this crate can re-encode.
99    #[error("a link is not a usable cid: {0}")]
100    Link(#[from] CidError),
101    /// A map key that is not a text string.
102    #[error("a map key must be a text string")]
103    KeyNotText,
104    /// Map keys out of order, which includes a repeated key.
105    #[error("map key {found:?} does not sort after {previous:?}")]
106    KeyOrder {
107        /// The key that was read.
108        found: String,
109        /// The key before it.
110        previous: String,
111    },
112    /// More nesting than [`MAX_DEPTH`].
113    #[error("the value nests deeper than {MAX_DEPTH} levels")]
114    TooDeep,
115}
116
117/// Decodes one block into the data model.
118///
119/// The block is one value and nothing after it, which is what a
120/// content-addressed block is: bytes that hash to a name. Trailing bytes are
121/// an error rather than an ignored tail, because they change the hash while
122/// leaving the value alone.
123///
124/// ```
125/// use didbot_data::{dag_cbor, Value};
126///
127/// let value = Value::from_json(&serde_json::json!({"a": 1})).unwrap();
128/// let block = dag_cbor::encode(&value);
129/// assert_eq!(dag_cbor::decode(&block).unwrap(), value);
130///
131/// // 5 written in two bytes rather than one is not a block.
132/// assert!(dag_cbor::decode(&[0x18, 0x05]).is_err());
133/// ```
134pub fn decode(block: &[u8]) -> Result<Value, CborError> {
135    let mut reader = Reader { rest: block };
136    let value = reader.value(0)?;
137    if !reader.rest.is_empty() {
138        return Err(CborError::Trailing(reader.rest.len()));
139    }
140    Ok(value)
141}
142
143/// A cursor over the block being read.
144struct Reader<'a> {
145    /// What has not been read yet.
146    rest: &'a [u8],
147}
148
149/// A major type and the argument that followed it.
150struct Header {
151    /// The major type, 0 to 7.
152    major: u8,
153    /// The argument, whatever width it was written in.
154    argument: u64,
155    /// How many bytes the argument was written in, 0 for the inline form.
156    ///
157    /// Kept because major type 7 reads it as a *kind* rather than a width:
158    /// two, four and eight bytes are a half, single and double float, and one
159    /// byte is a simple value in the extended range.
160    width: usize,
161}
162
163impl<'a> Reader<'a> {
164    /// Takes `count` bytes, or says how many were missing.
165    fn take(&mut self, count: usize) -> Result<&'a [u8], CborError> {
166        if self.rest.len() < count {
167            return Err(CborError::Truncated(count - self.rest.len()));
168        }
169        let (taken, rest) = self.rest.split_at(count);
170        self.rest = rest;
171        Ok(taken)
172    }
173
174    /// Reads a header, refusing every non-shortest and indefinite form.
175    fn header(&mut self) -> Result<Header, CborError> {
176        let initial = self.take(1)?[0];
177        let major = initial >> 5;
178        let information = initial & 0x1f;
179        let (argument, width) = match information {
180            0..=23 => (u64::from(information), 0),
181            24 => (u64::from(self.take(1)?[0]), 1),
182            25 => {
183                let bytes: [u8; 2] = self.take(2)?.try_into().unwrap_or_default();
184                (u64::from(u16::from_be_bytes(bytes)), 2)
185            }
186            26 => {
187                let bytes: [u8; 4] = self.take(4)?.try_into().unwrap_or_default();
188                (u64::from(u32::from_be_bytes(bytes)), 4)
189            }
190            27 => {
191                let bytes: [u8; 8] = self.take(8)?.try_into().unwrap_or_default();
192                (u64::from_be_bytes(bytes), 8)
193            }
194            31 => return Err(CborError::IndefiniteLength),
195            reserved => return Err(CborError::ReservedArgument(reserved)),
196        };
197        // A float is a major type 7 argument and is caught below, where the
198        // simple values are read: the widths overlap, and 25, 26 and 27 mean
199        // "half, single, double" there rather than "a wider integer".
200        //
201        // Major type 7's one-byte form is not a width either, and it has its
202        // own shortest rule: it carries a simple value in the extended range,
203        // 32 to 255, and a value below 32 written in it is a second spelling
204        // of a byte that already exists. `f8 14` and `f4` would both be
205        // `false`, and only one of them is the block that value hashes as.
206        let non_canonical = if major == MAJOR_SIMPLE {
207            width == 1 && argument < 32
208        } else {
209            !shortest(argument, width)
210        };
211        if non_canonical {
212            return Err(CborError::NonCanonicalArgument { argument, width });
213        }
214        Ok(Header {
215            major,
216            argument,
217            width,
218        })
219    }
220
221    /// Reads one value at `depth`.
222    fn value(&mut self, depth: usize) -> Result<Value, CborError> {
223        if depth > MAX_DEPTH {
224            return Err(CborError::TooDeep);
225        }
226        let header = self.header()?;
227        match header.major {
228            MAJOR_UNSIGNED => Ok(Value::Integer(integer(header.argument)?)),
229            // A negative integer is stored as -1 - n, so the representable
230            // magnitude is one larger than the positive side. `-1 - n` on the
231            // i64 the argument fits is exactly that.
232            MAJOR_NEGATIVE => Ok(Value::Integer(-1 - integer(header.argument)?)),
233            MAJOR_BYTES => self
234                .bytes(header.argument)
235                .map(<[u8]>::to_vec)
236                .map(Value::Bytes),
237            MAJOR_TEXT => self.text(header.argument).map(Value::String),
238            MAJOR_LIST => self.list(header.argument, depth),
239            MAJOR_MAP => self.map(header.argument, depth),
240            MAJOR_TAG => self.link(header.argument, depth),
241            // 7 by elimination: three bits hold nothing else. The wider
242            // forms are the floats; the inline and one-byte forms are simple
243            // values.
244            _ if header.width >= 2 => Err(CborError::Float),
245            _ => simple(header.argument),
246        }
247    }
248
249    /// Reads a byte string's contents.
250    fn bytes(&mut self, length: u64) -> Result<&'a [u8], CborError> {
251        let length = usize::try_from(length).map_err(|_| CborError::Truncated(0))?;
252        self.take(length)
253    }
254
255    /// Reads a text string's contents, which must be UTF-8.
256    fn text(&mut self, length: u64) -> Result<String, CborError> {
257        let bytes = self.bytes(length)?;
258        std::str::from_utf8(bytes)
259            .map(str::to_owned)
260            .map_err(|_| CborError::NotUtf8)
261    }
262
263    /// Reads a list's items.
264    fn list(&mut self, count: u64, depth: usize) -> Result<Value, CborError> {
265        let mut items = Vec::new();
266        for _ in 0..count {
267            // The count is a claim by the block and is not trusted as a
268            // capacity: a two-byte block can claim four billion items.
269            items.push(self.value(depth + 1)?);
270        }
271        Ok(Value::List(items))
272    }
273
274    /// Reads a map's entries, which must be text-keyed and in order.
275    ///
276    /// The ordering check is what makes decoding the inverse of encoding: a
277    /// `BTreeMap` would happily hold keys read in any order and re-encode
278    /// them sorted, so the block that arrived and the block that leaves would
279    /// differ while every value in them matched.
280    fn map(&mut self, count: u64, depth: usize) -> Result<Value, CborError> {
281        let mut entries = BTreeMap::new();
282        let mut previous: Option<String> = None;
283        for _ in 0..count {
284            let header = self.header()?;
285            if header.major != MAJOR_TEXT {
286                return Err(CborError::KeyNotText);
287            }
288            let key = self.text(header.argument)?;
289            if let Some(previous) = &previous {
290                if key_order(previous, &key) != std::cmp::Ordering::Less {
291                    return Err(CborError::KeyOrder {
292                        found: key,
293                        previous: previous.clone(),
294                    });
295                }
296            }
297            let value = self.value(depth + 1)?;
298            previous = Some(key.clone());
299            entries.insert(key, value);
300        }
301        Ok(Value::Map(entries))
302    }
303
304    /// Reads a tagged value, which in DAG-CBOR is a link and nothing else.
305    fn link(&mut self, tag: u64, depth: usize) -> Result<Value, CborError> {
306        if tag != TAG_LINK {
307            return Err(CborError::Tag(tag));
308        }
309        if depth > MAX_DEPTH {
310            return Err(CborError::TooDeep);
311        }
312        let header = self.header()?;
313        if header.major != MAJOR_BYTES {
314            return Err(CborError::LinkNotBytes);
315        }
316        let bytes = self.bytes(header.argument)?;
317        match bytes.split_first() {
318            Some((&IDENTITY_MULTIBASE, cid)) => Ok(Value::Link(Cid::from_bytes(cid)?)),
319            _ => Err(CborError::LinkPrefix),
320        }
321    }
322}
323
324/// Whether an argument is written in the narrowest form that holds it.
325fn shortest(argument: u64, width: usize) -> bool {
326    match width {
327        0 => argument < 24,
328        1 => argument >= 24 && u8::try_from(argument).is_ok(),
329        2 => argument > u64::from(u8::MAX) && u16::try_from(argument).is_ok(),
330        4 => argument > u64::from(u16::MAX) && u32::try_from(argument).is_ok(),
331        _ => argument > u64::from(u32::MAX),
332    }
333}
334
335/// An argument as the data model's one number type.
336fn integer(argument: u64) -> Result<i64, CborError> {
337    i64::try_from(argument).map_err(|_| CborError::IntegerRange(argument))
338}
339
340/// The three simple values the profile has, and the refusal of everything else.
341///
342/// Only the inline and one-byte forms reach here, so the argument is a simple
343/// value and never a float's payload; the floats are refused by width in
344/// [`Reader::value`]. 23 is `undefined`, which the data model does not have,
345/// and 32 upwards is the unassigned extended range.
346fn simple(argument: u64) -> Result<Value, CborError> {
347    match argument {
348        20 => Ok(Value::Bool(false)),
349        21 => Ok(Value::Bool(true)),
350        22 => Ok(Value::Null),
351        other => Err(CborError::Simple(u8::try_from(other).unwrap_or(u8::MAX))),
352    }
353}
354
355#[cfg(test)]
356mod tests {
357    use super::super::encode;
358    use super::*;
359    use serde_json::json;
360
361    /// Every value this crate encodes reads back as itself. This is the whole
362    /// claim the decoder makes, so it is made over a value with one of
363    /// everything in it.
364    #[test]
365    fn a_block_decodes_to_the_value_it_was_encoded_from() {
366        let mut value = Value::object_from_json(&json!({
367            "text": "quernstone",
368            "count": 23,
369            "wide": 65536,
370            "negative": -500,
371            "flag": true,
372            "missing": null,
373            "list": [1, 2, {"nested": []}],
374            "bytes": {"$bytes": "aGVsbG8"},
375            "link": {"$link": "bafyreidfayvfuwqa7qlnopdjiqrxzs6blmoeu4rujcjtnci5beludirz2a"},
376        }))
377        .expect("a data-model object");
378        // The map key ordering is the encoder's, so a value whose keys are
379        // both short and long exercises the length-first rule on the way back.
380        if let Value::Map(entries) = &mut value {
381            entries.insert("aa".to_owned(), Value::Integer(1));
382            entries.insert("b".to_owned(), Value::Integer(2));
383        }
384        assert_eq!(decode(&encode(&value)).unwrap(), value);
385    }
386
387    /// The widths a number can be written in are exactly one per number, and
388    /// the other four are errors rather than the same number again.
389    #[test]
390    fn a_wider_form_of_a_number_is_not_the_number() {
391        assert_eq!(decode(&[0x05]).unwrap(), Value::Integer(5));
392        for wider in [
393            vec![0x18, 0x05],
394            vec![0x19, 0x00, 0x05],
395            vec![0x1a, 0x00, 0x00, 0x00, 0x05],
396            vec![0x1b, 0, 0, 0, 0, 0, 0, 0, 5],
397        ] {
398            assert!(
399                matches!(
400                    decode(&wider),
401                    Err(CborError::NonCanonicalArgument { argument: 5, .. })
402                ),
403                "{wider:02x?} decoded"
404            );
405        }
406    }
407
408    /// The boundary of each width is canonical in the narrower form and not
409    /// in the wider one, which is where an off-by-one in `shortest` lands.
410    #[test]
411    fn each_width_holds_exactly_its_own_range() {
412        assert_eq!(decode(&[0x17]).unwrap(), Value::Integer(23));
413        assert_eq!(decode(&[0x18, 0x18]).unwrap(), Value::Integer(24));
414        assert_eq!(decode(&[0x18, 0xff]).unwrap(), Value::Integer(255));
415        assert_eq!(decode(&[0x19, 0x01, 0x00]).unwrap(), Value::Integer(256));
416        assert!(decode(&[0x19, 0x00, 0xff]).is_err());
417        assert!(decode(&[0x1a, 0x00, 0x00, 0xff, 0xff]).is_err());
418    }
419
420    /// An indefinite-length string is the form a streaming encoder reaches
421    /// for and the form a content-addressed block may never have.
422    #[test]
423    fn indefinite_lengths_are_refused() {
424        // 0x5f: an indefinite-length byte string, then a chunk, then a break.
425        assert_eq!(
426            decode(&[0x5f, 0x41, 0x61, 0xff]),
427            Err(CborError::IndefiniteLength)
428        );
429    }
430
431    /// `f8` carries a simple value in the extended range, 32 to 255. A value
432    /// below 32 written in it is a second spelling of a one-byte form, and
433    /// accepting it would mean two blocks — two CIDs — for one value.
434    ///
435    /// This is the sharpest form of the round-trip rule in this file, so it
436    /// is asserted as the round trip: whatever `decode` accepts must
437    /// re-encode to the bytes it came from.
438    #[test]
439    fn a_simple_value_below_the_extended_range_is_not_written_in_two_bytes() {
440        for value in 0..32u8 {
441            let block = [0xf8, value];
442            let Ok(decoded) = decode(&block) else {
443                continue;
444            };
445            assert_eq!(
446                encode(&decoded),
447                block,
448                "f8 {value:02x} decoded to {decoded:?}, which re-encodes to other bytes"
449            );
450        }
451        // The three that used to come back as values, named so the failure
452        // says which spelling was accepted rather than only that one was.
453        assert_eq!(
454            decode(&[0xf8, 20]),
455            Err(CborError::NonCanonicalArgument {
456                argument: 20,
457                width: 1
458            })
459        );
460        assert_eq!(
461            decode(&[0xf8, 21]),
462            Err(CborError::NonCanonicalArgument {
463                argument: 21,
464                width: 1
465            })
466        );
467        assert_eq!(
468            decode(&[0xf8, 22]),
469            Err(CborError::NonCanonicalArgument {
470                argument: 22,
471                width: 1
472            })
473        );
474        // 32 upwards is the range the form is for; it is unassigned rather
475        // than non-canonical, and refused for being a simple value the data
476        // model has no room for.
477        assert_eq!(decode(&[0xf8, 32]), Err(CborError::Simple(32)));
478    }
479
480    /// Floats decode to nothing, at every width.
481    #[test]
482    fn floats_are_refused_at_every_width() {
483        for float in [
484            vec![0xf9, 0x3c, 0x00],
485            vec![0xfa, 0x3f, 0x80, 0x00, 0x00],
486            vec![0xfb, 0x3f, 0xf0, 0, 0, 0, 0, 0, 0],
487        ] {
488            assert_eq!(decode(&float), Err(CborError::Float), "{float:02x?}");
489        }
490        assert_eq!(decode(&[0xf7]), Err(CborError::Simple(23)), "undefined");
491    }
492
493    /// Map keys arrive sorted or the block is not canonical. `b` before `aa`
494    /// is the encoder's order; `aa` before `b` is bytewise order, and a
495    /// decoder that accepted it would round-trip to different bytes.
496    #[test]
497    fn map_keys_must_arrive_in_the_order_the_encoder_writes_them() {
498        let sorted = [0xa2, 0x61, b'b', 0x02, 0x62, b'a', b'a', 0x01];
499        assert!(decode(&sorted).is_ok());
500        let bytewise = [0xa2, 0x62, b'a', b'a', 0x01, 0x61, b'b', 0x02];
501        assert!(matches!(decode(&bytewise), Err(CborError::KeyOrder { .. })));
502        let repeated = [0xa2, 0x61, b'b', 0x02, 0x61, b'b', 0x01];
503        assert!(matches!(decode(&repeated), Err(CborError::KeyOrder { .. })));
504    }
505
506    /// A tag that is not 42 is not a link, and a link that is not an
507    /// identity-prefixed CID is not a link either.
508    #[test]
509    fn only_the_link_tag_over_a_cid_decodes() {
510        let cid =
511            Cid::parse("bafyreidfayvfuwqa7qlnopdjiqrxzs6blmoeu4rujcjtnci5beludirz2a").unwrap();
512        let block = encode(&Value::Link(cid.clone()));
513        assert_eq!(decode(&block).unwrap(), Value::Link(cid));
514
515        // Tag 41 over the same bytes.
516        let mut wrong_tag = block.clone();
517        wrong_tag[1] = 41;
518        assert_eq!(decode(&wrong_tag), Err(CborError::Tag(41)));
519
520        // Tag 42 over a byte string with no multibase prefix.
521        let mut no_prefix = block.clone();
522        no_prefix.remove(4);
523        no_prefix[3] -= 1;
524        assert!(decode(&no_prefix).is_err());
525
526        // Tag 42 over an integer.
527        assert_eq!(decode(&[0xd8, 0x2a, 0x01]), Err(CborError::LinkNotBytes));
528    }
529
530    /// A block is one value. Bytes after it change the CID and mean the
531    /// sender and the receiver disagree about what was hashed.
532    #[test]
533    fn a_block_is_one_value_and_not_a_stream() {
534        assert_eq!(decode(&[0x01, 0x02]), Err(CborError::Trailing(1)));
535    }
536
537    /// A truncated block says how much was missing rather than reading past
538    /// the end of the buffer.
539    #[test]
540    fn a_truncated_block_is_an_error_and_not_a_panic() {
541        assert_eq!(decode(&[0x43, 0x61]), Err(CborError::Truncated(2)));
542        // A byte string claiming the whole address space and holding none.
543        // The length is a claim and is checked against the input before any
544        // of it is copied out.
545        assert!(matches!(
546            decode(&[0x5b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]),
547            Err(CborError::Truncated(_))
548        ));
549        assert_eq!(decode(&[]), Err(CborError::Truncated(1)));
550        // A list that claims four billion items and holds none.
551        assert!(matches!(
552            decode(&[0x9a, 0xff, 0xff, 0xff, 0xff]),
553            Err(CborError::Truncated(_))
554        ));
555    }
556
557    /// Nesting is bounded, because a recursive descent parser that runs out
558    /// of stack aborts the process rather than returning an error.
559    #[test]
560    fn nesting_is_bounded() {
561        let deep: Vec<u8> = std::iter::repeat_n(0x81, MAX_DEPTH + 2)
562            .chain(std::iter::once(0x01))
563            .collect();
564        assert_eq!(decode(&deep), Err(CborError::TooDeep));
565        let shallow: Vec<u8> = std::iter::repeat_n(0x81, MAX_DEPTH - 1)
566            .chain(std::iter::once(0x01))
567            .collect();
568        assert!(decode(&shallow).is_ok());
569    }
570
571    /// A text string that is not UTF-8 is refused rather than repaired.
572    ///
573    /// The repair is the danger: `from_utf8_lossy` would substitute a
574    /// replacement character, and the value would then re-encode to bytes
575    /// longer than the ones that arrived, under a CID that is not the one the
576    /// block came in under. A lone surrogate is the case a checker written
577    /// against UTF-16 lets through.
578    #[test]
579    fn a_text_string_that_is_not_utf8_is_refused_rather_than_repaired() {
580        // `ed a0 80` is the lone surrogate U+D800 in UTF-8's forbidden range.
581        assert_eq!(decode(&[0x63, 0xed, 0xa0, 0x80]), Err(CborError::NotUtf8));
582        // A bare continuation byte, which no scanner mistakes for anything.
583        assert_eq!(decode(&[0x61, 0x80]), Err(CborError::NotUtf8));
584        // The same rule reaches a map key, which is read through the same
585        // path and would otherwise name an entry nothing could look up.
586        assert_eq!(
587            decode(&[0xa1, 0x61, 0x80, 0x01]),
588            Err(CborError::NotUtf8),
589            "a map key is a text string and is held to the same rule"
590        );
591    }
592
593    /// An integer past `i64` has nowhere to live in the data model, and is
594    /// refused rather than wrapped.
595    #[test]
596    fn an_integer_past_the_model_is_refused() {
597        let huge = [0x1b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff];
598        assert_eq!(decode(&huge), Err(CborError::IntegerRange(u64::MAX)));
599        assert_eq!(
600            decode(&[0x1b, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]).unwrap(),
601            Value::Integer(i64::MAX)
602        );
603        // -1 - n, so the negative side reaches one further.
604        assert_eq!(
605            decode(&[0x3b, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]).unwrap(),
606            Value::Integer(i64::MIN)
607        );
608    }
609}