#![doc(html_root_url = "https://docs.rs/slice-of-array/0.3.2")]
#![cfg_attr(not(feature = "std"), no_std)]
//! Extension traits for viewing a slice as a slice of arrays or vice versa.
//!
//! Provides the following methods on `[T]`:
//!
//! * **[`nest`]**: `&[T] -> &[[T; n]]`
//! * **[`flat`]**: `&[[T; n]] -> &[T]`
//! * **[`as_array`]**: `&[T] -> &[T; n]` (the reverse is
//! already provided by a coercion)
//! * **`nest_mut`, `flat_mut`, `as_mut_array`** for `&mut [_]`.
//!
//! Altogether, these let you swap between arbitrary representations
//! of contiguous, `T`-aligned streams of `T` data. For instance,
//! to view a `[[i32; 6]; 5]` as a `&[[[i32; 3]; 2]; 5]`,
//! one could write
//!
//! ```
//! # // FIXME: Dumb/confusing example. I actually wrote it wrong
//! # // the first time, calling `flat()` twice because it
//! # // didn't occur to me that the outer '; 5' is already
//! # // automatically eliminated by coercion.
//! # //
//! # // Almost makes a case for providing `.as_slice()`
//! # // as an explicit form of this coercion.
//! #
//! # use slice_of_array::prelude::*;
//! # let _ = || {
//! # let x: [[i32; 6]; 5] = unimplemented!();
//! # let _: &[[[i32; 3]; 2]; 5] =
//! x.flat().nest().nest().as_array()
//! # ;
//! # };
//! ```
//!
//! Type inference generally works quite well, and as long as the
//! final shape is unambiguous there is no need to annotate types
//! in the middle of the method chain.
//!
//! In cases where type inference is unable to determine the target
//! array size, one can use a turbofish: e.g .`x.nest::<[_; 3]>()`.
//!
//! ```
//! use ::slice_of_array::prelude::*;
//!
//! let vec = vec![[2i32, 2, 2], [7, 7, 7], [4, 4, 4], [1, 1, 1]];
//! assert_eq!(vec.flat(), &[2, 2, 2, 7, 7, 7, 4, 4, 4, 1, 1, 1]);
//!
//! // note: this requires an annotation only due to polymorphism in PartialEq
//! let slc = vec.nest::<[_; 2]>();
//! assert_eq!(slc, &[[[2i32, 2, 2], [7, 7, 7]], [[4, 4, 4], [1, 1, 1]]]);
//! ```
//!
//! [`nest`] and [`as_array`] panic on failure rather than returning options.
//! The rationale is that it is believed that these these conversions are
//! seldom needed on arbitrary user data which may be the wrong size; rather,
//! they are most likely used when bridging the gap between APIs that work
//! with flattened slices and APIs that work with slices of arrays.
//!
//! Zero-cost conversions in owned data (e.g. between `Vec`
//! and `Vec<[T; n]>`) are not provided, and are probably impossible
//! in consideration of e.g. custom allocators. If you need to
//! convert between such types, you can use these traits in tandem
//! with `<[T]>::to_vec` to perform a copy:
//!
//! ```
//! # use ::slice_of_array::prelude::*;
//! let vec = vec![[2i32, 2, 2], [7, 7, 7]];
//!
//! // copying into a Vec
//! let flattened = vec.flat().to_vec();
//! assert_eq!(flattened, vec![2i32, 2, 2, 7, 7, 7]);
//! ```
//!
//! [`nest`]: SliceNestExt::nest
//! [`flat`]: SliceFlatExt::flat
//! [`as_array`]: SliceArrayExt::as_array
use core::slice;
pub mod prelude {
//! This module contains extension traits from `slice_of_array`.
//!
//! It is meant to be glob imported, by users who may find it obnoxious to remember
//! the precise names of the traits that each method belongs to.
//!
//! ```rust
//! use slice_of_array::prelude::*;
//! ```
//!
//! `slice_of_array` follows an opinionated policy on what preludes should and should
//! not contain. This prelude will never contain anything that the user will likely
//! want to refer to by name.
pub use super::SliceFlatExt;
pub use super::SliceNestExt;
pub use super::SliceArrayExt;
}
/// Marker trait used in bounds of `Slice{Flat,Nest,Array}Ext`.
///
/// This marks the array types approved for use with `slice_of_array`.
///
/// # Safety
///
/// For any implementation, `Self` must have the same size and
/// alignment as `[Self::Element; Self::LEN]`. Furthermore, you
/// must be comfortable with the possibility of `[Self]` being
/// reinterpreted bitwise as `[[Self::Element; Self::LEN]]` (or
/// vice versa) in any possible context.
///
/// # Notice
///
/// **Please do NOT use this trait in public interfaces in your code.**
///
/// `slice_of_array` is not yet 1.0, is not ready (or even designed)
/// to be used as a public dependency.
///
/// However, feel free to implement this trait on your own private
/// wrapper types around arrays and/or `#[repr(C)]` structs. (these use
/// cases are explicitly supported because the author does it himself,
/// and quite frankly, it's pretty convenient!)
pub unsafe trait IsSliceomorphic: Sized {
type Element;
const LEN: usize;
}
unsafe impl IsSliceomorphic for [T; N] {
type Element = T;
const LEN: usize = N;
}
// Validate some known assumptions of IsSliceomorphic "at runtime,"
// in a manner which should get optimized into thin air.
fn validate_alignment_and_size() {
use core::mem::{align_of, size_of};
assert_eq!(
align_of::(),
align_of::(),
);
assert_eq!(
V::LEN * size_of::(),
size_of::(),
);
}
/// Permits viewing a slice of arrays as a flat slice.
///
/// # Panics
///
/// Will panic if the new length exceeds `usize::MAX`.
/// (in practice, this can only happen with zero-sized types)
///
/// # Implementors
///
/// The methods are available on `&[[T; n]]` and `&mut [[T; n]]`
/// for all `T` and `n`. Of course, they are also available on
/// `Vec<[T; n]>` and any other type that derefs or unsizes to `[[T; n]]`.
///
/// `&[[T; 0]]` does support being flattened into an empty slice, however,
/// please do mind that the inverse operation [`SliceNestExt::nest`] will panic
/// (as it cannot possibly recover the original length of the slice).
///
/// # Notice
///
/// The existence of this trait is an implementation detail. Future versions may
/// split it up, merge or rename it.
/// Therefore, **please do NOT use this trait as a generic bound in your code.**
///
/// (Prefer `[V] where V: `[`IsSliceomorphic`]`` instead)
pub trait SliceFlatExt {
/// View `&[[T; n]]` as `&[T]`.
fn flat(&self) -> &[T];
/// View `&mut [[T; n]]` as `&mut [T]`
fn flat_mut(&mut self) -> &mut [T];
}
/// Permits viewing a slice as a slice of arrays.
///
/// The new array dimension can often be inferred.
/// When it is not, a turbofish can be used: `.nest::<[_; 3]>()`.
///
/// # Panics
///
/// All methods panic if the input length is not divisible by `n`.
///
/// # Implementors
///
/// The methods are available on `&[T]` and `&mut [T]` for all `T`.
/// Of course, they are also available on `Vec` and any other type
/// that derefs or unsizes to `[T]`.
///
/// **The implementation for `N=0` panics!** (even if the length of the slice is
/// zero, as in this case the length of the nested slice would be degenerate)
///
/// # Notice
///
/// The existence of this trait is an implementation detail. Future versions may
/// split it up, merge or rename it.
/// Therefore, **please do NOT use this trait as a generic bound in your code.**
///
/// (Prefer ` where V: `[`IsSliceomorphic`]`` instead)
pub trait SliceNestExt {
/// View `&[T]` as `&[[T; n]]` without copying.
fn nest>(&self) -> &[V];
/// View `&mut [T]` as `&mut [[T; n]]` without copying.
fn nest_mut>(&mut self) -> &mut [V];
}
/// Permits viewing a slice as an array.
///
/// The output array length can often be inferred.
/// When it is not, a turbofish can be used: `.as_array::<[_; 3]>()`.
///
/// # Panics
///
/// All methods panic if the slice is not exactly the requested length.
///
/// # Implementors
///
/// The methods are available on `&[T]` and `&mut [T]` for all `T`.
/// Of course, they are also available on `Vec` and any other type
/// that derefs or unsizes to `[T]`.
///
/// # Notice
///
/// The existence of this trait is an implementation detail. Future versions may
/// split it up, merge or rename it.
/// Therefore, **please do NOT use this trait as a generic bound in your code.**
///
/// (Prefer `V where V: `[`IsSliceomorphic`]`` instead)
pub trait SliceArrayExt {
/// View `&[T]` as `&[T; n]`.
fn as_array>(&self) -> &V;
/// View `&mut [T]` as `&mut [T; n]`.
fn as_mut_array>(&mut self) -> &mut V;
/// Clone `&[T]` to `[T; n]`.
///
/// This is provided because `.as_array().clone()` tends to cause trouble for
/// type inference.
fn to_array>(&self) -> V where V: Clone
{ self.as_array::().clone() }
}
impl SliceFlatExt for [V] {
fn flat(&self) -> &[V::Element] {
let new_len = checked_compute_flattened_len::(self.len());
// UNSAFETY: (::core::slice::from_raw_parts)
// - pointer must be non-null (even for zero-length)
// - pointer must be aligned
// - pointer must be valid for given size
// - lifetimes are unchecked
unsafe {
slice::from_raw_parts(
self.as_ptr() as *const _,
new_len,
)
}
}
fn flat_mut(&mut self) -> &mut [V::Element] {
let new_len = checked_compute_flattened_len::(self.len());
// UNSAFETY: (::core::slice::from_raw_parts_mut)
// - pointer must be non-null (even for zero-length)
// - pointer must be aligned
// - pointer must be valid for given size
// - lifetimes are unchecked
// - aliasing guarantees of &mut are unchecked
unsafe {
slice::from_raw_parts_mut(
self.as_mut_ptr() as *mut _,
new_len,
)
}
}
}
#[inline(always)]
fn checked_compute_flattened_len(len: usize) -> usize {
validate_alignment_and_size::();
if core::mem::size_of::() == 0 {
usize::checked_mul(len, V::LEN)
.expect("overflow when computing length of flattened array")
} else {
// Given that each value occupies at least one byte, the mere existence
// of the slice ensures that this will not overflow.
len * V::LEN
}
}
impl SliceNestExt for [T] {
fn nest>(&self) -> &[V] {
let new_len = checked_compute_nested_len::(self.len(), "&");
// UNSAFETY: (core::slice::from_raw_parts)
// - pointer must be non-null (even for zero-length)
// - pointer must be aligned
// - pointer must be valid for given size
// - lifetimes are unchecked
unsafe { slice::from_raw_parts(
self.as_ptr() as *const _,
new_len,
)}
}
fn nest_mut>(&mut self) -> &mut [V] {
let new_len = checked_compute_nested_len::(self.len(), "&mut ");
// UNSAFETY: (core::slice::from_raw_parts_mut)
// - pointer must be non-null (even for zero-length)
// - pointer must be aligned
// - pointer must be valid for given size
// - lifetimes are unchecked
// - aliasing guarantees of &mut are unchecked
unsafe { slice::from_raw_parts_mut(
self.as_mut_ptr() as *mut _,
new_len,
)}
}
}
#[inline(always)]
fn checked_compute_nested_len(len: usize, prefix: &str) -> usize {
validate_alignment_and_size::();
assert_ne!(
0, V::LEN,
"cannot nest arrays of length 0",
);
assert_eq!(
0, len % V::LEN,
"cannot view slice of length {} as {}[[_; {}]]",
len, prefix, V::LEN,
);
len / V::LEN
}
impl SliceArrayExt for [T] {
fn as_array>(&self) -> &V {
validate_as_array_assumptions::(self.len(), "&");
// &self.nest()[0] // <-- would not work for V::LEN = 0
// UNSAFETY: (<*const T>::as_ref)
// - pointer must be aligned
// - pointer must be valid for given size
// - lifetimes are unchecked
unsafe { (self.as_ptr() as *const V).as_ref().unwrap() }
}
fn as_mut_array>(&mut self) -> &mut V {
validate_as_array_assumptions::(self.len(), "&mut ");
// &mut self.nest_mut()[0] // <-- would not work for V::LEN = 0
// UNSAFETY: (<*mut T>::as_mut)
// - pointer must be aligned
// - pointer must be valid for given size
// - lifetimes are unchecked
// - aliasing guarantees of &mut are unchecked
unsafe { (self.as_mut_ptr() as *mut V).as_mut().unwrap() }
}
}
#[inline(always)]
fn validate_as_array_assumptions(len: usize, prefix: &str) {
validate_alignment_and_size::();
assert_eq!(
len, V::LEN,
"cannot view slice of length {} as {}[_; {}]",
len, prefix, V::LEN,
);
}
#[cfg(test)]
mod tests {
pub use super::prelude::*;
#[test]
fn inference_lattice() {
// Checks that chaining nest().nest() or nest().as_array()
// can be done without explicit annotations on the first method call.
let v: &mut [()] = &mut [(); 9];
{ let _: &[[(); 3]; 3] = v.nest().as_array(); }
{ let _: &[[[(); 3]; 3]] = v.nest().nest(); }
{ let _: &mut [[(); 3]; 3] = v.nest_mut().as_mut_array(); }
{ let _: &mut [[[(); 3]; 3]] = v.nest_mut().nest_mut(); }
{ let _: [[(); 3]; 3] = v.nest().to_array(); }
#[cfg(feature = "std")]
{ let _: Vec<[(); 3]> = v.nest().to_vec(); }
}
#[test]
fn test_flat_zst_and_non_zst() {
let v: &mut [_] = &mut [[(); 234]; 456];
assert_eq!(v.flat(), &[(); 234*456] as &[()]);
assert_eq!(v.flat_mut(), &[(); 234*456] as &[()]);
let v: &mut [_] = &mut [[1; 23]; 45];
assert_eq!(v.flat(), &[1; 23*45] as &[i32]);
assert_eq!(v.flat_mut(), &[1; 23*45] as &[i32]);
}
#[test]
fn test_flat_zero() {
let v: &mut [[(); 0]] = &mut [[(); 0]; 6];
assert_eq!(v.flat(), &[] as &[()]);
assert_eq!(v.flat_mut(), &[] as &[()]);
}
#[test]
fn test_array_zero() {
let v: &mut [[(); 0]] = &mut [[], [], [], []];
assert_eq!(v.flat(), &[] as &[()]);
assert_eq!(v.flat_mut(), &[] as &[()]);
}
mod failures {
use super::super::*;
// Two usizes that overflow when multiplied together.
const BIG_1: usize = 0x30;
const BIG_2: usize = usize::MAX >> 4;
#[test]
#[should_panic(expected = "overflow when computing length")]
fn flat_zst_overflow() {
let v: &[_] = &[[(); BIG_1]; BIG_2];
let _: &[()] = v.flat();
}
#[test]
#[should_panic(expected = "overflow when computing length")]
fn flat_mut_zst_overflow() {
let v: &mut [_] = &mut [[(); BIG_1]; BIG_2];
let _: &mut [()] = v.flat_mut();
}
#[test]
#[should_panic(expected = "cannot view slice of length 8")]
fn nest_not_multiple() {
let v: &[_] = &[(); 8];
let _: &[[(); 3]] = v.nest();
}
#[test]
#[should_panic(expected = "cannot view slice of length 8")]
fn nest_mut_not_multiple() {
let v: &mut [_] = &mut [(); 8];
let _: &mut [[(); 3]] = v.nest_mut();
}
#[test]
#[should_panic(expected = "cannot nest arrays of length 0")]
fn nest_zero() {
let v: &[_] = &[(); 0];
let _: &[[(); 0]] = v.nest();
}
#[test]
#[should_panic(expected = "cannot nest arrays of length 0")]
fn nest_mut_zero() {
let v: &mut [_] = &mut [(); 0];
let _: &mut [[(); 0]] = v.nest_mut();
}
// bad array size tests;
// we try converting slices of length 1 or 6 into a length 3 array.
// These sizes were chosen to catch accidental acceptance in
// the case of sizes that divide evenly
#[test]
#[should_panic(expected = "cannot view slice of length 1")]
fn as_array_too_small() {
let v: &[_] = &[(); 1];
let _: &[(); 3] = v.as_array();
}
#[test]
#[should_panic(expected = "cannot view slice of length 6")]
fn as_array_too_large() {
let v: &[_] = &[(); 6];
let _: &[(); 3] = v.as_array();
}
#[test]
#[should_panic(expected = "cannot view slice of length 6")]
fn as_array_bad_zero() {
let v: &[_] = &[(); 6];
let _: &[(); 0] = v.as_array();
}
#[test]
#[should_panic(expected = "cannot view slice of length 1")]
fn as_mut_array_too_small() {
let v: &mut [_] = &mut [(); 1];
let _: &mut [(); 3] = v.as_mut_array();
}
#[test]
#[should_panic(expected = "cannot view slice of length 6")]
fn as_mut_array_too_large() {
let v: &mut [_] = &mut [(); 6];
let _: &mut [(); 3] = v.as_mut_array();
}
#[test]
#[should_panic(expected = "cannot view slice of length 6")]
fn as_mut_array_bad_zero() {
let v: &mut [_] = &mut [(); 6];
let _: &[(); 0] = v.as_mut_array();
}
}
mod dox {
#[test]
fn test_readme_version() {
version_sync::assert_markdown_deps_updated!("README.md");
}
#[test]
fn test_html_root_url() {
version_sync::assert_html_root_url_updated!("lib.rs");
}
}
}