1818#include " src/__support/CPP/type_traits.h"
1919#include " src/__support/libc_assert.h"
2020#include " src/__support/macros/config.h"
21+ #include " src/__support/math_extras.h"
2122
2223#include < stdint.h>
2324
@@ -68,8 +69,8 @@ using cpp::optional;
6869// / The blocks store their offsets to the previous and next blocks. The latter
6970// / is also the block's size.
7071// /
71- // / Blocks will always be aligned to a `ALIGNMENT` boundary. Block sizes will
72- // / always be rounded up to a multiple of `ALIGNMENT` .
72+ // / All blocks have their usable space aligned to some multiple of max_align_t.
73+ // / This also implies that block outer sizes are aligned to max_align_t .
7374// /
7475// / As an example, the diagram below represents two contiguous `Block`s. The
7576// / indices indicate byte offsets:
@@ -129,8 +130,9 @@ class Block {
129130 Block (const Block &other) = delete ;
130131 Block &operator =(const Block &other) = delete ;
131132
132- // / Creates the first block for a given memory region, followed by a sentinel
133- // / last block. Returns the first block.
133+ // / Initializes a given memory region into a first block and a sentinel last
134+ // / block. Returns the first block, which has its usable space aligned to
135+ // / max_align_t.
134136 static optional<Block *> init (ByteSpan region);
135137
136138 // / @returns A pointer to a `Block`, given a pointer to the start of the
@@ -186,6 +188,9 @@ class Block {
186188 }
187189
188190 // / @returns A pointer to the usable space inside this block.
191+ // /
192+ // / Unless specifically requested otherwise, this will be aligned to
193+ // / max_align_t.
189194 LIBC_INLINE cpp::byte *usable_space () {
190195 return reinterpret_cast <cpp::byte *>(this ) + BLOCK_OVERHEAD;
191196 }
@@ -201,11 +206,12 @@ class Block {
201206 // / Attempts to split this block.
202207 // /
203208 // / If successful, the block will have an inner size of at least
204- // / `new_inner_size`, rounded to ensure that the split point is on an
205- // / ALIGNMENT boundary. The remaining space will be returned as a new block.
206- // / Note that the prev_ field of the next block counts as part of the inner
207- // / size of the returnd block.
208- optional<Block *> split (size_t new_inner_size);
209+ // / `new_inner_size`. The remaining space will be returned as a new block,
210+ // / with usable space aligned to `usable_space_alignment`. Note that the prev_
211+ // / field of the next block counts as part of the inner size of the block.
212+ // / `usable_space_alignment` must be a multiple of max_align_t.
213+ optional<Block *> split (size_t new_inner_size,
214+ size_t usable_space_alignment = alignof (max_align_t ));
209215
210216 // / Merges this block with the one that comes after it.
211217 bool merge_next ();
@@ -248,46 +254,48 @@ class Block {
248254 // / nullptr.
249255 LIBC_INLINE void mark_last () { next_ |= LAST_MASK; }
250256
251- LIBC_INLINE constexpr Block (size_t outer_size) : next_(outer_size) {
257+ LIBC_INLINE Block (size_t outer_size) : next_(outer_size) {
252258 LIBC_ASSERT (outer_size % ALIGNMENT == 0 && " block sizes must be aligned" );
259+ LIBC_ASSERT (is_usable_space_aligned (alignof (max_align_t )) &&
260+ " usable space must be aligned to a multiple of max_align_t" );
253261 }
254262
255263 LIBC_INLINE bool is_usable_space_aligned (size_t alignment) const {
256264 return reinterpret_cast <uintptr_t >(usable_space ()) % alignment == 0 ;
257265 }
258266
259- // / @returns The new inner size of this block that would give the usable
260- // / space of the next block the given alignment.
261- LIBC_INLINE size_t padding_for_alignment (size_t alignment) const {
262- if (is_usable_space_aligned (alignment))
263- return 0 ;
264-
265- // We need to ensure we can always split this block into a "padding" block
266- // and the aligned block. To do this, we need enough extra space for at
267- // least one block.
268- //
269- // |block |usable_space |
270- // |........|......................................|
271- // ^
272- // Alignment requirement
273- //
267+ // Returns the minimum inner size necessary for a block of that size to
268+ // always be able to allocate at the given size and alignment.
269+ //
270+ // Returns 0 if there is no such size.
271+ LIBC_INLINE static size_t min_size_for_allocation (size_t alignment,
272+ size_t size) {
273+ LIBC_ASSERT (alignment >= alignof (max_align_t ) &&
274+ alignment % alignof (max_align_t ) == 0 &&
275+ " alignment must be multiple of max_align_t" );
276+
277+ if (alignment == alignof (max_align_t ))
278+ return size;
279+
280+ // We must create a padding block to align the usable space of the next
281+ // block. The next block's usable space can be found by advancing by
282+ // sizeof(Block) then aligning up. The amount advanced is the amount of
283+ // additional inner size required.
274284 //
275- // |block |space |block |usable_space |
276- // |........|........|........|....................|
277- // ^
278- // Alignment requirement
279- //
280- alignment = cpp::max (alignment, ALIGNMENT);
281- uintptr_t start = reinterpret_cast <uintptr_t >(usable_space ());
282- uintptr_t next_usable_space = align_up (start + BLOCK_OVERHEAD, alignment);
283- uintptr_t next_block = next_usable_space - BLOCK_OVERHEAD;
284- return next_block - start + sizeof (prev_);
285+ // The minimum advancment is sizeof(Block), since the resulting position may
286+ // happen to be aligned. What is the maximum? Advancing by sizeof(Block)
287+ // leaves the result still aligned to alignof(Block). So the most additional
288+ // advancement required would be if the point is exactly alignof(Block) past
289+ // alignment. The remaining size to the next alignment would then be
290+ // alignment - alignof(Block). So the total maximum advancement required is
291+ // sizeof(Block) + alignment - alignof(Block).
292+ if (add_overflow (size, sizeof (Block), size))
293+ return 0 ;
294+ if (add_overflow (size, alignment - alignof (Block), size))
295+ return 0 ;
296+ return size;
285297 }
286298
287- // Check that we can `allocate` a block with a given alignment and size from
288- // this existing block.
289- bool can_allocate (size_t alignment, size_t size) const ;
290-
291299 // This is the return type for `allocate` which can split one block into up to
292300 // three blocks.
293301 struct BlockInfo {
@@ -309,21 +317,30 @@ class Block {
309317 Block *next;
310318 };
311319
312- // Divide a block into up to 3 blocks according to `BlockInfo`. This should
313- // only be called if `can_allocate` returns true .
320+ // Divide a block into up to 3 blocks according to `BlockInfo`. Behavior is
321+ // undefined if allocation is not possible for the given size and alignment .
314322 static BlockInfo allocate (Block *block, size_t alignment, size_t size);
315323
324+ LIBC_INLINE static uintptr_t next_possible_block_start (
325+ uintptr_t ptr, size_t usable_space_alignment = alignof (max_align_t )) {
326+ return align_up (ptr + sizeof (Block), usable_space_alignment) -
327+ sizeof (Block);
328+ }
329+ LIBC_INLINE static uintptr_t prev_possible_block_start (
330+ uintptr_t ptr, size_t usable_space_alignment = alignof (max_align_t )) {
331+ return align_down (ptr, usable_space_alignment) - sizeof (Block);
332+ }
333+
316334private:
317335 // / Construct a block to represent a span of bytes. Overwrites only enough
318336 // / memory for the block header; the rest of the span is left alone.
319337 LIBC_INLINE static Block *as_block (ByteSpan bytes) {
338+ LIBC_ASSERT (reinterpret_cast <uintptr_t >(bytes.data ()) % alignof (Block) ==
339+ 0 &&
340+ " block start must be suitably aligned" );
320341 return ::new (bytes.data ()) Block (bytes.size ());
321342 }
322343
323- // / Like `split`, but assumes the caller has already checked to parameters to
324- // / ensure the split will succeed.
325- Block *split_impl (size_t new_inner_size);
326-
327344 // / Offset from this block to the previous block. 0 if this is the first
328345 // / block. This field is only alive when the previous block is free;
329346 // / otherwise, its memory is reused as part of the previous block's usable
@@ -343,81 +360,46 @@ class Block {
343360 // / previous block is free.
344361 // / * If the `last` flag is set, the block is the sentinel last block. It is
345362 // / summarily considered used and has no next block.
346- } __attribute__((packed, aligned(cpp::max( alignof ( max_align_t ), size_t { 4 })))) ;
363+ };
347364
348365inline constexpr size_t Block::BLOCK_OVERHEAD =
349366 align_up (sizeof (Block), ALIGNMENT);
350367
351- LIBC_INLINE ByteSpan get_aligned_subspan (ByteSpan bytes, size_t alignment) {
352- if (bytes.data () == nullptr )
353- return ByteSpan ();
354-
355- auto unaligned_start = reinterpret_cast <uintptr_t >(bytes.data ());
356- auto aligned_start = align_up (unaligned_start, alignment);
357- auto unaligned_end = unaligned_start + bytes.size ();
358- auto aligned_end = align_down (unaligned_end, alignment);
359-
360- if (aligned_end <= aligned_start)
361- return ByteSpan ();
362-
363- return bytes.subspan (aligned_start - unaligned_start,
364- aligned_end - aligned_start);
365- }
366-
367368LIBC_INLINE
368369optional<Block *> Block::init (ByteSpan region) {
369- optional<ByteSpan> result = get_aligned_subspan (region, ALIGNMENT);
370- if (!result)
370+ if (!region.data ())
371371 return {};
372372
373- region = result.value ();
374- // Two blocks are allocated: a free block and a sentinel last block.
375- if (region.size () < 2 * BLOCK_OVERHEAD)
376- return {};
373+ uintptr_t start = reinterpret_cast <uintptr_t >(region.data ());
374+ uintptr_t end = start + region.size ();
377375
378- if (cpp::numeric_limits<size_t >::max () < region.size ())
376+ uintptr_t block_start = next_possible_block_start (start);
377+ uintptr_t last_start = prev_possible_block_start (end);
378+ if (block_start + sizeof (Block) > last_start)
379379 return {};
380380
381- Block *block = as_block (region.first (region.size () - BLOCK_OVERHEAD));
382- Block *last = as_block (region.last (BLOCK_OVERHEAD));
381+ auto *last_start_ptr = reinterpret_cast <cpp::byte *>(last_start);
382+ Block *block =
383+ as_block ({reinterpret_cast <cpp::byte *>(block_start), last_start_ptr});
384+ Block *last = as_block ({last_start_ptr, sizeof (Block)});
383385 block->mark_free ();
384386 last->mark_last ();
385387 return block;
386388}
387389
388- LIBC_INLINE
389- bool Block::can_allocate (size_t alignment, size_t size) const {
390- if (inner_size () < size)
391- return false ;
392- if (is_usable_space_aligned (alignment))
393- return true ;
394-
395- // Alignment isn't met, so a padding block is needed. Determine amount of
396- // inner_size() consumed by the padding block.
397- size_t padding_size = padding_for_alignment (alignment) - sizeof (prev_);
398-
399- // Check that there is room for the allocation in the following aligned block.
400- size_t aligned_inner_size = inner_size () - padding_size - BLOCK_OVERHEAD;
401- return size <= aligned_inner_size;
402- }
403-
404390LIBC_INLINE
405391Block::BlockInfo Block::allocate (Block *block, size_t alignment, size_t size) {
406- LIBC_ASSERT (
407- block->can_allocate (alignment, size) &&
408- " Calls to this function for a given alignment and size should only be "
409- " done if `can_allocate` for these parameters returns true." );
392+ LIBC_ASSERT (alignment % alignof (max_align_t ) == 0 &&
393+ " alignment must be a multiple of max_align_t" );
410394
411395 BlockInfo info{block, /* prev=*/ nullptr , /* next=*/ nullptr };
412396
413397 if (!info.block ->is_usable_space_aligned (alignment)) {
414398 Block *original = info.block ;
415- optional<Block *> maybe_aligned_block =
416- original->split (info. block -> padding_for_alignment ( alignment) );
399+ // The padding block has no minimum size requirement.
400+ optional<Block *> maybe_aligned_block = original->split (0 , alignment);
417401 LIBC_ASSERT (maybe_aligned_block.has_value () &&
418- " This split should always result in a new block. The check in "
419- " `can_allocate` ensures that we have enough space here to make "
420- " two blocks." );
402+ " it should always be possible to split for alignment" );
421403
422404 if (Block *prev = original->prev_free ()) {
423405 // If there is a free block before this, we can merge the current one with
@@ -441,33 +423,34 @@ Block::BlockInfo Block::allocate(Block *block, size_t alignment, size_t size) {
441423}
442424
443425LIBC_INLINE
444- optional<Block *> Block::split (size_t new_inner_size) {
426+ optional<Block *> Block::split (size_t new_inner_size,
427+ size_t usable_space_alignment) {
428+ LIBC_ASSERT (usable_space_alignment % alignof (max_align_t ) == 0 &&
429+ " alignment must be a multiple of max_align_t" );
430+
445431 if (used ())
446432 return {};
447- // The prev_ field of the next block is always available, so there is a
448- // minimum size to a block created through splitting.
449- if (new_inner_size < sizeof (prev_))
450- new_inner_size = sizeof (prev_);
451-
452- size_t old_inner_size = inner_size ();
453- new_inner_size =
454- align_up (new_inner_size - sizeof (prev_), ALIGNMENT) + sizeof (prev_);
455- if (old_inner_size < new_inner_size)
456- return {};
457433
458- if (old_inner_size - new_inner_size < BLOCK_OVERHEAD)
459- return {};
434+ size_t old_outer_size = outer_size ();
460435
461- return split_impl (new_inner_size);
462- }
436+ // Compute the minimum outer size that produces a block of at least
437+ // `new_inner_size`.
438+ size_t min_outer_size = outer_size (cpp::max (new_inner_size, sizeof (prev_)));
463439
464- LIBC_INLINE
465- Block *Block::split_impl (size_t new_inner_size) {
466- size_t outer_size1 = outer_size (new_inner_size);
467- LIBC_ASSERT (outer_size1 % ALIGNMENT == 0 && " new size must be aligned" );
468- ByteSpan new_region = region ().subspan (outer_size1);
440+ uintptr_t start = reinterpret_cast <uintptr_t >(this );
441+ uintptr_t next_block_start =
442+ next_possible_block_start (start + min_outer_size, usable_space_alignment);
443+ size_t new_outer_size = next_block_start - start;
444+ LIBC_ASSERT (new_outer_size % alignof (max_align_t ) == 0 &&
445+ " new size must be aligned to max_align_t" );
446+
447+ if (old_outer_size < new_outer_size ||
448+ old_outer_size - new_outer_size < sizeof (Block))
449+ return {};
450+
451+ ByteSpan new_region = region ().subspan (new_outer_size);
469452 next_ &= ~SIZE_MASK;
470- next_ |= outer_size1 ;
453+ next_ |= new_outer_size ;
471454
472455 Block *new_block = as_block (new_region);
473456 mark_free (); // Free status for this block is now stored in new_block.
0 commit comments