KTL
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 "overflow_fwd.h"
7 #include "type_allocator.h"
8 
9 #include <cstring>
10 #include <memory>
11 #include <ostream>
12 #include <type_traits>
13 
14 namespace ktl
15 {
16  template<typename Alloc, typename Stream>
17  class overflow
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 
82  overflow& operator=(overflow&&) = default;
83 
84  bool operator==(const overflow& rhs) const
85  noexcept(detail::has_nothrow_equal_v<Alloc>)
86  {
87  return m_Alloc == rhs.m_Alloc;
88  }
89 
90  bool operator!=(const overflow& rhs) const
91  noexcept(detail::has_nothrow_not_equal_v<Alloc>)
92  {
93  return m_Alloc != rhs.m_Alloc;
94  }
95 
96 #pragma region Allocation
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*>(m_Alloc.allocate(size));
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)
127  noexcept(detail::has_nothrow_deallocate_v<Alloc>)
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(
198  (detail::has_destroy_v<Alloc, T*>&& detail::has_nothrow_destroy_v<Alloc, T*>) ||
199  std::is_nothrow_destructible_v<T>)
200  {
201  m_Constructs--;
202 
203  if constexpr (detail::has_destroy_v<Alloc, T*>)
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
233  noexcept(detail::has_nothrow_owns_v<A>)
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 * allocate(size_type n) noexcept(detail::has_nothrow_allocate_v< Alloc >)
Attempts to allocate a chunk of memory defined by n.
Definition: overflow.h:104
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
Alloc & get_allocator() noexcept
Returns a reference to the underlying allocator.
Definition: overflow.h:243
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
overflow(const overflow &)=default
detail::get_size_type_t< Alloc > size_type
Definition: overflow.h:20
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 & operator=(overflow &&)=default
overflow & operator=(const overflow &)=default
bool operator!=(const overflow &rhs) const noexcept(detail::has_nothrow_not_equal_v< Alloc >)
Definition: overflow.h:90
Stream & get_stream() noexcept
Return a reference to the stream that will be used when leaks or corruption occur.
Definition: overflow.h:261
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
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_equal_v< Alloc >)
Definition: overflow.h:84
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(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:31
constexpr bool has_nothrow_max_size_v
Definition: meta.h:122
Definition: cascading.h:15