use std::fmt;
use bao_tree::ChunkNum;
use range_collections::{RangeSet2, RangeSetRef};
use serde::{Deserialize, Serialize};
use smallvec::{smallvec, SmallVec};
#[derive(Deserialize, Serialize, PartialEq, Eq, Clone, Hash)]
#[repr(transparent)]
pub struct RangeSpec(SmallVec<[u64; 2]>);
impl RangeSpec {
pub fn new(ranges: impl AsRef<RangeSetRef<ChunkNum>>) -> Self {
let ranges = ranges.as_ref().boundaries();
let mut res = SmallVec::new();
if let Some((start, rest)) = ranges.split_first() {
let mut prev = start.0;
res.push(prev);
for v in rest {
res.push(v.0 - prev);
prev = v.0;
}
}
Self(res)
}
pub const EMPTY: Self = Self(SmallVec::new_const());
pub fn all() -> Self {
Self(smallvec![0])
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn is_all(&self) -> bool {
self.0.len() == 1 && self.0[0] == 0
}
pub fn to_chunk_ranges(&self) -> RangeSet2<ChunkNum> {
let mut ranges = RangeSet2::empty();
let mut current = ChunkNum(0);
let mut on = false;
for &width in self.0.iter() {
let next = current + width;
if on {
ranges |= RangeSet2::from(current..next);
}
current = next;
on = !on;
}
if on {
ranges |= RangeSet2::from(current..);
}
ranges
}
}
impl fmt::Debug for RangeSpec {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if f.alternate() {
f.debug_list()
.entries(self.to_chunk_ranges().iter())
.finish()
} else if self.is_all() {
write!(f, "all")
} else if self.is_empty() {
write!(f, "empty")
} else {
f.debug_list().entries(self.0.iter()).finish()
}
}
}
#[derive(Deserialize, Serialize, Debug, PartialEq, Eq, Clone, Hash)]
#[repr(transparent)]
pub struct RangeSpecSeq(SmallVec<[(u64, RangeSpec); 2]>);
impl RangeSpecSeq {
#[allow(dead_code)]
pub const fn empty() -> Self {
Self(SmallVec::new_const())
}
pub fn as_single(&self) -> Option<(u64, &RangeSpec)> {
if self.0.len() != 2 {
return None;
}
let (fst_ofs, fst_val) = &self.0[0];
let (snd_ofs, snd_val) = &self.0[1];
if *snd_ofs == 1 && snd_val.is_empty() {
Some((*fst_ofs, fst_val))
} else {
None
}
}
pub fn all() -> Self {
Self(smallvec![(0, RangeSpec::all())])
}
pub fn from_ranges(
ranges: impl IntoIterator<Item = impl AsRef<RangeSetRef<ChunkNum>>>,
) -> Self {
Self::new(
ranges
.into_iter()
.map(RangeSpec::new)
.chain(std::iter::once(RangeSpec::EMPTY)),
)
}
pub fn from_ranges_infinite(
ranges: impl IntoIterator<Item = impl AsRef<RangeSetRef<ChunkNum>>>,
) -> Self {
Self::new(ranges.into_iter().map(RangeSpec::new))
}
pub fn new(children: impl IntoIterator<Item = RangeSpec>) -> Self {
let mut count = 0;
let mut res = SmallVec::new();
let before_all = RangeSpec::EMPTY;
for v in children.into_iter() {
let prev = res.last().map(|(_count, spec)| spec).unwrap_or(&before_all);
if &v == prev {
count += 1;
} else {
res.push((count, v.clone()));
count = 1;
}
}
Self(res)
}
pub fn iter(&self) -> RequestRangeSpecIter<'_> {
let before_first = self.0.get(0).map(|(c, _)| *c).unwrap_or_default();
RequestRangeSpecIter {
current: &EMPTY_RANGE_SPEC,
count: before_first,
remaining: &self.0,
}
}
pub fn iter_non_empty(&self) -> NonEmptyRequestRangeSpecIter<'_> {
NonEmptyRequestRangeSpecIter::new(self.iter())
}
}
static EMPTY_RANGE_SPEC: RangeSpec = RangeSpec::EMPTY;
#[derive(Debug)]
pub struct RequestRangeSpecIter<'a> {
current: &'a RangeSpec,
count: u64,
remaining: &'a [(u64, RangeSpec)],
}
impl<'a> RequestRangeSpecIter<'a> {
pub fn new(ranges: &'a [(u64, RangeSpec)]) -> Self {
let before_first = ranges.get(0).map(|(c, _)| *c).unwrap_or_default();
RequestRangeSpecIter {
current: &EMPTY_RANGE_SPEC,
count: before_first,
remaining: ranges,
}
}
pub fn is_at_end(&self) -> bool {
self.count == 0 && self.remaining.is_empty()
}
}
impl<'a> Iterator for RequestRangeSpecIter<'a> {
type Item = &'a RangeSpec;
fn next(&mut self) -> Option<Self::Item> {
Some(loop {
break if self.count > 0 {
self.count -= 1;
self.current
} else if let Some(((_, new), rest)) = self.remaining.split_first() {
self.current = new;
self.count = rest.get(0).map(|(c, _)| *c).unwrap_or_default();
self.remaining = rest;
continue;
} else {
self.current
};
})
}
}
#[derive(Debug)]
pub struct NonEmptyRequestRangeSpecIter<'a> {
inner: RequestRangeSpecIter<'a>,
count: u64,
}
impl<'a> NonEmptyRequestRangeSpecIter<'a> {
fn new(inner: RequestRangeSpecIter<'a>) -> Self {
Self { inner, count: 0 }
}
}
impl<'a> Iterator for NonEmptyRequestRangeSpecIter<'a> {
type Item = (u64, &'a RangeSpec);
fn next(&mut self) -> Option<Self::Item> {
loop {
let curr = self.inner.next().unwrap();
let count = self.count;
self.count = self.count.checked_add(1)?;
if !curr.is_empty() {
break Some((count, curr));
} else if self.inner.is_at_end() {
break None;
}
}
}
}
#[cfg(test)]
mod tests {
use std::ops::Range;
use super::*;
use bao_tree::ChunkNum;
use iroh_test::{assert_eq_hex, hexdump::parse_hexdump};
use proptest::prelude::*;
use range_collections::RangeSet2;
fn ranges(value_range: Range<u64>) -> impl Strategy<Value = RangeSet2<ChunkNum>> {
prop::collection::vec((value_range.clone(), value_range), 0..16).prop_map(|v| {
let mut res = RangeSet2::empty();
for (a, b) in v {
let start = a.min(b);
let end = a.max(b);
res |= RangeSet2::from(ChunkNum(start)..ChunkNum(end));
}
res
})
}
fn range_spec_seq_roundtrip_impl(ranges: &[RangeSet2<ChunkNum>]) -> Vec<RangeSet2<ChunkNum>> {
let spec = RangeSpecSeq::from_ranges(ranges.iter().cloned());
spec.iter()
.map(|x| x.to_chunk_ranges())
.take(ranges.len())
.collect::<Vec<_>>()
}
fn range_spec_seq_bytes_roundtrip_impl(
ranges: &[RangeSet2<ChunkNum>],
) -> Vec<RangeSet2<ChunkNum>> {
let spec = RangeSpecSeq::from_ranges(ranges.iter().cloned());
let bytes = postcard::to_allocvec(&spec).unwrap();
let spec2: RangeSpecSeq = postcard::from_bytes(&bytes).unwrap();
spec2
.iter()
.map(|x| x.to_chunk_ranges())
.take(ranges.len())
.collect::<Vec<_>>()
}
fn mk_case(case: Vec<Range<u64>>) -> Vec<RangeSet2<ChunkNum>> {
case.iter()
.map(|x| RangeSet2::from(ChunkNum(x.start)..ChunkNum(x.end)))
.collect::<Vec<_>>()
}
#[test]
fn range_spec_wire_format() {
let cases = [
(RangeSpec::EMPTY, "00"),
(
RangeSpec::all(),
r"
01 # length prefix - 1 element
00 # span width - 0. everything stating from 0 is included
",
),
(
RangeSpec::new(RangeSet2::from(ChunkNum(64)..)),
r"
01 # length prefix - 1 element
40 # span width - 64. everything starting from 64 is included
",
),
(
RangeSpec::new(RangeSet2::from(ChunkNum(10000)..)),
r"
01 # length prefix - 1 element
904E # span width - 10000, 904E in postcard varint encoding. everything starting from 10000 is included
",
),
(
RangeSpec::new(RangeSet2::from(..ChunkNum(64))),
r"
02 # length prefix - 2 elements
00 # span width - 0. everything stating from 0 is included
40 # span width - 64. everything starting from 64 is excluded
",
),
(
RangeSpec::new(
&RangeSet2::from(ChunkNum(1)..ChunkNum(3))
| &RangeSet2::from(ChunkNum(9)..ChunkNum(13)),
),
r"
04 # length prefix - 4 elements
01 # span width - 1
02 # span width - 2 (3 - 1)
06 # span width - 6 (9 - 3)
04 # span width - 4 (13 - 9)
",
),
];
for (case, expected_hex) in cases {
let expected = parse_hexdump(expected_hex).unwrap();
assert_eq_hex!(expected, postcard::to_stdvec(&case).unwrap());
}
}
#[test]
fn range_spec_seq_wire_format() {
let cases = [
(RangeSpecSeq::empty(), "00"),
(
RangeSpecSeq::all(),
r"
01 # 1 tuple in total
# first tuple
00 # span 0 until start
0100 # 1 element, RangeSpec::all()
",
),
(
RangeSpecSeq::from_ranges([
RangeSet2::from(ChunkNum(1)..ChunkNum(3)),
RangeSet2::from(ChunkNum(7)..ChunkNum(13)),
]),
r"
03 # 3 tuples in total
# first tuple
00 # span 0 until start
020102 # range 1..3
# second tuple
01 # span 1 until next
020706 # range 7..13
# third tuple
01 # span 1 until next
00 # empty range forever from now
",
),
(
RangeSpecSeq::from_ranges_infinite([
RangeSet2::empty(),
RangeSet2::empty(),
RangeSet2::empty(),
RangeSet2::from(ChunkNum(7)..),
RangeSet2::all(),
]),
r"
02 # 2 tuples in total
# first tuple
03 # span 3 until start (first 3 elements are empty)
01 07 # range 7..
# second tuple
01 # span 1 until next (1 element is 7..)
01 00 # RangeSet2::all() forever from now
",
),
];
for (case, expected_hex) in cases {
let expected = parse_hexdump(expected_hex).unwrap();
assert_eq_hex!(expected, postcard::to_stdvec(&case).unwrap());
}
}
#[test]
fn range_spec_seq_roundtrip_cases() {
for case in [
vec![0..1, 0..0],
vec![1..2, 1..2, 1..2],
vec![1..2, 1..2, 2..3, 2..3],
] {
let case = mk_case(case);
let expected = case.clone();
let actual = range_spec_seq_roundtrip_impl(&case);
assert_eq!(expected, actual);
}
}
#[test]
fn range_spec_seq_canonical() {
for (case, expected_count) in [
(vec![0..1, 0..0], 2),
(vec![1..2, 1..2, 1..2], 2),
(vec![1..2, 1..2, 2..3, 2..3], 3),
] {
let case = mk_case(case);
let spec = RangeSpecSeq::from_ranges(case);
assert_eq!(spec.0.len(), expected_count);
}
}
proptest! {
#[test]
fn range_spec_roundtrip(ranges in ranges(0..1000)) {
let spec = RangeSpec::new(&ranges);
let ranges2 = spec.to_chunk_ranges();
prop_assert_eq!(ranges, ranges2);
}
#[test]
fn range_spec_seq_roundtrip(ranges in proptest::collection::vec(ranges(0..100), 0..10)) {
let expected = ranges.clone();
let actual = range_spec_seq_roundtrip_impl(&ranges);
prop_assert_eq!(expected, actual);
}
#[test]
fn range_spec_seq_bytes_roundtrip(ranges in proptest::collection::vec(ranges(0..100), 0..10)) {
let expected = ranges.clone();
let actual = range_spec_seq_bytes_roundtrip_impl(&ranges);
prop_assert_eq!(expected, actual);
}
}
}