slice_of_array/
lib.rs

1#![doc(html_root_url = "https://docs.rs/slice-of-array/0.3.2")]
2#![cfg_attr(not(feature = "std"), no_std)]
3
4//! Extension traits for viewing a slice as a slice of arrays or vice versa.
5//!
6//! Provides the following methods on `[T]`:
7//!
8//!  * **[`nest`]**: `&[T] -> &[[T; n]]`
9//!  * **[`flat`]**: `&[[T; n]] -> &[T]`
10//!  * **[`as_array`]**: `&[T] -> &[T; n]` (the reverse is
11//!    already provided by a coercion)
12//!  * **`nest_mut`, `flat_mut`, `as_mut_array`** for `&mut [_]`.
13//!
14//! Altogether, these let you swap between arbitrary representations
15//! of contiguous, `T`-aligned streams of `T` data.  For instance,
16//! to view a `[[i32; 6]; 5]` as a `&[[[i32; 3]; 2]; 5]`,
17//! one could write
18//!
19//! ```
20//! # // FIXME: Dumb/confusing example. I actually wrote it wrong
21//! # //        the first time, calling `flat()` twice because it
22//! # //        didn't occur to me that the outer '; 5' is already
23//! # //        automatically eliminated by coercion.
24//! # //
25//! # //        Almost makes a case for providing `.as_slice()`
26//! # //        as an explicit form of this coercion.
27//! #
28//! # use slice_of_array::prelude::*;
29//! # let _ = || {
30//! #     let x: [[i32; 6]; 5] = unimplemented!();
31//! #     let _: &[[[i32; 3]; 2]; 5] =
32//! x.flat().nest().nest().as_array()
33//! #     ;
34//! # };
35//! ```
36//!
37//! Type inference generally works quite well, and as long as the
38//! final shape is unambiguous there is no need to annotate types
39//! in the middle of the method chain.
40//!
41//! In cases where type inference is unable to determine the target
42//! array size, one can use a turbofish: e.g .`x.nest::<[_; 3]>()`.
43//!
44//! ```
45//! use ::slice_of_array::prelude::*;
46//!
47//! let vec = vec![[2i32, 2, 2], [7, 7, 7], [4, 4, 4], [1, 1, 1]];
48//! assert_eq!(vec.flat(), &[2, 2, 2, 7, 7, 7, 4, 4, 4, 1, 1, 1]);
49//!
50//! // note: this requires an annotation only due to polymorphism in PartialEq
51//! let slc = vec.nest::<[_; 2]>();
52//! assert_eq!(slc, &[[[2i32, 2, 2], [7, 7, 7]], [[4, 4, 4], [1, 1, 1]]]);
53//! ```
54//!
55//! [`nest`] and [`as_array`] panic on failure rather than returning options.
56//! The rationale is that it is believed that these these conversions are
57//! seldom needed on arbitrary user data which may be the wrong size; rather,
58//! they are most likely used when bridging the gap between APIs that work
59//! with flattened slices and APIs that work with slices of arrays.
60//!
61//! Zero-cost conversions in owned data (e.g. between `Vec<T>`
62//! and `Vec<[T; n]>`) are not provided, and are probably impossible
63//! in consideration of e.g. custom allocators. If you need to
64//! convert between such types, you can use these traits in tandem
65//! with `<[T]>::to_vec` to perform a copy:
66//!
67//! ```
68//! # use ::slice_of_array::prelude::*;
69//! let vec = vec![[2i32, 2, 2], [7, 7, 7]];
70//!
71//! // copying into a Vec<i32>
72//! let flattened = vec.flat().to_vec();
73//! assert_eq!(flattened, vec![2i32, 2, 2, 7, 7, 7]);
74//! ```
75//!
76//! [`nest`]: SliceNestExt::nest
77//! [`flat`]: SliceFlatExt::flat
78//! [`as_array`]: SliceArrayExt::as_array
79
80use core::slice;
81
82pub mod prelude {
83    //! This module contains extension traits from `slice_of_array`.
84    //!
85    //! It is meant to be glob imported, by users who may find it obnoxious to remember
86    //! the precise names of the traits that each method belongs to.
87    //!
88    //! ```rust
89    //! use slice_of_array::prelude::*;
90    //! ```
91    //!
92    //! `slice_of_array` follows an opinionated policy on what preludes should and should
93    //! not contain. This prelude will never contain anything that the user will likely
94    //! want to refer to by name.
95
96    pub use super::SliceFlatExt;
97    pub use super::SliceNestExt;
98    pub use super::SliceArrayExt;
99}
100
101/// Marker trait used in bounds of `Slice{Flat,Nest,Array}Ext`.
102///
103/// This marks the array types approved for use with `slice_of_array`.
104///
105/// # Safety
106///
107/// For any implementation, `Self` must have the same size and
108/// alignment as `[Self::Element; Self::LEN]`.  Furthermore, you
109/// must be comfortable with the possibility of `[Self]` being
110/// reinterpreted bitwise as `[[Self::Element; Self::LEN]]` (or
111/// vice versa) in any possible context.
112///
113/// # Notice
114///
115/// **Please do NOT use this trait in public interfaces in your code.**
116///
117/// `slice_of_array` is not yet 1.0, is not ready (or even designed)
118/// to be used as a public dependency.
119///
120/// However, feel free to implement this trait on your own private
121/// wrapper types around arrays and/or `#[repr(C)]` structs. (these use
122/// cases are explicitly supported because the author does it himself,
123/// and quite frankly, it's pretty convenient!)
124pub unsafe trait IsSliceomorphic: Sized {
125    type Element;
126    const LEN: usize;
127}
128
129unsafe impl<T, const N: usize> IsSliceomorphic for [T; N] {
130    type Element = T;
131    const LEN: usize = N;
132}
133
134// Validate some known assumptions of IsSliceomorphic "at runtime,"
135//  in a manner which should get optimized into thin air.
136fn validate_alignment_and_size<V: IsSliceomorphic>() {
137    use core::mem::{align_of, size_of};
138
139    assert_eq!(
140        align_of::<V::Element>(),
141        align_of::<V>(),
142    );
143
144    assert_eq!(
145        V::LEN * size_of::<V::Element>(),
146        size_of::<V>(),
147    );
148}
149
150/// Permits viewing a slice of arrays as a flat slice.
151///
152/// # Panics
153///
154/// Will panic if the new length exceeds `usize::MAX`.
155/// (in practice, this can only happen with zero-sized types)
156///
157/// # Implementors
158///
159/// The methods are available on `&[[T; n]]` and `&mut [[T; n]]`
160/// for all `T` and `n`. Of course, they are also available on
161/// `Vec<[T; n]>` and any other type that derefs or unsizes to `[[T; n]]`.
162///
163/// `&[[T; 0]]` does support being flattened into an empty slice, however,
164/// please do mind that the inverse operation [`SliceNestExt::nest`] will panic
165/// (as it cannot possibly recover the original length of the slice).
166///
167/// # Notice
168///
169/// The existence of this trait is an implementation detail.  Future versions may
170/// split it up, merge or rename it.
171/// Therefore, **please do NOT use this trait as a generic bound in your code.**
172///
173/// (Prefer `[V] where V: `[`IsSliceomorphic`]`<Element=T>` instead)
174pub trait SliceFlatExt<T> {
175    /// View `&[[T; n]]` as `&[T]`.
176    fn flat(&self) -> &[T];
177
178    /// View `&mut [[T; n]]` as `&mut [T]`
179    fn flat_mut(&mut self) -> &mut [T];
180}
181
182/// Permits viewing a slice as a slice of arrays.
183///
184/// The new array dimension can often be inferred.
185/// When it is not, a turbofish can be used: `.nest::<[_; 3]>()`.
186///
187/// # Panics
188///
189/// All methods panic if the input length is not divisible by `n`.
190///
191/// # Implementors
192///
193/// The methods are available on `&[T]` and `&mut [T]` for all `T`.
194/// Of course, they are also available on `Vec<T>` and any other type
195/// that derefs or unsizes to `[T]`.
196///
197/// **The implementation for `N=0` panics!** (even if the length of the slice is
198/// zero, as in this case the length of the nested slice would be degenerate)
199///
200/// # Notice
201///
202/// The existence of this trait is an implementation detail.  Future versions may
203/// split it up, merge or rename it.
204/// Therefore, **please do NOT use this trait as a generic bound in your code.**
205///
206/// (Prefer `<V> where V: `[`IsSliceomorphic`]`<Element=T>` instead)
207pub trait SliceNestExt<T> {
208    /// View `&[T]` as `&[[T; n]]` without copying.
209    fn nest<V: IsSliceomorphic<Element=T>>(&self) -> &[V];
210
211    /// View `&mut [T]` as `&mut [[T; n]]` without copying.
212    fn nest_mut<V: IsSliceomorphic<Element=T>>(&mut self) -> &mut [V];
213}
214
215/// Permits viewing a slice as an array.
216///
217/// The output array length can often be inferred.
218/// When it is not, a turbofish can be used: `.as_array::<[_; 3]>()`.
219///
220/// # Panics
221///
222/// All methods panic if the slice is not exactly the requested length.
223///
224/// # Implementors
225///
226/// The methods are available on `&[T]` and `&mut [T]` for all `T`.
227/// Of course, they are also available on `Vec<T>` and any other type
228/// that derefs or unsizes to `[T]`.
229///
230/// # Notice
231///
232/// The existence of this trait is an implementation detail.  Future versions may
233/// split it up, merge or rename it.
234/// Therefore, **please do NOT use this trait as a generic bound in your code.**
235///
236/// (Prefer `V where V: `[`IsSliceomorphic`]`<Element=T>` instead)
237pub trait SliceArrayExt<T> {
238    /// View `&[T]` as `&[T; n]`.
239    fn as_array<V: IsSliceomorphic<Element=T>>(&self) -> &V;
240
241    /// View `&mut [T]` as `&mut [T; n]`.
242    fn as_mut_array<V: IsSliceomorphic<Element=T>>(&mut self) -> &mut V;
243
244    /// Clone `&[T]` to `[T; n]`.
245    ///
246    /// This is provided because `.as_array().clone()` tends to cause trouble for
247    /// type inference.
248    fn to_array<V: IsSliceomorphic<Element=T>>(&self) -> V where V: Clone
249    { self.as_array::<V>().clone() }
250}
251
252impl<V: IsSliceomorphic> SliceFlatExt<V::Element> for [V] {
253    fn flat(&self) -> &[V::Element] {
254        let new_len = checked_compute_flattened_len::<V>(self.len());
255
256        // UNSAFETY: (::core::slice::from_raw_parts)
257        // - pointer must be non-null (even for zero-length)
258        // - pointer must be aligned
259        // - pointer must be valid for given size
260        // - lifetimes are unchecked
261        unsafe {
262            slice::from_raw_parts(
263                self.as_ptr() as *const _,
264                new_len,
265            )
266        }
267    }
268
269    fn flat_mut(&mut self) -> &mut [V::Element] {
270        let new_len = checked_compute_flattened_len::<V>(self.len());
271
272        // UNSAFETY: (::core::slice::from_raw_parts_mut)
273        // - pointer must be non-null (even for zero-length)
274        // - pointer must be aligned
275        // - pointer must be valid for given size
276        // - lifetimes are unchecked
277        // - aliasing guarantees of &mut are unchecked
278        unsafe {
279            slice::from_raw_parts_mut(
280                self.as_mut_ptr() as *mut _,
281                new_len,
282            )
283        }
284    }
285}
286
287#[inline(always)]
288fn checked_compute_flattened_len<V: IsSliceomorphic>(len: usize) -> usize {
289    validate_alignment_and_size::<V>();
290
291    if core::mem::size_of::<V::Element>() == 0 {
292        usize::checked_mul(len, V::LEN)
293            .expect("overflow when computing length of flattened array")
294    } else {
295        // Given that each value occupies at least one byte, the mere existence
296        // of the slice ensures that this will not overflow.
297        len * V::LEN
298    }
299}
300
301impl<T> SliceNestExt<T> for [T] {
302    fn nest<V: IsSliceomorphic<Element=T>>(&self) -> &[V] {
303        let new_len = checked_compute_nested_len::<V>(self.len(), "&");
304
305        // UNSAFETY: (core::slice::from_raw_parts)
306        // - pointer must be non-null (even for zero-length)
307        // - pointer must be aligned
308        // - pointer must be valid for given size
309        // - lifetimes are unchecked
310        unsafe { slice::from_raw_parts(
311            self.as_ptr() as *const _,
312            new_len,
313        )}
314    }
315
316    fn nest_mut<V: IsSliceomorphic<Element=T>>(&mut self) -> &mut [V] {
317        let new_len = checked_compute_nested_len::<V>(self.len(), "&mut ");
318
319        // UNSAFETY: (core::slice::from_raw_parts_mut)
320        // - pointer must be non-null (even for zero-length)
321        // - pointer must be aligned
322        // - pointer must be valid for given size
323        // - lifetimes are unchecked
324        // - aliasing guarantees of &mut are unchecked
325        unsafe { slice::from_raw_parts_mut(
326            self.as_mut_ptr() as *mut _,
327            new_len,
328        )}
329    }
330}
331
332#[inline(always)]
333fn checked_compute_nested_len<V: IsSliceomorphic>(len: usize, prefix: &str) -> usize {
334    validate_alignment_and_size::<V>();
335    assert_ne!(
336        0, V::LEN,
337        "cannot nest arrays of length 0",
338    );
339    assert_eq!(
340        0, len % V::LEN,
341        "cannot view slice of length {} as {}[[_; {}]]",
342        len, prefix, V::LEN,
343    );
344
345    len / V::LEN
346}
347
348impl<T> SliceArrayExt<T> for [T] {
349    fn as_array<V: IsSliceomorphic<Element=T>>(&self) -> &V {
350        validate_as_array_assumptions::<V>(self.len(), "&");
351
352        // &self.nest()[0]  // <-- would not work for V::LEN = 0
353
354        // UNSAFETY: (<*const T>::as_ref)
355        // - pointer must be aligned
356        // - pointer must be valid for given size
357        // - lifetimes are unchecked
358        unsafe { (self.as_ptr() as *const V).as_ref().unwrap() }
359    }
360
361    fn as_mut_array<V: IsSliceomorphic<Element=T>>(&mut self) -> &mut V {
362        validate_as_array_assumptions::<V>(self.len(), "&mut ");
363
364        // &mut self.nest_mut()[0]  // <-- would not work for V::LEN = 0
365
366        // UNSAFETY: (<*mut T>::as_mut)
367        // - pointer must be aligned
368        // - pointer must be valid for given size
369        // - lifetimes are unchecked
370        // - aliasing guarantees of &mut are unchecked
371        unsafe { (self.as_mut_ptr() as *mut V).as_mut().unwrap() }
372    }
373}
374
375#[inline(always)]
376fn validate_as_array_assumptions<V: IsSliceomorphic>(len: usize, prefix: &str) {
377    validate_alignment_and_size::<V>();
378    assert_eq!(
379        len, V::LEN,
380        "cannot view slice of length {} as {}[_; {}]",
381        len, prefix, V::LEN,
382    );
383}
384
385#[cfg(test)]
386mod tests {
387    pub use super::prelude::*;
388
389    #[test]
390    fn inference_lattice() {
391        // Checks that chaining nest().nest() or nest().as_array()
392        // can be done without explicit annotations on the first method call.
393        let v: &mut [()] = &mut [(); 9];
394
395        { let _: &[[(); 3]; 3] = v.nest().as_array(); }
396        { let _: &[[[(); 3]; 3]] = v.nest().nest(); }
397        { let _: &mut [[(); 3]; 3] = v.nest_mut().as_mut_array(); }
398        { let _: &mut [[[(); 3]; 3]] = v.nest_mut().nest_mut(); }
399        { let _: [[(); 3]; 3] = v.nest().to_array(); }
400
401        #[cfg(feature = "std")]
402        { let _: Vec<[(); 3]> = v.nest().to_vec(); }
403    }
404
405    #[test]
406    fn test_flat_zst_and_non_zst() {
407        let v: &mut [_] = &mut [[(); 234]; 456];
408        assert_eq!(v.flat(), &[(); 234*456] as &[()]);
409        assert_eq!(v.flat_mut(), &[(); 234*456] as &[()]);
410
411        let v: &mut [_] = &mut [[1; 23]; 45];
412        assert_eq!(v.flat(), &[1; 23*45] as &[i32]);
413        assert_eq!(v.flat_mut(), &[1; 23*45] as &[i32]);
414    }
415
416    #[test]
417    fn test_flat_zero() {
418        let v: &mut [[(); 0]] = &mut [[(); 0]; 6];
419        assert_eq!(v.flat(), &[] as &[()]);
420        assert_eq!(v.flat_mut(), &[] as &[()]);
421    }
422
423    #[test]
424    fn test_array_zero() {
425        let v: &mut [[(); 0]] = &mut [[], [], [], []];
426        assert_eq!(v.flat(), &[] as &[()]);
427        assert_eq!(v.flat_mut(), &[] as &[()]);
428    }
429
430    mod failures {
431        use super::super::*;
432
433        // Two usizes that overflow when multiplied together.
434        const BIG_1: usize = 0x30;
435        const BIG_2: usize = usize::MAX >> 4;
436
437        #[test]
438        #[should_panic(expected = "overflow when computing length")]
439        fn flat_zst_overflow() {
440            let v: &[_] = &[[(); BIG_1]; BIG_2];
441            let _: &[()] = v.flat();
442        }
443
444        #[test]
445        #[should_panic(expected = "overflow when computing length")]
446        fn flat_mut_zst_overflow() {
447            let v: &mut [_] = &mut [[(); BIG_1]; BIG_2];
448            let _: &mut [()] = v.flat_mut();
449        }
450
451        #[test]
452        #[should_panic(expected = "cannot view slice of length 8")]
453        fn nest_not_multiple() {
454            let v: &[_] = &[(); 8];
455            let _: &[[(); 3]] = v.nest();
456        }
457
458        #[test]
459        #[should_panic(expected = "cannot view slice of length 8")]
460        fn nest_mut_not_multiple() {
461            let v: &mut [_] = &mut [(); 8];
462            let _: &mut [[(); 3]] = v.nest_mut();
463        }
464
465        #[test]
466        #[should_panic(expected = "cannot nest arrays of length 0")]
467        fn nest_zero() {
468            let v: &[_] = &[(); 0];
469            let _: &[[(); 0]] = v.nest();
470        }
471
472        #[test]
473        #[should_panic(expected = "cannot nest arrays of length 0")]
474        fn nest_mut_zero() {
475            let v: &mut [_] = &mut [(); 0];
476            let _: &mut [[(); 0]] = v.nest_mut();
477        }
478
479        // bad array size tests;
480        //  we try converting slices of length 1 or 6 into a length 3 array.
481        //  These sizes were chosen to catch accidental acceptance in
482        //    the case of sizes that divide evenly
483        #[test]
484        #[should_panic(expected = "cannot view slice of length 1")]
485        fn as_array_too_small() {
486            let v: &[_] = &[(); 1];
487            let _: &[(); 3] = v.as_array();
488        }
489
490        #[test]
491        #[should_panic(expected = "cannot view slice of length 6")]
492        fn as_array_too_large() {
493            let v: &[_] = &[(); 6];
494            let _: &[(); 3] = v.as_array();
495        }
496
497        #[test]
498        #[should_panic(expected = "cannot view slice of length 6")]
499        fn as_array_bad_zero() {
500            let v: &[_] = &[(); 6];
501            let _: &[(); 0] = v.as_array();
502        }
503
504        #[test]
505        #[should_panic(expected = "cannot view slice of length 1")]
506        fn as_mut_array_too_small() {
507            let v: &mut [_] = &mut [(); 1];
508            let _: &mut [(); 3] = v.as_mut_array();
509        }
510
511        #[test]
512        #[should_panic(expected = "cannot view slice of length 6")]
513        fn as_mut_array_too_large() {
514            let v: &mut [_] = &mut [(); 6];
515            let _: &mut [(); 3] = v.as_mut_array();
516        }
517
518        #[test]
519        #[should_panic(expected = "cannot view slice of length 6")]
520        fn as_mut_array_bad_zero() {
521            let v: &mut [_] = &mut [(); 6];
522            let _: &[(); 0] = v.as_mut_array();
523        }
524    }
525
526    mod dox {
527        #[test]
528        fn test_readme_version() {
529            version_sync::assert_markdown_deps_updated!("README.md");
530        }
531
532        #[test]
533        fn test_html_root_url() {
534            version_sync::assert_html_root_url_updated!("lib.rs");
535        }
536    }
537}