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}