1use rudb_common::{Error, Result};
51use rudb_encoding::bitpack;
52
53use crate::bits::BitVector;
54use crate::rid::{NO_PARENT, PART_ROWS, Rid};
55use crate::tail::Tail;
56
57const LAYOUT: u8 = 1;
59
60pub const HEADER_BYTES: usize = 32;
64
65#[derive(Debug, Clone, Copy, PartialEq, Eq)]
68pub struct Counts {
69 pub children: u64,
71 pub parents: u64,
73 pub linked: u64,
75 pub form: Form,
77}
78
79#[derive(Debug, Clone, Copy, PartialEq, Eq)]
81pub enum Form {
82 Packed,
84 Monotone,
86}
87
88impl Form {
89 #[must_use]
91 pub fn tag(self) -> u8 {
92 match self {
93 Self::Packed => 0,
94 Self::Monotone => 1,
95 }
96 }
97
98 #[must_use]
100 pub fn label(self) -> &'static str {
101 match self {
102 Self::Packed => "packed",
103 Self::Monotone => "monotone",
104 }
105 }
106
107 pub fn from_tag(tag: u8) -> Result<Self> {
113 match tag {
114 0 => Ok(Self::Packed),
115 1 => Ok(Self::Monotone),
116 _ => Err(malformed(format!("forward link form {tag} is not one this build knows"))),
117 }
118 }
119}
120
121pub type Bounds = Option<(Rid, Rid)>;
126
127#[derive(Debug, Clone)]
128enum Body {
129 Packed {
130 bytes: Tail,
132 width: usize,
133 heads: Vec<Bounds>,
135 },
136 Monotone {
137 vector: BitVector,
138 },
139}
140
141#[derive(Debug, Clone)]
143pub struct Link {
144 children: u64,
145 parents: u64,
146 linked: u64,
147 body: Body,
148}
149
150#[derive(Debug, Clone, Copy, Default)]
152pub struct Cursor {
153 at: Option<At>,
154}
155
156#[derive(Debug, Clone, Copy)]
157struct At {
158 parent: Rid,
159 zero: usize,
161 from: Rid,
162 to: Rid,
163}
164
165impl Cursor {
166 const NEAR: Rid = 64;
169}
170
171fn kth_zero(words: &[u64], start: usize, k: u64) -> Option<usize> {
174 let mut left = k;
175 let mut word = start / 64;
176 let mut zeros = !*words.get(word)? & (u64::MAX << (start % 64));
177 loop {
178 let here = u64::from(zeros.count_ones());
179 if left < here {
180 return Some(word * 64 + crate::bits::nth_set(zeros, left as u32) as usize);
182 }
183 left -= here;
184 word += 1;
185 zeros = !*words.get(word)?;
186 }
187}
188
189impl Link {
190 pub fn build(parents_of: &[Rid], parents: u64) -> Result<Self> {
204 let children = count(parents_of.len());
205 let mut linked = 0_u64;
206 let mut monotone = true;
207 let mut previous = 0_u64;
208 for parent in parents_of {
209 if *parent == NO_PARENT {
210 monotone = false;
211 continue;
212 }
213 if *parent >= parents {
214 return Err(malformed(format!(
215 "a forward link points at parent {parent} of a table with {parents} rows"
216 )));
217 }
218 if *parent < previous {
219 monotone = false;
220 }
221 previous = *parent;
222 linked += 1;
223 }
224 let body = if monotone && parents > 0 {
225 Body::Monotone { vector: runs(parents_of, parents)? }
226 } else {
227 packed(parents_of, parents)?
228 };
229 Ok(Self { children, parents, linked, body })
230 }
231
232 #[must_use]
234 pub fn form(&self) -> Form {
235 match self.body {
236 Body::Packed { .. } => Form::Packed,
237 Body::Monotone { .. } => Form::Monotone,
238 }
239 }
240
241 #[must_use]
243 pub fn children(&self) -> u64 {
244 self.children
245 }
246
247 #[must_use]
249 pub fn parents(&self) -> u64 {
250 self.parents
251 }
252
253 #[must_use]
255 pub fn linked(&self) -> u64 {
256 self.linked
257 }
258
259 pub(crate) fn runs(&self) -> Option<&BitVector> {
265 match &self.body {
266 Body::Monotone { vector } => Some(vector),
267 Body::Packed { .. } => None,
268 }
269 }
270
271 #[must_use]
273 pub fn bytes(&self) -> usize {
274 match &self.body {
275 Body::Packed { bytes, heads, .. } => bytes.len() + heads.len() * 16,
276 Body::Monotone { vector } => vector.bytes(),
277 }
278 }
279
280 #[must_use]
282 pub fn forward(&self, child: Rid) -> Option<Rid> {
283 if child >= self.children {
284 return None;
285 }
286 match &self.body {
287 Body::Packed { bytes, width, .. } => {
288 let value = bitpack::tail_at(bytes, *width, usize::try_from(child).ok()?).ok()?;
293 (value != reserved(*width)).then_some(value)
294 }
295 Body::Monotone { vector } => {
296 let at = vector.select1(child)?;
297 Some(vector.rank0(at))
298 }
299 }
300 }
301
302 pub fn forward_run(&self, first: Rid, out: &mut [Rid]) -> Result<()> {
314 let end = first.checked_add(count(out.len()));
315 if end.is_none_or(|end| end > self.children) {
316 return Err(Error::internal(format!(
317 "a run of {} children from {first} goes past the {} the link has",
318 out.len(),
319 self.children
320 )));
321 }
322 if out.is_empty() {
323 return Ok(());
324 }
325 match &self.body {
326 Body::Packed { bytes, width, .. } => {
327 let absent = reserved(*width);
328 let start = usize::try_from(first)
329 .map_err(|_| malformed("a child past what fits in memory"))?;
330 for (at, slot) in out.iter_mut().enumerate() {
331 let value = bitpack::tail_at(bytes, *width, start + at)?;
332 *slot = if value == absent { NO_PARENT } else { value };
333 }
334 }
335 Body::Monotone { vector } => {
336 let Some(mut at) = vector.select1(first) else {
339 return Err(malformed("a monotone link has fewer ones than children"));
340 };
341 let mut parent = vector.rank0(at);
342 let words = vector.words();
343 for slot in out.iter_mut() {
344 loop {
347 let word = words.get(at / 64).copied().unwrap_or(0) >> (at % 64);
348 if word == 0 {
349 let skipped = 64 - at % 64;
350 parent += count(skipped);
351 at += skipped;
352 if at >= vector.len() {
353 return Err(malformed("a monotone link ran out of ones"));
354 }
355 continue;
356 }
357 let zeros = word.trailing_zeros() as usize;
358 parent += count(zeros);
359 at += zeros;
360 break;
361 }
362 *slot = parent;
363 at += 1;
364 }
365 }
366 }
367 Ok(())
368 }
369
370 pub fn forward_each(&self, children: &[Rid], out: &mut Vec<Rid>) {
381 out.clear();
382 out.reserve(children.len());
383 let Body::Monotone { vector } = &self.body else {
384 out.extend(children.iter().map(|&child| self.forward(child).unwrap_or(NO_PARENT)));
385 return;
386 };
387 let words = vector.words();
388 let mut last: Option<(Rid, usize, Rid)> = None;
390 for &child in children {
391 if child >= self.children {
392 out.push(NO_PARENT);
393 continue;
394 }
395 let near = last.filter(|&(from, ..)| child >= from && child - from <= Self::WALK);
396 let (at, parent) = match near {
397 Some((from, at, parent)) => {
398 walk_ones(words, at, parent, child - from).unwrap_or((usize::MAX, NO_PARENT))
399 }
400 None => match vector.select1(child) {
401 Some(at) => (at, vector.rank0(at)),
402 None => (usize::MAX, NO_PARENT),
403 },
404 };
405 if parent == NO_PARENT {
406 last = None;
407 } else {
408 last = Some((child, at, parent));
409 }
410 out.push(parent);
411 }
412 }
413
414 const WALK: Rid = 1024;
420
421 #[must_use]
426 pub fn backward(&self, parent: Rid) -> Option<std::ops::Range<Rid>> {
427 let Body::Monotone { vector } = &self.body else { return None };
428 if parent >= self.parents {
429 return None;
430 }
431 let cum = |nth: Rid| -> Option<u64> { vector.select0(nth).map(|at| count(at) - nth) };
434 let from = if parent == 0 { 0 } else { cum(parent - 1)? };
435 Some(from..cum(parent)?)
436 }
437
438 #[must_use]
447 pub fn backward_from(&self, parent: Rid, cursor: &mut Cursor) -> Option<std::ops::Range<Rid>> {
448 let Body::Monotone { vector } = &self.body else { return None };
449 if parent >= self.parents {
450 return None;
451 }
452 if let Some(at) = cursor.at
453 && at.parent == parent
454 {
455 return Some(at.from..at.to);
456 }
457 let near = cursor.at.filter(|at| at.parent < parent && parent - at.parent <= Cursor::NEAR);
458 let found = match near {
459 Some(at) => {
460 let words = vector.words();
461 let start = at.zero + 1;
464 let between = parent - at.parent - 1;
465 let (first, from) = if between == 0 {
466 (start, at.to)
467 } else {
468 let before = kth_zero(words, start, between - 1)?;
469 (before + 1, at.to + count(before - start) - (between - 1))
470 };
471 let zero = kth_zero(words, first, 0)?;
472 At { parent, zero, from, to: from + count(zero - first) }
473 }
474 None => {
475 let zero = vector.select0(parent)?;
476 let to = count(zero) - parent;
477 let from =
478 if parent == 0 { 0 } else { count(vector.select0(parent - 1)?) - (parent - 1) };
479 At { parent, zero, from, to }
480 }
481 };
482 cursor.at = Some(found);
483 Some(found.from..found.to)
484 }
485
486 #[must_use]
491 pub fn part_bounds(&self, part: usize) -> Option<Bounds> {
492 let first = count(part * PART_ROWS);
493 if first >= self.children {
494 return None;
495 }
496 match &self.body {
497 Body::Packed { heads, .. } => heads.get(part).copied(),
498 Body::Monotone { .. } => {
501 let last = (first + count(PART_ROWS) - 1).min(self.children - 1);
502 match (self.forward(first), self.forward(last)) {
503 (Some(low), Some(high)) => Some(Some((low, high))),
504 _ => Some(None),
505 }
506 }
507 }
508 }
509
510 pub fn write(&self, out: &mut Vec<u8>) -> Result<()> {
516 let start = out.len();
517 out.extend_from_slice(&self.children.to_le_bytes());
518 out.extend_from_slice(&self.parents.to_le_bytes());
519 out.extend_from_slice(&self.linked.to_le_bytes());
520 out.push(self.form().tag());
521 out.push(match &self.body {
522 Body::Packed { width, .. } => u8::try_from(*width)
526 .map_err(|_| malformed("a forward link wider than a byte can name"))?,
527 Body::Monotone { .. } => 0,
528 });
529 out.push(LAYOUT);
530 out.extend_from_slice(&[0; 5]);
535 debug_assert_eq!(
536 out.len() - start,
537 HEADER_BYTES,
538 "the forward link header is thirty two bytes"
539 );
540 match &self.body {
541 Body::Packed { bytes, heads, .. } => {
542 for head in heads {
543 let (low, high) = head.unwrap_or((NO_PARENT, NO_PARENT));
544 out.extend_from_slice(&low.to_le_bytes());
545 out.extend_from_slice(&high.to_le_bytes());
546 }
547 out.extend_from_slice(bytes);
548 }
549 Body::Monotone { vector } => vector.write(out),
550 }
551 Ok(())
552 }
553
554 pub fn counts(bytes: &[u8]) -> Result<Counts> {
566 if bytes.len() < HEADER_BYTES {
567 return Err(malformed("a forward link payload is shorter than its header"));
568 }
569 let form = Form::from_tag(bytes[24])?;
570 if bytes[26] != LAYOUT {
571 return Err(malformed(format!(
572 "forward link layout {} is not one this build knows",
573 bytes[26]
574 )));
575 }
576 Ok(Counts {
577 children: number(&bytes[0..8])?,
578 parents: number(&bytes[8..16])?,
579 linked: number(&bytes[16..24])?,
580 form,
581 })
582 }
583
584 pub fn read(bytes: &[u8]) -> Result<Self> {
592 Self::read_from(bytes.to_vec(), 0)
593 }
594
595 pub fn read_from(payload: Vec<u8>, at: usize) -> Result<Self> {
602 let bytes = payload.get(at..).ok_or_else(|| malformed("a forward link header is torn"))?;
603 let Counts { children, parents, linked, form } = Self::counts(bytes)?;
604 let width = bytes[25] as usize;
605 let rest = &bytes[HEADER_BYTES..];
606 let body = match form {
607 Form::Packed => {
608 if width != width_for(parents) {
609 return Err(malformed(
610 "a forward link's width is not the one its parents imply",
611 ));
612 }
613 let parts = usize::try_from(children.div_ceil(count(PART_ROWS)))
614 .map_err(|_| malformed("a forward link with more parts than fit in memory"))?;
615 let head = parts * 16;
616 let rows = usize::try_from(children)
617 .map_err(|_| malformed("a forward link longer than fits in memory"))?;
618 let packed = bitpack::tail_len(rows, width);
619 if rest.len() != head + packed {
620 return Err(malformed(
621 "a forward link's body is not the size its header implies",
622 ));
623 }
624 let mut heads = Vec::with_capacity(parts);
625 for part in 0..parts {
626 let low = number(&rest[part * 16..part * 16 + 8])?;
627 let high = number(&rest[part * 16 + 8..part * 16 + 16])?;
628 heads.push((low != NO_PARENT).then_some((low, high)));
629 }
630 let bytes = Tail::of(payload, at + HEADER_BYTES + head).ok_or_else(|| {
631 malformed("a forward link's body is not the size its header implies")
632 })?;
633 Body::Packed { bytes, width, heads }
634 }
635 Form::Monotone => {
636 let len = usize::try_from(linked + parents)
637 .map_err(|_| malformed("a forward link longer than fits in memory"))?;
638 Body::Monotone { vector: BitVector::read(rest, len)? }
639 }
640 };
641 Ok(Self { children, parents, linked, body })
642 }
643}
644
645fn runs(parents_of: &[Rid], parents: u64) -> Result<BitVector> {
647 let len = usize::try_from(count(parents_of.len()) + parents)
648 .map_err(|_| malformed("a forward link longer than fits in memory"))?;
649 let mut words = vec![0_u64; len.div_ceil(64)];
650 let mut at = 0_usize;
651 let mut child = 0_usize;
652 for parent in 0..parents {
653 while child < parents_of.len() && parents_of[child] == parent {
654 words[at / 64] |= 1 << (at % 64);
655 at += 1;
656 child += 1;
657 }
658 at += 1;
659 }
660 debug_assert_eq!(at, len, "every child is a one and every parent is a zero");
661 BitVector::new(words, len)
662}
663
664fn packed(parents_of: &[Rid], parents: u64) -> Result<Body> {
672 let width = width_for(parents);
673 let absent = reserved(width);
674 let mut heads = Vec::with_capacity(parents_of.len().div_ceil(PART_ROWS));
675 for rows in parents_of.chunks(PART_ROWS) {
676 let mut bounds: Bounds = None;
677 for parent in rows {
678 if *parent == NO_PARENT {
679 continue;
680 }
681 bounds = Some(match bounds {
682 None => (*parent, *parent),
683 Some((low, high)) => (low.min(*parent), high.max(*parent)),
684 });
685 }
686 heads.push(bounds);
687 }
688 let values = parents_of
689 .iter()
690 .map(|parent| if *parent == NO_PARENT { absent } else { *parent })
691 .collect::<Vec<u64>>();
692 let mut bytes = Vec::with_capacity(bitpack::tail_len(values.len(), width));
693 bitpack::pack_linear(&values, width, &mut bytes)?;
694 Ok(Body::Packed { bytes: bytes.into(), width, heads })
695}
696
697fn width_for(parents: u64) -> usize {
703 (u64::BITS - parents.leading_zeros()).max(1) as usize
704}
705
706fn reserved(width: usize) -> u64 {
708 if width >= 64 { u64::MAX } else { (1_u64 << width) - 1 }
709}
710
711fn walk_ones(words: &[u64], at: usize, mut parent: Rid, skip: Rid) -> Option<(usize, Rid)> {
719 if skip == 0 {
720 return Some((at, parent));
721 }
722 let mut left = skip - 1;
723 let mut from = at + 1;
724 loop {
725 let index = from / 64;
726 let offset = from % 64;
727 let word = *words.get(index)? >> offset;
728 let span = 64 - offset;
729 let ones = u64::from(word.count_ones());
730 if ones > left {
731 #[expect(clippy::cast_possible_truncation, reason = "under the ones in one word")]
732 let within = crate::bits::nth_set(word, left as u32) as usize;
733 parent += count(within) - left;
735 return Some((from + within, parent));
736 }
737 parent += count(span) - ones;
740 left -= ones;
741 from += span;
742 }
743}
744
745fn count(rows: usize) -> u64 {
746 u64::try_from(rows).unwrap_or(u64::MAX)
747}
748
749fn number(bytes: &[u8]) -> Result<u64> {
750 Ok(u64::from_le_bytes(
751 bytes.try_into().map_err(|_| malformed("a forward link header is torn"))?,
752 ))
753}
754
755fn malformed(message: impl Into<String>) -> Error {
756 Error::invalid_input(format!("invalid rudb forward link: {}", message.into()))
757}
758
759#[cfg(test)]
760mod tests {
761 use super::*;
762
763 #[test]
764 fn a_cursor_finds_the_same_children_as_a_select() {
765 let mut parents_of = Vec::new();
767 for parent in 0..3_000_u64 {
768 for _ in 0..(parent * 7 + parent / 13) % 10 {
769 parents_of.push(parent);
770 }
771 }
772 let link = Link::build(&parents_of, 3_000).expect("build");
773 assert_eq!(link.form(), Form::Monotone);
774 let mut asked: Vec<Rid> = (0..3_000).step_by(3).collect();
775 asked.extend([2_000, 2_000, 2_001, 2_002, 5, 6, 2_900, 2_999, 0, 1_000, 1_064, 1_129]);
777 let mut cursor = Cursor::default();
778 for parent in asked {
779 assert_eq!(link.backward_from(parent, &mut cursor), link.backward(parent), "{parent}");
780 }
781 assert_eq!(link.backward_from(3_000, &mut cursor), None);
782 }
783
784 fn resolves(parents_of: &[Rid], parents: u64) -> Link {
787 let built = Link::build(parents_of, parents).expect("build");
788 let mut bytes = Vec::new();
789 built.write(&mut bytes).expect("write");
790 let read = Link::read(&bytes).expect("read");
791 let counts = Link::counts(&bytes[..HEADER_BYTES]).expect("the header alone");
792 assert_eq!(
793 counts,
794 Counts {
795 children: built.children(),
796 parents: built.parents(),
797 linked: built.linked(),
798 form: built.form()
799 }
800 );
801 assert_eq!(read.form(), built.form(), "the form survives the round trip");
802 assert_eq!(read.children(), built.children());
803 assert_eq!(read.parents(), built.parents());
804 assert_eq!(read.linked(), built.linked());
805 for link in [&built, &read] {
806 for (child, parent) in parents_of.iter().enumerate() {
807 let want = (*parent != NO_PARENT).then_some(*parent);
808 assert_eq!(link.forward(child as Rid), want, "child {child}");
809 }
810 assert_eq!(link.forward(parents_of.len() as Rid), None, "past the last child");
811 }
812 built
813 }
814
815 #[test]
816 fn a_clustered_child_takes_the_monotone_form_and_answers_both_directions() {
817 let link = resolves(&[0, 0, 0, 2, 2], 3);
820 assert_eq!(link.form(), Form::Monotone);
821 assert_eq!(link.backward(0), Some(0..3));
822 assert_eq!(link.backward(1), Some(3..3), "a parent with no children, not a missing parent");
823 assert_eq!(link.backward(2), Some(3..5));
824 assert_eq!(link.backward(3), None, "past the last parent");
825 }
826
827 #[test]
828 fn an_unclustered_child_takes_the_packed_form_and_answers_one_direction() {
829 let link = resolves(&[4, 1, 4, 0, 2], 5);
830 assert_eq!(link.form(), Form::Packed);
831 assert_eq!(link.backward(0), None, "the packed form does not answer backward");
832 }
833
834 #[test]
835 fn a_child_with_no_parent_keeps_the_link_out_of_the_monotone_form() {
836 let link = resolves(&[0, 1, NO_PARENT, 2], 3);
840 assert_eq!(link.form(), Form::Packed);
841 assert_eq!(link.linked(), 3, "the orphan is not linked and the other three are");
842 }
843
844 #[test]
845 fn every_child_pointing_at_one_parent_is_one_run() {
846 let link = resolves(&[7; 50], 8);
847 assert_eq!(link.form(), Form::Monotone);
848 assert_eq!(link.backward(6), Some(0..0));
849 assert_eq!(link.backward(7), Some(0..50));
850 }
851
852 #[test]
853 fn a_link_with_no_children_builds_and_resolves_nothing() {
854 let link = resolves(&[], 10);
855 assert_eq!(link.children(), 0);
856 assert_eq!(link.forward(0), None);
857 assert_eq!(link.part_bounds(0), None, "there is no part zero of an empty table");
858 }
859
860 #[test]
861 fn a_link_whose_parent_table_is_empty_is_packed_and_matches_nothing() {
862 let link = resolves(&[NO_PARENT, NO_PARENT], 0);
866 assert_eq!(link.form(), Form::Packed);
867 assert_eq!(link.linked(), 0);
868 }
869
870 #[test]
871 fn a_parent_rid_past_the_parent_table_is_refused_rather_than_stored() {
872 let error = Link::build(&[0, 9], 5).expect_err("refused");
875 assert!(error.to_string().contains("parent 9"), "{error}");
876 }
877
878 #[test]
879 fn the_reserved_value_is_not_a_parent_rid_even_at_the_width_boundary() {
880 assert_eq!(width_for(3), 2);
883 assert_eq!(width_for(4), 3);
884 assert_eq!(reserved(2), 3);
885 let link = resolves(&[2, 0, NO_PARENT], 3);
886 assert_eq!(link.form(), Form::Packed);
887 }
888
889 #[test]
890 fn a_packed_link_carries_the_bounds_of_every_part() {
891 let mut parents_of = vec![0_u64; PART_ROWS * 2 + 5];
892 for (child, parent) in parents_of.iter_mut().enumerate() {
893 *parent = (PART_ROWS - child % PART_ROWS) as u64;
896 }
897 let link = Link::build(&parents_of, PART_ROWS as u64 + 1).expect("build");
898 assert_eq!(link.form(), Form::Packed);
899 assert_eq!(link.part_bounds(0), Some(Some((1, PART_ROWS as u64))));
900 assert_eq!(link.part_bounds(2), Some(Some((PART_ROWS as u64 - 4, PART_ROWS as u64))));
901 assert_eq!(link.part_bounds(3), None, "there is no fourth part");
902 }
903
904 #[test]
905 fn a_part_in_which_nothing_matched_is_reported_as_skippable() {
906 let mut parents_of = vec![NO_PARENT; PART_ROWS * 2];
907 parents_of[PART_ROWS] = 3;
908 let link = Link::build(&parents_of, 10).expect("build");
909 assert_eq!(link.part_bounds(0), Some(None), "a part a reduction can skip outright");
910 assert_eq!(link.part_bounds(1), Some(Some((3, 3))));
911 }
912
913 #[test]
914 fn a_monotone_link_derives_its_part_bounds_from_its_ends() {
915 let parents_of = (0..PART_ROWS as u64 * 2).map(|child| child / 4).collect::<Vec<Rid>>();
916 let link = Link::build(&parents_of, PART_ROWS as u64).expect("build");
917 assert_eq!(link.form(), Form::Monotone);
918 assert_eq!(link.part_bounds(0), Some(Some((0, (PART_ROWS as u64 - 1) / 4))));
919 assert_eq!(
920 link.part_bounds(1),
921 Some(Some((PART_ROWS as u64 / 4, (PART_ROWS as u64 * 2 - 1) / 4)))
922 );
923 }
924
925 #[test]
926 fn the_monotone_form_is_a_bit_per_child_and_the_packed_form_is_a_rid_per_child() {
927 let ordered = (0..10_000_u64).map(|child| child / 10).collect::<Vec<Rid>>();
930 let monotone = Link::build(&ordered, 1000).expect("build");
931 assert_eq!(monotone.form(), Form::Monotone);
932 let mut shuffled = ordered.clone();
933 shuffled.swap(0, 9999);
934 let packed = Link::build(&shuffled, 1000).expect("build");
935 assert_eq!(packed.form(), Form::Packed);
936 assert!(
937 monotone.bytes() * 4 < packed.bytes(),
938 "monotone {} is not far below packed {}",
939 monotone.bytes(),
940 packed.bytes()
941 );
942 }
943
944 #[test]
945 fn a_payload_shorter_than_its_header_is_refused() {
946 let link = Link::build(&[0, 1], 2).expect("build");
947 let mut bytes = Vec::new();
948 link.write(&mut bytes).expect("write");
949 for cut in [0, 1, HEADER_BYTES - 1] {
950 assert!(Link::read(&bytes[..cut]).is_err(), "a payload of {cut} bytes is refused");
951 assert!(Link::counts(&bytes[..cut]).is_err(), "a header of {cut} bytes is refused");
952 }
953 }
954
955 #[test]
956 fn a_form_or_a_layout_this_build_does_not_know_is_refused() {
957 let link = Link::build(&[0, 1], 2).expect("build");
958 let mut bytes = Vec::new();
959 link.write(&mut bytes).expect("write");
960 let mut wrong = bytes.clone();
961 wrong[24] = 9;
962 assert!(Link::read(&wrong).is_err(), "an unknown form is refused");
963 assert!(Link::counts(&wrong).is_err(), "and its counts are not read");
964 let mut wrong = bytes;
965 wrong[26] = LAYOUT + 1;
966 assert!(Link::read(&wrong).is_err(), "an unknown layout is refused");
967 }
968
969 #[test]
970 fn a_width_that_does_not_match_the_parents_is_refused_rather_than_read_at() {
971 let link = Link::build(&[1, 0], 2).expect("build");
974 let mut bytes = Vec::new();
975 link.write(&mut bytes).expect("write");
976 bytes[25] = 7;
977 let error = Link::read(&bytes).expect_err("refused");
978 assert!(error.to_string().contains("width"), "{error}");
979 }
980
981 #[test]
982 fn a_truncated_body_is_refused_for_either_form() {
983 for parents_of in [vec![0_u64, 0, 1, 2], vec![2_u64, 0, 1, 0]] {
984 let link = Link::build(&parents_of, 3).expect("build");
985 let mut bytes = Vec::new();
986 link.write(&mut bytes).expect("write");
987 let short = &bytes[..bytes.len() - 1];
988 assert!(Link::read(short).is_err(), "a truncated {:?} body is refused", link.form());
989 }
990 }
991
992 #[test]
995 fn a_run_agrees_with_the_per_child_lookup_in_both_forms() {
996 let mut clustered = Vec::new();
997 for parent in 0..400_u64 {
998 let children = if parent % 50 < 45 { 0 } else { parent % 7 + 1 };
1001 clustered.extend(std::iter::repeat_n(parent, children as usize));
1002 }
1003 let scattered: Vec<Rid> = (0..3000_u64)
1004 .map(|child| if child % 13 == 0 { NO_PARENT } else { (child * 37) % 500 })
1005 .collect();
1006 for (parents_of, parents) in [(clustered, 400), (scattered, 500)] {
1007 let link = Link::build(&parents_of, parents).expect("build");
1008 let children = link.children();
1009 for first in [0, 1, 63, 64, 65, children / 2, children - 1] {
1010 for len in [0, 1, 2, 100, children - first] {
1011 let len = len.min(children - first);
1012 let mut out = vec![0; len as usize];
1013 link.forward_run(first, &mut out).expect("in range");
1014 let want: Vec<Rid> = (first..first + len)
1015 .map(|child| link.forward(child).unwrap_or(NO_PARENT))
1016 .collect();
1017 assert_eq!(out, want, "{:?} from {first} for {len}", link.form());
1018 }
1019 }
1020 let mut out = vec![0; 2];
1021 assert!(link.forward_run(children - 1, &mut out).is_err(), "past the last child");
1022 }
1023 }
1024
1025 #[test]
1028 fn a_list_agrees_with_the_per_child_lookup_in_both_forms() {
1029 let mut clustered = Vec::new();
1030 for parent in 0..3000_u64 {
1031 let children = if parent % 50 < 45 { parent % 3 } else { parent % 7 + 1 };
1032 clustered.extend(std::iter::repeat_n(parent, children as usize));
1033 }
1034 let scattered: Vec<Rid> = (0..3000_u64)
1035 .map(|child| if child % 13 == 0 { NO_PARENT } else { (child * 37) % 500 })
1036 .collect();
1037 for (parents_of, parents) in [(clustered, 3000), (scattered, 500)] {
1038 let link = Link::build(&parents_of, parents).expect("build");
1039 let children = link.children();
1040 let mut lists: Vec<Vec<Rid>> = vec![
1041 Vec::new(),
1042 (0..children).collect(),
1043 (0..children).step_by(7).collect(),
1044 (0..children).step_by(1500).collect(),
1045 (0..children).rev().step_by(11).collect(),
1046 vec![5, 5, 4, children - 1, children, children + 9, 0, 63, 64, 65],
1047 ];
1048 let mut state = 7_u64;
1049 let mut sparse = Vec::new();
1050 for child in 0..children {
1051 state = state.wrapping_mul(6_364_136_223_846_793_005).wrapping_add(1);
1052 if state >> 60 == 0 {
1053 sparse.push(child);
1054 }
1055 }
1056 lists.push(sparse);
1057 let mut out = Vec::new();
1058 for list in &lists {
1059 link.forward_each(list, &mut out);
1060 let want: Vec<Rid> =
1061 list.iter().map(|&child| link.forward(child).unwrap_or(NO_PARENT)).collect();
1062 assert_eq!(out, want, "{:?} over {} children", link.form(), list.len());
1063 }
1064 }
1065 }
1066}