1use crate::varint::{read_uvarint, write_uvarint};
10
11pub type Triple = (u32, u32, u32);
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub struct ZoneMap {
17 pub min_a: u32,
18 pub max_a: u32,
19 pub min_b: u32,
20 pub max_b: u32,
21 pub min_c: u32,
22 pub max_c: u32,
23 pub count: u32,
24}
25
26impl ZoneMap {
27 pub fn may_contain(&self, a: Option<u32>, b: Option<u32>, c: Option<u32>) -> bool {
30 let in_range = |v: Option<u32>, lo: u32, hi: u32| v.is_none_or(|x| lo <= x && x <= hi);
31 in_range(a, self.min_a, self.max_a)
32 && in_range(b, self.min_b, self.max_b)
33 && in_range(c, self.min_c, self.max_c)
34 }
35}
36
37#[derive(Debug, thiserror::Error)]
38pub enum TripleError {
39 #[error("malformed triple block: {0}")]
40 Malformed(&'static str),
41}
42
43#[derive(Default)]
45pub struct TripleBlockBuilder {
46 triples: Vec<Triple>,
47}
48
49impl TripleBlockBuilder {
50 pub fn new() -> Self {
51 Self::default()
52 }
53
54 pub fn push(&mut self, t: Triple) {
55 self.triples.push(t);
56 }
57
58 pub fn len(&self) -> usize {
59 self.triples.len()
60 }
61
62 pub fn is_empty(&self) -> bool {
63 self.triples.is_empty()
64 }
65
66 pub fn build(mut self) -> Vec<u8> {
68 self.triples.sort_unstable();
69 self.triples.dedup();
70 let t = &self.triples;
71
72 let mut out = Vec::new();
73 if t.is_empty() {
74 for _ in 0..7 {
76 write_uvarint(&mut out, 0);
77 }
78 write_uvarint(&mut out, 0); return out;
80 }
81
82 let (mut min_a, mut max_a) = (u32::MAX, 0u32);
84 let (mut min_b, mut max_b) = (u32::MAX, 0u32);
85 let (mut min_c, mut max_c) = (u32::MAX, 0u32);
86 for &(a, b, c) in t {
87 min_a = min_a.min(a);
88 max_a = max_a.max(a);
89 min_b = min_b.min(b);
90 max_b = max_b.max(b);
91 min_c = min_c.min(c);
92 max_c = max_c.max(c);
93 }
94 for v in [min_a, max_a, min_b, max_b, min_c, max_c, t.len() as u32] {
95 write_uvarint(&mut out, v as u64);
96 }
97
98 type BGroup = (u32, Vec<u32>);
102 type AGroup = (u32, Vec<BGroup>);
103 let mut i = 0;
104 let mut a_groups: Vec<AGroup> = Vec::new();
105 while i < t.len() {
106 let a = t[i].0;
107 let mut b_groups: Vec<(u32, Vec<u32>)> = Vec::new();
108 while i < t.len() && t[i].0 == a {
109 let b = t[i].1;
110 let mut cs = Vec::new();
111 while i < t.len() && t[i].0 == a && t[i].1 == b {
112 cs.push(t[i].2);
113 i += 1;
114 }
115 b_groups.push((b, cs));
116 }
117 a_groups.push((a, b_groups));
118 }
119
120 write_uvarint(&mut out, a_groups.len() as u64);
121 let mut prev_a = 0u32;
122 for (a, b_groups) in &a_groups {
123 write_uvarint(&mut out, (a - prev_a) as u64);
124 prev_a = *a;
125 write_uvarint(&mut out, b_groups.len() as u64);
126 let mut prev_b = 0u32;
127 for (b, cs) in b_groups {
128 write_uvarint(&mut out, (b - prev_b) as u64);
129 prev_b = *b;
130 write_uvarint(&mut out, cs.len() as u64);
131 let mut prev_c = 0u32;
132 for c in cs {
133 write_uvarint(&mut out, (c - prev_c) as u64);
134 prev_c = *c;
135 }
136 }
137 }
138 out
139 }
140}
141
142pub struct TripleBlock<'a> {
144 bytes: &'a [u8],
145 zone: ZoneMap,
146 body_start: usize,
147}
148
149impl<'a> TripleBlock<'a> {
150 pub fn parse(bytes: &'a [u8]) -> Result<Self, TripleError> {
151 let mut pos = 0;
152 let take = |pos: &mut usize| -> Result<u32, TripleError> {
153 let (v, n) = read_uvarint(&bytes[*pos..]).ok_or(TripleError::Malformed("truncated"))?;
154 *pos += n;
155 Ok(v as u32)
156 };
157 let zone = ZoneMap {
158 min_a: take(&mut pos)?,
159 max_a: take(&mut pos)?,
160 min_b: take(&mut pos)?,
161 max_b: take(&mut pos)?,
162 min_c: take(&mut pos)?,
163 max_c: take(&mut pos)?,
164 count: take(&mut pos)?,
165 };
166 Ok(Self {
167 bytes,
168 zone,
169 body_start: pos,
170 })
171 }
172
173 pub fn zone(&self) -> &ZoneMap {
174 &self.zone
175 }
176
177 pub fn triples(&self) -> Vec<Triple> {
182 self.try_triples().unwrap_or_default()
183 }
184
185 fn try_triples(&self) -> Option<Vec<Triple>> {
186 let mut out = Vec::with_capacity((self.zone.count as usize).min(self.bytes.len()));
189 let mut pos = self.body_start;
190 let g = |pos: &mut usize| -> Option<u32> {
191 let (v, n) = read_uvarint(self.bytes.get(*pos..)?)?;
192 *pos += n;
193 Some(v as u32)
194 };
195 let num_a = g(&mut pos)?;
196 let mut a = 0u32;
197 for _ in 0..num_a {
198 a = a.wrapping_add(g(&mut pos)?);
200 let num_b = g(&mut pos)?;
201 let mut b = 0u32;
202 for _ in 0..num_b {
203 b = b.wrapping_add(g(&mut pos)?);
204 let num_c = g(&mut pos)?;
205 let mut c = 0u32;
206 for _ in 0..num_c {
207 c = c.wrapping_add(g(&mut pos)?);
208 out.push((a, b, c));
209 }
210 }
211 }
212 Some(out)
213 }
214
215 pub fn group_directory(&self) -> GroupDirectory {
220 let bytes = self.bytes;
221 let mut entries = Vec::new();
222 let mut p = self.body_start;
223 let mut walk = || -> Option<()> {
224 let num_a = rd(bytes, &mut p)?;
225 entries.reserve((num_a as usize).min(bytes.len()));
228 let mut a = 0u32;
229 for i in 0..num_a {
230 a = a.wrapping_add(rd(bytes, &mut p)?);
231 let num_b = rd(bytes, &mut p)?;
232 entries.push(DirEntry {
233 a,
234 pos: p,
235 num_b,
236 a_rem_after: num_a - 1 - i,
237 });
238 for _ in 0..num_b {
239 rd(bytes, &mut p)?; let nc = rd(bytes, &mut p)?;
241 for _ in 0..nc {
242 rd(bytes, &mut p)?;
243 }
244 }
245 }
246 Some(())
247 };
248 let _ = walk();
249 GroupDirectory { entries }
250 }
251
252 pub fn scan_from(
257 &self,
258 dir: &GroupDirectory,
259 pa: u32,
260 pb: Option<u32>,
261 pc: Option<u32>,
262 ) -> BlockCursor<'a> {
263 let mut cursor = BlockCursor {
264 bytes: self.bytes,
265 pos: self.body_start,
266 a: 0,
267 b: 0,
268 c: 0,
269 a_rem: 0,
270 b_rem: 0,
271 c_rem: 0,
272 started: true, pa: Some(pa),
274 pb,
275 pc,
276 };
277 if let Ok(i) = dir.entries.binary_search_by_key(&pa, |e| e.a) {
278 let e = &dir.entries[i];
279 cursor.pos = e.pos;
282 cursor.a = e.a;
283 cursor.a_rem = e.a_rem_after;
284 cursor.b_rem = e.num_b;
285 }
286 cursor
287 }
288
289 pub fn scan_resume(
302 &self,
303 dir: &GroupDirectory,
304 from_a: u32,
305 pb: Option<u32>,
306 pc: Option<u32>,
307 ) -> BlockCursor<'a> {
308 let mut cursor = BlockCursor {
309 bytes: self.bytes,
310 pos: self.body_start,
311 a: 0,
312 b: 0,
313 c: 0,
314 a_rem: 0,
315 b_rem: 0,
316 c_rem: 0,
317 started: true, pa: None,
319 pb,
320 pc,
321 };
322 let i = dir.entries.partition_point(|e| e.a < from_a);
326 if let Some(e) = dir.entries.get(i) {
327 cursor.pos = e.pos;
330 cursor.a = e.a;
331 cursor.a_rem = e.a_rem_after;
332 cursor.b_rem = e.num_b;
333 }
334 cursor
335 }
336
337 pub fn scan(&self, pa: Option<u32>, pb: Option<u32>, pc: Option<u32>) -> BlockCursor<'a> {
355 BlockCursor {
356 bytes: self.bytes,
357 pos: self.body_start,
358 a: 0,
359 b: 0,
360 c: 0,
361 a_rem: 0,
362 b_rem: 0,
363 c_rem: 0,
364 started: false,
365 pa,
366 pb,
367 pc,
368 }
369 }
370}
371
372#[inline]
375fn rd(bytes: &[u8], pos: &mut usize) -> Option<u32> {
376 let (v, n) = read_uvarint(bytes.get(*pos..)?)?;
377 *pos += n;
378 Some(v as u32)
379}
380
381pub struct GroupDirectory {
386 entries: Vec<DirEntry>,
387}
388
389impl GroupDirectory {
390 pub fn len(&self) -> usize {
392 self.entries.len()
393 }
394
395 pub fn is_empty(&self) -> bool {
396 self.entries.is_empty()
397 }
398
399 pub fn heap_bytes(&self) -> u64 {
401 (self.entries.capacity() * std::mem::size_of::<DirEntry>()) as u64
402 }
403
404 pub fn first_at_or_after(&self, a: u32) -> Option<u32> {
406 let i = self.entries.partition_point(|e| e.a < a);
407 self.entries.get(i).map(|e| e.a)
408 }
409
410 #[cfg(test)]
412 pub(crate) fn empty_for_tests() -> Self {
413 GroupDirectory {
414 entries: Vec::new(),
415 }
416 }
417}
418
419struct DirEntry {
422 a: u32,
423 pos: usize,
424 num_b: u32,
425 a_rem_after: u32,
426}
427
428pub struct BlockCursor<'a> {
432 bytes: &'a [u8],
433 pos: usize,
434 a: u32,
436 b: u32,
437 c: u32,
438 a_rem: u32,
440 b_rem: u32,
441 c_rem: u32,
442 started: bool,
443 pa: Option<u32>,
444 pb: Option<u32>,
445 pc: Option<u32>,
446}
447
448impl Iterator for BlockCursor<'_> {
449 type Item = Triple;
450
451 fn next(&mut self) -> Option<Triple> {
452 let bytes = self.bytes;
453 if !self.started {
454 self.a_rem = rd(bytes, &mut self.pos)?; self.started = true;
456 }
457 loop {
458 while self.c_rem > 0 {
461 self.c_rem -= 1;
462 self.c = self.c.wrapping_add(rd(bytes, &mut self.pos)?);
463 if self.pc.is_none_or(|z| z == self.c) {
464 return Some((self.a, self.b, self.c));
465 }
466 }
467 while self.b_rem > 0 {
469 self.b_rem -= 1;
470 self.b = self.b.wrapping_add(rd(bytes, &mut self.pos)?);
471 let num_c = rd(bytes, &mut self.pos)?;
472 if self.pb.is_some_and(|y| y != self.b) {
473 for _ in 0..num_c {
474 rd(bytes, &mut self.pos)?; }
476 continue;
477 }
478 self.c = 0; self.c_rem = num_c;
480 break;
481 }
482 if self.c_rem > 0 {
483 continue; }
485 if self.a_rem == 0 {
487 return None;
488 }
489 self.a_rem -= 1;
490 self.a = self.a.wrapping_add(rd(bytes, &mut self.pos)?);
491 let num_b = rd(bytes, &mut self.pos)?;
492 self.b = 0; if let Some(x) = self.pa {
494 if self.a > x {
495 return None; }
497 if self.a < x {
498 for _ in 0..num_b {
500 rd(bytes, &mut self.pos)?; let nc = rd(bytes, &mut self.pos)?;
502 for _ in 0..nc {
503 rd(bytes, &mut self.pos)?;
504 }
505 }
506 continue;
507 }
508 }
509 self.b_rem = num_b;
510 }
511 }
512}
513
514#[cfg(test)]
515mod tests {
516 use super::*;
517
518 fn sample() -> Vec<Triple> {
519 vec![
521 (5, 2, 9),
522 (1, 1, 1),
523 (1, 1, 4),
524 (1, 3, 2),
525 (5, 2, 7),
526 (1, 1, 1), (2, 9, 9),
528 ]
529 }
530
531 #[test]
532 fn round_trip_sorted_dedup() {
533 let mut b = TripleBlockBuilder::new();
534 for t in sample() {
535 b.push(t);
536 }
537 let bytes = b.build();
538 let blk = TripleBlock::parse(&bytes).unwrap();
539
540 let mut expected = sample();
541 expected.sort_unstable();
542 expected.dedup();
543 assert_eq!(blk.zone().count as usize, expected.len());
544 assert_eq!(blk.triples(), expected);
545 }
546
547 #[test]
548 fn zone_map_bounds_and_skipping() {
549 let mut b = TripleBlockBuilder::new();
550 for t in sample() {
551 b.push(t);
552 }
553 let bytes = b.build();
554 let blk = TripleBlock::parse(&bytes).unwrap();
555 let z = blk.zone();
556 assert_eq!((z.min_a, z.max_a), (1, 5));
557 assert_eq!((z.min_b, z.max_b), (1, 9));
558 assert_eq!((z.min_c, z.max_c), (1, 9));
559 assert!(z.may_contain(Some(3), None, None));
561 assert!(!z.may_contain(Some(99), None, None));
562 assert!(z.may_contain(None, None, None)); }
564
565 #[test]
566 fn empty_block() {
567 let blk_bytes = TripleBlockBuilder::new().build();
568 let blk = TripleBlock::parse(&blk_bytes).unwrap();
569 assert_eq!(blk.zone().count, 0);
570 assert!(blk.triples().is_empty());
571 assert!(blk.scan(None, None, None).next().is_none());
572 assert!(blk.scan(Some(1), None, None).next().is_none());
573 }
574
575 #[test]
578 fn scan_matches_full_decode_every_shape() {
579 let mut b = TripleBlockBuilder::new();
580 for t in sample() {
581 b.push(t);
582 }
583 let bytes = b.build();
584 let blk = TripleBlock::parse(&bytes).unwrap();
585 let all = blk.triples(); let opt = |v: u32| [None, Some(v)];
588 for pa in opt(1).into_iter().chain([Some(5), Some(99)]) {
590 for pb in opt(1).into_iter().chain([Some(2), Some(99)]) {
591 for pc in opt(1).into_iter().chain([Some(9), Some(99)]) {
592 let want: Vec<Triple> = all
593 .iter()
594 .copied()
595 .filter(|&(a, bb, c)| {
596 pa.is_none_or(|x| x == a)
597 && pb.is_none_or(|x| x == bb)
598 && pc.is_none_or(|x| x == c)
599 })
600 .collect();
601 let got: Vec<Triple> = blk.scan(pa, pb, pc).collect();
603 assert_eq!(got, want, "scan({pa:?},{pb:?},{pc:?})");
604 }
605 }
606 }
607 }
608
609 #[test]
612 fn scan_range_stops_on_leading_bound() {
613 let mut b = TripleBlockBuilder::new();
614 for t in [(1, 1, 1), (1, 2, 2), (3, 1, 1), (5, 1, 1)] {
615 b.push(t);
616 }
617 let bytes = b.build();
618 let blk = TripleBlock::parse(&bytes).unwrap();
619 let got: Vec<Triple> = blk.scan(Some(1), None, None).collect();
620 assert_eq!(got, vec![(1, 1, 1), (1, 2, 2)]);
621 assert!(blk.scan(Some(2), None, None).next().is_none());
623 assert!(blk.scan(Some(99), None, None).next().is_none());
624 }
625
626 #[test]
629 fn scan_never_panics_on_bad_bytes() {
630 let mut b = TripleBlockBuilder::new();
631 for t in sample() {
632 b.push(t);
633 }
634 let bytes = b.build();
635 for len in 0..bytes.len() {
636 if let Ok(blk) = TripleBlock::parse(&bytes[..len]) {
637 for pat in [(None, None, None), (Some(1u32), Some(1u32), Some(1u32))] {
638 let _ = blk.scan(pat.0, pat.1, pat.2).count();
639 }
640 }
641 }
642 for i in 0..bytes.len() {
643 for v in [0x00u8, 0xff, 0x80, 0x7f] {
644 let mut bad = bytes.clone();
645 bad[i] = v;
646 if let Ok(blk) = TripleBlock::parse(&bad) {
647 let _ = blk.scan(None, None, None).count();
648 let _ = blk.scan(Some(1), None, None).count();
649 }
650 }
651 }
652 }
653}