slice_as_array/
lib.rs

1// Copyright 2016 Peter Reid. See the COPYRIGHT file at the top-level
2// directory of this distribution.
3//
4// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
7// option. This file may not be copied, modified, or distributed
8// except according to those terms.
9
10//! This crate provides macros to convert from slices, which have lengths
11//! that are stored and checked at runtime, into arrays, which have lengths
12//! known at compile time. This can make types more expressive (e.g.
13//! `&[u8; 32]` instead of `&[u8]`) and helps the compiler omit bounds checks.
14//!
15//! `slice_as_array!(xs, [u32; 4])` returns `Some(&[u32; 4])` if `xs` was
16//! a slice of length 4, or `None` otherwise.
17//!
18//! `slice_as_array_mut!(ys, [String; 7])` returns `Some(&mut [String; 7])`
19//!  if `ys` was a slice of length 7, or `None` otherwise.
20//!
21//! `slice_to_array_clone!(zs, [String; 4]` returns `Some([String; 4])`
22//! if `zs` was a slice of length 4, or `None otherwise. The passed-in slice
23//! remains intact and its elements are cloned.
24//!
25//! For most users, stating a dependency on this is simply:
26//!
27//! ```ignore
28//! [dependencies]
29//! slice_as_array "1.1.0"
30//! ```
31//! To support being called from a `#![no_std]` crate, this crate has a feature
32//! named `with_std` that is on by default. A `#![no_std]` crate should use:
33//!
34//! ```ignore
35//! [dependencies]
36//! slice_as_array = { version = "1.1.0", default-features = false }
37//! ```
38//!
39//! Example usage:
40//!
41//! ```ignore
42//! #[macro_use] extern crate slice_as_array;
43//!
44//! fn slice_as_hash(xs: &[u8]) -> &[u8; 32] {
45//!     slice_as_array!(xs, [u8; 32]).expect("bad hash length")
46//! }
47//!
48//! fn mutate_chunk(xs: &mut [u32; 10]) {
49//!     // ...
50//! }
51//!
52//! fn mutate_chunks(xs: &mut [u32; 100]) {
53//!     for chunk in xs.chunks_mut(10).map(|c| slice_as_array_mut!(c, [u32; 10]).unwrap() ) {
54//!         mutate_chunk(chunk)
55//!     }
56//! }
57//! ```
58//!
59
60#[doc(hidden)]
61pub mod reexport {
62    #[inline] pub fn clone<T: Clone>(source: T) -> T { source.clone() }
63    #[inline] pub unsafe fn ptr_write<T>(dst: *mut T, src: T) { ::std::ptr::write(dst, src) }
64    #[inline] pub unsafe fn ptr_read<T>(src: *const T) -> T { ::std::ptr::read(src) }
65    #[inline] pub fn forget<T>(t: T) { ::std::mem::forget(t) }
66    #[inline] pub unsafe fn uninitialized<T>() -> T { ::std::mem::uninitialized() }
67}
68
69#[cfg(feature="use_std")]
70#[macro_export]
71#[doc(hidden)]
72macro_rules! slice_as_array_transmute {
73    ($slice:expr) => { ::std::mem::transmute($slice) }
74}
75
76#[cfg(not(feature="use_std"))]
77#[macro_export]
78macro_rules! slice_as_array_transmute {
79    ($slice:expr) => { ::core::mem::transmute($slice) }
80}
81
82
83// In slice_as_array[_mut], the inner function is to set the lifetime of the created array.
84
85/// Convert a slice to an array.
86/// `slice_as_array!(slice, [element_type; array_length]) -> Option<&[element_type; array_length]>`
87#[macro_export]
88macro_rules! slice_as_array {
89    ($slice:expr, [$t:ty ; $len:expr] ) => {{
90        unsafe fn this_transmute(xs: &[$t]) -> &[$t; $len] {
91            slice_as_array_transmute!(xs.as_ptr())
92        }
93
94        let s: &[$t] = $slice;
95        if s.len() == $len {
96            Some( unsafe { this_transmute(s) } )
97        } else {
98            None
99        }
100    }}
101}
102
103/// Convert a mutable slice to a mutable array.
104/// `slice_as_array_mut!(mutable_slice, [element_type; array_length]) -> Option<&mut [element_type; array_length]>`
105#[macro_export]
106macro_rules! slice_as_array_mut {
107    ($slice:expr, [$t:ty ; $len:expr] ) => {{
108        unsafe fn this_transmute(xs: &mut [$t]) -> &mut [$t; $len] {
109            slice_as_array_transmute!(xs.as_mut_ptr())
110        }
111
112        let s: &mut [$t] = $slice;
113        if s.len() == $len {
114            Some( unsafe { this_transmute(s) } )
115        } else {
116            None
117        }
118    }}
119}
120
121/// Convert a slice to an array by cloning each element.
122/// `slice_to_array_clone!(slice, [element_type; array_length]) -> Option<[element_type; array_length]>`
123#[macro_export]
124macro_rules! slice_to_array_clone {
125    ($slice:expr, [$t:ty ; $len:expr] ) => {{
126        struct SafeArrayInitialization {
127            array: Option<[$t; $len]>,
128            count: usize,
129        }
130        impl SafeArrayInitialization {
131            fn new() -> Self {
132                SafeArrayInitialization { array: Some(unsafe { $crate::reexport::uninitialized() }), count: 0 }
133            }
134            fn init_from_slice(mut self, slice: &[$t]) -> Option<[$t; $len]> {
135                {
136                    let array_mut: &mut [$t] = self.array.as_mut().unwrap().as_mut();
137                    if slice.len() != array_mut.len() {
138                        return None;
139                    }
140                    debug_assert_eq!(self.count, 0);
141                    for (val, ptr) in slice.iter().zip(array_mut.iter_mut()) {
142                        let val = $crate::reexport::clone(*val);
143                        unsafe { $crate::reexport::ptr_write(ptr, val) };
144                        self.count += 1;
145                    }
146                }
147                self.array.take()
148            }
149        }
150        impl Drop for SafeArrayInitialization {
151            fn drop(&mut self) {
152                if let Some(mut array) = self.array.take() {
153                    let count = self.count;
154                    {
155                        for ptr in array.as_mut()[..count].iter_mut() {
156                            unsafe { $crate::reexport::ptr_read(ptr) };
157                        }
158                    }
159                    $crate::reexport::forget(array);
160                }
161            }
162        }
163
164        SafeArrayInitialization::new().init_from_slice($slice)
165    }}
166}
167
168#[cfg(test)]
169mod test {
170    #[test]
171    fn correct_length() {
172        let xs: [u32; 6] = [1, 2, 4, 8, 16, 32];
173        let xs_prefix: &[u32; 3] = slice_as_array!(&xs[1..4], [u32; 3]).expect("Length mismatch");
174        assert_eq!(xs_prefix, &[2, 4, 8]);
175    }
176
177    #[test]
178    fn incorrect_length() {
179        let xs: [u32; 6] = [1, 2, 4, 8, 16, 32];
180        let xs_prefix: Option<&[u32; 8]> = slice_as_array!(&xs[1..4], [u32; 8]);
181        assert_eq!(xs_prefix, None);
182    }
183
184    #[test]
185    #[should_panic]
186    fn overlong_length() {
187        let xs: [u32; 6] = [1, 2, 4, 8, 16, 32];
188        let xs_prefix: Option<&[u32; 8]> = slice_as_array!(&xs[0..8], [u32; 8]);
189        assert_eq!(xs_prefix, None);
190    }
191
192    #[test]
193    fn zero_length() {
194        let xs: [u32; 6] = [1, 2, 4, 8, 16, 32];
195        let xs_prefix: &[u32; 0] = slice_as_array!(&xs[1..1], [u32; 0]).unwrap();
196        assert_eq!(xs_prefix, &[]);
197    }
198
199    #[test]
200    fn array_of_arrays() {
201        let xs: [[u8; 4]; 3] = [
202            [10,11,12,13],
203            [20,21,22,23],
204            [30,31,32,33],
205        ];
206
207        let xs_suffix: &[[u8;4]; 2] = slice_as_array!(&xs[1..], [[u8; 4]; 2]).unwrap();
208        assert_eq!(xs_suffix[0][0], 20);
209        assert_eq!(xs_suffix[1][3], 33);
210    }
211
212    #[test]
213    fn clone_correct() {
214        let xs: [u32; 6] = [1, 2, 4, 8, 16, 32];
215        let xs_middle: [u32; 3] = slice_to_array_clone!(&xs[1..4], [u32; 3]).expect("Length mismatch");
216        assert_eq!(xs_middle, [2, 4, 8]);
217    }
218
219    #[test]
220    fn clone_wrong_length() {
221        let xs: [u32; 6] = [1, 2, 4, 8, 16, 32];
222        let xs_middle: Option<[u32; 3]> = slice_to_array_clone!(&xs[1..5], [u32; 3]);
223        assert_eq!(xs_middle, None);
224    }
225}