affix_allocator.h 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. #pragma once
  2. #include <type_traits>
  3. #include "Algorithm.h"
  4. namespace baselib
  5. {
  6. BASELIB_CPP_INTERFACE
  7. {
  8. // Baselib affix allocator implementation providing optional prefix and suffix memory regions in addition to requested size.
  9. //
  10. // The affix allocator purpose is to provide memory regions directly adjacent to allocated memory of requested size and alignment.
  11. // It is not intended to be a turn-key, general purpose solution, but rather act as a template building block for derived allocators which may extend,
  12. // add or ignore methods for specific needs.
  13. //
  14. // Allocation methods allocate, reallocate and deallocate are using the `Allocator` implementation for memory allocation, as are alignment properties.
  15. // As a rule of thumb, Allocator method calls may fail depending on their specific implementation.
  16. // What (if any) action is to be taken in such cases is intentionally left to be implemented by the derived class.
  17. //
  18. // No operations, synchronisation or alignment concept are applied to the prefix or suffix memory.
  19. // Prefix memory address is obtained using the `prefix` function and is always allocated memory pointer minus prefix_size (ptr - prefix_size).
  20. // Suffix memory address is obtained using the `suffix` function and is always directly adjacent to the end of allocated memory (ptr + size).
  21. //
  22. // Notes on memory footprint:
  23. // Internally allocated memory must be large enough to hold requested allocation size, prefix_size, suffix_size and alignment padding.
  24. // The internally allocated size is calculated as follows: size + suffix_size + (prefix_size rounded up to alignment).
  25. // If alignment padding is significant, it may be preferable to use a suffix over a prefix to reduce memory footprint.
  26. //
  27. template<class Allocator, size_t prefix_size, size_t suffix_size>
  28. class affix_allocator : protected Allocator
  29. {
  30. public:
  31. // Allocated memory is guaranteed to always be aligned to at least the value of `alignment`.
  32. static constexpr uint32_t alignment = Allocator::alignment;
  33. // Allocates a memory block large enough to hold `size` number of bytes. Zero size is valid.
  34. //
  35. // \returns Address to memory block of allocated memory.
  36. void* allocate(size_t size)
  37. {
  38. return OffsetPtrChecked(Allocator::allocate(size + m_AffixSize), m_PrefixAlignedSize);
  39. }
  40. // Reallocates previously allocated or reallocated memory block pointer reference `ptr` from `old_size` to `new_size` number of bytes.
  41. // Passing `nullptr` in `ptr` yield the same result as calling `allocate`.
  42. // If `suffix_size` is non-zero, the suffix memory is moved to the new location.
  43. //
  44. // \returns Address to memory block of reallocated memory.
  45. void* reallocate(void* ptr, size_t old_size, size_t new_size)
  46. {
  47. return ptr == nullptr ? allocate(new_size) : ReallocateImpl(ptr, old_size, new_size);
  48. }
  49. // Deallocates memory block previously allocated or reallocated with `size` pointed to by `ptr`.
  50. // Passing `nullptr` in `ptr` result in a no-op.
  51. //
  52. // \returns Always returns `true` (see notes on operation failure).
  53. bool deallocate(void* ptr, size_t size)
  54. {
  55. return Allocator::deallocate(OffsetPtr(ptr, -m_PrefixAlignedSize), size + m_AffixSize);
  56. }
  57. // Calculate optimal allocation of size of `Allocator` allocator given `size`.
  58. //
  59. // \returns Optimal size of allocations when allocating memory given `size`.
  60. constexpr size_t optimal_size(size_t size) const
  61. {
  62. return Allocator::optimal_size(size);
  63. }
  64. // Get prefix memory block address of allocation pointed to by `ptr`.
  65. // Memory must be a valid allocation from `allocate` or `reallocate`, or result is undefined.
  66. //
  67. // \returns Prefix memory address or nullptr if `prefix_size` is zero.
  68. void* prefix(void* ptr) const
  69. {
  70. return prefix_size == 0 ? nullptr : OffsetPtr(ptr, -static_cast<ptrdiff_t>(prefix_size));
  71. }
  72. // Get suffix memory block address of allocation with `size` pointed to by `ptr`.
  73. // Memory must be a valid allocation from `allocate` or `reallocate`, or result is undefined.
  74. //
  75. // \returns Suffix memory address or nullptr if `suffix_size` is zero.
  76. void* suffix(void* ptr, size_t size) const
  77. {
  78. return suffix_size == 0 ? nullptr : OffsetPtr(ptr, size);
  79. }
  80. private:
  81. static constexpr size_t AlignSize(size_t size) { return (size + Allocator::alignment - 1) & ~(Allocator::alignment - 1); }
  82. static FORCE_INLINE constexpr void *OffsetPtrChecked(const void *ptr, size_t offset) { return ptr == nullptr ? nullptr : OffsetPtr(ptr, offset); }
  83. static FORCE_INLINE constexpr void *OffsetPtr(const void *ptr, size_t offset)
  84. {
  85. return reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(ptr) + offset);
  86. }
  87. template<size_t value = suffix_size, typename std::enable_if<value == 0, bool>::type = 0>
  88. FORCE_INLINE void* ReallocateImpl(void* ptr, size_t old_size, size_t new_size)
  89. {
  90. return OffsetPtrChecked(Allocator::reallocate(OffsetPtr(ptr, -m_PrefixAlignedSize), old_size + m_PrefixAlignedSize, new_size + m_PrefixAlignedSize), m_PrefixAlignedSize);
  91. }
  92. template<size_t value = suffix_size, typename std::enable_if<value != 0, bool>::type = 0>
  93. FORCE_INLINE void* ReallocateImpl(void* ptr, size_t old_size, size_t new_size)
  94. {
  95. uint8_t tmpSuffix[m_SuffixSize];
  96. memcpy(tmpSuffix, suffix(ptr, old_size), m_SuffixSize);
  97. ptr = Allocator::reallocate(OffsetPtr(ptr, -m_PrefixAlignedSize), old_size + m_AffixSize, new_size + m_AffixSize);
  98. if (ptr)
  99. {
  100. ptr = OffsetPtr(ptr, m_PrefixAlignedSize);
  101. memcpy(suffix(ptr, new_size), tmpSuffix, m_SuffixSize);
  102. }
  103. return ptr;
  104. }
  105. static constexpr ptrdiff_t m_PrefixAlignedSize = AlignSize(prefix_size);
  106. static constexpr ptrdiff_t m_SuffixSize = suffix_size;
  107. static constexpr ptrdiff_t m_AffixSize = m_PrefixAlignedSize + m_SuffixSize;
  108. };
  109. }
  110. }