// Mark the given block as free. The given block must have been // wholly allocated before, but it's legal to release big // allocations in smaller non-overlapping sub-blocks. public void Release(HeapBlock block) { // Merge the newly released block with any free blocks on either // side of it. Remove those blocks from the list of free blocks, // as they no longer exist as separate blocks. block = Coalesce(block); SizeBin bin = new SizeBin(block); int index = FindSmallestSufficientBin(bin); // If the exact bin doesn't exist, add it. if (index >= m_SizeBins.Length || bin.CompareTo(m_SizeBins[index]) != 0) { index = AddNewBin(ref bin, index); } m_Blocks[m_SizeBins[index].blocksId].Push(block); m_Free += block.Length; #if DEBUG_ASSERTS Assert.IsFalse(m_FreeEndpoints.ContainsKey(block.begin)); Assert.IsFalse(m_FreeEndpoints.ContainsKey(block.end)); #endif // Store both endpoints of the free block to the hashmap for // easy coalescing. m_FreeEndpoints[block.begin] = block.end; m_FreeEndpoints[block.end] = block.begin; }
private HeapBlock CutAllocationFromBlock(SizeBin allocation, HeapBlock block) { #if DEBUG_ASSERTS Assert.IsTrue(block.Length >= allocation.Size, "Block is not large enough."); #endif // If the match is exact, no need to cut. if (allocation.Size == block.Length) { return(block); } // Otherwise, round the begin to next multiple of alignment, and then cut away the required size, // potentially leaving empty space on both ends. ulong alignedBegin = NextAligned(block.begin, allocation.AlignmentLog2); ulong alignedEnd = alignedBegin + allocation.Size; if (alignedBegin > block.begin) { Release(new HeapBlock(block.begin, alignedBegin)); } if (alignedEnd < block.end) { Release(new HeapBlock(alignedEnd, block.end)); } return(new HeapBlock(alignedBegin, alignedEnd)); }
// Attempt to allocate a block from the heap with at least the given // size and alignment. The allocated block might be bigger than the // requested size, but will never be smaller. // If the allocation fails, an empty block is returned. public HeapBlock Allocate(ulong size, uint alignment = 1) { // Always use at least the minimum alignment, and round all sizes // to multiples of the minimum alignment. size = NextAligned(size, m_MinimumAlignmentLog2); alignment = math.max(alignment, MinimumAlignment); SizeBin allocBin = new SizeBin(size, alignment); int index = FindSmallestSufficientBin(allocBin); while (index < m_SizeBins.Length) { SizeBin bin = m_SizeBins[index]; if (CanFitAllocation(allocBin, bin)) { HeapBlock block = PopBlockFromBin(bin, index); return(CutAllocationFromBlock(allocBin, block)); } else { ++index; } } return(new HeapBlock()); }
public SizeBin(HeapBlock block) { int alignLog2 = math.tzcnt(block.begin); alignLog2 = math.min(MaxAlignmentLog2, alignLog2); sizeClass = (block.Length << AlignmentBits) | (uint)alignLog2; blocksId = -1; }
private unsafe HeapBlock PopBlockFromBin(SizeBin bin, int index) { HeapBlock block = m_Blocks[bin.blocksId].Pop(); RemoveEndpoints(block); m_Free -= block.Length; RemoveBinIfEmpty(bin, index); return(block); }
public bool Remove(HeapBlock block) { for (int i = 0; i < m_Blocks->Length; ++i) { if (block.CompareTo(Block(i)) == 0) { m_Blocks->RemoveAtSwapBack <HeapBlock>(i); return(true); } } return(false); }
public HeapBlock Pop() { int len = m_Blocks->Length; if (len == 0) { return(new HeapBlock()); } HeapBlock block = Block(len - 1); m_Blocks->Resize <HeapBlock>(len - 1); return(block); }
private void RemoveFreeBlock(HeapBlock block) { RemoveEndpoints(block); SizeBin bin = new SizeBin(block); int index = FindSmallestSufficientBin(bin); #if DEBUG_ASSERTS Assert.IsTrue(index >= 0 && m_SizeBins[index].sizeClass == bin.sizeClass, "Expected to find exact match for size bin since block was supposed to exist"); #endif bool removed = m_Blocks[m_SizeBins[index].blocksId].Remove(block); RemoveBinIfEmpty(m_SizeBins[index], index); #if DEBUG_ASSERTS Assert.IsTrue(removed, "Block was supposed to exist"); #endif m_Free -= block.Length; }
private HeapBlock Coalesce(HeapBlock block, ulong endpoint) { if (m_FreeEndpoints.TryGetValue(endpoint, out ulong otherEnd)) { #if DEBUG_ASSERTS if (math.min(endpoint, otherEnd) == block.begin && math.max(endpoint, otherEnd) == block.end) { UnityEngine.Debug.Log("Inconsistent free block endpoint data"); } Assert.IsFalse( math.min(endpoint, otherEnd) == block.begin && math.max(endpoint, otherEnd) == block.end, "Block was already freed."); #endif if (endpoint == block.begin) { #if DEBUG_ASSERTS Assert.IsTrue(otherEnd < endpoint, "Unexpected endpoints"); #endif var coalesced = new HeapBlock(otherEnd, block.begin); RemoveFreeBlock(coalesced); return(new HeapBlock(coalesced.begin, block.end)); } else { #if DEBUG_ASSERTS Assert.IsTrue(otherEnd > endpoint, "Unexpected endpoints"); #endif var coalesced = new HeapBlock(block.end, otherEnd); RemoveFreeBlock(coalesced); return(new HeapBlock(block.begin, coalesced.end)); } } else { return(block); } }
// Attempt to grow or shrink the allocator. Growing always succeeds, // but shrinking might fail if the end of the heap is allocated. // TODO: Shrinking not implemented. public bool Resize(ulong newSize) { // Same size? No need to do anything. if (newSize == m_Size) { return(true); } // Growing? Release a block past the end. else if (newSize > m_Size) { ulong increase = newSize - m_Size; HeapBlock newSpace = HeapBlock.OfSize(m_Size, increase); Release(newSpace); m_Size = newSize; return(true); } // Shrinking? TODO else { return(false); } }
private HeapBlock Coalesce(HeapBlock block) { block = Coalesce(block, block.begin); // Left block = Coalesce(block, block.end); // Right return(block); }
private void RemoveEndpoints(HeapBlock block) { m_FreeEndpoints.Remove(block.begin); m_FreeEndpoints.Remove(block.end); }
// TODO: Priority queue semantics for address-ordered allocation public void Push(HeapBlock block) { m_Blocks->Add(block); }