Revision control

Copy as Markdown

Other Tools

//! 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<T: Copy>(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<T: Copy>(elems: &[T], vec: &mut Vec<T>) {↩
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<i32> = 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]);↩
}↩
}↩