123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296 |
- using System;
- using System.Diagnostics;
- using System.Runtime.CompilerServices;
- using Unity.Jobs;
- using System.Runtime.InteropServices;
-
- namespace Unity.Collections.LowLevel.Unsafe
- {
- /// <summary>
- /// A fixed-size circular buffer.
- /// </summary>
- /// <typeparam name="T">The type of the elements.</typeparam>
- [DebuggerDisplay("Length = {Length}, Capacity = {Capacity}, IsCreated = {IsCreated}, IsEmpty = {IsEmpty}")]
- [DebuggerTypeProxy(typeof(UnsafeRingQueueDebugView<>))]
- [GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int) })]
- [StructLayout(LayoutKind.Sequential)]
- public unsafe struct UnsafeRingQueue<T>
- : INativeDisposable
- where T : unmanaged
- {
- /// <summary>
- /// The internal buffer where the content is stored.
- /// </summary>
- /// <value>The internal buffer where the content is stored.</value>
- [NativeDisableUnsafePtrRestriction]
- public T* Ptr;
-
- /// <summary>
- /// The allocator used to create the internal buffer.
- /// </summary>
- /// <value>The allocator used to create the internal buffer.</value>
- public AllocatorManager.AllocatorHandle Allocator;
-
- internal readonly int m_Capacity;
- internal int m_Filled;
- internal int m_Write;
- internal int m_Read;
-
- /// <summary>
- /// Whether the queue is empty.
- /// </summary>
- /// <value>True if the queue is empty or the queue has not been constructed.</value>
- public readonly bool IsEmpty
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get => m_Filled == 0;
- }
-
- /// <summary>
- /// The number of elements currently in this queue.
- /// </summary>
- /// <value>The number of elements currently in this queue.</value>
- public readonly int Length
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get => m_Filled;
- }
-
- /// <summary>
- /// The number of elements that fit in the internal buffer.
- /// </summary>
- /// <value>The number of elements that fit in the internal buffer.</value>
- public readonly int Capacity
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get => m_Capacity;
- }
-
- /// <summary>
- /// Initializes and returns an instance of UnsafeRingQueue which aliasing an existing buffer.
- /// </summary>
- /// <param name="ptr">An existing buffer to set as the internal buffer.</param>
- /// <param name="capacity">The capacity.</param>
- public UnsafeRingQueue(T* ptr, int capacity)
- {
- Ptr = ptr;
- Allocator = AllocatorManager.None;
- m_Capacity = capacity;
- m_Filled = 0;
- m_Write = 0;
- m_Read = 0;
- }
-
- /// <summary>
- /// Initializes and returns an instance of UnsafeRingQueue.
- /// </summary>
- /// <param name="capacity">The capacity.</param>
- /// <param name="allocator">The allocator to use.</param>
- /// <param name="options">Whether newly allocated bytes should be zeroed out.</param>
- public UnsafeRingQueue(int capacity, AllocatorManager.AllocatorHandle allocator, NativeArrayOptions options = NativeArrayOptions.ClearMemory)
- {
- Allocator = allocator;
- m_Capacity = capacity;
- m_Filled = 0;
- m_Write = 0;
- m_Read = 0;
- var sizeInBytes = capacity * UnsafeUtility.SizeOf<T>();
- Ptr = (T*)Memory.Unmanaged.Allocate(sizeInBytes, 16, allocator);
-
- if (options == NativeArrayOptions.ClearMemory)
- {
- UnsafeUtility.MemClear(Ptr, sizeInBytes);
- }
- }
-
- internal static UnsafeRingQueue<T>* Alloc(AllocatorManager.AllocatorHandle allocator)
- {
- UnsafeRingQueue<T>* data = (UnsafeRingQueue<T>*)Memory.Unmanaged.Allocate(sizeof(UnsafeRingQueue<T>), UnsafeUtility.AlignOf<UnsafeRingQueue<T>>(), allocator);
- return data;
- }
-
- internal static void Free(UnsafeRingQueue<T>* data)
- {
- if (data == null)
- {
- throw new InvalidOperationException("UnsafeRingQueue has yet to be created or has been destroyed!");
- }
- var allocator = data->Allocator;
- data->Dispose();
- Memory.Unmanaged.Free(data, allocator);
- }
-
- /// <summary>
- /// Whether this queue has been allocated (and not yet deallocated).
- /// </summary>
- /// <value>True if this queue has been allocated (and not yet deallocated).</value>
- public readonly bool IsCreated
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get => Ptr != null;
- }
-
- /// <summary>
- /// Releases all resources (memory and safety handles).
- /// </summary>
- public void Dispose()
- {
- if (!IsCreated)
- {
- return;
- }
-
- if (CollectionHelper.ShouldDeallocate(Allocator))
- {
- Memory.Unmanaged.Free(Ptr, Allocator);
- Allocator = AllocatorManager.Invalid;
- }
-
- Ptr = null;
- }
-
- /// <summary>
- /// Creates and schedules a job that will dispose this queue.
- /// </summary>
- /// <param name="inputDeps">The handle of a job which the new job will depend upon.</param>
- /// <returns>The handle of a new job that will dispose this queue. The new job depends upon inputDeps.</returns>
- public JobHandle Dispose(JobHandle inputDeps)
- {
- if (!IsCreated)
- {
- return inputDeps;
- }
-
- if (CollectionHelper.ShouldDeallocate(Allocator))
- {
- var jobHandle = new UnsafeDisposeJob { Ptr = Ptr, Allocator = Allocator }.Schedule(inputDeps);
-
- Ptr = null;
- Allocator = AllocatorManager.Invalid;
-
- return jobHandle;
- }
-
- Ptr = null;
-
- return inputDeps;
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- bool TryEnqueueInternal(T value)
- {
- if (m_Filled == m_Capacity)
- return false;
- Ptr[m_Write] = value;
- m_Write++;
- if (m_Write == m_Capacity)
- m_Write = 0;
- m_Filled++;
- return true;
- }
-
- /// <summary>
- /// Adds an element at the front of the queue.
- /// </summary>
- /// <remarks>Does nothing if the queue is full.</remarks>
- /// <param name="value">The value to be added.</param>
- /// <returns>True if the value was added.</returns>
- public bool TryEnqueue(T value)
- {
- return TryEnqueueInternal(value);
- }
-
- [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")]
- static void ThrowQueueFull()
- {
- throw new InvalidOperationException("Trying to enqueue into full queue.");
- }
-
- /// <summary>
- /// Adds an element at the front of the queue.
- /// </summary>
- /// <param name="value">The value to be added.</param>
- /// <exception cref="InvalidOperationException">Thrown if the queue was full.</exception>
- public void Enqueue(T value)
- {
- if (!TryEnqueueInternal(value))
- {
- ThrowQueueFull();
- }
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- bool TryDequeueInternal(out T item)
- {
- item = Ptr[m_Read];
- if (m_Filled == 0)
- return false;
- m_Read = m_Read + 1;
- if (m_Read == m_Capacity)
- m_Read = 0;
- m_Filled--;
- return true;
- }
-
- /// <summary>
- /// Removes the element from the end of the queue.
- /// </summary>
- /// <remarks>Does nothing if the queue is empty.</remarks>
- /// <param name="item">Outputs the element removed.</param>
- /// <returns>True if an element was removed.</returns>
- public bool TryDequeue(out T item)
- {
- return TryDequeueInternal(out item);
- }
-
- [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")]
- static void ThrowQueueEmpty()
- {
- throw new InvalidOperationException("Trying to dequeue from an empty queue");
- }
-
- /// <summary>
- /// Removes the element from the end of the queue.
- /// </summary>
- /// <exception cref="InvalidOperationException">Thrown if the queue was empty.</exception>
- /// <returns>Returns the removed element.</returns>
- public T Dequeue()
- {
- if (!TryDequeueInternal(out T item))
- {
- ThrowQueueEmpty();
- }
-
- return item;
- }
- }
-
- internal sealed class UnsafeRingQueueDebugView<T>
- where T : unmanaged
- {
- UnsafeRingQueue<T> Data;
-
- public UnsafeRingQueueDebugView(UnsafeRingQueue<T> data)
- {
- Data = data;
- }
-
- public unsafe T[] Items
- {
- get
- {
- T[] result = new T[Data.Length];
-
- var read = Data.m_Read;
- var capacity = Data.m_Capacity;
-
- for (var i = 0; i < result.Length; ++i)
- {
- result[i] = Data.Ptr[(read + i) % capacity];
- }
-
- return result;
- }
- }
- }
- }
|