1use std::{fmt, sync::OnceLock};
9
10pub use bao_tree::ChunkRanges;
11use bao_tree::{ChunkNum, ChunkRangesRef};
12use serde::{Deserialize, Serialize};
13use smallvec::{smallvec, SmallVec};
14
15pub use crate::util::ChunkRangesExt;
16
17static CHUNK_RANGES_EMPTY: OnceLock<ChunkRanges> = OnceLock::new();
18
19fn chunk_ranges_empty() -> &'static ChunkRanges {
20 CHUNK_RANGES_EMPTY.get_or_init(ChunkRanges::empty)
21}
22
23#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
24#[serde(from = "wire::RangeSpecSeq", into = "wire::RangeSpecSeq")]
25pub struct ChunkRangesSeq(pub(crate) SmallVec<[(u64, ChunkRanges); 2]>);
26
27impl std::hash::Hash for ChunkRangesSeq {
28 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
29 for (i, r) in &self.0 {
30 i.hash(state);
31 r.boundaries().hash(state);
32 }
33 }
34}
35
36impl std::ops::Index<u64> for ChunkRangesSeq {
37 type Output = ChunkRanges;
38
39 fn index(&self, index: u64) -> &Self::Output {
40 match self.0.binary_search_by(|(o, _)| o.cmp(&index)) {
41 Ok(i) => &self.0[i].1,
42 Err(i) => {
43 if i == 0 {
44 chunk_ranges_empty()
45 } else {
46 &self.0[i - 1].1
47 }
48 }
49 }
50 }
51}
52
53impl ChunkRangesSeq {
54 pub const fn empty() -> Self {
55 Self(SmallVec::new_const())
56 }
57
58 pub fn root() -> Self {
60 let mut inner = SmallVec::new();
61 inner.push((0, ChunkRanges::all()));
62 inner.push((1, ChunkRanges::empty()));
63 Self(inner)
64 }
65
66 pub fn all() -> Self {
70 let mut inner = SmallVec::new();
71 inner.push((0, ChunkRanges::all()));
72 Self(inner)
73 }
74
75 pub fn verified_size() -> Self {
77 let mut inner = SmallVec::new();
78 inner.push((0, ChunkRanges::last_chunk()));
79 inner.push((1, ChunkRanges::empty()));
80 Self(inner)
81 }
82
83 pub fn verified_child_sizes() -> Self {
85 let mut inner = SmallVec::new();
86 inner.push((0, ChunkRanges::all()));
87 inner.push((1, ChunkRanges::last_chunk()));
88 Self(inner)
89 }
90
91 pub fn is_empty(&self) -> bool {
93 self.0.is_empty()
94 }
95
96 pub fn is_all(&self) -> bool {
98 if self.0.len() != 1 {
99 return false;
100 }
101 let Some((_, ranges)) = self.0.iter().next() else {
102 return false;
103 };
104 ranges.is_all()
105 }
106
107 pub fn as_single(&self) -> Option<(u64, &ChunkRanges)> {
110 if self.0.len() != 2 {
114 return None;
115 }
116 let (o1, v1) = self.0.iter().next().unwrap();
117 let (o2, v2) = self.0.iter().next_back().unwrap();
118 if *o1 == (o2 - 1) && v2.is_empty() {
119 Some((*o1, v1))
120 } else {
121 None
122 }
123 }
124
125 pub fn is_blob(&self) -> bool {
126 #[allow(clippy::match_like_matches_macro)]
127 match self.as_single() {
128 Some((0, _)) => true,
129 _ => false,
130 }
131 }
132
133 pub fn from_ranges_infinite(ranges: impl IntoIterator<Item = ChunkRanges>) -> Self {
137 let (ranges, _) = from_ranges_inner(ranges);
138 Self(ranges)
139 }
140
141 pub fn from_ranges(ranges: impl IntoIterator<Item = ChunkRanges>) -> Self {
145 let (mut res, next) = from_ranges_inner(ranges);
146 if let Some((_, r)) = res.iter().next_back() {
147 if !r.is_empty() {
148 res.push((next, ChunkRanges::empty()));
149 }
150 }
151 Self(res)
152 }
153
154 pub fn iter_non_empty_infinite(&self) -> NonEmptyRequestRangeSpecIter<'_> {
162 NonEmptyRequestRangeSpecIter::new(self.iter_infinite())
163 }
164
165 pub fn is_infinite(&self) -> bool {
167 self.0
168 .iter()
169 .next_back()
170 .map(|(_, v)| !v.is_empty())
171 .unwrap_or_default()
172 }
173
174 pub fn iter_infinite(&self) -> ChunkRangesSeqIterInfinite<'_> {
175 ChunkRangesSeqIterInfinite {
176 current: chunk_ranges_empty(),
177 offset: 0,
178 remaining: self.0.iter().peekable(),
179 }
180 }
181
182 pub fn iter(&self) -> ChunkRangesSeqIter<'_> {
183 ChunkRangesSeqIter {
184 current: chunk_ranges_empty(),
185 offset: 0,
186 remaining: self.0.iter().peekable(),
187 }
188 }
189}
190
191fn from_ranges_inner(
192 ranges: impl IntoIterator<Item = ChunkRanges>,
193) -> (SmallVec<[(u64, ChunkRanges); 2]>, u64) {
194 let mut res = SmallVec::new();
195 let mut i = 0;
196 for range in ranges.into_iter() {
197 if range
198 != res
199 .iter()
200 .next_back()
201 .map(|(_, v)| v)
202 .unwrap_or(&ChunkRanges::empty())
203 {
204 res.push((i, range));
205 }
206 i += 1;
207 }
208 (res, i)
209}
210
211#[derive(Debug)]
216pub struct ChunkRangesSeqIterInfinite<'a> {
217 current: &'a ChunkRanges,
219 offset: u64,
221 remaining: std::iter::Peekable<std::slice::Iter<'a, (u64, ChunkRanges)>>,
223}
224
225impl<'a> ChunkRangesSeqIterInfinite<'a> {
226 pub fn is_at_end(&mut self) -> bool {
231 self.remaining.peek().is_none()
232 }
233}
234
235impl<'a> Iterator for ChunkRangesSeqIterInfinite<'a> {
236 type Item = &'a ChunkRanges;
237
238 fn next(&mut self) -> Option<Self::Item> {
239 loop {
240 match self.remaining.peek() {
241 Some((offset, _)) if self.offset < *offset => {
242 self.offset += 1;
244 return Some(self.current);
245 }
246 None => {
247 self.offset += 1;
249 return Some(self.current);
250 }
251 Some((_, ranges)) => {
252 self.current = ranges;
254 self.remaining.next();
255 }
256 }
257 }
258 }
259}
260
261#[derive(Debug)]
266pub struct ChunkRangesSeqIter<'a> {
267 current: &'a ChunkRanges,
269 offset: u64,
271 remaining: std::iter::Peekable<std::slice::Iter<'a, (u64, ChunkRanges)>>,
273}
274
275impl<'a> Iterator for ChunkRangesSeqIter<'a> {
276 type Item = &'a ChunkRanges;
277
278 fn next(&mut self) -> Option<Self::Item> {
279 match self.remaining.peek()? {
280 (offset, _) if self.offset < *offset => {
281 self.offset += 1;
283 Some(self.current)
284 }
285 (_, ranges) => {
286 self.current = ranges;
288 self.remaining.next();
289 self.offset += 1;
290 Some(self.current)
291 }
292 }
293 }
294}
295
296#[derive(Debug)]
300pub struct NonEmptyRequestRangeSpecIter<'a> {
301 inner: ChunkRangesSeqIterInfinite<'a>,
302 count: u64,
303}
304
305impl<'a> NonEmptyRequestRangeSpecIter<'a> {
306 fn new(inner: ChunkRangesSeqIterInfinite<'a>) -> Self {
307 Self { inner, count: 0 }
308 }
309
310 pub(crate) fn offset(&self) -> u64 {
311 self.count
312 }
313
314 pub fn is_at_end(&mut self) -> bool {
315 self.inner.is_at_end()
316 }
317}
318
319impl<'a> Iterator for NonEmptyRequestRangeSpecIter<'a> {
320 type Item = (u64, &'a ChunkRanges);
321
322 fn next(&mut self) -> Option<Self::Item> {
323 loop {
324 let curr = self.inner.next().unwrap();
326 let count = self.count;
327 self.count = self.count.checked_add(1)?;
330 if !curr.is_empty() {
332 break Some((count, curr));
333 } else if self.inner.is_at_end() {
334 break None;
336 }
337 }
338 }
339}
340
341#[derive(Deserialize, Serialize, PartialEq, Eq, Clone, Hash)]
372#[repr(transparent)]
373pub struct RangeSpec(SmallVec<[u64; 2]>);
374
375impl RangeSpec {
376 pub fn new(ranges: impl AsRef<ChunkRangesRef>) -> Self {
378 let ranges = ranges.as_ref().boundaries();
379 let mut res = SmallVec::new();
380 if let Some((start, rest)) = ranges.split_first() {
381 let mut prev = start.0;
382 res.push(prev);
383 for v in rest {
384 res.push(v.0 - prev);
385 prev = v.0;
386 }
387 }
388 Self(res)
389 }
390
391 pub const EMPTY: Self = Self(SmallVec::new_const());
395
396 pub fn all() -> Self {
398 Self(smallvec![0])
399 }
400
401 pub fn verified_size() -> Self {
403 Self(smallvec![u64::MAX])
404 }
405
406 pub fn is_empty(&self) -> bool {
408 self.0.is_empty()
409 }
410
411 pub fn is_all(&self) -> bool {
413 self.0.len() == 1 && self.0[0] == 0
414 }
415
416 pub fn chunks(&self) -> (u64, Option<u64>) {
419 let mut min = 0;
420 for i in 0..self.0.len() / 2 {
421 min += self.0[2 * i + 1];
422 }
423 let max = if self.0.len() % 2 != 0 {
424 None
426 } else {
427 Some(min)
428 };
429 (min, max)
430 }
431
432 pub fn to_chunk_ranges(&self) -> ChunkRanges {
434 let mut ranges = ChunkRanges::empty();
437 let mut current = ChunkNum(0);
438 let mut on = false;
439 for &width in self.0.iter() {
440 let next = current + width;
441 if on {
442 ranges |= ChunkRanges::from(current..next);
443 }
444 current = next;
445 on = !on;
446 }
447 if on {
448 ranges |= ChunkRanges::from(current..);
449 }
450 ranges
451 }
452}
453
454impl fmt::Debug for RangeSpec {
455 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
456 if self.is_all() {
457 write!(f, "all")
458 } else if self.is_empty() {
459 write!(f, "empty")
460 } else if !f.alternate() {
461 f.debug_list()
462 .entries(self.to_chunk_ranges().iter())
463 .finish()
464 } else {
465 f.debug_list().entries(self.0.iter()).finish()
466 }
467 }
468}
469
470mod wire {
471
472 use serde::{Deserialize, Serialize};
473 use smallvec::SmallVec;
474
475 use super::{ChunkRangesSeq, RangeSpec};
476
477 #[derive(Deserialize, Serialize)]
478 pub struct RangeSpecSeq(SmallVec<[(u64, RangeSpec); 2]>);
479
480 impl From<RangeSpecSeq> for ChunkRangesSeq {
481 fn from(wire: RangeSpecSeq) -> Self {
482 let mut offset = 0;
483 let mut res = SmallVec::new();
484 for (delta, spec) in wire.0.iter() {
485 offset += *delta;
486 res.push((offset, spec.to_chunk_ranges()));
487 }
488 Self(res)
489 }
490 }
491
492 impl From<ChunkRangesSeq> for RangeSpecSeq {
493 fn from(value: ChunkRangesSeq) -> Self {
494 let mut res = SmallVec::new();
495 let mut offset = 0;
496 for (i, r) in value.0.iter() {
497 let delta = *i - offset;
498 res.push((delta, RangeSpec::new(r)));
499 offset = *i;
500 }
501 Self(res)
502 }
503 }
504}
505
506#[cfg(test)]
507mod tests {
508 use std::ops::Range;
509
510 use iroh_test::{assert_eq_hex, hexdump::parse_hexdump};
511 use proptest::prelude::*;
512
513 use super::*;
514 use crate::util::ChunkRangesExt;
515
516 fn ranges(value_range: Range<u64>) -> impl Strategy<Value = ChunkRanges> {
517 prop::collection::vec((value_range.clone(), value_range), 0..16).prop_map(|v| {
518 let mut res = ChunkRanges::empty();
519 for (a, b) in v {
520 let start = a.min(b);
521 let end = a.max(b);
522 res |= ChunkRanges::chunks(start..end);
523 }
524 res
525 })
526 }
527
528 fn range_spec_seq_roundtrip_impl(ranges: &[ChunkRanges]) -> Vec<ChunkRanges> {
529 let spec = ChunkRangesSeq::from_ranges(ranges.iter().cloned());
530 spec.iter_infinite()
531 .take(ranges.len())
532 .cloned()
533 .collect::<Vec<_>>()
534 }
535
536 fn range_spec_seq_bytes_roundtrip_impl(ranges: &[ChunkRanges]) -> Vec<ChunkRanges> {
537 let spec = ChunkRangesSeq::from_ranges(ranges.iter().cloned());
538 let bytes = postcard::to_allocvec(&spec).unwrap();
539 let spec2: ChunkRangesSeq = postcard::from_bytes(&bytes).unwrap();
540 spec2
541 .iter_infinite()
542 .take(ranges.len())
543 .cloned()
544 .collect::<Vec<_>>()
545 }
546
547 fn mk_case(case: Vec<Range<u64>>) -> Vec<ChunkRanges> {
548 case.iter()
549 .map(|x| ChunkRanges::chunks(x.start..x.end))
550 .collect::<Vec<_>>()
551 }
552
553 #[test]
554 fn range_spec_wire_format() {
555 let cases = [
557 (RangeSpec::EMPTY, "00"),
558 (
559 RangeSpec::all(),
560 r"
561 01 # length prefix - 1 element
562 00 # span width - 0. everything stating from 0 is included
563 ",
564 ),
565 (
566 RangeSpec::new(ChunkRanges::chunks(64..)),
567 r"
568 01 # length prefix - 1 element
569 40 # span width - 64. everything starting from 64 is included
570 ",
571 ),
572 (
573 RangeSpec::new(ChunkRanges::chunks(10000..)),
574 r"
575 01 # length prefix - 1 element
576 904E # span width - 10000, 904E in postcard varint encoding. everything starting from 10000 is included
577 ",
578 ),
579 (
580 RangeSpec::new(ChunkRanges::chunks(..64)),
581 r"
582 02 # length prefix - 2 elements
583 00 # span width - 0. everything stating from 0 is included
584 40 # span width - 64. everything starting from 64 is excluded
585 ",
586 ),
587 (
588 RangeSpec::new(&ChunkRanges::chunks(1..3) | &ChunkRanges::chunks(9..13)),
589 r"
590 04 # length prefix - 4 elements
591 01 # span width - 1
592 02 # span width - 2 (3 - 1)
593 06 # span width - 6 (9 - 3)
594 04 # span width - 4 (13 - 9)
595 ",
596 ),
597 ];
598 for (case, expected_hex) in cases {
599 let expected = parse_hexdump(expected_hex).unwrap();
600 assert_eq_hex!(expected, postcard::to_stdvec(&case).unwrap());
601 }
602 }
603
604 #[test]
605 fn range_spec_seq_wire_format() {
606 let cases = [
607 (ChunkRangesSeq::empty(), "00"),
608 (
609 ChunkRangesSeq::all(),
610 r"
611 01 # 1 tuple in total
612 # first tuple
613 00 # span 0 until start
614 0100 # 1 element, RangeSpec::all()
615 ",
616 ),
617 (
618 ChunkRangesSeq::from_ranges([
619 ChunkRanges::chunks(1..3),
620 ChunkRanges::chunks(7..13),
621 ]),
622 r"
623 03 # 3 tuples in total
624 # first tuple
625 00 # span 0 until start
626 020102 # range 1..3
627 # second tuple
628 01 # span 1 until next
629 020706 # range 7..13
630 # third tuple
631 01 # span 1 until next
632 00 # empty range forever from now
633 ",
634 ),
635 (
636 ChunkRangesSeq::from_ranges_infinite([
637 ChunkRanges::empty(),
638 ChunkRanges::empty(),
639 ChunkRanges::empty(),
640 ChunkRanges::chunks(7..),
641 ChunkRanges::all(),
642 ]),
643 r"
644 02 # 2 tuples in total
645 # first tuple
646 03 # span 3 until start (first 3 elements are empty)
647 01 07 # range 7..
648 # second tuple
649 01 # span 1 until next (1 element is 7..)
650 01 00 # ChunkRanges::all() forever from now
651 ",
652 ),
653 ];
654 for (case, expected_hex) in cases {
655 let expected = parse_hexdump(expected_hex).unwrap();
656 assert_eq_hex!(expected, postcard::to_stdvec(&case).unwrap());
657 }
658 }
659
660 #[test]
662 fn range_spec_seq_roundtrip_cases() {
663 for case in [
664 vec![0..1, 0..0],
665 vec![1..2, 1..2, 1..2],
666 vec![1..2, 1..2, 2..3, 2..3],
667 ] {
668 let case = mk_case(case);
669 let expected = case.clone();
670 let actual = range_spec_seq_roundtrip_impl(&case);
671 assert_eq!(expected, actual);
672 }
673 }
674
675 #[test]
677 fn range_spec_seq_canonical() {
678 for (case, expected_count) in [
679 (vec![0..1, 0..0], 2),
680 (vec![1..2, 1..2, 1..2], 2),
681 (vec![1..2, 1..2, 2..3, 2..3], 3),
682 ] {
683 let case = mk_case(case);
684 let spec = ChunkRangesSeq::from_ranges(case);
685 assert_eq!(spec.0.len(), expected_count);
686 }
687 }
688
689 proptest! {
690
691 #[test]
692 fn range_spec_roundtrip(ranges in ranges(0..1000)) {
693 let spec = RangeSpec::new(&ranges);
694 let ranges2 = spec.to_chunk_ranges();
695 prop_assert_eq!(ranges, ranges2);
696 }
697
698 #[test]
699 fn range_spec_seq_roundtrip(ranges in proptest::collection::vec(ranges(0..100), 0..10)) {
700 let expected = ranges.clone();
701 let actual = range_spec_seq_roundtrip_impl(&ranges);
702 prop_assert_eq!(expected, actual);
703 }
704
705 #[test]
706 fn range_spec_seq_bytes_roundtrip(ranges in proptest::collection::vec(ranges(0..100), 0..10)) {
707 let expected = ranges.clone();
708 let actual = range_spec_seq_bytes_roundtrip_impl(&ranges);
709 prop_assert_eq!(expected, actual);
710 }
711 }
712}