@@ -65,62 +65,58 @@ using cpp::optional;
6565
6666// / Memory region with links to adjacent blocks.
6767// /
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.
7270// /
7371// / 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`.
8275// /
8376// / As an example, the diagram below represents two contiguous
8477// / `Block<uint32_t, 8>`s. The indices indicate byte offsets:
8578// /
8679// / @code{.unparsed}
8780// / 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+ // / +----------+----------+--------------+
9588// / 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+ // / +----------+----------+--------------+
10396// / @endcode
10497// /
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`.
109100// /
110101// / @tparam OffsetType Unsigned integral type used to encode offsets. Larger
111102// / types can address more memory, but consume greater
112103// / overhead.
113104// / @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.
116107template <typename OffsetType = uintptr_t , size_t kAlign = alignof (OffsetType)>
117108class 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+
118114public:
119115 using offset_type = OffsetType;
120116 static_assert (cpp::is_unsigned_v<offset_type>,
121117 " 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 } );
124120 static constexpr size_t BLOCK_OVERHEAD = align_up(sizeof (Block), ALIGNMENT);
125121
126122 // No copy or move.
@@ -147,14 +143,11 @@ class Block {
147143 }
148144
149145 // / @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 ; }
151147
152148 // / @returns The number of usable bytes inside the block.
153149 size_t inner_size () const { return outer_size () - BLOCK_OVERHEAD; }
154150
155- // / @returns The number of bytes requested using AllocFirst or AllocLast.
156- size_t requested_size () const { return inner_size () - padding_; }
157-
158151 // / @returns A pointer to the usable space inside this block.
159152 cpp::byte *usable_space () {
160153 return reinterpret_cast <cpp::byte *>(this ) + BLOCK_OVERHEAD;
@@ -224,33 +217,30 @@ class Block {
224217 return block == nullptr ? nullptr : block->prev ();
225218 }
226219
227- // / Returns the current alignment of a block.
228- size_t alignment () const { return used () ? info_.alignment : 1 ; }
229-
230220 // / Indicates whether the block is in use.
231221 // /
232222 // / @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 ; }
234224
235225 // / Indicates whether this block is the last block or not (i.e. whether
236226 // / `next()` points to a valid block or not). This is needed because
237227 // / `next()` points to the end of this block, whether there is a valid
238228 // / block there or not.
239229 // /
240230 // / @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 ; }
242232
243233 // / Marks this block as in use.
244- void mark_used () { info_. used = 1 ; }
234+ void mark_used () { next_ |= USED_MASK ; }
245235
246236 // / Marks this block as free.
247- void mark_free () { info_. used = 0 ; }
237+ void mark_free () { next_ &= ~USED_MASK ; }
248238
249239 // / 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 ; }
251241
252242 // / Clears the last bit from this block.
253- void clear_last () { info_. last = 1 ; }
243+ void clear_last () { next_ &= ~LAST_MASK ; }
254244
255245 // / @brief Checks if a block is valid.
256246 // /
@@ -338,32 +328,26 @@ class Block {
338328 // / ensure the split will succeed.
339329 static Block *split_impl (Block *&block, size_t new_inner_size);
340330
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.
343333 offset_type prev_ = 0 ;
344334
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.
348337 offset_type next_ = 0 ;
349338
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+ // /
351344 // / * If the `used` flag is set, the block's usable memory has been allocated
352345 // / and is being used.
353346 // / * If the `last` flag is set, the block does not have a next block.
354347 // / * If the `used` flag is set, the alignment represents the requested value
355348 // / when the memory was allocated, which may be less strict than the actual
356349 // / 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 }))));
367351
368352// Public template method implementations.
369353
@@ -394,7 +378,7 @@ Block<OffsetType, kAlign>::init(ByteSpan region) {
394378 if (region.size () < BLOCK_OVERHEAD)
395379 return {};
396380
397- if (cpp::numeric_limits<OffsetType>::max () < region.size () / ALIGNMENT )
381+ if (cpp::numeric_limits<OffsetType>::max () < region.size ())
398382 return {};
399383
400384 Block *block = as_block (0 , region);
@@ -501,7 +485,7 @@ Block<OffsetType, kAlign>::split(Block *&block, size_t new_inner_size) {
501485template <typename OffsetType, size_t kAlign >
502486Block<OffsetType, kAlign > *
503487Block<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_ ;
505489 size_t outer_size1 = new_inner_size + BLOCK_OVERHEAD;
506490 bool is_last = block->last ();
507491 ByteSpan bytes = as_bytes (cpp::move (block));
@@ -529,7 +513,7 @@ bool Block<OffsetType, kAlign>::merge_next(Block *&block) {
529513 if (block->used () || next->used ())
530514 return false ;
531515
532- size_t prev_outer_size = block->prev_ * ALIGNMENT ;
516+ size_t prev_outer_size = block->prev_ ;
533517 bool is_last = next->last ();
534518 ByteSpan prev_bytes = as_bytes (cpp::move (block));
535519 ByteSpan next_bytes = as_bytes (cpp::move (next));
@@ -554,23 +538,18 @@ Block<OffsetType, kAlign> *Block<OffsetType, kAlign>::next() const {
554538
555539template <typename OffsetType, size_t kAlign >
556540Block<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_;
560542 return reinterpret_cast <Block *>(addr);
561543}
562544
563545// Private template method implementations.
564546
565547template <typename OffsetType, size_t kAlign >
566548constexpr 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;
574553}
575554
576555template <typename OffsetType, size_t kAlign >
0 commit comments