509 lines · c
1//===----------------------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#ifndef SUPPORT_CONTROLLED_ALLOCATORS_H10#define SUPPORT_CONTROLLED_ALLOCATORS_H11 12#include <memory>13#include <type_traits>14#include <cstddef>15#include <cstdlib>16#include <cstring>17#include <cstdint>18#include <cassert>19#include <new>20 21#include "test_macros.h"22#include "type_id.h"23 24#if TEST_STD_VER < 1125#error This header requires C++11 or greater26#endif27 28struct AllocController;29 // 'AllocController' is a concrete type that instruments and controls the30 // behavior of test allocators.31 32template <class T, std::size_t ID = 0>33class CountingAllocator;34 // 'CountingAllocator' is an basic implementation of the 'Allocator'35 // requirements that use the 'AllocController' interface.36 37template <class T>38class MinAlignAllocator;39 // 'MinAlignAllocator' is an instrumented test type which implements the40 // 'Allocator' requirements. 'MinAlignAllocator' ensures that it *never*41 // returns a pointer to over-aligned storage. For example42 // 'MinAlignPointer<char>{}.allocate(...)' will never a 2-byte aligned43 // pointer.44 45template <class T>46class NullAllocator;47 // 'NullAllocator' is an instrumented test type which implements the48 // 'Allocator' requirements except that 'allocator' and 'deallocate' are49 // nops.50 51 52#define DISALLOW_COPY(Type) \53 Type(Type const&) = delete; \54 Type& operator=(Type const&) = delete55 56constexpr std::size_t MaxAlignV = alignof(std::max_align_t);57 58struct TestException {};59 60struct AllocController {61 int copy_constructed = 0;62 int move_constructed = 0;63 64 int alive = 0;65 int alloc_count = 0;66 int dealloc_count = 0;67 int is_equal_count = 0;68 69 std::size_t alive_size;70 std::size_t allocated_size;71 std::size_t deallocated_size;72 73 std::size_t last_size = 0;74 std::size_t last_align = 0;75 void * last_pointer = 0;76 77 std::size_t last_alloc_size = 0;78 std::size_t last_alloc_align = 0;79 void * last_alloc_pointer = nullptr;80 81 std::size_t last_dealloc_size = 0;82 std::size_t last_dealloc_align = 0;83 void * last_dealloc_pointer = nullptr;84 85 bool throw_on_alloc = false;86 87 int construct_called = 0;88 void *last_construct_pointer = nullptr;89 TypeID const* last_construct_alloc = nullptr;90 TypeID const* last_construct_type = nullptr;91 TypeID const* last_construct_args = nullptr;92 93 int destroy_called = 0;94 void *last_destroy_pointer = nullptr;95 TypeID const* last_destroy_alloc = nullptr;96 TypeID const* last_destroy_type = nullptr;97 98 AllocController() = default;99 100 void countAlloc(void* p, std::size_t s, size_t a) {101 ++alive;102 ++alloc_count;103 alive_size += s;104 allocated_size += s;105 last_pointer = last_alloc_pointer = p;106 last_size = last_alloc_size = s;107 last_align = last_alloc_align = a;108 }109 110 void countDealloc(void* p, std::size_t s, size_t a) {111 --alive;112 ++dealloc_count;113 alive_size -= s;114 deallocated_size += s;115 last_pointer = last_dealloc_pointer = p;116 last_size = last_dealloc_size = s;117 last_align = last_dealloc_align = a;118 }119 120 template <class ...Args, class Alloc, class Tp>121 void countConstruct(Alloc const&, Tp *p) {122 ++construct_called;123 last_construct_pointer = p;124 last_construct_alloc = &makeTypeID<Alloc>();125 last_construct_type = &makeTypeID<Tp>();126 last_construct_args = &makeArgumentID<Args...>();127 }128 129 template <class Alloc, class Tp>130 void countDestroy(Alloc const&, Tp *p) {131 ++destroy_called;132 last_destroy_alloc = &makeTypeID<Alloc>();133 last_destroy_type = &makeTypeID<Tp>();134 last_destroy_pointer = p;135 }136 137 void reset() { std::memset(this, 0, sizeof(*this)); }138 void resetConstructDestroy() {139 construct_called = 0;140 last_construct_pointer = nullptr;141 last_construct_alloc = last_construct_args = last_construct_type = nullptr;142 destroy_called = 0;143 last_destroy_alloc = nullptr;144 last_destroy_pointer = nullptr;145 }146public:147 bool checkAlloc(void* p, std::size_t s, size_t a) const {148 return p == last_alloc_pointer &&149 s == last_alloc_size &&150 a == last_alloc_align;151 }152 153 bool checkAlloc(void* p, std::size_t s) const {154 return p == last_alloc_pointer &&155 s == last_alloc_size;156 }157 158 bool checkAllocAtLeast(void* p, std::size_t s, size_t a) const {159 return p == last_alloc_pointer &&160 s <= last_alloc_size &&161 a <= last_alloc_align;162 }163 164 bool checkAllocAtLeast(void* p, std::size_t s) const {165 return p == last_alloc_pointer &&166 s <= last_alloc_size;167 }168 169 bool checkDealloc(void* p, std::size_t s, size_t a) const {170 return p == last_dealloc_pointer &&171 s == last_dealloc_size &&172 a == last_dealloc_align;173 }174 175 bool checkDealloc(void* p, std::size_t s) const {176 return p == last_dealloc_pointer &&177 s == last_dealloc_size;178 }179 180 bool checkDeallocMatchesAlloc() const {181 return last_dealloc_pointer == last_alloc_pointer &&182 last_dealloc_size == last_alloc_size &&183 last_dealloc_align == last_alloc_align;184 }185 186 template <class ...Args, class Alloc, class Tp>187 bool checkConstruct(Alloc const&, Tp *p) const {188 auto expectAlloc = &makeTypeID<Alloc>();189 auto expectTp = &makeTypeID<Tp>();190 auto expectArgs = &makeArgumentID<Args...>();191 if (last_construct_pointer != p)192 return false;193 if (last_construct_alloc != expectAlloc)194 return false;195 if (last_construct_type != expectTp)196 return false;197 if (last_construct_args != expectArgs)198 return false;199 return true;200 }201 202 template <class Alloc, class Tp>203 bool checkDestroy(Alloc const&, Tp *p) const {204 return last_destroy_pointer == p &&205 last_destroy_alloc == &makeTypeID<Alloc>() &&206 last_destroy_type == &makeTypeID<Tp>();207 }208 209 bool checkDestroyMatchesConstruct() const {210 return last_destroy_pointer == last_construct_pointer &&211 last_destroy_type == last_construct_type;212 }213 214 void countIsEqual() {215 ++is_equal_count;216 }217 218 bool checkIsEqualCalledEq(int n) const {219 return is_equal_count == n;220 }221private:222 DISALLOW_COPY(AllocController);223};224 225template <class T, std::size_t ID>226class CountingAllocator227{228public:229 typedef T value_type;230 typedef T* pointer;231 232 template <class U>233 struct rebind { using other = CountingAllocator<U, ID>; };234 235 CountingAllocator() = delete;236 explicit CountingAllocator(AllocController& PP) : P(&PP) {}237 238 CountingAllocator(CountingAllocator const& other) : P(other.P) {239 P->copy_constructed += 1;240 }241 242 CountingAllocator(CountingAllocator&& other) : P(other.P) {243 P->move_constructed += 1;244 }245 246 template <class U>247 CountingAllocator(CountingAllocator<U, ID> const& other) TEST_NOEXCEPT : P(other.P) {248 P->copy_constructed += 1;249 }250 251 template <class U>252 CountingAllocator(CountingAllocator<U, ID>&& other) TEST_NOEXCEPT : P(other.P) {253 P->move_constructed += 1;254 }255 256 T* allocate(std::size_t n)257 {258 void* ret = ::operator new(n*sizeof(T));259 P->countAlloc(ret, n*sizeof(T), alignof(T));260 return static_cast<T*>(ret);261 }262 263 void deallocate(T* p, std::size_t n)264 {265 void* vp = static_cast<void*>(p);266 P->countDealloc(vp, n*sizeof(T), alignof(T));267 ::operator delete(vp);268 }269 270 template <class U, class ...Args>271 void construct(U *p, Args&&... args) {272 ::new ((void*)p) U(std::forward<Args>(args)...);273 P->countConstruct<Args&&...>(*this, p);274 }275 276 template <class U>277 void destroy(U* p) {278 p->~U();279 P->countDestroy(*this, p);280 }281 282 AllocController& getController() const { return *P; }283 284private:285 template <class Tp, std::size_t XID> friend class CountingAllocator;286 AllocController *P;287};288 289 290template <std::size_t ID>291class CountingAllocator<void, ID>292{293public:294 typedef void* pointer;295 typedef const void* const_pointer;296 typedef void value_type;297 298 template <class U>299 struct rebind { using other = CountingAllocator<U, ID>; };300 301 CountingAllocator() = delete;302 explicit CountingAllocator(AllocController& PP) : P(&PP) {}303 304 CountingAllocator(CountingAllocator const& other) : P(other.P) {305 P->copy_constructed += 1;306 }307 308 CountingAllocator(CountingAllocator&& other) : P(other.P) {309 P->move_constructed += 1;310 }311 312 template <class U>313 CountingAllocator(CountingAllocator<U, ID> const& other) TEST_NOEXCEPT : P(other.P) {314 P->copy_constructed += 1;315 }316 317 template <class U>318 CountingAllocator(CountingAllocator<U, ID>&& other) TEST_NOEXCEPT : P(other.P) {319 P->move_constructed += 1;320 }321 322 void construct(...) = delete;323 void destroy(void*) = delete;324 325 AllocController& getController() const { return *P; }326 327private:328 template <class Tp, std::size_t> friend class CountingAllocator;329 AllocController *P;330};331 332template <class T, class U, std::size_t ID>333inline bool operator==(CountingAllocator<T, ID> const& x,334 CountingAllocator<U, ID> const& y) {335 return &x.getController() == &y.getController();336}337 338template <class T, class U, std::size_t ID>339inline bool operator!=(CountingAllocator<T, ID> const& x,340 CountingAllocator<U, ID> const& y) {341 return !(x == y);342}343 344template <class T>345class MinAlignedAllocator346{347public:348 typedef T value_type;349 typedef T* pointer;350 351 MinAlignedAllocator() = delete;352 353 explicit MinAlignedAllocator(AllocController& R) : P(&R) {}354 355 MinAlignedAllocator(MinAlignedAllocator const& other) : P(other.P) {356 P->copy_constructed += 1;357 }358 359 MinAlignedAllocator(MinAlignedAllocator&& other) : P(other.P) {360 P->move_constructed += 1;361 }362 363 template <class U>364 MinAlignedAllocator(MinAlignedAllocator<U> const& other) TEST_NOEXCEPT : P(other.P) {365 P->copy_constructed += 1;366 }367 368 template <class U>369 MinAlignedAllocator(MinAlignedAllocator<U>&& other) TEST_NOEXCEPT : P(other.P) {370 P->move_constructed += 1;371 }372 373 T* allocate(std::size_t n) {374 char* aligned_ptr = (char*)::operator new(alloc_size(n*sizeof(T)));375 assert(is_max_aligned(aligned_ptr));376 377 char* unaligned_ptr = aligned_ptr + alignof(T);378 assert(is_min_aligned(unaligned_ptr));379 380 P->countAlloc(unaligned_ptr, n * sizeof(T), alignof(T));381 382 return ((T*)unaligned_ptr);383 }384 385 void deallocate(T* p, std::size_t n) {386 assert(is_min_aligned(p));387 388 char* aligned_ptr = ((char*)p) - alignof(T);389 assert(is_max_aligned(aligned_ptr));390 391 P->countDealloc(p, n*sizeof(T), alignof(T));392 393 return ::operator delete(static_cast<void*>(aligned_ptr));394 }395 396 template <class U, class ...Args>397 void construct(U *p, Args&&... args) {398 auto *c = ::new ((void*)p) U(std::forward<Args>(args)...);399 P->countConstruct<Args&&...>(*this, p);400 }401 402 template <class U>403 void destroy(U* p) {404 p->~U();405 P->countDestroy(*this, p);406 }407 408 AllocController& getController() const { return *P; }409 410private:411 static const std::size_t BlockSize = alignof(std::max_align_t);412 413 static std::size_t alloc_size(std::size_t s) {414 std::size_t bytes = (s + BlockSize - 1) & ~(BlockSize - 1);415 bytes += BlockSize;416 assert(bytes % BlockSize == 0);417 return bytes;418 }419 420 static bool is_max_aligned(void* p) {421 return reinterpret_cast<std::uintptr_t>(p) % BlockSize == 0;422 }423 424 static bool is_min_aligned(void* p) {425 if (alignof(T) == BlockSize) {426 return is_max_aligned(p);427 } else {428 return reinterpret_cast<std::uintptr_t>(p) % BlockSize == alignof(T);429 }430 }431 432 template <class Tp> friend class MinAlignedAllocator;433 mutable AllocController *P;434};435 436 437template <class T, class U>438inline bool operator==(MinAlignedAllocator<T> const& x,439 MinAlignedAllocator<U> const& y) {440 return &x.getController() == &y.getController();441}442 443template <class T, class U>444inline bool operator!=(MinAlignedAllocator<T> const& x,445 MinAlignedAllocator<U> const& y) {446 return !(x == y);447}448 449template <class T>450class NullAllocator451{452public:453 typedef T value_type;454 typedef T* pointer;455 NullAllocator() = delete;456 explicit NullAllocator(AllocController& PP) : P(&PP) {}457 458 NullAllocator(NullAllocator const& other) : P(other.P) {459 P->copy_constructed += 1;460 }461 462 NullAllocator(NullAllocator&& other) : P(other.P) {463 P->move_constructed += 1;464 }465 466 template <class U>467 NullAllocator(NullAllocator<U> const& other) TEST_NOEXCEPT : P(other.P) {468 P->copy_constructed += 1;469 }470 471 template <class U>472 NullAllocator(NullAllocator<U>&& other) TEST_NOEXCEPT : P(other.P) {473 P->move_constructed += 1;474 }475 476 T* allocate(std::size_t n)477 {478 P->countAlloc(nullptr, n*sizeof(T), alignof(T));479 return nullptr;480 }481 482 void deallocate(T* p, std::size_t n)483 {484 void* vp = static_cast<void*>(p);485 P->countDealloc(vp, n*sizeof(T), alignof(T));486 }487 488 AllocController& getController() const { return *P; }489 490private:491 template <class Tp> friend class NullAllocator;492 AllocController *P;493};494 495template <class T, class U>496inline bool operator==(NullAllocator<T> const& x,497 NullAllocator<U> const& y) {498 return &x.getController() == &y.getController();499}500 501template <class T, class U>502inline bool operator!=(NullAllocator<T> const& x,503 NullAllocator<U> const& y) {504 return !(x == y);505}506 507 508#endif /* SUPPORT_CONTROLLED_ALLOCATORS_H */509