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}