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