summaryrefslogtreecommitdiffstats
path: root/third_party/rust/safemem/src
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/safemem/src')
-rw-r--r--third_party/rust/safemem/src/lib.rs143
1 files changed, 143 insertions, 0 deletions
diff --git a/third_party/rust/safemem/src/lib.rs b/third_party/rust/safemem/src/lib.rs
new file mode 100644
index 0000000000..a30c933906
--- /dev/null
+++ b/third_party/rust/safemem/src/lib.rs
@@ -0,0 +1,143 @@
+//! 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]);
+ }
+}