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().cast::<u8>(), 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().cast::<u8>(), len) }
205    }
206
207    #[inline(always)]
208    const fn as_ptr(&self) -> *const u128 {
209        // SAFETY: Constructor allocates the first element for `strong_count`
210        unsafe { self.buffer.as_ptr().cast::<u128>().cast_const().add(1) }
211    }
212
213    #[inline(always)]
214    const fn as_mut_ptr(&mut self) -> *mut u128 {
215        // SAFETY: Constructor allocates the first element for `strong_count`
216        unsafe { self.buffer.as_ptr().cast::<u128>().add(1) }
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 u128 {
294        self.inner.as_ptr()
295    }
296
297    #[inline(always)]
298    pub const fn as_mut_ptr(&mut self) -> *mut u128 {
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                .cast::<u8>()
337                .copy_from_nonoverlapping(bytes.as_ptr(), bytes.len());
338
339            self.inner.set_len(len);
340        }
341    }
342
343    /// Will re-allocate if capacity is not enough to store provided bytes.
344    ///
345    /// Returns `false` if `self.len() + bytes.len()` doesn't fit into `u32`.
346    #[inline(always)]
347    #[must_use]
348    pub fn append(&mut self, bytes: &[u8]) -> bool {
349        let Ok(len) = u32::try_from(bytes.len()) else {
350            return false;
351        };
352
353        let Some(new_len) = self.len().checked_add(len) else {
354            return false;
355        };
356
357        if new_len > self.capacity() {
358            self.inner
359                .resize(new_len.max(self.capacity().saturating_mul(2)));
360        }
361
362        // SAFETY: Sufficient capacity guaranteed above, natural alignment of bytes is 1 for input
363        // and output, non-overlapping allocations guaranteed by the type system
364        unsafe {
365            self.as_mut_ptr()
366                .cast::<u8>()
367                .add(self.len() as usize)
368                .copy_from_nonoverlapping(bytes.as_ptr(), bytes.len());
369
370            self.inner.set_len(new_len);
371        }
372
373        true
374    }
375
376    #[inline(always)]
377    pub const fn is_empty(&self) -> bool {
378        self.inner.len() == 0
379    }
380
381    #[inline(always)]
382    pub const fn len(&self) -> u32 {
383        self.inner.len()
384    }
385
386    #[inline(always)]
387    pub const fn capacity(&self) -> u32 {
388        self.inner.capacity()
389    }
390
391    /// Set the length of the useful data to a specified value.
392    ///
393    /// # Safety
394    /// There must be `new_len` bytes initialized in the buffer.
395    ///
396    /// # Panics
397    /// If `bytes.len()` doesn't fit into `u32`
398    #[inline(always)]
399    pub unsafe fn set_len(&mut self, new_len: u32) {
400        // SAFETY: Guaranteed by method contract
401        unsafe {
402            self.inner.set_len(new_len);
403        }
404    }
405}
406
407/// Shared aligned buffer for executor purposes.
408///
409/// See [`OwnedAlignedBuffer`] for a version that can be mutated.
410///
411/// Data is aligned to 16 bytes (128 bits), which is the largest alignment required by primitive
412/// types and by extension any type that implements `TrivialType`/`IoType`.
413///
414/// NOTE: Counter for the number of shared instances is `u32` and will wrap around if exceeded
415/// breaking internal invariants (which is extremely unlikely, but still).
416#[derive(Debug, Default, Clone)]
417pub struct SharedAlignedBuffer {
418    inner: InnerBuffer,
419}
420
421impl Deref for SharedAlignedBuffer {
422    type Target = [u8];
423
424    #[inline(always)]
425    fn deref(&self) -> &Self::Target {
426        self.as_slice()
427    }
428}
429
430// SAFETY: Heap-allocated data structure, points to the same memory if moved
431unsafe impl StableDeref for SharedAlignedBuffer {}
432// SAFETY: Inner buffer is exactly the same and points to the same memory after clone
433unsafe impl CloneStableDeref for SharedAlignedBuffer {}
434// SAFETY: Inner buffer is exactly the same and points to the same memory after clone
435unsafe impl CloneableCart for SharedAlignedBuffer {}
436
437impl SharedAlignedBuffer {
438    /// Static reference to an empty buffer
439    #[inline(always)]
440    pub const fn empty_ref() -> &'static Self {
441        &EMPTY_SHARED_ALIGNED_BUFFER
442    }
443
444    /// Create a new instance from provided bytes.
445    ///
446    /// # Panics
447    /// If `bytes.len()` doesn't fit into `u32`
448    #[inline(always)]
449    pub fn from_bytes(bytes: &[u8]) -> Self {
450        OwnedAlignedBuffer::from_bytes(bytes).into_shared()
451    }
452
453    /// Convert into owned buffer.
454    ///
455    /// If this is the last shared instance, then allocation will be reused, otherwise a new
456    /// allocation will be created.
457    ///
458    /// Returns `None` if there exit other shared instances.
459    #[inline(always)]
460    pub fn into_owned(self) -> OwnedAlignedBuffer {
461        if self.inner.strong_count_ref().load(Ordering::Acquire) == 1 {
462            OwnedAlignedBuffer { inner: self.inner }
463        } else {
464            OwnedAlignedBuffer::from_bytes(self.as_slice())
465        }
466    }
467
468    #[inline(always)]
469    pub const fn as_slice(&self) -> &[u8] {
470        self.inner.as_slice()
471    }
472
473    #[inline(always)]
474    pub const fn as_ptr(&self) -> *const u128 {
475        self.inner.as_ptr()
476    }
477
478    #[inline(always)]
479    pub const fn is_empty(&self) -> bool {
480        self.inner.len() == 0
481    }
482
483    #[inline(always)]
484    pub const fn len(&self) -> u32 {
485        self.inner.len()
486    }
487}