diff --git a/shared/source/utilities/heap_allocator.h b/shared/source/utilities/heap_allocator.h index 9720e85155..5c095fd1b5 100644 --- a/shared/source/utilities/heap_allocator.h +++ b/shared/source/utilities/heap_allocator.h @@ -40,6 +40,11 @@ class HeapAllocator { } uint64_t allocate(size_t &sizeToAllocate) { + return allocateWithCustomAlignment(sizeToAllocate, this->allocationAlignment); + } + + uint64_t allocateWithCustomAlignment(size_t &sizeToAllocate, size_t alignment) { + UNRECOVERABLE_IF(alignment % allocationAlignment != 0); // custom alignment have to be a multiple of allocator alignment sizeToAllocate = alignUp(sizeToAllocate, allocationAlignment); std::lock_guard lock(mtx); @@ -53,16 +58,26 @@ class HeapAllocator { for (;;) { size_t sizeOfFreedChunk = 0; - uint64_t ptrReturn = getFromFreedChunks(sizeToAllocate, freedChunks, sizeOfFreedChunk); + uint64_t ptrReturn = getFromFreedChunks(sizeToAllocate, freedChunks, sizeOfFreedChunk, alignment); if (ptrReturn == 0llu) { if (sizeToAllocate > sizeThreshold) { - if (pLeftBound + sizeToAllocate <= pRightBound) { + const uint64_t misalignment = alignUp(pLeftBound, alignment) - pLeftBound; + if (pLeftBound + misalignment + sizeToAllocate <= pRightBound) { + if (misalignment) { + storeInFreedChunks(pLeftBound, static_cast(misalignment), freedChunks); + pLeftBound += misalignment; + } ptrReturn = pLeftBound; pLeftBound += sizeToAllocate; } } else { - if (pRightBound - sizeToAllocate >= pLeftBound) { + const uint64_t misalignment = pRightBound - alignDown(pRightBound, alignment); + if (pLeftBound + sizeToAllocate + misalignment <= pRightBound) { + if (misalignment) { + pRightBound -= misalignment; + storeInFreedChunks(pRightBound, static_cast(misalignment), freedChunks); + } pRightBound -= sizeToAllocate; ptrReturn = pRightBound; } @@ -76,6 +91,7 @@ class HeapAllocator { } else { availableSize -= sizeToAllocate; } + DEBUG_BREAK_IF(!isAligned(ptrReturn, alignment)); return ptrReturn; } @@ -133,13 +149,18 @@ class HeapAllocator { std::vector freedChunksBig; std::mutex mtx; - uint64_t getFromFreedChunks(size_t size, std::vector &freedChunks, size_t &sizeOfFreedChunk) { + uint64_t getFromFreedChunks(size_t size, std::vector &freedChunks, size_t &sizeOfFreedChunk, size_t requiredAlignment) { size_t elements = freedChunks.size(); size_t bestFitIndex = -1; size_t bestFitSize = 0; sizeOfFreedChunk = 0; for (size_t i = 0; i < elements; i++) { + const bool chunkAligned = isAligned(freedChunks[i].ptr, requiredAlignment); + if (!chunkAligned) { + continue; + } + if (freedChunks[i].size == size) { auto ptr = freedChunks[i].ptr; freedChunks.erase(freedChunks.begin() + i); diff --git a/shared/test/unit_test/utilities/heap_allocator_tests.cpp b/shared/test/unit_test/utilities/heap_allocator_tests.cpp index b71e80fa84..17a0fc40d2 100644 --- a/shared/test/unit_test/utilities/heap_allocator_tests.cpp +++ b/shared/test/unit_test/utilities/heap_allocator_tests.cpp @@ -31,9 +31,9 @@ class HeapAllocatorUnderTest : public HeapAllocator { size_t getThresholdSize() const { return this->sizeThreshold; } using HeapAllocator::defragment; - uint64_t getFromFreedChunks(size_t size, std::vector &vec) { + uint64_t getFromFreedChunks(size_t size, std::vector &vec, size_t requiredAlignment) { size_t sizeOfFreedChunk; - return HeapAllocator::getFromFreedChunks(size, vec, sizeOfFreedChunk); + return HeapAllocator::getFromFreedChunks(size, vec, sizeOfFreedChunk, requiredAlignment); } void storeInFreedChunks(uint64_t ptr, size_t size, std::vector &vec) { return HeapAllocator::storeInFreedChunks(ptr, size, vec); } @@ -84,7 +84,7 @@ TEST(HeapAllocatorTest, GivenExactSizeChunkInFreedChunksWhenGetIsCalledThenChunk size_t sizeFreed = MemoryConstants::pageSize * 2; freedChunks.emplace_back(ptrFreed, sizeFreed); - auto ptrReturned = heapAllocator->getFromFreedChunks(sizeFreed, freedChunks); + auto ptrReturned = heapAllocator->getFromFreedChunks(sizeFreed, freedChunks, allocationAlignment); EXPECT_EQ(ptrFreed, ptrReturned); // ptr returned is the one that was stored EXPECT_EQ(0u, freedChunks.size()); // entry in freed container is removed @@ -107,7 +107,7 @@ TEST(HeapAllocatorTest, GivenOnlySmallerSizeChunksInFreedChunksWhenGetIsCalledTh EXPECT_EQ(7u, freedChunks.size()); - auto ptrReturned = heapAllocator->getFromFreedChunks(4 * 4096, freedChunks); + auto ptrReturned = heapAllocator->getFromFreedChunks(4 * 4096, freedChunks, allocationAlignment); EXPECT_EQ(0llu, ptrReturned); EXPECT_EQ(7u, freedChunks.size()); @@ -138,7 +138,7 @@ TEST(HeapAllocatorTest, GivenOnlyBiggerSizeChunksInFreedChunksWhenGetIsCalledThe EXPECT_EQ(5u, freedChunks.size()); - auto ptrReturned = heapAllocator->getFromFreedChunks(3 * 4096, freedChunks); + auto ptrReturned = heapAllocator->getFromFreedChunks(3 * 4096, freedChunks, allocationAlignment); EXPECT_EQ(ptrExpected, ptrReturned); EXPECT_EQ(4u, freedChunks.size()); @@ -166,7 +166,7 @@ TEST(HeapAllocatorTest, GivenOnlyMoreThanTwiceBiggerSizeChunksInFreedChunksWhenG EXPECT_EQ(3u, freedChunks.size()); - auto ptrReturned = heapAllocator->getFromFreedChunks(requestedSize, freedChunks); + auto ptrReturned = heapAllocator->getFromFreedChunks(requestedSize, freedChunks, allocationAlignment); EXPECT_EQ(ptrExpected, ptrReturned); EXPECT_EQ(3u, freedChunks.size()); @@ -1000,3 +1000,366 @@ TEST(HeapAllocatorTest, Given10SmallAllocationsWhenMergedToBigAllocatedAsSmallNo EXPECT_EQ(0u, freedChunksSmall.size()); EXPECT_EQ(0u, freedChunksBig.size()); } + +TEST(HeapAllocatorTest, givenAlignedBoundWhenAllocatingMemoryWithCustomAlignmentFromLeftThenReturnAllocations) { + const uint64_t heapBase = 0x100000llu; + const size_t heapSize = 1024u * 4096u; + HeapAllocatorUnderTest heapAllocator(heapBase, heapSize, allocationAlignment, sizeThreshold); + + const size_t customAlignment = 32 * MemoryConstants::pageSize; + size_t ptrSize = 32 * MemoryConstants::pageSize; + uint64_t ptr = heapAllocator.allocateWithCustomAlignment(ptrSize, customAlignment); + EXPECT_EQ(heapBase + heapSize, heapAllocator.getRightBound()); + EXPECT_EQ(heapBase + ptrSize, heapAllocator.getLeftBound()); + EXPECT_EQ(32 * MemoryConstants::pageSize, ptrSize); + EXPECT_EQ(heapBase, ptr); + EXPECT_EQ(0u, heapAllocator.getFreedChunksBig().size()); + + ptrSize = 32 * MemoryConstants::pageSize; + ptr = heapAllocator.allocateWithCustomAlignment(ptrSize, customAlignment); + EXPECT_EQ(heapBase + heapSize, heapAllocator.getRightBound()); + EXPECT_EQ(heapBase + 2 * ptrSize, heapAllocator.getLeftBound()); + EXPECT_EQ(32 * MemoryConstants::pageSize, ptrSize); + EXPECT_EQ(heapBase + ptrSize, ptr); + EXPECT_EQ(0u, heapAllocator.getFreedChunksBig().size()); +} + +TEST(HeapAllocatorTest, givenAlignedBoundWhenAllocatingMemoryWithCustomAlignmentFromRightThenReturnAllocations) { + const uint64_t heapBase = 0x100000llu; + const size_t heapSize = 1024u * 4096u; + HeapAllocatorUnderTest heapAllocator(heapBase, heapSize, allocationAlignment, sizeThreshold); + + const size_t customAlignment = 8 * MemoryConstants::pageSize; + size_t ptrSize = 8 * MemoryConstants::pageSize; + uint64_t ptr = heapAllocator.allocateWithCustomAlignment(ptrSize, customAlignment); + EXPECT_EQ(heapBase + heapSize - ptrSize, heapAllocator.getRightBound()); + EXPECT_EQ(heapBase, heapAllocator.getLeftBound()); + EXPECT_EQ(8 * MemoryConstants::pageSize, ptrSize); + EXPECT_EQ(heapBase + heapSize - ptrSize, ptr); + EXPECT_EQ(0u, heapAllocator.getFreedChunksBig().size()); + EXPECT_EQ(heapSize - ptrSize, heapAllocator.getavailableSize()); + + ptrSize = 8 * MemoryConstants::pageSize; + ptr = heapAllocator.allocateWithCustomAlignment(ptrSize, customAlignment); + EXPECT_EQ(heapBase + heapSize - 2 * ptrSize, heapAllocator.getRightBound()); + EXPECT_EQ(heapBase, heapAllocator.getLeftBound()); + EXPECT_EQ(8 * MemoryConstants::pageSize, ptrSize); + EXPECT_EQ(heapBase + heapSize - 2 * ptrSize, ptr); + EXPECT_EQ(0u, heapAllocator.getFreedChunksBig().size()); + EXPECT_EQ(heapSize - 2 * ptrSize, heapAllocator.getavailableSize()); +} + +TEST(HeapAllocatorTest, givenUnalignedBoundWhenAllocatingWithCustomAlignmentFromLeftThenAlignBoundBeforeAllocation) { + const uint64_t heapBase = 0x100000llu; + const size_t heapSize = 1024u * 4096u; + HeapAllocatorUnderTest heapAllocator(heapBase, heapSize, allocationAlignment, 0); + + // Misalign the left bound + const size_t misaligningAllocationSize = 2 * MemoryConstants::pageSize; + size_t ptrSize = misaligningAllocationSize; + uint64_t ptr = heapAllocator.allocate(ptrSize); + EXPECT_EQ(heapBase, ptr); + EXPECT_EQ(misaligningAllocationSize, ptrSize); + EXPECT_EQ(heapBase + ptrSize, heapAllocator.getLeftBound()); + EXPECT_EQ(heapSize - misaligningAllocationSize, heapAllocator.getavailableSize()); + EXPECT_EQ(0u, heapAllocator.getFreedChunksBig().size()); + + // Allocate with alignment + const size_t customAlignment = 8 * MemoryConstants::pageSize; + const size_t alignedAllocationSize = 16 * MemoryConstants::pageSize; + ptrSize = alignedAllocationSize; + ptr = heapAllocator.allocateWithCustomAlignment(ptrSize, customAlignment); + EXPECT_EQ(alignedAllocationSize, ptrSize); + EXPECT_EQ(heapBase + customAlignment, ptr); + EXPECT_EQ(heapBase + customAlignment + alignedAllocationSize, heapAllocator.getLeftBound()); + EXPECT_EQ(heapSize - misaligningAllocationSize - alignedAllocationSize, heapAllocator.getavailableSize()); + EXPECT_EQ(1u, heapAllocator.getFreedChunksBig().size()); + + // Try to use w hole, we just created by aligning + const size_t additionalAllocationSize = customAlignment - misaligningAllocationSize; + ptrSize = additionalAllocationSize; + ptr = heapAllocator.allocate(ptrSize); + EXPECT_EQ(heapBase + misaligningAllocationSize, ptr); + EXPECT_EQ(additionalAllocationSize, ptrSize); + EXPECT_EQ(heapBase + customAlignment + alignedAllocationSize, heapAllocator.getLeftBound()); + EXPECT_EQ(heapSize - customAlignment - alignedAllocationSize, heapAllocator.getavailableSize()); + EXPECT_EQ(0u, heapAllocator.getFreedChunksBig().size()); +} + +TEST(HeapAllocatorTest, givenUnalignedBoundWhenAllocatingWithCustomAlignmentFromRightThenAlignBoundBeforeAllocation) { + const uint64_t heapBase = 0x100000llu; + const size_t heapSize = 1024u * 4096u; + HeapAllocatorUnderTest heapAllocator(heapBase, heapSize, allocationAlignment, std::numeric_limits::max()); + + // Misalign the right bound + const size_t misaligningAllocationSize = 2 * MemoryConstants::pageSize; + size_t ptrSize = misaligningAllocationSize; + uint64_t ptr = heapAllocator.allocate(ptrSize); + EXPECT_EQ(misaligningAllocationSize, ptrSize); + EXPECT_EQ(heapBase + heapSize - misaligningAllocationSize, ptr); + EXPECT_EQ(heapBase + heapSize - misaligningAllocationSize, heapAllocator.getRightBound()); + EXPECT_EQ(heapSize - misaligningAllocationSize, heapAllocator.getavailableSize()); + EXPECT_EQ(0u, heapAllocator.getFreedChunksSmall().size()); + + // Allocate with alignment + const size_t customAlignment = 8 * MemoryConstants::pageSize; + const size_t alignedAllocationSize = 16 * MemoryConstants::pageSize; + ptrSize = alignedAllocationSize; + ptr = heapAllocator.allocateWithCustomAlignment(ptrSize, customAlignment); + EXPECT_EQ(alignedAllocationSize, ptrSize); + EXPECT_EQ(heapBase + heapSize - customAlignment - alignedAllocationSize, ptr); + EXPECT_EQ(heapBase + heapSize - customAlignment - alignedAllocationSize, heapAllocator.getRightBound()); + EXPECT_EQ(heapSize - misaligningAllocationSize - alignedAllocationSize, heapAllocator.getavailableSize()); + EXPECT_EQ(1u, heapAllocator.getFreedChunksSmall().size()); + + // Try to use w hole, we just created by aligning + const size_t additionalAllocationSize = customAlignment - misaligningAllocationSize; + ptrSize = additionalAllocationSize; + ptr = heapAllocator.allocate(ptrSize); + EXPECT_EQ(heapBase + heapSize - customAlignment, ptr); + EXPECT_EQ(additionalAllocationSize, ptrSize); + EXPECT_EQ(heapBase + heapSize - customAlignment - alignedAllocationSize, heapAllocator.getRightBound()); + EXPECT_EQ(heapSize - customAlignment - alignedAllocationSize, heapAllocator.getavailableSize()); + EXPECT_EQ(0u, heapAllocator.getFreedChunksSmall().size()); +} + +TEST(HeapAllocatorTest, givenNoSpaceLeftWhenAllocatingWithCustomAlignmentFromLeftThenReturnZero) { + const uint64_t heapBase = 0x100000llu; + const size_t heapSize = 1024u * 4096u; + HeapAllocatorUnderTest heapAllocator(heapBase, heapSize, allocationAlignment, 0); + + const size_t customAlignment = 256 * MemoryConstants::pageSize; + const size_t alignedAllocationSize = 256 * MemoryConstants::pageSize; + size_t ptrSize = alignedAllocationSize; + + EXPECT_NE(0ull, heapAllocator.allocateWithCustomAlignment(ptrSize, customAlignment)); + EXPECT_EQ(heapBase + alignedAllocationSize, heapAllocator.getLeftBound()); + + EXPECT_NE(0ull, heapAllocator.allocateWithCustomAlignment(ptrSize, customAlignment)); + EXPECT_EQ(heapBase + 2 * alignedAllocationSize, heapAllocator.getLeftBound()); + + EXPECT_NE(0ull, heapAllocator.allocateWithCustomAlignment(ptrSize, customAlignment)); + EXPECT_EQ(heapBase + 3 * alignedAllocationSize, heapAllocator.getLeftBound()); + + EXPECT_NE(0ull, heapAllocator.allocateWithCustomAlignment(ptrSize, customAlignment)); + EXPECT_EQ(heapBase + 4 * alignedAllocationSize, heapAllocator.getLeftBound()); + + EXPECT_EQ(0ull, heapAllocator.allocateWithCustomAlignment(ptrSize, customAlignment)); + EXPECT_EQ(heapBase + 4 * alignedAllocationSize, heapAllocator.getLeftBound()); +} + +TEST(HeapAllocatorTest, givenNoSpaceLeftWhenAllocatingWithCustomAlignmentFromRightThenReturnZero) { + const uint64_t heapBase = 0x100000llu; + const size_t heapSize = 1024u * 4096u; + HeapAllocatorUnderTest heapAllocator(heapBase, heapSize, allocationAlignment, std::numeric_limits::max()); + + const size_t customAlignment = 256 * MemoryConstants::pageSize; + const size_t alignedAllocationSize = 256 * MemoryConstants::pageSize; + size_t ptrSize = alignedAllocationSize; + EXPECT_NE(0ull, heapAllocator.allocateWithCustomAlignment(ptrSize, customAlignment)); + EXPECT_EQ(heapBase + heapSize - alignedAllocationSize, heapAllocator.getRightBound()); + + EXPECT_NE(0ull, heapAllocator.allocateWithCustomAlignment(ptrSize, customAlignment)); + EXPECT_EQ(heapBase + heapSize - 2 * alignedAllocationSize, heapAllocator.getRightBound()); + + EXPECT_NE(0ull, heapAllocator.allocateWithCustomAlignment(ptrSize, customAlignment)); + EXPECT_EQ(heapBase + heapSize - 3 * alignedAllocationSize, heapAllocator.getRightBound()); + + EXPECT_NE(0ull, heapAllocator.allocateWithCustomAlignment(ptrSize, customAlignment)); + EXPECT_EQ(heapBase + heapSize - 4 * alignedAllocationSize, heapAllocator.getRightBound()); + + EXPECT_EQ(0ull, heapAllocator.allocateWithCustomAlignment(ptrSize, customAlignment)); + EXPECT_EQ(heapBase + heapSize - 4 * alignedAllocationSize, heapAllocator.getRightBound()); +} + +TEST(HeapAllocatorTest, givenNoSpaceLeftAfterAligningWhenAllocatingWithCustomAlignmentFromLeftThenReturnZero) { + const uint64_t heapBase = 0x100000llu; + const size_t heapSize = 1024u * 4096u; + HeapAllocatorUnderTest heapAllocator(heapBase, heapSize, allocationAlignment, sizeThreshold); + const size_t alignedAllocationSize = 64 * MemoryConstants::pageSize; + + // First create a state, where we have desired size free, but not aligned + size_t ptrSize = 16 * MemoryConstants::pageSize; + EXPECT_NE(0ull, heapAllocator.allocate(ptrSize)); + EXPECT_EQ(heapBase + heapSize - 16 * MemoryConstants::pageSize, heapAllocator.getRightBound()); + ptrSize = heapSize - alignedAllocationSize - ptrSize; + EXPECT_NE(0ull, heapAllocator.allocate(ptrSize)); + EXPECT_EQ(heapBase + ptrSize, heapAllocator.getLeftBound()); + EXPECT_EQ(alignedAllocationSize, heapAllocator.getavailableSize()); + + // Aligned allocation should fail + ptrSize = alignedAllocationSize; + EXPECT_EQ(0ull, heapAllocator.allocateWithCustomAlignment(ptrSize, ptrSize)); + EXPECT_EQ(alignedAllocationSize, heapAllocator.getavailableSize()); + + // Unaligned allocation can be done + ptrSize = alignedAllocationSize; + EXPECT_NE(0ull, heapAllocator.allocate(ptrSize)); + EXPECT_EQ(0ull, heapAllocator.getavailableSize()); +} + +TEST(HeapAllocatorTest, givenNoSpaceLeftAfterAligningWhenAllocatingWithCustomAlignmentFromRightThenReturnZero) { + const uint64_t heapBase = 0x100000llu; + const size_t heapSize = 1024u * 4096u; + HeapAllocatorUnderTest heapAllocator(heapBase, heapSize, allocationAlignment, sizeThreshold); + const size_t alignedAllocationSize = 8 * MemoryConstants::pageSize; + + // First create a state, where we have desired size free, but not aligned + size_t ptrSize = 4 * MemoryConstants::pageSize; + EXPECT_NE(0ull, heapAllocator.allocate(ptrSize)); + EXPECT_EQ(heapBase + heapSize - 4 * MemoryConstants::pageSize, heapAllocator.getRightBound()); + ptrSize = heapSize - alignedAllocationSize - ptrSize; + EXPECT_NE(0ull, heapAllocator.allocate(ptrSize)); + EXPECT_EQ(heapBase + ptrSize, heapAllocator.getLeftBound()); + EXPECT_EQ(alignedAllocationSize, heapAllocator.getavailableSize()); + + // Aligned allocation should fail + ptrSize = alignedAllocationSize; + EXPECT_EQ(0ull, heapAllocator.allocateWithCustomAlignment(ptrSize, ptrSize)); + EXPECT_EQ(alignedAllocationSize, heapAllocator.getavailableSize()); + + // Unaligned allocation can be done + ptrSize = alignedAllocationSize; + EXPECT_NE(0ull, heapAllocator.allocate(ptrSize)); + EXPECT_EQ(0ull, heapAllocator.getavailableSize()); +} + +TEST(HeapAllocatorTest, givenSizeNotAlignedToCustomAlignmentWhenAllocatingMemoryWithCustomAlignmentThenDoNotAlignToCustomAlignment) { + const uint64_t heapBase = 0x100000llu; + const size_t heapSize = 1024u * 4096u; + HeapAllocatorUnderTest heapAllocator(heapBase, heapSize, allocationAlignment, sizeThreshold); + + const size_t customAlignment = 32 * MemoryConstants::pageSize; + size_t ptrSize = 49 * MemoryConstants::pageSize; + uint64_t ptr = heapAllocator.allocateWithCustomAlignment(ptrSize, customAlignment); + EXPECT_EQ(heapBase + heapSize, heapAllocator.getRightBound()); + EXPECT_EQ(heapBase + ptrSize, heapAllocator.getLeftBound()); + EXPECT_EQ(49 * MemoryConstants::pageSize, ptrSize); + EXPECT_EQ(heapBase, ptr); + EXPECT_EQ(0u, heapAllocator.getFreedChunksBig().size()); + EXPECT_EQ(heapSize - ptrSize, heapAllocator.getavailableSize()); +} + +TEST(HeapAllocatorTest, givenSizeNotAlignedToBaseAllocatorAlignmentWhenAllocatingMemoryWithCustomAlignmentThenDoNotAlignToBaseAlignment) { + const uint64_t heapBase = 0x100000llu; + const size_t heapSize = 1024u * 4096u; + HeapAllocatorUnderTest heapAllocator(heapBase, heapSize, allocationAlignment, 0); + + const size_t customAlignment = 32 * MemoryConstants::pageSize; + size_t ptrSize = 1; + uint64_t ptr = heapAllocator.allocateWithCustomAlignment(ptrSize, customAlignment); + EXPECT_EQ(heapBase + heapSize, heapAllocator.getRightBound()); + EXPECT_EQ(heapBase + ptrSize, heapAllocator.getLeftBound()); + EXPECT_EQ(MemoryConstants::pageSize, ptrSize); + EXPECT_EQ(heapBase, ptr); + EXPECT_EQ(0u, heapAllocator.getFreedChunksBig().size()); + EXPECT_EQ(heapSize - ptrSize, heapAllocator.getavailableSize()); +} + +TEST(HeapAllocatorTest, givenAlignedFreedChunkAvailableWhenAllocatingMemoryWithCustomAlignmentFromLeftThenReturnUseFreedChunk) { + const uint64_t heapBase = 0x100000llu; + const size_t heapSize = 1024u * 4096u; + HeapAllocatorUnderTest heapAllocator(heapBase, heapSize, allocationAlignment, sizeThreshold); + + // First create an aligned freed chunk + size_t ptrSize = 32 * MemoryConstants::pageSize; + const uint64_t freeChunkAddress = heapAllocator.allocate(ptrSize); + heapAllocator.allocate(ptrSize); + heapAllocator.free(freeChunkAddress, ptrSize); + EXPECT_EQ(heapBase + 2 * ptrSize, heapAllocator.getLeftBound()); + EXPECT_EQ(1u, heapAllocator.getFreedChunksBig().size()); + EXPECT_EQ(heapSize - ptrSize, heapAllocator.getavailableSize()); + + // Allocate with custom alignment using the freed chunk + const size_t customAlignment = 32 * MemoryConstants::pageSize; + ptrSize = 32 * MemoryConstants::pageSize; + uint64_t ptr = heapAllocator.allocateWithCustomAlignment(ptrSize, customAlignment); + EXPECT_EQ(heapBase + 2 * ptrSize, heapAllocator.getLeftBound()); + EXPECT_EQ(0u, heapAllocator.getFreedChunksBig().size()); + EXPECT_EQ(freeChunkAddress, ptr); + EXPECT_EQ(heapSize - 2 * ptrSize, heapAllocator.getavailableSize()); +} + +TEST(HeapAllocatorTest, givenAlignedFreedChunkSlightlyBiggerThanAllocationeWhenAllocatingMemoryWithCustomAlignmentFromLeftThenUseEntireFreedChunk) { + const uint64_t heapBase = 0x100000llu; + const size_t heapSize = 1024u * 4096u; + HeapAllocatorUnderTest heapAllocator(heapBase, heapSize, allocationAlignment, sizeThreshold); + + // First create an aligned freed chunk + size_t ptrSize = 48 * MemoryConstants::pageSize; + const uint64_t freeChunkAddress = heapAllocator.allocate(ptrSize); + heapAllocator.allocate(ptrSize); + heapAllocator.free(freeChunkAddress, ptrSize); + EXPECT_EQ(heapBase + 2 * ptrSize, heapAllocator.getLeftBound()); + EXPECT_EQ(1u, heapAllocator.getFreedChunksBig().size()); + EXPECT_EQ(heapSize - ptrSize, heapAllocator.getavailableSize()); + + // Allocate with custom alignment using the freed chunk + const size_t customAlignment = 32 * MemoryConstants::pageSize; + size_t ptrSize2 = 32 * MemoryConstants::pageSize; + uint64_t ptr = heapAllocator.allocateWithCustomAlignment(ptrSize2, customAlignment); + EXPECT_EQ(ptrSize, ptrSize2); + EXPECT_EQ(heapBase + 2 * ptrSize, heapAllocator.getLeftBound()); + EXPECT_EQ(0u, heapAllocator.getFreedChunksBig().size()); + EXPECT_EQ(freeChunkAddress, ptr); + EXPECT_EQ(heapSize - 2 * ptrSize, heapAllocator.getavailableSize()); +} + +TEST(HeapAllocatorTest, givenAlignedFreedChunkTwoTimesBiggerThanAllocationeWhenAllocatingMemoryWithCustomAlignmentFromLeftThenUseAPortionOfTheFreedChunk) { + const uint64_t heapBase = 0x100000llu; + const size_t heapSize = 1024u * 4096u; + HeapAllocatorUnderTest heapAllocator(heapBase, heapSize, allocationAlignment, sizeThreshold); + + // First create an aligned freed chunk + size_t ptrSize = 64 * MemoryConstants::pageSize; + const uint64_t freeChunkAddress = heapAllocator.allocate(ptrSize); + heapAllocator.allocate(ptrSize); + heapAllocator.free(freeChunkAddress, ptrSize); + EXPECT_EQ(heapBase + 2 * ptrSize, heapAllocator.getLeftBound()); + EXPECT_EQ(1u, heapAllocator.getFreedChunksBig().size()); + EXPECT_EQ(heapSize - ptrSize, heapAllocator.getavailableSize()); + + // Allocate with custom alignment using the freed chunk + const size_t customAlignment = 32 * MemoryConstants::pageSize; + size_t ptrSize2 = 32 * MemoryConstants::pageSize; + uint64_t ptr = heapAllocator.allocateWithCustomAlignment(ptrSize2, customAlignment); + EXPECT_EQ(32 * MemoryConstants::pageSize, ptrSize2); + EXPECT_EQ(heapBase + 2 * ptrSize, heapAllocator.getLeftBound()); + EXPECT_EQ(1u, heapAllocator.getFreedChunksBig().size()); + EXPECT_EQ(freeChunkAddress + 32 * MemoryConstants::pageSize, ptr); + EXPECT_EQ(heapSize - ptrSize - ptrSize2, heapAllocator.getavailableSize()); +} + +TEST(HeapAllocatorTest, givenUnalignedFreedChunkAvailableWhenAllocatingMemoryWithCustomAlignmentFromLeftThenDoNotUseFreedChunk) { + const uint64_t heapBase = 0x100000llu; + const size_t heapSize = 1024u * 4096u; + HeapAllocatorUnderTest heapAllocator(heapBase, heapSize, allocationAlignment, 1); + + // First create an unaligned freed chunk + size_t ptrSize = MemoryConstants::pageSize; + heapAllocator.allocate(ptrSize); + ptrSize = 32 * MemoryConstants::pageSize; + const uint64_t freeChunkAddress = heapAllocator.allocate(ptrSize); + heapAllocator.allocate(ptrSize); + heapAllocator.free(freeChunkAddress, ptrSize); + EXPECT_EQ(heapBase + 65 * MemoryConstants::pageSize, heapAllocator.getLeftBound()); + EXPECT_EQ(1u, heapAllocator.getFreedChunksBig().size()); + EXPECT_EQ(heapSize - ptrSize - MemoryConstants::pageSize, heapAllocator.getavailableSize()); + + // Allocate with custom alignment, the freed chunk cannot be used + const size_t customAlignment = 32 * MemoryConstants::pageSize; + ptrSize = 32 * MemoryConstants::pageSize; + uint64_t ptr = heapAllocator.allocateWithCustomAlignment(ptrSize, customAlignment); + EXPECT_EQ(heapBase + 128 * MemoryConstants::pageSize, heapAllocator.getLeftBound()); + EXPECT_EQ(heapBase + 96 * MemoryConstants::pageSize, ptr); + EXPECT_EQ(2u, heapAllocator.getFreedChunksBig().size()); + EXPECT_EQ(heapSize - 2 * ptrSize - MemoryConstants::pageSize, heapAllocator.getavailableSize()); + + // Allocate without custom alignment, the freed chunk can be used + ptrSize = 32 * MemoryConstants::pageSize; + ptr = heapAllocator.allocate(ptrSize); + EXPECT_EQ(heapBase + 128 * MemoryConstants::pageSize, heapAllocator.getLeftBound()); + EXPECT_EQ(freeChunkAddress, ptr); + EXPECT_EQ(1u, heapAllocator.getFreedChunksBig().size()); + EXPECT_EQ(heapSize - 3 * ptrSize - MemoryConstants::pageSize, heapAllocator.getavailableSize()); +}