const generic expr based fixed length array manipulation crate
| -rw-r--r-- | Cargo.toml | 2 | ||||
| -rw-r--r-- | src/lib.rs | 61 | ||||
| -rw-r--r-- | src/slice.rs | 136 |
3 files changed, 183 insertions, 16 deletions
@@ -1,6 +1,6 @@ [package] name = "atools" -version = "0.1.4" +version = "0.1.5" edition = "2021" description = "const generic expr based fixed length array manipulation" authors = ["bend-n <[email protected]>"] @@ -2,6 +2,7 @@ #![cfg_attr(not(test), no_std)] #![allow(incomplete_features, internal_features)] #![feature( + adt_const_params, effects, const_refs_to_cell, generic_const_exprs, @@ -9,7 +10,6 @@ iter_intersperse, const_trait_impl, maybe_uninit_array_assume_init, - inline_const, array_windows, iter_map_windows )] @@ -23,6 +23,17 @@ clippy::use_self, missing_docs )] +use core::{ + array::from_fn, + intrinsics::transmute_unchecked, + mem::{ManuallyDrop as MD, MaybeUninit as MU}, +}; +pub mod pervasive; +mod slice; +mod tuple; +#[doc(inline)] +pub use slice::Slice; +pub use tuple::*; /// The prelude. You should /// ``` @@ -31,8 +42,8 @@ pub mod prelude { #[doc(inline)] pub use super::{ - pervasive::prelude::*, range, splat, Array, ArrayTools, Chunked, CollectArray, Couple, - DropFront, Flatten, Join, Pop, Trunc, Tuple, + pervasive::prelude::*, range, slice::r, slice::Slice, splat, Array, ArrayTools, Chunked, + CollectArray, Couple, DropFront, Flatten, Join, Pop, Split, Trunc, Tuple, }; #[doc(inline)] pub use core::array::from_fn; @@ -40,14 +51,13 @@ pub mod prelude { #[repr(C)] struct Pair<X, Y>(X, Y); - -use core::{ - array::from_fn, intrinsics::transmute_unchecked, mem::ManuallyDrop as MD, - mem::MaybeUninit as MU, -}; -pub mod pervasive; -mod tuple; -pub use tuple::*; +impl<X, Y> From<Pair<X, Y>> for (X, Y) { + fn from(p: Pair<X, Y>) -> Self { + // SAFETY: this is unsound, as the layout of the tuple may change. + // crater? you there yet? + unsafe { transmute_unchecked(p) } + } +} /// Convenience function for when clonage is required; prefer `[T; N]` if possible. Also useful if `N` should be inferred. pub fn splat<T: Clone, const N: usize>(a: T) -> [T; N] { @@ -116,14 +126,16 @@ pub trait Pop<T, const N: usize> { } impl<T, const N: usize> const Pop<T, N> for [T; N] { + #[doc(alias = "head")] fn pop_front(self) -> (T, [T; N - 1]) { - // SAFETY: hi crater - unsafe { core::intrinsics::transmute_unchecked(self) } + // SAFETY: the layout is alright. + unsafe { core::intrinsics::transmute_unchecked::<_, Pair<_, _>>(self) }.into() } + #[doc(alias = "last")] fn pop(self) -> ([T; N - 1], T) { - // SAFETY: i am evil - unsafe { core::intrinsics::transmute_unchecked(self) } + // SAFETY: the layout is still alright. + unsafe { core::intrinsics::transmute_unchecked::<_, Pair<_, _>>(self) }.into() } } @@ -275,6 +287,25 @@ impl<T, const N: usize, const N2: usize> const Flatten<T, N, N2> for [[T; N]; N2 } } +#[const_trait] +/// Splitting arrays up. +pub trait Split<T, const N: usize> { + /// Splits the array into twain. + /// ``` + /// # use atools::prelude::*; + /// let x = [1u8, 2, 3]; + /// let ([x], [y, z]) = x.split::<1>(); + /// ``` + fn split<const AT: usize>(self) -> ([T; AT], [T; N - AT]); +} + +impl<T, const N: usize> Split<T, N> for [T; N] { + fn split<const AT: usize>(self) -> ([T; AT], [T; N - AT]) { + // SAFETY: N - AT overflows when AT > N so the size of the returned "array" is the same. + unsafe { transmute_unchecked::<_, Pair<_, _>>(self) }.into() + } +} + /// Array tools. pub trait ArrayTools<T, const N: usize> { /// Skip `BY` elements. 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]); +} |