Skip to content

Commit 5dc3b27

Browse files
committed
feat: Add unsound feature
Add feature to opt in to the unsound implementation of `copy_to_nonoverlapping` which can improve performance.
1 parent d0049d9 commit 5dc3b27

2 files changed

Lines changed: 28 additions & 19 deletions

File tree

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ zstd = "0.13.0"
4343
derive = [ "dep:bitcode_derive" ]
4444
std = [ "serde?/std", "glam?/std", "arrayvec?/std" ]
4545
default = [ "derive", "std" ]
46+
unsound = [] # use benign undefined behavior for more performance. Miri will detect this. See GitHub Wiki "Security".
4647

4748
[package.metadata.docs.rs]
4849
features = [ "derive", "serde", "std" ]

src/derive/vec.rs

Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -53,28 +53,36 @@ macro_rules! unsafe_wild_copy {
5353
([$T:ident; $N:ident], $src:ident, $dst:ident, $n:ident) => {
5454
debug_assert!($n != 0 && $n <= $N);
5555

56-
let page_size = 4096;
57-
let read_size = core::mem::size_of::<[$T; $N]>();
58-
let within_page = $src as usize & (page_size - 1) < (page_size - read_size) && cfg!(all(
59-
// Miri doesn't like this.
60-
not(miri),
61-
// cargo fuzz's memory sanitizer complains about buffer overrun.
62-
// Without nightly we can't detect memory sanitizers, so we check debug_assertions.
63-
not(debug_assertions),
64-
// x86/x86_64/aarch64 all have min page size of 4096, so reading past the end of a non-empty
65-
// buffer won't page fault.
66-
any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64")
67-
));
56+
// Note: Neither Miri nor the fuzzer's memory sanitizer like this feature. We prevent
57+
// the latter from seeing it with `not(debug_assertions)`.
58+
#[cfg(all(feature = "unsound", not(debug_assertions)))]
59+
{
60+
let page_size = 4096;
61+
let read_size = core::mem::size_of::<[$T; $N]>();
62+
let within_page = $src as usize & (page_size - 1) < (page_size - read_size) && cfg!(all(
63+
// Miri doesn't like this.
64+
not(miri),
65+
// cargo fuzz's memory sanitizer complains about buffer overrun.
66+
// Without nightly we can't detect memory sanitizers, so we check debug_assertions.
67+
not(debug_assertions),
68+
// x86/x86_64/aarch64 all have min page size of 4096, so reading past the end of a non-empty
69+
// buffer won't page fault.
70+
any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64")
71+
));
6872

69-
if within_page {
70-
*($dst as *mut core::mem::MaybeUninit<[$T; $N]>) = core::ptr::read($src as *const core::mem::MaybeUninit<[$T; $N]>);
71-
} else {
72-
#[cold]
73-
unsafe fn cold<T>(src: *const T, dst: *mut T, n: usize) {
74-
src.copy_to_nonoverlapping(dst, n);
73+
if within_page {
74+
*($dst as *mut core::mem::MaybeUninit<[$T; $N]>) = core::ptr::read($src as *const core::mem::MaybeUninit<[$T; $N]>);
75+
} else {
76+
#[cold]
77+
unsafe fn cold<T>(src: *const T, dst: *mut T, n: usize) {
78+
src.copy_to_nonoverlapping(dst, n);
79+
}
80+
cold($src, $dst, $n);
7581
}
76-
cold($src, $dst, $n);
7782
}
83+
84+
#[cfg(any(not(feature = "unsound"), debug_assertions))]
85+
$src.copy_to_nonoverlapping($dst, $n);
7886
}
7987
}
8088
#[allow(unused_imports)]

0 commit comments

Comments
 (0)