1use rudb_common::{Error, Result};
51use rudb_encoding::bitpack;
52
53use crate::bits::BitVector;
54use crate::rid::{NO_PARENT, PART_ROWS, Rid};
55
56const LAYOUT: u8 = 1;
58
59pub const HEADER_BYTES: usize = 32;
63
64#[derive(Debug, Clone, Copy, PartialEq, Eq)]
66pub enum Form {
67 Packed,
69 Monotone,
71}
72
73impl Form {
74 #[must_use]
76 pub fn tag(self) -> u8 {
77 match self {
78 Self::Packed => 0,
79 Self::Monotone => 1,
80 }
81 }
82
83 #[must_use]
85 pub fn label(self) -> &'static str {
86 match self {
87 Self::Packed => "packed",
88 Self::Monotone => "monotone",
89 }
90 }
91
92 pub fn from_tag(tag: u8) -> Result<Self> {
98 match tag {
99 0 => Ok(Self::Packed),
100 1 => Ok(Self::Monotone),
101 _ => Err(malformed(format!("forward link form {tag} is not one this build knows"))),
102 }
103 }
104}
105
106pub type Bounds = Option<(Rid, Rid)>;
111
112#[derive(Debug, Clone)]
113enum Body {
114 Packed {
115 bytes: Vec<u8>,
117 width: usize,
118 heads: Vec<Bounds>,
120 },
121 Monotone {
122 vector: BitVector,
123 },
124}
125
126#[derive(Debug, Clone)]
128pub struct Link {
129 children: u64,
130 parents: u64,
131 linked: u64,
132 body: Body,
133}
134
135impl Link {
136 pub fn build(parents_of: &[Rid], parents: u64) -> Result<Self> {
150 let children = count(parents_of.len());
151 let mut linked = 0_u64;
152 let mut monotone = true;
153 let mut previous = 0_u64;
154 for parent in parents_of {
155 if *parent == NO_PARENT {
156 monotone = false;
157 continue;
158 }
159 if *parent >= parents {
160 return Err(malformed(format!(
161 "a forward link points at parent {parent} of a table with {parents} rows"
162 )));
163 }
164 if *parent < previous {
165 monotone = false;
166 }
167 previous = *parent;
168 linked += 1;
169 }
170 let body = if monotone && parents > 0 {
171 Body::Monotone { vector: runs(parents_of, parents)? }
172 } else {
173 packed(parents_of, parents)?
174 };
175 Ok(Self { children, parents, linked, body })
176 }
177
178 #[must_use]
180 pub fn form(&self) -> Form {
181 match self.body {
182 Body::Packed { .. } => Form::Packed,
183 Body::Monotone { .. } => Form::Monotone,
184 }
185 }
186
187 #[must_use]
189 pub fn children(&self) -> u64 {
190 self.children
191 }
192
193 #[must_use]
195 pub fn parents(&self) -> u64 {
196 self.parents
197 }
198
199 #[must_use]
201 pub fn linked(&self) -> u64 {
202 self.linked
203 }
204
205 #[must_use]
207 pub fn bytes(&self) -> usize {
208 match &self.body {
209 Body::Packed { bytes, heads, .. } => bytes.len() + heads.len() * 16,
210 Body::Monotone { vector } => vector.bytes(),
211 }
212 }
213
214 #[must_use]
216 pub fn forward(&self, child: Rid) -> Option<Rid> {
217 if child >= self.children {
218 return None;
219 }
220 match &self.body {
221 Body::Packed { bytes, width, .. } => {
222 let value = bitpack::tail_at(bytes, *width, usize::try_from(child).ok()?).ok()?;
227 (value != reserved(*width)).then_some(value)
228 }
229 Body::Monotone { vector } => {
230 let at = vector.select1(child)?;
231 Some(vector.rank0(at))
232 }
233 }
234 }
235
236 pub fn forward_run(&self, first: Rid, out: &mut [Rid]) -> Result<()> {
248 let end = first.checked_add(count(out.len()));
249 if end.is_none_or(|end| end > self.children) {
250 return Err(Error::internal(format!(
251 "a run of {} children from {first} goes past the {} the link has",
252 out.len(),
253 self.children
254 )));
255 }
256 if out.is_empty() {
257 return Ok(());
258 }
259 match &self.body {
260 Body::Packed { bytes, width, .. } => {
261 let absent = reserved(*width);
262 let start = usize::try_from(first)
263 .map_err(|_| malformed("a child past what fits in memory"))?;
264 for (at, slot) in out.iter_mut().enumerate() {
265 let value = bitpack::tail_at(bytes, *width, start + at)?;
266 *slot = if value == absent { NO_PARENT } else { value };
267 }
268 }
269 Body::Monotone { vector } => {
270 let Some(mut at) = vector.select1(first) else {
273 return Err(malformed("a monotone link has fewer ones than children"));
274 };
275 let mut parent = vector.rank0(at);
276 let words = vector.words();
277 for slot in out.iter_mut() {
278 loop {
281 let word = words.get(at / 64).copied().unwrap_or(0) >> (at % 64);
282 if word == 0 {
283 let skipped = 64 - at % 64;
284 parent += count(skipped);
285 at += skipped;
286 if at >= vector.len() {
287 return Err(malformed("a monotone link ran out of ones"));
288 }
289 continue;
290 }
291 let zeros = word.trailing_zeros() as usize;
292 parent += count(zeros);
293 at += zeros;
294 break;
295 }
296 *slot = parent;
297 at += 1;
298 }
299 }
300 }
301 Ok(())
302 }
303
304 #[must_use]
309 pub fn backward(&self, parent: Rid) -> Option<std::ops::Range<Rid>> {
310 let Body::Monotone { vector } = &self.body else { return None };
311 if parent >= self.parents {
312 return None;
313 }
314 let cum = |nth: Rid| -> Option<u64> { vector.select0(nth).map(|at| count(at) - nth) };
317 let from = if parent == 0 { 0 } else { cum(parent - 1)? };
318 Some(from..cum(parent)?)
319 }
320
321 #[must_use]
326 pub fn part_bounds(&self, part: usize) -> Option<Bounds> {
327 let first = count(part * PART_ROWS);
328 if first >= self.children {
329 return None;
330 }
331 match &self.body {
332 Body::Packed { heads, .. } => heads.get(part).copied(),
333 Body::Monotone { .. } => {
336 let last = (first + count(PART_ROWS) - 1).min(self.children - 1);
337 match (self.forward(first), self.forward(last)) {
338 (Some(low), Some(high)) => Some(Some((low, high))),
339 _ => Some(None),
340 }
341 }
342 }
343 }
344
345 pub fn write(&self, out: &mut Vec<u8>) -> Result<()> {
351 let start = out.len();
352 out.extend_from_slice(&self.children.to_le_bytes());
353 out.extend_from_slice(&self.parents.to_le_bytes());
354 out.extend_from_slice(&self.linked.to_le_bytes());
355 out.push(self.form().tag());
356 out.push(match &self.body {
357 Body::Packed { width, .. } => u8::try_from(*width)
361 .map_err(|_| malformed("a forward link wider than a byte can name"))?,
362 Body::Monotone { .. } => 0,
363 });
364 out.push(LAYOUT);
365 out.extend_from_slice(&[0; 5]);
370 debug_assert_eq!(
371 out.len() - start,
372 HEADER_BYTES,
373 "the forward link header is thirty two bytes"
374 );
375 match &self.body {
376 Body::Packed { bytes, heads, .. } => {
377 for head in heads {
378 let (low, high) = head.unwrap_or((NO_PARENT, NO_PARENT));
379 out.extend_from_slice(&low.to_le_bytes());
380 out.extend_from_slice(&high.to_le_bytes());
381 }
382 out.extend_from_slice(bytes);
383 }
384 Body::Monotone { vector } => vector.write(out),
385 }
386 Ok(())
387 }
388
389 pub fn read(bytes: &[u8]) -> Result<Self> {
397 if bytes.len() < HEADER_BYTES {
398 return Err(malformed("a forward link payload is shorter than its header"));
399 }
400 let children = number(&bytes[0..8])?;
401 let parents = number(&bytes[8..16])?;
402 let linked = number(&bytes[16..24])?;
403 let form = Form::from_tag(bytes[24])?;
404 let width = bytes[25] as usize;
405 if bytes[26] != LAYOUT {
406 return Err(malformed(format!(
407 "forward link layout {} is not one this build knows",
408 bytes[26]
409 )));
410 }
411 let rest = &bytes[HEADER_BYTES..];
412 let body = match form {
413 Form::Packed => {
414 if width != width_for(parents) {
415 return Err(malformed(
416 "a forward link's width is not the one its parents imply",
417 ));
418 }
419 let parts = usize::try_from(children.div_ceil(count(PART_ROWS)))
420 .map_err(|_| malformed("a forward link with more parts than fit in memory"))?;
421 let head = parts * 16;
422 let rows = usize::try_from(children)
423 .map_err(|_| malformed("a forward link longer than fits in memory"))?;
424 let packed = bitpack::tail_len(rows, width);
425 if rest.len() != head + packed {
426 return Err(malformed(
427 "a forward link's body is not the size its header implies",
428 ));
429 }
430 let mut heads = Vec::with_capacity(parts);
431 for part in 0..parts {
432 let low = number(&rest[part * 16..part * 16 + 8])?;
433 let high = number(&rest[part * 16 + 8..part * 16 + 16])?;
434 heads.push((low != NO_PARENT).then_some((low, high)));
435 }
436 Body::Packed { bytes: rest[head..].to_vec(), width, heads }
437 }
438 Form::Monotone => {
439 let len = usize::try_from(linked + parents)
440 .map_err(|_| malformed("a forward link longer than fits in memory"))?;
441 Body::Monotone { vector: BitVector::read(rest, len)? }
442 }
443 };
444 Ok(Self { children, parents, linked, body })
445 }
446}
447
448fn runs(parents_of: &[Rid], parents: u64) -> Result<BitVector> {
450 let len = usize::try_from(count(parents_of.len()) + parents)
451 .map_err(|_| malformed("a forward link longer than fits in memory"))?;
452 let mut words = vec![0_u64; len.div_ceil(64)];
453 let mut at = 0_usize;
454 let mut child = 0_usize;
455 for parent in 0..parents {
456 while child < parents_of.len() && parents_of[child] == parent {
457 words[at / 64] |= 1 << (at % 64);
458 at += 1;
459 child += 1;
460 }
461 at += 1;
462 }
463 debug_assert_eq!(at, len, "every child is a one and every parent is a zero");
464 BitVector::new(words, len)
465}
466
467fn packed(parents_of: &[Rid], parents: u64) -> Result<Body> {
475 let width = width_for(parents);
476 let absent = reserved(width);
477 let mut heads = Vec::with_capacity(parents_of.len().div_ceil(PART_ROWS));
478 for rows in parents_of.chunks(PART_ROWS) {
479 let mut bounds: Bounds = None;
480 for parent in rows {
481 if *parent == NO_PARENT {
482 continue;
483 }
484 bounds = Some(match bounds {
485 None => (*parent, *parent),
486 Some((low, high)) => (low.min(*parent), high.max(*parent)),
487 });
488 }
489 heads.push(bounds);
490 }
491 let values = parents_of
492 .iter()
493 .map(|parent| if *parent == NO_PARENT { absent } else { *parent })
494 .collect::<Vec<u64>>();
495 let mut bytes = Vec::with_capacity(bitpack::tail_len(values.len(), width));
496 bitpack::pack_linear(&values, width, &mut bytes)?;
497 Ok(Body::Packed { bytes, width, heads })
498}
499
500fn width_for(parents: u64) -> usize {
506 (u64::BITS - parents.leading_zeros()).max(1) as usize
507}
508
509fn reserved(width: usize) -> u64 {
511 if width >= 64 { u64::MAX } else { (1_u64 << width) - 1 }
512}
513
514fn count(rows: usize) -> u64 {
516 u64::try_from(rows).unwrap_or(u64::MAX)
517}
518
519fn number(bytes: &[u8]) -> Result<u64> {
520 Ok(u64::from_le_bytes(
521 bytes.try_into().map_err(|_| malformed("a forward link header is torn"))?,
522 ))
523}
524
525fn malformed(message: impl Into<String>) -> Error {
526 Error::invalid_input(format!("invalid rudb forward link: {}", message.into()))
527}
528
529#[cfg(test)]
530mod tests {
531 use super::*;
532
533 fn resolves(parents_of: &[Rid], parents: u64) -> Link {
536 let built = Link::build(parents_of, parents).expect("build");
537 let mut bytes = Vec::new();
538 built.write(&mut bytes).expect("write");
539 let read = Link::read(&bytes).expect("read");
540 assert_eq!(read.form(), built.form(), "the form survives the round trip");
541 assert_eq!(read.children(), built.children());
542 assert_eq!(read.parents(), built.parents());
543 assert_eq!(read.linked(), built.linked());
544 for link in [&built, &read] {
545 for (child, parent) in parents_of.iter().enumerate() {
546 let want = (*parent != NO_PARENT).then_some(*parent);
547 assert_eq!(link.forward(child as Rid), want, "child {child}");
548 }
549 assert_eq!(link.forward(parents_of.len() as Rid), None, "past the last child");
550 }
551 built
552 }
553
554 #[test]
555 fn a_clustered_child_takes_the_monotone_form_and_answers_both_directions() {
556 let link = resolves(&[0, 0, 0, 2, 2], 3);
559 assert_eq!(link.form(), Form::Monotone);
560 assert_eq!(link.backward(0), Some(0..3));
561 assert_eq!(link.backward(1), Some(3..3), "a parent with no children, not a missing parent");
562 assert_eq!(link.backward(2), Some(3..5));
563 assert_eq!(link.backward(3), None, "past the last parent");
564 }
565
566 #[test]
567 fn an_unclustered_child_takes_the_packed_form_and_answers_one_direction() {
568 let link = resolves(&[4, 1, 4, 0, 2], 5);
569 assert_eq!(link.form(), Form::Packed);
570 assert_eq!(link.backward(0), None, "the packed form does not answer backward");
571 }
572
573 #[test]
574 fn a_child_with_no_parent_keeps_the_link_out_of_the_monotone_form() {
575 let link = resolves(&[0, 1, NO_PARENT, 2], 3);
579 assert_eq!(link.form(), Form::Packed);
580 assert_eq!(link.linked(), 3, "the orphan is not linked and the other three are");
581 }
582
583 #[test]
584 fn every_child_pointing_at_one_parent_is_one_run() {
585 let link = resolves(&[7; 50], 8);
586 assert_eq!(link.form(), Form::Monotone);
587 assert_eq!(link.backward(6), Some(0..0));
588 assert_eq!(link.backward(7), Some(0..50));
589 }
590
591 #[test]
592 fn a_link_with_no_children_builds_and_resolves_nothing() {
593 let link = resolves(&[], 10);
594 assert_eq!(link.children(), 0);
595 assert_eq!(link.forward(0), None);
596 assert_eq!(link.part_bounds(0), None, "there is no part zero of an empty table");
597 }
598
599 #[test]
600 fn a_link_whose_parent_table_is_empty_is_packed_and_matches_nothing() {
601 let link = resolves(&[NO_PARENT, NO_PARENT], 0);
605 assert_eq!(link.form(), Form::Packed);
606 assert_eq!(link.linked(), 0);
607 }
608
609 #[test]
610 fn a_parent_rid_past_the_parent_table_is_refused_rather_than_stored() {
611 let error = Link::build(&[0, 9], 5).expect_err("refused");
614 assert!(error.to_string().contains("parent 9"), "{error}");
615 }
616
617 #[test]
618 fn the_reserved_value_is_not_a_parent_rid_even_at_the_width_boundary() {
619 assert_eq!(width_for(3), 2);
622 assert_eq!(width_for(4), 3);
623 assert_eq!(reserved(2), 3);
624 let link = resolves(&[2, 0, NO_PARENT], 3);
625 assert_eq!(link.form(), Form::Packed);
626 }
627
628 #[test]
629 fn a_packed_link_carries_the_bounds_of_every_part() {
630 let mut parents_of = vec![0_u64; PART_ROWS * 2 + 5];
631 for (child, parent) in parents_of.iter_mut().enumerate() {
632 *parent = (PART_ROWS - child % PART_ROWS) as u64;
635 }
636 let link = Link::build(&parents_of, PART_ROWS as u64 + 1).expect("build");
637 assert_eq!(link.form(), Form::Packed);
638 assert_eq!(link.part_bounds(0), Some(Some((1, PART_ROWS as u64))));
639 assert_eq!(link.part_bounds(2), Some(Some((PART_ROWS as u64 - 4, PART_ROWS as u64))));
640 assert_eq!(link.part_bounds(3), None, "there is no fourth part");
641 }
642
643 #[test]
644 fn a_part_in_which_nothing_matched_is_reported_as_skippable() {
645 let mut parents_of = vec![NO_PARENT; PART_ROWS * 2];
646 parents_of[PART_ROWS] = 3;
647 let link = Link::build(&parents_of, 10).expect("build");
648 assert_eq!(link.part_bounds(0), Some(None), "a part a reduction can skip outright");
649 assert_eq!(link.part_bounds(1), Some(Some((3, 3))));
650 }
651
652 #[test]
653 fn a_monotone_link_derives_its_part_bounds_from_its_ends() {
654 let parents_of = (0..PART_ROWS as u64 * 2).map(|child| child / 4).collect::<Vec<Rid>>();
655 let link = Link::build(&parents_of, PART_ROWS as u64).expect("build");
656 assert_eq!(link.form(), Form::Monotone);
657 assert_eq!(link.part_bounds(0), Some(Some((0, (PART_ROWS as u64 - 1) / 4))));
658 assert_eq!(
659 link.part_bounds(1),
660 Some(Some((PART_ROWS as u64 / 4, (PART_ROWS as u64 * 2 - 1) / 4)))
661 );
662 }
663
664 #[test]
665 fn the_monotone_form_is_a_bit_per_child_and_the_packed_form_is_a_rid_per_child() {
666 let ordered = (0..10_000_u64).map(|child| child / 10).collect::<Vec<Rid>>();
669 let monotone = Link::build(&ordered, 1000).expect("build");
670 assert_eq!(monotone.form(), Form::Monotone);
671 let mut shuffled = ordered.clone();
672 shuffled.swap(0, 9999);
673 let packed = Link::build(&shuffled, 1000).expect("build");
674 assert_eq!(packed.form(), Form::Packed);
675 assert!(
676 monotone.bytes() * 4 < packed.bytes(),
677 "monotone {} is not far below packed {}",
678 monotone.bytes(),
679 packed.bytes()
680 );
681 }
682
683 #[test]
684 fn a_payload_shorter_than_its_header_is_refused() {
685 let link = Link::build(&[0, 1], 2).expect("build");
686 let mut bytes = Vec::new();
687 link.write(&mut bytes).expect("write");
688 for cut in [0, 1, HEADER_BYTES - 1] {
689 assert!(Link::read(&bytes[..cut]).is_err(), "a payload of {cut} bytes is refused");
690 }
691 }
692
693 #[test]
694 fn a_form_or_a_layout_this_build_does_not_know_is_refused() {
695 let link = Link::build(&[0, 1], 2).expect("build");
696 let mut bytes = Vec::new();
697 link.write(&mut bytes).expect("write");
698 let mut wrong = bytes.clone();
699 wrong[24] = 9;
700 assert!(Link::read(&wrong).is_err(), "an unknown form is refused");
701 let mut wrong = bytes;
702 wrong[26] = LAYOUT + 1;
703 assert!(Link::read(&wrong).is_err(), "an unknown layout is refused");
704 }
705
706 #[test]
707 fn a_width_that_does_not_match_the_parents_is_refused_rather_than_read_at() {
708 let link = Link::build(&[1, 0], 2).expect("build");
711 let mut bytes = Vec::new();
712 link.write(&mut bytes).expect("write");
713 bytes[25] = 7;
714 let error = Link::read(&bytes).expect_err("refused");
715 assert!(error.to_string().contains("width"), "{error}");
716 }
717
718 #[test]
719 fn a_truncated_body_is_refused_for_either_form() {
720 for parents_of in [vec![0_u64, 0, 1, 2], vec![2_u64, 0, 1, 0]] {
721 let link = Link::build(&parents_of, 3).expect("build");
722 let mut bytes = Vec::new();
723 link.write(&mut bytes).expect("write");
724 let short = &bytes[..bytes.len() - 1];
725 assert!(Link::read(short).is_err(), "a truncated {:?} body is refused", link.form());
726 }
727 }
728
729 #[test]
732 fn a_run_agrees_with_the_per_child_lookup_in_both_forms() {
733 let mut clustered = Vec::new();
734 for parent in 0..400_u64 {
735 let children = if parent % 50 < 45 { 0 } else { parent % 7 + 1 };
738 clustered.extend(std::iter::repeat_n(parent, children as usize));
739 }
740 let scattered: Vec<Rid> = (0..3000_u64)
741 .map(|child| if child % 13 == 0 { NO_PARENT } else { (child * 37) % 500 })
742 .collect();
743 for (parents_of, parents) in [(clustered, 400), (scattered, 500)] {
744 let link = Link::build(&parents_of, parents).expect("build");
745 let children = link.children();
746 for first in [0, 1, 63, 64, 65, children / 2, children - 1] {
747 for len in [0, 1, 2, 100, children - first] {
748 let len = len.min(children - first);
749 let mut out = vec![0; len as usize];
750 link.forward_run(first, &mut out).expect("in range");
751 let want: Vec<Rid> = (first..first + len)
752 .map(|child| link.forward(child).unwrap_or(NO_PARENT))
753 .collect();
754 assert_eq!(out, want, "{:?} from {first} for {len}", link.form());
755 }
756 }
757 let mut out = vec![0; 2];
758 assert!(link.forward_run(children - 1, &mut out).is_err(), "past the last child");
759 }
760 }
761}