safemem/
lib.rs

1//! Safe wrappers for memory-accessing functions like `std::ptr::copy()`.
2
3#![cfg_attr(not(feature = "std"), no_std)]
4
5#[cfg(not(feature = "std"))]
6extern crate core as std;
7use std::ptr;
8
9macro_rules! idx_check (
10    ($slice:expr, $idx:expr) => {
11        assert!($idx < $slice.len(),
12            concat!("`", stringify!($idx), "` ({}) out of bounds. Length: {}"),
13            $idx, $slice.len());
14    }
15);
16
17macro_rules! len_check (
18    ($slice:expr, $start:ident, $len:ident) => {
19        assert!(
20            $start.checked_add($len)
21                .expect(concat!("Overflow evaluating ", stringify!($start + $len)))
22                <= $slice.len(),
23            "Length {} starting at {} is out of bounds (slice len {}).", $len, $start, $slice.len()
24        )
25    }
26);
27
28/// Copy `len` elements from `src_idx` to `dest_idx`. Ranges may overlap.
29///
30/// Safe wrapper for `memmove()`/`std::ptr::copy()`.
31///
32/// ###Panics
33/// * If either `src_idx` or `dest_idx` are out of bounds, or if either of these plus `len` is out of
34/// bounds.
35/// * If `src_idx + len` or `dest_idx + len` overflows.
36pub fn copy_over<T: Copy>(slice: &mut [T], src_idx: usize, dest_idx: usize, len: usize) {
37    if slice.len() == 0 { return; }
38
39    idx_check!(slice, src_idx);
40    idx_check!(slice, dest_idx);
41    len_check!(slice, src_idx, len);
42    len_check!(slice, dest_idx, len);
43
44    // At any point a Rust reference exists, the compiler is free to do this.
45    // So we explicitely add it to be caught by miri.
46    #[cfg(miri)]
47    slice.iter().copied().for_each(drop);
48
49    let ptr = slice.as_mut_ptr();
50
51    unsafe {
52        ptr::copy(ptr.offset(src_idx as isize), ptr.offset(dest_idx as isize), len);
53    }
54}
55
56/// Safe wrapper for `std::ptr::write_bytes()`/`memset()`.
57pub fn write_bytes(slice: &mut [u8], byte: u8) {
58    unsafe {
59        ptr::write_bytes(slice.as_mut_ptr(), byte, slice.len());
60    }
61}
62
63/// Prepend `elems` to `vec`, resizing if necessary.
64///
65/// ### Panics
66///
67/// If `vec.len() + elems.len()` overflows.
68#[cfg(feature = "std")]
69pub fn prepend<T: Copy>(elems: &[T], vec: &mut Vec<T>) {
70    let elems_len = elems.len(); // `<= isize::MAX as usize`
71    if elems_len == 0 { return; }
72
73    let old_len = vec.len(); // `<= isize::MAX as usize`
74    if old_len == 0 {
75        // Prepend = append: delegate to Rust's stdlib implementation.
76        vec.extend_from_slice(elems);
77    } else {
78        // Our overflow check occurs here, no need to do it ourselves.
79        vec.reserve(elems_len);
80        let ptr = vec.as_mut_ptr();
81        unsafe {
82            // Move the old elements down to the end.
83            ptr::copy(
84                ptr,
85                ptr.offset(elems_len as isize),
86                old_len,
87            );
88            // Copy the input elements to the start
89            ptr::copy_nonoverlapping(
90                elems.as_ptr(),
91                ptr,
92                elems_len,
93            );
94            // Set the len *after* having initialized the elements.
95            vec.set_len(old_len + elems_len);
96        }
97    }
98}
99
100#[cfg(test)]
101mod tests {
102    use super::*;
103
104    #[test]
105    #[should_panic]
106    fn bounds_check() {
107        let mut arr = [0i32, 1, 2, 3, 4, 5];
108
109        copy_over(&mut arr, 2, 1, 7);
110    }
111
112    #[test]
113    fn copy_empty() {
114        let mut arr: [i32; 0] = [];
115
116        copy_over(&mut arr, 0, 0, 0);
117    }
118
119    #[test]
120    #[cfg(feature = "std")]
121    fn prepend_empty() {
122        let mut vec: Vec<i32> = vec![];
123        prepend(&[1, 2, 3], &mut vec);
124    }
125
126    #[test]
127    #[cfg(feature = "std")]
128    fn prepend_i32() {
129        let mut vec = vec![3, 4, 5];
130        prepend(&[1, 2], &mut vec);
131        assert_eq!(vec, &[1, 2, 3, 4, 5]);
132    }
133
134    /// Detect potential uninit values when running miri
135    #[test]
136    #[cfg(all(
137        feature = "std",
138        miri,
139    ))]
140    fn prepend_bool() {
141        prepend(&[true], &mut vec![false]);
142    }
143}