Skip to main content

ab_aligned_buffer/
lib.rs

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