#ifndef PARTITION_ALLOC_COMPRESSED_POINTER_H_
#define PARTITION_ALLOC_COMPRESSED_POINTER_H_
#include <climits>
#include <type_traits>
#include "partition_alloc/buildflags.h"
#include "partition_alloc/partition_address_space.h"
#include "partition_alloc/partition_alloc_base/bits.h"
#include "partition_alloc/partition_alloc_base/compiler_specific.h"
#include "partition_alloc/partition_alloc_base/component_export.h"
#if PA_BUILDFLAG(ENABLE_POINTER_COMPRESSION)
#if !PA_BUILDFLAG(GLUE_CORE_POOLS)
#error "Pointer compression only works with glued pools"
#endif
#if PA_CONFIG(DYNAMICALLY_SELECT_POOL_SIZE)
#error "Pointer compression currently supports constant pool size"
#endif
#endif
namespace partition_alloc {
namespace internal {
IsDecayedSame;
#if PA_BUILDFLAG(ENABLE_POINTER_COMPRESSION)
class CompressedPointerBaseGlobal final {
public:
static constexpr size_t kUsefulBits =
base::bits::CountrZero(PartitionAddressSpace::CorePoolsSize());
static_assert(kUsefulBits >= sizeof(uint32_t) * CHAR_BIT);
static constexpr size_t kBitsToShift =
kUsefulBits - sizeof(uint32_t) * CHAR_BIT;
CompressedPointerBaseGlobal() = delete;
PA_ALWAYS_INLINE __attribute__((const)) static uintptr_t Get() {
PA_DCHECK(IsBaseConsistent());
return g_base_.base;
}
PA_ALWAYS_INLINE static bool IsSet() {
PA_DCHECK(IsBaseConsistent());
return (g_base_.base & ~kUsefulBitsMask) != 0;
}
private:
static constexpr uintptr_t kUsefulBitsMask =
PartitionAddressSpace::CorePoolsSize() - 1;
static union alignas(kPartitionCachelineSize)
PA_COMPONENT_EXPORT(PARTITION_ALLOC) Base {
uintptr_t base;
char cache_line[kPartitionCachelineSize];
} g_base_ PA_CONSTINIT;
PA_ALWAYS_INLINE static bool IsBaseConsistent() {
return kUsefulBitsMask == (g_base_.base & kUsefulBitsMask);
}
static void SetBase(uintptr_t base);
static void ResetBaseForTesting();
friend class PartitionAddressSpace;
};
#endif
}
#if PA_BUILDFLAG(ENABLE_POINTER_COMPRESSION)
template <typename T>
class PA_TRIVIAL_ABI CompressedPointer final {
public:
using UnderlyingType = uint32_t;
PA_ALWAYS_INLINE constexpr CompressedPointer() = default;
PA_ALWAYS_INLINE explicit CompressedPointer(T* ptr) : value_(Compress(ptr)) {}
PA_ALWAYS_INLINE constexpr explicit CompressedPointer(std::nullptr_t)
: value_(0u) {}
PA_ALWAYS_INLINE constexpr CompressedPointer(const CompressedPointer&) =
default;
PA_ALWAYS_INLINE constexpr CompressedPointer(
CompressedPointer&& other) noexcept = default;
template <typename U,
std::enable_if_t<std::is_convertible_v<U*, T*>>* = nullptr>
PA_ALWAYS_INLINE constexpr CompressedPointer(
const CompressedPointer<U>& other) {
if constexpr (internal::IsDecayedSame<T, U>) {
value_ = other.value_;
} else {
value_ = Compress(other.get());
}
}
template <typename U,
std::enable_if_t<std::is_convertible_v<U*, T*>>* = nullptr>
PA_ALWAYS_INLINE constexpr CompressedPointer(
CompressedPointer<U>&& other) noexcept
: CompressedPointer(other) {}
~CompressedPointer() = default;
PA_ALWAYS_INLINE constexpr CompressedPointer& operator=(
const CompressedPointer&) = default;
PA_ALWAYS_INLINE constexpr CompressedPointer& operator=(
CompressedPointer&& other) noexcept = default;
template <typename U,
std::enable_if_t<std::is_convertible_v<U*, T*>>* = nullptr>
PA_ALWAYS_INLINE constexpr CompressedPointer& operator=(
const CompressedPointer<U>& other) {
CompressedPointer copy(other);
value_ = copy.value_;
return *this;
}
template <typename U,
std::enable_if_t<std::is_convertible_v<U*, T*>>* = nullptr>
PA_ALWAYS_INLINE constexpr CompressedPointer& operator=(
CompressedPointer<U>&& other) noexcept {
*this = other;
return *this;
}
PA_ALWAYS_INLINE constexpr CompressedPointer& operator=(std::nullptr_t) {
value_ = 0u;
return *this;
}
PA_ALWAYS_INLINE T* get() const { return Decompress(value_); }
PA_ALWAYS_INLINE constexpr bool is_nonnull() const { return value_; }
PA_ALWAYS_INLINE constexpr UnderlyingType GetAsIntegral() const {
return value_;
}
PA_ALWAYS_INLINE constexpr explicit operator bool() const {
return is_nonnull();
}
template <typename U = T,
std::enable_if_t<!std::is_void_v<std::remove_cv_t<U>>>* = nullptr>
PA_ALWAYS_INLINE U& operator*() const {
PA_DCHECK(is_nonnull());
return *get();
}
PA_ALWAYS_INLINE T* operator->() const {
PA_DCHECK(is_nonnull());
return get();
}
PA_ALWAYS_INLINE constexpr void swap(CompressedPointer& other) {
std::swap(value_, other.value_);
}
private:
template <typename>
friend class CompressedPointer;
static constexpr size_t kBitsForSignExtension = 1;
static constexpr size_t kOverallBitsToShift =
internal::CompressedPointerBaseGlobal::kBitsToShift +
kBitsForSignExtension;
PA_ALWAYS_INLINE static UnderlyingType Compress(T* ptr) {
static constexpr size_t kMinimalRequiredAlignment = 8;
static_assert((1 << kOverallBitsToShift) == kMinimalRequiredAlignment);
#if PA_BUILDFLAG(DCHECKS_ARE_ON)
PA_DCHECK(reinterpret_cast<uintptr_t>(ptr) % kMinimalRequiredAlignment ==
0);
PA_DCHECK(internal::CompressedPointerBaseGlobal::IsSet());
const uintptr_t base = internal::CompressedPointerBaseGlobal::Get();
static constexpr size_t kCorePoolsBaseMask =
~(internal::PartitionAddressSpace::CorePoolsSize() - 1);
PA_DCHECK(!ptr ||
(base & kCorePoolsBaseMask) ==
(reinterpret_cast<uintptr_t>(ptr) & kCorePoolsBaseMask));
#endif
const auto uptr = reinterpret_cast<uintptr_t>(ptr);
auto compressed = static_cast<UnderlyingType>(uptr >> kOverallBitsToShift);
if (compressed) {
compressed |= (1u << (sizeof(uint32_t) * CHAR_BIT - 1));
}
return compressed;
}
PA_ALWAYS_INLINE static T* Decompress(UnderlyingType ptr) {
PA_DCHECK(internal::CompressedPointerBaseGlobal::IsSet());
const uintptr_t base = internal::CompressedPointerBaseGlobal::Get();
const uint64_t mask = static_cast<uint64_t>(static_cast<int32_t>(ptr))
<< (kOverallBitsToShift);
return reinterpret_cast<T*>(mask & base);
}
UnderlyingType value_;
};
template <typename T>
PA_ALWAYS_INLINE constexpr void swap(CompressedPointer<T>& a,
CompressedPointer<T>& b) {
a.swap(b);
}
template <typename T, typename U>
PA_ALWAYS_INLINE bool operator==(CompressedPointer<T> a,
CompressedPointer<U> b) {
if constexpr (internal::IsDecayedSame<T, U>) {
return a.GetAsIntegral() == b.GetAsIntegral();
} else {
return a.get() == b.get();
}
}
template <typename T, typename U>
PA_ALWAYS_INLINE constexpr bool operator==(CompressedPointer<T> a, U* b) {
return a == static_cast<CompressedPointer<U>>(b);
}
template <typename T, typename U>
PA_ALWAYS_INLINE constexpr bool operator==(T* a, CompressedPointer<U> b) {
return b == a;
}
template <typename T>
PA_ALWAYS_INLINE constexpr bool operator==(CompressedPointer<T> a,
std::nullptr_t) {
return !a.is_nonnull();
}
template <typename T, typename U>
PA_ALWAYS_INLINE constexpr bool operator==(std::nullptr_t,
CompressedPointer<U> b) {
return b == nullptr;
}
template <typename T, typename U>
PA_ALWAYS_INLINE constexpr bool operator!=(CompressedPointer<T> a,
CompressedPointer<U> b) {
return !(a == b);
}
template <typename T, typename U>
PA_ALWAYS_INLINE constexpr bool operator!=(CompressedPointer<T> a, U* b) {
return a != static_cast<CompressedPointer<U>>(b);
}
template <typename T, typename U>
PA_ALWAYS_INLINE constexpr bool operator!=(T* a, CompressedPointer<U> b) {
return b != a;
}
template <typename T>
PA_ALWAYS_INLINE constexpr bool operator!=(CompressedPointer<T> a,
std::nullptr_t) {
return a.is_nonnull();
}
template <typename T, typename U>
PA_ALWAYS_INLINE constexpr bool operator!=(std::nullptr_t,
CompressedPointer<U> b) {
return b != nullptr;
}
template <typename T, typename U>
PA_ALWAYS_INLINE constexpr bool operator<(CompressedPointer<T> a,
CompressedPointer<U> b) {
if constexpr (internal::IsDecayedSame<T, U>) {
return a.GetAsIntegral() < b.GetAsIntegral();
} else {
return a.get() < b.get();
}
}
template <typename T, typename U>
PA_ALWAYS_INLINE constexpr bool operator<(CompressedPointer<T> a, U* b) {
return a < static_cast<CompressedPointer<U>>(b);
}
template <typename T, typename U>
PA_ALWAYS_INLINE constexpr bool operator<(T* a, CompressedPointer<U> b) {
return static_cast<CompressedPointer<T>>(a) < b;
}
template <typename T, typename U>
PA_ALWAYS_INLINE constexpr bool operator<=(CompressedPointer<T> a,
CompressedPointer<U> b) {
if constexpr (internal::IsDecayedSame<T, U>) {
return a.GetAsIntegral() <= b.GetAsIntegral();
} else {
return a.get() <= b.get();
}
}
template <typename T, typename U>
PA_ALWAYS_INLINE constexpr bool operator<=(CompressedPointer<T> a, U* b) {
return a <= static_cast<CompressedPointer<U>>(b);
}
template <typename T, typename U>
PA_ALWAYS_INLINE constexpr bool operator<=(T* a, CompressedPointer<U> b) {
return static_cast<CompressedPointer<T>>(a) <= b;
}
template <typename T, typename U>
PA_ALWAYS_INLINE constexpr bool operator>(CompressedPointer<T> a,
CompressedPointer<U> b) {
return !(a <= b);
}
template <typename T, typename U>
PA_ALWAYS_INLINE constexpr bool operator>(CompressedPointer<T> a, U* b) {
return a > static_cast<CompressedPointer<U>>(b);
}
template <typename T, typename U>
PA_ALWAYS_INLINE constexpr bool operator>(T* a, CompressedPointer<U> b) {
return static_cast<CompressedPointer<T>>(a) > b;
}
template <typename T, typename U>
PA_ALWAYS_INLINE constexpr bool operator>=(CompressedPointer<T> a,
CompressedPointer<U> b) {
return !(a < b);
}
template <typename T, typename U>
PA_ALWAYS_INLINE constexpr bool operator>=(CompressedPointer<T> a, U* b) {
return a >= static_cast<CompressedPointer<U>>(b);
}
template <typename T, typename U>
PA_ALWAYS_INLINE constexpr bool operator>=(T* a, CompressedPointer<U> b) {
return static_cast<CompressedPointer<T>>(a) >= b;
}
#endif
template <typename T>
class PA_TRIVIAL_ABI UncompressedPointer final { … };
template <typename T>
PA_ALWAYS_INLINE constexpr void swap(UncompressedPointer<T>& a,
UncompressedPointer<T>& b) { … }
template <typename T, typename U>
PA_ALWAYS_INLINE constexpr bool operator==(UncompressedPointer<T> a,
UncompressedPointer<U> b) { … }
template <typename T, typename U>
PA_ALWAYS_INLINE constexpr bool operator==(UncompressedPointer<T> a, U* b) { … }
template <typename T, typename U>
PA_ALWAYS_INLINE constexpr bool operator==(T* a, UncompressedPointer<U> b) { … }
template <typename T>
PA_ALWAYS_INLINE constexpr bool operator==(UncompressedPointer<T> a,
std::nullptr_t) { … }
template <typename T, typename U>
PA_ALWAYS_INLINE constexpr bool operator==(std::nullptr_t,
UncompressedPointer<U> b) { … }
template <typename T, typename U>
PA_ALWAYS_INLINE constexpr bool operator!=(UncompressedPointer<T> a,
UncompressedPointer<U> b) { … }
template <typename T, typename U>
PA_ALWAYS_INLINE constexpr bool operator!=(UncompressedPointer<T> a, U* b) { … }
template <typename T, typename U>
PA_ALWAYS_INLINE constexpr bool operator!=(T* a, UncompressedPointer<U> b) { … }
template <typename T>
PA_ALWAYS_INLINE constexpr bool operator!=(UncompressedPointer<T> a,
std::nullptr_t) { … }
template <typename T, typename U>
PA_ALWAYS_INLINE constexpr bool operator!=(std::nullptr_t,
UncompressedPointer<U> b) { … }
template <typename T, typename U>
PA_ALWAYS_INLINE constexpr bool operator<(UncompressedPointer<T> a,
UncompressedPointer<U> b) { … }
template <typename T, typename U>
PA_ALWAYS_INLINE constexpr bool operator<(UncompressedPointer<T> a, U* b) { … }
template <typename T, typename U>
PA_ALWAYS_INLINE constexpr bool operator<(T* a, UncompressedPointer<U> b) { … }
template <typename T, typename U>
PA_ALWAYS_INLINE constexpr bool operator<=(UncompressedPointer<T> a,
UncompressedPointer<U> b) { … }
template <typename T, typename U>
PA_ALWAYS_INLINE constexpr bool operator<=(UncompressedPointer<T> a, U* b) { … }
template <typename T, typename U>
PA_ALWAYS_INLINE constexpr bool operator<=(T* a, UncompressedPointer<U> b) { … }
template <typename T, typename U>
PA_ALWAYS_INLINE constexpr bool operator>(UncompressedPointer<T> a,
UncompressedPointer<U> b) { … }
template <typename T, typename U>
PA_ALWAYS_INLINE constexpr bool operator>(UncompressedPointer<T> a, U* b) { … }
template <typename T, typename U>
PA_ALWAYS_INLINE constexpr bool operator>(T* a, UncompressedPointer<U> b) { … }
template <typename T, typename U>
PA_ALWAYS_INLINE constexpr bool operator>=(UncompressedPointer<T> a,
UncompressedPointer<U> b) { … }
template <typename T, typename U>
PA_ALWAYS_INLINE constexpr bool operator>=(UncompressedPointer<T> a, U* b) { … }
template <typename T, typename U>
PA_ALWAYS_INLINE constexpr bool operator>=(T* a, UncompressedPointer<U> b) { … }
}
#endif