Skip to main content

ab_aligned_buffer/
lib.rs

1//! Efficient abstraction for memory buffers aligned to 16 bytes (`u128`) with both owned and shared
2//! variants.
3//!
4//! [`OwnedAlignedBuffer`] represents a memory location aligned to 16 bytes that can be modified.
5//!
6//! [`SharedAlignedBuffer`] can't be modified but supports cheap reference-counting clones (like
7//! `Arc`, but much more efficient).
8//!
9//! Does not require a standard library (`no_std`) but does require allocator and atomics.
10
11#![cfg_attr(test, feature(pointer_is_aligned_to))]
12#![no_std]
13
14#[cfg(test)]
15mod tests;
16
17extern crate alloc;
18
19use alloc::alloc::realloc;
20use alloc::boxed::Box;
21use core::alloc::Layout;
22use core::mem::MaybeUninit;
23use core::ops::{Deref, DerefMut};
24use core::ptr::NonNull;
25use core::slice;
26use core::sync::atomic::{AtomicU32, Ordering};
27use stable_deref_trait::{CloneStableDeref, StableDeref};
28use yoke::CloneableCart;
29
30const _: () = {
31    assert!(
32        align_of::<u128>() == size_of::<u128>(),
33        "Size and alignment are both 16 bytes"
34    );
35    assert!(size_of::<u128>() >= size_of::<AtomicU32>());
36    assert!(align_of::<u128>() >= align_of::<AtomicU32>());
37};
38
39#[repr(C, align(16))]
40struct ConstInnerBuffer {
41    strong_count: AtomicU32,
42}
43
44const _: () = {
45    assert!(align_of::<ConstInnerBuffer>() == align_of::<u128>());
46    assert!(size_of::<ConstInnerBuffer>() == size_of::<u128>());
47};
48
49static EMPTY_SHARED_ALIGNED_BUFFER: SharedAlignedBuffer = SharedAlignedBuffer {
50    inner: InnerBuffer {
51        buffer: NonNull::from_ref({
52            static BUFFER: MaybeUninit<ConstInnerBuffer> = MaybeUninit::new(ConstInnerBuffer {
53                strong_count: AtomicU32::new(1),
54            });
55
56            &BUFFER
57        })
58        .cast::<MaybeUninit<u128>>(),
59        capacity: 0,
60        len: 0,
61    },
62};
63
64#[derive(Debug)]
65struct InnerBuffer {
66    // The first bytes are allocated for `strong_count`
67    buffer: NonNull<MaybeUninit<u128>>,
68    capacity: u32,
69    len: u32,
70}
71
72// SAFETY: Heap-allocated memory buffer can be used from any thread
73unsafe impl Send for InnerBuffer {}
74// SAFETY: Heap-allocated memory buffer can be used from any thread
75unsafe impl Sync for InnerBuffer {}
76
77impl Default for InnerBuffer {
78    #[inline(always)]
79    fn default() -> Self {
80        EMPTY_SHARED_ALIGNED_BUFFER.inner.clone()
81    }
82}
83
84impl Clone for InnerBuffer {
85    #[inline(always)]
86    fn clone(&self) -> Self {
87        self.strong_count_ref().fetch_add(1, Ordering::AcqRel);
88
89        Self {
90            buffer: self.buffer,
91            capacity: self.capacity,
92            len: self.len,
93        }
94    }
95}
96
97impl Drop for InnerBuffer {
98    #[inline(always)]
99    fn drop(&mut self) {
100        if self.strong_count_ref().fetch_sub(1, Ordering::AcqRel) == 1 {
101            // SAFETY: Created from `Box` in constructor
102            let _: Box<_> = unsafe {
103                Box::from_non_null(NonNull::slice_from_raw_parts(
104                    self.buffer,
105                    1 + (self.capacity as usize).div_ceil(size_of::<u128>()),
106                ))
107            };
108        }
109    }
110}
111
112impl InnerBuffer {
113    /// Allocates a new buffer + one `u128` worth of memory at the beginning for
114    /// `strong_count` in case it is later converted to [`SharedAlignedBuffer`].
115    ///
116    /// `strong_count` field is automatically initialized as `1`.
117    #[inline(always)]
118    fn allocate(capacity: u32) -> Self {
119        let buffer = Box::into_non_null(Box::<[u128]>::new_uninit_slice(
120            1 + (capacity as usize).div_ceil(size_of::<u128>()),
121        ));
122        // SAFETY: The first bytes are allocated for `strong_count`, which is a correctly aligned
123        // copy type
124        unsafe {
125            buffer.cast::<AtomicU32>().write(AtomicU32::new(1));
126        }
127        Self {
128            buffer: buffer.cast::<MaybeUninit<u128>>(),
129            capacity,
130            len: 0,
131        }
132    }
133
134    #[inline(always)]
135    fn resize(&mut self, capacity: u32) {
136        // SAFETY: Non-null correctly aligned pointer, correct size
137        let layout = Layout::for_value(unsafe {
138            slice::from_raw_parts(
139                self.buffer.as_ptr(),
140                1 + (self.capacity as usize).div_ceil(size_of::<u128>()),
141            )
142        });
143
144        // `size_of::<u128>()` is added because the first bytes are allocated for `strong_count`
145        let new_size = size_of::<u128>() + (capacity as usize).next_multiple_of(layout.align());
146
147        #[expect(
148            clippy::cast_ptr_alignment,
149            reason = "Cast from correct alignment to bytes and back due to API requirements"
150        )]
151        // SAFETY: Allocated with global allocator, correct layout, non-zero size that is a
152        // multiple of alignment
153        let new_ptr = unsafe {
154            realloc(self.buffer.as_ptr().cast::<u8>(), layout, new_size).cast::<MaybeUninit<u128>>()
155        };
156        let Some(new_ptr) = NonNull::new(new_ptr) else {
157            panic!("Realloc from {} to {new_size} has failed", self.capacity());
158        };
159
160        self.buffer = new_ptr;
161        self.capacity = capacity;
162    }
163
164    #[inline(always)]
165    const fn len(&self) -> u32 {
166        self.len
167    }
168
169    /// `len` bytes must be initialized
170    #[inline(always)]
171    unsafe fn set_len(&mut self, len: u32) {
172        debug_assert!(
173            len <= self.capacity(),
174            "Too many bytes {} > {}",
175            len,
176            self.capacity()
177        );
178        self.len = len;
179    }
180
181    #[inline(always)]
182    const fn capacity(&self) -> u32 {
183        self.capacity
184    }
185
186    #[inline(always)]
187    const fn strong_count_ref(&self) -> &AtomicU32 {
188        // SAFETY: The first bytes are allocated for `strong_count`, which is a correctly aligned
189        // copy type initialized in the constructor
190        unsafe { self.buffer.as_ptr().cast::<AtomicU32>().as_ref_unchecked() }
191    }
192
193    #[inline(always)]
194    const fn as_slice(&self) -> &[u8] {
195        let len = self.len() as usize;
196        // SAFETY: Not null and length is a protected invariant of the implementation
197        unsafe { slice::from_raw_parts(self.as_ptr(), len) }
198    }
199
200    #[inline(always)]
201    const fn as_mut_slice(&mut self) -> &mut [u8] {
202        let len = self.len() as usize;
203        // SAFETY: Not null and length is a protected invariant of the implementation
204        unsafe { slice::from_raw_parts_mut(self.as_mut_ptr(), len) }
205    }
206
207    #[inline(always)]
208    const fn as_ptr(&self) -> *const u8 {
209        // SAFETY: Constructor allocates the first element for `strong_count`
210        unsafe { self.buffer.as_ptr().cast_const().add(1).cast::<u8>() }
211    }
212
213    #[inline(always)]
214    const fn as_mut_ptr(&mut self) -> *mut u8 {
215        // SAFETY: Constructor allocates the first element for `strong_count`
216        unsafe { self.buffer.as_ptr().add(1).cast::<u8>() }
217    }
218}
219
220/// Owned aligned buffer for executor purposes.
221///
222/// See [`SharedAlignedBuffer`] for a version that can be cheaply cloned while reusing the original
223/// allocation.
224///
225/// Data is aligned to 16 bytes (128 bits), which is the largest alignment required by primitive
226/// types and by extension any type that implements `TrivialType`/`IoType`.
227#[derive(Debug)]
228pub struct OwnedAlignedBuffer {
229    inner: InnerBuffer,
230}
231
232impl Deref for OwnedAlignedBuffer {
233    type Target = [u8];
234
235    #[inline(always)]
236    fn deref(&self) -> &Self::Target {
237        self.as_slice()
238    }
239}
240
241impl DerefMut for OwnedAlignedBuffer {
242    #[inline(always)]
243    fn deref_mut(&mut self) -> &mut Self::Target {
244        self.as_mut_slice()
245    }
246}
247
248// SAFETY: Heap-allocated data structure, points to the same memory if moved
249unsafe impl StableDeref for OwnedAlignedBuffer {}
250
251impl Clone for OwnedAlignedBuffer {
252    #[inline(always)]
253    fn clone(&self) -> Self {
254        let mut new_instance = Self::with_capacity(self.capacity());
255        new_instance.copy_from_slice(self.as_slice());
256        new_instance
257    }
258}
259
260impl OwnedAlignedBuffer {
261    /// Create a new instance with at least specified capacity.
262    ///
263    /// NOTE: Actual capacity might be larger due to alignment requirements.
264    #[inline(always)]
265    pub fn with_capacity(capacity: u32) -> Self {
266        Self {
267            inner: InnerBuffer::allocate(capacity),
268        }
269    }
270
271    /// Create a new instance from provided bytes.
272    ///
273    /// # Panics
274    /// If `bytes.len()` doesn't fit into `u32`
275    #[inline(always)]
276    pub fn from_bytes(bytes: &[u8]) -> Self {
277        let mut instance = Self::with_capacity(0);
278        instance.copy_from_slice(bytes);
279        instance
280    }
281
282    #[inline(always)]
283    pub const fn as_slice(&self) -> &[u8] {
284        self.inner.as_slice()
285    }
286
287    #[inline(always)]
288    pub const fn as_mut_slice(&mut self) -> &mut [u8] {
289        self.inner.as_mut_slice()
290    }
291
292    #[inline(always)]
293    pub const fn as_ptr(&self) -> *const u8 {
294        self.inner.as_ptr()
295    }
296
297    #[inline(always)]
298    pub const fn as_mut_ptr(&mut self) -> *mut u8 {
299        self.inner.as_mut_ptr()
300    }
301
302    #[inline(always)]
303    pub fn into_shared(self) -> SharedAlignedBuffer {
304        SharedAlignedBuffer { inner: self.inner }
305    }
306
307    /// Ensure capacity of the buffer is at least `capacity`.
308    ///
309    /// Will re-allocate if necessary.
310    #[inline(always)]
311    pub fn ensure_capacity(&mut self, capacity: u32) {
312        if capacity > self.capacity() {
313            self.inner.resize(capacity);
314        }
315    }
316
317    /// Will re-allocate if capacity is not enough to store provided bytes.
318    ///
319    /// # Panics
320    /// If `bytes.len()` doesn't fit into `u32`
321    #[inline(always)]
322    pub fn copy_from_slice(&mut self, bytes: &[u8]) {
323        let Ok(len) = u32::try_from(bytes.len()) else {
324            panic!("Too many bytes {}", bytes.len());
325        };
326
327        if len > self.capacity() {
328            self.inner
329                .resize(len.max(self.capacity().saturating_mul(2)));
330        }
331
332        // SAFETY: Sufficient capacity guaranteed above, natural alignment of bytes is 1 for input
333        // and output, non-overlapping allocations guaranteed by the type system
334        unsafe {
335            self.as_mut_ptr()
336                .copy_from_nonoverlapping(bytes.as_ptr(), bytes.len());
337
338            self.inner.set_len(len);
339        }
340    }
341
342    /// Will re-allocate if capacity is not enough to store provided bytes.
343    ///
344    /// Returns `false` if `self.len() + bytes.len()` doesn't fit into `u32`.
345    #[inline(always)]
346    #[must_use]
347    pub fn append(&mut self, bytes: &[u8]) -> bool {
348        let Ok(len) = u32::try_from(bytes.len()) else {
349            return false;
350        };
351
352        let Some(new_len) = self.len().checked_add(len) else {
353            return false;
354        };
355
356        if new_len > self.capacity() {
357            self.inner
358                .resize(new_len.max(self.capacity().saturating_mul(2)));
359        }
360
361        // SAFETY: Sufficient capacity guaranteed above, natural alignment of bytes is 1 for input
362        // and output, non-overlapping allocations guaranteed by the type system
363        unsafe {
364            self.as_mut_ptr()
365                .add(self.len() as usize)
366                .copy_from_nonoverlapping(bytes.as_ptr(), bytes.len());
367
368            self.inner.set_len(new_len);
369        }
370
371        true
372    }
373
374    #[inline(always)]
375    pub const fn is_empty(&self) -> bool {
376        self.inner.len() == 0
377    }
378
379    #[inline(always)]
380    pub const fn len(&self) -> u32 {
381        self.inner.len()
382    }
383
384    #[inline(always)]
385    pub const fn capacity(&self) -> u32 {
386        self.inner.capacity()
387    }
388
389    /// Set the length of the useful data to a specified value.
390    ///
391    /// # Safety
392    /// There must be `new_len` bytes initialized in the buffer.
393    ///
394    /// # Panics
395    /// If `bytes.len()` doesn't fit into `u32`
396    #[inline(always)]
397    pub unsafe fn set_len(&mut self, new_len: u32) {
398        // SAFETY: Guaranteed by method contract
399        unsafe {
400            self.inner.set_len(new_len);
401        }
402    }
403}
404
405/// Shared aligned buffer for executor purposes.
406///
407/// See [`OwnedAlignedBuffer`] for a version that can be mutated.
408///
409/// Data is aligned to 16 bytes (128 bits), which is the largest alignment required by primitive
410/// types and by extension any type that implements `TrivialType`/`IoType`.
411///
412/// NOTE: Counter for the number of shared instances is `u32` and will wrap around if exceeded
413/// breaking internal invariants (which is extremely unlikely, but still).
414#[derive(Debug, Default, Clone)]
415pub struct SharedAlignedBuffer {
416    inner: InnerBuffer,
417}
418
419impl Deref for SharedAlignedBuffer {
420    type Target = [u8];
421
422    #[inline(always)]
423    fn deref(&self) -> &Self::Target {
424        self.as_slice()
425    }
426}
427
428// SAFETY: Heap-allocated data structure, points to the same memory if moved
429unsafe impl StableDeref for SharedAlignedBuffer {}
430// SAFETY: Inner buffer is exactly the same and points to the same memory after clone
431unsafe impl CloneStableDeref for SharedAlignedBuffer {}
432// SAFETY: Inner buffer is exactly the same and points to the same memory after clone
433unsafe impl CloneableCart for SharedAlignedBuffer {}
434
435impl SharedAlignedBuffer {
436    /// Static reference to an empty buffer
437    #[inline(always)]
438    pub const fn empty_ref() -> &'static Self {
439        &EMPTY_SHARED_ALIGNED_BUFFER
440    }
441
442    /// Create a new instance from provided bytes.
443    ///
444    /// # Panics
445    /// If `bytes.len()` doesn't fit into `u32`
446    #[inline(always)]
447    pub fn from_bytes(bytes: &[u8]) -> Self {
448        OwnedAlignedBuffer::from_bytes(bytes).into_shared()
449    }
450
451    /// Convert into owned buffer.
452    ///
453    /// If this is the last shared instance, then allocation will be reused, otherwise a new
454    /// allocation will be created.
455    ///
456    /// Returns `None` if there exit other shared instances.
457    #[inline(always)]
458    pub fn into_owned(self) -> OwnedAlignedBuffer {
459        if self.inner.strong_count_ref().load(Ordering::Acquire) == 1 {
460            OwnedAlignedBuffer { inner: self.inner }
461        } else {
462            OwnedAlignedBuffer::from_bytes(self.as_slice())
463        }
464    }
465
466    #[inline(always)]
467    pub const fn as_slice(&self) -> &[u8] {
468        self.inner.as_slice()
469    }
470
471    #[inline(always)]
472    pub const fn as_ptr(&self) -> *const u8 {
473        self.inner.as_ptr()
474    }
475
476    #[inline(always)]
477    pub const fn is_empty(&self) -> bool {
478        self.inner.len() == 0
479    }
480
481    #[inline(always)]
482    pub const fn len(&self) -> u32 {
483        self.inner.len()
484    }
485}