memory.h 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. // zlib open source license
  2. //
  3. // Copyright (c) 2024 David Forsgren Piuva
  4. //
  5. // This software is provided 'as-is', without any express or implied
  6. // warranty. In no event will the authors be held liable for any damages
  7. // arising from the use of this software.
  8. //
  9. // Permission is granted to anyone to use this software for any purpose,
  10. // including commercial applications, and to alter it and redistribute it
  11. // freely, subject to the following restrictions:
  12. //
  13. // 1. The origin of this software must not be misrepresented; you must not
  14. // claim that you wrote the original software. If you use this software
  15. // in a product, an acknowledgment in the product documentation would be
  16. // appreciated but is not required.
  17. //
  18. // 2. Altered source versions must be plainly marked as such, and must not be
  19. // misrepresented as being the original software.
  20. //
  21. // 3. This notice may not be removed or altered from any source
  22. // distribution.
  23. #ifndef DFPSR_MEMORY
  24. #define DFPSR_MEMORY
  25. // Safe pointer checks are removed in release mode for speed after having tested the program in debug mode for safety.
  26. #ifndef NDEBUG
  27. #define SAFE_POINTER_CHECKS
  28. #endif
  29. #include <cstdint>
  30. namespace dsr {
  31. #ifdef SAFE_POINTER_CHECKS
  32. // Methods for slowly serializing the content of allocations without buffering with any dynamic memory.
  33. using PrintCharacter = void(*)(char32_t character);
  34. using AllocationSerialization = void(*)(PrintCharacter target, void const * const allocation, uintptr_t maxLength);
  35. #endif
  36. // A header that is placed next to memory allocations.
  37. struct AllocationHeader {
  38. uintptr_t totalSize; // Size of both header and payload.
  39. #ifdef SAFE_POINTER_CHECKS
  40. // TODO: Replace the name with a function pointer serializing the buffer's data into a human readable format.
  41. // Because it is only for the debug version, lambdas with capture may be used to store additional information.
  42. // If string_toStreamIndented has been defined for the type, it should try to use it if no serialization function was provided manually.
  43. const char *name = nullptr; // Debug name of the allocation.
  44. uint64_t threadHash; // Hash of the owning thread identity for thread local memory, 0 for shared memory.
  45. uint64_t allocationIdentity; // Rotating identity of the allocation, to know if the memory has been freed and reused within a memory allocator.
  46. // A function for serialization.
  47. AllocationSerialization serializationMethod = nullptr;
  48. #endif
  49. // Header for freed memory.
  50. AllocationHeader();
  51. // Header for allocated memory.
  52. // threadLocal should be true iff the memory may not be accessed from other threads, such as virtual stack memory.
  53. AllocationHeader(uintptr_t totalSize, bool threadLocal, const char *name);
  54. // Give a new identity to a reused allocation header.
  55. void reuse(bool threadLocal, const char *name);
  56. };
  57. // A structure used to allocate memory before placing the content in SafePointer.
  58. struct UnsafeAllocation {
  59. uint8_t *data;
  60. AllocationHeader *header;
  61. UnsafeAllocation(uint8_t *data, AllocationHeader *header)
  62. : data(data), header(header) {}
  63. };
  64. // Post-condition: Returns size rounded up by (~alignmentAndMask) + 1.
  65. constexpr inline uintptr_t memory_getPaddedSize_usingAndMask(uintptr_t size, uintptr_t alignmentAndMask) {
  66. // The bitwise negation of alignmentAndMask equals the alignment minus one, which is just what we need to add before truncating down using the and mask.
  67. return (size + ~alignmentAndMask) & alignmentAndMask;
  68. }
  69. // Pre-condition: The alignment argument must be a power of two (1, 2, 4, 8, 16, 32, 64...).
  70. // Post-condition: Returns size rounded up by alignment.
  71. constexpr inline uintptr_t memory_getPaddedSize(uintptr_t size, uintptr_t alignment) {
  72. // For integers, you can round up to multiples of alignment, by adding alignment - 1 and rounding down.
  73. // When rounding down for a power of two, you can bit mask away the least significant bits.
  74. uintptr_t roundedBits = alignment - 1;
  75. return (size + roundedBits) & ~roundedBits;
  76. }
  77. // Post-condition: Returns the size of T rounded up by T's own alignment, which becomes the stride between elements in a memory aligned array.
  78. template <typename T>
  79. constexpr inline uintptr_t memory_getPaddedSize() {
  80. return memory_getPaddedSize((uintptr_t)sizeof(T), (uintptr_t)alignof(T));
  81. }
  82. // Create a mask for aligning memory in descending address space.
  83. // The bitwise and operation "&" between a pointer and this function's result becomes an address rounded down by alignment.
  84. // myAlignedPointer = (uintptr_t)myPointer & memory_createAlignmentAndMask(alignment)
  85. // By allocating memory from back to front, rounding down can be used for memory alignment.
  86. // Pre-condition:
  87. // alignment is a power of two (1, 2, 4, 8, 16, 32, 64...)
  88. // Post-condition:
  89. // Returns a bit mask for rounding an integer down to the closest multiple of alignment.
  90. constexpr inline uintptr_t memory_createAlignmentAndMask(uintptr_t alignment) {
  91. // alignment = ...00001000...
  92. // Subtracting one from a power of two gives a mask with ones for the remainder bits.
  93. // remainder = ...00000111...
  94. // Then we simply negate the mask to get the alignment mask for rounding down.
  95. // mask = ...11111000...
  96. return ~(alignment - 1);
  97. }
  98. }
  99. #endif