Ingen beskrivning
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 26KB

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