const generic expr based fixed length array manipulation crate
Diffstat (limited to 'src/slice.rs')
| -rw-r--r-- | src/slice.rs | 136 |
1 files changed, 136 insertions, 0 deletions
diff --git a/src/slice.rs b/src/slice.rs new file mode 100644 index 0000000..867020a --- /dev/null +++ b/src/slice.rs @@ -0,0 +1,136 @@ +use core::ops::{RangeFrom, RangeInclusive, RangeTo, RangeToInclusive}; +#[derive(Eq, PartialEq, Copy, Clone)] +pub enum Range { + Range(usize, usize), + RangeFrom(usize), + RangeTo(usize), +} + +impl Range { + pub const fn range<const N: usize>(self) -> (usize, usize) { + match self { + Range::Range(begin, end) => (begin, end), + Range::RangeFrom(begin) => (begin, N), + Range::RangeTo(end) => (0, end), + } + } + pub const fn length<const N: usize>(self) -> usize { + let (begin, end) = self.range::<N>(); + end - begin + } + pub const fn valid<const N: usize>(self) -> usize { + (self.range::<N>().1 <= N) as usize + } +} + +#[const_trait] +#[diagnostic::on_unimplemented( + message = "{Self} is not a valid range type", + label = "use a correct range type, such as {{Range(x..y), RangeInclusive(x..=y), RangeTo(..x), RangeToInclusive(..=x)}}" +)] +pub trait Ranged { + fn range(self) -> Range; +} +impl const Ranged for core::ops::Range<usize> { + fn range(self) -> Range { + Range::Range(self.start, self.end) + } +} +impl const Ranged for RangeInclusive<usize> { + fn range(self) -> Range { + Range::Range(*self.start(), *self.end() + 1) + } +} + +impl const Ranged for RangeFrom<usize> { + fn range(self) -> Range { + Range::RangeFrom(self.start) + } +} +impl const Ranged for RangeTo<usize> { + fn range(self) -> Range { + Range::RangeTo(self.end) + } +} +impl const Ranged for RangeToInclusive<usize> { + fn range(self) -> Range { + Range::RangeTo(self.end + 1) + } +} +/// Constifies a range. For use with [`slice`](Slice::slice). +/// +/// Takes a type in the form {[`Range`], [`RangeInclusive`], [`RangeTo`], [`RangeToInclusive`]}. +#[allow(private_bounds)] +pub const fn r<T: ~const Ranged>(x: T) -> Range { + Ranged::range(x) +} + +impl core::marker::ConstParamTy for Range {} + +#[const_trait] +/// Slicing arrays up. +pub trait Slice<T, const N: usize> { + /// Slices the array. + /// Compile time checked. + /// ``` + /// # use atools::prelude::*; + /// let x = atools::range::<5>(); + /// assert_eq!(*x.slice::<{ r(2..=4) }>(), [2, 3, 4]); + /// // x.slice::<{ r(..10) }>(); // ERROR + /// ``` + fn slice<const RANGE: Range>(&self) -> &[T; RANGE.length::<N>()] + where + // comptime length check + [(); RANGE.valid::<N>() - 1]:; + + /// Yields all but the last element. + /// ``` + /// # use atools::prelude::*; + /// let x = atools::range::<5>(); + /// assert!(*x.init() == atools::range::<4>()); + /// ``` + fn init(&self) -> &[T; N - 1]; + /// Yields all but the first element. + /// ``` + /// # use atools::prelude::*; + /// let x = atools::range::<5>(); + /// assert!(*x.tail() == atools::range::<4>().map(|x| x + 1)); + /// ``` + fn tail(&self) -> &[T; N - 1]; +} + +const unsafe fn slice<T, const N: usize, const TO: usize>(x: &[T; N], begin: usize) -> &[T; TO] { + // SAFETY: up to caller + unsafe { &*x.as_ptr().add(begin).cast::<[T; TO]>() } +} + +impl<T, const N: usize> const Slice<T, N> for [T; N] { + fn slice<const RANGE: Range>(&self) -> &[T; RANGE.length::<N>()] + where + [(); RANGE.valid::<N>() - 1]:, + { + // SAFETY: the validity check ensures that the array will be in bounds. + unsafe { slice::<T, N, { RANGE.length::<N>() }>(self, RANGE.range::<N>().0) } + } + + fn init(&self) -> &[T; N - 1] { + unsafe { slice::<T, N, { N - 1 }>(self, 0) } + } + + fn tail(&self) -> &[T; N - 1] { + unsafe { slice::<T, N, { N - 1 }>(self, 1) } + } +} + +#[test] +fn slicing() { + let x = [1, 2, 3]; + let &[y] = x.slice::<{ r(2..) }>(); + assert_eq!(y, 3); + let &[y, z] = x.slice::<{ r(1..=2) }>(); + assert_eq!([y, z], [2, 3]); + let &y = x.slice::<{ r(..=2) }>(); + assert_eq!(x, y); + let &y = x.slice::<{ r(..2) }>(); + assert_eq!(y, [1, 2]); +} |