bones_schema/alloc/
layout.rs

1//! Utilities for working with [`Layout`].
2
3use std::alloc::{Layout, LayoutError};
4
5/// Extension trait for the [`Layout`] type that copies useful nightly functions so that we can use
6/// them on stable.
7pub trait LayoutExt {
8    /// Creates a layout describing the record for `n` instances of
9    /// `self`, with a suitable amount of padding between each to
10    /// ensure that each instance is given its requested size and
11    /// alignment. On success, returns `(k, offs)` where `k` is the
12    /// layout of the array and `offs` is the distance between the start
13    /// of each element in the array.
14    ///
15    /// On arithmetic overflow, returns `LayoutError`.
16    fn repeat(&self, n: usize) -> Result<(Layout, usize), LayoutError>;
17    // Returns the amount of padding we must insert after `self`
18    /// to ensure that the following address will satisfy `align`
19    /// (measured in bytes).
20    ///
21    /// e.g., if `self.size()` is 9, then `self.padding_needed_for(4)`
22    /// returns 3, because that is the minimum number of bytes of
23    /// padding required to get a 4-aligned address (assuming that the
24    /// corresponding memory block starts at a 4-aligned address).
25    ///
26    /// The return value of this function has no meaning if `align` is
27    /// not a power-of-two.
28    ///
29    /// Note that the utility of the returned value requires `align`
30    /// to be less than or equal to the alignment of the starting
31    /// address for the whole allocated block of memory. One way to
32    /// satisfy this constraint is to ensure `align <= self.align()`.
33    fn padding_needed_for(&self, align: usize) -> usize;
34}
35
36// Hack to construct a [`LayoutError`] which cannot be constructed directly.
37const LAYOUT_ERR: LayoutError = if let Err(e) = Layout::from_size_align(0, 0) {
38    e
39} else {
40    unreachable!()
41};
42
43impl LayoutExt for Layout {
44    #[inline]
45    fn padding_needed_for(&self, align: usize) -> usize {
46        let len = self.size();
47
48        // Rounded up value is:
49        //   len_rounded_up = (len + align - 1) & !(align - 1);
50        // and then we return the padding difference: `len_rounded_up - len`.
51        //
52        // We use modular arithmetic throughout:
53        //
54        // 1. align is guaranteed to be > 0, so align - 1 is always
55        //    valid.
56        //
57        // 2. `len + align - 1` can overflow by at most `align - 1`,
58        //    so the &-mask with `!(align - 1)` will ensure that in the
59        //    case of overflow, `len_rounded_up` will itself be 0.
60        //    Thus the returned padding, when added to `len`, yields 0,
61        //    which trivially satisfies the alignment `align`.
62        //
63        // (Of course, attempts to allocate blocks of memory whose
64        // size and padding overflow in the above manner should cause
65        // the allocator to yield an error anyway.)
66
67        let len_rounded_up = len.wrapping_add(align).wrapping_sub(1) & !align.wrapping_sub(1);
68        len_rounded_up.wrapping_sub(len)
69    }
70
71    #[inline]
72    fn repeat(&self, n: usize) -> Result<(Self, usize), LayoutError> {
73        // This cannot overflow. Quoting from the invariant of Layout:
74        // > `size`, when rounded up to the nearest multiple of `align`,
75        // > must not overflow isize (i.e., the rounded value must be
76        // > less than or equal to `isize::MAX`)
77        let padded_size = self.size() + Layout::padding_needed_for(self, self.align());
78        let alloc_size = padded_size.checked_mul(n).ok_or(LAYOUT_ERR)?;
79
80        // The safe constructor is called here to enforce the isize size limit.
81        let layout = Layout::from_size_align(alloc_size, self.align())?;
82        Ok((layout, padded_size))
83    }
84}