Example #1
0
        public void ReclaimedBlockShouldReinitializeToDefault()
        {
            using var memory = new HeapAllocator(_logFactory);


            var handle_50331651_48 = Take(memory, 9150);

            memory.Validate("1");

            memory.Free(ref handle_50331651_48);
            memory.Validate("2");

            var handle_16777217_18 = Take(memory, 33831);

            memory.Validate("3");

            memory.Free(ref handle_16777217_18);
            memory.Validate("4");

            var handle_67108866_64 = Take(memory, 22);

            memory.Validate("5");

            memory.Free(ref handle_67108866_64);
            memory.Validate("6");
        }
 private void CheckCapacity()
 {
     if (_queues[0].Count <= 5)
     {
         // -------- Multiply by 2 the capacity
         LinkedIndexPoolEntry *olds = _entries;
         int newCapacity            = _capacity * 2;
         _entries = (LinkedIndexPoolEntry *)HeapAllocator.New(sizeof(LinkedIndexPoolEntry) * newCapacity);
         MemoryHelper.Copy((byte *)olds, (byte *)_entries, sizeof(LinkedIndexPoolEntry) * _capacity);
         for (int i = _capacity; i < newCapacity; i++)
         {
             _entries[i].Previous = i - 1;
             _entries[i].Next     = i + 1;
             _entries[i].Index    = i;
             _entries[i].Queue    = 0;
         }
         _entries[newCapacity - 1].Next = -1;
         // -------- Fix 0 queue :
         _entries[_queues[0].Last].Next = _capacity;
         _entries[_capacity].Previous   = _queues[0].Last;
         _queues[0].Last   = newCapacity - 1;
         _queues[0].Count += _capacity;
         // --------
         _capacity = newCapacity;
         HeapAllocator.Free((byte *)olds);
         CheckCoherency();
     }
 }
        public void ShouldAssignManyWithOne()
        {
            using var memory = new HeapAllocator(_logFactory);
            using var em     = new EntityManager(_logFactory, memory);
            using var buffer = new EntityCommandBuffer(em, memory);

            var spec = new EntitySpec(ComponentType <Velocity> .Type);

            var       position = new Position(10, 10);
            const int samples  = 32;

            var ids = new uint[samples];

            em.Create(spec, ids);

            buffer.Assign(ids, position);
            buffer.Execute();

            em.EntityCount.ShouldBe(32);
            em.EntityArrays.Count.ShouldBe(2);
            em.EntityArrays[0].EntityCount.ShouldBe(0);
            em.EntityArrays[1].EntityCount.ShouldBe(32);
            var positions = ids.Select(id => em.Get <Position>(id)).ToArray();

            positions.ShouldAllBe(x => x == position);
        }
        public void ShouldReplaceManyWithMany()
        {
            using var memory = new HeapAllocator(_logFactory);
            using var em     = new EntityManager(_logFactory, memory);
            using var buffer = new EntityCommandBuffer(em, memory);

            var spec = new EntitySpec(ComponentType <Position> .Type, ComponentType <Velocity> .Type);

            const int samples   = 32;
            var       positions = Enumerable.Range(0, samples).Select(x => new Position(x, 10)).ToArray();

            var ids = new uint[samples];

            em.Create(spec, ids);

            buffer.Replace(ids, positions.AsSpan());
            buffer.Execute();

            em.EntityCount.ShouldBe(32);
            em.EntityArrays[0].EntityCount.ShouldBe(32);

            var positionResults = ids.Select(id => em.Get <Position>(id)).ToArray();

            positionResults.ShouldBe(positions);
        }
Example #5
0
        public void BasicTests()
        {
            var allocator = new HeapAllocator(1000);

            allocator.DebugValidateInternalState();

            Assert.AreEqual(allocator.FreeSpace, 1000);

            HeapBlock b10 = allocator.Allocate(10);

            allocator.DebugValidateInternalState();
            HeapBlock b100 = allocator.Allocate(100);

            allocator.DebugValidateInternalState();

            // Check that the allocations have sufficient size.
            Assert.GreaterOrEqual(b10.Length, 10);
            Assert.GreaterOrEqual(b100.Length, 100);

            // Check that the amount of free space has decreased accordingly.
            Assert.LessOrEqual(allocator.FreeSpace, 1000 - 100 - 10);

            allocator.Release(b10);
            allocator.DebugValidateInternalState();
            allocator.Release(b100);
            allocator.DebugValidateInternalState();

            // Everything should now be freed.
            Assert.AreEqual(allocator.FreeSpace, 1000);
            allocator.Dispose();
        }
        public void ShouldDeleteManyEntities()
        {
            using var memory = new HeapAllocator(_logFactory);
            using var em     = new EntityManager(_logFactory, memory);
            using var buffer = new EntityCommandBuffer(em, memory);

            var spec = new EntitySpec(ComponentType <Position> .Type, ComponentType <Velocity> .Type);

            const int samples = 32;

            var ids = new uint[samples];

            em.Create(spec, ids);

            var idsToDelete = new[] { ids[0], ids[11], ids[22], ids[23], ids[3], ids[2], ids[15], ids[17], ids[29], ids[21] };

            buffer.Delete(idsToDelete);
            buffer.Execute();

            em.EntityCount.ShouldBe(samples - idsToDelete.Length);
            em.EntityArrays[0].EntityCount.ShouldBe(samples - idsToDelete.Length);
            var entities = em.EntityArrays[0].AllChunks[0].Entities.ToArray();

            var remainingIds = entities.Select(x => x.ID).ToArray();

            remainingIds.Any(x => idsToDelete.Contains(x)).ShouldBe(false);

            var otherIds = ids.Where(x => !idsToDelete.Contains(x)).ToArray();

            remainingIds.All(x => otherIds.Contains(x)).ShouldBe(true);
        }
Example #7
0
        public void Coalescing()
        {
            var allocator = new HeapAllocator(100);

            allocator.DebugValidateInternalState();

            // Try to allocate ten blocks. These should succeed, because the heap should not be fragmented yet.
            var blocks10 = Enumerable.Range(0, 10).Select(x => allocator.Allocate(10)).ToArray();

            allocator.DebugValidateInternalState();

            Assert.IsTrue(blocks10.All(b => b.Length == 10));
            Assert.IsTrue(allocator.Full);

            // Release all of them.
            foreach (var b in blocks10)
            {
                allocator.Release(b);
                allocator.DebugValidateInternalState();
            }

            // Now try to allocate the entire heap. It should succeed, because everything has been freed.
            Assert.AreEqual(allocator.Allocate(100).Length, 100);
            allocator.DebugValidateInternalState();
            allocator.Dispose();
        }
 public void Release()
 {
     if (_entries != null)
     {
         HeapAllocator.Free((byte *)_entries);
         _entries = null;
     }
 }
 public void EntityPoolNewMemoryFailure()
 {
     using var heap = new HeapAllocator(_logFactory);
     using var arr  = new NativeArray <Entity>(heap, 4096);
     using var arr1 = new NativeArray <Entity>(heap, 4096);
     using var arr2 = new NativeArray <Entity>(heap, 4096);
     using var arr3 = new NativeArray <Entity>(heap, 4096);
 }
Example #10
0
        public void AllocateEntireHeap()
        {
            var allocator = new HeapAllocator(100);

            // Check that it's possible to allocate the entire heap.
            Assert.AreEqual(allocator.Allocate(100).Length, 100);
            allocator.Dispose();
        }
 public void Release()
 {
     if (_capacity > 0)
     {
         HeapAllocator.Free((byte *)_entries);
         HeapAllocator.Free((byte *)_queues);
         _queueStack.Release();
         _capacity = -1;
     }
 }
Example #12
0
        public void HeapShouldAllocate()
        {
            //arrange
            using var memory = new HeapAllocator(_logFactory);

            //act
            var handle = memory.Take(1024);

            //assert
            handle.Address.ShouldNotBe(IntPtr.Zero);
        }
 public StackedIndexPoolUnsafe(int capacity, int idOffset)
 {
     _idOffset = idOffset;
     _entries  = (int *)HeapAllocator.New(capacity * sizeof(int));
     for (int i = 0; i < capacity; i++)
     {
         _entries[i] = i + idOffset;
     }
     _bottom   = 0;
     _capacity = capacity;
 }
        public void ShouldRemoveManyWithMany()
        {
            using var memory = new HeapAllocator(_logFactory);
            using var em     = new EntityManager(_logFactory, memory);
            using var buffer = new EntityCommandBuffer(em, memory);

            var spec = new EntitySpec(ComponentType <Velocity> .Type, ComponentType <Position> .Type, ComponentType <Filler> .Type);

            const int samples = 32;

            var ids = new uint[samples];

            em.Create(spec, ids);

            var idsToChange  = new[] { ids[0], ids[11], ids[22], ids[23], ids[3], ids[2], ids[15], ids[17], ids[29], ids[21] };
            var remainingIds = ids.Where(x => !idsToChange.Contains(x)).ToArray();

            var typesToRemove = new[] { spec.ComponentTypes[1], spec.ComponentTypes[2] };

            buffer.Remove(idsToChange, typesToRemove);
            buffer.Execute();

            em.EntityCount.ShouldBe(samples);
            em.EntityArrays.Count.ShouldBe(2);
            em.EntityArrays[0].EntityCount.ShouldBe(samples - idsToChange.Length);
            em.EntityArrays[1].EntityCount.ShouldBe(idsToChange.Length);

            var entities0 = em.EntityArrays[0].AllChunks[0].Entities.ToArray();

            entities0.Any(x => idsToChange.Contains(x.ID)).ShouldBe(false);
            entities0.All(x => remainingIds.Contains(x.ID)).ShouldBe(true);

            var entities1 = em.EntityArrays[1].AllChunks[0].Entities.ToArray();

            entities1.Any(x => remainingIds.Contains(x.ID)).ShouldBe(false);
            entities1.All(x => idsToChange.Contains(x.ID)).ShouldBe(true);

            var positionResults = idsToChange.Select(id => em.Has <Position>(id)).ToArray();

            positionResults.ShouldAllBe(x => x == false);

            var fillerResults = idsToChange.Select(id => em.Has <Filler>(id)).ToArray();

            fillerResults.ShouldAllBe(x => x == false);

            var velocityResults = idsToChange.Select(id => em.Has <Velocity>(id)).ToArray();

            velocityResults.ShouldAllBe(x => x == true);
        }
        public void ShouldCreateManyEntity()
        {
            using var memory = new HeapAllocator(_logFactory);
            using var em     = new EntityManager(_logFactory, memory);
            using var buffer = new EntityCommandBuffer(em, memory, 8);

            var spec = new EntitySpec(ComponentType <Position> .Type, ComponentType <Velocity> .Type);

            buffer.Create(spec, 16384);

            buffer.Execute();

            em.EntityCount.ShouldBe(16384);
            em.EntityArrays[0].EntityCount.ShouldBe(16384);
        }
 private void EnsureCpacity(int n)
 {
     if (n >= _capacity)
     {
         int  newCapacity = _capacity * 2;
         int *newEntries  = (int *)HeapAllocator.New(newCapacity * sizeof(int));
         MemoryHelper.Copy((byte *)_entries, (byte *)newEntries, _capacity * sizeof(int));
         HeapAllocator.Free((byte *)_entries);
         for (int i = _capacity; i < newCapacity; i++)
         {
             newEntries[i] = i + _idOffset;
         }
         _entries  = newEntries;
         _capacity = newCapacity;
     }
 }
        public void ShouldReplaceOne()
        {
            using var memory = new HeapAllocator(_logFactory);
            using var em     = new EntityManager(_logFactory, memory);
            using var buffer = new EntityCommandBuffer(em, memory);

            var spec = new EntitySpec(ComponentType <Position> .Type, ComponentType <Velocity> .Type);

            var id = em.Create(spec);

            buffer.Replace(id, new Position(10, 10));

            buffer.Execute();

            em.EntityCount.ShouldBe(1);
            em.EntityArrays[0].EntityCount.ShouldBe(1);
            em.Get <Position>(id).ShouldBe(new Position(10, 10));
        }
        public void ShouldRemoveOne()
        {
            using var memory = new HeapAllocator(_logFactory);
            using var em     = new EntityManager(_logFactory, memory);
            using var buffer = new EntityCommandBuffer(em, memory);

            var spec = new EntitySpec(ComponentType <Velocity> .Type, ComponentType <Position> .Type);

            var id = em.Create(spec);

            buffer.Remove <Position>(id);

            buffer.Execute();

            em.EntityCount.ShouldBe(1);
            em.EntityArrays.Count.ShouldBe(2);
            em.EntityArrays[0].EntityCount.ShouldBe(0);
            em.EntityArrays[1].EntityCount.ShouldBe(1);
            em.Has <Position>(id).ShouldBe(false);
        }
Example #19
0
        public void OrphanBlockWithSizeZeroShouldReclaim()
        {
            using var memory = new HeapAllocator(_logFactory);

            var handle0 = memory.Take(24);
            //memory.Validate();

            var handle1 = memory.Take(24);
            //memory.Validate();

            var handle2 = memory.Take(24);
            //memory.Validate();

            var handle4 = memory.Take(24);

            //memory.Validate();

            memory.Free(ref handle1);
            //memory.Validate();

            memory.Free(ref handle0);
            //memory.Validate();

            var handle5 = memory.Take(24);
            //memory.Validate();
            var handle6 = memory.Take(24);

            //memory.Validate();

            memory.Free(ref handle4);
            //memory.Validate();

            memory.Free(ref handle2);
            //memory.Validate();

            memory.Free(ref handle6);
            //memory.Validate();

            memory.Free(ref handle5);
            //memory.Validate();
        }
        public void ShouldDeleteEntityOnLastComponentOne()
        {
            using var memory = new HeapAllocator(_logFactory);
            using var em     = new EntityManager(_logFactory, memory);
            using var buffer = new EntityCommandBuffer(em, memory);

            var spec = new EntitySpec(ComponentType <Position> .Type, ComponentType <Velocity> .Type);

            const int samples = 32;

            var ids = new uint[samples];

            em.Create(spec, ids);

            buffer.Remove(ids, spec.ComponentTypes);
            buffer.Execute();

            em.EntityCount.ShouldBe(0);
            em.EntityArrays.Count.ShouldBe(1);
            em.EntityArrays[0].EntityCount.ShouldBe(0);
        }
        public void ShouldUpdateManyWithMany()
        {
            using var memory = new HeapAllocator(_logFactory);
            using var em     = new EntityManager(_logFactory, memory);
            using var buffer = new EntityCommandBuffer(em, memory);

            var spec = new EntitySpec(ComponentType <Velocity> .Type);

            const int samples = 32;

            var ids = new uint[samples];

            em.Create(spec, ids);

            var idsToChange  = new[] { ids[0], ids[11], ids[22], ids[23], ids[3], ids[2], ids[15], ids[17], ids[29], ids[21] };
            var remainingIds = ids.Where(x => !idsToChange.Contains(x)).ToArray();
            var positions    = idsToChange.Select(x => new Position((int)x, 10)).ToArray();

            buffer.Update(idsToChange, positions.AsSpan());
            buffer.Execute();

            em.EntityCount.ShouldBe(samples);
            em.EntityArrays.Count.ShouldBe(2);
            em.EntityArrays[0].EntityCount.ShouldBe(samples - idsToChange.Length);
            em.EntityArrays[1].EntityCount.ShouldBe(idsToChange.Length);

            var entities0 = em.EntityArrays[0].AllChunks[0].Entities.ToArray();

            entities0.Any(x => idsToChange.Contains(x.ID)).ShouldBe(false);
            entities0.All(x => remainingIds.Contains(x.ID)).ShouldBe(true);

            var entities1 = em.EntityArrays[1].AllChunks[0].Entities.ToArray();

            entities1.Any(x => remainingIds.Contains(x.ID)).ShouldBe(false);
            entities1.All(x => idsToChange.Contains(x.ID)).ShouldBe(true);

            var positionResults = idsToChange.Select(id => em.Get <Position>(id)).ToArray();

            positionResults.ShouldBe(positions);
        }
        /********************************************************************************/
        // -------- Queues
        public int GetFreeQueue()
        {
            int tmp = _queueStack.Pop();

            if (_queueStack.Capacity > _queueCount)
            {
                int newQueueCount = _queueStack.Capacity;
                LinkedIndexPoolQueue *_oldQueues = _queues;
                _queues = (LinkedIndexPoolQueue *)HeapAllocator.New(sizeof(LinkedIndexPoolQueue) * newQueueCount);
                for (int i = 0; i < _queueCount; i++)
                {
                    _queues[i] = _oldQueues[i];
                }
                for (int i = _queueCount; i < newQueueCount; i++)
                {
                    _queues[i].First = _queues[i].Last = -1;
                    _queues[i].Index = i;
                    _queues[i].Count = 0;
                }
            }
            return(tmp);
        }
 public LinkedIndexPool(int capacity, int queueCount, bool check = false)
 {
     if (capacity < 8)
     {
         capacity = 8;
     }
     if (queueCount < 2)
     {
         queueCount = 2;
     }
     // -------- Initialize entries
     _entries  = (LinkedIndexPoolEntry *)HeapAllocator.New(sizeof(LinkedIndexPoolEntry) * capacity);
     _capacity = capacity;
     for (int i = 0; i < capacity; i++)
     {
         _entries[i].Previous = i - 1;
         _entries[i].Next     = i + 1;
         _entries[i].Index    = i;
         _entries[i].Queue    = 0;
     }
     _entries[0].Previous        = -1;
     _entries[capacity - 1].Next = -1;
     // -------- Initialize queues
     _queues     = (LinkedIndexPoolQueue *)HeapAllocator.New(sizeof(LinkedIndexPoolQueue) * queueCount);
     _queueCount = queueCount;
     for (int i = 0; i < queueCount; i++)
     {
         _queues[i].First = _queues[i].Last = -1;
         _queues[i].Index = i;
         _queues[i].Count = 0;
     }
     _queues[0].First = 0;
     _queues[0].Last  = capacity - 1;
     _queues[0].Count = capacity;
     // -------- Initialize queues stack
     _queueStack = new StackedIndexPoolUnsafe(queueCount, 2);
     // -------- All is ok...
     _checkIt = check;
 }
Example #24
0
        private void LoadFromStream(Stream stream)
        {
            Quetzal quetzal = Quetzal.FromStream(stream);

            // make sure the save file matches the game file
            byte[] ifhd1 = quetzal["IFhd"];
            byte[] ifhd2 = image.GetOriginalIFHD();
            if (ifhd1 == null || ifhd1.Length != ifhd2.Length)
                throw new ArgumentException("Missing or invalid IFhd block");

            for (int i = 0; i < ifhd1.Length; i++)
                if (ifhd1[i] != ifhd2[i])
                    throw new ArgumentException("Saved game doesn't match this story file");

            // load the stack
            byte[] newStack = quetzal["Stks"];
            if (newStack == null)
                throw new ArgumentException("Missing Stks block");

            Array.Copy(newStack, stack, newStack.Length);
            sp = (uint)newStack.Length;

            // save the protected area of RAM
            byte[] protectedRam = new byte[protectionLength];
            image.ReadRAM(protectionStart, protectionLength, protectedRam);

            // load the contents of RAM, preferring a compressed chunk
            byte[] origRam = image.GetOriginalRAM();
            byte[] delta = quetzal["CMem"];
            if (delta != null)
            {
                byte[] newRam = Quetzal.DecompressMemory(origRam, delta);
                image.SetRAM(newRam, false);
            }
            else
            {
                // look for an uncompressed chunk
                byte[] newRam = quetzal["UMem"];
                if (newRam == null)
                    throw new ArgumentException("Missing CMem/UMem blocks");
                else
                    image.SetRAM(newRam, true);
            }

            // restore protected RAM
            image.WriteRAM(protectionStart, protectedRam);

            // pop a call stub to restore registers
            CallStub stub = PopCallStub();
            pc = stub.PC;
            fp = stub.FramePtr;
            frameLen = ReadFromStack(fp);
            localsPos = ReadFromStack(fp + 4);
            execMode = ExecutionMode.Code;

            // restore the heap if available
            if (quetzal.Contains("MAll"))
            {
                heap = new HeapAllocator(quetzal["MAll"], HandleHeapMemoryRequest);
                if (heap.BlockCount == 0)
                    heap = null;
                else
                    heap.MaxSize = maxHeapSize;
            }

            // give the original save opcode a result of -1 to show that it's been restored
            PerformDelayedStore(stub.DestType, stub.DestAddr, 0xFFFFFFFF);
        }
Example #25
0
        private void op_malloc(uint[] args)
        {
            uint size = args[0];
            if ((int)size <= 0)
            {
                args[1] = 0;
                return;
            }

            if (heap == null)
            {
                uint oldEndMem = image.EndMem;
                heap = new HeapAllocator(oldEndMem, HandleHeapMemoryRequest);
                heap.MaxSize = maxHeapSize;
                args[1] = heap.Alloc(size);
                if (args[1] == 0)
                {
                    heap = null;
                    image.EndMem = oldEndMem;
                }
            }
            else
            {
                args[1] = heap.Alloc(size);
            }
        }
Example #26
0
        public void RandomStressTest()
        {
            const int HeapSize           = 1_000_000;
            const int NumBlocks          = 1_000;
            const int NumRounds          = 20;
            const int MaxAlloc           = 10_000;
            const int OperationsPerRound = 10_000;

            int numAllocs   = 0;
            int numReleases = 0;
            int numFailed   = 0;

            var rnd       = new System.Random(293875);
            var allocator = new HeapAllocator(HeapSize);
            var blocks    = Enumerable.Range(0, NumBlocks).Select(x => new HeapBlock()).ToArray();

            // Stress test the allocator by doing a bunch of random allocs and deallocs and
            // try to verify that allocator internal asserts don't fire, and free space behaves
            // as expected.

            for (int i = 0; i < NumRounds; ++i)
            {
                Assert.IsTrue(allocator.Empty);

                // Perform random alloc/dealloc operations
                for (int j = 0; j < OperationsPerRound; ++j)
                {
                    ulong before = allocator.FreeSpace;

                    int b = rnd.Next(NumBlocks);

                    int size = 0;
                    if (blocks[b].Empty)
                    {
                        size      = rnd.Next(1, MaxAlloc);
                        blocks[b] = allocator.Allocate((ulong)size);

                        if (blocks[b].Empty)
                        {
                            size = 0;
                            ++numFailed;
                        }
                        else
                        {
                            size = (int)blocks[b].Length;
                        }

                        ++numAllocs;
                    }
                    else
                    {
                        size = -(int)blocks[b].Length;
                        allocator.Release(blocks[b]);
                        blocks[b] = new HeapBlock();

                        ++numReleases;
                    }

                    ulong after = allocator.FreeSpace;

                    Assert.AreEqual((long)after, (long)before - size);
                }

                for (int b = 0; b < NumBlocks; ++b)
                {
                    if (!blocks[b].Empty)
                    {
                        allocator.Release(blocks[b]);
                        blocks[b] = new HeapBlock();
                    }
                }
                Assert.IsTrue(allocator.Empty);
            }

            Debug.Log($"Allocs: {numAllocs}, Releases: {numReleases}, Failed: {numFailed}");
            allocator.Dispose();
        }
Example #27
0
        public void AllocationsDontOverlap()
        {
            // Make sure that allocations given by the allocator are disjoint (i.e. don't alias).

            const int HeapSize           = 1_000_000;
            const int NumBlocks          = 1_000;
            const int MaxAlloc           = 10_000;
            const int OperationsPerRound = 10_000;

            var rnd       = new System.Random(9283572);
            var allocator = new HeapAllocator(HeapSize);
            var blocks    = Enumerable.Range(0, NumBlocks).Select(x => new HeapBlock()).ToArray();
            var inUse     = new ulong[HeapSize / 8 + 1];

            Func <ulong, (ulong, int)> qword = (ulong i) => (i / 64, (int)(i % 64));

            // Perform random alloc/dealloc operations
            for (int i = 0; i < OperationsPerRound; ++i)
            {
                int         b        = rnd.Next(NumBlocks);
                const ulong kAllOnes = ~0UL;

                int size = 0;
                if (blocks[b].Empty)
                {
                    size      = rnd.Next(1, MaxAlloc);
                    blocks[b] = allocator.Allocate((ulong)size);

                    // Mark the block as allocated, and check that it wasn't allocated.

                    // Do tests and sets entire qwords at a time so it's fast
                    var begin = qword(blocks[b].begin);
                    var end   = qword(blocks[b].end);

                    if (begin.Item1 == end.Item1)
                    {
                        ulong qw   = begin.Item1;
                        ulong mask = kAllOnes << begin.Item2;
                        mask &= ~(kAllOnes << end.Item2);
                        Assert.IsTrue((inUse[qw] & mask) == 0, "Elements were already allocated");
                        inUse[qw] |= mask;
                    }
                    else
                    {
                        ulong qw   = begin.Item1;
                        ulong mask = kAllOnes << begin.Item2;

                        Assert.IsTrue((inUse[qw] & mask) == 0, "Elements were already allocated");
                        inUse[qw] |= mask;

                        for (qw = begin.Item1 + 1; qw < end.Item1; ++qw)
                        {
                            mask = kAllOnes;
                            Assert.IsTrue((inUse[qw] & mask) == 0, "Elements were already allocated");
                            inUse[qw] |= mask;
                        }

                        qw   = end.Item1;
                        mask = ~(kAllOnes << end.Item2);
                        Assert.IsTrue((inUse[qw] & mask) == 0, "Elements were already allocated");
                        inUse[qw] |= mask;
                    }
                }
                else
                {
                    allocator.Release(blocks[b]);

                    // Mark the block as not allocated, and check that it was allocated.

                    var begin = qword(blocks[b].begin);
                    var end   = qword(blocks[b].end);

                    if (begin.Item1 == end.Item1)
                    {
                        ulong qw   = begin.Item1;
                        ulong mask = kAllOnes << begin.Item2;
                        mask &= ~(kAllOnes << end.Item2);
                        Assert.IsTrue((inUse[qw] & mask) == mask, "Elements were not allocated");
                        inUse[qw] &= ~mask;
                    }
                    else
                    {
                        ulong qw   = begin.Item1;
                        ulong mask = kAllOnes << begin.Item2;

                        Assert.IsTrue((inUse[qw] & mask) == mask, "Elements were not allocated");
                        inUse[qw] &= ~mask;

                        for (qw = begin.Item1 + 1; qw < end.Item1; ++qw)
                        {
                            mask = kAllOnes;
                            Assert.IsTrue((inUse[qw] & mask) == mask, "Elements were not allocated");
                            inUse[qw] &= ~mask;
                        }

                        qw   = end.Item1;
                        mask = ~(kAllOnes << end.Item2);
                        Assert.IsTrue((inUse[qw] & mask) == mask, "Elements were not allocated");
                        inUse[qw] &= ~mask;
                    }

                    blocks[b] = new HeapBlock();
                }
            }

            allocator.Dispose();
        }