Skip to content

Commit 5dc6eef

Browse files
authored
Merge pull request #10 from KredeGC/debug-allocator
Debug allocator
2 parents 821adb3 + c1e8307 commit 5dc6eef

25 files changed

+975
-66
lines changed

generate.bat

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
call vendor\bin\premake\premake5.exe vs2019
1+
call vendor\bin\premake\premake5.exe vs2022
22
pause

include/ktl/allocators/cascading.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include "../utility/assert.h"
66
#include "../utility/empty_base.h"
77
#include "../utility/meta.h"
8+
#include "../utility/source_location.h"
89
#include "cascading_fwd.h"
910
#include "type_allocator.h"
1011

@@ -90,7 +91,7 @@ namespace ktl
9091
* @param n The amount of bytes to allocate memory for
9192
* @return A location in memory that is at least @p n bytes big or nullptr if it could not be allocated
9293
*/
93-
void* allocate(size_type n) noexcept(
94+
void* allocate(size_type n, const source_location source = KTL_SOURCE()) noexcept(
9495
std::is_nothrow_default_constructible_v<node> &&
9596
detail::has_nothrow_allocate_v<Alloc> &&
9697
(!detail::has_max_size_v<Alloc> || detail::has_nothrow_max_size_v<Alloc>))
@@ -105,7 +106,7 @@ namespace ktl
105106
return nullptr;
106107
}
107108

108-
void* p = m_Node->Allocator.allocate(n);
109+
void* p = detail::allocate(m_Node->Allocator, n, source);
109110

110111
// If the allocator was unable to allocate it, create a new one
111112
if (p == nullptr)
@@ -115,7 +116,7 @@ namespace ktl
115116
m_Node = detail::aligned_new<node>(detail::ALIGNMENT);
116117
m_Node->Next = next;
117118

118-
p = m_Node->Allocator.allocate(n);
119+
p = detail::allocate(m_Node->Allocator, n, source);
119120
}
120121

121122
if (p)

include/ktl/allocators/debug.h

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
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 "debug_fwd.h"
8+
#include "type_allocator.h"
9+
10+
#include <cstring>
11+
#include <memory>
12+
#include <ostream>
13+
#include <type_traits>
14+
15+
namespace ktl
16+
{
17+
template<typename Alloc, typename Container>
18+
class debug
19+
{
20+
private:
21+
static_assert(detail::has_no_value_type_v<Alloc>, "Building on top of typed allocators is not allowed. Use allocators without a type");
22+
23+
public:
24+
typedef typename detail::get_size_type_t<Alloc> size_type;
25+
26+
public:
27+
/**
28+
* @brief Construct the allocator with a reference to a container object
29+
* @param container The container to use for populating statistics
30+
*/
31+
explicit debug(Container& container)
32+
noexcept(std::is_nothrow_default_constructible_v<Alloc>) :
33+
m_Container(container),
34+
m_Alloc() {}
35+
36+
/**
37+
* @brief Constructor for forwarding any arguments to the underlying allocator
38+
*/
39+
template<typename... Args,
40+
typename = std::enable_if_t<
41+
std::is_constructible_v<Alloc, Args...>>>
42+
explicit debug(Container& container, Args&&... args)
43+
noexcept(std::is_nothrow_constructible_v<Alloc, Args...>) :
44+
m_Container(container),
45+
m_Alloc(std::forward<Args>(args)...) {}
46+
47+
debug(const debug&) = default;
48+
49+
debug(debug&&) = default;
50+
51+
debug& operator=(const debug&) = default;
52+
53+
debug& operator=(debug&&) = default;
54+
55+
bool operator==(const debug& rhs) const
56+
noexcept(detail::has_nothrow_equal_v<Alloc>)
57+
{
58+
return m_Alloc == rhs.m_Alloc;
59+
}
60+
61+
bool operator!=(const debug& rhs) const
62+
noexcept(detail::has_nothrow_not_equal_v<Alloc>)
63+
{
64+
return m_Alloc != rhs.m_Alloc;
65+
}
66+
67+
#pragma region Allocation
68+
/**
69+
* @brief Attempts to allocate a chunk of memory defined by @p n
70+
* @param n The amount of bytes to allocate memory for
71+
* @return A location in memory that is at least @p n bytes big or nullptr if it could not be allocated
72+
*/
73+
void* allocate(size_type n, const source_location& source = KTL_SOURCE())
74+
noexcept(detail::has_nothrow_allocate_v<Alloc>)
75+
{
76+
#ifdef KTL_SOURCE_LOCATION
77+
m_Container.push_back({ source.file_name(), source.line(), n });
78+
#endif
79+
80+
return detail::allocate(m_Alloc, n, source);
81+
}
82+
83+
/**
84+
* @brief Attempts to deallocate the memory at location @p p
85+
* @param p The location in memory to deallocate
86+
* @param n The size that was initially allocated
87+
*/
88+
void deallocate(void* p, size_type n)
89+
noexcept(detail::has_nothrow_deallocate_v<Alloc>)
90+
{
91+
m_Alloc.deallocate(p, n);
92+
}
93+
#pragma endregion
94+
95+
#pragma region Construction
96+
/**
97+
* @brief Constructs an object of T with the given @p ...args at the given location
98+
* @note Only defined if the underlying allocator defines it
99+
* @tparam ...Args The types of the arguments
100+
* @param p The location of the object in memory
101+
* @param ...args A range of arguments to use to construct the object
102+
*/
103+
template<typename T, typename... Args>
104+
typename std::enable_if<detail::has_construct_v<Alloc, T*, Args...>, void>::type
105+
construct(T* p, Args&&... args)
106+
noexcept(detail::has_nothrow_construct_v<Alloc, T*, Args...>)
107+
{
108+
m_Alloc.construct(p, std::forward<Args>(args)...);
109+
}
110+
111+
/**
112+
* @brief Destructs an object of T at the given location
113+
* @note Only defined if the underlying allocator defines it
114+
* @param p The location of the object in memory
115+
*/
116+
template<typename T>
117+
typename std::enable_if<detail::has_destroy_v<Alloc, T*>, void>::type
118+
destroy(T* p)
119+
noexcept(detail::has_nothrow_destroy_v<Alloc, T*>)
120+
{
121+
m_Alloc.destroy(p);
122+
}
123+
#pragma endregion
124+
125+
#pragma region Utility
126+
/**
127+
* @brief Returns the maximum size that an allocation can be
128+
* @note Only defined if the underlying allocator defines it
129+
* @return The maximum size an allocation may be
130+
*/
131+
template<typename A = Alloc>
132+
typename std::enable_if<detail::has_max_size_v<A>, size_type>::type
133+
max_size() const
134+
noexcept(detail::has_nothrow_max_size_v<A>)
135+
{
136+
return m_Alloc.max_size();
137+
}
138+
139+
/**
140+
* @brief Returns whether or not the allocator owns the given location in memory
141+
* @note Only defined if the underlying allocator defines it
142+
* @param p The location of the object in memory
143+
* @return Whether the allocator owns @p p
144+
*/
145+
template<typename A = Alloc>
146+
typename std::enable_if<detail::has_owns_v<A>, bool>::type
147+
owns(void* p) const
148+
noexcept(detail::has_nothrow_owns_v<A>)
149+
{
150+
return m_Alloc.owns(p);
151+
}
152+
#pragma endregion
153+
154+
/**
155+
* @brief Returns a reference to the underlying allocator
156+
* @return The allocator
157+
*/
158+
Alloc& get_allocator() noexcept
159+
{
160+
return m_Alloc;
161+
}
162+
163+
/**
164+
* @brief Returns a const reference to the underlying allocator
165+
* @return The allocator
166+
*/
167+
const Alloc& get_allocator() const noexcept
168+
{
169+
return m_Alloc;
170+
}
171+
172+
/**
173+
* @brief Return a reference to the container that will be used to accumulate statistics
174+
* @return The stream
175+
*/
176+
Container& get_container() noexcept
177+
{
178+
return m_Container;
179+
}
180+
181+
/**
182+
* @brief Return a const reference to the container that will be used to accumulate statistics
183+
* @return The stream
184+
*/
185+
const Container& get_container() const noexcept
186+
{
187+
return m_Container;
188+
}
189+
190+
private:
191+
Container& m_Container;
192+
KTL_EMPTY_BASE Alloc m_Alloc;
193+
};
194+
}

include/ktl/allocators/debug_fwd.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#pragma once
2+
3+
#include "shared_fwd.h"
4+
#include "threaded_fwd.h"
5+
#include "type_allocator_fwd.h"
6+
7+
#include <cstddef>
8+
#include <ostream>
9+
10+
namespace ktl
11+
{
12+
// debug
13+
template<typename Alloc, typename Container>
14+
class debug;
15+
16+
/**
17+
* @brief Shorthand for a typed overflow allocator
18+
*/
19+
template<typename T, typename Alloc, typename Container>
20+
using type_debug_allocator = type_allocator<T, debug<Alloc, Container>>;
21+
22+
/**
23+
* @brief Shorthand for a typed, ref-counted overflow allocator
24+
*/
25+
template<typename T, typename Alloc, typename Container>
26+
using type_shared_debug_allocator = type_allocator<T, shared<debug<Alloc, Container>>>;
27+
}

include/ktl/allocators/fallback.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#include "../utility/empty_base.h"
44
#include "../utility/meta.h"
5+
#include "../utility/source_location.h"
56
#include "fallback_fwd.h"
67
#include "type_allocator.h"
78

@@ -97,12 +98,12 @@ namespace ktl
9798
}
9899

99100
#pragma region Allocation
100-
void* allocate(size_t n)
101+
void* allocate(size_t n, const source_location source = KTL_SOURCE())
101102
noexcept(detail::has_nothrow_allocate_v<P> && detail::has_nothrow_allocate_v<F>)
102103
{
103-
void* ptr = m_Primary.allocate(n);
104+
void* ptr = detail::allocate(m_Primary, n, source);
104105
if (!ptr)
105-
return m_Fallback.allocate(n);
106+
return detail::allocate(m_Fallback, n, source);
106107
return ptr;
107108
}
108109

include/ktl/allocators/freelist.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include "../utility/assert.h"
44
#include "../utility/empty_base.h"
55
#include "../utility/meta.h"
6+
#include "../utility/source_location.h"
67
#include "freelist_fwd.h"
78
#include "type_allocator.h"
89

@@ -110,7 +111,7 @@ namespace ktl
110111
* @param n The amount of bytes to allocate memory for
111112
* @return A location in memory that is at least @p n bytes big or nullptr if it could not be allocated
112113
*/
113-
void* allocate(size_type n)
114+
void* allocate(size_type n, const source_location source = KTL_SOURCE())
114115
noexcept(detail::has_nothrow_allocate_v<Alloc>)
115116
{
116117
if (n > Min && n <= Max)
@@ -122,7 +123,7 @@ namespace ktl
122123
return next;
123124
}
124125

125-
return m_Alloc.allocate(Max);
126+
return detail::allocate(m_Alloc, Max, source);
126127
}
127128

128129
return nullptr;
@@ -134,7 +135,8 @@ namespace ktl
134135
* @param p The location in memory to deallocate
135136
* @param n The size that was initially allocated
136137
*/
137-
void deallocate(void* p, size_type n) noexcept
138+
void deallocate(void* p, size_type n)
139+
noexcept(detail::has_nothrow_deallocate_v<Alloc>)
138140
{
139141
KTL_ASSERT(p != nullptr);
140142

include/ktl/allocators/global.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include "../utility/alignment.h"
55
#include "../utility/empty_base.h"
66
#include "../utility/meta.h"
7+
#include "../utility/source_location.h"
78
#include "global_fwd.h"
89

910
#include <memory>
@@ -39,21 +40,21 @@ namespace ktl
3940
return *this;
4041
}
4142

42-
bool operator==(const global& rhs) const
43+
bool operator==(const global& rhs) const noexcept
4344
{
4445
return true;
4546
}
4647

47-
bool operator!=(const global& rhs) const
48+
bool operator!=(const global& rhs) const noexcept
4849
{
4950
return true;
5051
}
5152

5253
#pragma region Allocation
53-
void* allocate(size_t n)
54+
void* allocate(size_t n, const source_location source = KTL_SOURCE())
5455
noexcept(detail::has_nothrow_allocate_v<Alloc>)
5556
{
56-
return s_Alloc.allocate(n);
57+
return detail::allocate(s_Alloc, n, source);
5758
}
5859

5960
void deallocate(void* p, size_t n)

include/ktl/allocators/overflow.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
#include "../utility/assert.h"
44
#include "../utility/empty_base.h"
55
#include "../utility/meta.h"
6+
#include "../utility/source_location.h"
67
#include "overflow_fwd.h"
7-
#include "type_allocator.h"
88

99
#include <cstring>
1010
#include <memory>
@@ -101,13 +101,13 @@ namespace ktl
101101
* @param n The amount of bytes to allocate memory for
102102
* @return A location in memory that is at least @p n bytes big or nullptr if it could not be allocated
103103
*/
104-
void* allocate(size_type n)
104+
void* allocate(size_type n, const source_location source = KTL_SOURCE())
105105
noexcept(detail::has_nothrow_allocate_v<Alloc>)
106106
{
107107
m_Allocs += n;
108108

109109
size_type size = n + OVERFLOW_SIZE * 2;
110-
char* ptr = reinterpret_cast<char*>(m_Alloc.allocate(size));
110+
char* ptr = reinterpret_cast<char*>(detail::allocate(m_Alloc, size, source));
111111

112112
if (!ptr)
113113
return nullptr;

0 commit comments

Comments
 (0)