[libc-commits] [libc] [libc] Use Block as a byte-backed proxy (PR #201001)
Schrodinger ZHU Yifan via libc-commits
libc-commits at lists.llvm.org
Mon Jun 8 08:19:05 PDT 2026
https://github.com/SchrodingerZhu updated https://github.com/llvm/llvm-project/pull/201001
>From 8c1545899829effde7c9bab7fe822bad0f8023c3 Mon Sep 17 00:00:00 2001
From: Yifan Zhu <yfzhu at google.com>
Date: Fri, 5 Jun 2026 10:33:39 -0700
Subject: [PATCH 1/2] [libc] Use Block as a byte-backed proxy and refactor to
BlockRef
This squashes the changes from libc/block-proxy branch, which includes:
- Using Block as a byte-backed proxy.
- Renaming Block proxy class to BlockRef.
- Refactoring BlockPtr to inherit privately from BlockRef.
- Inline self-contained BlockRef methods.
- Cleanups and formatting.
---
libc/fuzzing/__support/freelist_heap_fuzz.cpp | 13 +-
libc/src/__support/block.h | 340 ++++++++------
libc/src/__support/freelist.cpp | 11 +-
libc/src/__support/freelist.h | 25 +-
libc/src/__support/freelist_heap.h | 95 ++--
libc/src/__support/freestore.h | 55 ++-
libc/src/__support/freetrie.h | 17 +-
libc/test/src/__support/block_test.cpp | 438 +++++++++---------
.../test/src/__support/freelist_heap_test.cpp | 13 +-
libc/test/src/__support/freelist_test.cpp | 29 +-
libc/test/src/__support/freestore_test.cpp | 72 +--
libc/test/src/__support/freetrie_test.cpp | 87 ++--
12 files changed, 647 insertions(+), 548 deletions(-)
diff --git a/libc/fuzzing/__support/freelist_heap_fuzz.cpp b/libc/fuzzing/__support/freelist_heap_fuzz.cpp
index 3675e6c6b7adc..9195119db1edb 100644
--- a/libc/fuzzing/__support/freelist_heap_fuzz.cpp
+++ b/libc/fuzzing/__support/freelist_heap_fuzz.cpp
@@ -1,4 +1,4 @@
-//===-- freelist_heap_fuzz.cpp --------------------------------------------===//
+//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
@@ -6,6 +6,7 @@
//
//===----------------------------------------------------------------------===//
///
+/// \file
/// Fuzzing test for llvm-libc freelist-based heap implementation.
///
//===----------------------------------------------------------------------===//
@@ -26,7 +27,7 @@ asm(R"(
__llvm_libc_heap_limit:
)");
-using LIBC_NAMESPACE::Block;
+using LIBC_NAMESPACE::BlockRef;
using LIBC_NAMESPACE::FreeListHeap;
using LIBC_NAMESPACE::inline_memset;
using LIBC_NAMESPACE::cpp::nullopt;
@@ -148,7 +149,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t remainder) {
// Perform allocation.
void *ptr = nullptr;
- size_t alignment = Block::MIN_ALIGN;
+ size_t alignment = BlockRef::MIN_ALIGN;
switch (alloc_type) {
case AllocType::MALLOC:
ptr = heap.allocate(alloc_size);
@@ -173,7 +174,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t remainder) {
alloc_size - alloc.size);
alloc.ptr = ptr;
alloc.size = alloc_size;
- alloc.alignment = Block::MIN_ALIGN;
+ alloc.alignment = BlockRef::MIN_ALIGN;
}
break;
}
@@ -195,8 +196,8 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t remainder) {
if (ptr) {
// aligned_allocate should automatically apply a minimum alignment.
- if (alignment < Block::MIN_ALIGN)
- alignment = Block::MIN_ALIGN;
+ if (alignment < BlockRef::MIN_ALIGN)
+ alignment = BlockRef::MIN_ALIGN;
// Check alignment.
if (reinterpret_cast<uintptr_t>(ptr) % alignment)
__builtin_trap();
diff --git a/libc/src/__support/block.h b/libc/src/__support/block.h
index 25a2240a06435..cec59e952916d 100644
--- a/libc/src/__support/block.h
+++ b/libc/src/__support/block.h
@@ -1,25 +1,31 @@
-//===-- Implementation header for a block of memory -------------*- C++ -*-===//
+//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Implementation header for a block of memory.
+///
+//===----------------------------------------------------------------------===//
#ifndef LLVM_LIBC_SRC___SUPPORT_BLOCK_H
#define LLVM_LIBC_SRC___SUPPORT_BLOCK_H
#include "hdr/stdint_proxy.h"
+#include "hdr/types/size_t.h"
#include "src/__support/CPP/algorithm.h"
#include "src/__support/CPP/cstddef.h"
#include "src/__support/CPP/limits.h"
#include "src/__support/CPP/new.h"
#include "src/__support/CPP/optional.h"
#include "src/__support/CPP/span.h"
-#include "src/__support/CPP/type_traits.h"
#include "src/__support/libc_assert.h"
#include "src/__support/macros/config.h"
#include "src/__support/math_extras.h"
+#include "src/string/memory_utils/inline_memcpy.h"
namespace LIBC_NAMESPACE_DECL {
@@ -38,15 +44,17 @@ LIBC_INLINE constexpr size_t align_up(size_t value, size_t alignment) {
using ByteSpan = cpp::span<LIBC_NAMESPACE::cpp::byte>;
using cpp::optional;
-/// Memory region with links to adjacent blocks.
+/// Proxy for a memory region with links to adjacent blocks.
///
/// The blocks store their offsets to the previous and next blocks. The latter
-/// is also the block's size.
+/// is also the block's size. The metadata is stored in raw bytes and accessed
+/// through aligned byte-copy loads and stores so the header can overlap user
+/// storage without creating typed aliasing accesses.
///
/// All blocks have their usable space aligned to some multiple of MIN_ALIGN.
/// This also implies that block outer sizes are aligned to MIN_ALIGN.
///
-/// As an example, the diagram below represents two contiguous `Block`s. The
+/// As an example, the diagram below represents two contiguous blocks. The
/// indices indicate byte offsets:
///
/// @code{.unparsed}
@@ -90,48 +98,69 @@ using cpp::optional;
///
/// The next offset of a block matches the previous offset of its next block.
/// The first block in a list is denoted by having a previous offset of `0`.
-class Block {
- // Masks for the contents of the next_ field.
+class BlockRef {
+ // Masks for the contents of the next field.
static constexpr size_t PREV_FREE_MASK = 1 << 0;
static constexpr size_t LAST_MASK = 1 << 1;
static constexpr size_t SIZE_MASK = ~(PREV_FREE_MASK | LAST_MASK);
+ // Header field offsets. The previous offset is only meaningful when this
+ // block's PREV_FREE_MASK bit is set in the next field.
+ static constexpr size_t PREV_OFFSET = 0;
+ static constexpr size_t NEXT_OFFSET = sizeof(size_t);
+
public:
+ static constexpr size_t HEADER_SIZE = 2 * sizeof(size_t);
+
// To ensure block sizes have two lower unused bits, ensure usable space is
// always aligned to at least 4 bytes. (The distances between usable spaces,
// the outer size, is then always also 4-aligned.)
static constexpr size_t MIN_ALIGN = cpp::max(size_t{4}, alignof(max_align_t));
- // No copy or move.
- Block(const Block &other) = delete;
- Block &operator=(const Block &other) = delete;
+
+ LIBC_INLINE constexpr BlockRef() = default;
+ LIBC_INLINE explicit constexpr BlockRef(cpp::byte *ptr) : self(ptr) {}
+ LIBC_INLINE explicit constexpr operator bool() const {
+ return self != nullptr;
+ }
+ LIBC_INLINE constexpr bool operator==(BlockRef other) const {
+ return self == other.self;
+ }
+ LIBC_INLINE constexpr bool operator!=(BlockRef other) const {
+ return !(*this == other);
+ }
+
+ LIBC_INLINE cpp::byte *data() const { return self; }
+ LIBC_INLINE uintptr_t addr() const {
+ return reinterpret_cast<uintptr_t>(self);
+ }
/// Initializes a given memory region into a first block and a sentinel last
/// block. Returns the first block, which has its usable space aligned to
/// MIN_ALIGN.
- static optional<Block *> init(ByteSpan region);
+ static optional<BlockRef> init(ByteSpan region);
- /// @returns A pointer to a `Block`, given a pointer to the start of the
- /// usable space inside the block.
+ /// @returns A pointer to a block, given a pointer to the start of the usable
+ /// space inside the block.
///
/// This is the inverse of `usable_space()`.
///
- /// @warning This method does not do any checking; passing a random
- /// pointer will return a non-null pointer.
- LIBC_INLINE static Block *from_usable_space(void *usable_space) {
+ /// @warning This method does not do any checking; passing a random pointer
+ /// will return a non-null pointer.
+ LIBC_INLINE static BlockRef from_usable_space(void *usable_space) {
auto *bytes = reinterpret_cast<cpp::byte *>(usable_space);
- return reinterpret_cast<Block *>(bytes - sizeof(Block));
+ return BlockRef(bytes - HEADER_SIZE);
}
- LIBC_INLINE static const Block *from_usable_space(const void *usable_space) {
+ LIBC_INLINE static BlockRef from_usable_space(const void *usable_space) {
const auto *bytes = reinterpret_cast<const cpp::byte *>(usable_space);
- return reinterpret_cast<const Block *>(bytes - sizeof(Block));
+ return BlockRef(const_cast<cpp::byte *>(bytes - HEADER_SIZE));
}
/// @returns The total size of the block in bytes, including the header.
- LIBC_INLINE size_t outer_size() const { return next_ & SIZE_MASK; }
+ LIBC_INLINE size_t outer_size() const { return load_next() & SIZE_MASK; }
- LIBC_INLINE static size_t outer_size(size_t inner_size) {
- // The usable region includes the prev_ field of the next block.
- return inner_size - sizeof(prev_) + sizeof(Block);
+ LIBC_INLINE static constexpr size_t outer_size(size_t inner_size) {
+ // The usable region includes the prev field of the next block.
+ return inner_size - PREV_FIELD_SIZE + HEADER_SIZE;
}
/// @returns The number of usable bytes inside the block were it to be
@@ -144,9 +173,9 @@ class Block {
/// @returns The number of usable bytes inside a block with the given outer
/// size were it to be allocated.
- LIBC_INLINE static size_t inner_size(size_t outer_size) {
- // The usable region includes the prev_ field of the next block.
- return inner_size_free(outer_size) + sizeof(prev_);
+ LIBC_INLINE static constexpr size_t inner_size(size_t outer_size) {
+ // The usable region includes the prev field of the next block.
+ return inner_size_free(outer_size) + PREV_FIELD_SIZE;
}
/// @returns The number of usable bytes inside the block if it remains free.
@@ -158,87 +187,68 @@ class Block {
/// @returns The number of usable bytes inside a block with the given outer
/// size if it remains free.
- LIBC_INLINE static size_t inner_size_free(size_t outer_size) {
- return outer_size - sizeof(Block);
+ LIBC_INLINE static constexpr size_t inner_size_free(size_t outer_size) {
+ return outer_size - HEADER_SIZE;
}
/// @returns A pointer to the usable space inside this block.
///
/// Aligned to some multiple of MIN_ALIGN.
- LIBC_INLINE cpp::byte *usable_space() {
- auto *s = reinterpret_cast<cpp::byte *>(this) + sizeof(Block);
- LIBC_ASSERT(reinterpret_cast<uintptr_t>(s) % MIN_ALIGN == 0 &&
- "usable space must be aligned to MIN_ALIGN");
- return s;
- }
- LIBC_INLINE const cpp::byte *usable_space() const {
- const auto *s = reinterpret_cast<const cpp::byte *>(this) + sizeof(Block);
+ LIBC_INLINE cpp::byte *usable_space() const {
+ auto *s = self + HEADER_SIZE;
LIBC_ASSERT(reinterpret_cast<uintptr_t>(s) % MIN_ALIGN == 0 &&
"usable space must be aligned to MIN_ALIGN");
return s;
}
// @returns The region of memory the block manages, including the header.
- LIBC_INLINE ByteSpan region() {
- return {reinterpret_cast<cpp::byte *>(this), outer_size()};
- }
+ LIBC_INLINE ByteSpan region() const { return {self, outer_size()}; }
/// Attempts to split this block.
///
/// If successful, the block will have an inner size of at least
/// `new_inner_size`. The remaining space will be returned as a new block,
- /// with usable space aligned to `usable_space_alignment`. Note that the prev_
+ /// with usable space aligned to `usable_space_alignment`. Note that the prev
/// field of the next block counts as part of the inner size of the block.
/// `usable_space_alignment` must be a multiple of MIN_ALIGN.
- optional<Block *> split(size_t new_inner_size,
- size_t usable_space_alignment = MIN_ALIGN);
+ optional<BlockRef> split(size_t new_inner_size,
+ size_t usable_space_alignment = MIN_ALIGN) const;
/// Merges this block with the one that comes after it.
- bool merge_next();
-
- /// @returns The block immediately after this one, or a null pointer if this
- /// is the last block.
- LIBC_INLINE Block *next() const {
- if (next_ & LAST_MASK)
- return nullptr;
- return reinterpret_cast<Block *>(reinterpret_cast<uintptr_t>(this) +
- outer_size());
+ bool merge_next() const;
+
+ /// @returns The block immediately after this one, or a null block if this is
+ /// the last block.
+ LIBC_INLINE BlockRef next() const {
+ size_t next_value = load_next();
+ if (next_value & LAST_MASK)
+ return BlockRef();
+ return BlockRef(self + (next_value & SIZE_MASK));
}
- /// @returns The free block immediately before this one, otherwise nullptr.
- LIBC_INLINE Block *prev_free() const {
- if (!(next_ & PREV_FREE_MASK))
- return nullptr;
- return reinterpret_cast<Block *>(reinterpret_cast<uintptr_t>(this) - prev_);
+ /// @returns The free block immediately before this one, otherwise null.
+ LIBC_INLINE BlockRef prev_free() const {
+ if (!(load_next() & PREV_FREE_MASK))
+ return BlockRef();
+ return BlockRef(self - load_prev());
}
/// @returns Whether the block is unavailable for allocation.
- LIBC_INLINE bool used() const { return !next() || !next()->prev_free(); }
+ LIBC_INLINE bool used() const { return !next() || !next().prev_free(); }
/// Marks this block as in use.
- LIBC_INLINE void mark_used() {
+ LIBC_INLINE void mark_used() const {
LIBC_ASSERT(next() && "last block is always considered used");
- next()->next_ &= ~PREV_FREE_MASK;
+ BlockRef next_block = next();
+ next_block.store_next(next_block.load_next() & ~PREV_FREE_MASK);
}
/// Marks this block as free.
- LIBC_INLINE void mark_free() {
+ LIBC_INLINE void mark_free() const {
LIBC_ASSERT(next() && "last block is always considered used");
- next()->next_ |= PREV_FREE_MASK;
- // The next block's prev_ field becomes alive, as it is no longer part of
- // this block's used space.
- *new (&next()->prev_) size_t = outer_size();
- }
-
- LIBC_INLINE Block(size_t outer_size, bool is_last) : next_(outer_size) {
- // Last blocks are not usable, so they need not have sizes aligned to
- // MIN_ALIGN.
- LIBC_ASSERT(outer_size % (is_last ? alignof(Block) : MIN_ALIGN) == 0 &&
- "block sizes must be aligned");
- LIBC_ASSERT(is_usable_space_aligned(MIN_ALIGN) &&
- "usable space must be aligned to a multiple of MIN_ALIGN");
- if (is_last)
- next_ |= LAST_MASK;
+ BlockRef next_block = next();
+ next_block.store_next(next_block.load_next() | PREV_FREE_MASK);
+ next_block.store_prev(outer_size());
}
LIBC_INLINE bool is_usable_space_aligned(size_t alignment) const {
@@ -259,7 +269,7 @@ class Block {
// We must create a new block inside this one (splitting). This requires a
// block header in addition to the requested size.
- if (add_overflow(size, sizeof(Block), size))
+ if (add_overflow(size, HEADER_SIZE, size))
return 0;
// Beyond that, padding space may need to remain in this block to ensure
@@ -275,79 +285,90 @@ class Block {
// So the maximum distance would be G - L. As a special case, if L is 1
// (unaligned), the max distance is G - 1.
//
- // This block's usable space is aligned to MIN_ALIGN >= Block. With zero
- // padding, the next block's usable space is sizeof(Block) past it, which is
- // a point aligned to Block. Thus the max padding needed is alignment -
- // alignof(Block).
- if (add_overflow(size, alignment - alignof(Block), size))
+ // This block's usable space is aligned to MIN_ALIGN >= header alignment.
+ // With zero padding, the next block's usable space is HEADER_SIZE past it,
+ // which is aligned to header alignment. Thus the max padding needed is
+ // alignment - alignof(size_t).
+ if (add_overflow(size, alignment - alignof(size_t), size))
return 0;
return size;
}
- // This is the return type for `allocate` which can split one block into up to
- // three blocks.
- struct BlockInfo {
- // This is the newly aligned block. It will have the alignment requested by
- // a call to `allocate` and at most `size`.
- Block *block;
-
- // If the usable_space in the new block was not aligned according to the
- // `alignment` parameter, we will need to split into this block and the
- // `block` to ensure `block` is properly aligned. In this case, `prev` will
- // be a pointer to this new "padding" block. `prev` will be nullptr if no
- // new block was created or we were able to merge the block before the
- // original block with the "padding" block.
- Block *prev;
-
- // This is the remainder of the next block after splitting the `block`
- // according to `size`. This can happen if there's enough space after the
- // `block`.
- Block *next;
- };
+ struct BlockInfo;
// Divide a block into up to 3 blocks according to `BlockInfo`. Behavior is
// undefined if allocation is not possible for the given size and alignment.
- static BlockInfo allocate(Block *block, size_t alignment, size_t size);
+ static BlockInfo allocate(BlockRef block, size_t alignment, size_t size);
// These two functions may wrap around.
LIBC_INLINE static uintptr_t
next_possible_block_start(uintptr_t ptr,
size_t usable_space_alignment = MIN_ALIGN) {
- return align_up(ptr + sizeof(Block), usable_space_alignment) -
- sizeof(Block);
+ return align_up(ptr + HEADER_SIZE, usable_space_alignment) - HEADER_SIZE;
}
LIBC_INLINE static uintptr_t
prev_possible_block_start(uintptr_t ptr,
size_t usable_space_alignment = MIN_ALIGN) {
- return align_down(ptr, usable_space_alignment) - sizeof(Block);
+ return align_down(ptr, usable_space_alignment) - HEADER_SIZE;
}
+ /// Only for testing.
+ static constexpr size_t PREV_FIELD_SIZE = sizeof(size_t);
+
private:
/// Construct a block to represent a span of bytes. Overwrites only enough
/// memory for the block header; the rest of the span is left alone.
- LIBC_INLINE static Block *as_block(ByteSpan bytes) {
- LIBC_ASSERT(reinterpret_cast<uintptr_t>(bytes.data()) % alignof(Block) ==
+ LIBC_INLINE static BlockRef as_block(ByteSpan bytes) {
+ LIBC_ASSERT(reinterpret_cast<uintptr_t>(bytes.data()) % alignof(size_t) ==
0 &&
"block start must be suitably aligned");
- return ::new (bytes.data()) Block(bytes.size(), /*is_last=*/false);
+ BlockRef block(bytes.data());
+ block.store_next(bytes.size());
+ return block;
}
LIBC_INLINE static void make_last_block(cpp::byte *start) {
- LIBC_ASSERT(reinterpret_cast<uintptr_t>(start) % alignof(Block) == 0 &&
+ LIBC_ASSERT(reinterpret_cast<uintptr_t>(start) % alignof(size_t) == 0 &&
"block start must be suitably aligned");
- ::new (start) Block(sizeof(Block), /*is_last=*/true);
+ BlockRef last(start);
+ last.store_next(HEADER_SIZE | LAST_MASK);
+ }
+
+ LIBC_INLINE cpp::byte *field_ptr(size_t offset) const {
+ cpp::byte *ptr = self + offset;
+ LIBC_ASSERT(reinterpret_cast<uintptr_t>(ptr) % alignof(size_t) == 0 &&
+ "block metadata fields must be aligned");
+ // BlockRef points to block header which should be well-aligned. However,
+ // the compiler may not know this information. Adding assume aligned to
+ // tell the compiler to emit a single load/store even when unaligned access
+ // is disallowed.
+#if __has_builtin(__builtin_assume_aligned)
+ return reinterpret_cast<cpp::byte *>(
+ __builtin_assume_aligned(ptr, alignof(size_t)));
+#else
+ return ptr;
+#endif
+ }
+
+ LIBC_INLINE size_t load_field(size_t offset) const {
+ size_t value;
+ inline_memcpy(&value, field_ptr(offset), sizeof(value));
+ return value;
+ }
+
+ LIBC_INLINE void store_field(size_t offset, size_t value) const {
+ inline_memcpy(field_ptr(offset), &value, sizeof(value));
}
/// Offset from this block to the previous block. 0 if this is the first
/// block. This field is only alive when the previous block is free;
/// otherwise, its memory is reused as part of the previous block's usable
/// space.
- size_t prev_ = 0;
+ LIBC_INLINE size_t load_prev() const { return load_field(PREV_OFFSET); }
/// Offset from this block to the next block. Valid even if this is the last
/// block, since it equals the size of the block.
- size_t next_ = 0;
-
+ ///
/// Information about the current state of the block is stored in the two low
/// order bits of the next_ value. These are guaranteed free by a minimum
/// alignment (and thus, alignment of the size) of 4. The lowest bit is the
@@ -357,14 +378,41 @@ class Block {
/// previous block is free.
/// * If the `last` flag is set, the block is the sentinel last block. It is
/// summarily considered used and has no next block.
+ LIBC_INLINE size_t load_next() const { return load_field(NEXT_OFFSET); }
-public:
- /// Only for testing.
- static constexpr size_t PREV_FIELD_SIZE = sizeof(prev_);
+ LIBC_INLINE void store_prev(size_t value) const {
+ store_field(PREV_OFFSET, value);
+ }
+ LIBC_INLINE void store_next(size_t value) const {
+ store_field(NEXT_OFFSET, value);
+ }
+
+ cpp::byte *self = nullptr;
+};
+
+// This is the return type for `allocate` which can split one block into up to
+// three blocks.
+struct BlockRef::BlockInfo {
+ // This is the newly aligned block. It will have the alignment requested by a
+ // call to `allocate` and at most `size`.
+ BlockRef block;
+
+ // If the usable_space in the new block was not aligned according to the
+ // `alignment` parameter, we will need to split into this block and the
+ // `block` to ensure `block` is properly aligned. In this case, `prev` will be
+ // this new "padding" block. `prev` will be null if no new block was created
+ // or we were able to merge the block before the original block with the
+ // "padding" block.
+ BlockRef prev;
+
+ // This is the remainder of the next block after splitting the `block`
+ // according to `size`. This can happen if there's enough space after the
+ // `block`.
+ BlockRef next;
};
LIBC_INLINE
-optional<Block *> Block::init(ByteSpan region) {
+optional<BlockRef> BlockRef::init(ByteSpan region) {
if (!region.data())
return {};
@@ -381,63 +429,64 @@ optional<Block *> Block::init(ByteSpan region) {
if (last_start >= end)
return {};
- if (block_start + sizeof(Block) > last_start)
+ if (block_start + HEADER_SIZE > last_start)
return {};
auto *last_start_ptr = reinterpret_cast<cpp::byte *>(last_start);
- Block *block =
+ BlockRef block =
as_block({reinterpret_cast<cpp::byte *>(block_start), last_start_ptr});
make_last_block(last_start_ptr);
- block->mark_free();
+ block.mark_free();
return block;
}
LIBC_INLINE
-Block::BlockInfo Block::allocate(Block *block, size_t alignment, size_t size) {
+BlockRef::BlockInfo BlockRef::allocate(BlockRef block, size_t alignment,
+ size_t size) {
LIBC_ASSERT(alignment % MIN_ALIGN == 0 &&
"alignment must be a multiple of MIN_ALIGN");
- BlockInfo info{block, /*prev=*/nullptr, /*next=*/nullptr};
+ BlockInfo info{block, BlockRef(), BlockRef()};
- if (!info.block->is_usable_space_aligned(alignment)) {
- Block *original = info.block;
+ if (!info.block.is_usable_space_aligned(alignment)) {
+ BlockRef original = info.block;
// The padding block has no minimum size requirement.
- optional<Block *> maybe_aligned_block = original->split(0, alignment);
+ optional<BlockRef> maybe_aligned_block = original.split(0, alignment);
LIBC_ASSERT(maybe_aligned_block.has_value() &&
"it should always be possible to split for alignment");
- if (Block *prev = original->prev_free()) {
+ if (BlockRef prev = original.prev_free()) {
// If there is a free block before this, we can merge the current one with
// the newly created one.
- prev->merge_next();
+ prev.merge_next();
} else {
info.prev = original;
}
- Block *aligned_block = *maybe_aligned_block;
- LIBC_ASSERT(aligned_block->is_usable_space_aligned(alignment) &&
+ BlockRef aligned_block = *maybe_aligned_block;
+ LIBC_ASSERT(aligned_block.is_usable_space_aligned(alignment) &&
"The aligned block isn't aligned somehow.");
info.block = aligned_block;
}
// Now get a block for the requested size.
- if (optional<Block *> next = info.block->split(size))
+ if (optional<BlockRef> next = info.block.split(size))
info.next = *next;
return info;
}
LIBC_INLINE
-optional<Block *> Block::split(size_t new_inner_size,
- size_t usable_space_alignment) {
+optional<BlockRef> BlockRef::split(size_t new_inner_size,
+ size_t usable_space_alignment) const {
LIBC_ASSERT(usable_space_alignment % MIN_ALIGN == 0 &&
"alignment must be a multiple of MIN_ALIGN");
// Compute the minimum outer size that produces a block of at least
// `new_inner_size`.
- size_t min_outer_size = outer_size(cpp::max(new_inner_size, sizeof(prev_)));
+ size_t min_outer_size = outer_size(cpp::max(new_inner_size, PREV_FIELD_SIZE));
- uintptr_t start = reinterpret_cast<uintptr_t>(this);
+ uintptr_t start = addr();
uintptr_t next_block_start =
next_possible_block_start(start + min_outer_size, usable_space_alignment);
if (next_block_start < start)
@@ -447,33 +496,32 @@ optional<Block *> Block::split(size_t new_inner_size,
"new size must be aligned to MIN_ALIGN");
if (outer_size() < new_outer_size ||
- outer_size() - new_outer_size < sizeof(Block))
+ outer_size() - new_outer_size < HEADER_SIZE)
return {};
bool was_free = !used();
ByteSpan new_region = region().subspan(new_outer_size);
- next_ &= ~SIZE_MASK;
- next_ |= new_outer_size;
+ store_next((load_next() & ~SIZE_MASK) | new_outer_size);
- Block *new_block = as_block(new_region);
- new_block->mark_free();
+ BlockRef new_block = as_block(new_region);
+ new_block.mark_free();
if (was_free)
mark_free();
- LIBC_ASSERT(new_block->is_usable_space_aligned(usable_space_alignment) &&
+ LIBC_ASSERT(new_block.is_usable_space_aligned(usable_space_alignment) &&
"usable space must have requested alignment");
return new_block;
}
LIBC_INLINE
-bool Block::merge_next() {
- if (used() || next()->used())
+bool BlockRef::merge_next() const {
+ BlockRef next_block = next();
+ if (used() || next_block.used())
return false;
- size_t new_size = outer_size() + next()->outer_size();
- next_ &= ~SIZE_MASK;
- next_ |= new_size;
- next()->prev_ = new_size;
+ size_t new_size = outer_size() + next_block.outer_size();
+ store_next((load_next() & ~SIZE_MASK) | new_size);
+ next().store_prev(new_size);
return true;
}
diff --git a/libc/src/__support/freelist.cpp b/libc/src/__support/freelist.cpp
index bfb90ae1c4db4..28f73b2726d7d 100644
--- a/libc/src/__support/freelist.cpp
+++ b/libc/src/__support/freelist.cpp
@@ -1,10 +1,15 @@
-//===-- Implementation for freelist ---------------------------------------===//
+//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Implementation for freelist.
+///
+//===----------------------------------------------------------------------===//
#include "freelist.h"
@@ -12,8 +17,8 @@ namespace LIBC_NAMESPACE_DECL {
void FreeList::push(Node *node) {
if (begin_) {
- LIBC_ASSERT(Block::from_usable_space(node)->outer_size() ==
- begin_->block()->outer_size() &&
+ LIBC_ASSERT(BlockRef::from_usable_space(node).outer_size() ==
+ begin_->block().outer_size() &&
"freelist entries must have the same size");
// Since the list is circular, insert the node immediately before begin_.
node->prev = begin_->prev;
diff --git a/libc/src/__support/freelist.h b/libc/src/__support/freelist.h
index c51f14fe57ae7..48e70c7c29df6 100644
--- a/libc/src/__support/freelist.h
+++ b/libc/src/__support/freelist.h
@@ -1,10 +1,15 @@
-//===-- Interface for freelist --------------------------------------------===//
+//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Interface for freelist.
+///
+//===----------------------------------------------------------------------===//
#ifndef LLVM_LIBC_SRC___SUPPORT_FREELIST_H
#define LLVM_LIBC_SRC___SUPPORT_FREELIST_H
@@ -26,15 +31,15 @@ class FreeList {
class Node {
public:
/// @returns The block containing this node.
- LIBC_INLINE const Block *block() const {
- return Block::from_usable_space(this);
+ LIBC_INLINE BlockRef block() const {
+ return BlockRef::from_usable_space(this);
}
/// @returns The block containing this node.
- LIBC_INLINE Block *block() { return Block::from_usable_space(this); }
+ LIBC_INLINE BlockRef block() { return BlockRef::from_usable_space(this); }
/// @returns The inner size of blocks in the list containing this node.
- LIBC_INLINE size_t size() const { return block()->inner_size(); }
+ LIBC_INLINE size_t size() const { return block().inner_size(); }
private:
// Circularly linked pointers to adjacent nodes.
@@ -58,16 +63,16 @@ class FreeList {
LIBC_INLINE Node *begin() { return begin_; }
/// @returns The first block in the list.
- LIBC_INLINE Block *front() { return begin_->block(); }
+ LIBC_INLINE BlockRef front() { return begin_->block(); }
/// Push a block to the back of the list.
/// The block must be large enough to contain a node.
- LIBC_INLINE void push(Block *block) {
- LIBC_ASSERT(!block->used() &&
+ LIBC_INLINE void push(BlockRef block) {
+ LIBC_ASSERT(!block.used() &&
"only free blocks can be placed on free lists");
- LIBC_ASSERT(block->inner_size_free() >= sizeof(FreeList) &&
+ LIBC_ASSERT(block.inner_size_free() >= sizeof(FreeList) &&
"block too small to accomodate free list node");
- push(new (block->usable_space()) Node);
+ push(new (block.usable_space()) Node);
}
/// Push an already-constructed node to the back of the list.
diff --git a/libc/src/__support/freelist_heap.h b/libc/src/__support/freelist_heap.h
index 2cce64381faea..05affc38fcb8c 100644
--- a/libc/src/__support/freelist_heap.h
+++ b/libc/src/__support/freelist_heap.h
@@ -1,10 +1,15 @@
-//===-- Interface for freelist_heap ---------------------------------------===//
+//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Interface for freelist_heap.
+///
+//===----------------------------------------------------------------------===//
#ifndef LLVM_LIBC_SRC___SUPPORT_FREELIST_HEAP_H
#define LLVM_LIBC_SRC___SUPPORT_FREELIST_HEAP_H
@@ -53,12 +58,12 @@ class FreeListHeap {
void *allocate_impl(size_t alignment, size_t size);
- bool shrink_in_place(Block *block, size_t size);
-
- span<cpp::byte> block_to_span(Block *block) {
- return span<cpp::byte>(block->usable_space(), block->inner_size());
+ span<cpp::byte> block_to_span(BlockRef block) {
+ return span<cpp::byte>(block.usable_space(), block.inner_size());
}
+ bool shrink_in_place(BlockRef block, size_t size);
+
bool is_valid_ptr(void *ptr) { return ptr >= begin && ptr < end; }
cpp::byte *begin;
@@ -77,9 +82,9 @@ template <size_t BUFF_SIZE> class FreeListHeapBuffer : public FreeListHeap {
LIBC_INLINE void FreeListHeap::init() {
LIBC_ASSERT(!is_initialized && "duplicate initialization");
- auto result = Block::init(region());
- Block *block = *result;
- free_store.set_range({0, cpp::bit_ceil(block->inner_size())});
+ auto result = BlockRef::init(region());
+ BlockRef block = *result;
+ free_store.set_range({0, cpp::bit_ceil(block.inner_size())});
free_store.insert(block);
is_initialized = true;
}
@@ -91,26 +96,26 @@ LIBC_INLINE void *FreeListHeap::allocate_impl(size_t alignment, size_t size) {
if (!is_initialized)
init();
- size_t request_size = Block::min_size_for_allocation(alignment, size);
+ size_t request_size = BlockRef::min_size_for_allocation(alignment, size);
if (!request_size)
return nullptr;
- Block *block = free_store.remove_best_fit(request_size);
+ BlockRef block = free_store.remove_best_fit(request_size);
if (!block)
return nullptr;
- auto block_info = Block::allocate(block, alignment, size);
+ auto block_info = BlockRef::allocate(block, alignment, size);
if (block_info.next)
free_store.insert(block_info.next);
if (block_info.prev)
free_store.insert(block_info.prev);
- block_info.block->mark_used();
- return block_info.block->usable_space();
+ block_info.block.mark_used();
+ return block_info.block.usable_space();
}
LIBC_INLINE void *FreeListHeap::allocate(size_t size) {
- return allocate_impl(Block::MIN_ALIGN, size);
+ return allocate_impl(BlockRef::MIN_ALIGN, size);
}
LIBC_INLINE void *FreeListHeap::aligned_allocate(size_t alignment,
@@ -123,8 +128,8 @@ LIBC_INLINE void *FreeListHeap::aligned_allocate(size_t alignment,
if (size % alignment != 0)
return nullptr;
- // The minimum alignment supported by Block is MIN_ALIGN.
- alignment = cpp::max(alignment, Block::MIN_ALIGN);
+ // The minimum alignment supported by BlockRef is MIN_ALIGN.
+ alignment = cpp::max(alignment, BlockRef::MIN_ALIGN);
return allocate_impl(alignment, size);
}
@@ -137,53 +142,43 @@ LIBC_INLINE void FreeListHeap::free(void *ptr) {
LIBC_ASSERT(is_valid_ptr(bytes) && "Invalid pointer");
- Block *block = Block::from_usable_space(bytes);
- LIBC_ASSERT(block->next() && "sentinel last block cannot be freed");
- LIBC_ASSERT(block->used() && "double free");
- block->mark_free();
+ BlockRef block = BlockRef::from_usable_space(bytes);
+ LIBC_ASSERT(block.next() && "sentinel last block cannot be freed");
+ LIBC_ASSERT(block.used() && "double free");
+ block.mark_free();
// Can we combine with the left or right blocks?
- Block *prev_free = block->prev_free();
- Block *next = block->next();
+ BlockRef prev_free = block.prev_free();
+ BlockRef next = block.next();
- if (prev_free != nullptr) {
+ if (prev_free) {
// Remove from free store and merge.
free_store.remove(prev_free);
block = prev_free;
- block->merge_next();
+ block.merge_next();
}
- if (!next->used()) {
+ if (!next.used()) {
free_store.remove(next);
- block->merge_next();
+ block.merge_next();
}
// Add back to the freelist
free_store.insert(block);
}
-LIBC_INLINE bool FreeListHeap::shrink_in_place(Block *block, size_t size) {
- size_t min_outer_size = Block::outer_size(cpp::max(size, sizeof(size_t)));
- uintptr_t next_block_start = Block::next_possible_block_start(
- reinterpret_cast<uintptr_t>(block) + min_outer_size, Block::MIN_ALIGN);
- size_t new_outer_size = next_block_start - reinterpret_cast<uintptr_t>(block);
- if (block->outer_size() >= new_outer_size) {
- // TODO: this is a temporary workaround due to Block's setting prev_
- // in its constructor. This code should be deleted once we finishing
- // refactoring.
- cpp::byte *overlap_ptr =
- reinterpret_cast<cpp::byte *>(block) + new_outer_size;
- size_t backup;
- LIBC_NAMESPACE::inline_memcpy(&backup, overlap_ptr, sizeof(size_t));
- optional<Block *> next = block->split(size);
-
- LIBC_NAMESPACE::inline_memcpy(overlap_ptr, &backup, sizeof(size_t));
-
+LIBC_INLINE bool FreeListHeap::shrink_in_place(BlockRef block, size_t size) {
+ size_t min_outer_size = BlockRef::outer_size(cpp::max(size, sizeof(size_t)));
+ uintptr_t next_block_start = BlockRef::next_possible_block_start(
+ block.addr() + min_outer_size, BlockRef::MIN_ALIGN);
+ size_t new_outer_size = next_block_start - block.addr();
+ if (block.outer_size() >= new_outer_size) {
+ optional<BlockRef> next = block.split(size);
// register the new block on successful split
if (next.has_value()) {
- Block *next_block = *next;
- Block *right = next_block->next();
- if (right != nullptr && !right->used()) {
+ BlockRef next_block = *next;
+ BlockRef right = next_block.next();
+ if (!right.used()) {
free_store.remove(right);
- next_block->merge_next();
+ next_block.merge_next();
}
free_store.insert(next_block);
}
@@ -209,10 +204,10 @@ LIBC_INLINE void *FreeListHeap::realloc(void *ptr, size_t size) {
if (!is_valid_ptr(bytes))
return nullptr;
- Block *block = Block::from_usable_space(bytes);
- if (!block->used())
+ BlockRef block = BlockRef::from_usable_space(bytes);
+ if (!block.used())
return nullptr;
- size_t old_size = block->inner_size();
+ size_t old_size = block.inner_size();
if (old_size >= size) {
shrink_in_place(block, size);
diff --git a/libc/src/__support/freestore.h b/libc/src/__support/freestore.h
index d611c17af4644..adc0e061ace93 100644
--- a/libc/src/__support/freestore.h
+++ b/libc/src/__support/freestore.h
@@ -1,10 +1,15 @@
-//===-- Interface for freestore ------------------------------------------===//
+//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Interface for freestore.
+///
+//===----------------------------------------------------------------------===//
#ifndef LLVM_LIBC_SRC___SUPPORT_FREESTORE_H
#define LLVM_LIBC_SRC___SUPPORT_FREESTORE_H
@@ -31,39 +36,39 @@ class FreeStore {
/// Insert a free block. If the block is too small to be tracked, nothing
/// happens.
- void insert(Block *block);
+ void insert(BlockRef block);
/// Remove a free block. If the block is too small to be tracked, nothing
/// happens.
- void remove(Block *block);
+ void remove(BlockRef block);
/// Remove a best-fit free block that can contain the given size when
/// allocated. Returns nullptr if there is no such block.
- Block *remove_best_fit(size_t size);
+ BlockRef remove_best_fit(size_t size);
private:
- static constexpr size_t MIN_OUTER_SIZE =
- align_up(sizeof(Block) + sizeof(FreeList::Node), Block::MIN_ALIGN);
- static constexpr size_t MIN_LARGE_OUTER_SIZE =
- align_up(sizeof(Block) + sizeof(FreeTrie::Node), Block::MIN_ALIGN);
+ static constexpr size_t MIN_OUTER_SIZE = align_up(
+ BlockRef::HEADER_SIZE + sizeof(FreeList::Node), BlockRef::MIN_ALIGN);
+ static constexpr size_t MIN_LARGE_OUTER_SIZE = align_up(
+ BlockRef::HEADER_SIZE + sizeof(FreeTrie::Node), BlockRef::MIN_ALIGN);
static constexpr size_t NUM_SMALL_SIZES =
- (MIN_LARGE_OUTER_SIZE - MIN_OUTER_SIZE) / Block::MIN_ALIGN;
+ (MIN_LARGE_OUTER_SIZE - MIN_OUTER_SIZE) / BlockRef::MIN_ALIGN;
- LIBC_INLINE static bool too_small(Block *block) {
- return block->outer_size() < MIN_OUTER_SIZE;
+ LIBC_INLINE static bool too_small(BlockRef block) {
+ return block.outer_size() < MIN_OUTER_SIZE;
}
- LIBC_INLINE static bool is_small(Block *block) {
- return block->outer_size() < MIN_LARGE_OUTER_SIZE;
+ LIBC_INLINE static bool is_small(BlockRef block) {
+ return block.outer_size() < MIN_LARGE_OUTER_SIZE;
}
- FreeList &small_list(Block *block);
+ FreeList &small_list(BlockRef block);
FreeList *find_best_small_fit(size_t size);
cpp::array<FreeList, NUM_SMALL_SIZES> small_lists;
FreeTrie large_trie;
};
-LIBC_INLINE void FreeStore::insert(Block *block) {
+LIBC_INLINE void FreeStore::insert(BlockRef block) {
if (too_small(block))
return;
if (is_small(block))
@@ -72,35 +77,35 @@ LIBC_INLINE void FreeStore::insert(Block *block) {
large_trie.push(block);
}
-LIBC_INLINE void FreeStore::remove(Block *block) {
+LIBC_INLINE void FreeStore::remove(BlockRef block) {
if (too_small(block))
return;
if (is_small(block)) {
small_list(block).remove(
- reinterpret_cast<FreeList::Node *>(block->usable_space()));
+ reinterpret_cast<FreeList::Node *>(block.usable_space()));
} else {
- large_trie.remove(
- reinterpret_cast<FreeTrie::Node *>(block->usable_space()));
+ large_trie.remove(reinterpret_cast<FreeTrie::Node *>(block.usable_space()));
}
}
-LIBC_INLINE Block *FreeStore::remove_best_fit(size_t size) {
+LIBC_INLINE BlockRef FreeStore::remove_best_fit(size_t size) {
if (FreeList *list = find_best_small_fit(size)) {
- Block *block = list->front();
+ BlockRef block = list->front();
list->pop();
return block;
}
if (FreeTrie::Node *best_fit = large_trie.find_best_fit(size)) {
- Block *block = best_fit->block();
+ BlockRef block = best_fit->block();
large_trie.remove(best_fit);
return block;
}
- return nullptr;
+ return BlockRef();
}
-LIBC_INLINE FreeList &FreeStore::small_list(Block *block) {
+LIBC_INLINE FreeList &FreeStore::small_list(BlockRef block) {
LIBC_ASSERT(is_small(block) && "only legal for small blocks");
- return small_lists[(block->outer_size() - MIN_OUTER_SIZE) / Block::MIN_ALIGN];
+ return small_lists[(block.outer_size() - MIN_OUTER_SIZE) /
+ BlockRef::MIN_ALIGN];
}
LIBC_INLINE FreeList *FreeStore::find_best_small_fit(size_t size) {
diff --git a/libc/src/__support/freetrie.h b/libc/src/__support/freetrie.h
index c1a8306c6f8d2..9e35463462b38 100644
--- a/libc/src/__support/freetrie.h
+++ b/libc/src/__support/freetrie.h
@@ -1,10 +1,15 @@
-//===-- Interface for freetrie --------------------------------------------===//
+//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Interface for freetrie.
+///
+//===----------------------------------------------------------------------===//
#ifndef LLVM_LIBC_SRC___SUPPORT_FREETRIE_H
#define LLVM_LIBC_SRC___SUPPORT_FREETRIE_H
@@ -96,7 +101,7 @@ class FreeTrie {
LIBC_INLINE bool empty() const { return !root; }
/// Push a block to the trie.
- void push(Block *block);
+ void push(BlockRef block);
/// Remove a node from this trie node's free list.
void remove(Node *node);
@@ -117,10 +122,10 @@ class FreeTrie {
SizeRange range;
};
-LIBC_INLINE void FreeTrie::push(Block *block) {
- LIBC_ASSERT(block->inner_size_free() >= sizeof(Node) &&
+LIBC_INLINE void FreeTrie::push(BlockRef block) {
+ LIBC_ASSERT(block.inner_size_free() >= sizeof(Node) &&
"block too small to accomodate free trie node");
- size_t size = block->inner_size();
+ size_t size = block.inner_size();
LIBC_ASSERT(range.contains(size) && "requested size out of trie range");
// Find the position in the tree to push to.
@@ -139,7 +144,7 @@ LIBC_INLINE void FreeTrie::push(Block *block) {
}
}
- Node *node = new (block->usable_space()) Node;
+ Node *node = new (block.usable_space()) Node;
FreeList list = *cur;
if (list.empty()) {
node->parent = parent;
diff --git a/libc/test/src/__support/block_test.cpp b/libc/test/src/__support/block_test.cpp
index 189e3cab1d784..6bd0ab5be24c0 100644
--- a/libc/test/src/__support/block_test.cpp
+++ b/libc/test/src/__support/block_test.cpp
@@ -1,10 +1,15 @@
-//===-- Unittests for a block of memory -------------------------*- C++ -*-===//
+//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Unittests for a block of memory.
+///
+//===----------------------------------------------------------------------===//
#include <stddef.h>
#include "src/__support/CPP/array.h"
@@ -14,7 +19,7 @@
#include "src/string/memcpy.h"
#include "test/UnitTest/Test.h"
-using LIBC_NAMESPACE::Block;
+using LIBC_NAMESPACE::BlockRef;
using LIBC_NAMESPACE::cpp::array;
using LIBC_NAMESPACE::cpp::bit_ceil;
using LIBC_NAMESPACE::cpp::byte;
@@ -22,54 +27,53 @@ using LIBC_NAMESPACE::cpp::span;
TEST(LlvmLibcBlockTest, CanCreateSingleAlignedBlock) {
constexpr size_t kN = 1024;
- alignas(Block::MIN_ALIGN) array<byte, kN> bytes;
+ alignas(BlockRef::MIN_ALIGN) array<byte, kN> bytes;
- auto result = Block::init(bytes);
+ auto result = BlockRef::init(bytes);
ASSERT_TRUE(result.has_value());
- Block *block = *result;
-
- EXPECT_EQ(reinterpret_cast<uintptr_t>(block) % alignof(Block), size_t{0});
- EXPECT_TRUE(block->is_usable_space_aligned(Block::MIN_ALIGN));
-
- Block *last = block->next();
- ASSERT_NE(last, static_cast<Block *>(nullptr));
- EXPECT_EQ(reinterpret_cast<uintptr_t>(last) % alignof(Block), size_t{0});
-
- EXPECT_EQ(last->outer_size(), sizeof(Block));
- EXPECT_EQ(last->prev_free(), block);
- EXPECT_TRUE(last->used());
-
- size_t block_outer_size =
- reinterpret_cast<uintptr_t>(last) - reinterpret_cast<uintptr_t>(block);
- EXPECT_EQ(block->outer_size(), block_outer_size);
- EXPECT_EQ(block->inner_size(),
- block_outer_size - sizeof(Block) + Block::PREV_FIELD_SIZE);
- EXPECT_EQ(block->prev_free(), static_cast<Block *>(nullptr));
- EXPECT_FALSE(block->used());
+ BlockRef block = *result;
+
+ EXPECT_EQ(block.addr() % alignof(size_t), size_t{0});
+ EXPECT_TRUE(block.is_usable_space_aligned(BlockRef::MIN_ALIGN));
+
+ BlockRef last = block.next();
+ ASSERT_NE(last.addr(), BlockRef().addr());
+ EXPECT_EQ(last.addr() % alignof(size_t), size_t{0});
+
+ EXPECT_EQ(last.outer_size(), BlockRef::HEADER_SIZE);
+ EXPECT_EQ(last.prev_free().addr(), block.addr());
+ EXPECT_TRUE(last.used());
+
+ size_t block_outer_size = last.addr() - block.addr();
+ EXPECT_EQ(block.outer_size(), block_outer_size);
+ EXPECT_EQ(block.inner_size(), block_outer_size - BlockRef::HEADER_SIZE +
+ BlockRef::PREV_FIELD_SIZE);
+ EXPECT_EQ(block.prev_free().addr(), BlockRef().addr());
+ EXPECT_FALSE(block.used());
}
TEST(LlvmLibcBlockTest, CanCreateUnalignedSingleBlock) {
constexpr size_t kN = 1024;
// Force alignment, so we can un-force it below
- alignas(Block::MIN_ALIGN) array<byte, kN> bytes;
+ alignas(BlockRef::MIN_ALIGN) array<byte, kN> bytes;
span<byte> aligned(bytes);
- auto result = Block::init(aligned.subspan(1));
+ auto result = BlockRef::init(aligned.subspan(1));
EXPECT_TRUE(result.has_value());
- Block *block = *result;
- EXPECT_EQ(reinterpret_cast<uintptr_t>(block) % alignof(Block), size_t{0});
- EXPECT_TRUE(block->is_usable_space_aligned(Block::MIN_ALIGN));
+ BlockRef block = *result;
+ EXPECT_EQ(block.addr() % alignof(size_t), size_t{0});
+ EXPECT_TRUE(block.is_usable_space_aligned(BlockRef::MIN_ALIGN));
- Block *last = block->next();
- ASSERT_NE(last, static_cast<Block *>(nullptr));
- EXPECT_EQ(reinterpret_cast<uintptr_t>(last) % alignof(Block), size_t{0});
+ BlockRef last = block.next();
+ ASSERT_NE(last.addr(), BlockRef().addr());
+ EXPECT_EQ(last.addr() % alignof(size_t), size_t{0});
}
TEST(LlvmLibcBlockTest, CannotCreateTooSmallBlock) {
array<byte, 2> bytes;
- auto result = Block::init(bytes);
+ auto result = BlockRef::init(bytes);
EXPECT_FALSE(result.has_value());
}
@@ -79,55 +83,55 @@ TEST(LlvmLibcBlockTest, CanSplitBlock) {
// Choose a split position such that the next block's usable space is 512
// bytes from this one's. This should be sufficient for any machine's
// alignment.
- const size_t kSplitN = Block::inner_size(512);
+ const size_t kSplitN = BlockRef::inner_size(512);
array<byte, kN> bytes;
- auto result = Block::init(bytes);
+ auto result = BlockRef::init(bytes);
ASSERT_TRUE(result.has_value());
- auto *block1 = *result;
- size_t orig_size = block1->outer_size();
+ BlockRef block1 = *result;
+ size_t orig_size = block1.outer_size();
- result = block1->split(kSplitN);
+ result = block1.split(kSplitN);
ASSERT_TRUE(result.has_value());
- auto *block2 = *result;
+ BlockRef block2 = *result;
- EXPECT_EQ(block1->inner_size(), kSplitN);
- EXPECT_EQ(block1->outer_size(),
- kSplitN - Block::PREV_FIELD_SIZE + sizeof(Block));
+ EXPECT_EQ(block1.inner_size(), kSplitN);
+ EXPECT_EQ(block1.outer_size(),
+ kSplitN - BlockRef::PREV_FIELD_SIZE + BlockRef::HEADER_SIZE);
- EXPECT_EQ(block2->outer_size(), orig_size - block1->outer_size());
- EXPECT_FALSE(block2->used());
- EXPECT_EQ(reinterpret_cast<uintptr_t>(block2) % alignof(Block), size_t{0});
- EXPECT_TRUE(block2->is_usable_space_aligned(Block::MIN_ALIGN));
+ EXPECT_EQ(block2.outer_size(), orig_size - block1.outer_size());
+ EXPECT_FALSE(block2.used());
+ EXPECT_EQ(block2.addr() % alignof(size_t), size_t{0});
+ EXPECT_TRUE(block2.is_usable_space_aligned(BlockRef::MIN_ALIGN));
- EXPECT_EQ(block1->next(), block2);
- EXPECT_EQ(block2->prev_free(), block1);
+ EXPECT_EQ(block1.next().addr(), block2.addr());
+ EXPECT_EQ(block2.prev_free().addr(), block1.addr());
}
TEST(LlvmLibcBlockTest, CanSplitBlockUnaligned) {
constexpr size_t kN = 1024;
array<byte, kN> bytes;
- auto result = Block::init(bytes);
+ auto result = BlockRef::init(bytes);
ASSERT_TRUE(result.has_value());
- Block *block1 = *result;
- size_t orig_size = block1->outer_size();
+ BlockRef block1 = *result;
+ size_t orig_size = block1.outer_size();
constexpr size_t kSplitN = 513;
- result = block1->split(kSplitN);
+ result = block1.split(kSplitN);
ASSERT_TRUE(result.has_value());
- Block *block2 = *result;
+ BlockRef block2 = *result;
- EXPECT_GE(block1->inner_size(), kSplitN);
+ EXPECT_GE(block1.inner_size(), kSplitN);
- EXPECT_EQ(block2->outer_size(), orig_size - block1->outer_size());
- EXPECT_FALSE(block2->used());
- EXPECT_EQ(reinterpret_cast<uintptr_t>(block2) % alignof(Block), size_t{0});
- EXPECT_TRUE(block2->is_usable_space_aligned(Block::MIN_ALIGN));
+ EXPECT_EQ(block2.outer_size(), orig_size - block1.outer_size());
+ EXPECT_FALSE(block2.used());
+ EXPECT_EQ(block2.addr() % alignof(size_t), size_t{0});
+ EXPECT_TRUE(block2.is_usable_space_aligned(BlockRef::MIN_ALIGN));
- EXPECT_EQ(block1->next(), block2);
- EXPECT_EQ(block2->prev_free(), block1);
+ EXPECT_EQ(block1.next().addr(), block2.addr());
+ EXPECT_EQ(block2.prev_free().addr(), block1.addr());
}
TEST(LlvmLibcBlockTest, CanSplitMidBlock) {
@@ -135,9 +139,9 @@ TEST(LlvmLibcBlockTest, CanSplitMidBlock) {
// pointers get rewired properly.
// I.e.
// [[ BLOCK 1 ]]
- // block1->split()
+ // block1.split()
// [[ BLOCK1 ]][[ BLOCK2 ]]
- // block1->split()
+ // block1.split()
// [[ BLOCK1 ]][[ BLOCK3 ]][[ BLOCK2 ]]
constexpr size_t kN = 1024;
@@ -145,33 +149,33 @@ TEST(LlvmLibcBlockTest, CanSplitMidBlock) {
constexpr size_t kSplit2 = 256;
array<byte, kN> bytes;
- auto result = Block::init(bytes);
+ auto result = BlockRef::init(bytes);
ASSERT_TRUE(result.has_value());
- Block *block1 = *result;
+ BlockRef block1 = *result;
- result = block1->split(kSplit1);
+ result = block1.split(kSplit1);
ASSERT_TRUE(result.has_value());
- Block *block2 = *result;
+ BlockRef block2 = *result;
- result = block1->split(kSplit2);
+ result = block1.split(kSplit2);
ASSERT_TRUE(result.has_value());
- Block *block3 = *result;
+ BlockRef block3 = *result;
- EXPECT_EQ(block1->next(), block3);
- EXPECT_EQ(block3->prev_free(), block1);
- EXPECT_EQ(block3->next(), block2);
- EXPECT_EQ(block2->prev_free(), block3);
+ EXPECT_EQ(block1.next().addr(), block3.addr());
+ EXPECT_EQ(block3.prev_free().addr(), block1.addr());
+ EXPECT_EQ(block3.next().addr(), block2.addr());
+ EXPECT_EQ(block2.prev_free().addr(), block3.addr());
}
TEST(LlvmLibcBlockTest, CannotSplitTooSmallBlock) {
constexpr size_t kN = 64;
array<byte, kN> bytes;
- auto result = Block::init(bytes);
+ auto result = BlockRef::init(bytes);
ASSERT_TRUE(result.has_value());
- Block *block = *result;
+ BlockRef block = *result;
- result = block->split(block->inner_size() + 1);
+ result = block.split(block.inner_size() + 1);
ASSERT_FALSE(result.has_value());
}
@@ -179,11 +183,11 @@ TEST(LlvmLibcBlockTest, CannotSplitBlockWithoutHeaderSpace) {
constexpr size_t kN = 1024;
array<byte, kN> bytes;
- auto result = Block::init(bytes);
+ auto result = BlockRef::init(bytes);
ASSERT_TRUE(result.has_value());
- Block *block = *result;
+ BlockRef block = *result;
- result = block->split(block->inner_size() - sizeof(Block) + 1);
+ result = block.split(block.inner_size() - BlockRef::HEADER_SIZE + 1);
ASSERT_FALSE(result.has_value());
}
@@ -192,11 +196,11 @@ TEST(LlvmLibcBlockTest, CannotMakeBlockLargerInSplit) {
constexpr size_t kN = 1024;
array<byte, kN> bytes;
- auto result = Block::init(bytes);
+ auto result = BlockRef::init(bytes);
ASSERT_TRUE(result.has_value());
- Block *block = *result;
+ BlockRef block = *result;
- result = block->split(block->inner_size() + 1);
+ result = block.split(block.inner_size() + 1);
ASSERT_FALSE(result.has_value());
}
@@ -205,13 +209,13 @@ TEST(LlvmLibcBlockTest, CanMakeMinimalSizeFirstBlock) {
constexpr size_t kN = 1024;
array<byte, kN> bytes;
- auto result = Block::init(bytes);
+ auto result = BlockRef::init(bytes);
ASSERT_TRUE(result.has_value());
- Block *block = *result;
+ BlockRef block = *result;
- result = block->split(0);
+ result = block.split(0);
ASSERT_TRUE(result.has_value());
- EXPECT_LE(block->outer_size(), sizeof(Block) + Block::MIN_ALIGN);
+ EXPECT_LE(block.outer_size(), BlockRef::HEADER_SIZE + BlockRef::MIN_ALIGN);
}
TEST(LlvmLibcBlockTest, CanMakeMinimalSizeSecondBlock) {
@@ -219,33 +223,34 @@ TEST(LlvmLibcBlockTest, CanMakeMinimalSizeSecondBlock) {
constexpr size_t kN = 1024;
array<byte, kN> bytes;
- auto result = Block::init(bytes);
+ auto result = BlockRef::init(bytes);
ASSERT_TRUE(result.has_value());
- Block *block1 = *result;
+ BlockRef block1 = *result;
- result = block1->split(Block::prev_possible_block_start(
- reinterpret_cast<uintptr_t>(block1->next())) -
- reinterpret_cast<uintptr_t>(block1->usable_space()) +
- Block::PREV_FIELD_SIZE);
+ result =
+ block1.split(BlockRef::prev_possible_block_start(block1.next().addr()) -
+ reinterpret_cast<uintptr_t>(block1.usable_space()) +
+ BlockRef::PREV_FIELD_SIZE);
ASSERT_TRUE(result.has_value());
- EXPECT_LE((*result)->outer_size(), sizeof(Block) + Block::MIN_ALIGN);
+ EXPECT_LE((*result).outer_size(),
+ BlockRef::HEADER_SIZE + BlockRef::MIN_ALIGN);
}
TEST(LlvmLibcBlockTest, CanMarkBlockUsed) {
constexpr size_t kN = 1024;
array<byte, kN> bytes;
- auto result = Block::init(bytes);
+ auto result = BlockRef::init(bytes);
ASSERT_TRUE(result.has_value());
- Block *block = *result;
- size_t orig_size = block->outer_size();
+ BlockRef block = *result;
+ size_t orig_size = block.outer_size();
- block->mark_used();
- EXPECT_TRUE(block->used());
- EXPECT_EQ(block->outer_size(), orig_size);
+ block.mark_used();
+ EXPECT_TRUE(block.used());
+ EXPECT_EQ(block.outer_size(), orig_size);
- block->mark_free();
- EXPECT_FALSE(block->used());
+ block.mark_free();
+ EXPECT_FALSE(block.used());
}
TEST(LlvmLibcBlockTest, CanSplitUsedBlock) {
@@ -253,19 +258,19 @@ TEST(LlvmLibcBlockTest, CanSplitUsedBlock) {
constexpr size_t kSplitN = 512;
array<byte, kN> bytes;
- auto result = Block::init(bytes);
+ auto result = BlockRef::init(bytes);
ASSERT_TRUE(result.has_value());
- Block *block = *result;
+ BlockRef block = *result;
- block->mark_used();
- result = block->split(kSplitN);
+ block.mark_used();
+ result = block.split(kSplitN);
ASSERT_TRUE(result.has_value());
- Block *new_block = *result;
+ BlockRef new_block = *result;
- EXPECT_TRUE(block->used());
- EXPECT_FALSE(new_block->used());
- EXPECT_EQ(block->next(), new_block);
- EXPECT_EQ(new_block->prev_free(), static_cast<Block *>(nullptr));
+ EXPECT_TRUE(block.used());
+ EXPECT_FALSE(new_block.used());
+ EXPECT_EQ(block.next().addr(), new_block.addr());
+ EXPECT_EQ(new_block.prev_free().addr(), BlockRef().addr());
}
TEST(LlvmLibcBlockTest, CanMergeWithNextBlock) {
@@ -275,25 +280,25 @@ TEST(LlvmLibcBlockTest, CanMergeWithNextBlock) {
constexpr size_t kSplit1 = 512;
constexpr size_t kSplit2 = 256;
array<byte, kN> bytes;
- auto result = Block::init(bytes);
+ auto result = BlockRef::init(bytes);
ASSERT_TRUE(result.has_value());
- Block *block1 = *result;
- size_t total_size = block1->outer_size();
+ BlockRef block1 = *result;
+ size_t total_size = block1.outer_size();
- result = block1->split(kSplit1);
+ result = block1.split(kSplit1);
ASSERT_TRUE(result.has_value());
- result = block1->split(kSplit2);
- size_t block1_size = block1->outer_size();
+ result = block1.split(kSplit2);
+ size_t block1_size = block1.outer_size();
ASSERT_TRUE(result.has_value());
- Block *block3 = *result;
+ BlockRef block3 = *result;
- EXPECT_TRUE(block3->merge_next());
+ EXPECT_TRUE(block3.merge_next());
- EXPECT_EQ(block1->next(), block3);
- EXPECT_EQ(block3->prev_free(), block1);
- EXPECT_EQ(block1->outer_size(), block1_size);
- EXPECT_EQ(block3->outer_size(), total_size - block1->outer_size());
+ EXPECT_EQ(block1.next().addr(), block3.addr());
+ EXPECT_EQ(block3.prev_free().addr(), block1.addr());
+ EXPECT_EQ(block1.outer_size(), block1_size);
+ EXPECT_EQ(block3.outer_size(), total_size - block1.outer_size());
}
TEST(LlvmLibcBlockTest, CannotMergeWithFirstOrLastBlock) {
@@ -301,16 +306,16 @@ TEST(LlvmLibcBlockTest, CannotMergeWithFirstOrLastBlock) {
constexpr size_t kSplitN = 512;
array<byte, kN> bytes;
- auto result = Block::init(bytes);
+ auto result = BlockRef::init(bytes);
ASSERT_TRUE(result.has_value());
- Block *block1 = *result;
+ BlockRef block1 = *result;
// Do a split, just to check that the checks on next/prev are different...
- result = block1->split(kSplitN);
+ result = block1.split(kSplitN);
ASSERT_TRUE(result.has_value());
- Block *block2 = *result;
+ BlockRef block2 = *result;
- EXPECT_FALSE(block2->merge_next());
+ EXPECT_FALSE(block2.merge_next());
}
TEST(LlvmLibcBlockTest, CannotMergeUsedBlock) {
@@ -318,40 +323,40 @@ TEST(LlvmLibcBlockTest, CannotMergeUsedBlock) {
constexpr size_t kSplitN = 512;
array<byte, kN> bytes;
- auto result = Block::init(bytes);
+ auto result = BlockRef::init(bytes);
ASSERT_TRUE(result.has_value());
- Block *block = *result;
+ BlockRef block = *result;
// Do a split, just to check that the checks on next/prev are different...
- result = block->split(kSplitN);
+ result = block.split(kSplitN);
ASSERT_TRUE(result.has_value());
- block->mark_used();
- EXPECT_FALSE(block->merge_next());
+ block.mark_used();
+ EXPECT_FALSE(block.merge_next());
}
TEST(LlvmLibcBlockTest, CanGetBlockFromUsableSpace) {
array<byte, 1024> bytes;
- auto result = Block::init(bytes);
+ auto result = BlockRef::init(bytes);
ASSERT_TRUE(result.has_value());
- Block *block1 = *result;
+ BlockRef block1 = *result;
- void *ptr = block1->usable_space();
- Block *block2 = Block::from_usable_space(ptr);
- EXPECT_EQ(block1, block2);
+ void *ptr = block1.usable_space();
+ BlockRef block2 = BlockRef::from_usable_space(ptr);
+ EXPECT_EQ(block1.addr(), block2.addr());
}
TEST(LlvmLibcBlockTest, CanGetConstBlockFromUsableSpace) {
constexpr size_t kN = 1024;
array<byte, kN> bytes{};
- auto result = Block::init(bytes);
+ auto result = BlockRef::init(bytes);
ASSERT_TRUE(result.has_value());
- const Block *block1 = *result;
+ BlockRef block1 = *result;
- const void *ptr = block1->usable_space();
- const Block *block2 = Block::from_usable_space(ptr);
- EXPECT_EQ(block1, block2);
+ const void *ptr = block1.usable_space();
+ BlockRef block2 = BlockRef::from_usable_space(ptr);
+ EXPECT_EQ(block1.addr(), block2.addr());
}
TEST(LlvmLibcBlockTest, Allocate) {
@@ -360,30 +365,30 @@ TEST(LlvmLibcBlockTest, Allocate) {
// Ensure we can allocate everything up to the block size within this block.
for (size_t i = 0; i < kN; ++i) {
array<byte, kN> bytes;
- auto result = Block::init(bytes);
+ auto result = BlockRef::init(bytes);
ASSERT_TRUE(result.has_value());
- Block *block = *result;
+ BlockRef block = *result;
- if (i > block->inner_size())
+ if (i > block.inner_size())
continue;
- auto info = Block::allocate(block, Block::MIN_ALIGN, i);
- EXPECT_NE(info.block, static_cast<Block *>(nullptr));
+ auto info = BlockRef::allocate(block, BlockRef::MIN_ALIGN, i);
+ EXPECT_NE(info.block.addr(), BlockRef().addr());
}
// Ensure we can allocate a byte at every guaranteeable alignment.
- for (size_t i = 1; i < kN / Block::MIN_ALIGN; ++i) {
+ for (size_t i = 1; i < kN / BlockRef::MIN_ALIGN; ++i) {
array<byte, kN> bytes;
- auto result = Block::init(bytes);
+ auto result = BlockRef::init(bytes);
ASSERT_TRUE(result.has_value());
- Block *block = *result;
+ BlockRef block = *result;
- size_t alignment = i * Block::MIN_ALIGN;
- if (Block::min_size_for_allocation(alignment, 1) > block->inner_size())
+ size_t alignment = i * BlockRef::MIN_ALIGN;
+ if (BlockRef::min_size_for_allocation(alignment, 1) > block.inner_size())
continue;
- auto info = Block::allocate(block, alignment, 1);
- EXPECT_NE(info.block, static_cast<Block *>(nullptr));
+ auto info = BlockRef::allocate(block, alignment, 1);
+ EXPECT_NE(info.block.addr(), BlockRef().addr());
}
}
@@ -391,98 +396,97 @@ TEST(LlvmLibcBlockTest, AllocateAlreadyAligned) {
constexpr size_t kN = 1024;
array<byte, kN> bytes;
- auto result = Block::init(bytes);
+ auto result = BlockRef::init(bytes);
ASSERT_TRUE(result.has_value());
- Block *block = *result;
- uintptr_t orig_end = reinterpret_cast<uintptr_t>(block) + block->outer_size();
+ BlockRef block = *result;
+ uintptr_t orig_end = block.addr() + block.outer_size();
- constexpr size_t SIZE = Block::PREV_FIELD_SIZE + 1;
+ constexpr size_t SIZE = BlockRef::PREV_FIELD_SIZE + 1;
auto [aligned_block, prev, next] =
- Block::allocate(block, Block::MIN_ALIGN, SIZE);
+ BlockRef::allocate(block, BlockRef::MIN_ALIGN, SIZE);
// Since this is already aligned, there should be no previous block.
- EXPECT_EQ(prev, static_cast<Block *>(nullptr));
+ EXPECT_EQ(prev.addr(), BlockRef().addr());
// Ensure we the block is aligned and large enough.
- EXPECT_NE(aligned_block, static_cast<Block *>(nullptr));
- EXPECT_TRUE(aligned_block->is_usable_space_aligned(Block::MIN_ALIGN));
- EXPECT_GE(aligned_block->inner_size(), SIZE);
+ EXPECT_NE(aligned_block.addr(), BlockRef().addr());
+ EXPECT_TRUE(aligned_block.is_usable_space_aligned(BlockRef::MIN_ALIGN));
+ EXPECT_GE(aligned_block.inner_size(), SIZE);
// Check the next block.
- EXPECT_NE(next, static_cast<Block *>(nullptr));
- EXPECT_EQ(aligned_block->next(), next);
- EXPECT_EQ(reinterpret_cast<uintptr_t>(next) + next->outer_size(), orig_end);
+ EXPECT_NE(next.addr(), BlockRef().addr());
+ EXPECT_EQ(aligned_block.next().addr(), next.addr());
+ EXPECT_EQ(next.addr() + next.outer_size(), orig_end);
}
TEST(LlvmLibcBlockTest, AllocateNeedsAlignment) {
constexpr size_t kN = 1024;
array<byte, kN> bytes;
- auto result = Block::init(bytes);
+ auto result = BlockRef::init(bytes);
ASSERT_TRUE(result.has_value());
- Block *block = *result;
+ BlockRef block = *result;
- uintptr_t orig_end = reinterpret_cast<uintptr_t>(block) + block->outer_size();
+ uintptr_t orig_end = block.addr() + block.outer_size();
// Now pick an alignment such that the usable space is not already aligned to
// it. We want to explicitly test that the block will split into one before
// it.
- size_t alignment = Block::MIN_ALIGN;
- while (block->is_usable_space_aligned(alignment))
- alignment += Block::MIN_ALIGN;
+ size_t alignment = BlockRef::MIN_ALIGN;
+ while (block.is_usable_space_aligned(alignment))
+ alignment += BlockRef::MIN_ALIGN;
- auto [aligned_block, prev, next] = Block::allocate(block, alignment, 10);
+ auto [aligned_block, prev, next] = BlockRef::allocate(block, alignment, 10);
// Check the previous block was created appropriately. Since this block is the
// first block, a new one should be made before this.
- EXPECT_NE(prev, static_cast<Block *>(nullptr));
- EXPECT_EQ(aligned_block->prev_free(), prev);
- EXPECT_EQ(prev->next(), aligned_block);
- EXPECT_EQ(prev->outer_size(), reinterpret_cast<uintptr_t>(aligned_block) -
- reinterpret_cast<uintptr_t>(prev));
+ EXPECT_NE(prev.addr(), BlockRef().addr());
+ EXPECT_EQ(aligned_block.prev_free().addr(), prev.addr());
+ EXPECT_EQ(prev.next().addr(), aligned_block.addr());
+ EXPECT_EQ(prev.outer_size(), aligned_block.addr() - prev.addr());
// Ensure we the block is aligned and the size we expect.
- EXPECT_NE(next, static_cast<Block *>(nullptr));
- EXPECT_TRUE(aligned_block->is_usable_space_aligned(alignment));
+ EXPECT_NE(next.addr(), BlockRef().addr());
+ EXPECT_TRUE(aligned_block.is_usable_space_aligned(alignment));
// Check the next block.
- EXPECT_NE(next, static_cast<Block *>(nullptr));
- EXPECT_EQ(aligned_block->next(), next);
- EXPECT_EQ(reinterpret_cast<uintptr_t>(next) + next->outer_size(), orig_end);
+ EXPECT_NE(next.addr(), BlockRef().addr());
+ EXPECT_EQ(aligned_block.next().addr(), next.addr());
+ EXPECT_EQ(next.addr() + next.outer_size(), orig_end);
}
TEST(LlvmLibcBlockTest, PreviousBlockMergedIfNotFirst) {
constexpr size_t kN = 1024;
array<byte, kN> bytes;
- auto result = Block::init(bytes);
+ auto result = BlockRef::init(bytes);
ASSERT_TRUE(result.has_value());
- Block *block = *result;
+ BlockRef block = *result;
// Split the block roughly halfway and work on the second half.
- auto result2 = block->split(kN / 2);
+ auto result2 = block.split(kN / 2);
ASSERT_TRUE(result2.has_value());
- Block *newblock = *result2;
- ASSERT_EQ(newblock->prev_free(), block);
- size_t old_prev_size = block->outer_size();
+ BlockRef newblock = *result2;
+ ASSERT_EQ(newblock.prev_free().addr(), block.addr());
+ size_t old_prev_size = block.outer_size();
// Now pick an alignment such that the usable space is not already aligned to
// it. We want to explicitly test that the block will split into one before
// it.
- size_t alignment = Block::MIN_ALIGN;
- while (newblock->is_usable_space_aligned(alignment))
- alignment += Block::MIN_ALIGN;
+ size_t alignment = BlockRef::MIN_ALIGN;
+ while (newblock.is_usable_space_aligned(alignment))
+ alignment += BlockRef::MIN_ALIGN;
// Ensure we can allocate in the new block.
- auto [aligned_block, prev, next] = Block::allocate(newblock, alignment, 1);
+ auto [aligned_block, prev, next] = BlockRef::allocate(newblock, alignment, 1);
// Now there should be no new previous block. Instead, the padding we did
// create should be merged into the original previous block.
- EXPECT_EQ(prev, static_cast<Block *>(nullptr));
- EXPECT_EQ(aligned_block->prev_free(), block);
- EXPECT_EQ(block->next(), aligned_block);
- EXPECT_GT(block->outer_size(), old_prev_size);
+ EXPECT_EQ(prev.addr(), BlockRef().addr());
+ EXPECT_EQ(aligned_block.prev_free().addr(), block.addr());
+ EXPECT_EQ(block.next().addr(), aligned_block.addr());
+ EXPECT_GT(block.outer_size(), old_prev_size);
}
TEST(LlvmLibcBlockTest, CanRemergeBlockAllocations) {
@@ -493,40 +497,40 @@ TEST(LlvmLibcBlockTest, CanRemergeBlockAllocations) {
constexpr size_t kN = 1024;
array<byte, kN> bytes;
- auto result = Block::init(bytes);
+ auto result = BlockRef::init(bytes);
ASSERT_TRUE(result.has_value());
- Block *block = *result;
+ BlockRef block = *result;
- Block *orig_block = block;
- size_t orig_size = orig_block->outer_size();
+ BlockRef orig_block = block;
+ size_t orig_size = orig_block.outer_size();
- Block *last = block->next();
+ BlockRef last = block.next();
- ASSERT_EQ(block->prev_free(), static_cast<Block *>(nullptr));
+ ASSERT_EQ(block.prev_free().addr(), BlockRef().addr());
// Now pick an alignment such that the usable space is not already aligned to
// it. We want to explicitly test that the block will split into one before
// it.
- size_t alignment = Block::MIN_ALIGN;
- while (block->is_usable_space_aligned(alignment))
- alignment += Block::MIN_ALIGN;
+ size_t alignment = BlockRef::MIN_ALIGN;
+ while (block.is_usable_space_aligned(alignment))
+ alignment += BlockRef::MIN_ALIGN;
- auto [aligned_block, prev, next] = Block::allocate(block, alignment, 1);
+ auto [aligned_block, prev, next] = BlockRef::allocate(block, alignment, 1);
// Check we have the appropriate blocks.
- ASSERT_NE(prev, static_cast<Block *>(nullptr));
- ASSERT_EQ(aligned_block->prev_free(), prev);
- EXPECT_NE(next, static_cast<Block *>(nullptr));
- EXPECT_EQ(aligned_block->next(), next);
- EXPECT_EQ(next->next(), last);
+ ASSERT_NE(prev.addr(), BlockRef().addr());
+ ASSERT_EQ(aligned_block.prev_free().addr(), prev.addr());
+ EXPECT_NE(next.addr(), BlockRef().addr());
+ EXPECT_EQ(aligned_block.next().addr(), next.addr());
+ EXPECT_EQ(next.next().addr(), last.addr());
// Now check for successful merges.
- EXPECT_TRUE(prev->merge_next());
- EXPECT_EQ(prev->next(), next);
- EXPECT_TRUE(prev->merge_next());
- EXPECT_EQ(prev->next(), last);
+ EXPECT_TRUE(prev.merge_next());
+ EXPECT_EQ(prev.next().addr(), next.addr());
+ EXPECT_TRUE(prev.merge_next());
+ EXPECT_EQ(prev.next().addr(), last.addr());
// We should have the original buffer.
- EXPECT_EQ(prev, orig_block);
- EXPECT_EQ(prev->outer_size(), orig_size);
+ EXPECT_EQ(prev.addr(), orig_block.addr());
+ EXPECT_EQ(prev.outer_size(), orig_size);
}
diff --git a/libc/test/src/__support/freelist_heap_test.cpp b/libc/test/src/__support/freelist_heap_test.cpp
index 3002834a51619..68cc30152cd6d 100644
--- a/libc/test/src/__support/freelist_heap_test.cpp
+++ b/libc/test/src/__support/freelist_heap_test.cpp
@@ -1,10 +1,15 @@
-//===-- Unittests for freelist_heap ---------------------------------------===//
+//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Unittests for freelist_heap.
+///
+//===----------------------------------------------------------------------===//
#include "src/__support/CPP/span.h"
#include "src/__support/freelist_heap.h"
@@ -23,7 +28,7 @@ asm(R"(
__llvm_libc_heap_limit:
)");
-using LIBC_NAMESPACE::Block;
+using LIBC_NAMESPACE::BlockRef;
using LIBC_NAMESPACE::freelist_heap;
using LIBC_NAMESPACE::FreeListHeap;
using LIBC_NAMESPACE::FreeListHeapBuffer;
@@ -124,12 +129,12 @@ TEST_FOR_EACH_ALLOCATOR(ReturnedPointersAreAligned, 2048) {
void *ptr1 = allocator.allocate(1);
uintptr_t ptr1_start = reinterpret_cast<uintptr_t>(ptr1);
- EXPECT_EQ(ptr1_start % Block::MIN_ALIGN, static_cast<size_t>(0));
+ EXPECT_EQ(ptr1_start % BlockRef::MIN_ALIGN, static_cast<size_t>(0));
void *ptr2 = allocator.allocate(1);
uintptr_t ptr2_start = reinterpret_cast<uintptr_t>(ptr2);
- EXPECT_EQ(ptr2_start % Block::MIN_ALIGN, static_cast<size_t>(0));
+ EXPECT_EQ(ptr2_start % BlockRef::MIN_ALIGN, static_cast<size_t>(0));
}
TEST_FOR_EACH_ALLOCATOR(CanRealloc, 2048) {
diff --git a/libc/test/src/__support/freelist_test.cpp b/libc/test/src/__support/freelist_test.cpp
index bd5ecec45d921..580f59ae62bd8 100644
--- a/libc/test/src/__support/freelist_test.cpp
+++ b/libc/test/src/__support/freelist_test.cpp
@@ -1,53 +1,58 @@
-//===-- Unittests for a freelist --------------------------------*- C++ -*-===//
+//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Unittests for a freelist.
+///
+//===----------------------------------------------------------------------===//
#include <stddef.h>
#include "src/__support/freelist.h"
#include "test/UnitTest/Test.h"
-using LIBC_NAMESPACE::Block;
+using LIBC_NAMESPACE::BlockRef;
using LIBC_NAMESPACE::FreeList;
using LIBC_NAMESPACE::cpp::byte;
using LIBC_NAMESPACE::cpp::optional;
TEST(LlvmLibcFreeList, FreeList) {
byte mem[1024];
- optional<Block *> maybeBlock = Block::init(mem);
+ optional<BlockRef> maybeBlock = BlockRef::init(mem);
ASSERT_TRUE(maybeBlock.has_value());
- Block *block1 = *maybeBlock;
+ BlockRef block1 = *maybeBlock;
- maybeBlock = block1->split(128);
+ maybeBlock = block1.split(128);
ASSERT_TRUE(maybeBlock.has_value());
- Block *block2 = *maybeBlock;
+ BlockRef block2 = *maybeBlock;
- maybeBlock = block2->split(128);
+ maybeBlock = block2.split(128);
ASSERT_TRUE(maybeBlock.has_value());
FreeList list;
list.push(block1);
ASSERT_FALSE(list.empty());
- EXPECT_EQ(list.front(), block1);
+ EXPECT_EQ(list.front().addr(), block1.addr());
list.push(block2);
- EXPECT_EQ(list.front(), block1);
+ EXPECT_EQ(list.front().addr(), block1.addr());
list.pop();
ASSERT_FALSE(list.empty());
- EXPECT_EQ(list.front(), block2);
+ EXPECT_EQ(list.front().addr(), block2.addr());
list.pop();
ASSERT_TRUE(list.empty());
list.push(block1);
list.push(block2);
- list.remove(reinterpret_cast<FreeList::Node *>(block2->usable_space()));
- EXPECT_EQ(list.front(), block1);
+ list.remove(reinterpret_cast<FreeList::Node *>(block2.usable_space()));
+ EXPECT_EQ(list.front().addr(), block1.addr());
list.pop();
ASSERT_TRUE(list.empty());
}
diff --git a/libc/test/src/__support/freestore_test.cpp b/libc/test/src/__support/freestore_test.cpp
index 7017d6b9ebe93..61103a9126c08 100644
--- a/libc/test/src/__support/freestore_test.cpp
+++ b/libc/test/src/__support/freestore_test.cpp
@@ -1,17 +1,22 @@
-//===-- Unittests for a freestore -------------------------------*- C++ -*-===//
+//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Unittests for a freestore.
+///
+//===----------------------------------------------------------------------===//
#include <stddef.h>
#include "src/__support/freestore.h"
#include "test/UnitTest/Test.h"
-using LIBC_NAMESPACE::Block;
+using LIBC_NAMESPACE::BlockRef;
using LIBC_NAMESPACE::FreeList;
using LIBC_NAMESPACE::FreeStore;
using LIBC_NAMESPACE::FreeTrie;
@@ -21,44 +26,46 @@ using LIBC_NAMESPACE::cpp::optional;
// Inserting or removing blocks too small to be tracked does nothing.
TEST(LlvmLibcFreeStore, TooSmall) {
byte mem[1024];
- optional<Block *> maybeBlock = Block::init(mem);
+ optional<BlockRef> maybeBlock = BlockRef::init(mem);
ASSERT_TRUE(maybeBlock.has_value());
- Block *too_small = *maybeBlock;
- maybeBlock = too_small->split(Block::PREV_FIELD_SIZE);
+ BlockRef too_small = *maybeBlock;
+ maybeBlock = too_small.split(BlockRef::PREV_FIELD_SIZE);
ASSERT_TRUE(maybeBlock.has_value());
// On platforms with high alignment the smallest legal block may be large
// enough for a node.
- if (too_small->outer_size() >= sizeof(Block) + sizeof(FreeList::Node))
+ if (too_small.outer_size() >= BlockRef::HEADER_SIZE + sizeof(FreeList::Node))
return;
- Block *remainder = *maybeBlock;
+ BlockRef remainder = *maybeBlock;
FreeStore store;
store.set_range({0, 4096});
store.insert(too_small);
store.insert(remainder);
- EXPECT_EQ(store.remove_best_fit(too_small->inner_size()), remainder);
+ EXPECT_EQ(store.remove_best_fit(too_small.inner_size()).addr(),
+ remainder.addr());
store.remove(too_small);
}
TEST(LlvmLibcFreeStore, RemoveBestFit) {
byte mem[1024];
- optional<Block *> maybeBlock = Block::init(mem);
+ optional<BlockRef> maybeBlock = BlockRef::init(mem);
ASSERT_TRUE(maybeBlock.has_value());
- Block *smallest = *maybeBlock;
- maybeBlock = smallest->split(sizeof(FreeList::Node) + Block::PREV_FIELD_SIZE);
+ BlockRef smallest = *maybeBlock;
+ maybeBlock =
+ smallest.split(sizeof(FreeList::Node) + BlockRef::PREV_FIELD_SIZE);
ASSERT_TRUE(maybeBlock.has_value());
- Block *largest_small = *maybeBlock;
- maybeBlock = largest_small->split(sizeof(FreeTrie::Node) +
- Block::PREV_FIELD_SIZE - Block::MIN_ALIGN);
+ BlockRef largest_small = *maybeBlock;
+ maybeBlock = largest_small.split(
+ sizeof(FreeTrie::Node) + BlockRef::PREV_FIELD_SIZE - BlockRef::MIN_ALIGN);
ASSERT_TRUE(maybeBlock.has_value());
- if (largest_small->inner_size() == smallest->inner_size())
+ if (largest_small.inner_size() == smallest.inner_size())
largest_small = smallest;
- ASSERT_GE(largest_small->inner_size(), smallest->inner_size());
+ ASSERT_GE(largest_small.inner_size(), smallest.inner_size());
- Block *remainder = *maybeBlock;
+ BlockRef remainder = *maybeBlock;
FreeStore store;
store.set_range({0, 4096});
@@ -68,32 +75,37 @@ TEST(LlvmLibcFreeStore, RemoveBestFit) {
store.insert(remainder);
// Find exact match for smallest.
- ASSERT_EQ(store.remove_best_fit(smallest->inner_size()), smallest);
+ ASSERT_EQ(store.remove_best_fit(smallest.inner_size()).addr(),
+ smallest.addr());
store.insert(smallest);
// Find exact match for largest.
- ASSERT_EQ(store.remove_best_fit(largest_small->inner_size()), largest_small);
+ ASSERT_EQ(store.remove_best_fit(largest_small.inner_size()).addr(),
+ largest_small.addr());
store.insert(largest_small);
// Search small list for best fit.
- Block *next_smallest = largest_small == smallest ? remainder : largest_small;
- ASSERT_EQ(store.remove_best_fit(smallest->inner_size() + 1), next_smallest);
+ BlockRef next_smallest =
+ largest_small == smallest ? remainder : largest_small;
+ ASSERT_EQ(store.remove_best_fit(smallest.inner_size() + 1).addr(),
+ next_smallest.addr());
store.insert(next_smallest);
// Continue search for best fit to large blocks.
- EXPECT_EQ(store.remove_best_fit(largest_small->inner_size() + 1), remainder);
+ EXPECT_EQ(store.remove_best_fit(largest_small.inner_size() + 1).addr(),
+ remainder.addr());
}
TEST(LlvmLibcFreeStore, Remove) {
byte mem[1024];
- optional<Block *> maybeBlock = Block::init(mem);
+ optional<BlockRef> maybeBlock = BlockRef::init(mem);
ASSERT_TRUE(maybeBlock.has_value());
- Block *small = *maybeBlock;
- maybeBlock = small->split(sizeof(FreeList::Node) + Block::PREV_FIELD_SIZE);
+ BlockRef small = *maybeBlock;
+ maybeBlock = small.split(sizeof(FreeList::Node) + BlockRef::PREV_FIELD_SIZE);
ASSERT_TRUE(maybeBlock.has_value());
- Block *remainder = *maybeBlock;
+ BlockRef remainder = *maybeBlock;
FreeStore store;
store.set_range({0, 4096});
@@ -101,9 +113,9 @@ TEST(LlvmLibcFreeStore, Remove) {
store.insert(remainder);
store.remove(remainder);
- ASSERT_EQ(store.remove_best_fit(remainder->inner_size()),
- static_cast<Block *>(nullptr));
+ ASSERT_EQ(store.remove_best_fit(remainder.inner_size()).addr(),
+ BlockRef().addr());
store.remove(small);
- ASSERT_EQ(store.remove_best_fit(small->inner_size()),
- static_cast<Block *>(nullptr));
+ ASSERT_EQ(store.remove_best_fit(small.inner_size()).addr(),
+ BlockRef().addr());
}
diff --git a/libc/test/src/__support/freetrie_test.cpp b/libc/test/src/__support/freetrie_test.cpp
index 5663a01687294..bf3284b2faf64 100644
--- a/libc/test/src/__support/freetrie_test.cpp
+++ b/libc/test/src/__support/freetrie_test.cpp
@@ -1,17 +1,22 @@
-//===-- Unittests for a freetrie --------------------------------*- C++ -*-===//
+//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Unittests for a freetrie.
+///
+//===----------------------------------------------------------------------===//
#include <stddef.h>
#include "src/__support/freetrie.h"
#include "test/UnitTest/Test.h"
-using LIBC_NAMESPACE::Block;
+using LIBC_NAMESPACE::BlockRef;
using LIBC_NAMESPACE::FreeTrie;
using LIBC_NAMESPACE::cpp::byte;
using LIBC_NAMESPACE::cpp::optional;
@@ -21,65 +26,67 @@ TEST(LlvmLibcFreeTrie, FindBestFitRoot) {
EXPECT_EQ(trie.find_best_fit(123), static_cast<FreeTrie::Node *>(nullptr));
byte mem[1024];
- optional<Block *> maybeBlock = Block::init(mem);
+ optional<BlockRef> maybeBlock = BlockRef::init(mem);
ASSERT_TRUE(maybeBlock.has_value());
- Block *block = *maybeBlock;
+ BlockRef block = *maybeBlock;
trie.push(block);
FreeTrie::Node *root = trie.find_best_fit(0);
- ASSERT_EQ(root->block(), block);
- EXPECT_EQ(trie.find_best_fit(block->inner_size() - 1), root);
- EXPECT_EQ(trie.find_best_fit(block->inner_size()), root);
- EXPECT_EQ(trie.find_best_fit(block->inner_size() + 1),
+ ASSERT_EQ(root->block().addr(), block.addr());
+ EXPECT_EQ(trie.find_best_fit(block.inner_size() - 1), root);
+ EXPECT_EQ(trie.find_best_fit(block.inner_size()), root);
+ EXPECT_EQ(trie.find_best_fit(block.inner_size() + 1),
static_cast<FreeTrie::Node *>(nullptr));
EXPECT_EQ(trie.find_best_fit(4095), static_cast<FreeTrie::Node *>(nullptr));
}
TEST(LlvmLibcFreeTrie, FindBestFitLower) {
byte mem[4096];
- optional<Block *> maybeBlock = Block::init(mem);
+ optional<BlockRef> maybeBlock = BlockRef::init(mem);
ASSERT_TRUE(maybeBlock.has_value());
- Block *lower = *maybeBlock;
- maybeBlock = lower->split(512);
+ BlockRef lower = *maybeBlock;
+ maybeBlock = lower.split(512);
ASSERT_TRUE(maybeBlock.has_value());
- Block *root = *maybeBlock;
+ BlockRef root = *maybeBlock;
FreeTrie trie({0, 4096});
trie.push(root);
trie.push(lower);
- EXPECT_EQ(trie.find_best_fit(0)->block(), lower);
+ EXPECT_EQ(trie.find_best_fit(0)->block().addr(), lower.addr());
}
TEST(LlvmLibcFreeTrie, FindBestFitUpper) {
byte mem[4096];
- optional<Block *> maybeBlock = Block::init(mem);
+ optional<BlockRef> maybeBlock = BlockRef::init(mem);
ASSERT_TRUE(maybeBlock.has_value());
- Block *root = *maybeBlock;
- maybeBlock = root->split(512);
+ BlockRef root = *maybeBlock;
+ maybeBlock = root.split(512);
ASSERT_TRUE(maybeBlock.has_value());
- Block *upper = *maybeBlock;
+ BlockRef upper = *maybeBlock;
FreeTrie trie({0, 4096});
trie.push(root);
trie.push(upper);
- EXPECT_EQ(trie.find_best_fit(root->inner_size() + 1)->block(), upper);
+ EXPECT_EQ(trie.find_best_fit(root.inner_size() + 1)->block().addr(),
+ upper.addr());
// The upper subtrie should be skipped if it could not contain a better fit.
- EXPECT_EQ(trie.find_best_fit(root->inner_size() - 1)->block(), root);
+ EXPECT_EQ(trie.find_best_fit(root.inner_size() - 1)->block().addr(),
+ root.addr());
}
TEST(LlvmLibcFreeTrie, FindBestFitLowerAndUpper) {
byte mem[4096];
- optional<Block *> maybeBlock = Block::init(mem);
+ optional<BlockRef> maybeBlock = BlockRef::init(mem);
ASSERT_TRUE(maybeBlock.has_value());
- Block *root = *maybeBlock;
- maybeBlock = root->split(1024);
+ BlockRef root = *maybeBlock;
+ maybeBlock = root.split(1024);
ASSERT_TRUE(maybeBlock.has_value());
- Block *lower = *maybeBlock;
- maybeBlock = lower->split(128);
+ BlockRef lower = *maybeBlock;
+ maybeBlock = lower.split(128);
ASSERT_TRUE(maybeBlock.has_value());
- Block *upper = *maybeBlock;
+ BlockRef upper = *maybeBlock;
FreeTrie trie({0, 4096});
trie.push(root);
@@ -87,30 +94,30 @@ TEST(LlvmLibcFreeTrie, FindBestFitLowerAndUpper) {
trie.push(upper);
// The lower subtrie is examined first.
- EXPECT_EQ(trie.find_best_fit(0)->block(), lower);
+ EXPECT_EQ(trie.find_best_fit(0)->block().addr(), lower.addr());
// The upper subtrie is examined if there are no fits found in the upper
// subtrie.
- EXPECT_EQ(trie.find_best_fit(2048)->block(), upper);
+ EXPECT_EQ(trie.find_best_fit(2048)->block().addr(), upper.addr());
}
TEST(LlvmLibcFreeTrie, Remove) {
byte mem[4096];
- optional<Block *> maybeBlock = Block::init(mem);
+ optional<BlockRef> maybeBlock = BlockRef::init(mem);
ASSERT_TRUE(maybeBlock.has_value());
- Block *small1 = *maybeBlock;
- maybeBlock = small1->split(512);
+ BlockRef small1 = *maybeBlock;
+ maybeBlock = small1.split(512);
ASSERT_TRUE(maybeBlock.has_value());
- Block *small2 = *maybeBlock;
- maybeBlock = small2->split(512);
+ BlockRef small2 = *maybeBlock;
+ maybeBlock = small2.split(512);
ASSERT_TRUE(maybeBlock.has_value());
- ASSERT_EQ(small1->inner_size(), small2->inner_size());
- Block *large = *maybeBlock;
+ ASSERT_EQ(small1.inner_size(), small2.inner_size());
+ BlockRef large = *maybeBlock;
// Removing the root empties the trie.
FreeTrie trie({0, 4096});
trie.push(large);
FreeTrie::Node *large_node = trie.find_best_fit(0);
- ASSERT_EQ(large_node->block(), large);
+ ASSERT_EQ(large_node->block().addr(), large.addr());
trie.remove(large_node);
ASSERT_TRUE(trie.empty());
@@ -118,8 +125,10 @@ TEST(LlvmLibcFreeTrie, Remove) {
trie.push(small1);
trie.push(small2);
trie.push(large);
- trie.remove(trie.find_best_fit(small1->inner_size()));
- EXPECT_EQ(trie.find_best_fit(large->inner_size())->block(), large);
- trie.remove(trie.find_best_fit(small1->inner_size()));
- EXPECT_EQ(trie.find_best_fit(large->inner_size())->block(), large);
+ trie.remove(trie.find_best_fit(small1.inner_size()));
+ EXPECT_EQ(trie.find_best_fit(large.inner_size())->block().addr(),
+ large.addr());
+ trie.remove(trie.find_best_fit(small1.inner_size()));
+ EXPECT_EQ(trie.find_best_fit(large.inner_size())->block().addr(),
+ large.addr());
}
>From b658f15d28be09cf9ecd7f44eb974e082ce616d4 Mon Sep 17 00:00:00 2001
From: Yifan Zhu <yfzhu at google.com>
Date: Mon, 8 Jun 2026 08:18:49 -0700
Subject: [PATCH 2/2] address comments
---
libc/src/__support/block.h | 32 +++++++++++++++---------------
libc/src/__support/freelist_heap.h | 5 +++++
2 files changed, 21 insertions(+), 16 deletions(-)
diff --git a/libc/src/__support/block.h b/libc/src/__support/block.h
index cec59e952916d..ac0987a3ad9a7 100644
--- a/libc/src/__support/block.h
+++ b/libc/src/__support/block.h
@@ -44,7 +44,7 @@ LIBC_INLINE constexpr size_t align_up(size_t value, size_t alignment) {
using ByteSpan = cpp::span<LIBC_NAMESPACE::cpp::byte>;
using cpp::optional;
-/// Proxy for a memory region with links to adjacent blocks.
+/// Reference to a memory region with links to adjacent blocks.
///
/// The blocks store their offsets to the previous and next blocks. The latter
/// is also the block's size. The metadata is stored in raw bytes and accessed
@@ -104,13 +104,13 @@ class BlockRef {
static constexpr size_t LAST_MASK = 1 << 1;
static constexpr size_t SIZE_MASK = ~(PREV_FREE_MASK | LAST_MASK);
- // Header field offsets. The previous offset is only meaningful when this
- // block's PREV_FREE_MASK bit is set in the next field.
+ // Header field offsets. The value at PREV_OFFSET is only meaningful when the
+ // PREV_FREE_MASK bit is set in the next field.
static constexpr size_t PREV_OFFSET = 0;
- static constexpr size_t NEXT_OFFSET = sizeof(size_t);
+ static constexpr size_t NEXT_OFFSET = PREV_OFFSET + sizeof(size_t);
public:
- static constexpr size_t HEADER_SIZE = 2 * sizeof(size_t);
+ static constexpr size_t HEADER_SIZE = NEXT_OFFSET + sizeof(size_t);
// To ensure block sizes have two lower unused bits, ensure usable space is
// always aligned to at least 4 bytes. (The distances between usable spaces,
@@ -118,20 +118,20 @@ class BlockRef {
static constexpr size_t MIN_ALIGN = cpp::max(size_t{4}, alignof(max_align_t));
LIBC_INLINE constexpr BlockRef() = default;
- LIBC_INLINE explicit constexpr BlockRef(cpp::byte *ptr) : self(ptr) {}
+ LIBC_INLINE explicit constexpr BlockRef(cpp::byte *ptr) : header_ptr(ptr) {}
LIBC_INLINE explicit constexpr operator bool() const {
- return self != nullptr;
+ return header_ptr != nullptr;
}
LIBC_INLINE constexpr bool operator==(BlockRef other) const {
- return self == other.self;
+ return header_ptr == other.header_ptr;
}
LIBC_INLINE constexpr bool operator!=(BlockRef other) const {
return !(*this == other);
}
- LIBC_INLINE cpp::byte *data() const { return self; }
+ LIBC_INLINE cpp::byte *data() const { return header_ptr; }
LIBC_INLINE uintptr_t addr() const {
- return reinterpret_cast<uintptr_t>(self);
+ return reinterpret_cast<uintptr_t>(header_ptr);
}
/// Initializes a given memory region into a first block and a sentinel last
@@ -195,14 +195,14 @@ class BlockRef {
///
/// Aligned to some multiple of MIN_ALIGN.
LIBC_INLINE cpp::byte *usable_space() const {
- auto *s = self + HEADER_SIZE;
+ auto *s = header_ptr + HEADER_SIZE;
LIBC_ASSERT(reinterpret_cast<uintptr_t>(s) % MIN_ALIGN == 0 &&
"usable space must be aligned to MIN_ALIGN");
return s;
}
// @returns The region of memory the block manages, including the header.
- LIBC_INLINE ByteSpan region() const { return {self, outer_size()}; }
+ LIBC_INLINE ByteSpan region() const { return {header_ptr, outer_size()}; }
/// Attempts to split this block.
///
@@ -223,14 +223,14 @@ class BlockRef {
size_t next_value = load_next();
if (next_value & LAST_MASK)
return BlockRef();
- return BlockRef(self + (next_value & SIZE_MASK));
+ return BlockRef(header_ptr + (next_value & SIZE_MASK));
}
/// @returns The free block immediately before this one, otherwise null.
LIBC_INLINE BlockRef prev_free() const {
if (!(load_next() & PREV_FREE_MASK))
return BlockRef();
- return BlockRef(self - load_prev());
+ return BlockRef(header_ptr - load_prev());
}
/// @returns Whether the block is unavailable for allocation.
@@ -335,7 +335,7 @@ class BlockRef {
}
LIBC_INLINE cpp::byte *field_ptr(size_t offset) const {
- cpp::byte *ptr = self + offset;
+ cpp::byte *ptr = header_ptr + offset;
LIBC_ASSERT(reinterpret_cast<uintptr_t>(ptr) % alignof(size_t) == 0 &&
"block metadata fields must be aligned");
// BlockRef points to block header which should be well-aligned. However,
@@ -387,7 +387,7 @@ class BlockRef {
store_field(NEXT_OFFSET, value);
}
- cpp::byte *self = nullptr;
+ cpp::byte *header_ptr = nullptr;
};
// This is the return type for `allocate` which can split one block into up to
diff --git a/libc/src/__support/freelist_heap.h b/libc/src/__support/freelist_heap.h
index 05affc38fcb8c..a591d63ddd4e5 100644
--- a/libc/src/__support/freelist_heap.h
+++ b/libc/src/__support/freelist_heap.h
@@ -176,6 +176,11 @@ LIBC_INLINE bool FreeListHeap::shrink_in_place(BlockRef block, size_t size) {
if (next.has_value()) {
BlockRef next_block = *next;
BlockRef right = next_block.next();
+ // Since the original block was not the last block (the sentinel last
+ // block is never split), the split-off remainder block `next_block` is
+ // also not the last block. Thus, its next block `right` is guaranteed
+ // to be non-null.
+ LIBC_ASSERT(right && "right block must be non-null");
if (!right.used()) {
free_store.remove(right);
next_block.merge_next();
More information about the libc-commits
mailing list