| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110 |
- // zlib open source license
- //
- // Copyright (c) 2024 David Forsgren Piuva
- //
- // This software is provided 'as-is', without any express or implied
- // warranty. In no event will the authors be held liable for any damages
- // arising from the use of this software.
- //
- // Permission is granted to anyone to use this software for any purpose,
- // including commercial applications, and to alter it and redistribute it
- // freely, subject to the following restrictions:
- //
- // 1. The origin of this software must not be misrepresented; you must not
- // claim that you wrote the original software. If you use this software
- // in a product, an acknowledgment in the product documentation would be
- // appreciated but is not required.
- //
- // 2. Altered source versions must be plainly marked as such, and must not be
- // misrepresented as being the original software.
- //
- // 3. This notice may not be removed or altered from any source
- // distribution.
- #ifndef DFPSR_MEMORY
- #define DFPSR_MEMORY
- // Safe pointer checks are removed in release mode for speed after having tested the program in debug mode for safety.
- #ifndef NDEBUG
- #define SAFE_POINTER_CHECKS
- #endif
- #include <cstdint>
- namespace dsr {
- #ifdef SAFE_POINTER_CHECKS
- // Methods for slowly serializing the content of allocations without buffering with any dynamic memory.
- using PrintCharacter = void(*)(char32_t character);
- using AllocationSerialization = void(*)(PrintCharacter target, void const * const allocation, uintptr_t maxLength);
- #endif
- // A header that is placed next to memory allocations.
- struct AllocationHeader {
- uintptr_t totalSize; // Size of both header and payload.
- #ifdef SAFE_POINTER_CHECKS
- // TODO: Replace the name with a function pointer serializing the buffer's data into a human readable format.
- // Because it is only for the debug version, lambdas with capture may be used to store additional information.
- // If string_toStreamIndented has been defined for the type, it should try to use it if no serialization function was provided manually.
- const char *name = nullptr; // Debug name of the allocation.
- uint64_t threadHash; // Hash of the owning thread identity for thread local memory, 0 for shared memory.
- uint64_t allocationIdentity; // Rotating identity of the allocation, to know if the memory has been freed and reused within a memory allocator.
- // A function for serialization.
- AllocationSerialization serializationMethod = nullptr;
- #endif
- // Header for freed memory.
- AllocationHeader();
- // Header for allocated memory.
- // threadLocal should be true iff the memory may not be accessed from other threads, such as virtual stack memory.
- AllocationHeader(uintptr_t totalSize, bool threadLocal, const char *name);
- // Give a new identity to a reused allocation header.
- void reuse(bool threadLocal, const char *name);
- };
- // A structure used to allocate memory before placing the content in SafePointer.
- struct UnsafeAllocation {
- uint8_t *data;
- AllocationHeader *header;
- UnsafeAllocation(uint8_t *data, AllocationHeader *header)
- : data(data), header(header) {}
- };
- // Post-condition: Returns size rounded up by (~alignmentAndMask) + 1.
- constexpr inline uintptr_t memory_getPaddedSize_usingAndMask(uintptr_t size, uintptr_t alignmentAndMask) {
- // 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.
- return (size + ~alignmentAndMask) & alignmentAndMask;
- }
- // Pre-condition: The alignment argument must be a power of two (1, 2, 4, 8, 16, 32, 64...).
- // Post-condition: Returns size rounded up by alignment.
- constexpr inline uintptr_t memory_getPaddedSize(uintptr_t size, uintptr_t alignment) {
- // For integers, you can round up to multiples of alignment, by adding alignment - 1 and rounding down.
- // When rounding down for a power of two, you can bit mask away the least significant bits.
- uintptr_t roundedBits = alignment - 1;
- return (size + roundedBits) & ~roundedBits;
- }
- // 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.
- template <typename T>
- constexpr inline uintptr_t memory_getPaddedSize() {
- return memory_getPaddedSize((uintptr_t)sizeof(T), (uintptr_t)alignof(T));
- }
- // Create a mask for aligning memory in descending address space.
- // The bitwise and operation "&" between a pointer and this function's result becomes an address rounded down by alignment.
- // myAlignedPointer = (uintptr_t)myPointer & memory_createAlignmentAndMask(alignment)
- // By allocating memory from back to front, rounding down can be used for memory alignment.
- // Pre-condition:
- // alignment is a power of two (1, 2, 4, 8, 16, 32, 64...)
- // Post-condition:
- // Returns a bit mask for rounding an integer down to the closest multiple of alignment.
- constexpr inline uintptr_t memory_createAlignmentAndMask(uintptr_t alignment) {
- // alignment = ...00001000...
- // Subtracting one from a power of two gives a mask with ones for the remainder bits.
- // remainder = ...00000111...
- // Then we simply negate the mask to get the alignment mask for rounding down.
- // mask = ...11111000...
- return ~(alignment - 1);
- }
- }
- #endif
|