Oregami
Repositories/oxedyne/fe2o3

oxedyne/fe2o3/fe2o3_sbj/src/index.rs

20.2 KiB, 1 run

created by r1870400018:22216, which is this file's identity for as long as the history lasts, whatever it is later renamed to

download · who wrote it · its history

1//! The optional trailing index: node id to byte offset. Derived, outside the hash. See §1.4.
2//!
3//! BDAT puts a byte length in front of every compound, so a tree can be walked without being
4//! decoded. The walk here reads only what it must: the fixed header of each node, the declared
5//! payload length of each map and list, and the map key that names a node's children. Everything
6//! else is skipped whole with `Dat::count_bytes`, which seeks over a daticle rather than building
7//! one. Nothing on the way is turned into a `Dat`.
8//!
9//! The index is derived data living outside the hash, so it is never trusted. What an index offset
10//! points at is checked before it is used: `node_at` confines a lookup to the tree region and
11//! refuses anything that does not decode as a node, and `check` compares a whole index against the
12//! tree it claims to describe, which costs one walk and no decoding.
13
14use crate::{
15 canon,
16 kinds::KEY_CHILDREN,
17 limit,
18};
19
20use oxedyne_fe2o3_core::prelude::*;
21use oxedyne_fe2o3_jdat::{
22 prelude::*,
23 bdat::DecodeLimits,
24 usr::UsrKindId,
25};
26
27use std::{
28 collections::BTreeMap,
29 io::Cursor,
30};
31
32/// The daticle nesting depth an index reaches: the map itself, then the scalar under each key.
33///
34/// An index is flat, so anything deeper is not an index, and a reader that decodes an index it did
35/// not build says so rather than descending into it.
36pub const INDEX_DAT_DEPTH: usize = 2;
37
38/// The limits an index region is decoded under.
39///
40/// An index names at most one offset per node, so a tree at the size limit of §5 indexes to well
41/// under that limit again, and the byte bound is the tree's. The bound matters because the index
42/// region trails the hashed part of the file: its length is whatever the file says it is, and
43/// nothing has vouched for a byte of it.
44fn decode_limits() -> DecodeLimits {
45 DecodeLimits::new(INDEX_DAT_DEPTH, limit::TREE_BYTES)
46}
47
48/// Builds an index over an encoded tree region.
49pub fn build(tree_bytes: &[u8]) -> Outcome<Vec<u8>> {
50 let offs = res!(offsets(tree_bytes));
51 let mut map = DaticleMap::new();
52 for (id, off) in &offs {
53 map.insert(Dat::C64(*id), Dat::C64(*off));
54 }
55 Ok(res!(Dat::Map(map).to_bytes(Vec::new())))
56}
57
58/// Parses an index, returning node id to byte offset.
59///
60/// The bytes lie outside the hash and outside the signature, so they are decoded under the limits
61/// of [`decode_limits`] rather than on trust.
62pub fn parse(buf: &[u8]) -> Outcome<BTreeMap<u64, u64>> {
63 let (dat, n) = res!(Dat::from_bytes_limited(buf, &decode_limits()));
64 if n != buf.len() {
65 return Err(err!(
66 "The index is {} bytes but its map ends at byte {}, leaving {} trailing bytes.",
67 buf.len(), n, buf.len() - n;
68 Invalid, Input, Decode));
69 }
70 let map = match dat {
71 Dat::Map(map) => map,
72 d => return Err(err!(
73 "An index must be a Dat::Map of node id to byte offset, found a {:?}.", d.kind();
74 Invalid, Input, Decode)),
75 };
76 let mut out = BTreeMap::new();
77 for (k, v) in map {
78 let id = match k {
79 Dat::C64(id) => id,
80 k => return Err(err!(
81 "An index key must be a c64 node id, found a {:?}.", k.kind();
82 Invalid, Input, Decode)),
83 };
84 let off = match v {
85 Dat::C64(off) => off,
86 v => return Err(err!(
87 "The index entry for node {} must be a c64 byte offset, found a {:?}.",
88 id, v.kind();
89 Invalid, Input, Decode)),
90 };
91 out.insert(id, off);
92 }
93 Ok(out)
94}
95
96/// Walks an encoded tree region, returning the true node id to byte offset map.
97///
98/// Ids are assigned in a depth-first, pre-order walk, counting from 0 at the root (§4.3), and an
99/// offset is measured from the start of the tree region.
100pub fn offsets(tree_bytes: &[u8]) -> Outcome<BTreeMap<u64, u64>> {
101 let mut out = BTreeMap::new();
102 let mut next: u64 = 0;
103 let end = res!(walk_node(tree_bytes, 0, 0, &mut next, &mut out));
104 if end != tree_bytes.len() {
105 return Err(err!(
106 "The tree region is {} bytes but its root node ends at byte {}, leaving {} \
107 trailing bytes.", tree_bytes.len(), end, tree_bytes.len() - end;
108 Invalid, Input, Decode));
109 }
110 Ok(out)
111}
112
113/// Decodes the node an untrusted index offset points at, refusing anything that is not one.
114///
115/// The offset is confined to the tree region and the daticle at it must decode as a node, so a
116/// corrupt offset yields an error rather than a read beyond the region or a panic. The guarantee is
117/// confinement, not identity: to establish that an index names the nodes it claims to, check it
118/// against the tree with `check`.
119pub fn node_at(
120 tree_bytes: &[u8],
121 off: u64,
122)
123 -> Outcome<Dat>
124{
125 let start = try_into!(usize, off);
126 if start >= tree_bytes.len() {
127 return Err(err!(
128 "An index offset of byte {} lies at or past the end of the {} byte tree region.",
129 start, tree_bytes.len();
130 Invalid, Input, Index));
131 }
132 if tree_bytes[start] != Dat::USR_CODE {
133 return Err(err!(
134 "An index offset of byte {} does not begin a node: a node starts with the usr code \
135 {}, but the byte there is {}.", start, Dat::USR_CODE, tree_bytes[start];
136 Invalid, Input, Index));
137 }
138 // The daticle at the offset is decoded under the tree's own limits, since an offset that lands
139 // inside a node, or on bytes an index built elsewhere invented, is not to be descended into
140 // without a bound on how far it goes.
141 let (dat, _len) = res!(Dat::from_bytes_limited(
142 &tree_bytes[start..],
143 &canon::decode_limits(),
144 ));
145 match dat {
146 Dat::Usr(..) => Ok(dat),
147 d => Err(err!(
148 "An index offset of byte {} decodes to a {:?} rather than a node.",
149 start, d.kind();
150 Invalid, Input, Index)),
151 }
152}
153
154/// Checks an index against the tree region it claims to describe, naming the first entry that lies.
155///
156/// An index may name fewer nodes than the tree holds, since a reader that misses an entry can
157/// always walk instead, but every entry it does carry must land on the node it names.
158pub fn check(
159 tree_bytes: &[u8],
160 index: &BTreeMap<u64, u64>,
161)
162 -> Outcome<()>
163{
164 let truth = res!(offsets(tree_bytes));
165 for (id, off) in index {
166 match truth.get(id) {
167 None => return Err(err!(
168 "The index names node {}, but the tree holds {} nodes.", id, truth.len();
169 Invalid, Input, Index)),
170 Some(real) if real != off => return Err(err!(
171 "The index puts node {} at byte {}, but it starts at byte {}.", id, off, real;
172 Invalid, Input, Index)),
173 Some(_) => (),
174 }
175 }
176 Ok(())
177}
178
179/// Walks one node, recording its id and offset, and returns the offset just past it.
180fn walk_node(
181 buf: &[u8],
182 off: usize,
183 depth: usize,
184 next: &mut u64,
185 out: &mut BTreeMap<u64, u64>,
186)
187 -> Outcome<usize>
188{
189 if depth > limit::DEPTH {
190 return Err(err!(
191 "The node at byte {} nests deeper than the limit of {}.", off, limit::DEPTH;
192 Invalid, Input, Excessive));
193 }
194 if off >= buf.len() {
195 return Err(err!(
196 "Expected a node at byte {}, but the tree region is only {} bytes.",
197 off, buf.len();
198 Invalid, Input, Decode));
199 }
200 if buf[off] != Dat::USR_CODE {
201 return Err(err!(
202 "Expected a node at byte {}: a node is a usr daticle, code {}, but the byte there \
203 is {}.", off, Dat::USR_CODE, buf[off];
204 Invalid, Input, Decode));
205 }
206 let id = *next;
207 *next += 1;
208 if *next as usize > limit::NODES {
209 return Err(err!(
210 "The tree holds more nodes than the limit of {}.", limit::NODES;
211 Invalid, Input, Excessive));
212 }
213 out.insert(id, try_into!(u64, off));
214 // A node's header is the usr code, the u16 kind code, then the marker saying whether a
215 // payload follows.
216 let mark = off + 1 + UsrKindId::CODE_BYTE_LEN;
217 if mark >= buf.len() {
218 return Err(err!(
219 "Node {} at byte {} is truncated: its header runs past the end of the {} byte tree \
220 region.", id, off, buf.len();
221 Invalid, Input, Decode));
222 }
223 match buf[mark] {
224 Dat::OPT_NONE_CODE => Ok(mark + 1),
225 Dat::OPT_SOME_CODE => {
226 let p = mark + 1;
227 if p >= buf.len() {
228 return Err(err!(
229 "Node {} at byte {} declares a payload, but the tree region ends at byte \
230 {}.", id, off, buf.len();
231 Invalid, Input, Decode));
232 }
233 match buf[p] {
234 Dat::MAP_CODE => walk_map(buf, p, id, depth, next, out),
235 // Any other payload, such as the string a text node carries, holds no children
236 // and is skipped whole.
237 _ => {
238 let len = res!(skip(buf, p));
239 Ok(p + len)
240 },
241 }
242 },
243 code => Err(err!(
244 "Node {} at byte {} carries the byte {} where a usr payload marker, {} or {}, \
245 belongs.", id, off, code, Dat::OPT_NONE_CODE, Dat::OPT_SOME_CODE;
246 Invalid, Input, Decode)),
247 }
248}
249
250/// Walks a node's payload map, descending into the children it names, and returns its end offset.
251fn walk_map(
252 buf: &[u8],
253 off: usize,
254 id: u64,
255 depth: usize,
256 next: &mut u64,
257 out: &mut BTreeMap<u64, u64>,
258)
259 -> Outcome<usize>
260{
261 let (plen, n) = res!(read_len(buf, off + 1));
262 let start = off + 1 + n;
263 let end = res!(region_end(start, plen, buf.len(), id, "payload map"));
264 let mut q = start;
265 while q < end {
266 let klen = res!(skip(buf, q));
267 let kend = q + klen;
268 if kend > end {
269 return Err(err!(
270 "A key of the payload map of node {} runs past the {} bytes the map declares.",
271 id, plen;
272 Invalid, Input, Decode));
273 }
274 q = if is_children_key(&buf[q..kend]) {
275 res!(walk_children(buf, kend, end, id, depth, next, out))
276 } else {
277 let vlen = res!(skip(buf, kend));
278 let vend = kend + vlen;
279 if vend > end {
280 return Err(err!(
281 "A value of the payload map of node {} runs past the {} bytes the map \
282 declares.", id, plen;
283 Invalid, Input, Decode));
284 }
285 vend
286 };
287 }
288 if q != end {
289 return Err(err!(
290 "The entries of the payload map of node {} end at byte {}, not at byte {} where the \
291 {} bytes it declares run out.", id, q, end, plen;
292 Invalid, Input, Decode));
293 }
294 Ok(end)
295}
296
297/// Walks the list of children a node carries, and returns the offset just past the list.
298fn walk_children(
299 buf: &[u8],
300 off: usize,
301 end: usize,
302 id: u64,
303 depth: usize,
304 next: &mut u64,
305 out: &mut BTreeMap<u64, u64>,
306)
307 -> Outcome<usize>
308{
309 if off >= buf.len() {
310 return Err(err!(
311 "Node {} names its children at byte {}, past the end of the {} byte tree region.",
312 id, off, buf.len();
313 Invalid, Input, Decode));
314 }
315 if buf[off] != Dat::LIST_CODE {
316 return Err(err!(
317 "The children of node {} must be a list, code {}, but the byte at {} is {}.",
318 id, Dat::LIST_CODE, off, buf[off];
319 Invalid, Input, Decode));
320 }
321 let (plen, n) = res!(read_len(buf, off + 1));
322 let start = off + 1 + n;
323 let stop = res!(region_end(start, plen, end, id, "children list"));
324 let mut q = start;
325 while q < stop {
326 q = res!(walk_node(buf, q, depth + 1, next, out));
327 }
328 if q != stop {
329 return Err(err!(
330 "The children of node {} end at byte {}, not at byte {} where the {} bytes the list \
331 declares run out.", id, q, stop, plen;
332 Invalid, Input, Decode));
333 }
334 Ok(stop)
335}
336
337/// Skips the daticle at an offset, returning its length in bytes without decoding it.
338fn skip(
339 buf: &[u8],
340 off: usize,
341)
342 -> Outcome<usize>
343{
344 if off >= buf.len() {
345 return Err(err!(
346 "Expected a daticle at byte {}, but the tree region is only {} bytes.",
347 off, buf.len();
348 Invalid, Input, Decode));
349 }
350 let mut cursor = Cursor::new(&buf[off..]);
351 let len = res!(Dat::count_bytes(&mut cursor));
352 let left = buf.len() - off;
353 if len == 0 || len > left {
354 return Err(err!(
355 "The daticle at byte {} counts {} bytes, which overruns the {} bytes left in the \
356 tree region.", off, len, left;
357 Invalid, Input, Decode));
358 }
359 Ok(len)
360}
361
362/// Reads the c64 byte length that BDAT puts in front of a compound.
363fn read_len(
364 buf: &[u8],
365 off: usize,
366)
367 -> Outcome<(usize, usize)>
368{
369 if off >= buf.len() {
370 return Err(err!(
371 "Expected a c64 length at byte {}, but the tree region is only {} bytes.",
372 off, buf.len();
373 Invalid, Input, Decode));
374 }
375 if buf[off] < Dat::C64_CODE_START || buf[off] > Dat::C64_CODE_END {
376 return Err(err!(
377 "Expected a c64 length at byte {}, whose code must lie between {} and {}, but the \
378 byte there is {}.", off, Dat::C64_CODE_START, Dat::C64_CODE_END, buf[off];
379 Invalid, Input, Decode));
380 }
381 let (len, n) = res!(Dat::read_c64(&buf[off..]));
382 Ok((try_into!(usize, len), n))
383}
384
385/// Returns the end of a declared region, refusing one that runs past the bytes available.
386fn region_end(
387 start: usize,
388 plen: usize,
389 avail: usize,
390 id: u64,
391 what: &str,
392)
393 -> Outcome<usize>
394{
395 match start.checked_add(plen) {
396 Some(end) if end <= avail => Ok(end),
397 _ => Err(err!(
398 "The {} of node {} declares {} bytes from byte {}, running past the {} bytes \
399 available.", what, id, plen, start, avail;
400 Invalid, Input, Decode)),
401 }
402}
403
404/// Whether an encoded map key is the one under which a node carries its children.
405fn is_children_key(byts: &[u8]) -> bool {
406 // A key encodes as the str code, a one byte c64 length, then the key itself.
407 let key = KEY_CHILDREN.as_bytes();
408 byts.len() == 3 + key.len()
409 && byts[0] == Dat::STR_CODE
410 && byts[1] == Dat::C64_CODE_START + 1
411 && byts[2] as usize == key.len()
412 && &byts[3..] == key
413}
414
415#[cfg(test)]
416mod tests {
417 use super::*;
418 use crate::kinds::NodeKind;
419
420 /// Wraps a payload as a node of the given kind.
421 fn node(kind: NodeKind, payload: Dat) -> Dat {
422 Dat::Usr(
423 UsrKindId::new(kind.code(), Some(kind.label()), None),
424 Some(Box::new(payload)),
425 )
426 }
427
428 /// A text node, whose payload is a bare string.
429 fn text(s: &str) -> Dat {
430 node(NodeKind::Text, Dat::Str(s.to_string()))
431 }
432
433 /// A node whose payload is a map of the given fields plus a list of children.
434 fn parent(kind: NodeKind, fields: Vec<(&str, Dat)>, children: Vec<Dat>) -> Dat {
435 let mut map = DaticleMap::new();
436 for (k, v) in fields {
437 map.insert(dat!(k), v);
438 }
439 map.insert(dat!(KEY_CHILDREN), Dat::List(children));
440 node(kind, Dat::Map(map))
441 }
442
443 /// A small tree exercising every shape the walk must cope with: a map payload, a bare string
444 /// payload, nesting, and siblings.
445 fn tree() -> Dat {
446 parent(NodeKind::Doc, vec![
447 ("title", dat!("Style without a cascade")),
448 ("lang", dat!("en")),
449 ], vec![
450 parent(NodeKind::Heading, vec![("level", Dat::U8(2))], vec![
451 text("Style without a cascade"),
452 ]),
453 parent(NodeKind::Para, vec![], vec![
454 text("A paragraph with "),
455 parent(NodeKind::Emph, vec![("strong", Dat::Bool(true))], vec![
456 text("emphasis"),
457 ]),
458 ]),
459 parent(NodeKind::List, vec![("ordered", Dat::Bool(false))], vec![
460 parent(NodeKind::Item, vec![], vec![
461 parent(NodeKind::Para, vec![], vec![
462 text("An item."),
463 ]),
464 ]),
465 ]),
466 ])
467 }
468
469 /// The kinds of the tree above, in depth-first pre-order: the node each id names.
470 fn expected() -> Vec<NodeKind> {
471 vec![
472 NodeKind::Doc,
473 NodeKind::Heading,
474 NodeKind::Text,
475 NodeKind::Para,
476 NodeKind::Text,
477 NodeKind::Emph,
478 NodeKind::Text,
479 NodeKind::List,
480 NodeKind::Item,
481 NodeKind::Para,
482 NodeKind::Text,
483 ]
484 }
485
486 /// The encoded tree region.
487 fn encoded() -> Outcome<Vec<u8>> {
488 Ok(res!(tree().to_bytes(Vec::new())))
489 }
490
491 /// The kind code of a decoded node.
492 fn code_of(dat: &Dat) -> Outcome<u16> {
493 match dat {
494 Dat::Usr(ukid, _) => Ok(ukid.code()),
495 d => Err(err!("Expected a node, found a {:?}.", d.kind(); Invalid, Input)),
496 }
497 }
498
499 #[test]
500 fn test_offsets_land_on_node_boundaries() -> Outcome<()> {
501 let byts = res!(encoded());
502 let offs = res!(offsets(&byts));
503 let want = expected();
504 assert_eq!(offs.len(), want.len());
505 // Every offset must decode to the node its id names.
506 for (i, kind) in want.iter().enumerate() {
507 let id = i as u64;
508 let off = match offs.get(&id) {
509 Some(off) => *off,
510 None => return Err(err!("No index entry for node {}.", id; Invalid, Input)),
511 };
512 let dat = res!(node_at(&byts, off));
513 assert_eq!(res!(code_of(&dat)), kind.code(), "node {} at byte {}", id, off);
514 }
515 // The root sits at byte 0 and offsets rise with the pre-order.
516 assert_eq!(offs.get(&0), Some(&0u64));
517 let mut last = 0u64;
518 for (id, off) in &offs {
519 if *id > 0 {
520 assert!(*off > last, "node {} at byte {} does not follow byte {}", id, off, last);
521 }
522 last = *off;
523 }
524 Ok(())
525 }
526
527 #[test]
528 fn test_round_trip_build_parse() -> Outcome<()> {
529 let byts = res!(encoded());
530 let idx = res!(build(&byts));
531 let parsed = res!(parse(&idx));
532 assert_eq!(parsed, res!(offsets(&byts)));
533 res!(check(&byts, &parsed));
534 Ok(())
535 }
536
537 #[test]
538 fn test_text_payload_is_skipped_not_descended() -> Outcome<()> {
539 // A text node's payload is a bare string, so it must contribute exactly one id and no
540 // children, even though the string it carries could be mistaken for anything.
541 let byts = res!(node(NodeKind::Text, dat!("children")).to_bytes(Vec::new()));
542 let offs = res!(offsets(&byts));
543 assert_eq!(offs.len(), 1);
544 assert_eq!(offs.get(&0), Some(&0u64));
545 Ok(())
546 }
547
548 #[test]
549 fn test_corrupt_offset_past_end_is_detected() -> Outcome<()> {
550 let byts = res!(encoded());
551 let past = byts.len() as u64;
552 assert!(node_at(&byts, past).is_err());
553 assert!(node_at(&byts, u64::MAX).is_err());
554 let mut idx = res!(offsets(&byts));
555 idx.insert(2, past);
556 assert!(check(&byts, &idx).is_err());
557 Ok(())
558 }
559
560 #[test]
561 fn test_corrupt_offset_inside_a_node_is_detected() -> Outcome<()> {
562 let byts = res!(encoded());
563 let truth = res!(offsets(&byts));
564 let real = match truth.get(&2) {
565 Some(off) => *off,
566 None => return Err(err!("No index entry for node 2."; Invalid, Input)),
567 };
568 // One byte into a node lands on its kind code, not on a node.
569 assert!(node_at(&byts, real + 1).is_err());
570 let mut idx = truth.clone();
571 idx.insert(2, real + 1);
572 assert!(check(&byts, &idx).is_err());
573 // An id the tree does not hold is refused too.
574 let mut idx = truth.clone();
575 idx.insert(999, 0);
576 assert!(check(&byts, &idx).is_err());
577 Ok(())
578 }
579
580 #[test]
581 fn test_no_offset_off_a_boundary_survives() -> Outcome<()> {
582 let byts = res!(encoded());
583 let truth = res!(offsets(&byts));
584 let bounds: Vec<u64> = truth.values().cloned().collect();
585 // Whatever a corrupt index says, a reader is confined to the tree region and every
586 // offset that is not a node boundary is refused when it is used.
587 for off in 0..(byts.len() as u64 + 64) {
588 let boundary = bounds.contains(&off);
589 assert_eq!(node_at(&byts, off).is_ok(), boundary,
590 "byte {} accepted as a node, boundary = {}", off, boundary);
591 let mut idx = BTreeMap::new();
592 idx.insert(0u64, off);
593 assert_eq!(check(&byts, &idx).is_ok(), off == 0, "byte {} accepted for node 0", off);
594 }
595 Ok(())
596 }
597
598 #[test]
599 fn test_truncated_and_padded_trees_are_refused() -> Outcome<()> {
600 let byts = res!(encoded());
601 // A tree region one byte short of the root node it holds.
602 assert!(offsets(&byts[..byts.len() - 1]).is_err());
603 // A tree region one byte longer than the root node it holds.
604 let mut padded = byts.clone();
605 padded.push(0);
606 assert!(offsets(&padded).is_err());
607 // Nothing at all.
608 assert!(offsets(&[]).is_err());
609 Ok(())
610 }
611
612 #[test]
613 fn test_index_nesting_is_bounded() -> Outcome<()> {
614 // The index region trails the hashed part of a file, so its length and its contents are
615 // whatever the last hand to touch the file made them. A hundred thousand nested lists cost
616 // a few hundred kilobytes to write and a stack to read, so the decoder is bounded, and an
617 // index is flat.
618 let mut buf = Vec::new();
619 for _ in 0..100_000 {
620 let mut lvl = vec![Dat::LIST_CODE];
621 lvl = res!(Dat::C64(buf.len() as u64).to_bytes(lvl));
622 lvl.extend_from_slice(&buf);
623 buf = lvl;
624 }
625 match parse(&buf) {
626 Ok(_) => Err(err!(
627 "A deeply nested index region was accepted.";
628 Test, Invalid)),
629 Err(e) => {
630 let msg = fmt!("{}", e);
631 assert!(msg.contains("nesting depth"),
632 "The rejection should name the depth limit, but says: {}", msg);
633 Ok(())
634 },
635 }
636 }
637
638 #[test]
639 fn test_index_bytes_are_a_map_of_c64_to_c64() -> Outcome<()> {
640 let byts = res!(encoded());
641 let idx = res!(build(&byts));
642 let (dat, n) = res!(Dat::from_bytes(&idx));
643 assert_eq!(n, idx.len());
644 match dat {
645 Dat::Map(map) => {
646 assert_eq!(map.len(), expected().len());
647 for (k, v) in &map {
648 assert!(matches!(k, Dat::C64(_)), "index key is not a c64: {:?}", k.kind());
649 assert!(matches!(v, Dat::C64(_)), "index value is not a c64: {:?}", v.kind());
650 }
651 },
652 d => return Err(err!("The index is a {:?}, not a map.", d.kind(); Invalid, Input)),
653 }
654 // Garbage is not an index.
655 assert!(parse(&[0xff, 0xff, 0xff]).is_err());
656 // A well formed daticle that is not a map is not an index either.
657 let not_a_map = res!(dat!("nope").to_bytes(Vec::new()));
658 assert!(parse(&not_a_map).is_err());
659 // Trailing bytes after the map are not an index either.
660 let mut padded = idx.clone();
661 padded.push(0);
662 assert!(parse(&padded).is_err());
663 Ok(())
664 }
665}