123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264 |
- using System;
- using System.Diagnostics;
- using System.Threading;
- using AOT;
- using Unity.Burst;
- using Unity.Collections.LowLevel.Unsafe;
- using Unity.Mathematics;
-
- namespace Unity.Collections
- {
- unsafe internal struct ArrayOfArrays<T> : IDisposable where T : unmanaged
- {
- AllocatorManager.AllocatorHandle m_backingAllocatorHandle;
- int m_lengthInElements;
- int m_capacityInElements;
- int m_log2BlockSizeInElements;
- int m_blocks;
- IntPtr* m_block;
-
- int BlockSizeInElements => 1 << m_log2BlockSizeInElements;
- int BlockSizeInBytes => BlockSizeInElements * sizeof(T);
- int BlockMask => BlockSizeInElements - 1;
-
- public int Length => m_lengthInElements;
- public int Capacity => m_capacityInElements;
-
- public ArrayOfArrays(int capacityInElements, AllocatorManager.AllocatorHandle backingAllocatorHandle, int log2BlockSizeInElements = 12)
- {
- this = default;
- m_backingAllocatorHandle = backingAllocatorHandle;
- m_lengthInElements = 0;
- m_capacityInElements = capacityInElements;
- m_log2BlockSizeInElements = log2BlockSizeInElements;
- m_blocks = (capacityInElements + BlockMask) >> m_log2BlockSizeInElements;
- m_block = (IntPtr*)Memory.Unmanaged.Allocate(sizeof(IntPtr) * m_blocks, 16, m_backingAllocatorHandle);
- UnsafeUtility.MemSet(m_block, 0, sizeof(IntPtr) * m_blocks);
- }
-
- public void LockfreeAdd(T t)
- {
- var elementIndex = Interlocked.Increment(ref m_lengthInElements) - 1;
- var blockIndex = BlockIndexOfElement(elementIndex);
- CheckBlockIndex(blockIndex);
- if(m_block[blockIndex] == IntPtr.Zero)
- {
- void* pointer = Memory.Unmanaged.Allocate(BlockSizeInBytes, 16, m_backingAllocatorHandle); // $$$!
- var lastBlock = math.min(m_blocks, blockIndex + 4); // don't overgrow too fast, simply to avoid a $$$ free
- for(; blockIndex < lastBlock; ++blockIndex)
- if(IntPtr.Zero == Interlocked.CompareExchange(ref m_block[blockIndex], (IntPtr)pointer, IntPtr.Zero))
- break; // install the new block, into *any* empty slot available, to avoid wasting the time we spent on malloc
- if(blockIndex == lastBlock)
- Memory.Unmanaged.Free(pointer, m_backingAllocatorHandle); // $$$, only if absolutely necessary
- }
- this[elementIndex] = t;
- }
-
- public ref T this[int elementIndex]
- {
- get
- {
- CheckElementIndex(elementIndex);
- var blockIndex = BlockIndexOfElement(elementIndex);
- CheckBlockIndex(blockIndex);
- CheckBlockIsNotNull(blockIndex);
- IntPtr blockIntPtr = m_block[blockIndex];
- var elementIndexInBlock = elementIndex & BlockMask;
- T* blockPointer = (T*)blockIntPtr;
- return ref blockPointer[elementIndexInBlock];
- }
- }
-
- public void Rewind()
- {
- m_lengthInElements = 0;
- }
-
- public void Clear()
- {
- Rewind();
- for(var i = 0; i < m_blocks; ++i)
- if(m_block[i] != IntPtr.Zero)
- {
- Memory.Unmanaged.Free((void*)m_block[i], m_backingAllocatorHandle);
- m_block[i] = IntPtr.Zero;
- }
- }
-
- public void Dispose()
- {
- Clear();
- Memory.Unmanaged.Free(m_block, m_backingAllocatorHandle);
- }
-
- [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")]
- void CheckElementIndex(int elementIndex)
- {
- if (elementIndex >= m_lengthInElements)
- throw new ArgumentException($"Element index {elementIndex} must be less than length in elements {m_lengthInElements}.");
- }
-
- [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")]
- void CheckBlockIndex(int blockIndex)
- {
- if (blockIndex >= m_blocks)
- throw new ArgumentException($"Block index {blockIndex} must be less than number of blocks {m_blocks}.");
- }
-
- [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")]
- void CheckBlockIsNotNull(int blockIndex)
- {
- if(m_block[blockIndex] == IntPtr.Zero)
- throw new ArgumentException($"Block index {blockIndex} is a null pointer.");
- }
-
- public void RemoveAtSwapBack(int elementIndex)
- {
- this[elementIndex] = this[Length-1];
- --m_lengthInElements;
- }
-
- int BlockIndexOfElement(int elementIndex)
- {
- return elementIndex >> m_log2BlockSizeInElements;
- }
-
- public void TrimExcess()
- {
- for(var blockIndex = BlockIndexOfElement(m_lengthInElements + BlockMask); blockIndex < m_blocks; ++blockIndex)
- {
- CheckBlockIndex(blockIndex);
- if(m_block[blockIndex] != IntPtr.Zero)
- {
- var blockIntPtr = m_block[blockIndex];
- void* blockPointer = (void*)blockIntPtr;
- Memory.Unmanaged.Free(blockPointer, m_backingAllocatorHandle);
- m_block[blockIndex] = IntPtr.Zero;
- }
- }
- }
- }
-
- [BurstCompile]
- internal struct AutoFreeAllocator : AllocatorManager.IAllocator
- {
- ArrayOfArrays<IntPtr> m_allocated;
- ArrayOfArrays<IntPtr> m_tofree;
- AllocatorManager.AllocatorHandle m_handle;
- AllocatorManager.AllocatorHandle m_backingAllocatorHandle;
-
- unsafe public void Update()
- {
- for(var i = m_tofree.Length; i --> 0;)
- for(var j = m_allocated.Length; j --> 0;)
- if(m_allocated[j] == m_tofree[i])
- {
- Memory.Unmanaged.Free((void*)m_tofree[i], m_backingAllocatorHandle);
- m_allocated.RemoveAtSwapBack(j);
- break;
- }
- m_tofree.Rewind();
- m_allocated.TrimExcess();
- }
-
- unsafe public void Initialize(AllocatorManager.AllocatorHandle backingAllocatorHandle)
- {
- m_allocated = new ArrayOfArrays<IntPtr>(1024 * 1024, backingAllocatorHandle);
- m_tofree = new ArrayOfArrays<IntPtr>(128 * 1024, backingAllocatorHandle);
- m_backingAllocatorHandle = backingAllocatorHandle;
- }
-
- unsafe public void FreeAll()
- {
- Update();
- m_handle.Rewind();
- for(var i = 0; i < m_allocated.Length; ++i)
- Memory.Unmanaged.Free((void*) m_allocated[i], m_backingAllocatorHandle);
- m_allocated.Rewind();
- }
-
- /// <summary>
- /// Dispose the allocator. This must be called to free the memory blocks that were allocated from the system.
- /// </summary>
- public void Dispose()
- {
- FreeAll();
- m_tofree.Dispose();
- m_allocated.Dispose();
- }
-
- /// <summary>
- /// The allocator function. It can allocate, deallocate, or reallocate.
- /// </summary>
- public AllocatorManager.TryFunction Function => Try;
-
- /// <summary>
- /// Invoke the allocator function.
- /// </summary>
- /// <param name="block">The block to allocate, deallocate, or reallocate. See <see cref="AllocatorManager.Try"/></param>
- /// <returns>0 if successful. Otherwise, returns the error code from the allocator function.</returns>
- public int Try(ref AllocatorManager.Block block)
- {
- unsafe
- {
- if (block.Range.Pointer == IntPtr.Zero)
- {
- if (block.Bytes == 0)
- {
- return 0;
- }
-
- var ptr = (byte*)Memory.Unmanaged.Allocate(block.Bytes, block.Alignment, m_backingAllocatorHandle);
- block.Range.Pointer = (IntPtr)ptr;
- block.AllocatedItems = block.Range.Items;
-
- m_allocated.LockfreeAdd(block.Range.Pointer);
-
- return 0;
- }
-
- if (block.Range.Items == 0)
- {
- m_tofree.LockfreeAdd(block.Range.Pointer);
-
- block.Range.Pointer = IntPtr.Zero;
- block.AllocatedItems = 0;
-
- return 0;
- }
-
- return -1;
- }
- }
-
- [BurstCompile]
- [MonoPInvokeCallback(typeof(AllocatorManager.TryFunction))]
- internal static int Try(IntPtr state, ref AllocatorManager.Block block)
- {
- unsafe { return ((AutoFreeAllocator*)state)->Try(ref block); }
- }
-
- /// <summary>
- /// This allocator.
- /// </summary>
- /// <value>This allocator.</value>
- public AllocatorManager.AllocatorHandle Handle { get { return m_handle; } set { m_handle = value; } }
-
- /// <summary>
- /// Cast the Allocator index into Allocator
- /// </summary>
- public Allocator ToAllocator { get { return m_handle.ToAllocator; } }
-
- /// <summary>
- /// Check whether an allocator is a custom allocator
- /// </summary>
- public bool IsCustomAllocator { get { return m_handle.IsCustomAllocator; } }
-
- /// <summary>
- /// Check whether this allocator will automatically dispose allocations.
- /// </summary>
- /// <remarks>Allocations made by Auto free allocator are automatically disposed.</remarks>
- /// <value>Always true</value>
- public bool IsAutoDispose { get { return true; } }
- }
- }
|