commit 620f926fdd9c5ba2aa8d7b5465f887dbbf19aa6c Author: Leonora Tindall Date: Mon Jun 14 14:34:49 2021 -0500 v0.1 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..a4cefd7 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "nslice" +version = "0.1.0" +authors = ["Leonora Tindall "] +edition = "2018" +description = "Structures for interpreting slices of variable length as arrays" +license = "MIT" +keywords = ["memory", "slice", "array"] +categories = ["data-structures"] +repository = "https://git.nora.codes/nora/nslice" diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..a444568 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,152 @@ +//! Structures for interpreting slices of variable length as arrays. +//! +//! `nslice` provides `MinSlice` and `ExactSlice` for representing slices known to have +//! either exactly or at least some compile-time-known number of values. +//! ``` +//! use nslice::MinSlice; +//! let slice = &[1, 2, 3, 4, 5, 6]; +//! let minslice: &MinSlice<_, 3> = MinSlice::from_slice(slice).unwrap(); +//! assert_eq!(minslice.tail.len(), 3); +//! assert_eq!(minslice.head[0], 1); +//! assert_eq!(minslice.tail[2], 6); +//! ``` +use std::slice; + +/// A reference to a region of memory which is known to contain `N` or more elements +/// of type `T`. +/// +/// Much like `[T]` itself, it is not possible to construct an owned `MinSlice`. +/// `MinSlice` is merely a way of reinterpreting an existing slice +/// (`&[T]` or `&mut [T]`), and it is exactly the same size as a slice: +/// one pointer and one `usize`. +pub struct MinSlice { + /// The bounded region of memory. Exactly `N` `T`s. + pub head: [T; N], + /// Zero or more remaining `T`s after the `N` in the bounded region. + pub tail: [T], +} + +/// A reference to a region of memory which contains exactly `N` elements of type `T`. +/// +/// `ExactSlice` is merely a way of reinterpreting an existing slice +/// (`&[T]` or `&mut [T]`), but because there is no need to store a length +/// for retrieval at runtime, its representation consists of just one pointer. + +pub struct ExactSlice { + /// The bounded region of memory. Exactly `N` `T`s. + head: [T; N] +} + +impl MinSlice { + /// Produce a `&MinSlice` from a slice of `T`s. + /// Returns `None` if there are not enough elements in `slice`. + pub fn from_slice(slice: &[T]) -> Option<&MinSlice> { + if slice.len() >= N { + Some(unsafe { Self::from_slice_unchecked(slice) }) + } else { + None + } + } + + /// Produce a `&mut MinSlice` from a mutable slice of `T`s. + /// Returns `None` if there are not enough elements in `slice`. + pub fn from_mut(slice: &mut [T]) -> Option<&mut MinSlice> { + if slice.len() >= N { + Some(unsafe { Self::from_mut_unchecked(slice) }) + } else { + None + } + } + + /// Produce a `&MinSlice` from a slice of `T`s without checking its length. + /// + /// # Safety + /// + /// The caller is responsible for upholding the length invariant + /// `slice.len() >= N`, in addition to all normal slice invariants. + pub unsafe fn from_slice_unchecked(slice: &[T]) -> &MinSlice { + let resized = slice::from_raw_parts(slice.as_ptr(), slice.len() - N); + &*(resized as *const [T] as *const MinSlice) + } + + /// Produce a `&mut MinSlice` from a slice of `T`s without checking its length. + /// + /// # Safety + /// + /// The caller is responsible for upholding the length invariant + /// `slice.len() >= N`, in addition to all normal slice invariants. + pub unsafe fn from_mut_unchecked(slice: &mut [T]) -> &mut MinSlice { + let resized = slice::from_raw_parts_mut(slice.as_mut_ptr(), slice.len() - N); + &mut *(resized as *mut [T] as *mut MinSlice) + } +} + +impl ExactSlice { + /// Produce an `&ExactSlice` from a slice of `T`s. + /// Returns `None` if there are not the correct number of elements in `slice`. + pub fn from_slice(slice: &[T]) -> Option<&ExactSlice> { + if slice.len() == N { + Some(unsafe { Self::from_slice_unchecked(slice) }) + } else { + None + } + } + + /// Produce an `&mut ExactSlice` from a mutable slice of `T`s. + /// Returns `None` if there are not the correct number of elements in `slice`. + pub fn from_mut(slice: &mut [T]) -> Option<&mut ExactSlice> { + if slice.len() == N { + Some(unsafe { Self::from_mut_unchecked(slice) }) + } else { + None + } + } + + /// Produce a `&MinSlice` from this `&ExactSlice`. Its `tail` will be empty. + pub fn as_min_slice(&self) -> Option<&MinSlice> { + MinSlice::from_slice(&self.head[..]) + } + + /// Produce a `&mut MinSlice` from this `&min ExactSlice`. Its `tail` will be empty. + pub fn as_mut_min_slice(&mut self) -> Option<&mut MinSlice> { + MinSlice::from_mut(&mut self.head[..]) + } + + /// Produce an `&ExactSlice` from a slice of `T`s without checking its length. + /// + /// # Safety + /// + /// The caller is responsible for upholding the length invariant + /// `slice.len() == N`, in addition to all normal slice invariants. + pub unsafe fn from_slice_unchecked(slice: &[T]) -> &ExactSlice { + &*(slice.as_ptr() as *const ExactSlice) + } + + /// Produce an `&mut ExactSlice` from a slice of `T`s without checking its length. + /// + /// # Safety + /// + /// The caller is responsible for upholding the length invariant + /// `slice.len() == N`, in addition to all normal slice invariants. + pub unsafe fn from_mut_unchecked(slice: &mut [T]) -> &mut ExactSlice { + &mut *(slice.as_mut_ptr() as *mut ExactSlice) + } +} + +#[test] +fn basic_min_success() { + let slice = &[1, 2, 3, 4, 5, 6]; + let minslice: &MinSlice<_, 3> = MinSlice::from_slice(slice).unwrap(); + assert_eq!(minslice.tail.len(), 3); + assert_eq!(minslice.head[0], 1); + assert_eq!(minslice.tail[2], 6); +} + + +#[test] +fn basic_min_failure() { + let slice = &[1, 2, 3, 4, 5, 6]; + let minslice: Option<&MinSlice<_, 7>> = MinSlice::from_slice(slice); + assert!(minslice.is_none()); +} +