1
+ //
2
+ // Copyright (c) 2020-present Caps Collective & contributors
3
+ // Originally authored by Jonathan Moallem (@jonjondev) & Aryeh Zinn (@Raelr)
4
+ //
5
+ // This code is released under an unmodified zlib license.
6
+ // For conditions of distribution and use, please see:
7
+ // https://opensource.org/licenses/Zlib
8
+ //
9
+
10
+ #include " TlsfAllocator.h"
11
+ #include " Logging.h"
12
+
13
+ #include < memory>
14
+
15
+ #define TO_BYTES (val ) reinterpret_cast <uint8_t *>(val)
16
+ #define TO_HEADER (val ) reinterpret_cast <BlockHeader*>(val)
17
+ #define TO_FOOTER (val ) reinterpret_cast <BlockFooter*>(val)
18
+ #define TO_FREE_BLOCK (val ) reinterpret_cast <FreeBlockNode*>(val)
19
+
20
+ #define HEADER_SIZE sizeof (BlockHeader)
21
+ #define FOOTER_SIZE sizeof (BlockFooter)
22
+ #define FREE_BLOCK_SIZE sizeof (FreeBlockNode)
23
+ #define PAD_SIZE (size ) size + HEADER_SIZE + FOOTER_SIZE
24
+
25
+ #define FL_MIN_INDEX 4
26
+ #define MAX_SL_BUCKETS 16
27
+ #define FL (size ) 63 - __builtin_clzll(size)
28
+ #define SL (size, fl ) (size >> fl) & ((1 << FL_MIN_INDEX) - 1 );
29
+ #define FLAG_OFFSET 3
30
+ #define MIN_ALLOCATION 16
31
+ #define INVALID_INDEX UINT64_MAX
32
+
33
+ namespace Siege
34
+ {
35
+
36
+ TlsfAllocator::TlsfAllocator () {}
37
+
38
+ TlsfAllocator::TlsfAllocator (const uint64_t size)
39
+ : capacity {size}, bytesRemaining {size}
40
+ {
41
+ if (size == 0 || size < MIN_ALLOCATION || size >= UINT64_MAX)
42
+ {
43
+ capacity = 0 ;
44
+ bytesRemaining = 0 ;
45
+ return ;
46
+ }
47
+
48
+ uint64_t maxBuckets = (FL (size) - FL_MIN_INDEX) + 1 ;
49
+
50
+ uint64_t totalSize = PAD_SIZE (size);
51
+ uint64_t slBucketSize = sizeof (uint16_t ) * maxBuckets;
52
+ uint64_t freeListSize = maxBuckets * MAX_SL_BUCKETS * sizeof (FreeBlockNode*);
53
+
54
+ uint64_t allocSize = totalSize + slBucketSize + freeListSize;
55
+
56
+ data = TO_BYTES (calloc (1 , allocSize));
57
+
58
+ CC_ASSERT (data, " Allocation returned null. This should not happen and implies an implementation failure." )
59
+
60
+ freeList = (FreeBlockNode**)(data + totalSize);
61
+ slBitmasks = (uint16_t *)(data + totalSize + freeListSize);
62
+
63
+ BlockHeader* firstHeader = CreateHeader (data, totalSize, FREE);
64
+
65
+ AddNewBlock (totalSize, firstHeader);
66
+ }
67
+
68
+ TlsfAllocator::BlockHeader* TlsfAllocator::CreateHeader (uint8_t * ptr, const uint64_t size,
69
+ HeaderFlags flags)
70
+ {
71
+ if (!ptr) return nullptr ;
72
+ BlockHeader* header = TO_HEADER (ptr);
73
+ header->sizeAndFlags = (size << FLAG_OFFSET) | flags;
74
+ return header;
75
+ }
76
+
77
+ void * TlsfAllocator::Allocate (const uint64_t & size)
78
+ {
79
+ size_t requiredSize = sizeof (BlockHeader) + size + sizeof (BlockFooter);
80
+ if (!data || capacity == 0 ||size == 0 || requiredSize < size || requiredSize > bytesRemaining
81
+ || requiredSize < MIN_ALLOCATION) return nullptr ;
82
+
83
+ FreeBlockNode* block = FindFreeBlock (requiredSize);
84
+
85
+ if (!block) return nullptr ;
86
+
87
+ BlockHeader* header = TrySplitBlock (block, requiredSize);
88
+ header = CreateHeader (TO_BYTES (header), requiredSize, FULL);
89
+ BlockFooter* footer = CreateFooter (TO_BYTES (GetFooter (header)) , requiredSize);
90
+
91
+ uint8_t * ptr = GetBlockData (header);
92
+ bytesRemaining -= requiredSize;
93
+ return ptr;
94
+ }
95
+
96
+ TlsfAllocator::BlockHeader* TlsfAllocator::TrySplitBlock (TlsfAllocator::FreeBlockNode* node, uint64_t allocatedSize)
97
+ {
98
+ if (!node) return nullptr ;
99
+
100
+ uint64_t oldSize = GetHeaderSize (GetHeader (node));
101
+
102
+ BlockHeader* header = GetHeader (node);
103
+
104
+ if (!RemoveFreeBlock (node)) return nullptr ;
105
+
106
+ if (oldSize <= allocatedSize || ((oldSize - allocatedSize) <
107
+ (HEADER_SIZE + FREE_BLOCK_SIZE + FOOTER_SIZE))) return header;
108
+
109
+ AddNewBlock (oldSize - allocatedSize, TO_HEADER (TO_BYTES (header) + allocatedSize));
110
+
111
+ return header;
112
+ }
113
+
114
+ void TlsfAllocator::AddNewBlock (const uint64_t size, BlockHeader* header)
115
+ {
116
+ uint64_t fl = 0 ,sl = 0 , index;
117
+ index = CalculateFreeBlockIndices (size, fl, sl);
118
+
119
+ BlockHeader* newHeader = CreateHeader (TO_BYTES (header), size, FREE);
120
+ FreeBlockNode* node = CreateFreeBlock (TO_BYTES (GetFreeBlock (newHeader)), nullptr , freeList[index]);
121
+ BlockFooter* footer = CreateFooter (TO_BYTES (GetFooter (newHeader)), size);
122
+
123
+ if (node->next ) node->next ->prev = node;
124
+
125
+ freeList[index] = node;
126
+ flBitmask |= (1ULL << fl);
127
+ slBitmasks[fl] |= (1 << sl);
128
+ }
129
+
130
+ bool TlsfAllocator::RemoveFreeBlock (TlsfAllocator::FreeBlockNode* node)
131
+ {
132
+ BlockHeader* header = GetHeader (node);
133
+
134
+ if (!header) return false ;
135
+
136
+ uint64_t oldSize = GetHeaderSize (header);
137
+
138
+ uint64_t fl, sl, index;
139
+
140
+ index = CalculateFreeBlockIndices (oldSize, fl, sl);
141
+
142
+ if (!node->prev ) freeList[index] = node->next ;
143
+ else node->prev ->next = node->next ;
144
+
145
+ if (node->next ) node->next ->prev = node->prev ;
146
+
147
+ if (!freeList[index]) slBitmasks[fl] &= ~(1 << sl);
148
+ if (!slBitmasks[fl]) flBitmask &= ~(1ULL << fl);
149
+
150
+ node->next = nullptr ;
151
+ node->prev = nullptr ;
152
+
153
+ return true ;
154
+ }
155
+
156
+ TlsfAllocator::FreeBlockNode* TlsfAllocator::FindFreeBlock (const uint64_t & size)
157
+ {
158
+ uint64_t fl, sl, index;
159
+ index = CalculateFreeBlockIndices (size, fl, sl);
160
+
161
+ if (!IsFree (fl, sl)) index = GetNextFreeSlotIndex (fl, sl);
162
+ if (index == INVALID_INDEX) return nullptr ;
163
+
164
+ return freeList[index];
165
+ }
166
+
167
+ const uint64_t TlsfAllocator::GetNextFreeSlotIndex (uint64_t & fl, uint64_t & sl)
168
+ {
169
+ sl = __builtin_ctz (slBitmasks[fl] & ~(((1 << (sl + 1 )) - 1 )));
170
+
171
+ if (sl == 32 ) sl = 0 ;
172
+ if (sl) return fl * MAX_SL_BUCKETS + sl;
173
+
174
+ fl = flBitmask & ~(((1ULL << (sl + 1 )) - 1 ));
175
+
176
+ if (!fl) return INVALID_INDEX;
177
+
178
+ fl = __builtin_ctzll (fl);
179
+ CC_ASSERT (slBitmasks[fl] > 0 ,
180
+ " SlBitmasks is returning 0. This should not be happening and indicates an implementation error." )
181
+
182
+ sl = __builtin_ctz (slBitmasks[fl]);
183
+
184
+ return fl * MAX_SL_BUCKETS + sl;
185
+ }
186
+
187
+ uint64_t TlsfAllocator::CalculateFreeBlockIndices (uint64_t size, OUT uint64_t & fl, OUT uint64_t & sl)
188
+ {
189
+ uint64_t rawFl = FL (size);
190
+ fl = rawFl - FL_MIN_INDEX;
191
+ sl = SL (size, fl);
192
+ return fl * MAX_SL_BUCKETS + sl;
193
+ }
194
+
195
+ TlsfAllocator::BlockFooter* TlsfAllocator::CreateFooter (uint8_t * ptr, const uint64_t size)
196
+ {
197
+ if (!ptr) return nullptr ;
198
+ BlockFooter* footer = TO_FOOTER (ptr);
199
+ footer->totalBlockSize = size;
200
+ return footer;
201
+ }
202
+
203
+ TlsfAllocator::BlockHeader* TlsfAllocator::GetHeader (TlsfAllocator::FreeBlockNode* node)
204
+ {
205
+ if (!node) return nullptr ;
206
+ return TO_HEADER (TO_BYTES (node) - HEADER_SIZE);
207
+ }
208
+
209
+ TlsfAllocator::BlockHeader* TlsfAllocator::GetPrevHeader (TlsfAllocator::BlockHeader* header)
210
+ {
211
+ if (!header || TO_BYTES (header) == data) return nullptr ;
212
+ return TO_HEADER (TO_BYTES (header) - GetPrevFooter (header)->totalBlockSize );
213
+ }
214
+
215
+ TlsfAllocator::BlockHeader* TlsfAllocator::GetNextHeader (TlsfAllocator::BlockHeader* header)
216
+ {
217
+ if (!header) return nullptr ;
218
+ return TO_HEADER (TO_BYTES (header) + GetHeaderSize (header));
219
+ }
220
+
221
+ TlsfAllocator::FreeBlockNode* TlsfAllocator::CreateFreeBlock (uint8_t * ptr,
222
+ TlsfAllocator::FreeBlockNode* prev,
223
+ TlsfAllocator::FreeBlockNode* next)
224
+ {
225
+ if (!ptr) return nullptr ;
226
+ FreeBlockNode* node = TO_FREE_BLOCK (ptr);
227
+ node->prev = prev;
228
+ node->next = next;
229
+ return node;
230
+ }
231
+
232
+ TlsfAllocator::FreeBlockNode* TlsfAllocator::GetFreeBlock (BlockHeader* header)
233
+ {
234
+ if (!IsFree (header)) return nullptr ;
235
+ return TO_FREE_BLOCK (TO_BYTES (header) + HEADER_SIZE);
236
+ }
237
+
238
+ TlsfAllocator::FreeBlockNode* TlsfAllocator::GetFreeBlock (const uint64_t fl, const uint64_t sl)
239
+ {
240
+ return freeList[fl * MAX_SL_BUCKETS + sl];
241
+ }
242
+
243
+ TlsfAllocator::BlockFooter* TlsfAllocator::GetFooter (TlsfAllocator::BlockHeader* header)
244
+ {
245
+ uint64_t size = GetHeaderSize (header);
246
+ return TO_FOOTER (TO_BYTES (header) + (size - FOOTER_SIZE));
247
+ }
248
+
249
+ TlsfAllocator::BlockFooter* TlsfAllocator::GetPrevFooter (TlsfAllocator::BlockHeader* header)
250
+ {
251
+ uint8_t * raw = TO_BYTES (header);
252
+ if (raw == data) return nullptr ;
253
+ return TO_FOOTER (raw - FOOTER_SIZE);
254
+ }
255
+
256
+ uint8_t * TlsfAllocator::GetBlockData (TlsfAllocator::BlockHeader* header)
257
+ {
258
+ return TO_BYTES (header) + HEADER_SIZE;
259
+ }
260
+
261
+ const uint64_t TlsfAllocator::GetHeaderSize (BlockHeader* header)
262
+ {
263
+ return header->sizeAndFlags >> FLAG_OFFSET;
264
+ }
265
+
266
+ bool TlsfAllocator::IsFree (BlockHeader* header)
267
+ {
268
+ return header->sizeAndFlags & FREE;
269
+ }
270
+
271
+ bool TlsfAllocator::IsFree (uint64_t fl, uint64_t sl)
272
+ {
273
+ return slBitmasks[fl] & (1 << sl);
274
+ }
275
+
276
+ bool TlsfAllocator::PrevBlockIsFree (BlockHeader* header)
277
+ {
278
+ uint8_t * raw = TO_BYTES (header);
279
+ if (raw == data) return false ;
280
+ return header && IsFree (TO_HEADER (raw - GetPrevFooter (header)->totalBlockSize ));
281
+ }
282
+
283
+ } // namespace Siege
0 commit comments