]>
Commit | Line | Data |
---|---|---|
f20569fa XL |
1 | //! This package contains just four macros, which enable the creation |
2 | //! of array references to portions of arrays or slices (or things | |
3 | //! that can be sliced). | |
4 | //! | |
5 | //! # Examples | |
6 | //! | |
7 | //! Here is a simple example of slicing and dicing a slice into array | |
8 | //! references with these macros. Here we implement a simple | |
9 | //! little-endian conversion from bytes to `u16`, and demonstrate code | |
10 | //! that uses `array_ref!` to extract an array reference from a larger | |
11 | //! array. Note that the documentation for each macro also has an | |
12 | //! example of its use. | |
13 | //! | |
14 | //! ``` | |
15 | //! #[macro_use] | |
16 | //! extern crate arrayref; | |
17 | //! | |
18 | //! fn read_u16(bytes: &[u8; 2]) -> u16 { | |
19 | //! bytes[0] as u16 + ((bytes[1] as u16) << 8) | |
20 | //! } | |
21 | //! // ... | |
22 | //! # fn main() { | |
23 | //! let data = [0,1,2,3,4,0,6,7,8,9]; | |
24 | //! assert_eq!(256, read_u16(array_ref![data,0,2])); | |
25 | //! assert_eq!(4, read_u16(array_ref![data,4,2])); | |
26 | //! # } | |
27 | //! ``` | |
28 | #![deny(warnings)] | |
29 | #![no_std] | |
30 | ||
31 | #[cfg(test)] | |
32 | #[macro_use] | |
33 | extern crate std; | |
34 | ||
35 | /// You can use `array_ref` to generate an array reference to a subset | |
36 | /// of a sliceable bit of data (which could be an array, or a slice, | |
37 | /// or a Vec). | |
38 | /// | |
39 | /// **Panics** if the slice is out of bounds. | |
40 | /// | |
41 | /// ``` | |
42 | /// #[macro_use] | |
43 | /// extern crate arrayref; | |
44 | /// | |
45 | /// fn read_u16(bytes: &[u8; 2]) -> u16 { | |
46 | /// bytes[0] as u16 + ((bytes[1] as u16) << 8) | |
47 | /// } | |
48 | /// // ... | |
49 | /// # fn main() { | |
50 | /// let data = [0,1,2,3,4,0,6,7,8,9]; | |
51 | /// assert_eq!(256, read_u16(array_ref![data,0,2])); | |
52 | /// assert_eq!(4, read_u16(array_ref![data,4,2])); | |
53 | /// # } | |
54 | /// ``` | |
55 | ||
56 | #[macro_export] | |
57 | macro_rules! array_ref { | |
58 | ($arr:expr, $offset:expr, $len:expr) => {{ | |
59 | { | |
60 | #[inline] | |
61 | unsafe fn as_array<T>(slice: &[T]) -> &[T; $len] { | |
62 | &*(slice.as_ptr() as *const [_; $len]) | |
63 | } | |
64 | let offset = $offset; | |
65 | let slice = & $arr[offset..offset + $len]; | |
66 | #[allow(unused_unsafe)] | |
67 | unsafe { | |
68 | as_array(slice) | |
69 | } | |
70 | } | |
71 | }} | |
72 | } | |
73 | ||
74 | /// You can use `array_refs` to generate a series of array references | |
75 | /// to an input array reference. The idea is if you want to break an | |
76 | /// array into a series of contiguous and non-overlapping arrays. | |
77 | /// `array_refs` is a bit funny in that it insists on slicing up the | |
78 | /// *entire* array. This is intentional, as I find it handy to make | |
79 | /// me ensure that my sub-arrays add up to the entire array. This | |
80 | /// macro will *never* panic, since the sizes are all checked at | |
81 | /// compile time. | |
82 | /// | |
83 | /// Note that unlike `array_ref!`, `array_refs` *requires* that the | |
84 | /// first argument be an array reference. The following arguments are | |
85 | /// the lengths of each subarray you wish a reference to. The total | |
86 | /// of these arguments *must* equal the size of the array itself. | |
87 | /// | |
88 | /// ``` | |
89 | /// #[macro_use] | |
90 | /// extern crate arrayref; | |
91 | /// | |
92 | /// fn read_u16(bytes: &[u8; 2]) -> u16 { | |
93 | /// bytes[0] as u16 + ((bytes[1] as u16) << 8) | |
94 | /// } | |
95 | /// // ... | |
96 | /// # fn main() { | |
97 | /// let data = [0,1,2,3,4,0,6,7]; | |
98 | /// let (a,b,c) = array_refs![&data,2,2,4]; | |
99 | /// assert_eq!(read_u16(a), 256); | |
100 | /// assert_eq!(read_u16(b), 3*256+2); | |
101 | /// assert_eq!(*c, [4,0,6,7]); | |
102 | /// # } | |
103 | /// ``` | |
104 | #[macro_export] | |
105 | macro_rules! array_refs { | |
106 | ( $arr:expr, $( $pre:expr ),* ; .. ; $( $post:expr ),* ) => {{ | |
107 | { | |
108 | use std::slice; | |
109 | #[inline] | |
110 | #[allow(unused_assignments)] | |
111 | #[allow(eval_order_dependence)] | |
112 | unsafe fn as_arrays<T>(a: &[T]) -> ( $( &[T; $pre], )* &[T], $( &[T; $post], )*) { | |
113 | let min_len = $( $pre + )* $( $post + )* 0; | |
114 | let var_len = a.len() - min_len; | |
115 | assert!(a.len() >= min_len); | |
116 | let mut p = a.as_ptr(); | |
117 | ( $( { | |
118 | let aref = & *(p as *const [T; $pre]); | |
119 | p = p.offset($pre as isize); | |
120 | aref | |
121 | } ),* , { | |
122 | let sl = slice::from_raw_parts(p as *const T, var_len); | |
123 | p = p.offset(var_len as isize); | |
124 | sl | |
125 | }, $( { | |
126 | let aref = & *(p as *const [T; $post]); | |
127 | p = p.offset($post as isize); | |
128 | aref | |
129 | } ),*) | |
130 | } | |
131 | let input = $arr; | |
132 | #[allow(unused_unsafe)] | |
133 | unsafe { | |
134 | as_arrays(input) | |
135 | } | |
136 | } | |
137 | }}; | |
138 | ( $arr:expr, $( $len:expr ),* ) => {{ | |
139 | { | |
140 | #[inline] | |
141 | #[allow(unused_assignments)] | |
142 | #[allow(eval_order_dependence)] | |
143 | unsafe fn as_arrays<T>(a: &[T; $( $len + )* 0 ]) -> ( $( &[T; $len], )* ) { | |
144 | let mut p = a.as_ptr(); | |
145 | ( $( { | |
146 | let aref = &*(p as *const [T; $len]); | |
147 | p = p.offset($len as isize); | |
148 | aref | |
149 | } ),* ) | |
150 | } | |
151 | let input = $arr; | |
152 | #[allow(unused_unsafe)] | |
153 | unsafe { | |
154 | as_arrays(input) | |
155 | } | |
156 | } | |
157 | }} | |
158 | } | |
159 | ||
160 | ||
161 | /// You can use `mut_array_refs` to generate a series of mutable array | |
162 | /// references to an input mutable array reference. The idea is if | |
163 | /// you want to break an array into a series of contiguous and | |
164 | /// non-overlapping mutable array references. Like `array_refs!`, | |
165 | /// `mut_array_refs!` is a bit funny in that it insists on slicing up | |
166 | /// the *entire* array. This is intentional, as I find it handy to | |
167 | /// make me ensure that my sub-arrays add up to the entire array. | |
168 | /// This macro will *never* panic, since the sizes are all checked at | |
169 | /// compile time. | |
170 | /// | |
171 | /// Note that unlike `array_mut_ref!`, `mut_array_refs` *requires* | |
172 | /// that the first argument be a mutable array reference. The | |
173 | /// following arguments are the lengths of each subarray you wish a | |
174 | /// reference to. The total of these arguments *must* equal the size | |
175 | /// of the array itself. Also note that this macro allows you to take | |
176 | /// out multiple mutable references to a single object, which is both | |
177 | /// weird and powerful. | |
178 | /// | |
179 | /// ``` | |
180 | /// #[macro_use] | |
181 | /// extern crate arrayref; | |
182 | /// | |
183 | /// fn write_u16(bytes: &mut [u8; 2], num: u16) { | |
184 | /// bytes[0] = num as u8; | |
185 | /// bytes[1] = (num >> 8) as u8; | |
186 | /// } | |
187 | /// fn write_u32(bytes: &mut [u8; 4], num: u32) { | |
188 | /// bytes[0] = num as u8; | |
189 | /// bytes[1] = (num >> 8) as u8; // this is buggy to save space... | |
190 | /// } | |
191 | /// // ... | |
192 | /// # fn main() { | |
193 | /// let mut data = [0,1,2,3,4,0,6,7]; | |
194 | /// let (a,b,c) = mut_array_refs![&mut data,2,2,4]; | |
195 | /// // let's write out some nice prime numbers! | |
196 | /// write_u16(a, 37); | |
197 | /// write_u16(b, 73); | |
198 | /// write_u32(c, 137); // approximate inverse of the fine structure constant! | |
199 | /// # } | |
200 | /// ``` | |
201 | #[macro_export] | |
202 | macro_rules! mut_array_refs { | |
203 | ( $arr:expr, $( $pre:expr ),* ; .. ; $( $post:expr ),* ) => {{ | |
204 | { | |
205 | use std::slice; | |
206 | #[inline] | |
207 | #[allow(unused_assignments)] | |
208 | #[allow(eval_order_dependence)] | |
209 | unsafe fn as_arrays<T>(a: &mut [T]) -> ( $( &mut [T; $pre], )* &mut [T], $( &mut [T; $post], )*) { | |
210 | let min_len = $( $pre + )* $( $post + )* 0; | |
211 | let var_len = a.len() - min_len; | |
212 | assert!(a.len() >= min_len); | |
213 | let mut p = a.as_mut_ptr(); | |
214 | ( $( { | |
215 | let aref = &mut *(p as *mut [T; $pre]); | |
216 | p = p.offset($pre as isize); | |
217 | aref | |
218 | } ),* , { | |
219 | let sl = slice::from_raw_parts_mut(p as *mut T, var_len); | |
220 | p = p.offset(var_len as isize); | |
221 | sl | |
222 | }, $( { | |
223 | let aref = &mut *(p as *mut [T; $post]); | |
224 | p = p.offset($post as isize); | |
225 | aref | |
226 | } ),*) | |
227 | } | |
228 | let input = $arr; | |
229 | #[allow(unused_unsafe)] | |
230 | unsafe { | |
231 | as_arrays(input) | |
232 | } | |
233 | } | |
234 | }}; | |
235 | ( $arr:expr, $( $len:expr ),* ) => {{ | |
236 | { | |
237 | #[inline] | |
238 | #[allow(unused_assignments)] | |
239 | #[allow(eval_order_dependence)] | |
240 | unsafe fn as_arrays<T>(a: &mut [T; $( $len + )* 0 ]) -> ( $( &mut [T; $len], )* ) { | |
241 | let mut p = a.as_mut_ptr(); | |
242 | ( $( { | |
243 | let aref = &mut *(p as *mut [T; $len]); | |
244 | p = p.offset($len as isize); | |
245 | aref | |
246 | } ),* ) | |
247 | } | |
248 | let input = $arr; | |
249 | #[allow(unused_unsafe)] | |
250 | unsafe { | |
251 | as_arrays(input) | |
252 | } | |
253 | } | |
254 | }}; | |
255 | } | |
256 | ||
257 | /// You can use `array_mut_ref` to generate a mutable array reference | |
258 | /// to a subset of a sliceable bit of data (which could be an array, | |
259 | /// or a slice, or a Vec). | |
260 | /// | |
261 | /// **Panics** if the slice is out of bounds. | |
262 | /// | |
263 | /// ``` | |
264 | /// #[macro_use] | |
265 | /// extern crate arrayref; | |
266 | /// | |
267 | /// fn write_u16(bytes: &mut [u8; 2], num: u16) { | |
268 | /// bytes[0] = num as u8; | |
269 | /// bytes[1] = (num >> 8) as u8; | |
270 | /// } | |
271 | /// // ... | |
272 | /// # fn main() { | |
273 | /// let mut data = [0,1,2,3,4,0,6,7,8,9]; | |
274 | /// write_u16(array_mut_ref![data,0,2], 1); | |
275 | /// write_u16(array_mut_ref![data,2,2], 5); | |
276 | /// assert_eq!(*array_ref![data,0,4], [1,0,5,0]); | |
277 | /// *array_mut_ref![data,4,5] = [4,3,2,1,0]; | |
278 | /// assert_eq!(data, [1,0,5,0,4,3,2,1,0,9]); | |
279 | /// # } | |
280 | /// ``` | |
281 | #[macro_export] | |
282 | macro_rules! array_mut_ref { | |
283 | ($arr:expr, $offset:expr, $len:expr) => {{ | |
284 | { | |
285 | #[inline] | |
286 | unsafe fn as_array<T>(slice: &mut [T]) -> &mut [T; $len] { | |
287 | &mut *(slice.as_mut_ptr() as *mut [_; $len]) | |
288 | } | |
289 | let offset = $offset; | |
290 | let slice = &mut $arr[offset..offset + $len]; | |
291 | #[allow(unused_unsafe)] | |
292 | unsafe { | |
293 | as_array(slice) | |
294 | } | |
295 | } | |
296 | }} | |
297 | } | |
298 | ||
299 | ||
300 | #[cfg(test)] | |
301 | mod test { | |
302 | ||
303 | extern crate quickcheck; | |
304 | ||
305 | use std::vec::Vec; | |
306 | ||
307 | // use super::*; | |
308 | ||
309 | #[test] | |
310 | #[should_panic] | |
311 | fn checks_bounds() { | |
312 | let foo: [u8; 11] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; | |
313 | let bar = array_ref!(foo, 1, 11); | |
314 | println!("I am checking that I can dereference bar[0] = {}", bar[0]); | |
315 | } | |
316 | ||
317 | #[test] | |
318 | fn simple_case_works() { | |
319 | fn check(expected: [u8; 3], actual: &[u8; 3]) { | |
320 | for (e, a) in (&expected).iter().zip(actual.iter()) { | |
321 | assert_eq!(e, a) | |
322 | } | |
323 | } | |
324 | let mut foo: [u8; 11] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; | |
325 | { | |
326 | let bar = array_ref!(foo, 2, 3); | |
327 | check([2, 3, 4], bar); | |
328 | } | |
329 | check([0, 1, 2], array_ref!(foo, 0, 3)); | |
330 | fn zero2(x: &mut [u8; 2]) { | |
331 | x[0] = 0; | |
332 | x[1] = 0; | |
333 | } | |
334 | zero2(array_mut_ref!(foo, 8, 2)); | |
335 | check([0, 0, 10], array_ref!(foo, 8, 3)); | |
336 | } | |
337 | ||
338 | ||
339 | #[test] | |
340 | fn check_array_ref_5() { | |
341 | fn f(data: Vec<u8>, offset: usize) -> quickcheck::TestResult { | |
342 | if data.len() < offset + 5 { | |
343 | return quickcheck::TestResult::discard(); | |
344 | } | |
345 | let out = array_ref!(data, offset, 5); | |
346 | quickcheck::TestResult::from_bool(out.len() == 5) | |
347 | } | |
348 | quickcheck::quickcheck(f as fn(Vec<u8>, usize) -> quickcheck::TestResult); | |
349 | } | |
350 | ||
351 | #[test] | |
352 | fn check_array_ref_out_of_bounds_5() { | |
353 | fn f(data: Vec<u8>, offset: usize) -> quickcheck::TestResult { | |
354 | if data.len() >= offset + 5 { | |
355 | return quickcheck::TestResult::discard(); | |
356 | } | |
357 | quickcheck::TestResult::must_fail(move || { | |
358 | array_ref!(data, offset, 5); | |
359 | }) | |
360 | } | |
361 | quickcheck::quickcheck(f as fn(Vec<u8>, usize) -> quickcheck::TestResult); | |
362 | } | |
363 | ||
364 | #[test] | |
365 | fn check_array_mut_ref_7() { | |
366 | fn f(mut data: Vec<u8>, offset: usize) -> quickcheck::TestResult { | |
367 | if data.len() < offset + 7 { | |
368 | return quickcheck::TestResult::discard(); | |
369 | } | |
370 | let out = array_mut_ref!(data, offset, 7); | |
371 | out[6] = 3; | |
372 | quickcheck::TestResult::from_bool(out.len() == 7) | |
373 | } | |
374 | quickcheck::quickcheck(f as fn(Vec<u8>, usize) -> quickcheck::TestResult); | |
375 | } | |
376 | ||
377 | ||
378 | #[test] | |
379 | fn check_array_mut_ref_out_of_bounds_32() { | |
380 | fn f(mut data: Vec<u8>, offset: usize) -> quickcheck::TestResult { | |
381 | if data.len() >= offset + 32 { | |
382 | return quickcheck::TestResult::discard(); | |
383 | } | |
384 | quickcheck::TestResult::must_fail(move || { | |
385 | array_mut_ref!(data, offset, 32); | |
386 | }) | |
387 | } | |
388 | quickcheck::quickcheck(f as fn(Vec<u8>, usize) -> quickcheck::TestResult); | |
389 | } | |
390 | ||
391 | ||
392 | #[test] | |
393 | fn test_5_array_refs() { | |
394 | let mut data: [usize; 128] = [0; 128]; | |
395 | for i in 0..128 { | |
396 | data[i] = i; | |
397 | } | |
398 | let data = data; | |
399 | let (a,b,c,d,e) = array_refs!(&data, 1, 14, 3, 100, 10); | |
400 | assert_eq!(a.len(), 1 as usize); | |
401 | assert_eq!(b.len(), 14 as usize); | |
402 | assert_eq!(c.len(), 3 as usize); | |
403 | assert_eq!(d.len(), 100 as usize); | |
404 | assert_eq!(e.len(), 10 as usize); | |
405 | assert_eq!(a, array_ref![data, 0, 1]); | |
406 | assert_eq!(b, array_ref![data, 1, 14]); | |
407 | assert_eq!(c, array_ref![data, 15, 3]); | |
408 | assert_eq!(e, array_ref![data, 118, 10]); | |
409 | } | |
410 | ||
411 | #[test] | |
412 | fn test_5_array_refs_dotdot() { | |
413 | let mut data: [usize; 128] = [0; 128]; | |
414 | for i in 0..128 { | |
415 | data[i] = i; | |
416 | } | |
417 | let data = data; | |
418 | let (a,b,c,d,e) = array_refs!(&data, 1, 14, 3; ..; 10); | |
419 | assert_eq!(a.len(), 1 as usize); | |
420 | assert_eq!(b.len(), 14 as usize); | |
421 | assert_eq!(c.len(), 3 as usize); | |
422 | assert_eq!(d.len(), 100 as usize); | |
423 | assert_eq!(e.len(), 10 as usize); | |
424 | assert_eq!(a, array_ref![data, 0, 1]); | |
425 | assert_eq!(b, array_ref![data, 1, 14]); | |
426 | assert_eq!(c, array_ref![data, 15, 3]); | |
427 | assert_eq!(e, array_ref![data, 118, 10]); | |
428 | } | |
429 | ||
430 | ||
431 | #[test] | |
432 | fn test_5_mut_xarray_refs() { | |
433 | let mut data: [usize; 128] = [0; 128]; | |
434 | { | |
435 | // temporarily borrow the data to modify it. | |
436 | let (a,b,c,d,e) = mut_array_refs!(&mut data, 1, 14, 3, 100, 10); | |
437 | assert_eq!(a.len(), 1 as usize); | |
438 | assert_eq!(b.len(), 14 as usize); | |
439 | assert_eq!(c.len(), 3 as usize); | |
440 | assert_eq!(d.len(), 100 as usize); | |
441 | assert_eq!(e.len(), 10 as usize); | |
442 | *a = [1; 1]; | |
443 | *b = [14; 14]; | |
444 | *c = [3; 3]; | |
445 | *d = [100; 100]; | |
446 | *e = [10; 10]; | |
447 | } | |
448 | assert_eq!(&[1;1], array_ref![data, 0, 1]); | |
449 | assert_eq!(&[14;14], array_ref![data, 1, 14]); | |
450 | assert_eq!(&[3;3], array_ref![data, 15, 3]); | |
451 | assert_eq!(&[10;10], array_ref![data, 118, 10]); | |
452 | } | |
453 | ||
454 | #[test] | |
455 | fn test_5_mut_xarray_refs_with_dotdot() { | |
456 | let mut data: [usize; 128] = [0; 128]; | |
457 | { | |
458 | // temporarily borrow the data to modify it. | |
459 | let (a,b,c,d,e) = mut_array_refs!(&mut data, 1, 14, 3; ..; 10); | |
460 | assert_eq!(a.len(), 1 as usize); | |
461 | assert_eq!(b.len(), 14 as usize); | |
462 | assert_eq!(c.len(), 3 as usize); | |
463 | assert_eq!(d.len(), 100 as usize); | |
464 | assert_eq!(e.len(), 10 as usize); | |
465 | *a = [1; 1]; | |
466 | *b = [14; 14]; | |
467 | *c = [3; 3]; | |
468 | *e = [10; 10]; | |
469 | } | |
470 | assert_eq!(&[1;1], array_ref![data, 0, 1]); | |
471 | assert_eq!(&[14;14], array_ref![data, 1, 14]); | |
472 | assert_eq!(&[3;3], array_ref![data, 15, 3]); | |
473 | assert_eq!(&[10;10], array_ref![data, 118, 10]); | |
474 | } | |
475 | ||
476 | } // mod test |