1//===----------------------------------------------------------------------===//
2// DuckDB
3//
4// duckdb/common/allocator.hpp
5//
6//
7//===----------------------------------------------------------------------===//
8
9#pragma once
10
11#include "duckdb/common/common.hpp"
12#include "duckdb/common/optional_ptr.hpp"
13
14namespace duckdb {
15class Allocator;
16class AttachedDatabase;
17class ClientContext;
18class DatabaseInstance;
19class ExecutionContext;
20class ThreadContext;
21
22struct AllocatorDebugInfo;
23
24struct PrivateAllocatorData {
25 PrivateAllocatorData();
26 virtual ~PrivateAllocatorData();
27
28 unique_ptr<AllocatorDebugInfo> debug_info;
29
30 template <class TARGET>
31 TARGET &Cast() {
32 D_ASSERT(dynamic_cast<TARGET *>(this));
33 return reinterpret_cast<TARGET &>(*this);
34 }
35 template <class TARGET>
36 const TARGET &Cast() const {
37 D_ASSERT(dynamic_cast<const TARGET *>(this));
38 return reinterpret_cast<const TARGET &>(*this);
39 }
40};
41
42typedef data_ptr_t (*allocate_function_ptr_t)(PrivateAllocatorData *private_data, idx_t size);
43typedef void (*free_function_ptr_t)(PrivateAllocatorData *private_data, data_ptr_t pointer, idx_t size);
44typedef data_ptr_t (*reallocate_function_ptr_t)(PrivateAllocatorData *private_data, data_ptr_t pointer, idx_t old_size,
45 idx_t size);
46
47class AllocatedData {
48public:
49 DUCKDB_API AllocatedData();
50 DUCKDB_API AllocatedData(Allocator &allocator, data_ptr_t pointer, idx_t allocated_size);
51 DUCKDB_API ~AllocatedData();
52 // disable copy constructors
53 AllocatedData(const AllocatedData &other) = delete;
54 AllocatedData &operator=(const AllocatedData &) = delete;
55 //! enable move constructors
56 DUCKDB_API AllocatedData(AllocatedData &&other) noexcept;
57 DUCKDB_API AllocatedData &operator=(AllocatedData &&) noexcept;
58
59 data_ptr_t get() {
60 return pointer;
61 }
62 const_data_ptr_t get() const {
63 return pointer;
64 }
65 idx_t GetSize() const {
66 return allocated_size;
67 }
68 bool IsSet() {
69 return pointer;
70 }
71 void Reset();
72
73private:
74 optional_ptr<Allocator> allocator;
75 data_ptr_t pointer;
76 idx_t allocated_size;
77};
78
79class Allocator {
80 // 281TB ought to be enough for anybody
81 static constexpr const idx_t MAXIMUM_ALLOC_SIZE = 281474976710656ULL;
82
83public:
84 DUCKDB_API Allocator();
85 DUCKDB_API Allocator(allocate_function_ptr_t allocate_function_p, free_function_ptr_t free_function_p,
86 reallocate_function_ptr_t reallocate_function_p,
87 unique_ptr<PrivateAllocatorData> private_data);
88 Allocator &operator=(Allocator &&allocator) noexcept = delete;
89 DUCKDB_API ~Allocator();
90
91 DUCKDB_API data_ptr_t AllocateData(idx_t size);
92 DUCKDB_API void FreeData(data_ptr_t pointer, idx_t size);
93 DUCKDB_API data_ptr_t ReallocateData(data_ptr_t pointer, idx_t old_size, idx_t new_size);
94
95 AllocatedData Allocate(idx_t size) {
96 return AllocatedData(*this, AllocateData(size), size);
97 }
98 static data_ptr_t DefaultAllocate(PrivateAllocatorData *private_data, idx_t size) {
99 return data_ptr_cast(src: malloc(size: size));
100 }
101 static void DefaultFree(PrivateAllocatorData *private_data, data_ptr_t pointer, idx_t size) {
102 free(ptr: pointer);
103 }
104 static data_ptr_t DefaultReallocate(PrivateAllocatorData *private_data, data_ptr_t pointer, idx_t old_size,
105 idx_t size) {
106 return data_ptr_cast(src: realloc(ptr: pointer, size: size));
107 }
108 static Allocator &Get(ClientContext &context);
109 static Allocator &Get(DatabaseInstance &db);
110 static Allocator &Get(AttachedDatabase &db);
111
112 PrivateAllocatorData *GetPrivateData() {
113 return private_data.get();
114 }
115
116 DUCKDB_API static Allocator &DefaultAllocator();
117 DUCKDB_API static shared_ptr<Allocator> &DefaultAllocatorReference();
118
119private:
120 allocate_function_ptr_t allocate_function;
121 free_function_ptr_t free_function;
122 reallocate_function_ptr_t reallocate_function;
123
124 unique_ptr<PrivateAllocatorData> private_data;
125};
126
127template <class T>
128T *AllocateArray(idx_t size) {
129 return (T *)Allocator::DefaultAllocator().AllocateData(size: size * sizeof(T));
130}
131
132template <class T>
133void DeleteArray(T *ptr, idx_t size) {
134 Allocator::DefaultAllocator().FreeData(pointer: data_ptr_cast(ptr), size: size * sizeof(T));
135}
136
137template <typename T, typename... ARGS>
138T *AllocateObject(ARGS &&... args) {
139 auto data = Allocator::DefaultAllocator().AllocateData(size: sizeof(T));
140 return new (data) T(std::forward<ARGS>(args)...);
141}
142
143template <typename T>
144void DestroyObject(T *ptr) {
145 ptr->~T();
146 Allocator::DefaultAllocator().FreeData(pointer: data_ptr_cast(ptr), size: sizeof(T));
147}
148
149//! The BufferAllocator is a wrapper around the global allocator class that sends any allocations made through the
150//! buffer manager. This makes the buffer manager aware of the memory usage, allowing it to potentially free
151//! other blocks to make space in memory.
152//! Note that there is a cost to doing so (several atomic operations will be performed on allocation/free).
153//! As such this class should be used primarily for larger allocations.
154struct BufferAllocator {
155 DUCKDB_API static Allocator &Get(ClientContext &context);
156 DUCKDB_API static Allocator &Get(DatabaseInstance &db);
157 DUCKDB_API static Allocator &Get(AttachedDatabase &db);
158};
159
160} // namespace duckdb
161