1 // Copyright 2022 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_COMPRESSED_POINTER_H_
6 #define BASE_ALLOCATOR_PARTITION_ALLOCATOR_COMPRESSED_POINTER_H_
7
8 #include <climits>
9 #include <type_traits>
10
11 #include "base/allocator/partition_allocator/partition_address_space.h"
12 #include "base/allocator/partition_allocator/partition_alloc_base/bits.h"
13 #include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h"
14 #include "base/allocator/partition_allocator/partition_alloc_base/component_export.h"
15
16 #if PA_CONFIG(POINTER_COMPRESSION)
17
18 #if !PA_CONFIG(GLUE_CORE_POOLS)
19 #error "Pointer compression only works with glued pools"
20 #endif
21 #if PA_CONFIG(DYNAMICALLY_SELECT_POOL_SIZE)
22 #error "Pointer compression currently supports constant pool size"
23 #endif
24
25 #endif // PA_CONFIG(POINTER_COMPRESSION)
26
27 namespace partition_alloc {
28
29 namespace internal {
30
31 template <typename T1, typename T2>
32 constexpr bool IsDecayedSame =
33 std::is_same_v<std::decay_t<T1>, std::decay_t<T2>>;
34
35 #if PA_CONFIG(POINTER_COMPRESSION)
36
37 // Pointer compression works by storing only the 'useful' 32-bit part of the
38 // pointer. The other half (the base) is stored in a global variable
39 // (CompressedPointerBaseGlobal::g_base_), which is used on decompression. To
40 // support fast branchless decompression of nullptr, we use the most significant
41 // bit in the compressed pointer to leverage sign-extension (for non-nullptr
42 // pointers, the most significant bit is set, whereas for nullptr it's not).
43 // Using this bit and supporting heaps larger than 4GB relies on having
44 // alignment bits in pointers. Assuming that all pointers point to at least
45 // 8-byte alignment objects, pointer compression can support heaps of size <=
46 // 16GB.
47 // ((3 alignment bits) = (1 bit for sign-extension) + (2 bits for 16GB heap)).
48 //
49 // Example: heap base: 0x4b0'ffffffff
50 // - g_base: 0x4b3'ffffffff (lower 34 bits set)
51 // - normal pointer: 0x4b2'a08b6480
52 // - compression:
53 // - shift right by 3: 0x96'54116c90
54 // - truncate: 0x54116c90
55 // - mark MSB: 0xd4116c90
56 // - decompression:
57 // - sign-extend: 0xffffffff'd4116c90
58 // - shift left by 3: 0xfffffffe'a08b6480
59 // - 'and' with g_base: 0x000004b2'a08b6480
60 //
61 // - nullptr: 0x00000000'00000000
62 // - compression:
63 // - shift right by 3: 0x00000000'00000000
64 // - truncate: 0x00000000
65 // - (don't mark MSB for nullptr)
66 // - decompression:
67 // - sign-extend: 0x00000000'00000000
68 // - shift left by 3: 0x00000000'00000000
69 // - 'and' with g_base: 0x00000000'00000000
70 //
71 // Pointer compression relies on having both the regular and the BRP pool (core
72 // pools) 'glued', so that the same base could be used for both. For simplicity,
73 // the configurations with dynamically selected pool size are not supported.
74 // However, they can be at the cost of performing an extra load for
75 // core-pools-shift-size on both compression and decompression.
76
77 class CompressedPointerBaseGlobal final {
78 public:
79 static constexpr size_t kUsefulBits =
80 base::bits::CountTrailingZeroBits(PartitionAddressSpace::CorePoolsSize());
81 static_assert(kUsefulBits >= sizeof(uint32_t) * CHAR_BIT);
82 static constexpr size_t kBitsToShift =
83 kUsefulBits - sizeof(uint32_t) * CHAR_BIT;
84
85 CompressedPointerBaseGlobal() = delete;
86
87 // Attribute const allows the compiler to assume that
88 // CompressedPointerBaseGlobal::g_base_ doesn't change (e.g. across calls) and
89 // thereby avoid redundant loads.
Get()90 PA_ALWAYS_INLINE __attribute__((const)) static uintptr_t Get() {
91 PA_DCHECK(IsBaseConsistent());
92 return g_base_.base;
93 }
94
IsSet()95 PA_ALWAYS_INLINE static bool IsSet() {
96 PA_DCHECK(IsBaseConsistent());
97 return (g_base_.base & ~kUsefulBitsMask) != 0;
98 }
99
100 private:
101 static constexpr uintptr_t kUsefulBitsMask =
102 PartitionAddressSpace::CorePoolsSize() - 1;
103
104 static union alignas(kPartitionCachelineSize)
PA_COMPONENT_EXPORT(PARTITION_ALLOC)105 PA_COMPONENT_EXPORT(PARTITION_ALLOC) Base {
106 uintptr_t base;
107 char cache_line[kPartitionCachelineSize];
108 } g_base_ PA_CONSTINIT;
109
IsBaseConsistent()110 PA_ALWAYS_INLINE static bool IsBaseConsistent() {
111 return kUsefulBitsMask == (g_base_.base & kUsefulBitsMask);
112 }
113
114 static void SetBase(uintptr_t base);
115 static void ResetBaseForTesting();
116
117 friend class PartitionAddressSpace;
118 };
119
120 #endif // PA_CONFIG(POINTER_COMPRESSION)
121
122 } // namespace internal
123
124 #if PA_CONFIG(POINTER_COMPRESSION)
125
126 template <typename T>
127 class PA_TRIVIAL_ABI CompressedPointer final {
128 public:
129 using UnderlyingType = uint32_t;
130
131 PA_ALWAYS_INLINE constexpr CompressedPointer() = default;
CompressedPointer(T * ptr)132 PA_ALWAYS_INLINE explicit CompressedPointer(T* ptr) : value_(Compress(ptr)) {}
CompressedPointer(std::nullptr_t)133 PA_ALWAYS_INLINE constexpr explicit CompressedPointer(std::nullptr_t)
134 : value_(0u) {}
135
136 PA_ALWAYS_INLINE constexpr CompressedPointer(const CompressedPointer&) =
137 default;
138 PA_ALWAYS_INLINE constexpr CompressedPointer(
139 CompressedPointer&& other) noexcept = default;
140
141 template <typename U,
142 std::enable_if_t<std::is_convertible_v<U*, T*>>* = nullptr>
CompressedPointer(const CompressedPointer<U> & other)143 PA_ALWAYS_INLINE constexpr CompressedPointer(
144 const CompressedPointer<U>& other) {
145 if constexpr (internal::IsDecayedSame<T, U>) {
146 // When pointers have the same type modulo constness, avoid the
147 // compress-decompress round.
148 value_ = other.value_;
149 } else {
150 // When the types are different, perform the round, because the pointer
151 // may need to be adjusted.
152 // TODO(1376980): Avoid the cycle here.
153 value_ = Compress(other.get());
154 }
155 }
156
157 template <typename U,
158 std::enable_if_t<std::is_convertible_v<U*, T*>>* = nullptr>
CompressedPointer(CompressedPointer<U> && other)159 PA_ALWAYS_INLINE constexpr CompressedPointer(
160 CompressedPointer<U>&& other) noexcept
161 : CompressedPointer(other) {}
162
163 ~CompressedPointer() = default;
164
165 PA_ALWAYS_INLINE constexpr CompressedPointer& operator=(
166 const CompressedPointer&) = default;
167 PA_ALWAYS_INLINE constexpr CompressedPointer& operator=(
168 CompressedPointer&& other) noexcept = default;
169
170 template <typename U,
171 std::enable_if_t<std::is_convertible_v<U*, T*>>* = nullptr>
172 PA_ALWAYS_INLINE constexpr CompressedPointer& operator=(
173 const CompressedPointer<U>& other) {
174 CompressedPointer copy(other);
175 value_ = copy.value_;
176 return *this;
177 }
178
179 template <typename U,
180 std::enable_if_t<std::is_convertible_v<U*, T*>>* = nullptr>
181 PA_ALWAYS_INLINE constexpr CompressedPointer& operator=(
182 CompressedPointer<U>&& other) noexcept {
183 *this = other;
184 return *this;
185 }
186
187 // Don't perform compression when assigning to nullptr.
188 PA_ALWAYS_INLINE constexpr CompressedPointer& operator=(std::nullptr_t) {
189 value_ = 0u;
190 return *this;
191 }
192
get()193 PA_ALWAYS_INLINE T* get() const { return Decompress(value_); }
194
is_nonnull()195 PA_ALWAYS_INLINE constexpr bool is_nonnull() const { return value_; }
196
GetAsIntegral()197 PA_ALWAYS_INLINE constexpr UnderlyingType GetAsIntegral() const {
198 return value_;
199 }
200
201 PA_ALWAYS_INLINE constexpr explicit operator bool() const {
202 return is_nonnull();
203 }
204
205 template <typename U = T,
206 std::enable_if_t<!std::is_void_v<std::remove_cv_t<U>>>* = nullptr>
207 PA_ALWAYS_INLINE U& operator*() const {
208 PA_DCHECK(is_nonnull());
209 return *get();
210 }
211
212 PA_ALWAYS_INLINE T* operator->() const {
213 PA_DCHECK(is_nonnull());
214 return get();
215 }
216
swap(CompressedPointer & other)217 PA_ALWAYS_INLINE constexpr void swap(CompressedPointer& other) {
218 std::swap(value_, other.value_);
219 }
220
221 private:
222 template <typename>
223 friend class CompressedPointer;
224
225 static constexpr size_t kBitsForSignExtension = 1;
226 static constexpr size_t kOverallBitsToShift =
227 internal::CompressedPointerBaseGlobal::kBitsToShift +
228 kBitsForSignExtension;
229
Compress(T * ptr)230 PA_ALWAYS_INLINE static UnderlyingType Compress(T* ptr) {
231 static constexpr size_t kMinimalRequiredAlignment = 8;
232 static_assert((1 << kOverallBitsToShift) == kMinimalRequiredAlignment);
233
234 #if BUILDFLAG(PA_DCHECK_IS_ON)
235 PA_DCHECK(reinterpret_cast<uintptr_t>(ptr) % kMinimalRequiredAlignment ==
236 0);
237 PA_DCHECK(internal::CompressedPointerBaseGlobal::IsSet());
238
239 const uintptr_t base = internal::CompressedPointerBaseGlobal::Get();
240 static constexpr size_t kCorePoolsBaseMask =
241 ~(internal::PartitionAddressSpace::CorePoolsSize() - 1);
242 PA_DCHECK(!ptr ||
243 (base & kCorePoolsBaseMask) ==
244 (reinterpret_cast<uintptr_t>(ptr) & kCorePoolsBaseMask));
245 #endif // BUILDFLAG(PA_DCHECK_IS_ON)
246
247 const auto uptr = reinterpret_cast<uintptr_t>(ptr);
248 // Shift the pointer and truncate.
249 auto compressed = static_cast<UnderlyingType>(uptr >> kOverallBitsToShift);
250 // If the pointer is non-null, mark the most-significant-bit to sign-extend
251 // it on decompression. Assuming compression is a significantly less
252 // frequent operation, we let more work here in favor of faster
253 // decompression.
254 // TODO(1376980): Avoid this by overreserving the heap.
255 if (compressed) {
256 compressed |= (1u << (sizeof(uint32_t) * CHAR_BIT - 1));
257 }
258
259 return compressed;
260 }
261
Decompress(UnderlyingType ptr)262 PA_ALWAYS_INLINE static T* Decompress(UnderlyingType ptr) {
263 PA_DCHECK(internal::CompressedPointerBaseGlobal::IsSet());
264 const uintptr_t base = internal::CompressedPointerBaseGlobal::Get();
265 // Treat compressed pointer as signed and cast it to uint64_t, which will
266 // sign-extend it. Then, shift the result by one. It's important to shift
267 // the already unsigned value, as otherwise it would result in undefined
268 // behavior.
269 const uint64_t mask = static_cast<uint64_t>(static_cast<int32_t>(ptr))
270 << (kOverallBitsToShift);
271 return reinterpret_cast<T*>(mask & base);
272 }
273
274 UnderlyingType value_;
275 };
276
277 template <typename T>
swap(CompressedPointer<T> & a,CompressedPointer<T> & b)278 PA_ALWAYS_INLINE constexpr void swap(CompressedPointer<T>& a,
279 CompressedPointer<T>& b) {
280 a.swap(b);
281 }
282
283 // operators==.
284 template <typename T, typename U>
285 PA_ALWAYS_INLINE bool operator==(CompressedPointer<T> a,
286 CompressedPointer<U> b) {
287 if constexpr (internal::IsDecayedSame<T, U>) {
288 // When pointers have the same type modulo constness, simply compare
289 // compressed values.
290 return a.GetAsIntegral() == b.GetAsIntegral();
291 } else {
292 // When the types are different, compare decompressed pointers, because the
293 // pointers may need to be adjusted.
294 // TODO(1376980): Avoid decompression here.
295 return a.get() == b.get();
296 }
297 }
298
299 template <typename T, typename U>
300 PA_ALWAYS_INLINE constexpr bool operator==(CompressedPointer<T> a, U* b) {
301 // Do compression, since it is less expensive.
302 return a == static_cast<CompressedPointer<U>>(b);
303 }
304
305 template <typename T, typename U>
306 PA_ALWAYS_INLINE constexpr bool operator==(T* a, CompressedPointer<U> b) {
307 return b == a;
308 }
309
310 template <typename T>
311 PA_ALWAYS_INLINE constexpr bool operator==(CompressedPointer<T> a,
312 std::nullptr_t) {
313 return !a.is_nonnull();
314 }
315
316 template <typename T, typename U>
317 PA_ALWAYS_INLINE constexpr bool operator==(std::nullptr_t,
318 CompressedPointer<U> b) {
319 return b == nullptr;
320 }
321
322 // operators!=.
323 template <typename T, typename U>
324 PA_ALWAYS_INLINE constexpr bool operator!=(CompressedPointer<T> a,
325 CompressedPointer<U> b) {
326 return !(a == b);
327 }
328
329 template <typename T, typename U>
330 PA_ALWAYS_INLINE constexpr bool operator!=(CompressedPointer<T> a, U* b) {
331 // Do compression, since it is less expensive.
332 return a != static_cast<CompressedPointer<U>>(b);
333 }
334
335 template <typename T, typename U>
336 PA_ALWAYS_INLINE constexpr bool operator!=(T* a, CompressedPointer<U> b) {
337 return b != a;
338 }
339
340 template <typename T>
341 PA_ALWAYS_INLINE constexpr bool operator!=(CompressedPointer<T> a,
342 std::nullptr_t) {
343 return a.is_nonnull();
344 }
345
346 template <typename T, typename U>
347 PA_ALWAYS_INLINE constexpr bool operator!=(std::nullptr_t,
348 CompressedPointer<U> b) {
349 return b != nullptr;
350 }
351
352 // operators<.
353 template <typename T, typename U>
354 PA_ALWAYS_INLINE constexpr bool operator<(CompressedPointer<T> a,
355 CompressedPointer<U> b) {
356 if constexpr (internal::IsDecayedSame<T, U>) {
357 // When pointers have the same type modulo constness, simply compare
358 // compressed values.
359 return a.GetAsIntegral() < b.GetAsIntegral();
360 } else {
361 // When the types are different, compare decompressed pointers, because the
362 // pointers may need to be adjusted.
363 // TODO(1376980): Avoid decompression here.
364 return a.get() < b.get();
365 }
366 }
367
368 template <typename T, typename U>
369 PA_ALWAYS_INLINE constexpr bool operator<(CompressedPointer<T> a, U* b) {
370 // Do compression, since it is less expensive.
371 return a < static_cast<CompressedPointer<U>>(b);
372 }
373
374 template <typename T, typename U>
375 PA_ALWAYS_INLINE constexpr bool operator<(T* a, CompressedPointer<U> b) {
376 // Do compression, since it is less expensive.
377 return static_cast<CompressedPointer<T>>(a) < b;
378 }
379
380 // operators<=.
381 template <typename T, typename U>
382 PA_ALWAYS_INLINE constexpr bool operator<=(CompressedPointer<T> a,
383 CompressedPointer<U> b) {
384 if constexpr (internal::IsDecayedSame<T, U>) {
385 // When pointers have the same type modulo constness, simply compare
386 // compressed values.
387 return a.GetAsIntegral() <= b.GetAsIntegral();
388 } else {
389 // When the types are different, compare decompressed pointers, because the
390 // pointers may need to be adjusted.
391 // TODO(1376980): Avoid decompression here.
392 return a.get() <= b.get();
393 }
394 }
395
396 template <typename T, typename U>
397 PA_ALWAYS_INLINE constexpr bool operator<=(CompressedPointer<T> a, U* b) {
398 // Do compression, since it is less expensive.
399 return a <= static_cast<CompressedPointer<U>>(b);
400 }
401
402 template <typename T, typename U>
403 PA_ALWAYS_INLINE constexpr bool operator<=(T* a, CompressedPointer<U> b) {
404 // Do compression, since it is less expensive.
405 return static_cast<CompressedPointer<T>>(a) <= b;
406 }
407
408 // operators>.
409 template <typename T, typename U>
410 PA_ALWAYS_INLINE constexpr bool operator>(CompressedPointer<T> a,
411 CompressedPointer<U> b) {
412 return !(a <= b);
413 }
414
415 template <typename T, typename U>
416 PA_ALWAYS_INLINE constexpr bool operator>(CompressedPointer<T> a, U* b) {
417 // Do compression, since it is less expensive.
418 return a > static_cast<CompressedPointer<U>>(b);
419 }
420
421 template <typename T, typename U>
422 PA_ALWAYS_INLINE constexpr bool operator>(T* a, CompressedPointer<U> b) {
423 // Do compression, since it is less expensive.
424 return static_cast<CompressedPointer<T>>(a) > b;
425 }
426
427 // operators>=.
428 template <typename T, typename U>
429 PA_ALWAYS_INLINE constexpr bool operator>=(CompressedPointer<T> a,
430 CompressedPointer<U> b) {
431 return !(a < b);
432 }
433
434 template <typename T, typename U>
435 PA_ALWAYS_INLINE constexpr bool operator>=(CompressedPointer<T> a, U* b) {
436 // Do compression, since it is less expensive.
437 return a >= static_cast<CompressedPointer<U>>(b);
438 }
439
440 template <typename T, typename U>
441 PA_ALWAYS_INLINE constexpr bool operator>=(T* a, CompressedPointer<U> b) {
442 // Do compression, since it is less expensive.
443 return static_cast<CompressedPointer<T>>(a) >= b;
444 }
445
446 #endif // PA_CONFIG(POINTER_COMPRESSION)
447
448 // Simple wrapper over the raw pointer.
449 template <typename T>
450 class PA_TRIVIAL_ABI UncompressedPointer final {
451 public:
452 PA_ALWAYS_INLINE constexpr UncompressedPointer() = default;
UncompressedPointer(T * ptr)453 PA_ALWAYS_INLINE constexpr explicit UncompressedPointer(T* ptr) : ptr_(ptr) {}
UncompressedPointer(std::nullptr_t)454 PA_ALWAYS_INLINE constexpr explicit UncompressedPointer(std::nullptr_t)
455 : ptr_(nullptr) {}
456
457 PA_ALWAYS_INLINE constexpr UncompressedPointer(const UncompressedPointer&) =
458 default;
459 PA_ALWAYS_INLINE constexpr UncompressedPointer(
460 UncompressedPointer&& other) noexcept = default;
461
462 template <typename U,
463 std::enable_if_t<std::is_convertible_v<U*, T*>>* = nullptr>
UncompressedPointer(const UncompressedPointer<U> & other)464 PA_ALWAYS_INLINE constexpr explicit UncompressedPointer(
465 const UncompressedPointer<U>& other)
466 : ptr_(other.ptr_) {}
467
468 template <typename U,
469 std::enable_if_t<std::is_convertible_v<U*, T*>>* = nullptr>
UncompressedPointer(UncompressedPointer<U> && other)470 PA_ALWAYS_INLINE constexpr explicit UncompressedPointer(
471 UncompressedPointer<U>&& other) noexcept
472 : ptr_(std::move(other.ptr_)) {}
473
474 ~UncompressedPointer() = default;
475
476 PA_ALWAYS_INLINE constexpr UncompressedPointer& operator=(
477 const UncompressedPointer&) = default;
478 PA_ALWAYS_INLINE constexpr UncompressedPointer& operator=(
479 UncompressedPointer&& other) noexcept = default;
480
481 template <typename U,
482 std::enable_if_t<std::is_convertible_v<U*, T*>>* = nullptr>
483 PA_ALWAYS_INLINE constexpr UncompressedPointer& operator=(
484 const UncompressedPointer<U>& other) {
485 ptr_ = other.ptr_;
486 return *this;
487 }
488
489 template <typename U,
490 std::enable_if_t<std::is_convertible_v<U*, T*>>* = nullptr>
491 PA_ALWAYS_INLINE constexpr UncompressedPointer& operator=(
492 UncompressedPointer<U>&& other) noexcept {
493 ptr_ = std::move(other.ptr_);
494 return *this;
495 }
496
497 PA_ALWAYS_INLINE constexpr UncompressedPointer& operator=(std::nullptr_t) {
498 ptr_ = nullptr;
499 return *this;
500 }
501
get()502 PA_ALWAYS_INLINE constexpr T* get() const { return ptr_; }
503
is_nonnull()504 PA_ALWAYS_INLINE constexpr bool is_nonnull() const { return ptr_; }
505
506 PA_ALWAYS_INLINE constexpr explicit operator bool() const {
507 return is_nonnull();
508 }
509
510 template <typename U = T,
511 std::enable_if_t<!std::is_void_v<std::remove_cv_t<U>>>* = nullptr>
512 PA_ALWAYS_INLINE constexpr U& operator*() const {
513 PA_DCHECK(is_nonnull());
514 return *get();
515 }
516
517 PA_ALWAYS_INLINE constexpr T* operator->() const {
518 PA_DCHECK(is_nonnull());
519 return get();
520 }
521
swap(UncompressedPointer & other)522 PA_ALWAYS_INLINE constexpr void swap(UncompressedPointer& other) {
523 std::swap(ptr_, other.ptr_);
524 }
525
526 private:
527 template <typename>
528 friend class UncompressedPointer;
529
530 T* ptr_;
531 };
532
533 template <typename T>
swap(UncompressedPointer<T> & a,UncompressedPointer<T> & b)534 PA_ALWAYS_INLINE constexpr void swap(UncompressedPointer<T>& a,
535 UncompressedPointer<T>& b) {
536 a.swap(b);
537 }
538
539 // operators==.
540 template <typename T, typename U>
541 PA_ALWAYS_INLINE constexpr bool operator==(UncompressedPointer<T> a,
542 UncompressedPointer<U> b) {
543 return a.get() == b.get();
544 }
545
546 template <typename T, typename U>
547 PA_ALWAYS_INLINE constexpr bool operator==(UncompressedPointer<T> a, U* b) {
548 return a == static_cast<UncompressedPointer<U>>(b);
549 }
550
551 template <typename T, typename U>
552 PA_ALWAYS_INLINE constexpr bool operator==(T* a, UncompressedPointer<U> b) {
553 return b == a;
554 }
555
556 template <typename T>
557 PA_ALWAYS_INLINE constexpr bool operator==(UncompressedPointer<T> a,
558 std::nullptr_t) {
559 return !a.is_nonnull();
560 }
561
562 template <typename T, typename U>
563 PA_ALWAYS_INLINE constexpr bool operator==(std::nullptr_t,
564 UncompressedPointer<U> b) {
565 return b == nullptr;
566 }
567
568 // operators!=.
569 template <typename T, typename U>
570 PA_ALWAYS_INLINE constexpr bool operator!=(UncompressedPointer<T> a,
571 UncompressedPointer<U> b) {
572 return !(a == b);
573 }
574
575 template <typename T, typename U>
576 PA_ALWAYS_INLINE constexpr bool operator!=(UncompressedPointer<T> a, U* b) {
577 return a != static_cast<UncompressedPointer<U>>(b);
578 }
579
580 template <typename T, typename U>
581 PA_ALWAYS_INLINE constexpr bool operator!=(T* a, UncompressedPointer<U> b) {
582 return b != a;
583 }
584
585 template <typename T>
586 PA_ALWAYS_INLINE constexpr bool operator!=(UncompressedPointer<T> a,
587 std::nullptr_t) {
588 return a.is_nonnull();
589 }
590
591 template <typename T, typename U>
592 PA_ALWAYS_INLINE constexpr bool operator!=(std::nullptr_t,
593 UncompressedPointer<U> b) {
594 return b != nullptr;
595 }
596
597 // operators<.
598 template <typename T, typename U>
599 PA_ALWAYS_INLINE constexpr bool operator<(UncompressedPointer<T> a,
600 UncompressedPointer<U> b) {
601 return a.get() < b.get();
602 }
603
604 template <typename T, typename U>
605 PA_ALWAYS_INLINE constexpr bool operator<(UncompressedPointer<T> a, U* b) {
606 return a < static_cast<UncompressedPointer<U>>(b);
607 }
608
609 template <typename T, typename U>
610 PA_ALWAYS_INLINE constexpr bool operator<(T* a, UncompressedPointer<U> b) {
611 return static_cast<UncompressedPointer<T>>(a) < b;
612 }
613
614 // operators<=.
615 template <typename T, typename U>
616 PA_ALWAYS_INLINE constexpr bool operator<=(UncompressedPointer<T> a,
617 UncompressedPointer<U> b) {
618 return a.get() <= b.get();
619 }
620
621 template <typename T, typename U>
622 PA_ALWAYS_INLINE constexpr bool operator<=(UncompressedPointer<T> a, U* b) {
623 return a <= static_cast<UncompressedPointer<U>>(b);
624 }
625
626 template <typename T, typename U>
627 PA_ALWAYS_INLINE constexpr bool operator<=(T* a, UncompressedPointer<U> b) {
628 return static_cast<UncompressedPointer<T>>(a) <= b;
629 }
630
631 // operators>.
632 template <typename T, typename U>
633 PA_ALWAYS_INLINE constexpr bool operator>(UncompressedPointer<T> a,
634 UncompressedPointer<U> b) {
635 return !(a <= b);
636 }
637
638 template <typename T, typename U>
639 PA_ALWAYS_INLINE constexpr bool operator>(UncompressedPointer<T> a, U* b) {
640 return a > static_cast<UncompressedPointer<U>>(b);
641 }
642
643 template <typename T, typename U>
644 PA_ALWAYS_INLINE constexpr bool operator>(T* a, UncompressedPointer<U> b) {
645 return static_cast<UncompressedPointer<T>>(a) > b;
646 }
647
648 // operators>=.
649 template <typename T, typename U>
650 PA_ALWAYS_INLINE constexpr bool operator>=(UncompressedPointer<T> a,
651 UncompressedPointer<U> b) {
652 return !(a < b);
653 }
654
655 template <typename T, typename U>
656 PA_ALWAYS_INLINE constexpr bool operator>=(UncompressedPointer<T> a, U* b) {
657 return a >= static_cast<UncompressedPointer<U>>(b);
658 }
659
660 template <typename T, typename U>
661 PA_ALWAYS_INLINE constexpr bool operator>=(T* a, UncompressedPointer<U> b) {
662 return static_cast<UncompressedPointer<T>>(a) >= b;
663 }
664
665 } // namespace partition_alloc
666
667 #endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_COMPRESSED_POINTER_H_
668