#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_PAGE_H_
#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_PAGE_H_
#include <cstdint>
#include <cstring>
#include <limits>
#include <utility>
#include "base/allocator/partition_allocator/address_pool_manager.h"
#include "base/allocator/partition_allocator/address_pool_manager_types.h"
#include "base/allocator/partition_allocator/freeslot_bitmap_constants.h"
#include "base/allocator/partition_allocator/partition_address_space.h"
#include "base/allocator/partition_allocator/partition_alloc_base/bits.h"
#include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h"
#include "base/allocator/partition_allocator/partition_alloc_base/component_export.h"
#include "base/allocator/partition_allocator/partition_alloc_base/debug/debugging_buildflags.h"
#include "base/allocator/partition_allocator/partition_alloc_base/thread_annotations.h"
#include "base/allocator/partition_allocator/partition_alloc_buildflags.h"
#include "base/allocator/partition_allocator/partition_alloc_check.h"
#include "base/allocator/partition_allocator/partition_alloc_constants.h"
#include "base/allocator/partition_allocator/partition_alloc_forward.h"
#include "base/allocator/partition_allocator/partition_bucket.h"
#include "base/allocator/partition_allocator/partition_freelist_entry.h"
#include "base/allocator/partition_allocator/reservation_offset_table.h"
#include "base/allocator/partition_allocator/tagging.h"
#include "build/build_config.h"
#if BUILDFLAG(USE_STARSCAN)
#include "base/allocator/partition_allocator/starscan/state_bitmap.h"
#endif
#if BUILDFLAG(PUT_REF_COUNT_IN_PREVIOUS_SLOT)
#include "base/allocator/partition_allocator/partition_ref_count.h"
#endif
extern uintptr_t g_bucket_cookie;
extern uintptr_t g_root_cookie;
PA_ALWAYS_INLINE static void* EncodeBucket(void* ptr) {
if (g_bucket_cookie == 0)
g_bucket_cookie = GetRandomValue();
if (ptr == nullptr)
return nullptr;
return reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(ptr) ^ g_bucket_cookie);
}
PA_ALWAYS_INLINE static void* EncodeRoot(void* ptr) {
if (g_root_cookie == 0)
g_root_cookie = GetRandomValue();
if (ptr == nullptr)
return nullptr;
return reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(ptr) ^ g_root_cookie);
}
namespace partition_alloc::internal {
template <bool thread_safe>
struct PartitionSuperPageExtentEntry {
PartitionRoot<thread_safe>* root;
PartitionSuperPageExtentEntry<thread_safe>* next;
uint16_t number_of_consecutive_super_pages;
uint16_t number_of_nonempty_slot_spans;
PA_ALWAYS_INLINE void IncrementNumberOfNonemptySlotSpans();
PA_ALWAYS_INLINE void DecrementNumberOfNonemptySlotSpans();
};
static_assert(
sizeof(PartitionSuperPageExtentEntry<ThreadSafe>) <= kPageMetadataSize,
"PartitionSuperPageExtentEntry must be able to fit in a metadata slot");
static_assert(
kMaxSuperPagesInPool / kSuperPageSize <=
std::numeric_limits<
decltype(PartitionSuperPageExtentEntry<
ThreadSafe>::number_of_consecutive_super_pages)>::max(),
"number_of_consecutive_super_pages must be big enough");
template <bool thread_safe>
PA_ALWAYS_INLINE uintptr_t SuperPagesBeginFromExtent(
const PartitionSuperPageExtentEntry<thread_safe>* extent) {
PA_DCHECK(0 < extent->number_of_consecutive_super_pages);
uintptr_t extent_as_uintptr = reinterpret_cast<uintptr_t>(extent);
PA_DCHECK(IsManagedByNormalBuckets(extent_as_uintptr));
return base::bits::AlignDown(extent_as_uintptr, kSuperPageAlignment);
}
template <bool thread_safe>
PA_ALWAYS_INLINE uintptr_t SuperPagesEndFromExtent(
const PartitionSuperPageExtentEntry<thread_safe>* extent) {
return SuperPagesBeginFromExtent(extent) +
(extent->number_of_consecutive_super_pages * kSuperPageSize);
}
#if BUILDFLAG(USE_STARSCAN)
using AllocationStateMap =
StateBitmap<kSuperPageSize, kSuperPageAlignment, kAlignment>;
#endif
#pragma pack(push, 1)
template <bool thread_safe>
struct SlotSpanMetadata {
private:
PartitionFreelistEntry* freelist_head = nullptr;
public:
SlotSpanMetadata<thread_safe>* next_slot_span = nullptr;
PartitionBucket<thread_safe>* const bucket = nullptr;
#if BUILDFLAG(HAS_64_BIT_POINTERS) && BUILDFLAG(IS_APPLE)
static constexpr size_t kMaxSlotsPerSlotSpan =
4 * (1 << 14) / kSmallestBucket;
#elif BUILDFLAG(IS_LINUX) && defined(ARCH_CPU_ARM64)
static constexpr size_t kMaxSlotsPerSlotSpan =
4 * (1 << 14) / kSmallestBucket;
#else
static constexpr size_t kMaxSlotsPerSlotSpan =
PartitionPageSize() / kSmallestBucket;
#endif
static constexpr size_t kMaxSlotsPerSlotSpanBits = 13;
static_assert(kMaxSlotsPerSlotSpan < (1 << kMaxSlotsPerSlotSpanBits), "");
uint32_t marked_full : 1;
uint32_t num_allocated_slots : kMaxSlotsPerSlotSpanBits;
uint32_t num_unprovisioned_slots : kMaxSlotsPerSlotSpanBits;
private:
const uint32_t can_store_raw_size_ : 1;
uint32_t freelist_is_sorted_ : 1;
uint32_t unused1_ : (32 - 1 - 2 * kMaxSlotsPerSlotSpanBits - 1 - 1);
uint16_t in_empty_cache_ : 1;
uint16_t empty_cache_index_ : kEmptyCacheIndexBits;
uint16_t unused2_ : (16 - 1 - kEmptyCacheIndexBits);
public:
PA_COMPONENT_EXPORT(PARTITION_ALLOC)
explicit SlotSpanMetadata(PartitionBucket<thread_safe>* bucket);
PA_NOINLINE PA_COMPONENT_EXPORT(PARTITION_ALLOC) void FreeSlowPath(
size_t number_of_freed);
PA_ALWAYS_INLINE PartitionFreelistEntry* PopForAlloc(size_t size);
PA_ALWAYS_INLINE void Free(uintptr_t ptr);
PA_ALWAYS_INLINE void AppendFreeList(PartitionFreelistEntry* head,
PartitionFreelistEntry* tail,
size_t number_of_freed);
void Decommit(PartitionRoot<thread_safe>* root);
void DecommitIfPossible(PartitionRoot<thread_safe>* root);
void SortFreelist();
void RegisterEmpty();
PA_ALWAYS_INLINE static uintptr_t ToSlotSpanStart(
const SlotSpanMetadata* slot_span);
PA_ALWAYS_INLINE static SlotSpanMetadata* FromAddr(uintptr_t address);
PA_ALWAYS_INLINE static SlotSpanMetadata* FromSlotStart(uintptr_t slot_start);
PA_ALWAYS_INLINE static SlotSpanMetadata* FromObject(void* object);
PA_ALWAYS_INLINE static SlotSpanMetadata* FromObjectInnerAddr(
uintptr_t address);
PA_ALWAYS_INLINE static SlotSpanMetadata* FromObjectInnerPtr(void* ptr);
PA_ALWAYS_INLINE PartitionSuperPageExtentEntry<thread_safe>*
ToSuperPageExtent() const;
PA_ALWAYS_INLINE bool CanStoreRawSize() const { return can_store_raw_size_; }
PA_ALWAYS_INLINE void SetRawSize(size_t raw_size);
PA_ALWAYS_INLINE size_t GetRawSize() const;
PA_ALWAYS_INLINE PartitionFreelistEntry* get_freelist_head() const {
return freelist_head;
}
PA_ALWAYS_INLINE void SetFreelistHead(PartitionFreelistEntry* new_head);
PA_ALWAYS_INLINE size_t GetUtilizedSlotSize() const {
if (PA_LIKELY(!CanStoreRawSize())) {
return bucket->slot_size;
}
return GetRawSize();
}
PA_ALWAYS_INLINE size_t GetSlotSizeForBookkeeping() const {
return bucket->slot_size;
}
PA_ALWAYS_INLINE size_t
GetUsableSize(PartitionRoot<thread_safe>* root) const {
return root->AdjustSizeForExtrasSubtract(GetUtilizedSlotSize());
}
PA_ALWAYS_INLINE size_t GetProvisionedSize() const {
size_t num_provisioned_slots =
bucket->get_slots_per_span() - num_unprovisioned_slots;
size_t provisioned_size = num_provisioned_slots * bucket->slot_size;
PA_DCHECK(provisioned_size <= bucket->get_bytes_per_span());
return provisioned_size;
}
size_t GetFreelistLength() const {
size_t num_provisioned_slots =
bucket->get_slots_per_span() - num_unprovisioned_slots;
return num_provisioned_slots - num_allocated_slots;
}
PA_ALWAYS_INLINE void Reset();
PA_COMPONENT_EXPORT(PARTITION_ALLOC)
static const SlotSpanMetadata* get_sentinel_slot_span();
static SlotSpanMetadata* get_sentinel_slot_span_non_const();
PA_ALWAYS_INLINE bool is_active() const;
PA_ALWAYS_INLINE bool is_full() const;
PA_ALWAYS_INLINE bool is_empty() const;
PA_ALWAYS_INLINE bool is_decommitted() const;
PA_ALWAYS_INLINE bool in_empty_cache() const { return in_empty_cache_; }
PA_ALWAYS_INLINE bool freelist_is_sorted() const {
return freelist_is_sorted_;
}
PA_ALWAYS_INLINE void set_freelist_sorted() { freelist_is_sorted_ = true; }
private:
static const SlotSpanMetadata sentinel_slot_span_;
constexpr SlotSpanMetadata() noexcept
: marked_full(0),
num_allocated_slots(0),
num_unprovisioned_slots(0),
can_store_raw_size_(false),
freelist_is_sorted_(true),
unused1_(0),
in_empty_cache_(0),
empty_cache_index_(0),
unused2_(0) {}
};
#pragma pack(pop)
static_assert(sizeof(SlotSpanMetadata<ThreadSafe>) <= kPageMetadataSize,
"SlotSpanMetadata must fit into a Page Metadata slot.");
struct SubsequentPageMetadata {
size_t raw_size;
};
#pragma pack(push, 1)
template <bool thread_safe>
struct PartitionPage {
union {
SlotSpanMetadata<thread_safe> slot_span_metadata;
SubsequentPageMetadata subsequent_page_metadata;
char optional_padding[kPageMetadataSize - sizeof(uint8_t) - sizeof(bool)];
};
static constexpr uint16_t kMaxSlotSpanMetadataBits = 6;
static constexpr uint16_t kMaxSlotSpanMetadataOffset =
(1 << kMaxSlotSpanMetadataBits) - 1;
uint8_t slot_span_metadata_offset : kMaxSlotSpanMetadataBits;
bool is_valid : 1;
bool has_valid_span_after_this : 1;
uint8_t unused;
PA_ALWAYS_INLINE static PartitionPage* FromAddr(uintptr_t address);
};
#pragma pack(pop)
static_assert(sizeof(PartitionPage<ThreadSafe>) == kPageMetadataSize,
"PartitionPage must be able to fit in a metadata slot");
static_assert(offsetof(PartitionPage<ThreadSafe>, slot_span_metadata) == 0, "");
static_assert(offsetof(PartitionPage<ThreadSafe>, subsequent_page_metadata) ==
0,
"");
template <bool thread_safe>
PA_ALWAYS_INLINE PartitionPage<thread_safe>* PartitionSuperPageToMetadataArea(
uintptr_t super_page) {
PA_DCHECK(IsReservationStart(super_page));
PA_DCHECK(!(super_page & kSuperPageOffsetMask));
return reinterpret_cast<PartitionPage<thread_safe>*>(super_page +
SystemPageSize());
}
PA_ALWAYS_INLINE const SubsequentPageMetadata* GetSubsequentPageMetadata(
const PartitionPage<ThreadSafe>* page) {
return &(page + 1)->subsequent_page_metadata;
}
PA_ALWAYS_INLINE SubsequentPageMetadata* GetSubsequentPageMetadata(
PartitionPage<ThreadSafe>* page) {
return &(page + 1)->subsequent_page_metadata;
}
template <bool thread_safe>
PA_ALWAYS_INLINE PartitionSuperPageExtentEntry<thread_safe>*
PartitionSuperPageToExtent(uintptr_t super_page) {
return reinterpret_cast<PartitionSuperPageExtentEntry<thread_safe>*>(
PartitionSuperPageToMetadataArea<thread_safe>(super_page));
}
#if BUILDFLAG(USE_STARSCAN)
PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR size_t
ReservedStateBitmapSize() {
return base::bits::AlignUp(sizeof(AllocationStateMap), PartitionPageSize());
}
PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR size_t
CommittedStateBitmapSize() {
return base::bits::AlignUp(sizeof(AllocationStateMap), SystemPageSize());
}
PA_ALWAYS_INLINE uintptr_t SuperPageStateBitmapAddr(uintptr_t super_page) {
PA_DCHECK(!(super_page % kSuperPageAlignment));
return super_page + PartitionPageSize() +
(IsManagedByNormalBuckets(super_page) ? ReservedFreeSlotBitmapSize()
: 0);
}
PA_ALWAYS_INLINE AllocationStateMap* SuperPageStateBitmap(
uintptr_t super_page) {
return reinterpret_cast<AllocationStateMap*>(
SuperPageStateBitmapAddr(super_page));
}
#else
PA_ALWAYS_INLINE PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR size_t
ReservedStateBitmapSize() {
return 0ull;
}
#endif
PA_ALWAYS_INLINE uintptr_t
SuperPagePayloadStartOffset(bool is_managed_by_normal_buckets,
bool with_quarantine) {
return PartitionPageSize() +
(is_managed_by_normal_buckets ? ReservedFreeSlotBitmapSize() : 0) +
(with_quarantine ? ReservedStateBitmapSize() : 0);
}
PA_ALWAYS_INLINE uintptr_t SuperPagePayloadBegin(uintptr_t super_page,
bool with_quarantine) {
PA_DCHECK(!(super_page % kSuperPageAlignment));
return super_page +
SuperPagePayloadStartOffset(IsManagedByNormalBuckets(super_page),
with_quarantine);
}
PA_ALWAYS_INLINE uintptr_t SuperPagePayloadEndOffset() {
return kSuperPageSize - PartitionPageSize();
}
PA_ALWAYS_INLINE uintptr_t SuperPagePayloadEnd(uintptr_t super_page) {
PA_DCHECK(!(super_page % kSuperPageAlignment));
return super_page + SuperPagePayloadEndOffset();
}
PA_ALWAYS_INLINE size_t SuperPagePayloadSize(uintptr_t super_page,
bool with_quarantine) {
return SuperPagePayloadEnd(super_page) -
SuperPagePayloadBegin(super_page, with_quarantine);
}
template <bool thread_safe>
PA_ALWAYS_INLINE void PartitionSuperPageExtentEntry<
thread_safe>::IncrementNumberOfNonemptySlotSpans() {
#if BUILDFLAG(PA_DCHECK_IS_ON)
uintptr_t super_page = base::bits::AlignDown(
reinterpret_cast<uintptr_t>(this), kSuperPageAlignment);
PA_DCHECK((SuperPagePayloadSize(super_page, root->IsQuarantineAllowed()) /
PartitionPageSize()) > number_of_nonempty_slot_spans);
#endif
++number_of_nonempty_slot_spans;
}
template <bool thread_safe>
PA_ALWAYS_INLINE void PartitionSuperPageExtentEntry<
thread_safe>::DecrementNumberOfNonemptySlotSpans() {
PA_DCHECK(number_of_nonempty_slot_spans);
--number_of_nonempty_slot_spans;
}
template <bool thread_safe>
PA_ALWAYS_INLINE PartitionSuperPageExtentEntry<thread_safe>*
SlotSpanMetadata<thread_safe>::ToSuperPageExtent() const {
uintptr_t super_page = reinterpret_cast<uintptr_t>(this) & kSuperPageBaseMask;
return PartitionSuperPageToExtent<thread_safe>(super_page);
}
PA_ALWAYS_INLINE bool IsWithinSuperPagePayload(uintptr_t address,
bool with_quarantine) {
PA_DCHECK(!with_quarantine || IsManagedByNormalBuckets(address));
uintptr_t super_page = address & kSuperPageBaseMask;
uintptr_t payload_start = SuperPagePayloadBegin(super_page, with_quarantine);
uintptr_t payload_end = SuperPagePayloadEnd(super_page);
return address >= payload_start && address < payload_end;
}
template <bool thread_safe>
PA_ALWAYS_INLINE PartitionPage<thread_safe>*
PartitionPage<thread_safe>::FromAddr(uintptr_t address) {
uintptr_t super_page = address & kSuperPageBaseMask;
#if BUILDFLAG(PA_DCHECK_IS_ON)
PA_DCHECK(IsReservationStart(super_page));
auto* extent = PartitionSuperPageToExtent<thread_safe>(super_page);
#if defined(OHOS_ENABLE_POINTER_HARDENED)
PartitionRoot* real_root = (PartitionRoot*)EncodeRoot((void*)(extent->root));
PA_DCHECK(IsWithinSuperPagePayload(address,
IsManagedByNormalBuckets(address) &&
real_root->IsQuarantineAllowed()));
#else
PA_DCHECK(IsWithinSuperPagePayload(address,
IsManagedByNormalBuckets(address) &&
extent->root->IsQuarantineAllowed()));
#endif
#endif
uintptr_t partition_page_index =
(address & kSuperPageOffsetMask) >> PartitionPageShift();
PA_DCHECK(partition_page_index);
PA_DCHECK(partition_page_index < NumPartitionPagesPerSuperPage() - 1);
return PartitionSuperPageToMetadataArea<thread_safe>(super_page) +
partition_page_index;
}
template <bool thread_safe>
PA_ALWAYS_INLINE uintptr_t SlotSpanMetadata<thread_safe>::ToSlotSpanStart(
const SlotSpanMetadata* slot_span) {
uintptr_t pointer_as_uint = reinterpret_cast<uintptr_t>(slot_span);
uintptr_t super_page_offset = (pointer_as_uint & kSuperPageOffsetMask);
PA_DCHECK(super_page_offset > SystemPageSize());
PA_DCHECK(super_page_offset <
SystemPageSize() +
(NumPartitionPagesPerSuperPage() * kPageMetadataSize));
uintptr_t partition_page_index =
(super_page_offset - SystemPageSize()) >> kPageMetadataShift;
PA_DCHECK(partition_page_index);
PA_DCHECK(partition_page_index < NumPartitionPagesPerSuperPage() - 1);
uintptr_t super_page_base = (pointer_as_uint & kSuperPageBaseMask);
return super_page_base + (partition_page_index << PartitionPageShift());
}
template <bool thread_safe>
PA_ALWAYS_INLINE SlotSpanMetadata<thread_safe>*
SlotSpanMetadata<thread_safe>::FromAddr(uintptr_t address) {
auto* page = PartitionPage<thread_safe>::FromAddr(address);
PA_DCHECK(page->is_valid);
page -= page->slot_span_metadata_offset;
PA_DCHECK(page->is_valid);
PA_DCHECK(!page->slot_span_metadata_offset);
auto* slot_span = &page->slot_span_metadata;
PA_DCHECK(PartitionRoot<thread_safe>::IsValidSlotSpan(slot_span));
PA_DCHECK(slot_span->bucket->slot_size);
return slot_span;
}
template <bool thread_safe>
PA_ALWAYS_INLINE SlotSpanMetadata<thread_safe>*
SlotSpanMetadata<thread_safe>::FromSlotStart(uintptr_t slot_start) {
auto* slot_span = FromAddr(slot_start);
#if BUILDFLAG(PA_DCHECK_IS_ON)
uintptr_t slot_span_start = ToSlotSpanStart(slot_span);
PA_DCHECK(!((slot_start - slot_span_start) % slot_span->bucket->slot_size));
#endif
return slot_span;
}
template <bool thread_safe>
PA_ALWAYS_INLINE SlotSpanMetadata<thread_safe>*
SlotSpanMetadata<thread_safe>::FromObject(void* object) {
uintptr_t object_addr = ObjectPtr2Addr(object);
auto* slot_span = FromAddr(object_addr);
#if BUILDFLAG(PA_DCHECK_IS_ON)
uintptr_t slot_span_start = ToSlotSpanStart(slot_span);
auto* root = PartitionRoot<thread_safe>::FromSlotSpan(slot_span);
PA_DCHECK((object_addr - slot_span_start) % slot_span->bucket->slot_size ==
root->flags.extras_offset);
#endif
return slot_span;
}
template <bool thread_safe>
PA_ALWAYS_INLINE SlotSpanMetadata<thread_safe>*
SlotSpanMetadata<thread_safe>::FromObjectInnerAddr(uintptr_t address) {
auto* slot_span = FromAddr(address);
#if BUILDFLAG(PA_DCHECK_IS_ON)
uintptr_t slot_span_start = ToSlotSpanStart(slot_span);
auto* root = PartitionRoot<thread_safe>::FromSlotSpan(slot_span);
uintptr_t shift_from_slot_start =
(address - slot_span_start) % slot_span->bucket->slot_size;
PA_DCHECK(shift_from_slot_start >= root->flags.extras_offset);
PA_DCHECK(shift_from_slot_start <=
root->flags.extras_offset + slot_span->GetUsableSize(root));
#endif
return slot_span;
}
template <bool thread_safe>
PA_ALWAYS_INLINE SlotSpanMetadata<thread_safe>*
SlotSpanMetadata<thread_safe>::FromObjectInnerPtr(void* ptr) {
return FromObjectInnerAddr(ObjectInnerPtr2Addr(ptr));
}
template <bool thread_safe>
PA_ALWAYS_INLINE void SlotSpanMetadata<thread_safe>::SetRawSize(
size_t raw_size) {
PA_DCHECK(CanStoreRawSize());
auto* subsequent_page_metadata = GetSubsequentPageMetadata(
reinterpret_cast<PartitionPage<thread_safe>*>(this));
subsequent_page_metadata->raw_size = raw_size;
}
template <bool thread_safe>
PA_ALWAYS_INLINE size_t SlotSpanMetadata<thread_safe>::GetRawSize() const {
PA_DCHECK(CanStoreRawSize());
const auto* subsequent_page_metadata = GetSubsequentPageMetadata(
reinterpret_cast<const PartitionPage<thread_safe>*>(this));
return subsequent_page_metadata->raw_size;
}
template <bool thread_safe>
PA_ALWAYS_INLINE void SlotSpanMetadata<thread_safe>::SetFreelistHead(
PartitionFreelistEntry* new_head) {
#if BUILDFLAG(PA_DCHECK_IS_ON)
uintptr_t new_head_untagged = UntagPtr(new_head);
PA_DCHECK(!new_head ||
(reinterpret_cast<uintptr_t>(this) & kSuperPageBaseMask) ==
(new_head_untagged & kSuperPageBaseMask));
#endif
freelist_head = new_head;
freelist_is_sorted_ = false;
}
template <bool thread_safe>
PA_ALWAYS_INLINE PartitionFreelistEntry*
SlotSpanMetadata<thread_safe>::PopForAlloc(size_t size) {
PA_DCHECK(size == bucket->slot_size);
PartitionFreelistEntry* result = freelist_head;
freelist_head = freelist_head->GetNext(size);
num_allocated_slots++;
return result;
}
template <bool thread_safe>
PA_ALWAYS_INLINE void SlotSpanMetadata<thread_safe>::Free(uintptr_t slot_start)
PA_EXCLUSIVE_LOCKS_REQUIRED(
PartitionRoot<thread_safe>::FromSlotSpan(this)->lock_) {
#if BUILDFLAG(PA_DCHECK_IS_ON)
auto* root = PartitionRoot<thread_safe>::FromSlotSpan(this);
root->lock_.AssertAcquired();
#endif
auto* entry = static_cast<internal::PartitionFreelistEntry*>(
SlotStartAddr2Ptr(slot_start));
PA_CHECK(entry != freelist_head);
PA_DCHECK(!freelist_head ||
entry != freelist_head->GetNext(bucket->slot_size));
entry->SetNext(freelist_head);
SetFreelistHead(entry);
PA_CHECK(num_allocated_slots);
--num_allocated_slots;
if (PA_UNLIKELY(marked_full || num_allocated_slots == 0)) {
FreeSlowPath(1);
} else {
PA_DCHECK(!CanStoreRawSize());
}
}
template <bool thread_safe>
PA_ALWAYS_INLINE void SlotSpanMetadata<thread_safe>::AppendFreeList(
PartitionFreelistEntry* head,
PartitionFreelistEntry* tail,
size_t number_of_freed)
PA_EXCLUSIVE_LOCKS_REQUIRED(
PartitionRoot<thread_safe>::FromSlotSpan(this)->lock_) {
#if BUILDFLAG(PA_DCHECK_IS_ON)
auto* root = PartitionRoot<thread_safe>::FromSlotSpan(this);
root->lock_.AssertAcquired();
PA_DCHECK(!tail->GetNext(bucket->slot_size));
PA_DCHECK(number_of_freed);
PA_DCHECK(num_allocated_slots);
if (CanStoreRawSize()) {
PA_DCHECK(number_of_freed == 1);
}
{
size_t number_of_entries = 0;
for (auto* entry = head; entry;
entry = entry->GetNext(bucket->slot_size), ++number_of_entries) {
uintptr_t untagged_entry = UntagPtr(entry);
PA_DCHECK(ToSlotSpanStart(this) <= untagged_entry);
PA_DCHECK(untagged_entry <
ToSlotSpanStart(this) + bucket->get_bytes_per_span());
}
PA_DCHECK(number_of_entries == number_of_freed);
}
#endif
tail->SetNext(freelist_head);
SetFreelistHead(head);
PA_DCHECK(num_allocated_slots >= number_of_freed);
num_allocated_slots -= number_of_freed;
if (PA_UNLIKELY(marked_full || num_allocated_slots == 0)) {
FreeSlowPath(number_of_freed);
} else {
PA_DCHECK(!CanStoreRawSize());
}
}
template <bool thread_safe>
PA_ALWAYS_INLINE bool SlotSpanMetadata<thread_safe>::is_active() const {
PA_DCHECK(this != get_sentinel_slot_span());
bool ret =
(num_allocated_slots > 0 && (freelist_head || num_unprovisioned_slots));
if (ret) {
PA_DCHECK(!marked_full);
PA_DCHECK(num_allocated_slots < bucket->get_slots_per_span());
}
return ret;
}
template <bool thread_safe>
PA_ALWAYS_INLINE bool SlotSpanMetadata<thread_safe>::is_full() const {
PA_DCHECK(this != get_sentinel_slot_span());
bool ret = (num_allocated_slots == bucket->get_slots_per_span());
if (ret) {
PA_DCHECK(!freelist_head);
PA_DCHECK(!num_unprovisioned_slots);
}
return ret;
}
template <bool thread_safe>
PA_ALWAYS_INLINE bool SlotSpanMetadata<thread_safe>::is_empty() const {
PA_DCHECK(this != get_sentinel_slot_span());
bool ret = (!num_allocated_slots && freelist_head);
if (ret) {
PA_DCHECK(!marked_full);
}
return ret;
}
template <bool thread_safe>
PA_ALWAYS_INLINE bool SlotSpanMetadata<thread_safe>::is_decommitted() const {
PA_DCHECK(this != get_sentinel_slot_span());
bool ret = (!num_allocated_slots && !freelist_head);
if (ret) {
PA_DCHECK(!marked_full);
PA_DCHECK(!num_unprovisioned_slots);
PA_DCHECK(!in_empty_cache_);
}
return ret;
}
template <bool thread_safe>
PA_ALWAYS_INLINE void SlotSpanMetadata<thread_safe>::Reset() {
PA_DCHECK(is_decommitted());
num_unprovisioned_slots = bucket->get_slots_per_span();
PA_DCHECK(num_unprovisioned_slots);
ToSuperPageExtent()->IncrementNumberOfNonemptySlotSpans();
next_slot_span = nullptr;
}
#if BUILDFLAG(USE_STARSCAN)
PA_ALWAYS_INLINE AllocationStateMap* StateBitmapFromAddr(uintptr_t address) {
PA_DCHECK(IsManagedByNormalBuckets(address));
uintptr_t super_page = address & kSuperPageBaseMask;
return SuperPageStateBitmap(super_page);
}
#endif
template <bool thread_safe, typename Callback>
void IterateSlotSpans(uintptr_t super_page,
bool with_quarantine,
Callback callback) {
#if BUILDFLAG(PA_DCHECK_IS_ON)
PA_DCHECK(!(super_page % kSuperPageAlignment));
auto* extent_entry = PartitionSuperPageToExtent<thread_safe>(super_page);
#if defined(OHOS_ENABLE_POINTER_HARDENED)
PartitionRoot* real_root = (PartitionRoot*)EncodeRoot((void*)(extent_entry->root));
real_root->lock_.AssertAcquired();
#else
extent_entry->root->lock_.AssertAcquired();
#endif
#endif
using Page = PartitionPage<thread_safe>;
using SlotSpan = SlotSpanMetadata<thread_safe>;
auto* const first_page =
Page::FromAddr(SuperPagePayloadBegin(super_page, with_quarantine));
auto* const last_page =
Page::FromAddr(SuperPagePayloadEnd(super_page) - PartitionPageSize());
Page* page;
SlotSpan* slot_span;
for (page = first_page; page <= last_page;) {
PA_DCHECK(!page->slot_span_metadata_offset);
if (!page->is_valid) {
if (page->has_valid_span_after_this) {
++page;
continue;
}
break;
}
slot_span = &page->slot_span_metadata;
if (callback(slot_span)) {
return;
}
page += slot_span->bucket->get_pages_per_slot_span();
}
PA_DCHECK(page > first_page);
PA_DCHECK(page == reinterpret_cast<Page*>(slot_span) +
slot_span->bucket->get_pages_per_slot_span());
}
}
#endif