1pub mod open;
22pub mod parts;
23pub mod write;
24
25#[cfg(test)]
26mod roundtrip;
27
28use crate::file::FileError;
29use crate::varint::{read_uvarint, write_uvarint};
30
31pub const VERSION: u8 = 0x06;
33pub const HEADER_LEN: usize = 128;
35pub const FIRST_READ: u64 = 64 * 1024;
38pub const FOOTER: [u8; 8] = *b"\x06\x00\x00\x00RETE";
41pub const ROOT_BUDGET: usize = 40 * 1024;
43pub const LEAF_START: usize = 16 * 1024;
45pub const CARD_SLACK_MIN: u32 = 8 * 1024;
47
48pub const F_NAMED: u8 = 0b0001;
50pub const F_QUOTED: u8 = 0b0010;
52pub const F_TEXT_NAMED: u8 = 0b0100;
54pub const F_QUAD_GRAPH_MAJOR: u8 = 0b1000;
57
58pub const P_TEXT: u16 = 1;
60pub const P_TEXT_PARTIAL: u16 = 2;
62pub const P_LABELS: u16 = 4;
64pub const P_SCHEMA: u16 = 8;
66pub const P_PYRAMID: u16 = 16;
68pub const P_QUAD: u16 = 32;
70
71#[allow(missing_docs)]
73pub mod kind {
74 pub const SCHEMA: u8 = 1;
75 pub const DICT_LEAVES: u8 = 2;
76 pub const INDEX_LEAVES: u8 = 3;
77 pub const QUAD_LEAVES: u8 = 4;
78 pub const LABEL_LEAVES: u8 = 5;
79 pub const TEXT_LEAVES: u8 = 6;
80 pub const DICT_DATA: u8 = 16;
81 pub const INDEX_DATA: u8 = 17;
82 pub const QUAD_DATA: u8 = 18;
83 pub const LABEL_DATA: u8 = 19;
84 pub const TEXT_DATA: u8 = 20;
85 pub const PYRAMID: u8 = 21;
86
87 pub const ORDER: [u8; 12] = [
89 SCHEMA,
90 DICT_LEAVES,
91 INDEX_LEAVES,
92 QUAD_LEAVES,
93 LABEL_LEAVES,
94 TEXT_LEAVES,
95 DICT_DATA,
96 INDEX_DATA,
97 QUAD_DATA,
98 LABEL_DATA,
99 TEXT_DATA,
100 PYRAMID,
101 ];
102
103 pub fn name(k: u8) -> &'static str {
105 match k {
106 SCHEMA => "schema",
107 DICT_LEAVES => "dict-leaves",
108 INDEX_LEAVES => "index-leaves",
109 QUAD_LEAVES => "quad-leaves",
110 LABEL_LEAVES => "label-leaves",
111 TEXT_LEAVES => "text-leaves",
112 DICT_DATA => "dict",
113 INDEX_DATA => "index",
114 QUAD_DATA => "quad",
115 LABEL_DATA => "labels",
116 TEXT_DATA => "text",
117 PYRAMID => "pyramid",
118 _ => "unknown",
119 }
120 }
121
122 pub fn is_front(k: u8) -> bool {
124 k < DICT_DATA
125 }
126}
127
128#[allow(missing_docs)]
130pub mod tag {
131 pub const DICT: u8 = 1;
132 pub const INDEX: u8 = 2;
133 pub const QUAD: u8 = 3;
134 pub const LABELS: u8 = 4;
135 pub const TEXT: u8 = 5;
136}
137
138#[derive(Debug, Clone, Default, PartialEq, Eq)]
140#[allow(missing_docs)]
141pub struct Header06 {
142 pub flags: u8,
143 pub content_hash: [u8; 16],
144 pub file_len: u64,
145 pub root_len: u32,
146 pub card_region_len: u32,
147 pub data_offset: u64,
148 pub quad_count: u64,
149 pub term_count: u64,
150 pub graph_count: u64,
151 pub perm_mask: u8,
152 pub dict_codec: u8,
153 pub block_codec: u8,
154 pub internal_codec: u8,
155 pub pyramid_levels: u16,
156 pub id_widths: u8,
159 pub presence: u16,
160 pub card_off: u32,
161 pub card_len: u32,
162 pub card_digest: [u8; 16],
163 pub build_off: u32,
164 pub build_len: u32,
165 pub root_digest: [u8; 16],
166}
167
168impl Header06 {
169 pub fn to_bytes(&self) -> [u8; HEADER_LEN] {
171 let mut b = [0u8; HEADER_LEN];
172 b[0..4].copy_from_slice(&crate::header::MAGIC);
173 b[4] = VERSION;
174 b[5] = self.flags;
175 b[6..8].copy_from_slice(&(HEADER_LEN as u16).to_le_bytes());
176 b[8..24].copy_from_slice(&self.content_hash);
177 b[24..32].copy_from_slice(&self.file_len.to_le_bytes());
178 b[32..36].copy_from_slice(&self.root_len.to_le_bytes());
179 b[36..40].copy_from_slice(&self.card_region_len.to_le_bytes());
180 b[40..48].copy_from_slice(&self.data_offset.to_le_bytes());
181 b[48..56].copy_from_slice(&self.quad_count.to_le_bytes());
182 b[56..64].copy_from_slice(&self.term_count.to_le_bytes());
183 b[64..72].copy_from_slice(&self.graph_count.to_le_bytes());
184 b[72] = self.perm_mask;
185 b[73] = self.dict_codec;
186 b[74] = self.block_codec;
187 b[75] = self.internal_codec;
188 b[76] = self.pyramid_levels as u8;
189 b[77] = self.id_widths;
190 b[78..80].copy_from_slice(&self.presence.to_le_bytes());
191 b[80..84].copy_from_slice(&self.card_off.to_le_bytes());
192 b[84..88].copy_from_slice(&self.card_len.to_le_bytes());
193 b[88..104].copy_from_slice(&self.card_digest);
194 b[104..108].copy_from_slice(&self.build_off.to_le_bytes());
195 b[108..112].copy_from_slice(&self.build_len.to_le_bytes());
196 b[112..128].copy_from_slice(&self.root_digest);
197 b
198 }
199
200 pub fn from_bytes(b: &[u8]) -> Result<Self, FileError> {
202 if b.len() < HEADER_LEN {
203 return Err(FileError::Container("0x06 header truncated"));
204 }
205 if b[0..4] != crate::header::MAGIC || b[4] != VERSION {
206 return Err(FileError::Container("not a 0x06 file"));
207 }
208 if u16::from_le_bytes([b[6], b[7]]) as usize != HEADER_LEN {
209 return Err(FileError::Container("0x06 header length is not 128"));
210 }
211 let u32_at = |o: usize| u32::from_le_bytes(b[o..o + 4].try_into().unwrap());
212 let u64_at = |o: usize| u64::from_le_bytes(b[o..o + 8].try_into().unwrap());
213 let h16 = |o: usize| {
214 let mut x = [0u8; 16];
215 x.copy_from_slice(&b[o..o + 16]);
216 x
217 };
218 let h = Header06 {
219 flags: b[5],
220 content_hash: h16(8),
221 file_len: u64_at(24),
222 root_len: u32_at(32),
223 card_region_len: u32_at(36),
224 data_offset: u64_at(40),
225 quad_count: u64_at(48),
226 term_count: u64_at(56),
227 graph_count: u64_at(64),
228 perm_mask: b[72],
229 dict_codec: b[73],
230 block_codec: b[74],
231 internal_codec: b[75],
232 pyramid_levels: b[76] as u16,
233 id_widths: b[77],
234 presence: u16::from_le_bytes([b[78], b[79]]),
235 card_off: u32_at(80),
236 card_len: u32_at(84),
237 card_digest: h16(88),
238 build_off: u32_at(104),
239 build_len: u32_at(108),
240 root_digest: h16(112),
241 };
242 let front_min = (HEADER_LEN as u64)
245 .checked_add(h.root_len as u64)
246 .and_then(|x| x.checked_add(h.card_region_len as u64))
247 .ok_or(FileError::Container("0x06 front overflows"))?;
248 if front_min > h.data_offset || h.data_offset > h.file_len {
249 return Err(FileError::Container("0x06 front overruns the data region"));
250 }
251 let card_end = h.card_off as u64 + h.card_len as u64;
252 let build_end = h.build_off as u64 + h.build_len as u64;
253 if card_end > h.card_region_len as u64 || build_end > h.card_region_len as u64 {
254 return Err(FileError::Container("0x06 card region overrun"));
255 }
256 crate::index::PermSet::from_bits(h.perm_mask)
257 .map_err(|_| FileError::Container("0x06 permutation mask invalid"))?;
258 check_id_widths_byte(h.id_widths)?;
259 Ok(h)
260 }
261
262 pub fn root_offset(&self) -> u64 {
264 HEADER_LEN as u64
265 }
266 pub fn card_region_offset(&self) -> u64 {
268 HEADER_LEN as u64 + self.root_len as u64
269 }
270 pub fn front_base(&self) -> u64 {
272 self.card_region_offset() + self.card_region_len as u64
273 }
274 pub fn card_range(&self) -> crate::file::ByteRange {
276 crate::file::ByteRange {
277 offset: self.card_region_offset() + self.card_off as u64,
278 len: self.card_len as u64,
279 }
280 }
281 pub fn build_range(&self) -> crate::file::ByteRange {
283 crate::file::ByteRange {
284 offset: self.card_region_offset() + self.build_off as u64,
285 len: self.build_len as u64,
286 }
287 }
288 pub fn perms(&self) -> crate::index::PermSet {
290 crate::index::PermSet::from_bits(self.perm_mask).unwrap_or(crate::index::PermSet::ALL)
291 }
292 pub fn has(&self, bit: u16) -> bool {
294 self.presence & bit != 0
295 }
296}
297
298pub fn digest16(bytes: &[u8]) -> [u8; 16] {
300 let mut out = [0u8; 16];
301 out.copy_from_slice(&blake3::hash(bytes).as_bytes()[..16]);
302 out
303}
304
305const ID_POSITIONS: [&str; 4] = ["subjects", "objects", "predicates", "graphs"];
307
308fn check_id_widths_byte(w: u8) -> Result<(), FileError> {
311 for i in 0..4 {
312 match (w >> (2 * i)) & 3 {
313 0 => {}
314 1 => {
315 return Err(FileError::Container(match i {
316 0 => "u64 term ids (subjects): not supported by this reader",
317 1 => "u64 term ids (objects): not supported by this reader",
318 2 => "u64 term ids (predicates): not supported by this reader",
319 _ => "u64 term ids (graphs): not supported by this reader",
320 }))
321 }
322 _ => return Err(FileError::Container("0x06 header: reserved term id width")),
323 }
324 }
325 Ok(())
326}
327
328pub fn id_widths_for(max_ids: [u64; 4]) -> Result<u8, FileError> {
333 for (i, m) in max_ids.iter().enumerate() {
334 if *m > u32::MAX as u64 {
335 return Err(FileError::Container(match i {
336 0 => "this dataset needs u64 term ids in subjects, not supported yet",
337 1 => "this dataset needs u64 term ids in objects, not supported yet",
338 2 => "this dataset needs u64 term ids in predicates, not supported yet",
339 _ => "this dataset needs u64 term ids in graphs, not supported yet",
340 }));
341 }
342 }
343 Ok(0)
344}
345
346pub fn id_position_names() -> [&'static str; 4] {
348 ID_POSITIONS
349}
350
351pub fn content_hash(h: &Header06) -> [u8; 16] {
355 let b = h.to_bytes();
356 let mut x = blake3::Hasher::new();
357 x.update(b"rete/0x06");
358 x.update(&b[4..8]);
359 x.update(&b[48..80]);
360 x.update(&h.card_len.to_le_bytes());
361 x.update(&h.card_digest);
362 x.update(&h.root_len.to_le_bytes());
363 x.update(&h.root_digest);
364 let mut out = [0u8; 16];
365 out.copy_from_slice(&x.finalize().as_bytes()[..16]);
366 out
367}
368
369#[derive(Debug, Clone, Copy, PartialEq, Eq)]
371pub struct Sec06 {
372 pub kind: u8,
374 pub max_block: u32,
378 pub offset: u64,
380 pub len: u64,
382 pub digest: [u8; 16],
384}
385
386pub const SEC_ENTRY_LEN: usize = 38;
388
389pub const BLOCK_CEILING: u64 = 4 << 20;
392pub const UNIT_MAX: u64 = 16 << 20;
394pub const CARD_MAX: u64 = 64 << 20;
396
397impl Sec06 {
398 pub fn abs(&self, h: &Header06) -> Result<crate::file::ByteRange, crate::file::FileError> {
401 let base = if kind::is_front(self.kind) {
402 h.front_base()
403 } else {
404 h.data_offset
405 };
406 let overflow = || crate::file::FileError::Container("0x06 section overflows");
407 let offset = base.checked_add(self.offset).ok_or_else(overflow)?;
408 offset.checked_add(self.len).ok_or_else(overflow)?;
409 Ok(crate::file::ByteRange {
410 offset,
411 len: self.len,
412 })
413 }
414}
415
416#[derive(Debug, Clone, Default)]
418pub struct Root06 {
419 pub sections: Vec<Sec06>,
421 pub units: Vec<(u8, Vec<u8>)>,
423}
424
425impl Root06 {
426 pub fn section(&self, k: u8) -> Option<&Sec06> {
428 self.sections.iter().find(|s| s.kind == k)
429 }
430
431 pub fn unit(&self, t: u8) -> Option<&[u8]> {
433 self.units
434 .iter()
435 .find(|(x, _)| *x == t)
436 .map(|(_, p)| p.as_slice())
437 }
438
439 pub fn encode(&self, codec: u8) -> Vec<u8> {
441 let mut units = Vec::new();
442 for (t, p) in &self.units {
443 units.push(*t);
444 write_uvarint(&mut units, p.len() as u64);
445 units.extend_from_slice(p);
446 }
447 let stored = crate::file::compress(codec, &units);
448 let mut out =
449 Vec::with_capacity(2 + SEC_ENTRY_LEN * self.sections.len() + 4 + stored.len());
450 out.push(1u8);
451 out.push(self.sections.len() as u8);
452 for s in &self.sections {
453 out.push(s.kind);
454 out.push(0);
455 out.extend_from_slice(&s.max_block.to_le_bytes());
456 out.extend_from_slice(&s.offset.to_le_bytes());
457 out.extend_from_slice(&s.len.to_le_bytes());
458 out.extend_from_slice(&s.digest);
459 }
460 out.extend_from_slice(&(stored.len() as u32).to_le_bytes());
461 out.extend_from_slice(&stored);
462 out
463 }
464
465 pub fn decode(bytes: &[u8], codec: u8) -> Result<Self, FileError> {
467 let bad = |w| FileError::Container(w);
468 if bytes.len() < 2 || bytes[0] != 1 {
469 return Err(bad("0x06 root block: unknown version"));
470 }
471 let n = bytes[1] as usize;
472 let table_end = 2 + n * SEC_ENTRY_LEN;
473 if bytes.len() < table_end + 4 {
474 return Err(bad("0x06 root block truncated"));
475 }
476 let mut sections = Vec::with_capacity(n);
477 for i in 0..n {
478 let e = &bytes[2 + i * SEC_ENTRY_LEN..2 + (i + 1) * SEC_ENTRY_LEN];
479 let mut digest = [0u8; 16];
480 digest.copy_from_slice(&e[22..38]);
481 sections.push(Sec06 {
482 kind: e[0],
483 max_block: u32::from_le_bytes(e[2..6].try_into().unwrap()),
484 offset: u64::from_le_bytes(e[6..14].try_into().unwrap()),
485 len: u64::from_le_bytes(e[14..22].try_into().unwrap()),
486 digest,
487 });
488 }
489 let ulen = u32::from_le_bytes(bytes[table_end..table_end + 4].try_into().unwrap()) as usize;
490 let stored = bytes
491 .get(table_end + 4..table_end + 4 + ulen)
492 .ok_or(bad("0x06 root units overrun"))?;
493 let units_raw = crate::file::decompress_bounded(codec, stored, UNIT_MAX)?;
494 let mut units = Vec::new();
495 let mut c = Cur::new(&units_raw);
496 while !c.done() {
497 let t = c.u8()?;
498 let l = c.len()?;
499 units.push((t, c.bytes(l)?.to_vec()));
500 }
501 Ok(Root06 { sections, units })
502 }
503}
504
505pub struct Cur<'a> {
507 b: &'a [u8],
508 pub pos: usize,
510}
511
512#[allow(clippy::len_without_is_empty)]
514impl<'a> Cur<'a> {
515 pub fn new(b: &'a [u8]) -> Self {
517 Cur { b, pos: 0 }
518 }
519 pub fn done(&self) -> bool {
521 self.pos >= self.b.len()
522 }
523 pub fn uv(&mut self) -> Result<u64, FileError> {
525 let (v, n) = read_uvarint(self.b.get(self.pos..).unwrap_or(&[]))
526 .ok_or(FileError::Container("0x06: truncated varint"))?;
527 self.pos += n;
528 Ok(v)
529 }
530 pub fn u32v(&mut self) -> Result<u32, FileError> {
532 u32::try_from(self.uv()?).map_err(|_| FileError::Container("0x06: value exceeds u32"))
533 }
534 pub fn len(&mut self) -> Result<usize, FileError> {
536 let v = self.uv()?;
537 let rest = self.b.len().saturating_sub(self.pos) as u64;
538 if v > rest {
539 return Err(FileError::Container("0x06: length overruns"));
540 }
541 Ok(v as usize)
542 }
543 pub fn count(&mut self, max: u64) -> Result<usize, FileError> {
545 let v = self.uv()?;
546 if v > max {
547 return Err(FileError::Container("0x06: count out of range"));
548 }
549 Ok(v as usize)
550 }
551 pub fn u8(&mut self) -> Result<u8, FileError> {
553 let v = *self
554 .b
555 .get(self.pos)
556 .ok_or(FileError::Container("0x06: truncated"))?;
557 self.pos += 1;
558 Ok(v)
559 }
560 pub fn bytes(&mut self, n: usize) -> Result<&'a [u8], FileError> {
562 let s = self
563 .b
564 .get(
565 self.pos
566 ..self
567 .pos
568 .checked_add(n)
569 .ok_or(FileError::Container("0x06: overflow"))?,
570 )
571 .ok_or(FileError::Container("0x06: truncated bytes"))?;
572 self.pos += n;
573 Ok(s)
574 }
575 pub fn lp(&mut self) -> Result<&'a [u8], FileError> {
577 let n = self.len()?;
578 self.bytes(n)
579 }
580}
581
582pub fn put_lp(out: &mut Vec<u8>, b: &[u8]) {
584 write_uvarint(out, b.len() as u64);
585 out.extend_from_slice(b);
586}
587
588#[derive(Debug, Clone, PartialEq, Eq, Default)]
594pub struct LeafRef {
595 pub first: usize,
597 pub n: usize,
599 pub off: u64,
601 pub len: u64,
603}
604
605#[derive(Debug, Clone, Default)]
607pub struct DictSecRoot {
608 pub term_count: u32,
610 pub restart_interval: u32,
612 pub n_chunks: usize,
614 pub data_off: u64,
616 pub data_len: u64,
618 pub leaves: Vec<(LeafRef, usize, u64, Vec<u8>)>,
620}
621
622pub fn encode_dict_root(secs: &[DictSecRoot; 4]) -> Vec<u8> {
624 let mut o = Vec::new();
625 for s in secs {
626 write_uvarint(&mut o, s.term_count as u64);
627 write_uvarint(&mut o, s.restart_interval as u64);
628 write_uvarint(&mut o, s.n_chunks as u64);
629 write_uvarint(&mut o, s.data_off);
630 write_uvarint(&mut o, s.data_len);
631 write_uvarint(&mut o, s.leaves.len() as u64);
632 for (l, run, coff, key) in &s.leaves {
633 write_uvarint(&mut o, l.n as u64);
634 write_uvarint(&mut o, l.off);
635 write_uvarint(&mut o, l.len);
636 write_uvarint(&mut o, *run as u64);
637 write_uvarint(&mut o, *coff);
638 put_lp(&mut o, key);
639 }
640 }
641 o
642}
643
644pub fn decode_dict_root(p: &[u8]) -> Result<[DictSecRoot; 4], FileError> {
646 let mut c = Cur::new(p);
647 let mut out: [DictSecRoot; 4] = Default::default();
648 for s in out.iter_mut() {
649 s.term_count = c.u32v()?;
650 s.restart_interval = c.u32v()?;
651 s.n_chunks = c.count(u32::MAX as u64)?;
652 s.data_off = c.uv()?;
653 s.data_len = c.uv()?;
654 let nl = c.count(p.len() as u64)?;
655 let mut first = 0usize;
656 for _ in 0..nl {
657 let n = c.count(u32::MAX as u64)?;
658 let off = c.uv()?;
659 let len = c.uv()?;
660 let run = c.count(u64::MAX)?;
661 let coff = c.uv()?;
662 let key = c.lp()?.to_vec();
663 s.leaves
664 .push((LeafRef { first, n, off, len }, run, coff, key));
665 first = first
666 .checked_add(n)
667 .ok_or(FileError::Container("0x06 dict leaves overflow"))?;
668 }
669 if first != s.n_chunks {
670 return Err(FileError::Container(
671 "0x06 dict leaves do not cover the chunks",
672 ));
673 }
674 if s.restart_interval == 0 && s.term_count > 0 {
675 return Err(FileError::Container("0x06 dict zero restart interval"));
676 }
677 }
678 Ok(out)
679}
680
681#[derive(Debug, Clone, Default)]
683pub struct PermRoot {
684 pub si: usize,
686 pub n_tiles: usize,
688 pub data_off: u64,
690 pub data_len: u64,
692 pub leaves: Vec<(LeafRef, u32, u32, u64)>,
694}
695
696pub fn encode_index_root(perms: &[PermRoot]) -> Vec<u8> {
698 let mut o = Vec::new();
699 write_uvarint(&mut o, perms.len() as u64);
700 for p in perms {
701 write_uvarint(&mut o, p.si as u64);
702 write_uvarint(&mut o, p.n_tiles as u64);
703 write_uvarint(&mut o, p.data_off);
704 write_uvarint(&mut o, p.data_len);
705 write_uvarint(&mut o, p.leaves.len() as u64);
706 for (l, mn, mx, toff) in &p.leaves {
707 write_uvarint(&mut o, l.n as u64);
708 write_uvarint(&mut o, l.off);
709 write_uvarint(&mut o, l.len);
710 write_uvarint(&mut o, *mn as u64);
711 write_uvarint(&mut o, *mx as u64);
712 write_uvarint(&mut o, *toff);
713 }
714 }
715 o
716}
717
718pub fn decode_index_root(p: &[u8]) -> Result<Vec<PermRoot>, FileError> {
720 let mut c = Cur::new(p);
721 let n = c.count(6)?;
722 let mut out = Vec::with_capacity(n);
723 for _ in 0..n {
724 let si = c.count(5)?;
725 let n_tiles = c.count(u32::MAX as u64)?;
726 let data_off = c.uv()?;
727 let data_len = c.uv()?;
728 let nl = c.count(p.len() as u64)?;
729 let mut leaves = Vec::with_capacity(nl.min(p.len() / 5));
730 let mut first = 0usize;
731 for _ in 0..nl {
732 let ln = c.count(u32::MAX as u64)?;
733 let off = c.uv()?;
734 let len = c.uv()?;
735 let mn = c.u32v()?;
736 let mx = c.u32v()?;
737 let toff = c.uv()?;
738 leaves.push((
739 LeafRef {
740 first,
741 n: ln,
742 off,
743 len,
744 },
745 mn,
746 mx,
747 toff,
748 ));
749 first = first
750 .checked_add(ln)
751 .ok_or(FileError::Container("0x06 index leaves overflow"))?;
752 }
753 if first != n_tiles {
754 return Err(FileError::Container(
755 "0x06 index leaves do not cover the tiles",
756 ));
757 }
758 out.push(PermRoot {
759 si,
760 n_tiles,
761 data_off,
762 data_len,
763 leaves,
764 });
765 }
766 Ok(out)
767}
768
769#[derive(Debug, Clone, Default)]
771pub struct TextRoot {
772 pub scope: Vec<u8>,
774 pub n_tokens: usize,
776 pub leaves: Vec<(LeafRef, Vec<u8>)>,
778}
779
780pub fn encode_text_root(t: &TextRoot) -> Vec<u8> {
782 let mut o = Vec::new();
783 put_lp(&mut o, &t.scope);
784 write_uvarint(&mut o, t.n_tokens as u64);
785 write_uvarint(&mut o, t.leaves.len() as u64);
786 for (l, first) in &t.leaves {
787 write_uvarint(&mut o, l.n as u64);
788 write_uvarint(&mut o, l.off);
789 write_uvarint(&mut o, l.len);
790 put_lp(&mut o, first);
791 }
792 o
793}
794
795pub fn decode_text_root(p: &[u8]) -> Result<TextRoot, FileError> {
797 let mut c = Cur::new(p);
798 let scope = c.lp()?.to_vec();
799 let n_tokens = c.count(u32::MAX as u64)?;
800 let nl = c.count(p.len() as u64)?;
801 let mut leaves = Vec::with_capacity(nl.min(p.len() / 5));
802 let mut first = 0usize;
803 for _ in 0..nl {
804 let n = c.count(u32::MAX as u64)?;
805 let off = c.uv()?;
806 let len = c.uv()?;
807 let tok = c.lp()?.to_vec();
808 leaves.push((LeafRef { first, n, off, len }, tok));
809 first = first
810 .checked_add(n)
811 .ok_or(FileError::Container("0x06 text leaves overflow"))?;
812 }
813 if first != n_tokens {
814 return Err(FileError::Container(
815 "0x06 text leaves do not cover the tokens",
816 ));
817 }
818 Ok(TextRoot {
819 scope,
820 n_tokens,
821 leaves,
822 })
823}
824
825#[derive(Debug, Clone, Default)]
828pub struct LabelRoot {
829 pub data_len: u64,
832 pub head: Vec<u8>,
834}
835
836pub fn encode_label_root(l: &LabelRoot) -> Vec<u8> {
838 let mut o = Vec::new();
839 write_uvarint(&mut o, l.data_len);
840 put_lp(&mut o, &l.head);
841 o
842}
843
844pub fn decode_label_root(p: &[u8]) -> Result<LabelRoot, FileError> {
846 let mut c = Cur::new(p);
847 let data_len = c.uv()?;
848 let head = c.lp()?.to_vec();
849 if head.len() < 32 {
850 return Err(FileError::Container("0x06 label root truncated"));
851 }
852 Ok(LabelRoot { data_len, head })
853}
854
855#[derive(Debug, Clone, Default)]
857pub struct QuadRoot {
858 pub graph_count: u64,
860 pub quad_count: u64,
862 pub block_graphs: u64,
864 pub p0: Vec<u8>,
867 pub gdir_len: u64,
869 pub orders: Vec<QuadOrderRoot>,
872}
873
874#[derive(Debug, Clone, Default)]
876pub struct QuadOrderRoot {
877 pub si: usize,
879 pub n_tiles: usize,
881 pub leaves: Vec<QuadLeafRoot>,
883}
884
885pub type QuadLeafRoot = (LeafRef, (u32, u32), (u32, u32), u64);
888
889pub fn encode_quad_root(q: &QuadRoot) -> Vec<u8> {
891 let mut o = Vec::new();
892 write_uvarint(&mut o, q.graph_count);
893 write_uvarint(&mut o, q.quad_count);
894 write_uvarint(&mut o, q.block_graphs);
895 put_lp(&mut o, &q.p0);
896 write_uvarint(&mut o, q.gdir_len);
897 write_uvarint(&mut o, q.orders.len() as u64);
898 for ord in &q.orders {
899 write_uvarint(&mut o, ord.si as u64);
900 write_uvarint(&mut o, ord.n_tiles as u64);
901 write_uvarint(&mut o, ord.leaves.len() as u64);
902 for (l, f, la, toff) in &ord.leaves {
903 write_uvarint(&mut o, l.n as u64);
904 write_uvarint(&mut o, l.off);
905 write_uvarint(&mut o, l.len);
906 write_uvarint(&mut o, f.0 as u64);
907 write_uvarint(&mut o, f.1 as u64);
908 write_uvarint(&mut o, la.0 as u64);
909 write_uvarint(&mut o, la.1 as u64);
910 write_uvarint(&mut o, *toff);
911 }
912 }
913 o
914}
915
916pub fn decode_quad_root(p: &[u8]) -> Result<QuadRoot, FileError> {
918 let mut c = Cur::new(p);
919 let graph_count = c.uv()?;
920 let quad_count = c.uv()?;
921 let block_graphs = c.uv()?;
922 let p0 = c.lp()?.to_vec();
923 let gdir_len = c.uv()?;
924 let no = c.count(6)?;
925 let mut orders = Vec::with_capacity(no);
926 for _ in 0..no {
927 let si = c.count(5)?;
928 let n_tiles = c.count(u32::MAX as u64)?;
929 let nl = c.count(p.len() as u64)?;
930 let mut leaves = Vec::with_capacity(nl.min(p.len() / 5));
931 let mut first = 0usize;
932 for _ in 0..nl {
933 let n = c.count(u32::MAX as u64)?;
934 let off = c.uv()?;
935 let len = c.uv()?;
936 let f = (c.u32v()?, c.u32v()?);
937 let la = (c.u32v()?, c.u32v()?);
938 let toff = c.uv()?;
939 leaves.push((LeafRef { first, n, off, len }, f, la, toff));
940 first = first
941 .checked_add(n)
942 .ok_or(FileError::Container("0x06 quad leaves overflow"))?;
943 }
944 if first != n_tiles {
945 return Err(FileError::Container(
946 "0x06 quad leaves do not cover the tiles",
947 ));
948 }
949 orders.push(QuadOrderRoot {
950 si,
951 n_tiles,
952 leaves,
953 });
954 }
955 if block_graphs == 0 && graph_count > 0 {
956 return Err(FileError::Container("0x06 quad block size is zero"));
957 }
958 Ok(QuadRoot {
959 graph_count,
960 quad_count,
961 block_graphs,
962 p0,
963 gdir_len,
964 orders,
965 })
966}
967
968#[derive(Debug, Clone, PartialEq, Eq)]
974pub struct ChunkEnt {
975 pub first_run: usize,
977 pub key: Vec<u8>,
979 pub clen: u64,
981}
982
983pub fn encode_chunk_leaf(ents: &[ChunkEnt]) -> Vec<u8> {
985 let mut o = Vec::new();
986 let mut prev = 0usize;
987 for (i, e) in ents.iter().enumerate() {
988 write_uvarint(
989 &mut o,
990 (if i == 0 {
991 e.first_run
992 } else {
993 e.first_run - prev
994 }) as u64,
995 );
996 put_lp(&mut o, &e.key);
997 write_uvarint(&mut o, e.clen);
998 prev = e.first_run;
999 }
1000 o
1001}
1002
1003pub fn decode_chunk_leaf(b: &[u8], n: usize) -> Result<Vec<ChunkEnt>, FileError> {
1005 let mut c = Cur::new(b);
1006 let mut out = Vec::with_capacity(n.min(b.len()));
1007 let mut prev = 0usize;
1008 for i in 0..n {
1009 let d = c.count(u64::MAX)?;
1010 let first_run = if i == 0 {
1011 d
1012 } else {
1013 prev.checked_add(d)
1014 .ok_or(FileError::Container("0x06 chunk run overflow"))?
1015 };
1016 let key = c.lp()?.to_vec();
1017 let clen = c.uv()?;
1018 out.push(ChunkEnt {
1019 first_run,
1020 key,
1021 clen,
1022 });
1023 prev = first_run;
1024 }
1025 Ok(out)
1026}
1027
1028#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1030pub struct TileEnt {
1031 pub min_a: u32,
1033 pub max_a: u32,
1035 pub clen: u64,
1037 pub syn: (u32, u32, u32, u32),
1039}
1040
1041pub fn encode_tile_leaf(ents: &[TileEnt]) -> Vec<u8> {
1043 let mut o = Vec::new();
1044 let mut prev = 0u32;
1045 for (i, e) in ents.iter().enumerate() {
1046 write_uvarint(
1047 &mut o,
1048 (if i == 0 {
1049 e.min_a
1050 } else {
1051 e.min_a.wrapping_sub(prev)
1052 }) as u64,
1053 );
1054 write_uvarint(&mut o, e.max_a.wrapping_sub(e.min_a) as u64);
1055 write_uvarint(&mut o, e.clen);
1056 prev = e.min_a;
1057 }
1058 for e in ents {
1059 write_uvarint(&mut o, e.syn.0 as u64);
1060 write_uvarint(&mut o, e.syn.1.wrapping_sub(e.syn.0) as u64);
1061 write_uvarint(&mut o, e.syn.2 as u64);
1062 write_uvarint(&mut o, e.syn.3.wrapping_sub(e.syn.2) as u64);
1063 }
1064 o
1065}
1066
1067pub fn decode_tile_leaf(b: &[u8], n: usize) -> Result<Vec<TileEnt>, FileError> {
1069 let mut c = Cur::new(b);
1070 let mut out = Vec::with_capacity(n.min(b.len()));
1071 let mut prev = 0u32;
1072 for i in 0..n {
1073 let d = c.u32v()?;
1074 let min_a = if i == 0 { d } else { prev.wrapping_add(d) };
1075 let span = c.u32v()?;
1076 let clen = c.uv()?;
1077 out.push(TileEnt {
1078 min_a,
1079 max_a: min_a.wrapping_add(span),
1080 clen,
1081 syn: (0, 0, 0, 0),
1082 });
1083 prev = min_a;
1084 }
1085 for e in out.iter_mut() {
1086 let mb = c.u32v()?;
1087 let db = c.u32v()?;
1088 let mc = c.u32v()?;
1089 let dc = c.u32v()?;
1090 e.syn = (mb, mb.wrapping_add(db), mc, mc.wrapping_add(dc));
1091 }
1092 Ok(out)
1093}
1094
1095#[derive(Debug, Clone, PartialEq, Eq)]
1097pub struct TokenEnt {
1098 pub token: Vec<u8>,
1100 pub off: u64,
1102 pub len: u64,
1104}
1105
1106pub fn encode_token_leaf(ents: &[TokenEnt]) -> Vec<u8> {
1108 let mut o = Vec::new();
1109 let mut prev: &[u8] = &[];
1110 for e in ents {
1111 let shared = prev
1112 .iter()
1113 .zip(&e.token)
1114 .take_while(|(a, b)| a == b)
1115 .count();
1116 write_uvarint(&mut o, shared as u64);
1117 put_lp(&mut o, &e.token[shared..]);
1118 write_uvarint(&mut o, e.off);
1119 write_uvarint(&mut o, e.len);
1120 prev = &e.token;
1121 }
1122 o
1123}
1124
1125pub fn decode_token_leaf(b: &[u8], n: usize) -> Result<Vec<TokenEnt>, FileError> {
1127 let mut c = Cur::new(b);
1128 let mut out: Vec<TokenEnt> = Vec::with_capacity(n.min(b.len()));
1129 for _ in 0..n {
1130 let shared = c.count(u32::MAX as u64)?;
1131 let suffix = c.lp()?;
1132 let mut token = match out.last() {
1133 Some(p) if shared <= p.token.len() => p.token[..shared].to_vec(),
1134 None if shared == 0 => Vec::new(),
1135 _ => return Err(FileError::Container("0x06 token front-coding out of range")),
1136 };
1137 token.extend_from_slice(suffix);
1138 let off = c.uv()?;
1139 let len = c.uv()?;
1140 out.push(TokenEnt { token, off, len });
1141 }
1142 Ok(out)
1143}
1144
1145#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1147pub struct QTileEnt {
1148 pub first: (u32, u32),
1150 pub last: (u32, u32),
1152 pub clen: u64,
1154 pub raw: bool,
1156 pub syn: (u32, u32, u32, u32),
1158 pub off: u64,
1160}
1161
1162pub fn encode_qtile_leaf(ents: &[QTileEnt], leaf_off: u64) -> Vec<u8> {
1166 let mut o = Vec::new();
1167 let mut prev_g = 0u32;
1168 let mut next = leaf_off;
1169 for (i, e) in ents.iter().enumerate() {
1170 write_uvarint(
1171 &mut o,
1172 (if i == 0 {
1173 e.first.0
1174 } else {
1175 e.first.0 - prev_g
1176 }) as u64,
1177 );
1178 write_uvarint(&mut o, e.first.1 as u64);
1179 write_uvarint(&mut o, (e.last.0 - e.first.0) as u64);
1180 write_uvarint(&mut o, e.last.1 as u64);
1181 write_uvarint(&mut o, (e.clen << 1) | e.raw as u64);
1182 write_uvarint(&mut o, e.syn.0 as u64);
1183 write_uvarint(&mut o, e.syn.1.wrapping_sub(e.syn.0) as u64);
1184 write_uvarint(&mut o, e.syn.2 as u64);
1185 write_uvarint(&mut o, e.syn.3.wrapping_sub(e.syn.2) as u64);
1186 write_uvarint(&mut o, if e.off == next { 0 } else { e.off + 1 });
1187 next = e.off + e.clen;
1188 prev_g = e.first.0;
1189 }
1190 o
1191}
1192
1193pub fn decode_qtile_leaf(b: &[u8], n: usize, leaf_off: u64) -> Result<Vec<QTileEnt>, FileError> {
1195 let mut c = Cur::new(b);
1196 let mut out = Vec::with_capacity(n.min(b.len()));
1197 let mut prev_g = 0u32;
1198 let mut next = leaf_off;
1199 for i in 0..n {
1200 let d = c.u32v()?;
1201 let g = if i == 0 {
1202 d
1203 } else {
1204 prev_g
1205 .checked_add(d)
1206 .ok_or(FileError::Container("0x06 quad graph overflow"))?
1207 };
1208 let fa = c.u32v()?;
1209 let dg = c.u32v()?;
1210 let la = c.u32v()?;
1211 let packed = c.uv()?;
1212 let mb = c.u32v()?;
1213 let db = c.u32v()?;
1214 let mc = c.u32v()?;
1215 let dc = c.u32v()?;
1216 let oc = c.uv()?;
1217 let off = if oc == 0 { next } else { oc - 1 };
1218 let clen = packed >> 1;
1219 out.push(QTileEnt {
1220 first: (g, fa),
1221 last: (
1222 g.checked_add(dg)
1223 .ok_or(FileError::Container("0x06 quad graph overflow"))?,
1224 la,
1225 ),
1226 clen,
1227 raw: packed & 1 == 1,
1228 syn: (mb, mb.wrapping_add(db), mc, mc.wrapping_add(dc)),
1229 off,
1230 });
1231 next = off
1232 .checked_add(clen)
1233 .ok_or(FileError::Container("0x06 quad offset overflow"))?;
1234 prev_g = g;
1235 }
1236 Ok(out)
1237}
1238
1239pub fn cut(sizes: &[usize], target: usize) -> Vec<(usize, usize)> {
1242 let mut out = Vec::new();
1243 let mut start = 0usize;
1244 let mut acc = 0usize;
1245 for (i, &s) in sizes.iter().enumerate() {
1246 acc += s;
1247 if acc >= target {
1248 out.push((start, i + 1));
1249 start = i + 1;
1250 acc = 0;
1251 }
1252 }
1253 if start < sizes.len() {
1254 out.push((start, sizes.len()));
1255 }
1256 out
1257}
1258
1259pub fn logical_header(h: &Header06) -> crate::header::Header {
1265 use crate::header::{Section, SectionKind};
1266 let end = h.file_len;
1267 let mut flags = crate::header::FLAG_TILE_SYNOPSIS;
1268 if h.flags & F_NAMED != 0 {
1269 flags |= crate::header::FLAG_HAS_QUADS;
1270 }
1271 if h.flags & F_QUOTED != 0 {
1272 flags |= crate::header::FLAG_HAS_QUOTED_TRIPLES;
1273 }
1274 if h.has(P_QUAD) {
1275 flags |= crate::header::FLAG_QUAD_INDEX;
1276 }
1277 if h.flags & F_TEXT_NAMED != 0 && h.has(P_TEXT) {
1278 flags |= crate::header::FLAG_TEXT_INDEX_NAMED_GRAPHS;
1279 }
1280 let present = |bit: u16| if h.has(bit) { 1u64 } else { 0 };
1281 let mut extra = Vec::new();
1282 if h.has(P_LABELS) {
1283 extra.push(Section {
1284 kind: SectionKind::LabelIndex,
1285 flags: 0,
1286 offset: end,
1287 length: 1,
1288 });
1289 }
1290 if h.has(P_SCHEMA) {
1291 extra.push(Section {
1292 kind: SectionKind::Schema,
1293 flags: 0,
1294 offset: end,
1295 length: 1,
1296 });
1297 }
1298 if h.has(P_QUAD) {
1299 extra.push(Section {
1300 kind: SectionKind::QuadIndex,
1301 flags: 0,
1302 offset: end,
1303 length: 1,
1304 });
1305 }
1306 crate::header::Header {
1307 version: VERSION,
1308 flags,
1309 metadata_offset: end,
1310 metadata_len: h.card_len as u64,
1311 dictionary_offset: end,
1312 dictionary_len: 0,
1313 root_dir_offset: end,
1314 root_dir_len: 0,
1315 pyramid_meta_offset: end,
1316 pyramid_meta_len: present(P_PYRAMID),
1317 dict_codec: h.dict_codec,
1318 block_codec: h.block_codec,
1319 pyramid_levels: h.pyramid_levels,
1320 id_widths: h.id_widths,
1321 quad_count: h.quad_count,
1322 term_count: h.term_count,
1323 content_hash: h.content_hash,
1324 named_graphs_offset: 0,
1325 named_graphs_len: 0,
1326 schema_meta_len: 0,
1327 text_index_offset: end,
1328 text_index_len: present(P_TEXT),
1329 text_index_partial: h.has(P_TEXT_PARTIAL),
1330 build_info_offset: end,
1331 build_info_len: h.build_len as u64,
1332 perms: h.perms(),
1333 extra_sections: extra,
1334 }
1335}
1336
1337#[cfg(test)]
1338mod tests {
1339 use super::*;
1340
1341 #[test]
1342 fn header_round_trips() {
1343 let h = Header06 {
1344 flags: F_NAMED | F_QUAD_GRAPH_MAJOR,
1345 content_hash: [7; 16],
1346 file_len: 10_000,
1347 root_len: 300,
1348 card_region_len: 100,
1349 data_offset: 2_000,
1350 quad_count: 5,
1351 term_count: 9,
1352 graph_count: 2,
1353 perm_mask: 0b11_1111,
1354 dict_codec: 1,
1355 block_codec: 1,
1356 internal_codec: 1,
1357 pyramid_levels: 0,
1358 id_widths: 0,
1359 presence: P_TEXT | P_QUAD,
1360 card_off: 0,
1361 card_len: 40,
1362 card_digest: [3; 16],
1363 build_off: 40,
1364 build_len: 20,
1365 root_digest: [4; 16],
1366 };
1367 let b = h.to_bytes();
1368 assert_eq!(Header06::from_bytes(&b).unwrap(), h);
1369 let mut h2 = h.clone();
1371 h2.data_offset = 3_000;
1372 h2.build_len = 0;
1373 h2.file_len = 11_000;
1374 assert_eq!(content_hash(&h), content_hash(&h2));
1375 h2.card_digest = [9; 16];
1376 assert_ne!(content_hash(&h), content_hash(&h2));
1377 }
1378
1379 #[test]
1380 fn leaves_round_trip() {
1381 let c = vec![
1382 ChunkEnt {
1383 first_run: 0,
1384 key: vec![],
1385 clen: 10,
1386 },
1387 ChunkEnt {
1388 first_run: 5,
1389 key: b"ab".to_vec(),
1390 clen: 20,
1391 },
1392 ];
1393 assert_eq!(decode_chunk_leaf(&encode_chunk_leaf(&c), 2).unwrap(), c);
1394 let t = vec![
1395 TileEnt {
1396 min_a: 3,
1397 max_a: 9,
1398 clen: 100,
1399 syn: (1, 4, 2, 8),
1400 },
1401 TileEnt {
1402 min_a: 9,
1403 max_a: 20,
1404 clen: 7,
1405 syn: (0, 0, 5, 5),
1406 },
1407 ];
1408 assert_eq!(decode_tile_leaf(&encode_tile_leaf(&t), 2).unwrap(), t);
1409 let k = vec![
1410 TokenEnt {
1411 token: b"apple".to_vec(),
1412 off: 0,
1413 len: 3,
1414 },
1415 TokenEnt {
1416 token: b"apply".to_vec(),
1417 off: 3,
1418 len: 4,
1419 },
1420 ];
1421 assert_eq!(decode_token_leaf(&encode_token_leaf(&k), 2).unwrap(), k);
1422 let q = vec![
1423 QTileEnt {
1424 first: (5, 1),
1425 last: (6, 2),
1426 clen: 30,
1427 raw: true,
1428 syn: (0, 1, 2, 3),
1429 off: 100,
1430 },
1431 QTileEnt {
1432 first: (6, 3),
1433 last: (9, 0),
1434 clen: 10,
1435 raw: false,
1436 syn: (4, 4, 4, 4),
1437 off: 500,
1438 },
1439 ];
1440 assert_eq!(
1441 decode_qtile_leaf(&encode_qtile_leaf(&q, 100), 2, 100).unwrap(),
1442 q
1443 );
1444 }
1445
1446 #[test]
1447 fn cut_covers_everything() {
1448 assert_eq!(cut(&[5, 5, 5, 5, 5], 10), vec![(0, 2), (2, 4), (4, 5)]);
1449 assert_eq!(cut(&[], 10), Vec::<(usize, usize)>::new());
1450 assert_eq!(cut(&[50], 10), vec![(0, 1)]);
1451 }
1452}
1453
1454#[cfg(test)]
1455mod id_width_tests {
1456 use super::*;
1457
1458 fn sample() -> Header06 {
1459 Header06 {
1460 file_len: 10_000,
1461 root_len: 300,
1462 card_region_len: 100,
1463 data_offset: 2_000,
1464 perm_mask: 0b11_1111,
1465 pyramid_levels: 7,
1466 ..Default::default()
1467 }
1468 }
1469
1470 #[test]
1471 fn width_byte_round_trips_and_is_hashed() {
1472 let h = sample();
1473 assert_eq!(Header06::from_bytes(&h.to_bytes()).unwrap(), h);
1474 assert_eq!(h.to_bytes()[76], 7);
1475 assert_eq!(h.to_bytes()[77], 0);
1476 let mut other = h.clone();
1478 other.id_widths = 1;
1479 assert_ne!(content_hash(&h), content_hash(&other));
1480 }
1481
1482 #[test]
1483 fn a_reader_refuses_every_nonzero_width() {
1484 for (i, name) in id_position_names().iter().enumerate() {
1485 let mut b = sample().to_bytes();
1486 b[77] = 1 << (2 * i);
1487 let e = Header06::from_bytes(&b).unwrap_err().to_string();
1488 assert!(
1489 e.contains(&format!(
1490 "u64 term ids ({name}): not supported by this reader"
1491 )),
1492 "{e}"
1493 );
1494 for reserved in [2u8, 3] {
1495 let mut b = sample().to_bytes();
1496 b[77] = reserved << (2 * i);
1497 let e = Header06::from_bytes(&b).unwrap_err().to_string();
1498 assert!(e.contains("reserved term id width"), "{e}");
1499 }
1500 }
1501 }
1502
1503 #[test]
1504 fn a_writer_refuses_an_id_that_does_not_fit_u32() {
1505 assert_eq!(id_widths_for([u32::MAX as u64; 4]).unwrap(), 0);
1506 for (i, name) in id_position_names().iter().enumerate() {
1507 let mut m = [1u64; 4];
1508 m[i] = u32::MAX as u64 + 1;
1509 let e = id_widths_for(m).unwrap_err().to_string();
1510 assert!(
1511 e.contains(&format!("needs u64 term ids in {name}, not supported yet")),
1512 "{e}"
1513 );
1514 }
1515 }
1516}
1517
1518#[cfg(test)]
1519mod hostile_root_tests {
1520 use super::*;
1521
1522 fn sec(kind: u8, offset: u64, len: u64) -> Sec06 {
1523 Sec06 {
1524 kind,
1525 max_block: 0,
1526 offset,
1527 len,
1528 digest: [0; 16],
1529 }
1530 }
1531
1532 #[test]
1533 fn section_range_overflow_is_an_error() {
1534 let h = Header06 {
1535 data_offset: 100,
1536 ..Header06::default()
1537 };
1538 for (k, o, l) in [
1541 (kind::DICT_DATA, u64::MAX, 0),
1542 (kind::DICT_DATA, u64::MAX - 50, 10),
1543 (kind::DICT_DATA, 10, u64::MAX),
1544 (kind::DICT_LEAVES, u64::MAX, 1),
1545 ] {
1546 assert!(sec(k, o, l).abs(&h).is_err(), "kind {k} offset {o} len {l}");
1547 }
1548 let ok = sec(kind::DICT_DATA, 5, 7).abs(&h).unwrap();
1549 assert_eq!((ok.offset, ok.len), (105, 7));
1550 }
1551
1552 fn varints(vals: &[u64]) -> Vec<u8> {
1555 let mut v = Vec::new();
1556 for &x in vals {
1557 write_uvarint(&mut v, x);
1558 }
1559 v
1560 }
1561
1562 #[test]
1563 fn index_root_with_a_huge_leaf_count_is_refused() {
1564 let mut p = varints(&[1, 0, 1, 0, 0, 60]);
1566 p.resize(p.len() + 54, 0);
1567 assert!(decode_index_root(&p).is_err());
1568 }
1569
1570 #[test]
1571 fn text_root_with_a_huge_leaf_count_is_refused() {
1572 let mut p = varints(&[0, 1, 60]);
1574 p.resize(p.len() + 50, 0);
1575 assert!(decode_text_root(&p).is_err());
1576 }
1577
1578 #[test]
1579 fn quad_root_with_a_huge_leaf_count_is_refused() {
1580 let mut p = varints(&[1, 1, 1, 0, 0, 1, 0, 1, 60]);
1583 p.resize(p.len() + 50, 0);
1584 assert!(decode_quad_root(&p).is_err());
1585 }
1586}