use std::borrow::Cow;
use std::ops::RangeInclusive;
pub(crate) trait Successor: Sized {
fn next(&self) -> Option<Self>;
}
impl Successor for u32 {
fn next(&self) -> Option<Self> {
self.checked_add(1)
}
}
impl Successor for u128 {
fn next(&self) -> Option<Self> {
self.checked_add(1)
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) struct DenseRangeMap<K, V1, V2>
where
K: Clone + 'static,
V1: Clone + 'static,
V2: Clone + 'static,
{
starts: Cow<'static, [K]>,
values1: Cow<'static, [Option<V1>]>,
values2: Option<Cow<'static, [Option<V2>]>>,
}
impl<K, V1, V2> Default for DenseRangeMap<K, V1, V2>
where
K: Clone + 'static,
V1: Clone + 'static,
V2: Clone + 'static,
{
fn default() -> Self {
Self {
starts: Default::default(),
values1: Default::default(),
values2: None,
}
}
}
struct DenseRangeMapBuilder<K, V1, V2> {
starts: Vec<K>,
values1: Vec<Option<V1>>,
values2: Option<Vec<Option<V2>>>,
prev_end: Option<K>,
}
#[derive(Debug, Clone, thiserror::Error)]
pub(crate) enum Error {
#[error("Found an entry with an invalid range")]
BadEntry,
#[error("Entries were not sorted")]
Unsorted,
}
impl<K: Eq + Ord + Successor, V1, V2> DenseRangeMapBuilder<K, V1, V2>
where
K: Clone + 'static,
V1: Clone + 'static,
V2: Clone + 'static,
{
fn new() -> Self {
Self {
starts: Vec::new(),
values1: Vec::new(),
values2: Some(Vec::new()),
prev_end: None,
}
}
fn push(&mut self, start: K, v1: Option<V1>, v2: Option<V2>) {
self.starts.push(start);
self.values1.push(v1);
if let Some(values2) = self.values2.as_mut() {
values2.push(v2);
}
}
fn build(mut self) -> DenseRangeMap<K, V1, V2> {
if let Some(prev_end) = self.prev_end.take()
&& let Some(next_range_start) = prev_end.next()
{
self.push(next_range_start, None, None);
}
self.starts.shrink_to_fit();
self.values1.shrink_to_fit();
if let Some(values2) = self.values2.as_mut() {
values2.shrink_to_fit();
}
let map = DenseRangeMap {
starts: self.starts.into(),
values1: self.values1.into(),
values2: self.values2.map(Into::into),
};
#[cfg(test)]
map.assert_valid();
map
}
fn add_entry(
&mut self,
range: RangeInclusive<K>,
value1: Option<V1>,
value2: Option<V2>,
) -> Result<(), Error> {
if value1.is_none() && value2.is_none() {
return Ok(());
}
use std::cmp::Ordering::*;
let (start, end) = range.into_inner();
if start > end {
return Err(Error::BadEntry);
}
if let Some(prev_end) = self.prev_end.take() {
let gap_start = prev_end.next().ok_or(Error::Unsorted)?;
match gap_start.cmp(&start) {
Less => {
self.push(gap_start, None, None);
}
Equal => {
}
Greater => {
return Err(Error::Unsorted);
}
}
}
self.push(start, value1, value2);
self.prev_end = Some(end);
Ok(())
}
}
impl<K: Eq + Ord + Successor, V1, V2> DenseRangeMap<K, V1, V2>
where
K: Clone + 'static,
V1: Clone + 'static,
V2: Clone + 'static,
{
pub(crate) fn from_static_parts(
starts: &'static [K],
values1: &'static [Option<V1>],
values2: Option<&'static [Option<V2>]>,
) -> Self {
let map = Self {
starts: starts.into(),
values1: values1.into(),
values2: values2.map(Into::into),
};
#[cfg(test)]
map.assert_valid();
map
}
#[cfg(test)]
pub(crate) fn from_sorted_inclusive_ranges<S>(iter: S) -> Result<Self, Error>
where
S: Iterator<Item = (RangeInclusive<K>, V1)>,
{
let discard_v2 = true;
Self::try_from_sorted_inclusive_ranges(
iter.map(|(r, v1)| Ok((r, Some(v1), None))),
discard_v2,
)
}
pub(crate) fn try_from_sorted_inclusive_ranges<S, E>(
iter: S,
discard_v2: bool,
) -> Result<Self, E>
where
S: Iterator<Item = Result<(RangeInclusive<K>, Option<V1>, Option<V2>), E>>,
E: From<Error>,
{
let mut b = DenseRangeMapBuilder::new();
if discard_v2 {
b.values2 = None;
}
for entry in iter {
let (range, value1, mut value2) = entry?;
if discard_v2 {
value2 = None;
}
b.add_entry(range, value1, value2)?;
}
Ok(b.build())
}
pub(crate) fn index_for_key(&self, key: &K) -> Option<usize> {
match self.starts.binary_search(key) {
Ok(v) => Some(v),
Err(0) => None,
Err(v) => Some(v - 1),
}
}
pub(crate) fn get1(&self, key: &K) -> Option<&V1> {
self.index_for_key(key)
.and_then(|index| self.values1[index].as_ref())
}
pub(crate) fn get2(&self, key: &K) -> Option<&V2> {
if let Some(values2) = self.values2.as_ref() {
self.index_for_key(key)
.and_then(|index| values2[index].as_ref())
} else {
None
}
}
#[cfg(test)]
fn assert_valid(&self) {
for pair in self.starts.windows(2) {
assert!(pair[0] < pair[1]);
}
assert_eq!(self.values1.len(), self.starts.len());
if let Some(values2) = &self.values2 {
assert_eq!(values2.len(), self.starts.len());
}
}
#[cfg(test)]
fn rep(&self) -> Vec<(K, Option<V1>)>
where
K: Clone + 'static,
V1: Clone + 'static,
{
self.starts
.iter()
.cloned()
.zip(self.values1.iter().cloned())
.collect()
}
#[cfg(feature = "export")]
#[allow(clippy::type_complexity)]
pub(crate) fn export(&self) -> (&[K], &[Option<V1>], Option<&[Option<V2>]>) {
(
self.starts.as_ref(),
self.values1.as_ref(),
self.values2.as_ref().map(|v2| v2.as_ref()),
)
}
}
#[cfg(test)]
mod test {
#![allow(clippy::bool_assert_comparison)]
#![allow(clippy::clone_on_copy)]
#![allow(clippy::dbg_macro)]
#![allow(clippy::mixed_attributes_style)]
#![allow(clippy::print_stderr)]
#![allow(clippy::print_stdout)]
#![allow(clippy::single_char_pattern)]
#![allow(clippy::unwrap_used)]
#![allow(clippy::unchecked_time_subtraction)]
#![allow(clippy::useless_vec)]
#![allow(clippy::needless_pass_by_value)]
use super::*;
use proptest::prelude::*;
type M = DenseRangeMap<u32, &'static str, ()>;
#[test]
fn empty() {
let map = M::from_sorted_inclusive_ranges(std::iter::empty()).unwrap();
assert_eq!(map.get1(&0), None);
assert_eq!(map.get1(&1), None);
assert_eq!(map.get1(&50), None);
assert_eq!(map.get1(&(u32::MAX - 1)), None);
assert_eq!(map.get1(&(u32::MAX)), None);
}
#[test]
fn both_ends_open() {
let map = M::from_sorted_inclusive_ranges(
[
(5..=10, "small"),
(11..=90, "medium"),
(100..=1000, "big"),
]
.into_iter(),
)
.unwrap();
map.assert_valid();
assert_eq!(
map.rep()[..],
[
(5, Some("small")),
(11, Some("medium")),
(91, None),
(100, Some("big")),
(1001, None),
]
);
assert_eq!(map.get1(&0), None);
assert_eq!(map.get1(&1), None);
assert_eq!(map.get1(&5), Some(&"small"));
assert_eq!(map.get1(&10), Some(&"small"));
assert_eq!(map.get1(&11), Some(&"medium"));
assert_eq!(map.get1(&85), Some(&"medium"));
assert_eq!(map.get1(&90), Some(&"medium"));
assert_eq!(map.get1(&91), None);
assert_eq!(map.get1(&99), None);
assert_eq!(map.get1(&100), Some(&"big"));
assert_eq!(map.get1(&500), Some(&"big"));
assert_eq!(map.get1(&1000), Some(&"big"));
assert_eq!(map.get1(&1001), None);
assert_eq!(map.get1(&(u32::MAX - 1)), None);
assert_eq!(map.get1(&u32::MAX), None);
}
#[test]
fn both_ends_filled_map() {
let map = M::from_sorted_inclusive_ranges(
[
(0..=10, "small"),
(11..=90, "medium"),
(100..=u32::MAX, "big"),
]
.into_iter(),
)
.unwrap();
assert_eq!(
map.rep()[..],
[
(0, Some("small")),
(11, Some("medium")),
(91, None),
(100, Some("big")),
]
);
assert_eq!(map.get1(&0), Some(&"small"));
assert_eq!(map.get1(&1), Some(&"small"));
assert_eq!(map.get1(&5), Some(&"small"));
assert_eq!(map.get1(&10), Some(&"small"));
assert_eq!(map.get1(&11), Some(&"medium"));
assert_eq!(map.get1(&85), Some(&"medium"));
assert_eq!(map.get1(&90), Some(&"medium"));
assert_eq!(map.get1(&91), None);
assert_eq!(map.get1(&99), None);
assert_eq!(map.get1(&100), Some(&"big"));
assert_eq!(map.get1(&500), Some(&"big"));
assert_eq!(map.get1(&1000), Some(&"big"));
assert_eq!(map.get1(&1001), Some(&"big"));
assert_eq!(map.get1(&(u32::MAX - 1)), Some(&"big"));
assert_eq!(map.get1(&u32::MAX), Some(&"big"));
}
proptest! {
#[test]
fn matches_rangemap(ranges: Vec<RangeInclusive<u32>>, probes: Vec<u32>) {
let mut rangemap: rangemap::RangeInclusiveMap<u32, usize> = Default::default();
for (n, range) in ranges.into_iter().enumerate() {
rangemap.insert(range, n);
}
let dense_map = DenseRangeMap::<u32, usize, ()>::from_sorted_inclusive_ranges(
rangemap.iter().map(|(k,v)| (k.clone(), *v))
).unwrap();
for probe in probes.iter() {
assert_eq!(rangemap.get(probe), dense_map.get1(probe));
}
}
#[test]
fn matches_rangemap2(r: Vec<(u32,u32)>, probes: Vec<u32>) {
let mut ranges = vec![];
let mut next = 0_u32;
for (gap,len) in r {
let Some(start) = next.checked_add(gap) else {break;};
let end = start.saturating_add(len);
ranges.push(start..=end);
if let Some(n) = end.checked_add(1) {
next = n;
} else {
break;
}
}
let mut rangemap: rangemap::RangeInclusiveMap<u32, usize> = Default::default();
for (n, range) in ranges.iter().enumerate() {
rangemap.insert(range.clone(), n);
}
let dense_map = DenseRangeMap::<u32, usize, ()>::from_sorted_inclusive_ranges(
ranges.into_iter().enumerate().map(|(n, r)| (r, n))
).unwrap();
for probe in probes.iter() {
assert_eq!(rangemap.get(probe), dense_map.get1(probe));
}
}
}
}