plugify 1.2.8
Loading...
Searching...
No Matches
allocator.hpp
1#pragma once
2
3#include <cstddef> // for std::size_t, std::ptrdiff_t
4#include <cstdlib> // for std::malloc, std::free, std::aligned_alloc
5#include <new> // for ::operator new, ::operator delete
6#include <type_traits> // for std::is_constant_evaluated
7
8#include "plg/config.hpp"
9
10namespace plg {
11 template<typename T>
12 class allocator;
13
14 template<>
15 class allocator<void> {
16 public:
17 using value_type = void;
18
19 template<class U>
20 struct rebind { using other = allocator<U>; };
21 };
22
23 template<typename T>
24 class allocator {
25 static_assert(!std::is_const_v<T>, "plg::allocator does not support const types");
26 static_assert(!std::is_volatile_v<T>, "plg::allocator does not support volatile types");
27 public:
28 using value_type = T;
29 using size_type = std::size_t;
30 using difference_type = std::ptrdiff_t;
31
32 constexpr allocator() noexcept = default;
33
34 template<class U>
35 constexpr allocator(const allocator<U>&) noexcept {}
36
37 template<class U>
38 struct rebind { using other = allocator<U>; };
39
40 [[nodiscard]] constexpr T* allocate(size_type n) {
41 static_assert(sizeof(T) != 0, "cannot allocate incomplete types");
42 static_assert((alignof(T) & (alignof(T) - 1)) == 0, "alignof(T) must be a power of 2");
43
44 if (n > std::allocator_traits<allocator>::max_size(*this)) {
45 throw_bad_array_new_length();
46 }
47
48 size_type size = n * sizeof(T);
49 if (std::is_constant_evaluated()) {
50 return static_cast<T*>(::operator new(size));
51 } else {
52 return malloc_allocate(size);
53 }
54 }
55
56 constexpr void deallocate(T* p, [[maybe_unused]] size_type n) {
57 if (std::is_constant_evaluated()) {
58 ::operator delete(p);
59 } else {
60 std::free(p);
61 }
62 }
63
64 private:
65 static T* malloc_allocate(size_type size) {
66 T* ret;
67 if constexpr (alignof(T) > alignof(std::max_align_t)) {
68 size_type aligned_size = (size + (alignof(T) - 1)) & ~(alignof(T) - 1);
69 ret = static_cast<T*>(aligned_allocate(alignof(T), aligned_size));
70 } else {
71 ret = static_cast<T*>(std::malloc(size));
72 }
73 if (!ret) {
74 throw_bad_alloc();
75 }
76 return ret;
77 }
78
79 [[noreturn]] static void throw_bad_array_new_length() {
80 PLUGIFY_THROW("bad array new length", std::bad_array_new_length);
81 }
82
83 [[noreturn]] static void throw_bad_alloc() {
84 PLUGIFY_THROW("bad allocation", std::bad_alloc);
85 }
86
87 static void* aligned_allocate(size_type alignment, size_type size) {
88#if PLUGIFY_PLATFORM_WINDOWS
89 return _aligned_malloc(size, alignment);
90#else
91 return std::aligned_alloc(alignment, size);
92#endif // PLUGIFY_PLATFORM_WINDOWS
93 }
94 };
95
96 template<typename T, typename U>
97 constexpr bool operator==(const allocator<T>&, const allocator<U>) { return true; }
98
99 template<typename T, typename U>
100 constexpr bool operator!=(const allocator<T>&, const allocator<U>) { return false; }
101
102 template <typename Alloc>
103 void swap_allocator(Alloc& a1, Alloc& a2, std::true_type) {
104 using std::swap;
105 swap(a1, a2);
106 }
107
108 template <typename Alloc>
109 void swap_allocator(Alloc&, Alloc&, std::false_type) noexcept {}
110
111 template <typename Alloc>
112 void swap_allocator(Alloc& a1, Alloc& a2) {
113 swap_allocator(a1, a2, std::integral_constant<bool, std::allocator_traits<Alloc>::propagate_on_container_swap::value>());
114 }
115
116 template <class Pointer, class Size = std::size_t>
118 Pointer ptr;
119 Size count;
120 };
121
122 template <class Alloc>
124 allocate_at_least(Alloc& alloc, size_t n) {
125 return { alloc.allocate(n), n };
126 }
127
128 template <class T, class U>
129 constexpr bool is_pointer_in_range(const T* begin, const T* end, const U* ptr) {
130 if (std::is_constant_evaluated())
131 return false;
132 return reinterpret_cast<const char*>(begin) <= reinterpret_cast<const char*>(ptr) &&
133 reinterpret_cast<const char*>(ptr) < reinterpret_cast<const char*>(end);
134 }
135
136 template <class T, class U>
137 constexpr bool is_overlapping_range(const T* begin, const T* end, const U* begin2) {
138 auto size = end - begin;
139 auto end2 = begin2 + size;
140 return is_pointer_in_range(begin, end, begin2) || is_pointer_in_range(begin2, end2, begin);
141 }
142
143 // asan_annotate_container_with_allocator determines whether containers with custom allocators are annotated. This is
144 // a public customization point to disable annotations if the custom allocator assumes that the memory isn't poisoned.
145 // See the https://libcxx.llvm.org/UsingLibcxx.html#turning-off-asan-annotation-in-containers for more information.
146#if PLUGIFY_INSTRUMENTED_WITH_ASAN
147 template <class Alloc>
148 struct asan_annotate_container_with_allocator : std::true_type {};
149#endif // PLUGIFY_INSTRUMENTED_WITH_ASAN
150
151 // Annotate a contiguous range.
152 // [__first_storage, __last_storage) is the allocated memory region,
153 // __old_last_contained is the previously last allowed (unpoisoned) element, and
154 // __new_last_contained is the new last allowed (unpoisoned) element.
155 template <class Allocator>
156 constexpr void annotate_contiguous_container(
157 [[maybe_unused]] const void* first_storage,
158 [[maybe_unused]] const void* last_storage,
159 [[maybe_unused]] const void* old_last_contained,
160 [[maybe_unused]] const void* new_last_contained
161 ) {
162#if PLUGIFY_INSTRUMENTED_WITH_ASAN
163 if (!std::is_constant_evaluated()
164 && asan_annotate_container_with_allocator<Allocator>::value
165 && first_storage != nullptr) {
166 __sanitizer_annotate_contiguous_container(
167 first_storage,
168 last_storage,
169 old_last_contained,
170 new_last_contained
171 );
172 }
173#endif // PLUGIFY_INSTRUMENTED_WITH_ASAN
174 }
175} // namespace plg