// 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);
            }