KTL
Loading...
Searching...
No Matches
overflow.h
Go to the documentation of this file.
1#pragma once
2
3#include "../utility/assert.h"
4#include "../utility/empty_base.h"
5#include "../utility/meta.h"
6#include "../utility/source_location.h"
7#include "overflow_fwd.h"
8
9#include <cstring>
10#include <memory>
11#include <ostream>
12#include <type_traits>
13
14namespace ktl
15{
16 template<typename Alloc, typename Stream>
18 {
19 private:
20 static_assert(detail::has_no_value_type_v<Alloc>, "Building on top of typed allocators is not allowed. Use allocators without a type");
21
22 public:
24
25 private:
26 static constexpr unsigned int OVERFLOW_PATTERN = 0b10100101101001011010010110100101;
27 static constexpr unsigned char OVERFLOW_TEST = 0b10100101;
28 static constexpr size_t OVERFLOW_SIZE = 64;
29
30 public:
35 explicit overflow(Stream& stream)
36 noexcept(std::is_nothrow_default_constructible_v<Alloc>) :
37 m_Stream(stream),
38 m_Alloc(),
39 m_Allocs(0),
40 m_Constructs(0) {}
41
45 template<typename... Args,
46 typename = std::enable_if_t<
47 std::is_constructible_v<Alloc, Args...>>>
48 explicit overflow(Stream& stream, Args&&... args)
49 noexcept(std::is_nothrow_constructible_v<Alloc, Args...>) :
50 m_Stream(stream),
51 m_Alloc(std::forward<Args>(args)...),
52 m_Allocs(0),
53 m_Constructs(0) {}
54
55 overflow(const overflow&) = default;
56
57 overflow(overflow&&) = default;
58
60 {
61 if (m_Allocs != 0)
62 {
63 m_Stream << "--------MEMORY LEAK DETECTED--------\nAllocator destroyed while having:\n";
64 if (m_Allocs > 0)
65 m_Stream << " Allocated memory (" << m_Allocs << " bytes)\n";
66 else
67 m_Stream << " Too many frees (" << -m_Allocs << " bytes)\n";
68 }
69
70 if (m_Constructs != 0)
71 {
72 m_Stream << "--------POSSIBLE LOGIC ERROR DETECTED--------\nAllocator destroyed while having:\n";
73 if (m_Constructs > 0)
74 m_Stream << " Too many constructor calls (" << m_Constructs << ")\n";
75 else
76 m_Stream << " Too many destructor calls (" << -m_Constructs << ")\n";
77 }
78 }
79
80 overflow& operator=(const overflow&) = default;
81
83
84 bool operator==(const overflow& rhs) const
86 {
87 return m_Alloc == rhs.m_Alloc;
88 }
89
90 bool operator!=(const overflow& rhs) const
92 {
93 return m_Alloc != rhs.m_Alloc;
94 }
95
96#pragma region Allocation
104 void* allocate(size_type n, const source_location source = KTL_SOURCE())
105 noexcept(detail::has_nothrow_allocate_v<Alloc>)
106 {
107 m_Allocs += n;
108
109 size_type size = n + OVERFLOW_SIZE * 2;
110 char* ptr = reinterpret_cast<char*>(detail::allocate(m_Alloc, size, source));
111
112 if (!ptr)
113 return nullptr;
114
115 std::memset(ptr, OVERFLOW_PATTERN, OVERFLOW_SIZE);
116 std::memset(ptr + OVERFLOW_SIZE + n, OVERFLOW_PATTERN, OVERFLOW_SIZE);
117
118 return ptr + OVERFLOW_SIZE;
119 }
120
126 void deallocate(void* p, size_type n)
128 {
129 KTL_ASSERT(p != nullptr);
130
131 m_Allocs -= n;
132
133 if (p)
134 {
135 unsigned char* ptr = reinterpret_cast<unsigned char*>(p);
136
137 size_t before = 0;
138 size_t after = 0;
139
140 // Check against corruption
141 for (unsigned char* i = ptr - 1; i >= ptr - OVERFLOW_SIZE; i--)
142 {
143 if (*i != OVERFLOW_TEST)
144 before = ptr - i;
145 }
146
147 for (unsigned char* i = ptr + n; i < ptr + n + OVERFLOW_SIZE; i++)
148 {
149 if (*i != OVERFLOW_TEST)
150 after = i - ptr - n + 1;
151 }
152
153 if (before || after)
154 {
155 m_Stream << "--------MEMORY CORRUPTION DETECTED--------\nThe area around " << p << " (" << n << " bytes) has been illegally modified\n";
156
157 if (before)
158 m_Stream << " Before (" << before << " bytes)\n";
159
160 if (after)
161 m_Stream << " After (" << after << " bytes)\n";
162 }
163
164 size_type size = n + OVERFLOW_SIZE * 2;
165 m_Alloc.deallocate(ptr - OVERFLOW_SIZE, size);
166 }
167 }
168#pragma endregion
169
170#pragma region Construction
178 template<typename T, typename... Args>
179 void construct(T* p, Args&&... args) noexcept(
180 (detail::has_construct_v<Alloc, T*, Args...> && detail::has_nothrow_construct_v<Alloc, T*, Args...>) ||
181 std::is_nothrow_constructible_v<T, Args...>)
182 {
183 m_Constructs++;
184
185 if constexpr (detail::has_construct_v<Alloc, T*, Args...>)
186 m_Alloc.construct(p, std::forward<Args>(args)...);
187 else
188 ::new(p) T(std::forward<Args>(args)...);
189 }
190
196 template<typename T>
197 void destroy(T* p) noexcept(
199 std::is_nothrow_destructible_v<T>)
200 {
201 m_Constructs--;
202
204 m_Alloc.destroy(p);
205 else
206 p->~T();
207 }
208#pragma endregion
209
210#pragma region Utility
216 template<typename A = Alloc>
217 typename std::enable_if<detail::has_max_size_v<A>, size_type>::type
218 max_size() const
219 noexcept(detail::has_nothrow_max_size_v<A>)
220 {
221 return m_Alloc.max_size();
222 }
223
230 template<typename A = Alloc>
231 typename std::enable_if<detail::has_owns_v<A>, bool>::type
232 owns(void* p) const
234 {
235 return m_Alloc.owns(p);
236 }
237#pragma endregion
238
243 Alloc& get_allocator() noexcept
244 {
245 return m_Alloc;
246 }
247
252 const Alloc& get_allocator() const noexcept
253 {
254 return m_Alloc;
255 }
256
261 Stream& get_stream() noexcept
262 {
263 return m_Stream;
264 }
265
270 const Stream& get_stream() const noexcept
271 {
272 return m_Stream;
273 }
274
275 private:
276 Stream& m_Stream;
277 KTL_EMPTY_BASE Alloc m_Alloc;
278 int64_t m_Allocs;
279 int64_t m_Constructs;
280 };
281}
#define KTL_ASSERT(x)
Definition assert.h:17
Definition overflow.h:18
const Alloc & get_allocator() const noexcept
Returns a const reference to the underlying allocator.
Definition overflow.h:252
void construct(T *p, Args &&... args) noexcept((detail::has_construct_v< Alloc, T *, Args... > &&detail::has_nothrow_construct_v< Alloc, T *, Args... >)||std::is_nothrow_constructible_v< T, Args... >)
Constructs an object of T with the given ...args at the given location.
Definition overflow.h:179
~overflow()
Definition overflow.h:59
void destroy(T *p) noexcept((detail::has_destroy_v< Alloc, T * > &&detail::has_nothrow_destroy_v< Alloc, T * >)||std::is_nothrow_destructible_v< T >)
Destructs an object of T at the given location.
Definition overflow.h:197
overflow & operator=(overflow &&)=default
std::enable_if< detail::has_owns_v< A >, bool >::type owns(void *p) const noexcept(detail::has_nothrow_owns_v< A >)
Returns whether or not the allocator owns the given location in memory.
Definition overflow.h:232
Alloc & get_allocator() noexcept
Returns a reference to the underlying allocator.
Definition overflow.h:243
std::enable_if< detail::has_max_size_v< A >, size_type >::type max_size() const noexcept(detail::has_nothrow_max_size_v< A >)
Returns the maximum size that an allocation can be.
Definition overflow.h:218
overflow(const overflow &)=default
detail::get_size_type_t< Alloc > size_type
Definition overflow.h:23
const Stream & get_stream() const noexcept
Return a const reference to the stream that will be used when leaks or corruption occur.
Definition overflow.h:270
bool operator!=(const overflow &rhs) const noexcept(detail::has_nothrow_not_equal_v< Alloc >)
Definition overflow.h:90
overflow(Stream &stream, Args &&... args) noexcept(std::is_nothrow_constructible_v< Alloc, Args... >)
Constructor for forwarding any arguments to the underlying allocator.
Definition overflow.h:48
bool operator==(const overflow &rhs) const noexcept(detail::has_nothrow_equal_v< Alloc >)
Definition overflow.h:84
void * allocate(size_type n, const source_location source=KTL_SOURCE()) noexcept(detail::has_nothrow_allocate_v< Alloc >)
Attempts to allocate a chunk of memory defined by n.
Definition overflow.h:104
Stream & get_stream() noexcept
Return a reference to the stream that will be used when leaks or corruption occur.
Definition overflow.h:261
void deallocate(void *p, size_type n) noexcept(detail::has_nothrow_deallocate_v< Alloc >)
Attempts to deallocate the memory at location p.
Definition overflow.h:126
overflow & operator=(const overflow &)=default
overflow(overflow &&)=default
overflow(Stream &stream) noexcept(std::is_nothrow_default_constructible_v< Alloc >)
Construct the allocator with a reference to a stream object.
Definition overflow.h:35
#define KTL_EMPTY_BASE
Definition empty_base.h:6
typename get_size_type< Alloc, void >::type get_size_type_t
Definition meta.h:33
constexpr bool has_nothrow_construct_v
Definition meta.h:123
constexpr bool has_construct_v
Definition meta.h:53
void * allocate(Alloc &alloc, size_t n, const source_location source) noexcept(false)
Definition meta.h:161
constexpr bool has_no_value_type_v
Definition meta.h:17
Definition cascading.h:16
#define KTL_SOURCE()
Definition source_location.h:11
Definition source_location.h:19