ORIGINAL
Loading...
Searching...
No Matches
allocator.h
Go to the documentation of this file.
1#ifndef ALLOCATOR_H
2#define ALLOCATOR_H
3
4
5#include "config.h"
6#include "error.h"
7#include "maths.h"
8#include "type_traits"
9#include "utility"
10
35namespace original {
36
43 class allocators final {
44 public:
54 template <typename TYPE>
55 static TYPE* malloc(u_integer size);
56
63 template <typename TYPE>
64 static void free(TYPE* ptr);
65 };
66
84 template<typename TYPE, template <typename> typename DERIVED>
86 public:
89 using propagate_on_container_swap = std::false_type;
90 using propagate_on_container_merge = std::false_type;
91
96 template <typename O_TYPE>
97 using rebind_alloc = DERIVED<O_TYPE>;
98
105 constexpr allocatorBase();
106
112 virtual TYPE* allocate(u_integer size) = 0;
113
119 virtual void deallocate(TYPE* ptr, u_integer size) = 0;
120
128 template<typename O_TYPE, typename... Args>
129 void construct(O_TYPE* o_ptr, Args&&... args);
130
136 template<typename O_TYPE>
137 static void destroy(O_TYPE* o_ptr);
138
139 virtual ~allocatorBase() = 0;
140 };
141
153 template<typename TYPE>
154 class allocator : public allocatorBase<TYPE, allocator> {
155 public:
160
168 TYPE* allocate(u_integer size) override;
169
175 void deallocate(TYPE* ptr, u_integer size) override;
176 };
177
202 template<typename TYPE>
203 class objPoolAllocator final : public allocatorBase<TYPE, objPoolAllocator>
204 {
210 class freeChunk
211 {
212 public:
213 freeChunk* next = nullptr;
214 };
215
220 class allocatedChunks {
221 public:
222 void* chunks = nullptr;
223 allocatedChunks* next = nullptr;
224 };
225
233 static constexpr u_integer CHUNK_SIZE = sizeof(TYPE) > sizeof(freeChunk*) ? sizeof(TYPE) : sizeof(freeChunk*);
234
235 u_integer size_class_count;
236 u_integer chunk_count_init;
237 u_integer* chunk_count;
238 freeChunk** free_list_head;
239 allocatedChunks* allocated_list_head;
240 u_integer* chunks_available;
241
242
252 void poolInit();
253
259 [[nodiscard]] static constexpr u_integer getChunkIndex(u_integer size);
260
267 void chunkAllocate(u_integer num_element, u_integer index);
268
277 void release() noexcept;
278
279 public:
282 using propagate_on_container_swap = std::true_type;
283 using propagate_on_container_merge = std::true_type;
284
290 explicit objPoolAllocator(u_integer size_class_count = 8, u_integer count = 4);
291
294
306
312 objPoolAllocator(objPoolAllocator&& other) noexcept;
313
322
330 TYPE* allocate(u_integer size) override;
331
338 void deallocate(TYPE* ptr, u_integer size) override;
339
343 ~objPoolAllocator() override;
344 };
345}
346
347template<typename TYPE>
349 if (size == 0) {
350 return nullptr;
351 }
352
353 try {
354 return static_cast<TYPE*>(operator new(size * sizeof(TYPE)));
355 } catch (const std::bad_alloc&) {
356 throw allocateError();
357 }
358}
359
360template<typename TYPE>
362 ::operator delete(ptr);
363}
364
365template <typename TYPE, template <typename> typename DERIVED>
367{
368 staticError<allocateError, sizeof(TYPE) == 0 || std::is_void_v<TYPE>>::asserts();
369}
370
371template<typename TYPE, template <typename> typename DERIVED>
373
374template<typename TYPE, template <typename> typename DERIVED>
375template<typename O_TYPE, typename... Args>
376void original::allocatorBase<TYPE, DERIVED>::construct(O_TYPE *o_ptr, Args &&... args) {
377 new (o_ptr) O_TYPE{std::forward<Args>(args)...};
378}
379
380template<typename TYPE, template <typename> typename DERIVED>
381template<typename O_TYPE>
383 o_ptr->~O_TYPE();
384}
385
386template<typename TYPE>
390
391template<typename TYPE>
395
396template<typename TYPE>
397constexpr original::u_integer
399 u_integer index = 0;
400 while (static_cast<u_integer>(1) << index < size) {
401 index += 1;
402 }
403 return index;
404}
405
406template<typename TYPE>
408 this->chunk_count = allocators::malloc<u_integer>(this->size_class_count);
409 this->free_list_head = allocators::malloc<freeChunk*>(this->size_class_count);
410 this->chunks_available = allocators::malloc<u_integer>(this->size_class_count);
411 this->allocated_list_head = nullptr;
412
413 for (u_integer i = 0; i < this->size_class_count; i++) {
414 this->chunk_count[i] = this->chunk_count_init;
415 this->free_list_head[i] = nullptr;
416 this->chunks_available[i] = 0;
417 }
418}
419
420template <typename TYPE>
422{
423 const u_integer block_size = (1 << index) * CHUNK_SIZE;
424 auto new_free_chunk = allocators::malloc<byte>(num_element * block_size);
425 auto new_allocated_chunk = allocators::malloc<allocatedChunks>(1);
426
427 new_allocated_chunk->chunks = new_free_chunk;
428 new_allocated_chunk->next = this->allocated_list_head;
429 this->allocated_list_head = new_allocated_chunk;
430
431 for (u_integer i = 0; i < num_element; i++)
432 {
433 auto cur_ptr = reinterpret_cast<freeChunk*>(new_free_chunk + i * block_size);
434 cur_ptr->next = this->free_list_head[index];
435 this->free_list_head[index] = cur_ptr;
436 }
437 this->chunks_available[index] += num_element;
438 this->chunk_count[index] = this->chunk_count[index] + (this->chunk_count[index] >> 1);
439}
440
441template<typename TYPE>
443 while (this->allocated_list_head) {
444 auto next_chunk = this->allocated_list_head->next;
445 allocators::free(this->allocated_list_head->chunks);
446 allocators::free(this->allocated_list_head);
447 this->allocated_list_head = next_chunk;
448 }
449
450 if (this->chunk_count) {
451 allocators::free(this->chunk_count);
452 this->chunk_count = nullptr;
453 }
454 if (this->free_list_head) {
455 allocators::free(this->free_list_head);
456 this->free_list_head = nullptr;
457 }
458 if (this->chunks_available) {
459 allocators::free(this->chunks_available);
460 this->chunks_available = nullptr;
461 }
462}
463
464template <typename TYPE>
466 : size_class_count(size_class_count), chunk_count_init(count), chunk_count(nullptr),
467 free_list_head(nullptr), allocated_list_head(nullptr), chunks_available(nullptr) {
468 this->poolInit();
469}
470
471template<typename TYPE>
473 if (this == &other)
474 return *this;
475
476 auto size_class_count_max = max(this->size_class_count, other.size_class_count);
477 auto chunk_count_init_max = max(this->chunk_count_init, other.chunk_count_init);
478 auto chunk_count_max = allocators::malloc<u_integer>(size_class_count_max);
479 auto free_list_head_max = allocators::malloc<freeChunk*>(size_class_count_max);
480 auto chunks_available_max = allocators::malloc<u_integer>(size_class_count_max);
481
482 for (u_integer i = 0; i < size_class_count_max; i++) {
483 if (i < this->size_class_count && i < other.size_class_count) {
484 chunk_count_max[i] = max(this->chunk_count[i], other.chunk_count[i]);
485 chunks_available_max[i] = this->chunks_available[i] + other.chunks_available[i];
486 auto cur_list_head_i = other.free_list_head[i];
487 if (cur_list_head_i) {
488 while (cur_list_head_i->next) {
489 cur_list_head_i = cur_list_head_i->next;
490 }
491 cur_list_head_i->next = this->free_list_head[i];
492 free_list_head_max[i] = other.free_list_head[i];
493 }else {
494 free_list_head_max[i] = this->free_list_head[i];
495 }
496 }else if (i < this->size_class_count) {
497 chunk_count_max[i] = this->chunk_count[i];
498 chunks_available_max[i] = this->chunks_available[i];
499 free_list_head_max[i] = this->free_list_head[i];
500 }else {
501 chunk_count_max[i] = other.chunk_count[i];
502 chunks_available_max[i] = other.chunks_available[i];
503 free_list_head_max[i] = other.free_list_head[i];
504 }
505 }
506
507 auto allocated_list_head_max = other.allocated_list_head;
508 auto cur_allocated_list_head = allocated_list_head_max;
509 if (cur_allocated_list_head) {
510 while (cur_allocated_list_head->next) {
511 cur_allocated_list_head = cur_allocated_list_head->next;
512 }
513 cur_allocated_list_head->next = this->allocated_list_head;
514 }else {
515 allocated_list_head_max = this->allocated_list_head;
516 }
517 this->allocated_list_head = nullptr;
518 other.allocated_list_head = nullptr;
519
520 this->release();
521 other.release();
522 other.poolInit();
523
524 this->size_class_count = size_class_count_max;
525 this->chunk_count_init = chunk_count_init_max;
526 this->chunk_count = chunk_count_max;
527 this->free_list_head = free_list_head_max;
528 this->chunks_available = chunks_available_max;
529 this->allocated_list_head = allocated_list_head_max;
530
531 return *this;
532}
533
534template<typename TYPE>
536 this->operator=(std::move(other));
537}
538
539template<typename TYPE>
541 if (this == &other)
542 return *this;
543
544 this->release();
545
546 this->size_class_count = other.size_class_count;
547 this->chunk_count_init = other.chunk_count_init;
548 this->chunk_count = other.chunk_count;
549 this->free_list_head = other.free_list_head;
550 this->allocated_list_head = other.allocated_list_head;
551 this->chunks_available = other.chunks_available;
552
553 other.poolInit();
554 return *this;
555}
556
557template <typename TYPE>
559{
560 if (size == 0) {
561 return nullptr;
562 }
563
564 u_integer index = getChunkIndex(size);
565
566 if (index >= this->size_class_count) {
567 return allocators::malloc<TYPE>(size);
568 }
569
570 if (!this->free_list_head[index] || this->chunks_available[index] * (static_cast<u_integer>(1) << index) < size) {
571 this->chunkAllocate(max<u_integer>(1, this->chunk_count[index]), index);
572 }
573
574 auto cur_ptr = this->free_list_head[index];
575 this->free_list_head[index] = this->free_list_head[index]->next;
576 this->chunks_available[index] -= 1;
577 return reinterpret_cast<TYPE*>(cur_ptr);
578}
579
580template <typename TYPE>
582{
583 if (size == 0) {
584 return;
585 }
586
587 u_integer index = getChunkIndex(size);
588
589 if (index >= this->size_class_count) {
590 allocators::free(ptr);
591 return;
592 }
593
594 auto p = reinterpret_cast<freeChunk*>(ptr);
595 p->next = this->free_list_head[index];
596 this->free_list_head[index] = p;
597 this->chunks_available[index] += 1;
598}
599
600template<typename TYPE>
604
605#endif //ALLOCATOR_H
Exception for memory allocation failures.
Definition error.h:285
Interface for other memory allocator implementations.
Definition allocator.h:85
std::false_type propagate_on_container_swap
No propagation on swap.
Definition allocator.h:89
std::false_type propagate_on_container_copy_assignment
No propagation on copy.
Definition allocator.h:87
DERIVED< O_TYPE > rebind_alloc
Rebinds allocator to different type.
Definition allocator.h:97
virtual ~allocatorBase()=0
Virtual destructor.
std::false_type propagate_on_container_move_assignment
No propagation on move.
Definition allocator.h:88
void construct(O_TYPE *o_ptr, Args &&... args)
Constructs an object in allocated memory.
Definition allocator.h:376
constexpr allocatorBase()
Constructs a new allocatorBase instance.
Definition allocator.h:366
static void destroy(O_TYPE *o_ptr)
Destroys an object without deallocating.
Definition allocator.h:382
virtual void deallocate(TYPE *ptr, u_integer size)=0
Deallocates memory.
virtual TYPE * allocate(u_integer size)=0
Allocates raw memory.
std::false_type propagate_on_container_merge
No propagation on merge.
Definition allocator.h:90
Default memory allocator using allocators utilities.
Definition allocator.h:154
void deallocate(TYPE *ptr, u_integer size) override
Deallocates memory using global operator delete.
Definition allocator.h:392
TYPE * allocate(u_integer size) override
Allocates memory using global operator new.
Definition allocator.h:387
Utility class providing static memory allocation/de-allocation functions.
Definition allocator.h:43
static TYPE * malloc(u_integer size)
Allocates raw memory using global operator new.
Definition allocator.h:348
static void free(TYPE *ptr)
Deallocates memory using global operator delete.
Definition allocator.h:361
Object pool allocator for efficient fixed-size memory management.
Definition allocator.h:204
objPoolAllocator & operator+=(objPoolAllocator &other)
Merges another pool allocator into this one.
Definition allocator.h:472
objPoolAllocator(const objPoolAllocator &)=delete
Copy construction disabled.
TYPE * allocate(u_integer size) override
Allocates memory from the pool.
Definition allocator.h:558
std::true_type propagate_on_container_swap
Allows propagation on swap.
Definition allocator.h:282
void deallocate(TYPE *ptr, u_integer size) override
Returns memory to the pool.
Definition allocator.h:581
std::true_type propagate_on_container_move_assignment
Allows propagation on move.
Definition allocator.h:281
objPoolAllocator(u_integer size_class_count=8, u_integer count=4)
Constructs a new object pool allocator.
Definition allocator.h:465
std::true_type propagate_on_container_merge
Allows propagation on merge.
Definition allocator.h:283
~objPoolAllocator() override
Destructor - releases all allocated memory.
Definition allocator.h:601
objPoolAllocator & operator=(const objPoolAllocator &)=delete
Copy assignment disabled.
Compile-time error triggering utility.
Definition error.h:112
Platform-independent type definitions and compiler/platform detection.
Custom exception classes and callback validation utilities.
std::uint32_t u_integer
32-bit unsigned integer type for sizes and indexes
Definition config.h:263
Mathematical utilities and constants.
Main namespace for the project Original.
Definition algorithms.h:21
TYPE max(TYPE a, TYPE b)
Returns the larger of two given values.