| 1 | // Copyright (c) 2017-2022, The rav1e contributors. All rights reserved |
| 2 | // |
| 3 | // This source code is subject to the terms of the BSD 2 Clause License and |
| 4 | // the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License |
| 5 | // was not distributed with this source code in the LICENSE file, you can |
| 6 | // obtain it at www.aomedia.org/license/software. If the Alliance for Open |
| 7 | // Media Patent License 1.0 was not distributed with this source code in the |
| 8 | // PATENTS file, you can obtain it at www.aomedia.org/license/patent. |
| 9 | |
| 10 | use std::alloc::{alloc, dealloc, Layout}; |
| 11 | use std::mem::MaybeUninit; |
| 12 | use std::ptr; |
| 13 | use std::{fmt, mem}; |
| 14 | |
| 15 | #[repr (align(64))] |
| 16 | pub struct Align64; |
| 17 | |
| 18 | // A 64 byte aligned piece of data. |
| 19 | // # Examples |
| 20 | // ``` |
| 21 | // let mut x: Aligned<[i16; 64 * 64]> = Aligned::new([0; 64 * 64]); |
| 22 | // assert!(x.data.as_ptr() as usize % 16 == 0); |
| 23 | // |
| 24 | // let mut x: Aligned<[i16; 64 * 64]> = Aligned::uninitialized(); |
| 25 | // assert!(x.data.as_ptr() as usize % 16 == 0); |
| 26 | // ``` |
| 27 | pub struct Aligned<T> { |
| 28 | _alignment: [Align64; 0], |
| 29 | pub data: T, |
| 30 | } |
| 31 | |
| 32 | #[cfg (any(test, feature = "bench" ))] |
| 33 | impl<const N: usize, T> Aligned<[T; N]> { |
| 34 | #[inline (always)] |
| 35 | pub fn from_fn<F>(cb: F) -> Self |
| 36 | where |
| 37 | F: FnMut(usize) -> T, |
| 38 | { |
| 39 | Aligned { _alignment: [], data: std::array::from_fn(cb) } |
| 40 | } |
| 41 | } |
| 42 | |
| 43 | impl<const N: usize, T> Aligned<[MaybeUninit<T>; N]> { |
| 44 | #[inline (always)] |
| 45 | pub const fn uninit_array() -> Self { |
| 46 | Aligned { |
| 47 | _alignment: [], |
| 48 | // SAFETY: Uninitialized [MaybeUninit<T>; N] is valid. |
| 49 | data: unsafe { MaybeUninit::uninit().assume_init() }, |
| 50 | } |
| 51 | } |
| 52 | } |
| 53 | |
| 54 | impl<T> Aligned<T> { |
| 55 | pub const fn new(data: T) -> Self { |
| 56 | Aligned { _alignment: [], data } |
| 57 | } |
| 58 | #[allow (clippy::uninit_assumed_init)] |
| 59 | /// # Safety |
| 60 | /// |
| 61 | /// The resulting `Aligned<T>` *must* be written to before it is read from. |
| 62 | pub const unsafe fn uninitialized() -> Self { |
| 63 | Self::new(data:MaybeUninit::uninit().assume_init()) |
| 64 | } |
| 65 | } |
| 66 | |
| 67 | /// An analog to a Box<[T]> where the underlying slice is aligned. |
| 68 | /// Alignment is according to the architecture-specific SIMD constraints. |
| 69 | pub struct AlignedBoxedSlice<T> { |
| 70 | ptr: std::ptr::NonNull<T>, |
| 71 | len: usize, |
| 72 | } |
| 73 | |
| 74 | impl<T> AlignedBoxedSlice<T> { |
| 75 | // Data alignment in bytes. |
| 76 | cfg_if::cfg_if! { |
| 77 | if #[cfg(target_arch = "wasm32" )] { |
| 78 | // FIXME: wasm32 allocator fails for alignment larger than 3 |
| 79 | const DATA_ALIGNMENT_LOG2: usize = 3; |
| 80 | } else { |
| 81 | const DATA_ALIGNMENT_LOG2: usize = 6; |
| 82 | } |
| 83 | } |
| 84 | |
| 85 | const fn layout(len: usize) -> Layout { |
| 86 | // SAFETY: We are ensuring that `align` is non-zero and is a multiple of 2. |
| 87 | unsafe { |
| 88 | Layout::from_size_align_unchecked( |
| 89 | len * mem::size_of::<T>(), |
| 90 | 1 << Self::DATA_ALIGNMENT_LOG2, |
| 91 | ) |
| 92 | } |
| 93 | } |
| 94 | |
| 95 | fn alloc(len: usize) -> std::ptr::NonNull<T> { |
| 96 | // SAFETY: We are not calling this with a null pointer, so it's safe. |
| 97 | unsafe { ptr::NonNull::new_unchecked(alloc(Self::layout(len)) as *mut T) } |
| 98 | } |
| 99 | |
| 100 | /// Creates a [`AlignedBoxedSlice`] with a slice of length [`len`] filled with |
| 101 | /// [`val`]. |
| 102 | pub fn new(len: usize, val: T) -> Self |
| 103 | where |
| 104 | T: Clone, |
| 105 | { |
| 106 | let mut output = Self { ptr: Self::alloc(len), len }; |
| 107 | |
| 108 | for a in output.iter_mut() { |
| 109 | *a = val.clone(); |
| 110 | } |
| 111 | |
| 112 | output |
| 113 | } |
| 114 | } |
| 115 | |
| 116 | impl<T: fmt::Debug> fmt::Debug for AlignedBoxedSlice<T> { |
| 117 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 118 | fmt::Debug::fmt(&**self, f) |
| 119 | } |
| 120 | } |
| 121 | |
| 122 | impl<T> std::ops::Deref for AlignedBoxedSlice<T> { |
| 123 | type Target = [T]; |
| 124 | |
| 125 | fn deref(&self) -> &[T] { |
| 126 | // SAFETY: We know that `self.ptr` is not null, and we know its length. |
| 127 | unsafe { |
| 128 | let p: *mut T = self.ptr.as_ptr(); |
| 129 | |
| 130 | std::slice::from_raw_parts(data:p, self.len) |
| 131 | } |
| 132 | } |
| 133 | } |
| 134 | |
| 135 | impl<T> std::ops::DerefMut for AlignedBoxedSlice<T> { |
| 136 | fn deref_mut(&mut self) -> &mut [T] { |
| 137 | // SAFETY: We know that `self.ptr` is not null, and we know its length. |
| 138 | unsafe { |
| 139 | let p: *mut T = self.ptr.as_ptr(); |
| 140 | |
| 141 | std::slice::from_raw_parts_mut(data:p, self.len) |
| 142 | } |
| 143 | } |
| 144 | } |
| 145 | |
| 146 | impl<T> std::ops::Drop for AlignedBoxedSlice<T> { |
| 147 | fn drop(&mut self) { |
| 148 | // SAFETY: We know that the contents of this struct are aligned and valid to drop. |
| 149 | unsafe { |
| 150 | for a: &mut T in self.iter_mut() { |
| 151 | ptr::drop_in_place(to_drop:a) |
| 152 | } |
| 153 | |
| 154 | dealloc(self.ptr.as_ptr() as *mut u8, Self::layout(self.len)); |
| 155 | } |
| 156 | } |
| 157 | } |
| 158 | |
| 159 | unsafe impl<T> Send for AlignedBoxedSlice<T> where T: Send {} |
| 160 | unsafe impl<T> Sync for AlignedBoxedSlice<T> where T: Sync {} |
| 161 | |
| 162 | #[cfg (test)] |
| 163 | mod test { |
| 164 | use super::*; |
| 165 | |
| 166 | fn is_aligned<T>(ptr: *const T, n: usize) -> bool { |
| 167 | ((ptr as usize) & ((1 << n) - 1)) == 0 |
| 168 | } |
| 169 | |
| 170 | #[test ] |
| 171 | fn sanity_stack() { |
| 172 | let a: Aligned<_> = Aligned::new([0u8; 3]); |
| 173 | assert!(is_aligned(a.data.as_ptr(), 4)); |
| 174 | } |
| 175 | |
| 176 | #[test ] |
| 177 | fn sanity_heap() { |
| 178 | let a: AlignedBoxedSlice<_> = AlignedBoxedSlice::new(3, 0u8); |
| 179 | assert!(is_aligned(a.as_ptr(), 4)); |
| 180 | } |
| 181 | } |
| 182 | |