plugify 1.2.6
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 <memory> // for std::allocator and std::allocator_traits
6#include <type_traits> // for std::is_constant_evaluated
7
8#include "macro.hpp"
9
10namespace plg {
11 // Forward declaration for allocator<void>
12 template<typename T>
13 class allocator;
14
15 // Specialization for `void`, but we no longer need to define `pointer` and `const_pointer`
16 template<>
17 class allocator<void> {
18 public:
19 using value_type = void;
20
21 // Rebind struct
22 template<class U>
23 struct rebind { using other = allocator<U>; };
24 };
25
26 // Define the custom allocator inheriting from std::allocator
27 template<typename T>
28 class allocator {
29 public:
30 using value_type = T;
31 using pointer = T*;
32 using const_pointer = const T*;
33 using reference = T&;
34 using const_reference = const T&;
35 using size_type = std::size_t;
36 using difference_type = std::ptrdiff_t;
37
38 // Default constructor
39 constexpr allocator() noexcept = default;
40
41 // Copy constructor
42 template<class U>
43 constexpr allocator(const allocator<U>&) noexcept {}
44
45 // Rebind struct
46 template<class U>
47 struct rebind { using other = allocator<U>; };
48
49 // Override allocate method to use custom allocation function
50 constexpr pointer allocate(size_type n, [[maybe_unused]] std::allocator_traits<allocator<void>>::const_pointer hint = nullptr) {
51 static_assert(sizeof(T) != 0, "cannot allocate incomplete types");
52 static_assert((alignof(T) & (alignof(T) - 1)) == 0, "alignof(T) must be a power of 2");
53
54 if (n > max_size()) [[unlikely]] {
55 if (n > static_cast<size_type>(-1) / sizeof(T)) {
56 PLUGIFY_ASSERT(false, "plg::allocator::allocate(): bad array new length", std::bad_array_new_length);
57 }
58 PLUGIFY_ASSERT(false, "plg::allocator::allocate(): too big", std::bad_alloc);
59 }
60
61 pointer ret = nullptr;
62 if (std::is_constant_evaluated()) {
63 ret = new T[n];
64 } else {
65 size_type size = n * sizeof(T);
66 if constexpr (alignof(T) > alignof(std::max_align_t)) {
67 size_type aligned_size = (size + (alignof(T) - 1)) & ~(alignof(T) - 1);
68 ret = static_cast<T*>(aligned_allocate(alignof(T), aligned_size));
69 } else {
70 ret = static_cast<T*>(std::malloc(size));
71 }
72
73 if (!ret) {
74 PLUGIFY_ASSERT(false, "plg::allocator::allocate(): bad allocation", std::bad_alloc);
75 }
76 }
77
78 return ret;
79 }
80
81 // Override deallocate method to use custom deallocation function
82 constexpr void deallocate(pointer p, [[maybe_unused]] size_type n) {
83 if (std::is_constant_evaluated()) {
84 delete[] p;
85 } else {
86 std::free(static_cast<void*>(p));
87 }
88 }
89
90 private:
91 constexpr size_type max_size() noexcept {
92#if __PTRDIFF_MAX__ < __SIZE_MAX__
93 return static_cast<size_type>(__PTRDIFF_MAX__) / sizeof(T);
94#else
95 return static_cast<size_type>(-1) / sizeof(T);
96#endif // __PTRDIFF_MAX__
97 }
98
99 PLUGIFY_FORCE_INLINE void* aligned_allocate(size_type alignment, size_type size) {
100#if PLUGIFY_COMPILER_MSVC
101 return _aligned_malloc(size, alignment);
102#else
103 return std::aligned_alloc(alignment, size);
104#endif// PLUGIFY_COMPILER_MSVC
105 }
106 };
107
108 // Comparison operators for compatibility
109 template<typename T, typename U>
110 constexpr bool operator==(const allocator<T>&, const allocator<U>) { return true; }
111
112 template<typename T, typename U>
113 constexpr bool operator!=(const allocator<T>&, const allocator<U>) { return false; }
114
115} // namespace plg