123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706 |
- using System;
- using System.Runtime.CompilerServices;
- using Unity.Burst;
- using Unity.Jobs;
- using Unity.Jobs.LowLevel.Unsafe;
- using UnityEngine.Assertions;
-
- namespace Unity.Collections.LowLevel.Unsafe
- {
- [GenerateTestsForBurstCompatibility]
- internal unsafe struct UnsafeStreamBlock
- {
- internal UnsafeStreamBlock* Next;
- internal fixed byte Data[1];
- }
-
- [GenerateTestsForBurstCompatibility]
- internal unsafe struct UnsafeStreamRange
- {
- internal UnsafeStreamBlock* Block;
- internal int OffsetInFirstBlock;
- internal int ElementCount;
-
- /// One byte past the end of the last byte written
- internal int LastOffset;
- internal int NumberOfBlocks;
- }
-
- [GenerateTestsForBurstCompatibility]
- internal unsafe struct UnsafeStreamBlockData
- {
- internal const int AllocationSize = 4 * 1024;
- internal AllocatorManager.AllocatorHandle Allocator;
-
- internal UnsafeStreamBlock** Blocks;
- internal int BlockCount;
-
- internal AllocatorManager.Block Ranges;
- internal int RangeCount;
-
- internal UnsafeStreamBlock* Allocate(UnsafeStreamBlock* oldBlock, int threadIndex)
- {
- Assert.IsTrue(threadIndex < BlockCount && threadIndex >= 0);
-
- UnsafeStreamBlock* block = (UnsafeStreamBlock*)Memory.Unmanaged.Array.Resize(null, 0, AllocationSize, Allocator, 1, 16);
- block->Next = null;
-
- if (oldBlock == null)
- {
- // Append our new block in front of the previous head.
- block->Next = Blocks[threadIndex];
- Blocks[threadIndex] = block;
- }
- else
- {
- block->Next = oldBlock->Next;
- oldBlock->Next = block;
- }
-
- return block;
- }
-
- internal void Free(UnsafeStreamBlock* oldBlock)
- {
- Memory.Unmanaged.Array.Resize(oldBlock, AllocationSize, 0, Allocator, 1, 16);
- }
- }
-
- /// <summary>
- /// A set of untyped, append-only buffers. Allows for concurrent reading and concurrent writing without synchronization.
- /// </summary>
- /// <remarks>
- /// As long as each individual buffer is written in one thread and read in one thread, multiple
- /// threads can read and write the stream concurrently, *e.g.*
- /// while thread *A* reads from buffer *X* of a stream, thread *B* can read from
- /// buffer *Y* of the same stream.
- ///
- /// Each buffer is stored as a chain of blocks. When a write exceeds a buffer's current capacity, another block
- /// is allocated and added to the end of the chain. Effectively, expanding the buffer never requires copying the existing
- /// data (unlike, for example, with <see cref="NativeList{T}"/>).
- ///
- /// **All writing to a stream should be completed before the stream is first read. Do not write to a stream after the first read.**
- ///
- /// Writing is done with <see cref="NativeStream.Writer"/>, and reading is done with <see cref="NativeStream.Reader"/>.
- /// An individual reader or writer cannot be used concurrently across threads. Each thread must use its own.
- ///
- /// The data written to an individual buffer can be heterogeneous in type, and the data written
- /// to different buffers of a stream can be entirely different in type, number, and order. Just make sure
- /// that the code reading from a particular buffer knows what to expect to read from it.
- /// </remarks>
- [GenerateTestsForBurstCompatibility]
- public unsafe struct UnsafeStream
- : INativeDisposable
- {
- [NativeDisableUnsafePtrRestriction]
- internal AllocatorManager.Block m_BlockData;
-
- /// <summary>
- /// Initializes and returns an instance of UnsafeStream.
- /// </summary>
- /// <param name="bufferCount">The number of buffers to give the stream. You usually want
- /// one buffer for each thread that will read or write the stream.</param>
- /// <param name="allocator">The allocator to use.</param>
- public UnsafeStream(int bufferCount, AllocatorManager.AllocatorHandle allocator)
- {
- AllocateBlock(out this, allocator);
- AllocateForEach(bufferCount);
- }
-
- /// <summary>
- /// Creates and schedules a job to allocate a new stream.
- /// </summary>
- /// <remarks>The stream can be used on the main thread after completing the returned job or used in other jobs that depend upon the returned job.
- ///
- /// Using a job to allocate the buffers can be more efficient, particularly for a stream with many buffers.
- /// </remarks>
- /// <typeparam name="T">Ignored.</typeparam>
- /// <param name="stream">Outputs the new stream.</param>
- /// <param name="bufferCount">A list whose length determines the number of buffers in the stream.</param>
- /// <param name="dependency">A job handle. The new job will depend upon this handle.</param>
- /// <param name="allocator">The allocator to use.</param>
- /// <returns>The handle of the new job.</returns>
- [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
- public static JobHandle ScheduleConstruct<T>(out UnsafeStream stream, NativeList<T> bufferCount, JobHandle dependency, AllocatorManager.AllocatorHandle allocator)
- where T : unmanaged
- {
- AllocateBlock(out stream, allocator);
- var jobData = new ConstructJobList { List = (UntypedUnsafeList*)bufferCount.GetUnsafeList(), Container = stream };
- return jobData.Schedule(dependency);
- }
-
- /// <summary>
- /// Creates and schedules a job to allocate a new stream.
- /// </summary>
- /// <remarks>The stream can be used on the main thread after completing the returned job or used in other jobs that depend upon the returned job.
- ///
- /// Allocating the buffers in a job can be more efficient, particularly for a stream with many buffers.
- /// </remarks>
- /// <param name="stream">Outputs the new stream.</param>
- /// <param name="bufferCount">An array whose value at index 0 determines the number of buffers in the stream.</param>
- /// <param name="dependency">A job handle. The new job will depend upon this handle.</param>
- /// <param name="allocator">The allocator to use.</param>
- /// <returns>The handle of the new job.</returns>
- public static JobHandle ScheduleConstruct(out UnsafeStream stream, NativeArray<int> bufferCount, JobHandle dependency, AllocatorManager.AllocatorHandle allocator)
- {
- AllocateBlock(out stream, allocator);
- var jobData = new ConstructJob { Length = bufferCount, Container = stream };
- return jobData.Schedule(dependency);
- }
-
- internal static void AllocateBlock(out UnsafeStream stream, AllocatorManager.AllocatorHandle allocator)
- {
- #if UNITY_2022_2_14F1_OR_NEWER
- int maxThreadCount = JobsUtility.ThreadIndexCount;
- #else
- int maxThreadCount = JobsUtility.MaxJobThreadCount;
- #endif
-
- int blockCount = maxThreadCount;
-
- int allocationSize = sizeof(UnsafeStreamBlockData) + sizeof(UnsafeStreamBlock*) * blockCount;
-
- AllocatorManager.Block blk = AllocatorManager.AllocateBlock(ref allocator, allocationSize, 16, 1);
- UnsafeUtility.MemClear( (void*)blk.Range.Pointer, blk.AllocatedBytes);
-
- stream.m_BlockData = blk;
-
- var blockData = (UnsafeStreamBlockData*)blk.Range.Pointer;
- blockData->Allocator = allocator;
- blockData->BlockCount = blockCount;
- blockData->Blocks = (UnsafeStreamBlock**)(blk.Range.Pointer + sizeof(UnsafeStreamBlockData));
-
- blockData->Ranges = default;
- blockData->RangeCount = 0;
- }
-
- internal void AllocateForEach(int forEachCount)
- {
- long allocationSize = sizeof(UnsafeStreamRange) * forEachCount;
-
- var blockData = (UnsafeStreamBlockData*)m_BlockData.Range.Pointer;
- blockData->Ranges = AllocatorManager.AllocateBlock(ref m_BlockData.Range.Allocator, sizeof(UnsafeStreamRange), 16, forEachCount);
- blockData->RangeCount = forEachCount;
- UnsafeUtility.MemClear((void*)blockData->Ranges.Range.Pointer, blockData->Ranges.AllocatedBytes);
- }
-
- /// <summary>
- /// Returns true if this stream is empty.
- /// </summary>
- /// <returns>True if this stream is empty or the stream has not been constructed.</returns>
- public readonly bool IsEmpty()
- {
- if (!IsCreated)
- {
- return true;
- }
-
- var blockData = (UnsafeStreamBlockData*)m_BlockData.Range.Pointer;
- var ranges = (UnsafeStreamRange*)blockData->Ranges.Range.Pointer;
-
- for (int i = 0; i != blockData->RangeCount; i++)
- {
- if (ranges[i].ElementCount > 0)
- {
- return false;
- }
- }
-
- return true;
- }
-
- /// <summary>
- /// Whether this stream has been allocated (and not yet deallocated).
- /// </summary>
- /// <remarks>Does not necessarily reflect whether the buffers of the stream have themselves been allocated.</remarks>
- /// <value>True if this stream has been allocated (and not yet deallocated).</value>
- public readonly bool IsCreated
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get => m_BlockData.Range.Pointer != IntPtr.Zero;
- }
-
- /// <summary>
- /// The number of buffers in this stream.
- /// </summary>
- /// <value>The number of buffers in this stream.</value>
- public readonly int ForEachCount => ((UnsafeStreamBlockData*)m_BlockData.Range.Pointer)->RangeCount;
-
- /// <summary>
- /// Returns a reader of this stream.
- /// </summary>
- /// <returns>A reader of this stream.</returns>
- public Reader AsReader()
- {
- return new Reader(ref this);
- }
-
- /// <summary>
- /// Returns a writer of this stream.
- /// </summary>
- /// <returns>A writer of this stream.</returns>
- public Writer AsWriter()
- {
- return new Writer(ref this);
- }
-
- /// <summary>
- /// Returns the total number of items in the buffers of this stream.
- /// </summary>
- /// <remarks>Each <see cref="Writer.Write{T}"/> and <see cref="Writer.Allocate"/> call increments this number.</remarks>
- /// <returns>The total number of items in the buffers of this stream.</returns>
- public int Count()
- {
- int itemCount = 0;
-
- var blockData = (UnsafeStreamBlockData*)m_BlockData.Range.Pointer;
- var ranges = (UnsafeStreamRange*)blockData->Ranges.Range.Pointer;
-
- for (int i = 0; i != blockData->RangeCount; i++)
- {
- itemCount += ranges[i].ElementCount;
- }
-
- return itemCount;
- }
-
- /// <summary>
- /// Returns a new NativeArray copy of this stream's data.
- /// </summary>
- /// <remarks>The length of the array will equal the count of this stream.
- ///
- /// Each buffer of this stream is copied to the array, one after the other.
- /// </remarks>
- /// <typeparam name="T">The type of values in the array.</typeparam>
- /// <param name="allocator">The allocator to use.</param>
- /// <returns>A new NativeArray copy of this stream's data.</returns>
- [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
- public NativeArray<T> ToNativeArray<T>(AllocatorManager.AllocatorHandle allocator) where T : unmanaged
- {
- var array = CollectionHelper.CreateNativeArray<T>(Count(), allocator, NativeArrayOptions.UninitializedMemory);
- var reader = AsReader();
-
- int offset = 0;
- for (int i = 0; i != reader.ForEachCount; i++)
- {
- reader.BeginForEachIndex(i);
- int rangeItemCount = reader.RemainingItemCount;
- for (int j = 0; j < rangeItemCount; ++j)
- {
- array[offset] = reader.Read<T>();
- offset++;
- }
- reader.EndForEachIndex();
- }
-
- return array;
- }
-
- void Deallocate()
- {
- if (!IsCreated)
- {
- return;
- }
-
- var blockData = (UnsafeStreamBlockData*)m_BlockData.Range.Pointer;
-
- for (int i = 0; i != blockData->BlockCount; i++)
- {
- UnsafeStreamBlock* block = blockData->Blocks[i];
- while (block != null)
- {
- UnsafeStreamBlock* next = block->Next;
- blockData->Free(block);
- block = next;
- }
- }
-
- blockData->Ranges.Dispose();
-
- m_BlockData.Dispose();
- m_BlockData = default;
- }
-
- /// <summary>
- /// Releases all resources (memory).
- /// </summary>
- public void Dispose()
- {
- if (!IsCreated)
- {
- return;
- }
-
- Deallocate();
- }
-
- /// <summary>
- /// Creates and schedules a job that will release all resources (memory and safety handles) of this stream.
- /// </summary>
- /// <param name="inputDeps">A job handle which the newly scheduled job will depend upon.</param>
- /// <returns>The handle of a new job that will release all resources (memory and safety handles) of this stream.</returns>
- public JobHandle Dispose(JobHandle inputDeps)
- {
- if (!IsCreated)
- {
- return inputDeps;
- }
-
- var jobHandle = new DisposeJob { Container = this }.Schedule(inputDeps);
-
- m_BlockData = default;
-
- return jobHandle;
- }
-
- [BurstCompile]
- struct DisposeJob : IJob
- {
- public UnsafeStream Container;
-
- public void Execute()
- {
- Container.Deallocate();
- }
- }
-
- [BurstCompile]
- struct ConstructJobList : IJob
- {
- public UnsafeStream Container;
-
- [ReadOnly]
- [NativeDisableUnsafePtrRestriction]
- public UntypedUnsafeList* List;
-
- public void Execute()
- {
- Container.AllocateForEach(List->m_length);
- }
- }
-
- [BurstCompile]
- struct ConstructJob : IJob
- {
- public UnsafeStream Container;
-
- [ReadOnly]
- public NativeArray<int> Length;
-
- public void Execute()
- {
- Container.AllocateForEach(Length[0]);
- }
- }
-
- /// <summary>
- /// Writes data into a buffer of an <see cref="UnsafeStream"/>.
- /// </summary>
- /// <remarks>An individual writer can only be used for one buffer of one stream.
- /// Do not create more than one writer for an individual buffer.</remarks>
- [GenerateTestsForBurstCompatibility]
- public unsafe struct Writer
- {
- [NativeDisableUnsafePtrRestriction]
- internal AllocatorManager.Block m_BlockData;
-
- [NativeDisableUnsafePtrRestriction]
- UnsafeStreamBlock* m_CurrentBlock;
-
- [NativeDisableUnsafePtrRestriction]
- byte* m_CurrentPtr;
-
- [NativeDisableUnsafePtrRestriction]
- byte* m_CurrentBlockEnd;
-
- internal int m_ForeachIndex;
- int m_ElementCount;
-
- [NativeDisableUnsafePtrRestriction]
- UnsafeStreamBlock* m_FirstBlock;
-
- int m_FirstOffset;
- int m_NumberOfBlocks;
-
- [NativeSetThreadIndex]
- int m_ThreadIndex;
-
- internal Writer(ref UnsafeStream stream)
- {
- m_BlockData = stream.m_BlockData;
- m_ForeachIndex = int.MinValue;
- m_ElementCount = -1;
- m_CurrentBlock = null;
- m_CurrentBlockEnd = null;
- m_CurrentPtr = null;
- m_FirstBlock = null;
- m_NumberOfBlocks = 0;
- m_FirstOffset = 0;
- m_ThreadIndex = 0;
- }
-
- /// <summary>
- /// The number of buffers in the stream of this writer.
- /// </summary>
- /// <value>The number of buffers in the stream of this writer.</value>
- public int ForEachCount => ((UnsafeStreamBlockData*)m_BlockData.Range.Pointer)->RangeCount;
-
- /// <summary>
- /// Readies this writer to write to a particular buffer of the stream.
- /// </summary>
- /// <remarks>Must be called before using this writer. For an individual writer, call this method only once.
- ///
- /// When done using this writer, you must call <see cref="EndForEachIndex"/>.</remarks>
- /// <param name="foreachIndex">The index of the buffer to write.</param>
- public void BeginForEachIndex(int foreachIndex)
- {
- m_ForeachIndex = foreachIndex;
- m_ElementCount = 0;
- m_NumberOfBlocks = 0;
- m_FirstBlock = m_CurrentBlock;
- m_FirstOffset = (int)(m_CurrentPtr - (byte*)m_CurrentBlock);
- }
-
- /// <summary>
- /// Readies the buffer written by this writer for reading.
- /// </summary>
- /// <remarks>Must be called before reading the buffer written by this writer.</remarks>
- public void EndForEachIndex()
- {
- var blockData = (UnsafeStreamBlockData*)m_BlockData.Range.Pointer;
- var ranges = (UnsafeStreamRange*)blockData->Ranges.Range.Pointer;
-
- ranges[m_ForeachIndex].ElementCount = m_ElementCount;
- ranges[m_ForeachIndex].OffsetInFirstBlock = m_FirstOffset;
- ranges[m_ForeachIndex].Block = m_FirstBlock;
-
- ranges[m_ForeachIndex].LastOffset = (int)(m_CurrentPtr - (byte*)m_CurrentBlock);
- ranges[m_ForeachIndex].NumberOfBlocks = m_NumberOfBlocks;
- }
-
- /// <summary>
- /// Write a value to a buffer.
- /// </summary>
- /// <remarks>The value is written to the buffer which was specified
- /// with <see cref="BeginForEachIndex"/>.</remarks>
- /// <typeparam name="T">The type of value to write.</typeparam>
- /// <param name="value">The value to write.</param>
- [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
- public void Write<T>(T value) where T : unmanaged
- {
- ref T dst = ref Allocate<T>();
- dst = value;
- }
-
- /// <summary>
- /// Allocate space in a buffer.
- /// </summary>
- /// <remarks>The space is allocated in the buffer which was specified
- /// with <see cref="BeginForEachIndex"/>.</remarks>
- /// <typeparam name="T">The type of value to allocate space for.</typeparam>
- /// <returns>A reference to the allocation.</returns>
- [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
- public ref T Allocate<T>() where T : unmanaged
- {
- int size = UnsafeUtility.SizeOf<T>();
- return ref UnsafeUtility.AsRef<T>(Allocate(size));
- }
-
- /// <summary>
- /// Allocate space in a buffer.
- /// </summary>
- /// <remarks>The space is allocated in the buffer which was specified
- /// with <see cref="BeginForEachIndex"/>.</remarks>
- /// <param name="size">The number of bytes to allocate.</param>
- /// <returns>The allocation.</returns>
- public byte* Allocate(int size)
- {
- byte* ptr = m_CurrentPtr;
- m_CurrentPtr += size;
-
- if (m_CurrentPtr > m_CurrentBlockEnd)
- {
- UnsafeStreamBlock* oldBlock = m_CurrentBlock;
-
- var blockData = (UnsafeStreamBlockData*)m_BlockData.Range.Pointer;
-
- m_CurrentBlock = blockData->Allocate(oldBlock, m_ThreadIndex);
- m_CurrentPtr = m_CurrentBlock->Data;
-
- if (m_FirstBlock == null)
- {
- m_FirstOffset = (int)(m_CurrentPtr - (byte*)m_CurrentBlock);
- m_FirstBlock = m_CurrentBlock;
- }
- else
- {
- m_NumberOfBlocks++;
- }
-
- m_CurrentBlockEnd = (byte*)m_CurrentBlock + UnsafeStreamBlockData.AllocationSize;
- ptr = m_CurrentPtr;
- m_CurrentPtr += size;
- }
-
- m_ElementCount++;
-
- return ptr;
- }
- }
-
- /// <summary>
- /// Reads data from a buffer of an <see cref="UnsafeStream"/>.
- /// </summary>
- /// <remarks>An individual reader can only be used for one buffer of one stream.
- /// Do not create more than one reader for an individual buffer.</remarks>
- [GenerateTestsForBurstCompatibility]
- public unsafe struct Reader
- {
- [NativeDisableUnsafePtrRestriction]
- internal AllocatorManager.Block m_BlockData;
-
- [NativeDisableUnsafePtrRestriction]
- internal UnsafeStreamBlock* m_CurrentBlock;
-
- [NativeDisableUnsafePtrRestriction]
- internal byte* m_CurrentPtr;
-
- [NativeDisableUnsafePtrRestriction]
- internal byte* m_CurrentBlockEnd;
-
- internal int m_RemainingItemCount;
- internal int m_LastBlockSize;
-
- internal Reader(ref UnsafeStream stream)
- {
- m_BlockData = stream.m_BlockData;
- m_CurrentBlock = null;
- m_CurrentPtr = null;
- m_CurrentBlockEnd = null;
- m_RemainingItemCount = 0;
- m_LastBlockSize = 0;
- }
-
- /// <summary>
- /// Readies this reader to read a particular buffer of the stream.
- /// </summary>
- /// <remarks>Must be called before using this reader. For an individual reader, call this method only once.
- ///
- /// When done using this reader, you must call <see cref="EndForEachIndex"/>.</remarks>
- /// <param name="foreachIndex">The index of the buffer to read.</param>
- /// <returns>The number of remaining elements to read from the buffer.</returns>
- public int BeginForEachIndex(int foreachIndex)
- {
- var blockData = (UnsafeStreamBlockData*)m_BlockData.Range.Pointer;
- var ranges = (UnsafeStreamRange*)blockData->Ranges.Range.Pointer;
-
- m_RemainingItemCount = ranges[foreachIndex].ElementCount;
- m_LastBlockSize = ranges[foreachIndex].LastOffset;
-
- m_CurrentBlock = ranges[foreachIndex].Block;
- m_CurrentPtr = (byte*)m_CurrentBlock + ranges[foreachIndex].OffsetInFirstBlock;
- m_CurrentBlockEnd = (byte*)m_CurrentBlock + UnsafeStreamBlockData.AllocationSize;
-
- return m_RemainingItemCount;
- }
-
- /// <summary>
- /// Does nothing.
- /// </summary>
- /// <remarks>Included only for consistency with <see cref="NativeStream"/>.</remarks>
- public void EndForEachIndex()
- {
- }
-
- /// <summary>
- /// The number of buffers in the stream of this reader.
- /// </summary>
- /// <value>The number of buffers in the stream of this reader.</value>
- public int ForEachCount => ((UnsafeStreamBlockData*)m_BlockData.Range.Pointer)->RangeCount;
-
- /// <summary>
- /// The number of items not yet read from the buffer.
- /// </summary>
- /// <value>The number of items not yet read from the buffer.</value>
- public int RemainingItemCount => m_RemainingItemCount;
-
- /// <summary>
- /// Returns a pointer to the next position to read from the buffer. Advances the reader some number of bytes.
- /// </summary>
- /// <param name="size">The number of bytes to advance the reader.</param>
- /// <returns>A pointer to the next position to read from the buffer.</returns>
- /// <exception cref="System.ArgumentException">Thrown if the reader has been advanced past the end of the buffer.</exception>
- public byte* ReadUnsafePtr(int size)
- {
- m_RemainingItemCount--;
-
- byte* ptr = m_CurrentPtr;
- m_CurrentPtr += size;
-
- if (m_CurrentPtr > m_CurrentBlockEnd)
- {
- m_CurrentBlock = m_CurrentBlock->Next;
- m_CurrentPtr = m_CurrentBlock->Data;
-
- m_CurrentBlockEnd = (byte*)m_CurrentBlock + UnsafeStreamBlockData.AllocationSize;
-
- ptr = m_CurrentPtr;
- m_CurrentPtr += size;
- }
-
- return ptr;
- }
-
- /// <summary>
- /// Reads the next value from the buffer.
- /// </summary>
- /// <remarks>Each read advances the reader to the next item in the buffer.</remarks>
- /// <typeparam name="T">The type of value to read.</typeparam>
- /// <returns>A reference to the next value from the buffer.</returns>
- [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
- public ref T Read<T>() where T : unmanaged
- {
- int size = UnsafeUtility.SizeOf<T>();
- return ref UnsafeUtility.AsRef<T>(ReadUnsafePtr(size));
- }
-
- /// <summary>
- /// Reads the next value from the buffer. Does not advance the reader.
- /// </summary>
- /// <typeparam name="T">The type of value to read.</typeparam>
- /// <returns>A reference to the next value from the buffer.</returns>
- [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
- public ref T Peek<T>() where T : unmanaged
- {
- int size = UnsafeUtility.SizeOf<T>();
-
- byte* ptr = m_CurrentPtr;
- if (ptr + size > m_CurrentBlockEnd)
- {
- ptr = m_CurrentBlock->Next->Data;
- }
-
- return ref UnsafeUtility.AsRef<T>(ptr);
- }
-
- /// <summary>
- /// Returns the total number of items in the buffers of the stream.
- /// </summary>
- /// <returns>The total number of items in the buffers of the stream.</returns>
- public int Count()
- {
- var blockData = (UnsafeStreamBlockData*)m_BlockData.Range.Pointer;
- var ranges = (UnsafeStreamRange*)blockData->Ranges.Range.Pointer;
-
- int itemCount = 0;
- for (int i = 0; i != blockData->RangeCount; i++)
- {
- itemCount += ranges[i].ElementCount;
- }
-
- return itemCount;
- }
- }
- }
- }
|