18
18
#include " src/__support/CPP/type_traits.h"
19
19
#include " src/__support/libc_assert.h"
20
20
#include " src/__support/macros/config.h"
21
+ #include " src/__support/math_extras.h"
21
22
22
23
#include < stdint.h>
23
24
@@ -68,8 +69,8 @@ using cpp::optional;
68
69
// / The blocks store their offsets to the previous and next blocks. The latter
69
70
// / is also the block's size.
70
71
// /
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 .
73
74
// /
74
75
// / As an example, the diagram below represents two contiguous `Block`s. The
75
76
// / indices indicate byte offsets:
@@ -129,8 +130,9 @@ class Block {
129
130
Block (const Block &other) = delete ;
130
131
Block &operator =(const Block &other) = delete ;
131
132
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.
134
136
static optional<Block *> init (ByteSpan region);
135
137
136
138
// / @returns A pointer to a `Block`, given a pointer to the start of the
@@ -186,6 +188,9 @@ class Block {
186
188
}
187
189
188
190
// / @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.
189
194
LIBC_INLINE cpp::byte *usable_space () {
190
195
return reinterpret_cast <cpp::byte *>(this ) + BLOCK_OVERHEAD;
191
196
}
@@ -201,11 +206,12 @@ class Block {
201
206
// / Attempts to split this block.
202
207
// /
203
208
// / 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 ));
209
215
210
216
// / Merges this block with the one that comes after it.
211
217
bool merge_next ();
@@ -248,46 +254,48 @@ class Block {
248
254
// / nullptr.
249
255
LIBC_INLINE void mark_last () { next_ |= LAST_MASK; }
250
256
251
- LIBC_INLINE constexpr Block (size_t outer_size) : next_(outer_size) {
257
+ LIBC_INLINE Block (size_t outer_size) : next_(outer_size) {
252
258
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" );
253
261
}
254
262
255
263
LIBC_INLINE bool is_usable_space_aligned (size_t alignment) const {
256
264
return reinterpret_cast <uintptr_t >(usable_space ()) % alignment == 0 ;
257
265
}
258
266
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.
274
284
//
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;
285
297
}
286
298
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
-
291
299
// This is the return type for `allocate` which can split one block into up to
292
300
// three blocks.
293
301
struct BlockInfo {
@@ -309,21 +317,30 @@ class Block {
309
317
Block *next;
310
318
};
311
319
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 .
314
322
static BlockInfo allocate (Block *block, size_t alignment, size_t size);
315
323
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
+
316
334
private:
317
335
// / Construct a block to represent a span of bytes. Overwrites only enough
318
336
// / memory for the block header; the rest of the span is left alone.
319
337
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" );
320
341
return ::new (bytes.data ()) Block (bytes.size ());
321
342
}
322
343
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
-
327
344
// / Offset from this block to the previous block. 0 if this is the first
328
345
// / block. This field is only alive when the previous block is free;
329
346
// / otherwise, its memory is reused as part of the previous block's usable
@@ -343,81 +360,46 @@ class Block {
343
360
// / previous block is free.
344
361
// / * If the `last` flag is set, the block is the sentinel last block. It is
345
362
// / summarily considered used and has no next block.
346
- } __attribute__((packed, aligned(cpp::max(alignof( max_align_t ), size_t { 4 })))) ;
363
+ };
347
364
348
365
inline constexpr size_t Block::BLOCK_OVERHEAD =
349
366
align_up (sizeof (Block), ALIGNMENT);
350
367
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
-
367
368
LIBC_INLINE
368
369
optional<Block *> Block::init (ByteSpan region) {
369
- optional<ByteSpan> result = get_aligned_subspan (region, ALIGNMENT);
370
- if (!result)
370
+ if (!region.data ())
371
371
return {};
372
372
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 ();
377
375
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)
379
379
return {};
380
380
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)});
383
385
block->mark_free ();
384
386
last->mark_last ();
385
387
return block;
386
388
}
387
389
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
-
404
390
LIBC_INLINE
405
391
Block::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" );
410
394
411
395
BlockInfo info{block, /* prev=*/ nullptr , /* next=*/ nullptr };
412
396
413
397
if (!info.block ->is_usable_space_aligned (alignment)) {
414
398
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);
417
401
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" );
421
403
422
404
if (Block *prev = original->prev_free ()) {
423
405
// 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) {
441
423
}
442
424
443
425
LIBC_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
+
445
431
if (used ())
446
432
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 {};
457
433
458
- if (old_inner_size - new_inner_size < BLOCK_OVERHEAD)
459
- return {};
434
+ size_t old_outer_size = outer_size ();
460
435
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_)));
463
439
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);
469
452
next_ &= ~SIZE_MASK;
470
- next_ |= outer_size1 ;
453
+ next_ |= new_outer_size ;
471
454
472
455
Block *new_block = as_block (new_region);
473
456
mark_free (); // Free status for this block is now stored in new_block.
0 commit comments