//! Safe wrappers for memory-accessing functions like `std::ptr::copy()`. #![cfg_attr(not(feature = "std"), no_std)] #[cfg(not(feature = "std"))] extern crate core as std; use std::ptr; macro_rules! idx_check ( ($slice:expr, $idx:expr) => { assert!($idx < $slice.len(), concat!("`", stringify!($idx), "` ({}) out of bounds. Length: {}"), $idx, $slice.len()); } ); macro_rules! len_check ( ($slice:expr, $start:ident, $len:ident) => { assert!( $start.checked_add($len) .expect(concat!("Overflow evaluating ", stringify!($start + $len))) <= $slice.len(), "Length {} starting at {} is out of bounds (slice len {}).", $len, $start, $slice.len() ) } ); /// Copy `len` elements from `src_idx` to `dest_idx`. Ranges may overlap. /// /// Safe wrapper for `memmove()`/`std::ptr::copy()`. /// /// ###Panics /// * If either `src_idx` or `dest_idx` are out of bounds, or if either of these plus `len` is out of /// bounds. /// * If `src_idx + len` or `dest_idx + len` overflows. pub fn copy_over(slice: &mut [T], src_idx: usize, dest_idx: usize, len: usize) { if slice.len() == 0 { return; } idx_check!(slice, src_idx); idx_check!(slice, dest_idx); len_check!(slice, src_idx, len); len_check!(slice, dest_idx, len); // At any point a Rust reference exists, the compiler is free to do this. // So we explicitely add it to be caught by miri. #[cfg(miri)] slice.iter().copied().for_each(drop); let ptr = slice.as_mut_ptr(); unsafe { ptr::copy(ptr.offset(src_idx as isize), ptr.offset(dest_idx as isize), len); } } /// Safe wrapper for `std::ptr::write_bytes()`/`memset()`. pub fn write_bytes(slice: &mut [u8], byte: u8) { unsafe { ptr::write_bytes(slice.as_mut_ptr(), byte, slice.len()); } } /// Prepend `elems` to `vec`, resizing if necessary. /// /// ### Panics /// /// If `vec.len() + elems.len()` overflows. #[cfg(feature = "std")] pub fn prepend(elems: &[T], vec: &mut Vec) { let elems_len = elems.len(); // `<= isize::MAX as usize` if elems_len == 0 { return; } let old_len = vec.len(); // `<= isize::MAX as usize` if old_len == 0 { // Prepend = append: delegate to Rust's stdlib implementation. vec.extend_from_slice(elems); } else { // Our overflow check occurs here, no need to do it ourselves. vec.reserve(elems_len); let ptr = vec.as_mut_ptr(); unsafe { // Move the old elements down to the end. ptr::copy( ptr, ptr.offset(elems_len as isize), old_len, ); // Copy the input elements to the start ptr::copy_nonoverlapping( elems.as_ptr(), ptr, elems_len, ); // Set the len *after* having initialized the elements. vec.set_len(old_len + elems_len); } } } #[cfg(test)] mod tests { use super::*; #[test] #[should_panic] fn bounds_check() { let mut arr = [0i32, 1, 2, 3, 4, 5]; copy_over(&mut arr, 2, 1, 7); } #[test] fn copy_empty() { let mut arr: [i32; 0] = []; copy_over(&mut arr, 0, 0, 0); } #[test] #[cfg(feature = "std")] fn prepend_empty() { let mut vec: Vec = vec![]; prepend(&[1, 2, 3], &mut vec); } #[test] #[cfg(feature = "std")] fn prepend_i32() { let mut vec = vec![3, 4, 5]; prepend(&[1, 2], &mut vec); assert_eq!(vec, &[1, 2, 3, 4, 5]); } /// Detect potential uninit values when running miri #[test] #[cfg(all( feature = "std", miri, ))] fn prepend_bool() { prepend(&[true], &mut vec![false]); } }