#[cfg(target_arch = "wasm32")]
use core::hint::black_box;
use core::{
ptr,
sync::atomic::{compiler_fence, fence, Ordering},
};
mod sealed {
pub trait Sealed {}
}
pub(crate) unsafe trait ZeroValidPlainData: Copy {
const ZERO: Self;
}
macro_rules! impl_zero_valid_plain_data {
($($ty:ty => $zero:expr),+ $(,)?) => {
$(
unsafe impl ZeroValidPlainData for $ty {
const ZERO: Self = $zero;
}
)+
};
}
impl_zero_valid_plain_data!(
u8 => 0,
u16 => 0,
u32 => 0,
u64 => 0,
u128 => 0,
usize => 0,
i8 => 0,
i16 => 0,
i32 => 0,
i64 => 0,
i128 => 0,
isize => 0,
bool => false,
char => '\0',
f32 => 0.0,
f64 => 0.0,
);
pub(crate) trait ErasureBackend: sealed::Sealed {
fn erase(ptr: *mut u8, len: usize);
#[cfg(feature = "multi-pass-clear")]
fn fill(ptr: *mut u8, len: usize, value: u8);
}
struct VolatileRam;
impl sealed::Sealed for VolatileRam {}
impl ErasureBackend for VolatileRam {
#[inline(never)]
fn erase(ptr: *mut u8, len: usize) {
ordered_volatile_store(ptr, len, 0);
}
#[cfg(feature = "multi-pass-clear")]
#[inline(never)]
fn fill(ptr: *mut u8, len: usize, value: u8) {
ordered_volatile_store(ptr, len, value);
}
}
#[inline(never)]
pub(crate) fn erase(ptr: *mut u8, len: usize) {
VolatileRam::erase(ptr, len);
}
#[inline(never)]
pub(crate) fn erase_plain_data<T: ZeroValidPlainData>(value: &mut T) {
compiler_fence(Ordering::SeqCst);
unsafe {
ptr::write_volatile(value, T::ZERO);
}
compiler_fence(Ordering::SeqCst);
fence(Ordering::SeqCst);
}
#[cfg(feature = "multi-pass-clear")]
#[inline(never)]
pub(crate) fn erase_multi_pass(ptr: *mut u8, len: usize) {
VolatileRam::erase(ptr, len);
VolatileRam::fill(ptr, len, 0xFF);
VolatileRam::erase(ptr, len);
}
#[cfg(not(target_arch = "wasm32"))]
#[inline(never)]
fn ordered_volatile_store(ptr: *mut u8, len: usize, value: u8) {
compiler_fence(Ordering::SeqCst);
let mut offset = 0;
while offset < len {
unsafe {
ptr::write_volatile(ptr.add(offset), value);
}
offset += 1;
}
compiler_fence(Ordering::SeqCst);
fence(Ordering::SeqCst);
}
#[cfg(target_arch = "wasm32")]
#[inline(never)]
fn ordered_volatile_store(ptr: *mut u8, len: usize, value: u8) {
compiler_fence(Ordering::SeqCst);
let store: fn(*mut u8, usize, u8) = wasm_volatile_store_impl;
black_box(store)(ptr, len, value);
compiler_fence(Ordering::SeqCst);
fence(Ordering::SeqCst);
}
#[cfg(target_arch = "wasm32")]
#[inline(never)]
fn wasm_volatile_store_impl(ptr: *mut u8, len: usize, value: u8) {
let mut offset = 0;
while offset < len {
unsafe {
ptr::write_volatile(ptr.add(offset), value);
}
offset += 1;
}
}