@@ -65,62 +65,58 @@ using cpp::optional;
65
65
66
66
// / Memory region with links to adjacent blocks.
67
67
// /
68
- // / The blocks do not encode their size directly. Instead, they encode offsets
69
- // / to the next and previous blocks using the type given by the `OffsetType`
70
- // / template parameter. The encoded offsets are simply the offsets divded by the
71
- // / minimum block alignment, `ALIGNMENT`.
68
+ // / The blocks store their offsets to the previous and next blocks. The latter
69
+ // / is also the block's size.
72
70
// /
73
71
// / The `ALIGNMENT` constant provided by the derived block is typically the
74
- // / minimum value of `alignof(OffsetType)`. Since the addressable range of a
75
- // / block is given by `std::numeric_limits<OffsetType>::max() *
76
- // / ALIGNMENT`, it may be advantageous to set a higher alignment if it allows
77
- // / using a smaller offset type, even if this wastes some bytes in order to
78
- // / align block headers.
79
- // /
80
- // / Blocks will always be aligned to a `ALIGNMENT` boundary. Block sizes will
81
- // / always be rounded up to a multiple of `ALIGNMENT`.
72
+ // / minimum value of `alignof(OffsetType)`. Blocks will always be aligned to a
73
+ // / `ALIGNMENT` boundary. Block sizes will always be rounded up to a multiple of
74
+ // / `ALIGNMENT`.
82
75
// /
83
76
// / As an example, the diagram below represents two contiguous
84
77
// / `Block<uint32_t, 8>`s. The indices indicate byte offsets:
85
78
// /
86
79
// / @code{.unparsed}
87
80
// / Block 1:
88
- // / +---------------------+------+------ --------+
89
- // / | Header | Info | Usable space |
90
- // / +----------+----------+------+------ --------+
91
- // / | prev | next | | |
92
- // / | 0......3 | 4......7 | 8..9 | 10 .......280 |
93
- // / | 00000000 | 00000046 | 8008 | <app data> |
94
- // / +----------+----------+------+------ --------+
81
+ // / +---------------------+--------------+
82
+ // / | Header | Usable space |
83
+ // / +----------+----------+--------------+
84
+ // / | prev | next | |
85
+ // / | 0......3 | 4......7 | 8........227 |
86
+ // / | 00000000 | 00000230 | <app data> |
87
+ // / +----------+----------+--------------+
95
88
// / Block 2:
96
- // / +---------------------+------+------ --------+
97
- // / | Header | Info | Usable space |
98
- // / +----------+----------+------+------ --------+
99
- // / | prev | next | | |
100
- // / | 0......3 | 4......7 | 8..9 | 10 ......1056 |
101
- // / | 00000046 | 00000106 | 2008 | f7f7....f7f7 |
102
- // / +----------+----------+------+------ --------+
89
+ // / +---------------------+--------------+
90
+ // / | Header | Usable space |
91
+ // / +----------+----------+--------------+
92
+ // / | prev | next | |
93
+ // / | 0......3 | 4......7 | 8........827 |
94
+ // / | 00000230 | 00000830 | f7f7....f7f7 |
95
+ // / +----------+----------+--------------+
103
96
// / @endcode
104
97
// /
105
- // / The overall size of the block (e.g. 280 bytes) is given by its next offset
106
- // / multiplied by the alignment (e.g. 0x106 * 4). Also, the next offset of a
107
- // / block matches the previous offset of its next block. The first block in a
108
- // / list is denoted by having a previous offset of `0`.
98
+ // / The next offset of a block matches the previous offset of its next block.
99
+ // / The first block in a list is denoted by having a previous offset of `0`.
109
100
// /
110
101
// / @tparam OffsetType Unsigned integral type used to encode offsets. Larger
111
102
// / types can address more memory, but consume greater
112
103
// / overhead.
113
104
// / @tparam kAlign Sets the overall alignment for blocks. Minimum is
114
- // / `alignof(OffsetType)` (the default). Larger values can
115
- // / address more memory, but consume greater overhead.
105
+ // / `alignof(OffsetType)` (the default). Larger values
106
+ // / cause greater overhead.
116
107
template <typename OffsetType = uintptr_t , size_t kAlign = alignof(OffsetType)>
117
108
class Block {
109
+ // Masks for the contents of the next_ field.
110
+ static constexpr size_t USED_MASK = 1 << 0 ;
111
+ static constexpr size_t LAST_MASK = 1 << 1 ;
112
+ static constexpr size_t SIZE_MASK = ~(USED_MASK | LAST_MASK);
113
+
118
114
public:
119
115
using offset_type = OffsetType;
120
116
static_assert (cpp::is_unsigned_v<offset_type>,
121
117
" offset type must be unsigned" );
122
-
123
- static constexpr size_t ALIGNMENT = cpp::max(kAlign , alignof(offset_type));
118
+ static constexpr size_t ALIGNMENT =
119
+ cpp::max (cpp::max( kAlign , alignof(offset_type)), size_t { 4 } );
124
120
static constexpr size_t BLOCK_OVERHEAD = align_up(sizeof (Block), ALIGNMENT);
125
121
126
122
// No copy or move.
@@ -147,14 +143,11 @@ class Block {
147
143
}
148
144
149
145
// / @returns The total size of the block in bytes, including the header.
150
- size_t outer_size () const { return next_ * ALIGNMENT ; }
146
+ size_t outer_size () const { return next_ & SIZE_MASK ; }
151
147
152
148
// / @returns The number of usable bytes inside the block.
153
149
size_t inner_size () const { return outer_size () - BLOCK_OVERHEAD; }
154
150
155
- // / @returns The number of bytes requested using AllocFirst or AllocLast.
156
- size_t requested_size () const { return inner_size () - padding_; }
157
-
158
151
// / @returns A pointer to the usable space inside this block.
159
152
cpp::byte *usable_space () {
160
153
return reinterpret_cast <cpp::byte *>(this ) + BLOCK_OVERHEAD;
@@ -224,33 +217,30 @@ class Block {
224
217
return block == nullptr ? nullptr : block->prev ();
225
218
}
226
219
227
- // / Returns the current alignment of a block.
228
- size_t alignment () const { return used () ? info_.alignment : 1 ; }
229
-
230
220
// / Indicates whether the block is in use.
231
221
// /
232
222
// / @returns `true` if the block is in use or `false` if not.
233
- bool used () const { return info_. used ; }
223
+ bool used () const { return next_ & USED_MASK ; }
234
224
235
225
// / Indicates whether this block is the last block or not (i.e. whether
236
226
// / `next()` points to a valid block or not). This is needed because
237
227
// / `next()` points to the end of this block, whether there is a valid
238
228
// / block there or not.
239
229
// /
240
230
// / @returns `true` is this is the last block or `false` if not.
241
- bool last () const { return info_. last ; }
231
+ bool last () const { return next_ & LAST_MASK ; }
242
232
243
233
// / Marks this block as in use.
244
- void mark_used () { info_. used = 1 ; }
234
+ void mark_used () { next_ |= USED_MASK ; }
245
235
246
236
// / Marks this block as free.
247
- void mark_free () { info_. used = 0 ; }
237
+ void mark_free () { next_ &= ~USED_MASK ; }
248
238
249
239
// / Marks this block as the last one in the chain.
250
- constexpr void mark_last () { info_. last = 1 ; }
240
+ constexpr void mark_last () { next_ |= LAST_MASK ; }
251
241
252
242
// / Clears the last bit from this block.
253
- void clear_last () { info_. last = 1 ; }
243
+ void clear_last () { next_ &= ~LAST_MASK ; }
254
244
255
245
// / @brief Checks if a block is valid.
256
246
// /
@@ -338,32 +328,26 @@ class Block {
338
328
// / ensure the split will succeed.
339
329
static Block *split_impl (Block *&block, size_t new_inner_size);
340
330
341
- // / Offset (in increments of the minimum alignment) from this block to the
342
- // / previous block. 0 if this is the first block.
331
+ // / Offset from this block to the previous block. 0 if this is the first
332
+ // / block.
343
333
offset_type prev_ = 0 ;
344
334
345
- // / Offset (in increments of the minimum alignment) from this block to the
346
- // / next block. Valid even if this is the last block, since it equals the
347
- // / size of the block.
335
+ // / Offset from this block to the next block. Valid even if this is the last
336
+ // / block, since it equals the size of the block.
348
337
offset_type next_ = 0 ;
349
338
350
- // / Information about the current state of the block:
339
+ // / Information about the current state of the block is stored in the two low
340
+ // / order bits of the next_ value. These are guaranteed free by a minimum
341
+ // / alignment (and thus, alignment of the size) of 4. The lowest bit is the
342
+ // / `used` flag, and the other bit is the `last` flag.
343
+ // /
351
344
// / * If the `used` flag is set, the block's usable memory has been allocated
352
345
// / and is being used.
353
346
// / * If the `last` flag is set, the block does not have a next block.
354
347
// / * If the `used` flag is set, the alignment represents the requested value
355
348
// / when the memory was allocated, which may be less strict than the actual
356
349
// / alignment.
357
- struct {
358
- uint16_t used : 1 ;
359
- uint16_t last : 1 ;
360
- uint16_t alignment : 14 ;
361
- } info_;
362
-
363
- // / Number of bytes allocated beyond what was requested. This will be at most
364
- // / the minimum alignment, i.e. `alignof(offset_type).`
365
- uint16_t padding_ = 0 ;
366
- } __attribute__((packed, aligned(kAlign )));
350
+ } __attribute__((packed, aligned(cpp::max(kAlign , size_t {4 }))));
367
351
368
352
// Public template method implementations.
369
353
@@ -394,7 +378,7 @@ Block<OffsetType, kAlign>::init(ByteSpan region) {
394
378
if (region.size () < BLOCK_OVERHEAD)
395
379
return {};
396
380
397
- if (cpp::numeric_limits<OffsetType>::max () < region.size () / ALIGNMENT )
381
+ if (cpp::numeric_limits<OffsetType>::max () < region.size ())
398
382
return {};
399
383
400
384
Block *block = as_block (0 , region);
@@ -501,7 +485,7 @@ Block<OffsetType, kAlign>::split(Block *&block, size_t new_inner_size) {
501
485
template <typename OffsetType, size_t kAlign >
502
486
Block<OffsetType, kAlign > *
503
487
Block<OffsetType, kAlign >::split_impl(Block *&block, size_t new_inner_size) {
504
- size_t prev_outer_size = block->prev_ * ALIGNMENT ;
488
+ size_t prev_outer_size = block->prev_ ;
505
489
size_t outer_size1 = new_inner_size + BLOCK_OVERHEAD;
506
490
bool is_last = block->last ();
507
491
ByteSpan bytes = as_bytes (cpp::move (block));
@@ -529,7 +513,7 @@ bool Block<OffsetType, kAlign>::merge_next(Block *&block) {
529
513
if (block->used () || next->used ())
530
514
return false ;
531
515
532
- size_t prev_outer_size = block->prev_ * ALIGNMENT ;
516
+ size_t prev_outer_size = block->prev_ ;
533
517
bool is_last = next->last ();
534
518
ByteSpan prev_bytes = as_bytes (cpp::move (block));
535
519
ByteSpan next_bytes = as_bytes (cpp::move (next));
@@ -554,23 +538,18 @@ Block<OffsetType, kAlign> *Block<OffsetType, kAlign>::next() const {
554
538
555
539
template <typename OffsetType, size_t kAlign >
556
540
Block<OffsetType, kAlign > *Block<OffsetType, kAlign >::prev() const {
557
- uintptr_t addr =
558
- (prev_ == 0 ) ? 0
559
- : reinterpret_cast <uintptr_t >(this ) - (prev_ * ALIGNMENT);
541
+ uintptr_t addr = (prev_ == 0 ) ? 0 : reinterpret_cast <uintptr_t >(this ) - prev_;
560
542
return reinterpret_cast <Block *>(addr);
561
543
}
562
544
563
545
// Private template method implementations.
564
546
565
547
template <typename OffsetType, size_t kAlign >
566
548
constexpr Block<OffsetType, kAlign >::Block(size_t prev_outer_size,
567
- size_t outer_size)
568
- : info_{} {
569
- prev_ = prev_outer_size / ALIGNMENT;
570
- next_ = outer_size / ALIGNMENT;
571
- info_.used = 0 ;
572
- info_.last = 0 ;
573
- info_.alignment = ALIGNMENT;
549
+ size_t outer_size) {
550
+ prev_ = prev_outer_size;
551
+ LIBC_ASSERT (outer_size % ALIGNMENT == 0 && " block sizes must be aligned" );
552
+ next_ = outer_size;
574
553
}
575
554
576
555
template <typename OffsetType, size_t kAlign >
0 commit comments