use crate::internal::Sealed;
use std::hash::Hash;
pub unsafe trait RecordIndex: Sealed + Eq + Copy + Send + Sync + Ord + Hash {
fn contains_bounds(container: &Bounds<Self>, bounds: &Bounds<Self>) -> bool;
fn in_bounds(&self, bounds: &Bounds<Self>) -> bool;
fn enclose_index(bounds: &mut Bounds<Self>, index: Self);
fn empty_bounds() -> Bounds<Self>;
fn bounds_for_index(index: Self) -> Bounds<Self>;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Bounds<I> {
pub offset: I,
pub extent: I,
}
impl<I> Bounds<I> {
pub fn zip<I2>(self, other: Bounds<I2>) -> Bounds<(I, I2)> {
Bounds {
offset: (self.offset, other.offset),
extent: (self.extent, other.extent),
}
}
}
impl<I: RecordIndex> Bounds<I> {
pub fn contains_bounds(&self, other: &Bounds<I>) -> bool {
I::contains_bounds(self, other)
}
pub fn contains_index(&self, index: I) -> bool {
index.in_bounds(self)
}
pub fn enclose_index(&mut self, index: I) {
I::enclose_index(self, index)
}
pub fn new_empty() -> Self {
I::empty_bounds()
}
pub fn bounds_for_index(index: I) -> Self {
I::bounds_for_index(index)
}
}
macro_rules! impl_single_dim_index {
($ty:ty) => {
unsafe impl RecordIndex for $ty {
#[inline]
fn contains_bounds(container: &Bounds<Self>, bounds: &Bounds<Self>) -> bool {
let left_contained = container.offset <= bounds.offset;
let right_contained =
bounds.offset + bounds.extent <= container.offset + container.extent;
left_contained && right_contained
}
#[inline]
fn in_bounds(&self, bounds: &Bounds<Self>) -> bool {
let Bounds { offset, extent } = *bounds;
let i = *self;
offset <= i && i < (offset + extent)
}
#[inline]
fn enclose_index(bounds: &mut Bounds<Self>, index: Self) {
let new_offset = Self::min(bounds.offset, index);
bounds.offset = new_offset;
bounds.extent = Self::max(bounds.extent, index - new_offset + 1)
}
#[inline]
fn empty_bounds() -> Bounds<Self> {
Bounds {
offset: 0,
extent: 0,
}
}
#[inline]
fn bounds_for_index(index: Self) -> Bounds<Self> {
Bounds {
offset: index,
extent: 1,
}
}
}
};
}
impl_single_dim_index!(usize);
#[cfg(any(target_pointer_width = "32", target_pointer_width = "64",))]
impl_single_dim_index!(u32);
#[cfg(any(target_pointer_width = "64"))]
impl_single_dim_index!(u64);
macro_rules! join_expressions {
($separator:tt; $token_head:expr, $($token_tail:expr),*) => {
$token_head $($separator $token_tail)*
}
}
macro_rules! impl_tuple_index {
(($($idx_type:tt),*), ($($idx:tt),*)) => {
unsafe impl<$($idx_type: RecordIndex),*> RecordIndex for ($($idx_type),*) {
#[inline]
fn contains_bounds(container: &Bounds<Self>, bounds: &Bounds<Self>) -> bool {
let container_bounds = (
$(Bounds { offset: container.offset.$idx, extent: container.extent.$idx }),*
);
let bounds = (
$(Bounds { offset: bounds.offset.$idx, extent: bounds.extent.$idx }),*
);
join_expressions!(
&&;
$($idx_type::contains_bounds(&container_bounds.$idx, &bounds.$idx)),*
)
}
#[inline]
fn in_bounds(&self, bounds: &Bounds<Self>) -> bool {
let bounds = (
$(Bounds { offset: bounds.offset.$idx, extent: bounds.extent.$idx }),*
);
join_expressions!(
&&;
$(self.$idx.in_bounds(&bounds.$idx)),*
)
}
#[inline]
fn enclose_index(bounds: &mut Bounds<Self>, index: Self) {
let mut bounds_1d = (
$(Bounds { offset: bounds.offset.$idx, extent: bounds.extent.$idx }),*
);
$(bounds_1d.$idx.enclose_index(index.$idx);)*
$(bounds.offset.$idx = bounds_1d.$idx.offset;)*
$(bounds.extent.$idx = bounds_1d.$idx.extent;)*
}
#[inline]
fn empty_bounds() -> Bounds<Self> {
let bounds_1d = ($($idx_type::empty_bounds()),*);
Bounds {
offset: ($(bounds_1d.$idx.offset),*),
extent: ($(bounds_1d.$idx.offset),*)
}
}
#[inline]
fn bounds_for_index(index: Self) -> Bounds<Self> {
let bounds_1d = ($($idx_type::bounds_for_index(index.$idx)),*);
Bounds {
offset: ($(bounds_1d.$idx.offset),*),
extent: ($(bounds_1d.$idx.offset),*)
}
}
}
};
}
impl_tuple_index!((I0, I1), (0, 1));
impl_tuple_index!((I0, I1, I2), (0, 1, 2));
impl_tuple_index!((I0, I1, I2, I3), (0, 1, 2, 3));
impl_tuple_index!((I0, I1, I2, I3, I4), (0, 1, 2, 3, 4));
#[cfg(test)]
mod tests {
use crate::{Bounds, RecordIndex};
#[rustfmt::skip]
#[test]
fn usize_in_bounds() {
assert!(0usize.in_bounds(&Bounds { offset: 0, extent: 1 }));
assert!(1usize.in_bounds(&Bounds { offset: 1, extent: 1 }));
assert!(1usize.in_bounds(&Bounds { offset: 1, extent: 1 }));
assert!(1usize.in_bounds(&Bounds { offset: 0, extent: 2 }));
assert!(!0usize.in_bounds(&Bounds { offset: 0, extent: 0 }));
assert!(!1usize.in_bounds(&Bounds { offset: 0, extent: 0 }));
assert!(!1usize.in_bounds(&Bounds { offset: 0, extent: 1 }));
}
#[rustfmt::skip]
#[test]
fn usize_2dim_in_bounds() {
let bounds_zero_extent = Bounds { offset: (0, 0), extent: (0, 0) };
assert!(!(0usize, 0usize).in_bounds(&bounds_zero_extent)); assert!(!(1usize, 1usize).in_bounds(&bounds_zero_extent));
let bounds_normal = Bounds { offset: (0, 0), extent: (2, 2) };
assert!((0usize, 0usize).in_bounds(&bounds_normal)); assert!((1usize, 1usize).in_bounds(&bounds_normal)); assert!(!(2usize, 2usize).in_bounds(&bounds_normal));
let bounds_offset = Bounds { offset: (1, 1), extent: (2, 2) };
assert!(!(0usize, 0usize).in_bounds(&bounds_offset)); assert!((1usize, 1usize).in_bounds(&bounds_offset)); assert!((2usize, 2usize).in_bounds(&bounds_offset)); assert!(!(3usize, 3usize).in_bounds(&bounds_offset));
let bounds_large = Bounds { offset: (0, 0), extent: (5, 5) };
assert!((0usize, 0usize).in_bounds(&bounds_large)); assert!((4usize, 4usize).in_bounds(&bounds_large)); assert!(!(5usize, 5usize).in_bounds(&bounds_large)); }
#[rustfmt::skip]
#[test]
fn usize_3dim_in_bounds() {
let bounds_zero_extent = Bounds { offset: (0, 0, 0), extent: (0, 0, 0) };
assert!(!(0usize, 0usize, 0usize).in_bounds(&bounds_zero_extent)); assert!(!(1usize, 1usize, 1usize).in_bounds(&bounds_zero_extent));
let bounds_normal = Bounds { offset: (0, 0, 0), extent: (3, 3, 3) };
assert!((0usize, 0usize, 0usize).in_bounds(&bounds_normal)); assert!((1usize, 1usize, 1usize).in_bounds(&bounds_normal)); assert!((2usize, 2usize, 2usize).in_bounds(&bounds_normal)); assert!(!(3usize, 3usize, 3usize).in_bounds(&bounds_normal));
let bounds_offset = Bounds { offset: (1, 1, 1), extent: (2, 2, 2) };
assert!(!(0usize, 0usize, 0usize).in_bounds(&bounds_offset)); assert!((1usize, 1usize, 1usize).in_bounds(&bounds_offset)); assert!((2usize, 2usize, 2usize).in_bounds(&bounds_offset)); assert!(!(3usize, 3usize, 3usize).in_bounds(&bounds_offset));
let bounds_large = Bounds { offset: (0, 0, 0), extent: (5, 5, 5) };
assert!((0usize, 0usize, 0usize).in_bounds(&bounds_large)); assert!((4usize, 4usize, 4usize).in_bounds(&bounds_large)); assert!(!(5usize, 5usize, 5usize).in_bounds(&bounds_large)); }
#[rustfmt::skip]
#[test]
fn usize_contains_bounds() {
assert!(usize::contains_bounds(&Bounds { offset: 0, extent: 1 },
&Bounds { offset: 0, extent: 1 }));
assert!(usize::contains_bounds(&Bounds { offset: 0, extent: 5 },
&Bounds { offset: 1, extent: 3 }));
assert!(!usize::contains_bounds(&Bounds { offset: 1, extent: 3 },
&Bounds { offset: 0, extent: 5 }));
assert!(!usize::contains_bounds(&Bounds { offset: 0, extent: 3 },
&Bounds { offset: 2, extent: 2 }));
assert!(!usize::contains_bounds(&Bounds { offset: 0, extent: 2 },
&Bounds { offset: 3, extent: 2 }));
assert!(!usize::contains_bounds(&Bounds { offset: 0, extent: 2 },
&Bounds { offset: 2, extent: 1 }));
assert!(usize::contains_bounds(&Bounds { offset: 0, extent: 5 },
&Bounds { offset: 1, extent: 1 }));
assert!(usize::contains_bounds(&Bounds { offset: 0, extent: 4 },
&Bounds { offset: 0, extent: 4 }));
}
#[rustfmt::skip]
#[test]
fn usize_2dim_contains_bounds() {
assert!(<(usize, usize)>::contains_bounds(&Bounds { offset: (0, 0), extent: (0, 0) },
&Bounds { offset: (0, 0), extent: (0, 0) }));
assert!(<(usize, usize)>::contains_bounds(&Bounds { offset: (0, 0), extent: (1, 1) },
&Bounds { offset: (0, 0), extent: (1, 1) }));
assert!(<(usize, usize)>::contains_bounds(&Bounds { offset: (0, 0), extent: (3, 3) },
&Bounds { offset: (1, 1), extent: (1, 1) }));
assert!(<(usize, usize)>::contains_bounds(&Bounds { offset: (0, 0), extent: (5, 5) },
&Bounds { offset: (1, 1), extent: (3, 3) }));
assert!(!<(usize, usize)>::contains_bounds(&Bounds { offset: (1, 1), extent: (3, 3) },
&Bounds { offset: (0, 0), extent: (2, 2) }));
assert!(!<(usize, usize)>::contains_bounds(&Bounds { offset: (0, 0), extent: (2, 2) },
&Bounds { offset: (1, 1), extent: (2, 2) }));
assert!(!<(usize, usize)>::contains_bounds(&Bounds { offset: (0, 0), extent: (1, 1) },
&Bounds { offset: (2, 2), extent: (1, 1) }));
assert!(!<(usize, usize)>::contains_bounds(&Bounds { offset: (0, 0), extent: (2, 2) },
&Bounds { offset: (2, 2), extent: (1, 1) }));
}
#[rustfmt::skip]
#[test]
fn usize_3dim_contains_bounds() {
assert!(<(usize, usize, usize)>::contains_bounds(&Bounds { offset: (0, 0, 0), extent: (0, 0, 0) },
&Bounds { offset: (0, 0, 0), extent: (0, 0, 0) }));
assert!(<(usize, usize, usize)>::contains_bounds(&Bounds { offset: (0, 0, 0), extent: (1, 1, 1) },
&Bounds { offset: (0, 0, 0), extent: (1, 1, 1) }));
assert!(<(usize, usize, usize)>::contains_bounds(&Bounds { offset: (0, 0, 0), extent: (3, 3, 3) },
&Bounds { offset: (1, 1, 1), extent: (1, 1, 1) }));
assert!(<(usize, usize, usize)>::contains_bounds(&Bounds { offset: (0, 0, 0), extent: (5, 5, 5) },
&Bounds { offset: (1, 1, 1), extent: (3, 3, 3) }));
assert!(!<(usize, usize, usize)>::contains_bounds(&Bounds { offset: (1, 1, 1), extent: (4, 4, 4) },
&Bounds { offset: (0, 0, 0), extent: (3, 3, 3) }));
assert!(!<(usize, usize, usize)>::contains_bounds(&Bounds { offset: (0, 0, 0), extent: (2, 2, 2) },
&Bounds { offset: (1, 1, 1), extent: (2, 2, 2) }));
assert!(!<(usize, usize, usize)>::contains_bounds(&Bounds { offset: (0, 0, 0), extent: (1, 1, 1) },
&Bounds { offset: (2, 2, 2), extent: (1, 1, 1) }));
assert!(!<(usize, usize, usize)>::contains_bounds(&Bounds { offset: (0, 0, 0), extent: (2, 2, 2) },
&Bounds { offset: (2, 2, 2), extent: (1, 1, 1) }));
}
}