Açıklama Yok
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

UnsafeStream.cs 27KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706
  1. using System;
  2. using System.Runtime.CompilerServices;
  3. using Unity.Burst;
  4. using Unity.Jobs;
  5. using Unity.Jobs.LowLevel.Unsafe;
  6. using UnityEngine.Assertions;
  7. namespace Unity.Collections.LowLevel.Unsafe
  8. {
  9. [GenerateTestsForBurstCompatibility]
  10. internal unsafe struct UnsafeStreamBlock
  11. {
  12. internal UnsafeStreamBlock* Next;
  13. internal fixed byte Data[1];
  14. }
  15. [GenerateTestsForBurstCompatibility]
  16. internal unsafe struct UnsafeStreamRange
  17. {
  18. internal UnsafeStreamBlock* Block;
  19. internal int OffsetInFirstBlock;
  20. internal int ElementCount;
  21. /// One byte past the end of the last byte written
  22. internal int LastOffset;
  23. internal int NumberOfBlocks;
  24. }
  25. [GenerateTestsForBurstCompatibility]
  26. internal unsafe struct UnsafeStreamBlockData
  27. {
  28. internal const int AllocationSize = 4 * 1024;
  29. internal AllocatorManager.AllocatorHandle Allocator;
  30. internal UnsafeStreamBlock** Blocks;
  31. internal int BlockCount;
  32. internal AllocatorManager.Block Ranges;
  33. internal int RangeCount;
  34. internal UnsafeStreamBlock* Allocate(UnsafeStreamBlock* oldBlock, int threadIndex)
  35. {
  36. Assert.IsTrue(threadIndex < BlockCount && threadIndex >= 0);
  37. UnsafeStreamBlock* block = (UnsafeStreamBlock*)Memory.Unmanaged.Array.Resize(null, 0, AllocationSize, Allocator, 1, 16);
  38. block->Next = null;
  39. if (oldBlock == null)
  40. {
  41. // Append our new block in front of the previous head.
  42. block->Next = Blocks[threadIndex];
  43. Blocks[threadIndex] = block;
  44. }
  45. else
  46. {
  47. block->Next = oldBlock->Next;
  48. oldBlock->Next = block;
  49. }
  50. return block;
  51. }
  52. internal void Free(UnsafeStreamBlock* oldBlock)
  53. {
  54. Memory.Unmanaged.Array.Resize(oldBlock, AllocationSize, 0, Allocator, 1, 16);
  55. }
  56. }
  57. /// <summary>
  58. /// A set of untyped, append-only buffers. Allows for concurrent reading and concurrent writing without synchronization.
  59. /// </summary>
  60. /// <remarks>
  61. /// As long as each individual buffer is written in one thread and read in one thread, multiple
  62. /// threads can read and write the stream concurrently, *e.g.*
  63. /// while thread *A* reads from buffer *X* of a stream, thread *B* can read from
  64. /// buffer *Y* of the same stream.
  65. ///
  66. /// Each buffer is stored as a chain of blocks. When a write exceeds a buffer's current capacity, another block
  67. /// is allocated and added to the end of the chain. Effectively, expanding the buffer never requires copying the existing
  68. /// data (unlike, for example, with <see cref="NativeList{T}"/>).
  69. ///
  70. /// **All writing to a stream should be completed before the stream is first read. Do not write to a stream after the first read.**
  71. ///
  72. /// Writing is done with <see cref="NativeStream.Writer"/>, and reading is done with <see cref="NativeStream.Reader"/>.
  73. /// An individual reader or writer cannot be used concurrently across threads. Each thread must use its own.
  74. ///
  75. /// The data written to an individual buffer can be heterogeneous in type, and the data written
  76. /// to different buffers of a stream can be entirely different in type, number, and order. Just make sure
  77. /// that the code reading from a particular buffer knows what to expect to read from it.
  78. /// </remarks>
  79. [GenerateTestsForBurstCompatibility]
  80. public unsafe struct UnsafeStream
  81. : INativeDisposable
  82. {
  83. [NativeDisableUnsafePtrRestriction]
  84. internal AllocatorManager.Block m_BlockData;
  85. /// <summary>
  86. /// Initializes and returns an instance of UnsafeStream.
  87. /// </summary>
  88. /// <param name="bufferCount">The number of buffers to give the stream. You usually want
  89. /// one buffer for each thread that will read or write the stream.</param>
  90. /// <param name="allocator">The allocator to use.</param>
  91. public UnsafeStream(int bufferCount, AllocatorManager.AllocatorHandle allocator)
  92. {
  93. AllocateBlock(out this, allocator);
  94. AllocateForEach(bufferCount);
  95. }
  96. /// <summary>
  97. /// Creates and schedules a job to allocate a new stream.
  98. /// </summary>
  99. /// <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.
  100. ///
  101. /// Using a job to allocate the buffers can be more efficient, particularly for a stream with many buffers.
  102. /// </remarks>
  103. /// <typeparam name="T">Ignored.</typeparam>
  104. /// <param name="stream">Outputs the new stream.</param>
  105. /// <param name="bufferCount">A list whose length determines the number of buffers in the stream.</param>
  106. /// <param name="dependency">A job handle. The new job will depend upon this handle.</param>
  107. /// <param name="allocator">The allocator to use.</param>
  108. /// <returns>The handle of the new job.</returns>
  109. [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
  110. public static JobHandle ScheduleConstruct<T>(out UnsafeStream stream, NativeList<T> bufferCount, JobHandle dependency, AllocatorManager.AllocatorHandle allocator)
  111. where T : unmanaged
  112. {
  113. AllocateBlock(out stream, allocator);
  114. var jobData = new ConstructJobList { List = (UntypedUnsafeList*)bufferCount.GetUnsafeList(), Container = stream };
  115. return jobData.Schedule(dependency);
  116. }
  117. /// <summary>
  118. /// Creates and schedules a job to allocate a new stream.
  119. /// </summary>
  120. /// <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.
  121. ///
  122. /// Allocating the buffers in a job can be more efficient, particularly for a stream with many buffers.
  123. /// </remarks>
  124. /// <param name="stream">Outputs the new stream.</param>
  125. /// <param name="bufferCount">An array whose value at index 0 determines the number of buffers in the stream.</param>
  126. /// <param name="dependency">A job handle. The new job will depend upon this handle.</param>
  127. /// <param name="allocator">The allocator to use.</param>
  128. /// <returns>The handle of the new job.</returns>
  129. public static JobHandle ScheduleConstruct(out UnsafeStream stream, NativeArray<int> bufferCount, JobHandle dependency, AllocatorManager.AllocatorHandle allocator)
  130. {
  131. AllocateBlock(out stream, allocator);
  132. var jobData = new ConstructJob { Length = bufferCount, Container = stream };
  133. return jobData.Schedule(dependency);
  134. }
  135. internal static void AllocateBlock(out UnsafeStream stream, AllocatorManager.AllocatorHandle allocator)
  136. {
  137. #if UNITY_2022_2_14F1_OR_NEWER
  138. int maxThreadCount = JobsUtility.ThreadIndexCount;
  139. #else
  140. int maxThreadCount = JobsUtility.MaxJobThreadCount;
  141. #endif
  142. int blockCount = maxThreadCount;
  143. int allocationSize = sizeof(UnsafeStreamBlockData) + sizeof(UnsafeStreamBlock*) * blockCount;
  144. AllocatorManager.Block blk = AllocatorManager.AllocateBlock(ref allocator, allocationSize, 16, 1);
  145. UnsafeUtility.MemClear( (void*)blk.Range.Pointer, blk.AllocatedBytes);
  146. stream.m_BlockData = blk;
  147. var blockData = (UnsafeStreamBlockData*)blk.Range.Pointer;
  148. blockData->Allocator = allocator;
  149. blockData->BlockCount = blockCount;
  150. blockData->Blocks = (UnsafeStreamBlock**)(blk.Range.Pointer + sizeof(UnsafeStreamBlockData));
  151. blockData->Ranges = default;
  152. blockData->RangeCount = 0;
  153. }
  154. internal void AllocateForEach(int forEachCount)
  155. {
  156. long allocationSize = sizeof(UnsafeStreamRange) * forEachCount;
  157. var blockData = (UnsafeStreamBlockData*)m_BlockData.Range.Pointer;
  158. blockData->Ranges = AllocatorManager.AllocateBlock(ref m_BlockData.Range.Allocator, sizeof(UnsafeStreamRange), 16, forEachCount);
  159. blockData->RangeCount = forEachCount;
  160. UnsafeUtility.MemClear((void*)blockData->Ranges.Range.Pointer, blockData->Ranges.AllocatedBytes);
  161. }
  162. /// <summary>
  163. /// Returns true if this stream is empty.
  164. /// </summary>
  165. /// <returns>True if this stream is empty or the stream has not been constructed.</returns>
  166. public readonly bool IsEmpty()
  167. {
  168. if (!IsCreated)
  169. {
  170. return true;
  171. }
  172. var blockData = (UnsafeStreamBlockData*)m_BlockData.Range.Pointer;
  173. var ranges = (UnsafeStreamRange*)blockData->Ranges.Range.Pointer;
  174. for (int i = 0; i != blockData->RangeCount; i++)
  175. {
  176. if (ranges[i].ElementCount > 0)
  177. {
  178. return false;
  179. }
  180. }
  181. return true;
  182. }
  183. /// <summary>
  184. /// Whether this stream has been allocated (and not yet deallocated).
  185. /// </summary>
  186. /// <remarks>Does not necessarily reflect whether the buffers of the stream have themselves been allocated.</remarks>
  187. /// <value>True if this stream has been allocated (and not yet deallocated).</value>
  188. public readonly bool IsCreated
  189. {
  190. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  191. get => m_BlockData.Range.Pointer != IntPtr.Zero;
  192. }
  193. /// <summary>
  194. /// The number of buffers in this stream.
  195. /// </summary>
  196. /// <value>The number of buffers in this stream.</value>
  197. public readonly int ForEachCount => ((UnsafeStreamBlockData*)m_BlockData.Range.Pointer)->RangeCount;
  198. /// <summary>
  199. /// Returns a reader of this stream.
  200. /// </summary>
  201. /// <returns>A reader of this stream.</returns>
  202. public Reader AsReader()
  203. {
  204. return new Reader(ref this);
  205. }
  206. /// <summary>
  207. /// Returns a writer of this stream.
  208. /// </summary>
  209. /// <returns>A writer of this stream.</returns>
  210. public Writer AsWriter()
  211. {
  212. return new Writer(ref this);
  213. }
  214. /// <summary>
  215. /// Returns the total number of items in the buffers of this stream.
  216. /// </summary>
  217. /// <remarks>Each <see cref="Writer.Write{T}"/> and <see cref="Writer.Allocate"/> call increments this number.</remarks>
  218. /// <returns>The total number of items in the buffers of this stream.</returns>
  219. public int Count()
  220. {
  221. int itemCount = 0;
  222. var blockData = (UnsafeStreamBlockData*)m_BlockData.Range.Pointer;
  223. var ranges = (UnsafeStreamRange*)blockData->Ranges.Range.Pointer;
  224. for (int i = 0; i != blockData->RangeCount; i++)
  225. {
  226. itemCount += ranges[i].ElementCount;
  227. }
  228. return itemCount;
  229. }
  230. /// <summary>
  231. /// Returns a new NativeArray copy of this stream's data.
  232. /// </summary>
  233. /// <remarks>The length of the array will equal the count of this stream.
  234. ///
  235. /// Each buffer of this stream is copied to the array, one after the other.
  236. /// </remarks>
  237. /// <typeparam name="T">The type of values in the array.</typeparam>
  238. /// <param name="allocator">The allocator to use.</param>
  239. /// <returns>A new NativeArray copy of this stream's data.</returns>
  240. [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
  241. public NativeArray<T> ToNativeArray<T>(AllocatorManager.AllocatorHandle allocator) where T : unmanaged
  242. {
  243. var array = CollectionHelper.CreateNativeArray<T>(Count(), allocator, NativeArrayOptions.UninitializedMemory);
  244. var reader = AsReader();
  245. int offset = 0;
  246. for (int i = 0; i != reader.ForEachCount; i++)
  247. {
  248. reader.BeginForEachIndex(i);
  249. int rangeItemCount = reader.RemainingItemCount;
  250. for (int j = 0; j < rangeItemCount; ++j)
  251. {
  252. array[offset] = reader.Read<T>();
  253. offset++;
  254. }
  255. reader.EndForEachIndex();
  256. }
  257. return array;
  258. }
  259. void Deallocate()
  260. {
  261. if (!IsCreated)
  262. {
  263. return;
  264. }
  265. var blockData = (UnsafeStreamBlockData*)m_BlockData.Range.Pointer;
  266. for (int i = 0; i != blockData->BlockCount; i++)
  267. {
  268. UnsafeStreamBlock* block = blockData->Blocks[i];
  269. while (block != null)
  270. {
  271. UnsafeStreamBlock* next = block->Next;
  272. blockData->Free(block);
  273. block = next;
  274. }
  275. }
  276. blockData->Ranges.Dispose();
  277. m_BlockData.Dispose();
  278. m_BlockData = default;
  279. }
  280. /// <summary>
  281. /// Releases all resources (memory).
  282. /// </summary>
  283. public void Dispose()
  284. {
  285. if (!IsCreated)
  286. {
  287. return;
  288. }
  289. Deallocate();
  290. }
  291. /// <summary>
  292. /// Creates and schedules a job that will release all resources (memory and safety handles) of this stream.
  293. /// </summary>
  294. /// <param name="inputDeps">A job handle which the newly scheduled job will depend upon.</param>
  295. /// <returns>The handle of a new job that will release all resources (memory and safety handles) of this stream.</returns>
  296. public JobHandle Dispose(JobHandle inputDeps)
  297. {
  298. if (!IsCreated)
  299. {
  300. return inputDeps;
  301. }
  302. var jobHandle = new DisposeJob { Container = this }.Schedule(inputDeps);
  303. m_BlockData = default;
  304. return jobHandle;
  305. }
  306. [BurstCompile]
  307. struct DisposeJob : IJob
  308. {
  309. public UnsafeStream Container;
  310. public void Execute()
  311. {
  312. Container.Deallocate();
  313. }
  314. }
  315. [BurstCompile]
  316. struct ConstructJobList : IJob
  317. {
  318. public UnsafeStream Container;
  319. [ReadOnly]
  320. [NativeDisableUnsafePtrRestriction]
  321. public UntypedUnsafeList* List;
  322. public void Execute()
  323. {
  324. Container.AllocateForEach(List->m_length);
  325. }
  326. }
  327. [BurstCompile]
  328. struct ConstructJob : IJob
  329. {
  330. public UnsafeStream Container;
  331. [ReadOnly]
  332. public NativeArray<int> Length;
  333. public void Execute()
  334. {
  335. Container.AllocateForEach(Length[0]);
  336. }
  337. }
  338. /// <summary>
  339. /// Writes data into a buffer of an <see cref="UnsafeStream"/>.
  340. /// </summary>
  341. /// <remarks>An individual writer can only be used for one buffer of one stream.
  342. /// Do not create more than one writer for an individual buffer.</remarks>
  343. [GenerateTestsForBurstCompatibility]
  344. public unsafe struct Writer
  345. {
  346. [NativeDisableUnsafePtrRestriction]
  347. internal AllocatorManager.Block m_BlockData;
  348. [NativeDisableUnsafePtrRestriction]
  349. UnsafeStreamBlock* m_CurrentBlock;
  350. [NativeDisableUnsafePtrRestriction]
  351. byte* m_CurrentPtr;
  352. [NativeDisableUnsafePtrRestriction]
  353. byte* m_CurrentBlockEnd;
  354. internal int m_ForeachIndex;
  355. int m_ElementCount;
  356. [NativeDisableUnsafePtrRestriction]
  357. UnsafeStreamBlock* m_FirstBlock;
  358. int m_FirstOffset;
  359. int m_NumberOfBlocks;
  360. [NativeSetThreadIndex]
  361. int m_ThreadIndex;
  362. internal Writer(ref UnsafeStream stream)
  363. {
  364. m_BlockData = stream.m_BlockData;
  365. m_ForeachIndex = int.MinValue;
  366. m_ElementCount = -1;
  367. m_CurrentBlock = null;
  368. m_CurrentBlockEnd = null;
  369. m_CurrentPtr = null;
  370. m_FirstBlock = null;
  371. m_NumberOfBlocks = 0;
  372. m_FirstOffset = 0;
  373. m_ThreadIndex = 0;
  374. }
  375. /// <summary>
  376. /// The number of buffers in the stream of this writer.
  377. /// </summary>
  378. /// <value>The number of buffers in the stream of this writer.</value>
  379. public int ForEachCount => ((UnsafeStreamBlockData*)m_BlockData.Range.Pointer)->RangeCount;
  380. /// <summary>
  381. /// Readies this writer to write to a particular buffer of the stream.
  382. /// </summary>
  383. /// <remarks>Must be called before using this writer. For an individual writer, call this method only once.
  384. ///
  385. /// When done using this writer, you must call <see cref="EndForEachIndex"/>.</remarks>
  386. /// <param name="foreachIndex">The index of the buffer to write.</param>
  387. public void BeginForEachIndex(int foreachIndex)
  388. {
  389. m_ForeachIndex = foreachIndex;
  390. m_ElementCount = 0;
  391. m_NumberOfBlocks = 0;
  392. m_FirstBlock = m_CurrentBlock;
  393. m_FirstOffset = (int)(m_CurrentPtr - (byte*)m_CurrentBlock);
  394. }
  395. /// <summary>
  396. /// Readies the buffer written by this writer for reading.
  397. /// </summary>
  398. /// <remarks>Must be called before reading the buffer written by this writer.</remarks>
  399. public void EndForEachIndex()
  400. {
  401. var blockData = (UnsafeStreamBlockData*)m_BlockData.Range.Pointer;
  402. var ranges = (UnsafeStreamRange*)blockData->Ranges.Range.Pointer;
  403. ranges[m_ForeachIndex].ElementCount = m_ElementCount;
  404. ranges[m_ForeachIndex].OffsetInFirstBlock = m_FirstOffset;
  405. ranges[m_ForeachIndex].Block = m_FirstBlock;
  406. ranges[m_ForeachIndex].LastOffset = (int)(m_CurrentPtr - (byte*)m_CurrentBlock);
  407. ranges[m_ForeachIndex].NumberOfBlocks = m_NumberOfBlocks;
  408. }
  409. /// <summary>
  410. /// Write a value to a buffer.
  411. /// </summary>
  412. /// <remarks>The value is written to the buffer which was specified
  413. /// with <see cref="BeginForEachIndex"/>.</remarks>
  414. /// <typeparam name="T">The type of value to write.</typeparam>
  415. /// <param name="value">The value to write.</param>
  416. [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
  417. public void Write<T>(T value) where T : unmanaged
  418. {
  419. ref T dst = ref Allocate<T>();
  420. dst = value;
  421. }
  422. /// <summary>
  423. /// Allocate space in a buffer.
  424. /// </summary>
  425. /// <remarks>The space is allocated in the buffer which was specified
  426. /// with <see cref="BeginForEachIndex"/>.</remarks>
  427. /// <typeparam name="T">The type of value to allocate space for.</typeparam>
  428. /// <returns>A reference to the allocation.</returns>
  429. [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
  430. public ref T Allocate<T>() where T : unmanaged
  431. {
  432. int size = UnsafeUtility.SizeOf<T>();
  433. return ref UnsafeUtility.AsRef<T>(Allocate(size));
  434. }
  435. /// <summary>
  436. /// Allocate space in a buffer.
  437. /// </summary>
  438. /// <remarks>The space is allocated in the buffer which was specified
  439. /// with <see cref="BeginForEachIndex"/>.</remarks>
  440. /// <param name="size">The number of bytes to allocate.</param>
  441. /// <returns>The allocation.</returns>
  442. public byte* Allocate(int size)
  443. {
  444. byte* ptr = m_CurrentPtr;
  445. m_CurrentPtr += size;
  446. if (m_CurrentPtr > m_CurrentBlockEnd)
  447. {
  448. UnsafeStreamBlock* oldBlock = m_CurrentBlock;
  449. var blockData = (UnsafeStreamBlockData*)m_BlockData.Range.Pointer;
  450. m_CurrentBlock = blockData->Allocate(oldBlock, m_ThreadIndex);
  451. m_CurrentPtr = m_CurrentBlock->Data;
  452. if (m_FirstBlock == null)
  453. {
  454. m_FirstOffset = (int)(m_CurrentPtr - (byte*)m_CurrentBlock);
  455. m_FirstBlock = m_CurrentBlock;
  456. }
  457. else
  458. {
  459. m_NumberOfBlocks++;
  460. }
  461. m_CurrentBlockEnd = (byte*)m_CurrentBlock + UnsafeStreamBlockData.AllocationSize;
  462. ptr = m_CurrentPtr;
  463. m_CurrentPtr += size;
  464. }
  465. m_ElementCount++;
  466. return ptr;
  467. }
  468. }
  469. /// <summary>
  470. /// Reads data from a buffer of an <see cref="UnsafeStream"/>.
  471. /// </summary>
  472. /// <remarks>An individual reader can only be used for one buffer of one stream.
  473. /// Do not create more than one reader for an individual buffer.</remarks>
  474. [GenerateTestsForBurstCompatibility]
  475. public unsafe struct Reader
  476. {
  477. [NativeDisableUnsafePtrRestriction]
  478. internal AllocatorManager.Block m_BlockData;
  479. [NativeDisableUnsafePtrRestriction]
  480. internal UnsafeStreamBlock* m_CurrentBlock;
  481. [NativeDisableUnsafePtrRestriction]
  482. internal byte* m_CurrentPtr;
  483. [NativeDisableUnsafePtrRestriction]
  484. internal byte* m_CurrentBlockEnd;
  485. internal int m_RemainingItemCount;
  486. internal int m_LastBlockSize;
  487. internal Reader(ref UnsafeStream stream)
  488. {
  489. m_BlockData = stream.m_BlockData;
  490. m_CurrentBlock = null;
  491. m_CurrentPtr = null;
  492. m_CurrentBlockEnd = null;
  493. m_RemainingItemCount = 0;
  494. m_LastBlockSize = 0;
  495. }
  496. /// <summary>
  497. /// Readies this reader to read a particular buffer of the stream.
  498. /// </summary>
  499. /// <remarks>Must be called before using this reader. For an individual reader, call this method only once.
  500. ///
  501. /// When done using this reader, you must call <see cref="EndForEachIndex"/>.</remarks>
  502. /// <param name="foreachIndex">The index of the buffer to read.</param>
  503. /// <returns>The number of remaining elements to read from the buffer.</returns>
  504. public int BeginForEachIndex(int foreachIndex)
  505. {
  506. var blockData = (UnsafeStreamBlockData*)m_BlockData.Range.Pointer;
  507. var ranges = (UnsafeStreamRange*)blockData->Ranges.Range.Pointer;
  508. m_RemainingItemCount = ranges[foreachIndex].ElementCount;
  509. m_LastBlockSize = ranges[foreachIndex].LastOffset;
  510. m_CurrentBlock = ranges[foreachIndex].Block;
  511. m_CurrentPtr = (byte*)m_CurrentBlock + ranges[foreachIndex].OffsetInFirstBlock;
  512. m_CurrentBlockEnd = (byte*)m_CurrentBlock + UnsafeStreamBlockData.AllocationSize;
  513. return m_RemainingItemCount;
  514. }
  515. /// <summary>
  516. /// Does nothing.
  517. /// </summary>
  518. /// <remarks>Included only for consistency with <see cref="NativeStream"/>.</remarks>
  519. public void EndForEachIndex()
  520. {
  521. }
  522. /// <summary>
  523. /// The number of buffers in the stream of this reader.
  524. /// </summary>
  525. /// <value>The number of buffers in the stream of this reader.</value>
  526. public int ForEachCount => ((UnsafeStreamBlockData*)m_BlockData.Range.Pointer)->RangeCount;
  527. /// <summary>
  528. /// The number of items not yet read from the buffer.
  529. /// </summary>
  530. /// <value>The number of items not yet read from the buffer.</value>
  531. public int RemainingItemCount => m_RemainingItemCount;
  532. /// <summary>
  533. /// Returns a pointer to the next position to read from the buffer. Advances the reader some number of bytes.
  534. /// </summary>
  535. /// <param name="size">The number of bytes to advance the reader.</param>
  536. /// <returns>A pointer to the next position to read from the buffer.</returns>
  537. /// <exception cref="System.ArgumentException">Thrown if the reader has been advanced past the end of the buffer.</exception>
  538. public byte* ReadUnsafePtr(int size)
  539. {
  540. m_RemainingItemCount--;
  541. byte* ptr = m_CurrentPtr;
  542. m_CurrentPtr += size;
  543. if (m_CurrentPtr > m_CurrentBlockEnd)
  544. {
  545. m_CurrentBlock = m_CurrentBlock->Next;
  546. m_CurrentPtr = m_CurrentBlock->Data;
  547. m_CurrentBlockEnd = (byte*)m_CurrentBlock + UnsafeStreamBlockData.AllocationSize;
  548. ptr = m_CurrentPtr;
  549. m_CurrentPtr += size;
  550. }
  551. return ptr;
  552. }
  553. /// <summary>
  554. /// Reads the next value from the buffer.
  555. /// </summary>
  556. /// <remarks>Each read advances the reader to the next item in the buffer.</remarks>
  557. /// <typeparam name="T">The type of value to read.</typeparam>
  558. /// <returns>A reference to the next value from the buffer.</returns>
  559. [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
  560. public ref T Read<T>() where T : unmanaged
  561. {
  562. int size = UnsafeUtility.SizeOf<T>();
  563. return ref UnsafeUtility.AsRef<T>(ReadUnsafePtr(size));
  564. }
  565. /// <summary>
  566. /// Reads the next value from the buffer. Does not advance the reader.
  567. /// </summary>
  568. /// <typeparam name="T">The type of value to read.</typeparam>
  569. /// <returns>A reference to the next value from the buffer.</returns>
  570. [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
  571. public ref T Peek<T>() where T : unmanaged
  572. {
  573. int size = UnsafeUtility.SizeOf<T>();
  574. byte* ptr = m_CurrentPtr;
  575. if (ptr + size > m_CurrentBlockEnd)
  576. {
  577. ptr = m_CurrentBlock->Next->Data;
  578. }
  579. return ref UnsafeUtility.AsRef<T>(ptr);
  580. }
  581. /// <summary>
  582. /// Returns the total number of items in the buffers of the stream.
  583. /// </summary>
  584. /// <returns>The total number of items in the buffers of the stream.</returns>
  585. public int Count()
  586. {
  587. var blockData = (UnsafeStreamBlockData*)m_BlockData.Range.Pointer;
  588. var ranges = (UnsafeStreamRange*)blockData->Ranges.Range.Pointer;
  589. int itemCount = 0;
  590. for (int i = 0; i != blockData->RangeCount; i++)
  591. {
  592. itemCount += ranges[i].ElementCount;
  593. }
  594. return itemCount;
  595. }
  596. }
  597. }
  598. }