fastbuf/
lib.rs

1#![feature(negative_impls)]
2#![feature(auto_traits)]
3#![feature(maybe_uninit_uninit_array)]
4#![feature(slice_index_methods)]
5#![feature(min_specialization)]
6#![feature(const_copy_from_slice)]
7#![feature(const_trait_impl)]
8#![cfg_attr(feature = "std", feature(new_zeroed_alloc))]
9#![feature(allocator_api)]
10#![cfg_attr(test, feature(test))]
11#![cfg_attr(not(feature = "std"), no_std)]
12#![doc = include_str!("../README.md")]
13
14#[cfg(test)]
15extern crate self as fastbuf;
16#[cfg(test)]
17extern crate test;
18
19#[cfg(not(feature = "std"))]
20extern crate core as std;
21
22mod traits;
23pub use traits::*;
24
25mod buffer;
26pub use buffer::*;
27
28mod chunk;
29
30#[cfg(not(feature = "std"))]
31pub(crate) struct EmptyAlloc;
32
33#[cfg(not(feature = "std"))]
34unsafe impl std::alloc::Allocator for EmptyAlloc {
35    fn allocate(
36        &self,
37        _layout: std::alloc::Layout,
38    ) -> Result<std::ptr::NonNull<[u8]>, std::alloc::AllocError> {
39        unreachable!()
40    }
41
42    unsafe fn deallocate(&self, _ptr: std::ptr::NonNull<u8>, _layout: std::alloc::Layout) {
43        unreachable!()
44    }
45}
46
47pub(crate) mod macros {
48
49    #[macro_export]
50    macro_rules! declare_const_fn {
51        ($(#[$($attrs:tt)*])* $visibility:vis fn $($tokens:tt)*) => {
52            #[cfg(feature = "const-trait")]
53            $(#[$($attrs)*])*
54            $visibility const fn $($tokens)*
55
56            #[cfg(not(feature = "const-trait"))]
57            $visibility fn $($tokens)*
58        };
59    }
60
61    #[macro_export]
62    macro_rules! declare_const_trait {
63        ($visibility:vis trait $name:ident<($($generics:tt)*)>
64         : const ($($const_supertrait:path),*), ($($supertrait:path),*) {$($body:tt)*}) => {
65            #[cfg(not(feature = "const-trait"))]
66            $visibility trait $name<$($generics)*>: $($const_supertrait +)* $($supertrait + )* {
67                $($body)*
68            }
69
70            #[cfg(feature = "const-trait")]
71            #[const_trait]
72            $visibility trait $name<$($generics)*>: $(const $const_supertrait +)* $($supertrait + )* {
73                $($body)*
74            }
75        };
76    }
77
78    #[macro_export]
79    macro_rules! declare_const_impl {
80        (($($impl:tt)*), ($($impl_const:tt)*) {$($body:tt)*}) => {
81            #[cfg(feature = "const-trait")]
82            $($impl_const)* { $($body)* }
83
84            #[cfg(not(feature = "const-trait"))]
85            $($impl)* { $($body)* }
86        };
87    }
88
89    #[cfg(feature = "const-trait")]
90    #[macro_export]
91    macro_rules! const_min {
92        ($a:expr, $b:expr) => {
93            if $a > $b {
94                $b
95            } else {
96                $a
97            }
98        };
99    }
100
101    #[cfg(not(feature = "const-trait"))]
102    #[macro_export]
103    macro_rules! const_min {
104        ($a:expr, $b:expr) => {
105            core::cmp::min($a, $b)
106        };
107    }
108}
109
110/// Copies `N` or `n` bytes from `src` to `dst` depending on if `src` lies within a memory page.
111/// https://stackoverflow.com/questions/37800739/is-it-safe-to-read-past-the-end-of-a-buffer-within-the-same-page-on-x86-and-x64
112/// # Safety
113/// Same as [`std::ptr::copy_nonoverlapping`] but with the additional requirements that
114/// `n != 0 && n <= N` and `dst` has room for a `[T; N]`.
115/// Is a macro instead of an `#[inline(always)] fn` because it optimizes better.
116macro_rules! unsafe_wild_copy {
117    // pub unsafe fn wild_copy<T, const N: usize>(src: *const T, dst: *mut T, n: usize) {
118    ([$T:ident; $N:ident], $src:ident, $dst:ident, $n:ident) => {
119        debug_assert!($n != 0 && $n <= $N);
120
121        let page_size = 4096;
122        let read_size = core::mem::size_of::<[$T; $N]>();
123        let within_page = $src as usize & (page_size - 1) < (page_size - read_size) && cfg!(all(
124            // Miri doesn't like this.
125            not(miri),
126            // cargo fuzz's memory sanitizer complains about buffer overrun.
127            // Without nightly we can't detect memory sanitizers, so we check debug_assertions.
128            not(debug_assertions),
129            // x86/x86_64/aarch64 all have min page size of 4096, so reading past the end of a non-empty
130            // buffer won't page fault.
131            any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64")
132        ));
133
134        if within_page {
135            *($dst as *mut core::mem::MaybeUninit<[$T; $N]>) = core::ptr::read($src as *const core::mem::MaybeUninit<[$T; $N]>);
136        } else {
137            $src.copy_to_nonoverlapping($dst, $n);
138        }
139    }
140}
141pub(crate) use unsafe_wild_copy;