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
400struct DirEntry {
403 a: u32,
404 pos: usize,
405 num_b: u32,
406 a_rem_after: u32,
407}
408
409pub struct BlockCursor<'a> {
413 bytes: &'a [u8],
414 pos: usize,
415 a: u32,
417 b: u32,
418 c: u32,
419 a_rem: u32,
421 b_rem: u32,
422 c_rem: u32,
423 started: bool,
424 pa: Option<u32>,
425 pb: Option<u32>,
426 pc: Option<u32>,
427}
428
429impl Iterator for BlockCursor<'_> {
430 type Item = Triple;
431
432 fn next(&mut self) -> Option<Triple> {
433 let bytes = self.bytes;
434 if !self.started {
435 self.a_rem = rd(bytes, &mut self.pos)?; self.started = true;
437 }
438 loop {
439 while self.c_rem > 0 {
442 self.c_rem -= 1;
443 self.c = self.c.wrapping_add(rd(bytes, &mut self.pos)?);
444 if self.pc.is_none_or(|z| z == self.c) {
445 return Some((self.a, self.b, self.c));
446 }
447 }
448 while self.b_rem > 0 {
450 self.b_rem -= 1;
451 self.b = self.b.wrapping_add(rd(bytes, &mut self.pos)?);
452 let num_c = rd(bytes, &mut self.pos)?;
453 if self.pb.is_some_and(|y| y != self.b) {
454 for _ in 0..num_c {
455 rd(bytes, &mut self.pos)?; }
457 continue;
458 }
459 self.c = 0; self.c_rem = num_c;
461 break;
462 }
463 if self.c_rem > 0 {
464 continue; }
466 if self.a_rem == 0 {
468 return None;
469 }
470 self.a_rem -= 1;
471 self.a = self.a.wrapping_add(rd(bytes, &mut self.pos)?);
472 let num_b = rd(bytes, &mut self.pos)?;
473 self.b = 0; if let Some(x) = self.pa {
475 if self.a > x {
476 return None; }
478 if self.a < x {
479 for _ in 0..num_b {
481 rd(bytes, &mut self.pos)?; let nc = rd(bytes, &mut self.pos)?;
483 for _ in 0..nc {
484 rd(bytes, &mut self.pos)?;
485 }
486 }
487 continue;
488 }
489 }
490 self.b_rem = num_b;
491 }
492 }
493}
494
495#[cfg(test)]
496mod tests {
497 use super::*;
498
499 fn sample() -> Vec<Triple> {
500 vec![
502 (5, 2, 9),
503 (1, 1, 1),
504 (1, 1, 4),
505 (1, 3, 2),
506 (5, 2, 7),
507 (1, 1, 1), (2, 9, 9),
509 ]
510 }
511
512 #[test]
513 fn round_trip_sorted_dedup() {
514 let mut b = TripleBlockBuilder::new();
515 for t in sample() {
516 b.push(t);
517 }
518 let bytes = b.build();
519 let blk = TripleBlock::parse(&bytes).unwrap();
520
521 let mut expected = sample();
522 expected.sort_unstable();
523 expected.dedup();
524 assert_eq!(blk.zone().count as usize, expected.len());
525 assert_eq!(blk.triples(), expected);
526 }
527
528 #[test]
529 fn zone_map_bounds_and_skipping() {
530 let mut b = TripleBlockBuilder::new();
531 for t in sample() {
532 b.push(t);
533 }
534 let bytes = b.build();
535 let blk = TripleBlock::parse(&bytes).unwrap();
536 let z = blk.zone();
537 assert_eq!((z.min_a, z.max_a), (1, 5));
538 assert_eq!((z.min_b, z.max_b), (1, 9));
539 assert_eq!((z.min_c, z.max_c), (1, 9));
540 assert!(z.may_contain(Some(3), None, None));
542 assert!(!z.may_contain(Some(99), None, None));
543 assert!(z.may_contain(None, None, None)); }
545
546 #[test]
547 fn empty_block() {
548 let blk_bytes = TripleBlockBuilder::new().build();
549 let blk = TripleBlock::parse(&blk_bytes).unwrap();
550 assert_eq!(blk.zone().count, 0);
551 assert!(blk.triples().is_empty());
552 assert!(blk.scan(None, None, None).next().is_none());
553 assert!(blk.scan(Some(1), None, None).next().is_none());
554 }
555
556 #[test]
559 fn scan_matches_full_decode_every_shape() {
560 let mut b = TripleBlockBuilder::new();
561 for t in sample() {
562 b.push(t);
563 }
564 let bytes = b.build();
565 let blk = TripleBlock::parse(&bytes).unwrap();
566 let all = blk.triples(); let opt = |v: u32| [None, Some(v)];
569 for pa in opt(1).into_iter().chain([Some(5), Some(99)]) {
571 for pb in opt(1).into_iter().chain([Some(2), Some(99)]) {
572 for pc in opt(1).into_iter().chain([Some(9), Some(99)]) {
573 let want: Vec<Triple> = all
574 .iter()
575 .copied()
576 .filter(|&(a, bb, c)| {
577 pa.is_none_or(|x| x == a)
578 && pb.is_none_or(|x| x == bb)
579 && pc.is_none_or(|x| x == c)
580 })
581 .collect();
582 let got: Vec<Triple> = blk.scan(pa, pb, pc).collect();
584 assert_eq!(got, want, "scan({pa:?},{pb:?},{pc:?})");
585 }
586 }
587 }
588 }
589
590 #[test]
593 fn scan_range_stops_on_leading_bound() {
594 let mut b = TripleBlockBuilder::new();
595 for t in [(1, 1, 1), (1, 2, 2), (3, 1, 1), (5, 1, 1)] {
596 b.push(t);
597 }
598 let bytes = b.build();
599 let blk = TripleBlock::parse(&bytes).unwrap();
600 let got: Vec<Triple> = blk.scan(Some(1), None, None).collect();
601 assert_eq!(got, vec![(1, 1, 1), (1, 2, 2)]);
602 assert!(blk.scan(Some(2), None, None).next().is_none());
604 assert!(blk.scan(Some(99), None, None).next().is_none());
605 }
606
607 #[test]
610 fn scan_never_panics_on_bad_bytes() {
611 let mut b = TripleBlockBuilder::new();
612 for t in sample() {
613 b.push(t);
614 }
615 let bytes = b.build();
616 for len in 0..bytes.len() {
617 if let Ok(blk) = TripleBlock::parse(&bytes[..len]) {
618 for pat in [(None, None, None), (Some(1u32), Some(1u32), Some(1u32))] {
619 let _ = blk.scan(pat.0, pat.1, pat.2).count();
620 }
621 }
622 }
623 for i in 0..bytes.len() {
624 for v in [0x00u8, 0xff, 0x80, 0x7f] {
625 let mut bad = bytes.clone();
626 bad[i] = v;
627 if let Ok(blk) = TripleBlock::parse(&bad) {
628 let _ = blk.scan(None, None, None).count();
629 let _ = blk.scan(Some(1), None, None).count();
630 }
631 }
632 }
633 }
634}