const generic expr based fixed length array manipulation crate
add slice and split
bendn 2024-05-30
parent ddb9ee5 · commit 7218b15
-rw-r--r--Cargo.toml2
-rw-r--r--src/lib.rs61
-rw-r--r--src/slice.rs136
3 files changed, 183 insertions, 16 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 31cadbc..e4f6085 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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]>"]
diff --git a/src/lib.rs b/src/lib.rs
index d6e79c6..4eda659 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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]);
+}