use std::sync::atomic::{Ordering, compiler_fence};
use super::{Error, Result};
#[cfg(all(target_arch = "x86_64", not(miri)))]
#[path = "memory/x86.rs"]
mod x86;
#[cfg(all(target_arch = "x86_64", not(miri)))]
const STREAMING_MIN_BYTES: usize = 1024;
#[cfg(all(target_arch = "x86_64", not(miri)))]
const STREAMING_MAX_BYTES: usize = 4 * 1024 * 1024;
#[inline(always)]
pub fn copy(destination: &mut [u8], source: &[u8]) -> Result<()> {
if destination.len() != source.len() {
return Err(length_mismatch(
destination.len(),
source.len(),
"memory::copy",
));
}
unsafe {
copy_to_ptr(destination.as_mut_ptr(), source.as_ptr(), source.len());
}
Ok(())
}
#[inline(always)]
pub fn copy_streaming(destination: &mut [u8], source: &[u8]) -> Result<()> {
if destination.len() != source.len() {
return Err(length_mismatch(
destination.len(),
source.len(),
"memory::copy_streaming",
));
}
unsafe {
copy_streaming_to_ptr(destination.as_mut_ptr(), source.as_ptr(), source.len());
}
Ok(())
}
#[must_use]
pub fn equal(left: &[u8], right: &[u8]) -> bool {
if left.len() != right.len() {
return false;
}
if left.as_ptr() == right.as_ptr() || left.is_empty() {
return true;
}
#[cfg(all(target_arch = "x86_64", not(miri)))]
if std::arch::is_x86_feature_detected!("avx512f")
&& std::arch::is_x86_feature_detected!("avx512bw")
{
return unsafe { x86::equal_avx512(left.as_ptr(), right.as_ptr(), left.len()) };
}
#[cfg(all(target_arch = "x86_64", not(miri)))]
if std::arch::is_x86_feature_detected!("avx2") {
return unsafe { x86::equal_avx2(left.as_ptr(), right.as_ptr(), left.len()) };
}
left == right
}
#[must_use]
pub fn equal_constant_time(left: &[u8], right: &[u8]) -> bool {
if left.len() != right.len() {
return false;
}
#[cfg(all(target_arch = "x86_64", not(miri)))]
if std::arch::is_x86_feature_detected!("avx512f")
&& std::arch::is_x86_feature_detected!("avx512bw")
{
return unsafe { x86::equal_constant_time_avx512(left.as_ptr(), right.as_ptr(), left.len()) };
}
#[cfg(all(target_arch = "x86_64", not(miri)))]
if std::arch::is_x86_feature_detected!("avx2") {
return unsafe { x86::equal_constant_time_avx2(left.as_ptr(), right.as_ptr(), left.len()) };
}
let mut difference = 0_u8;
for (&left_byte, &right_byte) in left.iter().zip(right) {
difference |= left_byte ^ right_byte;
}
compiler_fence(Ordering::SeqCst);
difference == 0
}
pub fn zero_secure(bytes: &mut [u8]) {
for byte in bytes {
unsafe {
std::ptr::write_volatile(byte, 0);
}
}
compiler_fence(Ordering::SeqCst);
}
#[cold]
#[inline(never)]
fn length_mismatch(destination: usize, source: usize, operation: &'static str) -> Error {
Error::invalid_argument(format!(
"{operation} requires equal lengths; destination is {destination} bytes, source is {source} bytes"
))
}
#[inline(always)]
pub(crate) unsafe fn copy_to_ptr(destination: *mut u8, source: *const u8, length: usize) {
#[cfg(all(target_arch = "x86_64", not(target_feature = "avx2")))]
{
unsafe { std::ptr::copy_nonoverlapping(source, destination, length) };
}
#[cfg(not(all(target_arch = "x86_64", not(target_feature = "avx2"))))]
unsafe {
copy_native_to_ptr(destination, source, length)
};
}
#[inline(always)]
#[cfg(not(all(target_arch = "x86_64", not(target_feature = "avx2"))))]
unsafe fn copy_native_to_ptr(destination: *mut u8, source: *const u8, length: usize) {
if length > 256 {
#[cfg(all(target_arch = "x86_64", not(miri)))]
if length <= 4096
&& (cfg!(target_feature = "avx512f") || length >= 2048)
&& std::arch::is_x86_feature_detected!("avx512f")
{
unsafe { x86::copy_cached_avx512(destination, source, length) };
return;
}
unsafe {
std::ptr::copy_nonoverlapping(source, destination, length);
}
return;
}
if length == 0 {
return;
}
unsafe {
if length <= 16 {
if length >= 8 {
copy_block::<8>(destination, source);
copy_block::<8>(destination.add(length - 8), source.add(length - 8));
} else if length >= 4 {
copy_block::<4>(destination, source);
copy_block::<4>(destination.add(length - 4), source.add(length - 4));
} else if length >= 2 {
copy_block::<2>(destination, source);
copy_block::<2>(destination.add(length - 2), source.add(length - 2));
} else {
copy_block::<1>(destination, source);
}
return;
}
if length <= 32 {
copy_block::<16>(destination, source);
copy_block::<16>(destination.add(length - 16), source.add(length - 16));
return;
}
if length <= 64 {
copy_block::<32>(destination, source);
copy_block::<32>(destination.add(length - 32), source.add(length - 32));
return;
}
if length <= 128 {
copy_block::<32>(destination, source);
copy_block::<32>(destination.add(32), source.add(32));
copy_block::<32>(destination.add(length - 64), source.add(length - 64));
copy_block::<32>(destination.add(length - 32), source.add(length - 32));
return;
}
if length <= 256 {
copy_block::<32>(destination, source);
copy_block::<32>(destination.add(32), source.add(32));
copy_block::<32>(destination.add(64), source.add(64));
copy_block::<32>(destination.add(96), source.add(96));
copy_block::<32>(destination.add(length - 128), source.add(length - 128));
copy_block::<32>(destination.add(length - 96), source.add(length - 96));
copy_block::<32>(destination.add(length - 64), source.add(length - 64));
copy_block::<32>(destination.add(length - 32), source.add(length - 32));
}
}
}
#[inline(always)]
pub(crate) unsafe fn copy_streaming_to_ptr(destination: *mut u8, source: *const u8, length: usize) {
#[cfg(all(target_arch = "x86_64", not(miri)))]
if (STREAMING_MIN_BYTES..=STREAMING_MAX_BYTES).contains(&length)
&& std::arch::is_x86_feature_detected!("avx512f")
&& std::arch::is_x86_feature_detected!("avx512bw")
{
unsafe {
x86::copy_streaming_avx512(destination, source, length);
}
return;
}
#[cfg(all(target_arch = "x86_64", not(miri)))]
if (STREAMING_MIN_BYTES..=STREAMING_MAX_BYTES).contains(&length)
&& std::arch::is_x86_feature_detected!("avx2")
{
unsafe {
x86::copy_streaming_avx2(destination, source, length);
}
return;
}
unsafe {
if length > 256 {
std::ptr::copy_nonoverlapping(source, destination, length);
} else {
copy_to_ptr(destination, source, length);
}
}
}
#[inline(always)]
#[cfg(not(all(target_arch = "x86_64", not(target_feature = "avx2"))))]
unsafe fn copy_block<const LENGTH: usize>(destination: *mut u8, source: *const u8) {
unsafe {
std::ptr::copy_nonoverlapping(source, destination, LENGTH);
}
}