#include "partition_alloc/freeslot_bitmap.h"
#include <cstdint>
#include <limits>
#include "partition_alloc/buildflags.h"
#include "partition_alloc/freeslot_bitmap_constants.h"
#include "partition_alloc/partition_alloc.h"
#include "partition_alloc/partition_alloc_constants.h"
#include "partition_alloc/partition_alloc_forward.h"
#include "partition_alloc/partition_page.h"
#include "partition_alloc/partition_root.h"
#include "testing/gtest/include/gtest/gtest.h"
#if PA_BUILDFLAG(USE_FREESLOT_BITMAP) && \
!defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
namespace partition_alloc::internal {
namespace {
class PartitionAllocFreeSlotBitmapTest : public ::testing::Test {
protected:
static constexpr FreeSlotBitmapCellType kAllUsed = 0u;
static constexpr FreeSlotBitmapCellType kAllFree =
std::numeric_limits<FreeSlotBitmapCellType>::max();
void SetUp() override {
allocator_.init(PartitionOptions{});
allocated_ptr_ = reinterpret_cast<uintptr_t>(
allocator_.root()->Alloc(2 * kSuperPageSize));
super_page_ = (allocated_ptr_ + kSuperPageSize) & kSuperPageBaseMask;
PA_DCHECK(super_page_ + kSuperPageSize <=
allocated_ptr_ + 2 * kSuperPageSize);
}
void TearDown() override {
allocator_.root()->Free(reinterpret_cast<void*>(allocated_ptr_));
}
uintptr_t SlotAddr(size_t index) {
return SuperPagePayloadBegin(super_page_, false) + index * kSmallestBucket;
}
uintptr_t LastSlotAddr() {
return super_page_ + kSuperPageSize - PartitionPageSize() - kSmallestBucket;
}
private:
uintptr_t allocated_ptr_;
uintptr_t super_page_;
PartitionAllocator allocator_;
};
}
TEST_F(PartitionAllocFreeSlotBitmapTest, MarkFirstSlotAsUsed) {
uintptr_t slot_addr = SlotAddr(0);
FreeSlotBitmapMarkSlotAsFree(slot_addr);
EXPECT_FALSE(FreeSlotBitmapSlotIsUsed(slot_addr));
FreeSlotBitmapMarkSlotAsUsed(slot_addr);
EXPECT_TRUE(FreeSlotBitmapSlotIsUsed(slot_addr));
}
TEST_F(PartitionAllocFreeSlotBitmapTest, MarkFirstSlotAsFree) {
uintptr_t slot_addr = SlotAddr(0);
EXPECT_TRUE(FreeSlotBitmapSlotIsUsed(slot_addr));
FreeSlotBitmapMarkSlotAsFree(slot_addr);
EXPECT_FALSE(FreeSlotBitmapSlotIsUsed(slot_addr));
}
TEST_F(PartitionAllocFreeSlotBitmapTest, MarkAllBitsInCellAsUsed) {
const size_t kFirstSlotAddr = SlotAddr(0);
const size_t kLastSlotAddr = SlotAddr(kFreeSlotBitmapBitsPerCell);
auto [cell_first_slot, bit_index_first_slot] =
GetFreeSlotBitmapCellPtrAndBitIndex(kFirstSlotAddr);
auto [cell_last_slot, bit_index_last_slot] =
GetFreeSlotBitmapCellPtrAndBitIndex(kLastSlotAddr);
EXPECT_EQ(0u, bit_index_first_slot);
EXPECT_EQ(0u, bit_index_last_slot);
EXPECT_NE(cell_first_slot, cell_last_slot);
for (size_t slot_addr = kFirstSlotAddr; slot_addr < kLastSlotAddr;
slot_addr += kSmallestBucket) {
FreeSlotBitmapMarkSlotAsFree(slot_addr);
}
EXPECT_EQ(kAllFree, *cell_first_slot);
for (size_t slot_addr = kFirstSlotAddr; slot_addr < kLastSlotAddr;
slot_addr += kSmallestBucket) {
FreeSlotBitmapMarkSlotAsUsed(slot_addr);
}
EXPECT_EQ(kAllUsed, *cell_first_slot);
}
TEST_F(PartitionAllocFreeSlotBitmapTest, MarkLastSlotAsUsed) {
uintptr_t last_slot_addr = LastSlotAddr();
FreeSlotBitmapMarkSlotAsFree(last_slot_addr);
EXPECT_FALSE(FreeSlotBitmapSlotIsUsed(last_slot_addr));
FreeSlotBitmapMarkSlotAsUsed(last_slot_addr);
EXPECT_TRUE(FreeSlotBitmapSlotIsUsed(last_slot_addr));
}
TEST_F(PartitionAllocFreeSlotBitmapTest, ResetBitmap) {
const size_t kNumSlots = 3 * kFreeSlotBitmapBitsPerCell;
for (size_t i = 0; i < kNumSlots; ++i) {
FreeSlotBitmapMarkSlotAsFree(SlotAddr(i));
}
auto [cell_first_slot, bit_index_first_slot] =
GetFreeSlotBitmapCellPtrAndBitIndex(SlotAddr(0));
EXPECT_EQ(0u, bit_index_first_slot);
EXPECT_EQ(kAllFree, *cell_first_slot);
EXPECT_EQ(kAllFree, *(cell_first_slot + 1));
EXPECT_EQ(kAllFree, *(cell_first_slot + 2));
FreeSlotBitmapReset(SlotAddr(kFreeSlotBitmapBitsPerCell),
SlotAddr(2 * kFreeSlotBitmapBitsPerCell),
kSmallestBucket);
EXPECT_EQ(kAllFree, *cell_first_slot);
EXPECT_EQ(kAllUsed, *(cell_first_slot + 1));
EXPECT_EQ(kAllFree, *(cell_first_slot + 2));
}
TEST_F(PartitionAllocFreeSlotBitmapTest, ResetBitmapWithZeroLengthRange) {
const size_t kNumSlots = 3 * kFreeSlotBitmapBitsPerCell;
for (size_t i = 0; i < kNumSlots; ++i) {
FreeSlotBitmapMarkSlotAsFree(SlotAddr(i));
}
uintptr_t aligned_addr = SlotAddr(0);
auto [cell_aligned, bit_index_aligned] =
GetFreeSlotBitmapCellPtrAndBitIndex(aligned_addr);
EXPECT_EQ(0u, bit_index_aligned);
EXPECT_EQ(kAllFree, *cell_aligned);
FreeSlotBitmapReset(aligned_addr, aligned_addr, kSmallestBucket);
EXPECT_EQ(kAllFree, *cell_aligned);
uintptr_t non_aligned_addr = SlotAddr(1);
auto [cell_non_aligned, bit_index_non_aligned] =
GetFreeSlotBitmapCellPtrAndBitIndex(non_aligned_addr);
EXPECT_EQ(1u, bit_index_non_aligned);
EXPECT_EQ(kAllFree, *cell_non_aligned);
FreeSlotBitmapReset(non_aligned_addr, non_aligned_addr, kSmallestBucket);
EXPECT_EQ(kAllFree, *cell_non_aligned);
}
TEST_F(PartitionAllocFreeSlotBitmapTest, ResetSingleBitInMiddleOfCell) {
const size_t kNumSlots = 3 * kFreeSlotBitmapBitsPerCell;
for (size_t i = 0; i < kNumSlots; ++i) {
FreeSlotBitmapMarkSlotAsFree(SlotAddr(i));
}
uintptr_t mid_cell_slot_addr = SlotAddr(kFreeSlotBitmapBitsPerCell / 2);
auto [cell_mid, bit_index_mid] =
GetFreeSlotBitmapCellPtrAndBitIndex(mid_cell_slot_addr);
EXPECT_NE(0u, bit_index_mid);
EXPECT_TRUE(*cell_mid & CellWithAOne(bit_index_mid));
FreeSlotBitmapReset(mid_cell_slot_addr, mid_cell_slot_addr + kSmallestBucket,
kSmallestBucket);
EXPECT_FALSE(*cell_mid & CellWithAOne(bit_index_mid));
}
}
#endif