No Description
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.

DataStreamReader.cs 37KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809
  1. using System;
  2. using System.Diagnostics;
  3. using Unity.Collections.LowLevel.Unsafe;
  4. using UnityEngine.Scripting.APIUpdating;
  5. namespace Unity.Collections
  6. {
  7. /// <summary>
  8. /// Writes data in an endian format to deserialize data.
  9. /// </summary>
  10. /// <remarks>
  11. /// The DataStreamReader class is the counterpart of the
  12. /// <see cref="DataStreamWriter"/> class and can be be used to deserialize
  13. /// data which was prepared with it.
  14. ///
  15. /// DataStreamWriter writes this data in the endian format native
  16. /// to the current machine architecture.
  17. /// <br/>
  18. /// For network byte order use the so named methods.
  19. /// <br/>
  20. /// Simple usage example:
  21. /// <code>
  22. /// using (var dataWriter = new DataStreamWriter(16, Allocator.Persistent))
  23. /// {
  24. /// dataWriter.Write(42);
  25. /// dataWriter.Write(1234);
  26. /// // Length is the actual amount of data inside the writer,
  27. /// // Capacity is the total amount.
  28. /// var dataReader = new DataStreamReader(dataWriter, 0, dataWriter.Length);
  29. /// var context = default(DataStreamReader.Context);
  30. /// var myFirstInt = dataReader.ReadInt(ref context);
  31. /// var mySecondInt = dataReader.ReadInt(ref context);
  32. /// }
  33. /// </code>
  34. ///
  35. /// DataStreamReader carries the position of the read pointer inside the struct,
  36. /// taking a copy of the reader will also copy the read position. This includes passing the
  37. /// reader to a method by value instead of by ref.
  38. ///
  39. /// <seealso cref="DataStreamWriter"/>
  40. /// <seealso cref="IsLittleEndian"/>
  41. /// </remarks>
  42. [MovedFrom(true, "Unity.Networking.Transport")]
  43. [GenerateTestsForBurstCompatibility]
  44. public unsafe struct DataStreamReader
  45. {
  46. struct Context
  47. {
  48. public int m_ReadByteIndex;
  49. public int m_BitIndex;
  50. public ulong m_BitBuffer;
  51. public int m_FailedReads;
  52. }
  53. [NativeDisableUnsafePtrRestriction] internal byte* m_BufferPtr;
  54. Context m_Context;
  55. int m_Length;
  56. #if ENABLE_UNITY_COLLECTIONS_CHECKS
  57. AtomicSafetyHandle m_Safety;
  58. #endif
  59. /// <summary>
  60. /// Initializes a new instance of the DataStreamReader struct with a NativeArray&lt;byte&gt;
  61. /// </summary>
  62. /// <param name="array">The buffer to attach to the DataStreamReader.</param>
  63. public DataStreamReader(NativeArray<byte> array)
  64. {
  65. Initialize(out this, array);
  66. }
  67. static void Initialize(out DataStreamReader self, NativeArray<byte> array)
  68. {
  69. #if ENABLE_UNITY_COLLECTIONS_CHECKS
  70. self.m_Safety = NativeArrayUnsafeUtility.GetAtomicSafetyHandle(array);
  71. #endif
  72. self.m_BufferPtr = (byte*)array.GetUnsafeReadOnlyPtr();
  73. self.m_Length = array.Length;
  74. self.m_Context = default;
  75. }
  76. /// <summary>
  77. /// Show the byte order in which the current computer architecture stores data.
  78. /// </summary>
  79. /// <remarks>
  80. /// Different computer architectures store data using different byte orders.
  81. /// <list type="bullet">
  82. /// <item>Big-endian: the most significant byte is at the left end of a word.</item>
  83. /// <item>Little-endian: means the most significant byte is at the right end of a word.</item>
  84. /// </list>
  85. /// </remarks>
  86. public static bool IsLittleEndian { get { return DataStreamWriter.IsLittleEndian; } }
  87. static short ByteSwap(short val)
  88. {
  89. return (short)(((val & 0xff) << 8) | ((val >> 8) & 0xff));
  90. }
  91. static int ByteSwap(int val)
  92. {
  93. return (int)(((val & 0xff) << 24) | ((val & 0xff00) << 8) | ((val >> 8) & 0xff00) | ((val >> 24) & 0xff));
  94. }
  95. /// <summary>
  96. /// If there is a read failure this returns true. A read failure might happen if this attempts to read more than there is capacity for.
  97. /// </summary>
  98. public readonly bool HasFailedReads => m_Context.m_FailedReads > 0;
  99. /// <summary>
  100. /// The total size of the buffer space this reader is working with.
  101. /// </summary>
  102. public readonly int Length
  103. {
  104. get
  105. {
  106. CheckRead();
  107. return m_Length;
  108. }
  109. }
  110. /// <summary>
  111. /// True if the reader has been pointed to a valid buffer space. This
  112. /// would be false if the reader was created with no arguments.
  113. /// </summary>
  114. public readonly bool IsCreated
  115. {
  116. get { return m_BufferPtr != null; }
  117. }
  118. void ReadBytesInternal(byte* data, int length)
  119. {
  120. CheckRead();
  121. if (GetBytesRead() + length > m_Length)
  122. {
  123. ++m_Context.m_FailedReads;
  124. #if (ENABLE_UNITY_COLLECTIONS_CHECKS || UNITY_DOTS_DEBUG)
  125. UnityEngine.Debug.LogError($"Trying to read {length} bytes from a stream where only {m_Length - GetBytesRead()} are available");
  126. #endif
  127. UnsafeUtility.MemClear(data, length);
  128. return;
  129. }
  130. // Restore the full bytes moved to the bit buffer but no consumed
  131. m_Context.m_ReadByteIndex -= (m_Context.m_BitIndex >> 3);
  132. m_Context.m_BitIndex = 0;
  133. m_Context.m_BitBuffer = 0;
  134. UnsafeUtility.MemCpy(data, m_BufferPtr + m_Context.m_ReadByteIndex, length);
  135. m_Context.m_ReadByteIndex += length;
  136. }
  137. /// <summary>
  138. /// Read and copy data into the given NativeArray of bytes. An error will
  139. /// be logged if not enough bytes are available to fill the array, and
  140. /// <see cref="HasFailedReads"/> will then be true.
  141. /// </summary>
  142. /// <param name="array">Array to copy data into.</param>
  143. public void ReadBytes(NativeArray<byte> array)
  144. {
  145. ReadBytesInternal((byte*)array.GetUnsafePtr(), array.Length);
  146. }
  147. /// <summary>
  148. /// Read and copy data into the given <c>Span</c> of bytes. An error will
  149. /// be logged if not enough bytes are available to fill the array, and
  150. /// <see cref="HasFailedReads"/> will then be true.
  151. /// </summary>
  152. /// <param name="span">Span to copy data into.</param>
  153. public void ReadBytes(Span<byte> span)
  154. {
  155. fixed (byte* ptr = span)
  156. {
  157. ReadBytesInternal(ptr, span.Length);
  158. }
  159. }
  160. /// <summary>
  161. /// Gets the number of bytes read from the data stream.
  162. /// </summary>
  163. /// <returns>Number of bytes read.</returns>
  164. public int GetBytesRead()
  165. {
  166. return m_Context.m_ReadByteIndex - (m_Context.m_BitIndex >> 3);
  167. }
  168. /// <summary>
  169. /// Gets the number of bits read from the data stream.
  170. /// </summary>
  171. /// <returns>Number of bits read.</returns>
  172. public int GetBitsRead()
  173. {
  174. return (m_Context.m_ReadByteIndex << 3) - m_Context.m_BitIndex;
  175. }
  176. /// <summary>
  177. /// Sets the current position of this stream to the given value.
  178. /// An error will be logged if <paramref name="pos"/> is outside the length of the stream.
  179. /// <br/>
  180. /// In addition this will reset the bit index and the bit buffer.
  181. /// </summary>
  182. /// <param name="pos">Seek position.</param>
  183. public void SeekSet(int pos)
  184. {
  185. if (pos > m_Length)
  186. {
  187. ++m_Context.m_FailedReads;
  188. #if (ENABLE_UNITY_COLLECTIONS_CHECKS || UNITY_DOTS_DEBUG)
  189. UnityEngine.Debug.LogError($"Trying to seek to {pos} in a stream of length {m_Length}");
  190. #endif
  191. return;
  192. }
  193. m_Context.m_ReadByteIndex = pos;
  194. m_Context.m_BitIndex = 0;
  195. m_Context.m_BitBuffer = 0UL;
  196. }
  197. /// <summary>
  198. /// Reads an unsigned byte from the current stream and advances the current position of the stream by one byte.
  199. /// </summary>
  200. /// <returns>The next byte read from the current stream, or 0 if the end of the stream has been reached.</returns>
  201. public byte ReadByte()
  202. {
  203. byte data;
  204. ReadBytesInternal((byte*)&data, sizeof(byte));
  205. return data;
  206. }
  207. /// <summary>
  208. /// Reads a 2-byte signed short from the current stream and advances the current position of the stream by two bytes.
  209. /// </summary>
  210. /// <returns>A 2-byte signed short read from the current stream, or 0 if the end of the stream has been reached.</returns>
  211. public short ReadShort()
  212. {
  213. short data;
  214. ReadBytesInternal((byte*)&data, sizeof(short));
  215. return data;
  216. }
  217. /// <summary>
  218. /// Reads a 2-byte unsigned short from the current stream and advances the current position of the stream by two bytes.
  219. /// </summary>
  220. /// <returns>A 2-byte unsigned short read from the current stream, or 0 if the end of the stream has been reached.</returns>
  221. public ushort ReadUShort()
  222. {
  223. ushort data;
  224. ReadBytesInternal((byte*)&data, sizeof(ushort));
  225. return data;
  226. }
  227. /// <summary>
  228. /// Reads a 4-byte signed integer from the current stream and advances the current position of the stream by four bytes.
  229. /// </summary>
  230. /// <returns>A 4-byte signed integer read from the current stream, or 0 if the end of the stream has been reached.</returns>
  231. public int ReadInt()
  232. {
  233. int data;
  234. ReadBytesInternal((byte*)&data, sizeof(int));
  235. return data;
  236. }
  237. /// <summary>
  238. /// Reads a 4-byte unsigned integer from the current stream and advances the current position of the stream by four bytes.
  239. /// </summary>
  240. /// <returns>A 4-byte unsigned integer read from the current stream, or 0 if the end of the stream has been reached.</returns>
  241. public uint ReadUInt()
  242. {
  243. uint data;
  244. ReadBytesInternal((byte*)&data, sizeof(uint));
  245. return data;
  246. }
  247. /// <summary>
  248. /// Reads an 8-byte signed long from the stream and advances the current position of the stream by eight bytes.
  249. /// </summary>
  250. /// <returns>An 8-byte signed long read from the current stream, or 0 if the end of the stream has been reached.</returns>
  251. public long ReadLong()
  252. {
  253. long data;
  254. ReadBytesInternal((byte*)&data, sizeof(long));
  255. return data;
  256. }
  257. /// <summary>
  258. /// Reads an 8-byte unsigned long from the stream and advances the current position of the stream by eight bytes.
  259. /// </summary>
  260. /// <returns>An 8-byte unsigned long read from the current stream, or 0 if the end of the stream has been reached.</returns>
  261. public ulong ReadULong()
  262. {
  263. ulong data;
  264. ReadBytesInternal((byte*)&data, sizeof(ulong));
  265. return data;
  266. }
  267. /// <summary>
  268. /// Reads a 2-byte signed short from the current stream in Big-endian byte order and advances the current position of the stream by two bytes.
  269. /// If the current endianness is in little-endian order, the byte order will be swapped.
  270. /// </summary>
  271. /// <returns>A 2-byte signed short read from the current stream, or 0 if the end of the stream has been reached.</returns>
  272. public short ReadShortNetworkByteOrder()
  273. {
  274. short data;
  275. ReadBytesInternal((byte*)&data, sizeof(short));
  276. return IsLittleEndian ? ByteSwap(data) : data;
  277. }
  278. /// <summary>
  279. /// Reads a 2-byte unsigned short from the current stream in Big-endian byte order and advances the current position of the stream by two bytes.
  280. /// If the current endianness is in little-endian order, the byte order will be swapped.
  281. /// </summary>
  282. /// <returns>A 2-byte unsigned short read from the current stream, or 0 if the end of the stream has been reached.</returns>
  283. public ushort ReadUShortNetworkByteOrder()
  284. {
  285. return (ushort)ReadShortNetworkByteOrder();
  286. }
  287. /// <summary>
  288. /// Reads a 4-byte signed integer from the current stream in Big-endian byte order and advances the current position of the stream by four bytes.
  289. /// If the current endianness is in little-endian order, the byte order will be swapped.
  290. /// </summary>
  291. /// <returns>A 4-byte signed integer read from the current stream, or 0 if the end of the stream has been reached.</returns>
  292. public int ReadIntNetworkByteOrder()
  293. {
  294. int data;
  295. ReadBytesInternal((byte*)&data, sizeof(int));
  296. return IsLittleEndian ? ByteSwap(data) : data;
  297. }
  298. /// <summary>
  299. /// Reads a 4-byte unsigned integer from the current stream in Big-endian byte order and advances the current position of the stream by four bytes.
  300. /// If the current endianness is in little-endian order, the byte order will be swapped.
  301. /// </summary>
  302. /// <returns>A 4-byte unsigned integer read from the current stream, or 0 if the end of the stream has been reached.</returns>
  303. public uint ReadUIntNetworkByteOrder()
  304. {
  305. return (uint)ReadIntNetworkByteOrder();
  306. }
  307. /// <summary>
  308. /// Reads a 4-byte floating point value from the current stream and advances the current position of the stream by four bytes.
  309. /// </summary>
  310. /// <returns>A 4-byte floating point value read from the current stream, or 0 if the end of the stream has been reached.</returns>
  311. public float ReadFloat()
  312. {
  313. UIntFloat uf = new UIntFloat();
  314. uf.intValue = (uint)ReadInt();
  315. return uf.floatValue;
  316. }
  317. /// <summary>
  318. /// Reads a 8-byte floating point value from the current stream and advances the current position of the stream by four bytes.
  319. /// </summary>
  320. /// <returns>A 8-byte floating point value read from the current stream, or 0 if the end of the stream has been reached.</returns>
  321. public double ReadDouble()
  322. {
  323. UIntFloat uf = new UIntFloat();
  324. uf.longValue = (ulong)ReadLong();
  325. return uf.doubleValue;
  326. }
  327. /// <summary>
  328. /// Reads a 4-byte unsigned integer from the current stream using a <see cref="StreamCompressionModel"/> and advances the current position the number of bits depending on the model.
  329. /// </summary>
  330. /// <param name="model"><see cref="StreamCompressionModel"/> model for reading value in a packed manner.</param>
  331. /// <returns>A 4-byte unsigned integer read from the current stream, or 0 if the end of the stream has been reached.</returns>
  332. public uint ReadPackedUInt(in StreamCompressionModel model)
  333. {
  334. return ReadPackedUIntInternal(StreamCompressionModel.k_MaxHuffmanSymbolLength, model);
  335. }
  336. uint ReadPackedUIntInternal(int maxSymbolLength, in StreamCompressionModel model)
  337. {
  338. CheckRead();
  339. FillBitBuffer();
  340. uint peekMask = (1u << maxSymbolLength) - 1u;
  341. uint peekBits = (uint)m_Context.m_BitBuffer & peekMask;
  342. ushort huffmanEntry = model.decodeTable[(int)peekBits];
  343. int symbol = huffmanEntry >> 8;
  344. int length = huffmanEntry & 0xFF;
  345. if (m_Context.m_BitIndex < length)
  346. {
  347. ++m_Context.m_FailedReads;
  348. #if (ENABLE_UNITY_COLLECTIONS_CHECKS || UNITY_DOTS_DEBUG)
  349. UnityEngine.Debug.LogError($"Trying to read {length} bits from a stream where only {m_Context.m_BitIndex} are available");
  350. #endif
  351. return 0;
  352. }
  353. // Skip Huffman bits
  354. m_Context.m_BitBuffer >>= length;
  355. m_Context.m_BitIndex -= length;
  356. uint offset = model.bucketOffsets[symbol];
  357. byte bits = model.bucketSizes[symbol];
  358. return ReadRawBitsInternal(bits) + offset;
  359. }
  360. void FillBitBuffer()
  361. {
  362. while (m_Context.m_BitIndex <= 56 && m_Context.m_ReadByteIndex < m_Length)
  363. {
  364. m_Context.m_BitBuffer |= (ulong)m_BufferPtr[m_Context.m_ReadByteIndex++] << m_Context.m_BitIndex;
  365. m_Context.m_BitIndex += 8;
  366. }
  367. }
  368. uint ReadRawBitsInternal(int numbits)
  369. {
  370. CheckBits(numbits);
  371. if (m_Context.m_BitIndex < numbits)
  372. {
  373. ++m_Context.m_FailedReads;
  374. #if (ENABLE_UNITY_COLLECTIONS_CHECKS || UNITY_DOTS_DEBUG)
  375. UnityEngine.Debug.LogError($"Trying to read {numbits} bits from a stream where only {m_Context.m_BitIndex} are available");
  376. #endif
  377. return 0;
  378. }
  379. uint res = (uint)(m_Context.m_BitBuffer & ((1UL << numbits) - 1UL));
  380. m_Context.m_BitBuffer >>= numbits;
  381. m_Context.m_BitIndex -= numbits;
  382. return res;
  383. }
  384. /// <summary>
  385. /// Reads a specified number of bits from the data stream.
  386. /// </summary>
  387. /// <param name="numbits">A positive number of bytes to write.</param>
  388. /// <returns>A 4-byte unsigned integer read from the current stream, or 0 if the end of the stream has been reached.</returns>
  389. public uint ReadRawBits(int numbits)
  390. {
  391. CheckRead();
  392. FillBitBuffer();
  393. return ReadRawBitsInternal(numbits);
  394. }
  395. /// <summary>
  396. /// Reads an 8-byte unsigned long value from the data stream using a <see cref="StreamCompressionModel"/>.
  397. /// </summary>
  398. /// <param name="model"><see cref="StreamCompressionModel"/> model for reading value in a packed manner.</param>
  399. /// <returns>An 8-byte unsigned long read from the current stream, or 0 if the end of the stream has been reached.</returns>
  400. public ulong ReadPackedULong(in StreamCompressionModel model)
  401. {
  402. ulong value;
  403. ((uint*)&value)[0] = ReadPackedUInt(model);
  404. ((uint*)&value)[1] = ReadPackedUInt(model);
  405. return value;
  406. }
  407. /// <summary>
  408. /// Reads a 4-byte signed integer value from the data stream using a <see cref="StreamCompressionModel"/>.
  409. /// <br/>
  410. /// Negative values de-interleaves from positive values before returning, for example (0, -1, 1, -2, 2) -> (-2, -1, 0, 1, 2)
  411. /// </summary>
  412. /// <param name="model"><see cref="StreamCompressionModel"/> model for reading value in a packed manner.</param>
  413. /// <returns>A 4-byte signed integer read from the current stream, or 0 if the end of the stream has been reached.</returns>
  414. public int ReadPackedInt(in StreamCompressionModel model)
  415. {
  416. uint folded = ReadPackedUInt(model);
  417. return (int)(folded >> 1) ^ -(int)(folded & 1); // Deinterleave values from [0, -1, 1, -2, 2...] to [..., -2, -1, -0, 1, 2, ...]
  418. }
  419. /// <summary>
  420. /// Reads an 8-byte signed long value from the data stream using a <see cref="StreamCompressionModel"/>.
  421. /// <br/>
  422. /// Negative values de-interleaves from positive values before returning, for example (0, -1, 1, -2, 2) -> (-2, -1, 0, 1, 2)
  423. /// </summary>
  424. /// <param name="model"><see cref="StreamCompressionModel"/> model for reading value in a packed manner.</param>
  425. /// <returns>An 8-byte signed long read from the current stream, or 0 if the end of the stream has been reached.</returns>
  426. public long ReadPackedLong(in StreamCompressionModel model)
  427. {
  428. ulong folded = ReadPackedULong(model);
  429. return (long)(folded >> 1) ^ -(long)(folded & 1); // Deinterleave values from [0, -1, 1, -2, 2...] to [..., -2, -1, -0, 1, 2, ...]
  430. }
  431. /// <summary>
  432. /// Reads a 4-byte floating point value from the data stream using a <see cref="StreamCompressionModel"/>.
  433. /// </summary>
  434. /// <param name="model"><see cref="StreamCompressionModel"/> model for reading value in a packed manner.</param>
  435. /// <returns>A 4-byte floating point value read from the current stream, or 0 if the end of the stream has been reached.</returns>
  436. public float ReadPackedFloat(in StreamCompressionModel model)
  437. {
  438. return ReadPackedFloatDelta(0, model);
  439. }
  440. /// <summary>
  441. /// Reads a 8-byte floating point value from the data stream using a <see cref="StreamCompressionModel"/>.
  442. /// </summary>
  443. /// <param name="model"><see cref="StreamCompressionModel"/> model for reading value in a packed manner.</param>
  444. /// <returns>A 8-byte floating point value read from the current stream, or 0 if the end of the stream has been reached.</returns>
  445. public double ReadPackedDouble(in StreamCompressionModel model)
  446. {
  447. return ReadPackedDoubleDelta(0, model);
  448. }
  449. /// <summary>
  450. /// Reads a 4-byte signed integer delta value from the data stream using a <see cref="StreamCompressionModel"/>.
  451. /// </summary>
  452. /// <param name="baseline">The previous 4-byte signed integer value, used to compute the diff.</param>
  453. /// <param name="model"><see cref="StreamCompressionModel"/> model for reading value in a packed manner.</param>
  454. /// <returns>A 4-byte signed integer read from the current stream, or 0 if the end of the stream has been reached.
  455. /// If the data did not change, this also returns 0.
  456. /// <br/>
  457. /// See: <see cref="HasFailedReads"/> to verify if the read failed.</returns>
  458. public int ReadPackedIntDelta(int baseline, in StreamCompressionModel model)
  459. {
  460. int delta = ReadPackedInt(model);
  461. return baseline - delta;
  462. }
  463. /// <summary>
  464. /// Reads a 4-byte unsigned integer delta value from the data stream using a <see cref="StreamCompressionModel"/>.
  465. /// </summary>
  466. /// <param name="baseline">The previous 4-byte unsigned integer value, used to compute the diff.</param>
  467. /// <param name="model"><see cref="StreamCompressionModel"/> model for reading value in a packed manner.</param>
  468. /// <returns>A 4-byte unsigned integer read from the current stream, or 0 if the end of the stream has been reached.
  469. /// If the data did not change, this also returns 0.
  470. /// <br/>
  471. /// See: <see cref="HasFailedReads"/> to verify if the read failed.</returns>
  472. public uint ReadPackedUIntDelta(uint baseline, in StreamCompressionModel model)
  473. {
  474. uint delta = (uint)ReadPackedInt(model);
  475. return baseline - delta;
  476. }
  477. /// <summary>
  478. /// Reads an 8-byte signed long delta value from the data stream using a <see cref="StreamCompressionModel"/>.
  479. /// </summary>
  480. /// <param name="baseline">The previous 8-byte signed long value, used to compute the diff.</param>
  481. /// <param name="model"><see cref="StreamCompressionModel"/> model for reading value in a packed manner.</param>
  482. /// <returns>An 8-byte signed long read from the current stream, or 0 if the end of the stream has been reached.
  483. /// If the data did not change, this also returns 0.
  484. /// <br/>
  485. /// See: <see cref="HasFailedReads"/> to verify if the read failed.</returns>
  486. public long ReadPackedLongDelta(long baseline, in StreamCompressionModel model)
  487. {
  488. long delta = ReadPackedLong(model);
  489. return baseline - delta;
  490. }
  491. /// <summary>
  492. /// Reads an 8-byte unsigned long delta value from the data stream using a <see cref="StreamCompressionModel"/>.
  493. /// </summary>
  494. /// <param name="baseline">The previous 8-byte unsigned long value, used to compute the diff.</param>
  495. /// <param name="model"><see cref="StreamCompressionModel"/> model for reading value in a packed manner.</param>
  496. /// <returns>An 8-byte unsigned long read from the current stream, or 0 if the end of the stream has been reached.
  497. /// If the data did not change, this also returns 0.
  498. /// <br/>
  499. /// See: <see cref="HasFailedReads"/> to verify if the read failed.</returns>
  500. public ulong ReadPackedULongDelta(ulong baseline, in StreamCompressionModel model)
  501. {
  502. ulong delta = (ulong)ReadPackedLong(model);
  503. return baseline - delta;
  504. }
  505. /// <summary>
  506. /// Reads a 4-byte floating point value from the data stream.
  507. ///
  508. /// If the first bit is 0, the data did not change and <paramref name="baseline"/> will be returned.
  509. /// </summary>
  510. /// <param name="baseline">The previous 4-byte floating point value.</param>
  511. /// <param name="model">Not currently used.</param>
  512. /// <returns>A 4-byte floating point value read from the current stream, or <paramref name="baseline"/> if there are no changes to the value.
  513. /// <br/>
  514. /// See: <see cref="HasFailedReads"/> to verify if the read failed.</returns>
  515. public float ReadPackedFloatDelta(float baseline, in StreamCompressionModel model)
  516. {
  517. CheckRead();
  518. FillBitBuffer();
  519. if (ReadRawBitsInternal(1) == 0)
  520. return baseline;
  521. var bits = 32;
  522. UIntFloat uf = new UIntFloat();
  523. uf.intValue = ReadRawBitsInternal(bits);
  524. return uf.floatValue;
  525. }
  526. /// <summary>
  527. /// Reads a 8-byte floating point value from the data stream.
  528. ///
  529. /// If the first bit is 0, the data did not change and <paramref name="baseline"/> will be returned.
  530. /// </summary>
  531. /// <param name="baseline">The previous 8-byte floating point value.</param>
  532. /// <param name="model">Not currently used.</param>
  533. /// <returns>A 8-byte floating point value read from the current stream, or <paramref name="baseline"/> if there are no changes to the value.
  534. /// <br/>
  535. /// See: <see cref="HasFailedReads"/> to verify if the read failed.</returns>
  536. public double ReadPackedDoubleDelta(double baseline, in StreamCompressionModel model)
  537. {
  538. CheckRead();
  539. FillBitBuffer();
  540. if (ReadRawBitsInternal(1) == 0)
  541. return baseline;
  542. var bits = 32;
  543. UIntFloat uf = new UIntFloat();
  544. var data = (uint*)&uf.longValue;
  545. data[0] = ReadRawBitsInternal(bits);
  546. FillBitBuffer();
  547. data[1] |= ReadRawBitsInternal(bits);
  548. return uf.doubleValue;
  549. }
  550. /// <summary>
  551. /// Reads a <c>FixedString32Bytes</c> value from the current stream and advances the current position of the stream by the length of the string.
  552. /// </summary>
  553. /// <returns>A <c>FixedString32Bytes</c> value read from the current stream, or 0 if the end of the stream has been reached.</returns>
  554. public unsafe FixedString32Bytes ReadFixedString32()
  555. {
  556. FixedString32Bytes str;
  557. byte* data = ((byte*)&str) + 2;
  558. *(ushort*)&str = ReadFixedStringInternal(data, str.Capacity);
  559. return str;
  560. }
  561. /// <summary>
  562. /// Reads a <c>FixedString64Bytes</c> value from the current stream and advances the current position of the stream by the length of the string.
  563. /// </summary>
  564. /// <returns>A <c>FixedString64Bytes</c> value read from the current stream, or 0 if the end of the stream has been reached.</returns>
  565. public unsafe FixedString64Bytes ReadFixedString64()
  566. {
  567. FixedString64Bytes str;
  568. byte* data = ((byte*)&str) + 2;
  569. *(ushort*)&str = ReadFixedStringInternal(data, str.Capacity);
  570. return str;
  571. }
  572. /// <summary>
  573. /// Reads a <c>FixedString128Bytes</c> value from the current stream and advances the current position of the stream by the length of the string.
  574. /// </summary>
  575. /// <returns>A <c>FixedString128Bytes</c> value read from the current stream, or 0 if the end of the stream has been reached.</returns>
  576. public unsafe FixedString128Bytes ReadFixedString128()
  577. {
  578. FixedString128Bytes str;
  579. byte* data = ((byte*)&str) + 2;
  580. *(ushort*)&str = ReadFixedStringInternal(data, str.Capacity);
  581. return str;
  582. }
  583. /// <summary>
  584. /// Reads a <c>FixedString512Bytes</c> value from the current stream and advances the current position of the stream by the length of the string.
  585. /// </summary>
  586. /// <returns>A <c>FixedString512Bytes</c> value read from the current stream, or 0 if the end of the stream has been reached.</returns>
  587. public unsafe FixedString512Bytes ReadFixedString512()
  588. {
  589. FixedString512Bytes str;
  590. byte* data = ((byte*)&str) + 2;
  591. *(ushort*)&str = ReadFixedStringInternal(data, str.Capacity);
  592. return str;
  593. }
  594. /// <summary>
  595. /// Reads a <c>FixedString4096Bytes</c> value from the current stream and advances the current position of the stream by the length of the string.
  596. /// </summary>
  597. /// <returns>A <c>FixedString4096Bytes</c> value read from the current stream, or 0 if the end of the stream has been reached.</returns>
  598. public unsafe FixedString4096Bytes ReadFixedString4096()
  599. {
  600. FixedString4096Bytes str;
  601. byte* data = ((byte*)&str) + 2;
  602. *(ushort*)&str = ReadFixedStringInternal(data, str.Capacity);
  603. return str;
  604. }
  605. /// <summary>
  606. /// Read and copy data into the given NativeArray of bytes, an error will
  607. /// be logged if not enough bytes are available in the array.
  608. /// </summary>
  609. /// <param name="array">Buffer to write the string bytes to.</param>
  610. /// <returns>Length of data read into byte array, or zero if error occurred.</returns>
  611. public ushort ReadFixedString(NativeArray<byte> array)
  612. {
  613. return ReadFixedStringInternal((byte*)array.GetUnsafePtr(), array.Length);
  614. }
  615. unsafe ushort ReadFixedStringInternal(byte* data, int maxLength)
  616. {
  617. ushort length = ReadUShort();
  618. if (length > maxLength)
  619. {
  620. #if (ENABLE_UNITY_COLLECTIONS_CHECKS || UNITY_DOTS_DEBUG)
  621. UnityEngine.Debug.LogError($"Trying to read a string of length {length} but max length is {maxLength}");
  622. #endif
  623. return 0;
  624. }
  625. ReadBytesInternal(data, length);
  626. return length;
  627. }
  628. /// <summary>
  629. /// Reads a <c>FixedString32Bytes</c> delta value to the data stream using a <see cref="StreamCompressionModel"/>.
  630. /// </summary>
  631. /// <param name="baseline">The previous <c>FixedString32Bytes</c> value, used to compute the diff.</param>
  632. /// <param name="model"><see cref="StreamCompressionModel"/> model for writing value in a packed manner.</param>
  633. /// <returns>A <c>FixedString32Bytes</c> value read from the current stream, or 0 if the end of the stream has been reached.</returns>
  634. public unsafe FixedString32Bytes ReadPackedFixedString32Delta(FixedString32Bytes baseline, in StreamCompressionModel model)
  635. {
  636. FixedString32Bytes str;
  637. byte* data = ((byte*)&str) + 2;
  638. *(ushort*)&str = ReadPackedFixedStringDeltaInternal(data, str.Capacity, ((byte*)&baseline) + 2, *((ushort*)&baseline), model);
  639. return str;
  640. }
  641. /// <summary>
  642. /// Reads a <c>FixedString64Bytes</c> delta value to the data stream using a <see cref="StreamCompressionModel"/>.
  643. /// </summary>
  644. /// <param name="baseline">The previous <c>FixedString64Bytes</c> value, used to compute the diff.</param>
  645. /// <param name="model"><see cref="StreamCompressionModel"/> model for writing value in a packed manner.</param>
  646. /// <returns>A <c>FixedString64Bytes</c> value read from the current stream, or 0 if the end of the stream has been reached.</returns>
  647. public unsafe FixedString64Bytes ReadPackedFixedString64Delta(FixedString64Bytes baseline, in StreamCompressionModel model)
  648. {
  649. FixedString64Bytes str;
  650. byte* data = ((byte*)&str) + 2;
  651. *(ushort*)&str = ReadPackedFixedStringDeltaInternal(data, str.Capacity, ((byte*)&baseline) + 2, *((ushort*)&baseline), model);
  652. return str;
  653. }
  654. /// <summary>
  655. /// Reads a <c>FixedString128Bytes</c> delta value to the data stream using a <see cref="StreamCompressionModel"/>.
  656. /// </summary>
  657. /// <param name="baseline">The previous <c>FixedString128Bytes</c> value, used to compute the diff.</param>
  658. /// <param name="model"><see cref="StreamCompressionModel"/> model for writing value in a packed manner.</param>
  659. /// <returns>A <c>FixedString128Bytes</c> value read from the current stream, or 0 if the end of the stream has been reached.</returns>
  660. public unsafe FixedString128Bytes ReadPackedFixedString128Delta(FixedString128Bytes baseline, in StreamCompressionModel model)
  661. {
  662. FixedString128Bytes str;
  663. byte* data = ((byte*)&str) + 2;
  664. *(ushort*)&str = ReadPackedFixedStringDeltaInternal(data, str.Capacity, ((byte*)&baseline) + 2, *((ushort*)&baseline), model);
  665. return str;
  666. }
  667. /// <summary>
  668. /// Reads a <c>FixedString512Bytes</c> delta value to the data stream using a <see cref="StreamCompressionModel"/>.
  669. /// </summary>
  670. /// <param name="baseline">The previous <c>FixedString512Bytes</c> value, used to compute the diff.</param>
  671. /// <param name="model"><see cref="StreamCompressionModel"/> model for writing value in a packed manner.</param>
  672. /// <returns>A <c>FixedString512Bytes</c> value read from the current stream, or 0 if the end of the stream has been reached.</returns>
  673. public unsafe FixedString512Bytes ReadPackedFixedString512Delta(FixedString512Bytes baseline, in StreamCompressionModel model)
  674. {
  675. FixedString512Bytes str;
  676. byte* data = ((byte*)&str) + 2;
  677. *(ushort*)&str = ReadPackedFixedStringDeltaInternal(data, str.Capacity, ((byte*)&baseline) + 2, *((ushort*)&baseline), model);
  678. return str;
  679. }
  680. /// <summary>
  681. /// Reads a <c>FixedString4096Bytes</c> delta value to the data stream using a <see cref="StreamCompressionModel"/>.
  682. /// </summary>
  683. /// <param name="baseline">The previous <c>FixedString4096Bytes</c> value, used to compute the diff.</param>
  684. /// <param name="model"><see cref="StreamCompressionModel"/> model for writing value in a packed manner.</param>
  685. /// <returns>A <c>FixedString4096Bytes</c> value read from the current stream, or 0 if the end of the stream has been reached.</returns>
  686. public unsafe FixedString4096Bytes ReadPackedFixedString4096Delta(FixedString4096Bytes baseline, in StreamCompressionModel model)
  687. {
  688. FixedString4096Bytes str;
  689. byte* data = ((byte*)&str) + 2;
  690. *(ushort*)&str = ReadPackedFixedStringDeltaInternal(data, str.Capacity, ((byte*)&baseline) + 2, *((ushort*)&baseline), model);
  691. return str;
  692. }
  693. /// <summary>
  694. /// Read and copy data into the given NativeArray of bytes, an error will
  695. /// be logged if not enough bytes are available in the array.
  696. /// </summary>
  697. /// <param name="data">Array for the current fixed string.</param>
  698. /// <param name="baseData">Array containing the previous value, used to compute the diff.</param>
  699. /// <param name="model"><see cref="StreamCompressionModel"/> model for writing value in a packed manner.</param>
  700. /// <returns>Length of data read into byte array, or zero if error occurred.</returns>
  701. public ushort ReadPackedFixedStringDelta(NativeArray<byte> data, NativeArray<byte> baseData, in StreamCompressionModel model)
  702. {
  703. return ReadPackedFixedStringDeltaInternal((byte*)data.GetUnsafePtr(), data.Length, (byte*)baseData.GetUnsafePtr(), (ushort)baseData.Length, model);
  704. }
  705. unsafe ushort ReadPackedFixedStringDeltaInternal(byte* data, int maxLength, byte* baseData, ushort baseLength, in StreamCompressionModel model)
  706. {
  707. uint length = ReadPackedUIntDelta(baseLength, model);
  708. if (length > (uint)maxLength)
  709. {
  710. #if (ENABLE_UNITY_COLLECTIONS_CHECKS || UNITY_DOTS_DEBUG)
  711. UnityEngine.Debug.LogError($"Trying to read a string of length {length} but max length is {maxLength}");
  712. #endif
  713. return 0;
  714. }
  715. if (length <= baseLength)
  716. {
  717. for (int i = 0; i < length; ++i)
  718. data[i] = (byte)ReadPackedUIntDelta(baseData[i], model);
  719. }
  720. else
  721. {
  722. for (int i = 0; i < baseLength; ++i)
  723. data[i] = (byte)ReadPackedUIntDelta(baseData[i], model);
  724. for (int i = baseLength; i < length; ++i)
  725. data[i] = (byte)ReadPackedUInt(model);
  726. }
  727. return (ushort)length;
  728. }
  729. [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
  730. internal readonly void CheckRead()
  731. {
  732. #if ENABLE_UNITY_COLLECTIONS_CHECKS
  733. AtomicSafetyHandle.CheckReadAndThrow(m_Safety);
  734. #endif
  735. }
  736. [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")]
  737. static void CheckBits(int numbits)
  738. {
  739. if (numbits < 0 || numbits > 32)
  740. throw new ArgumentOutOfRangeException("Invalid number of bits");
  741. }
  742. }
  743. }