UnityGame/Library/PackageCache/com.unity.collections/Unity.Collections/DataStreamReader.cs

819 lines
38 KiB
C#
Raw Normal View History

2024-10-27 10:53:47 +03:00
using System;
using System.Diagnostics;
using Unity.Collections.LowLevel.Unsafe;
using UnityEngine.Scripting.APIUpdating;
namespace Unity.Collections
{
/// <summary>
/// Writes data in an endian format to deserialize data.
/// </summary>
/// <remarks>
/// The DataStreamReader class is the counterpart of the
/// <see cref="DataStreamWriter"/> class and can be be used to deserialize
/// data which was prepared with it.
///
/// DataStreamWriter writes this data in the endian format native
/// to the current machine architecture.
/// <br/>
/// For network byte order use the so named methods.
/// <br/>
/// Simple usage example:
/// <code>
/// using (var dataWriter = new DataStreamWriter(16, Allocator.Persistent))
/// {
/// dataWriter.Write(42);
/// dataWriter.Write(1234);
/// // Length is the actual amount of data inside the writer,
/// // Capacity is the total amount.
/// var dataReader = new DataStreamReader(dataWriter, 0, dataWriter.Length);
/// var context = default(DataStreamReader.Context);
/// var myFirstInt = dataReader.ReadInt(ref context);
/// var mySecondInt = dataReader.ReadInt(ref context);
/// }
/// </code>
///
/// DataStreamReader carries the position of the read pointer inside the struct,
/// taking a copy of the reader will also copy the read position. This includes passing the
/// reader to a method by value instead of by ref.
///
/// <seealso cref="DataStreamWriter"/>
/// <seealso cref="IsLittleEndian"/>
/// </remarks>
[MovedFrom(true, "Unity.Networking.Transport")]
[GenerateTestsForBurstCompatibility]
public unsafe struct DataStreamReader
{
struct Context
{
public int m_ReadByteIndex;
public int m_BitIndex;
public ulong m_BitBuffer;
public int m_FailedReads;
}
[NativeDisableUnsafePtrRestriction] internal byte* m_BufferPtr;
Context m_Context;
int m_Length;
#if ENABLE_UNITY_COLLECTIONS_CHECKS
AtomicSafetyHandle m_Safety;
#endif
/// <summary>
/// Initializes a new instance of the DataStreamReader struct with a NativeArray&lt;byte&gt;
/// </summary>
/// <param name="array">The buffer to attach to the DataStreamReader.</param>
public DataStreamReader(NativeArray<byte> array)
{
Initialize(out this, array);
}
static void Initialize(out DataStreamReader self, NativeArray<byte> array)
{
#if ENABLE_UNITY_COLLECTIONS_CHECKS
self.m_Safety = NativeArrayUnsafeUtility.GetAtomicSafetyHandle(array);
#endif
self.m_BufferPtr = (byte*)array.GetUnsafeReadOnlyPtr();
self.m_Length = array.Length;
self.m_Context = default;
}
/// <summary>
/// Show the byte order in which the current computer architecture stores data.
/// </summary>
/// <remarks>
/// Different computer architectures store data using different byte orders.
/// <list type="bullet">
/// <item>Big-endian: the most significant byte is at the left end of a word.</item>
/// <item>Little-endian: means the most significant byte is at the right end of a word.</item>
/// </list>
/// </remarks>
public static bool IsLittleEndian { get { return DataStreamWriter.IsLittleEndian; } }
static short ByteSwap(short val)
{
return (short)(((val & 0xff) << 8) | ((val >> 8) & 0xff));
}
static int ByteSwap(int val)
{
return (int)(((val & 0xff) << 24) | ((val & 0xff00) << 8) | ((val >> 8) & 0xff00) | ((val >> 24) & 0xff));
}
/// <summary>
/// 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.
/// </summary>
public readonly bool HasFailedReads => m_Context.m_FailedReads > 0;
/// <summary>
/// The total size of the buffer space this reader is working with.
/// </summary>
public readonly int Length
{
get
{
CheckRead();
return m_Length;
}
}
/// <summary>
/// True if the reader has been pointed to a valid buffer space. This
/// would be false if the reader was created with no arguments.
/// </summary>
public readonly bool IsCreated
{
get { return m_BufferPtr != null; }
}
void ReadBytesInternal(byte* data, int length)
{
CheckRead();
if (GetBytesRead() + length > m_Length)
{
++m_Context.m_FailedReads;
#if (ENABLE_UNITY_COLLECTIONS_CHECKS || UNITY_DOTS_DEBUG)
UnityEngine.Debug.LogError($"Trying to read {length} bytes from a stream where only {m_Length - GetBytesRead()} are available");
#endif
UnsafeUtility.MemClear(data, length);
return;
}
// Restore the full bytes moved to the bit buffer but no consumed
Flush();
UnsafeUtility.MemCpy(data, m_BufferPtr + m_Context.m_ReadByteIndex, length);
m_Context.m_ReadByteIndex += length;
}
/// <summary>
/// Read and copy data into the given NativeArray of bytes. An error will
/// be logged if not enough bytes are available to fill the array, and
/// <see cref="HasFailedReads"/> will then be true.
/// </summary>
/// <param name="array">Array to copy data into.</param>
public void ReadBytes(NativeArray<byte> array)
{
ReadBytesInternal((byte*)array.GetUnsafePtr(), array.Length);
}
/// <summary>
/// Read and copy data into the given <c>Span</c> of bytes. An error will
/// be logged if not enough bytes are available to fill the array, and
/// <see cref="HasFailedReads"/> will then be true.
/// </summary>
/// <param name="span">Span to copy data into.</param>
public void ReadBytes(Span<byte> span)
{
fixed (byte* ptr = span)
{
ReadBytesInternal(ptr, span.Length);
}
}
/// <summary>
/// Gets the number of bytes read from the data stream.
/// </summary>
/// <returns>Number of bytes read.</returns>
public int GetBytesRead()
{
return m_Context.m_ReadByteIndex - (m_Context.m_BitIndex >> 3);
}
/// <summary>
/// Gets the number of bits read from the data stream.
/// </summary>
/// <returns>Number of bits read.</returns>
public int GetBitsRead()
{
return (m_Context.m_ReadByteIndex << 3) - m_Context.m_BitIndex;
}
/// <summary>
/// Sets the current position of this stream to the given value.
/// An error will be logged if <paramref name="pos"/> is outside the length of the stream.
/// <br/>
/// In addition this will reset the bit index and the bit buffer.
/// </summary>
/// <param name="pos">Seek position.</param>
public void SeekSet(int pos)
{
if (pos > m_Length)
{
++m_Context.m_FailedReads;
#if (ENABLE_UNITY_COLLECTIONS_CHECKS || UNITY_DOTS_DEBUG)
UnityEngine.Debug.LogError($"Trying to seek to {pos} in a stream of length {m_Length}");
#endif
return;
}
m_Context.m_ReadByteIndex = pos;
m_Context.m_BitIndex = 0;
m_Context.m_BitBuffer = 0UL;
}
/// <summary>
/// Reads an unsigned byte from the current stream and advances the current position of the stream by one byte.
/// </summary>
/// <returns>The next byte read from the current stream, or 0 if the end of the stream has been reached.</returns>
public byte ReadByte()
{
byte data;
ReadBytesInternal((byte*)&data, sizeof(byte));
return data;
}
/// <summary>
/// Reads a 2-byte signed short from the current stream and advances the current position of the stream by two bytes.
/// </summary>
/// <returns>A 2-byte signed short read from the current stream, or 0 if the end of the stream has been reached.</returns>
public short ReadShort()
{
short data;
ReadBytesInternal((byte*)&data, sizeof(short));
return data;
}
/// <summary>
/// Reads a 2-byte unsigned short from the current stream and advances the current position of the stream by two bytes.
/// </summary>
/// <returns>A 2-byte unsigned short read from the current stream, or 0 if the end of the stream has been reached.</returns>
public ushort ReadUShort()
{
ushort data;
ReadBytesInternal((byte*)&data, sizeof(ushort));
return data;
}
/// <summary>
/// Reads a 4-byte signed integer from the current stream and advances the current position of the stream by four bytes.
/// </summary>
/// <returns>A 4-byte signed integer read from the current stream, or 0 if the end of the stream has been reached.</returns>
public int ReadInt()
{
int data;
ReadBytesInternal((byte*)&data, sizeof(int));
return data;
}
/// <summary>
/// Reads a 4-byte unsigned integer from the current stream and advances the current position of the stream by four bytes.
/// </summary>
/// <returns>A 4-byte unsigned integer read from the current stream, or 0 if the end of the stream has been reached.</returns>
public uint ReadUInt()
{
uint data;
ReadBytesInternal((byte*)&data, sizeof(uint));
return data;
}
/// <summary>
/// Reads an 8-byte signed long from the stream and advances the current position of the stream by eight bytes.
/// </summary>
/// <returns>An 8-byte signed long read from the current stream, or 0 if the end of the stream has been reached.</returns>
public long ReadLong()
{
long data;
ReadBytesInternal((byte*)&data, sizeof(long));
return data;
}
/// <summary>
/// Reads an 8-byte unsigned long from the stream and advances the current position of the stream by eight bytes.
/// </summary>
/// <returns>An 8-byte unsigned long read from the current stream, or 0 if the end of the stream has been reached.</returns>
public ulong ReadULong()
{
ulong data;
ReadBytesInternal((byte*)&data, sizeof(ulong));
return data;
}
/// <summary>
/// Aligns the read pointer to the next byte-aligned position. Does nothing if already aligned.
/// </summary>
/// <remarks>If you call <see cref="DataStreamWriter.Flush"/>, call this to bit-align the reader.</remarks>
public void Flush()
{
m_Context.m_ReadByteIndex -= (m_Context.m_BitIndex >> 3);
m_Context.m_BitIndex = 0;
m_Context.m_BitBuffer = 0;
}
/// <summary>
/// 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.
/// If the current endianness is in little-endian order, the byte order will be swapped.
/// </summary>
/// <returns>A 2-byte signed short read from the current stream, or 0 if the end of the stream has been reached.</returns>
public short ReadShortNetworkByteOrder()
{
short data;
ReadBytesInternal((byte*)&data, sizeof(short));
return IsLittleEndian ? ByteSwap(data) : data;
}
/// <summary>
/// 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.
/// If the current endianness is in little-endian order, the byte order will be swapped.
/// </summary>
/// <returns>A 2-byte unsigned short read from the current stream, or 0 if the end of the stream has been reached.</returns>
public ushort ReadUShortNetworkByteOrder()
{
return (ushort)ReadShortNetworkByteOrder();
}
/// <summary>
/// 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.
/// If the current endianness is in little-endian order, the byte order will be swapped.
/// </summary>
/// <returns>A 4-byte signed integer read from the current stream, or 0 if the end of the stream has been reached.</returns>
public int ReadIntNetworkByteOrder()
{
int data;
ReadBytesInternal((byte*)&data, sizeof(int));
return IsLittleEndian ? ByteSwap(data) : data;
}
/// <summary>
/// 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.
/// If the current endianness is in little-endian order, the byte order will be swapped.
/// </summary>
/// <returns>A 4-byte unsigned integer read from the current stream, or 0 if the end of the stream has been reached.</returns>
public uint ReadUIntNetworkByteOrder()
{
return (uint)ReadIntNetworkByteOrder();
}
/// <summary>
/// Reads a 4-byte floating point value from the current stream and advances the current position of the stream by four bytes.
/// </summary>
/// <returns>A 4-byte floating point value read from the current stream, or 0 if the end of the stream has been reached.</returns>
public float ReadFloat()
{
UIntFloat uf = new UIntFloat();
uf.intValue = (uint)ReadInt();
return uf.floatValue;
}
/// <summary>
/// Reads a 8-byte floating point value from the current stream and advances the current position of the stream by four bytes.
/// </summary>
/// <returns>A 8-byte floating point value read from the current stream, or 0 if the end of the stream has been reached.</returns>
public double ReadDouble()
{
UIntFloat uf = new UIntFloat();
uf.longValue = (ulong)ReadLong();
return uf.doubleValue;
}
/// <summary>
/// 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.
/// </summary>
/// <param name="model"><see cref="StreamCompressionModel"/> model for reading value in a packed manner.</param>
/// <returns>A 4-byte unsigned integer read from the current stream, or 0 if the end of the stream has been reached.</returns>
public uint ReadPackedUInt(in StreamCompressionModel model)
{
return ReadPackedUIntInternal(StreamCompressionModel.k_MaxHuffmanSymbolLength, model);
}
uint ReadPackedUIntInternal(int maxSymbolLength, in StreamCompressionModel model)
{
CheckRead();
FillBitBuffer();
uint peekMask = (1u << maxSymbolLength) - 1u;
uint peekBits = (uint)m_Context.m_BitBuffer & peekMask;
ushort huffmanEntry = model.decodeTable[(int)peekBits];
int symbol = huffmanEntry >> 8;
int length = huffmanEntry & 0xFF;
if (m_Context.m_BitIndex < length)
{
++m_Context.m_FailedReads;
#if (ENABLE_UNITY_COLLECTIONS_CHECKS || UNITY_DOTS_DEBUG)
UnityEngine.Debug.LogError($"Trying to read {length} bits from a stream where only {m_Context.m_BitIndex} are available");
#endif
return 0;
}
// Skip Huffman bits
m_Context.m_BitBuffer >>= length;
m_Context.m_BitIndex -= length;
uint offset = model.bucketOffsets[symbol];
byte bits = model.bucketSizes[symbol];
return ReadRawBitsInternal(bits) + offset;
}
void FillBitBuffer()
{
while (m_Context.m_BitIndex <= 56 && m_Context.m_ReadByteIndex < m_Length)
{
m_Context.m_BitBuffer |= (ulong)m_BufferPtr[m_Context.m_ReadByteIndex++] << m_Context.m_BitIndex;
m_Context.m_BitIndex += 8;
}
}
uint ReadRawBitsInternal(int numbits)
{
CheckBits(numbits);
if (m_Context.m_BitIndex < numbits)
{
++m_Context.m_FailedReads;
#if (ENABLE_UNITY_COLLECTIONS_CHECKS || UNITY_DOTS_DEBUG)
UnityEngine.Debug.LogError($"Trying to read {numbits} bits from a stream where only {m_Context.m_BitIndex} are available");
#endif
return 0;
}
uint res = (uint)(m_Context.m_BitBuffer & ((1UL << numbits) - 1UL));
m_Context.m_BitBuffer >>= numbits;
m_Context.m_BitIndex -= numbits;
return res;
}
/// <summary>
/// Reads a specified number of bits from the data stream.
/// </summary>
/// <param name="numbits">A positive number of bytes to write.</param>
/// <returns>A 4-byte unsigned integer read from the current stream, or 0 if the end of the stream has been reached.</returns>
public uint ReadRawBits(int numbits)
{
CheckRead();
FillBitBuffer();
return ReadRawBitsInternal(numbits);
}
/// <summary>
/// Reads an 8-byte unsigned long value from the data stream using a <see cref="StreamCompressionModel"/>.
/// </summary>
/// <param name="model"><see cref="StreamCompressionModel"/> model for reading value in a packed manner.</param>
/// <returns>An 8-byte unsigned long read from the current stream, or 0 if the end of the stream has been reached.</returns>
public ulong ReadPackedULong(in StreamCompressionModel model)
{
ulong value;
((uint*)&value)[0] = ReadPackedUInt(model);
((uint*)&value)[1] = ReadPackedUInt(model);
return value;
}
/// <summary>
/// Reads a 4-byte signed integer value from the data stream using a <see cref="StreamCompressionModel"/>.
/// <br/>
/// Negative values de-interleaves from positive values before returning, for example (0, -1, 1, -2, 2) -> (-2, -1, 0, 1, 2)
/// </summary>
/// <param name="model"><see cref="StreamCompressionModel"/> model for reading value in a packed manner.</param>
/// <returns>A 4-byte signed integer read from the current stream, or 0 if the end of the stream has been reached.</returns>
public int ReadPackedInt(in StreamCompressionModel model)
{
uint folded = ReadPackedUInt(model);
return (int)(folded >> 1) ^ -(int)(folded & 1); // Deinterleave values from [0, -1, 1, -2, 2...] to [..., -2, -1, -0, 1, 2, ...]
}
/// <summary>
/// Reads an 8-byte signed long value from the data stream using a <see cref="StreamCompressionModel"/>.
/// <br/>
/// Negative values de-interleaves from positive values before returning, for example (0, -1, 1, -2, 2) -> (-2, -1, 0, 1, 2)
/// </summary>
/// <param name="model"><see cref="StreamCompressionModel"/> model for reading value in a packed manner.</param>
/// <returns>An 8-byte signed long read from the current stream, or 0 if the end of the stream has been reached.</returns>
public long ReadPackedLong(in StreamCompressionModel model)
{
ulong folded = ReadPackedULong(model);
return (long)(folded >> 1) ^ -(long)(folded & 1); // Deinterleave values from [0, -1, 1, -2, 2...] to [..., -2, -1, -0, 1, 2, ...]
}
/// <summary>
/// Reads a 4-byte floating point value from the data stream using a <see cref="StreamCompressionModel"/>.
/// </summary>
/// <param name="model"><see cref="StreamCompressionModel"/> model for reading value in a packed manner.</param>
/// <returns>A 4-byte floating point value read from the current stream, or 0 if the end of the stream has been reached.</returns>
public float ReadPackedFloat(in StreamCompressionModel model)
{
return ReadPackedFloatDelta(0, model);
}
/// <summary>
/// Reads a 8-byte floating point value from the data stream using a <see cref="StreamCompressionModel"/>.
/// </summary>
/// <param name="model"><see cref="StreamCompressionModel"/> model for reading value in a packed manner.</param>
/// <returns>A 8-byte floating point value read from the current stream, or 0 if the end of the stream has been reached.</returns>
public double ReadPackedDouble(in StreamCompressionModel model)
{
return ReadPackedDoubleDelta(0, model);
}
/// <summary>
/// Reads a 4-byte signed integer delta value from the data stream using a <see cref="StreamCompressionModel"/>.
/// </summary>
/// <param name="baseline">The previous 4-byte signed integer value, used to compute the diff.</param>
/// <param name="model"><see cref="StreamCompressionModel"/> model for reading value in a packed manner.</param>
/// <returns>A 4-byte signed integer read from the current stream, or 0 if the end of the stream has been reached.
/// If the data did not change, this also returns 0.
/// <br/>
/// See: <see cref="HasFailedReads"/> to verify if the read failed.</returns>
public int ReadPackedIntDelta(int baseline, in StreamCompressionModel model)
{
int delta = ReadPackedInt(model);
return baseline - delta;
}
/// <summary>
/// Reads a 4-byte unsigned integer delta value from the data stream using a <see cref="StreamCompressionModel"/>.
/// </summary>
/// <param name="baseline">The previous 4-byte unsigned integer value, used to compute the diff.</param>
/// <param name="model"><see cref="StreamCompressionModel"/> model for reading value in a packed manner.</param>
/// <returns>A 4-byte unsigned integer read from the current stream, or 0 if the end of the stream has been reached.
/// If the data did not change, this also returns 0.
/// <br/>
/// See: <see cref="HasFailedReads"/> to verify if the read failed.</returns>
public uint ReadPackedUIntDelta(uint baseline, in StreamCompressionModel model)
{
uint delta = (uint)ReadPackedInt(model);
return baseline - delta;
}
/// <summary>
/// Reads an 8-byte signed long delta value from the data stream using a <see cref="StreamCompressionModel"/>.
/// </summary>
/// <param name="baseline">The previous 8-byte signed long value, used to compute the diff.</param>
/// <param name="model"><see cref="StreamCompressionModel"/> model for reading value in a packed manner.</param>
/// <returns>An 8-byte signed long read from the current stream, or 0 if the end of the stream has been reached.
/// If the data did not change, this also returns 0.
/// <br/>
/// See: <see cref="HasFailedReads"/> to verify if the read failed.</returns>
public long ReadPackedLongDelta(long baseline, in StreamCompressionModel model)
{
long delta = ReadPackedLong(model);
return baseline - delta;
}
/// <summary>
/// Reads an 8-byte unsigned long delta value from the data stream using a <see cref="StreamCompressionModel"/>.
/// </summary>
/// <param name="baseline">The previous 8-byte unsigned long value, used to compute the diff.</param>
/// <param name="model"><see cref="StreamCompressionModel"/> model for reading value in a packed manner.</param>
/// <returns>An 8-byte unsigned long read from the current stream, or 0 if the end of the stream has been reached.
/// If the data did not change, this also returns 0.
/// <br/>
/// See: <see cref="HasFailedReads"/> to verify if the read failed.</returns>
public ulong ReadPackedULongDelta(ulong baseline, in StreamCompressionModel model)
{
ulong delta = (ulong)ReadPackedLong(model);
return baseline - delta;
}
/// <summary>
/// Reads a 4-byte floating point value from the data stream.
///
/// If the first bit is 0, the data did not change and <paramref name="baseline"/> will be returned.
/// </summary>
/// <param name="baseline">The previous 4-byte floating point value.</param>
/// <param name="model">Not currently used.</param>
/// <returns>A 4-byte floating point value read from the current stream, or <paramref name="baseline"/> if there are no changes to the value.
/// <br/>
/// See: <see cref="HasFailedReads"/> to verify if the read failed.</returns>
public float ReadPackedFloatDelta(float baseline, in StreamCompressionModel model)
{
CheckRead();
FillBitBuffer();
if (ReadRawBitsInternal(1) == 0)
return baseline;
var bits = 32;
UIntFloat uf = new UIntFloat();
uf.intValue = ReadRawBitsInternal(bits);
return uf.floatValue;
}
/// <summary>
/// Reads a 8-byte floating point value from the data stream.
///
/// If the first bit is 0, the data did not change and <paramref name="baseline"/> will be returned.
/// </summary>
/// <param name="baseline">The previous 8-byte floating point value.</param>
/// <param name="model">Not currently used.</param>
/// <returns>A 8-byte floating point value read from the current stream, or <paramref name="baseline"/> if there are no changes to the value.
/// <br/>
/// See: <see cref="HasFailedReads"/> to verify if the read failed.</returns>
public double ReadPackedDoubleDelta(double baseline, in StreamCompressionModel model)
{
CheckRead();
FillBitBuffer();
if (ReadRawBitsInternal(1) == 0)
return baseline;
var bits = 32;
UIntFloat uf = new UIntFloat();
var data = (uint*)&uf.longValue;
data[0] = ReadRawBitsInternal(bits);
FillBitBuffer();
data[1] |= ReadRawBitsInternal(bits);
return uf.doubleValue;
}
/// <summary>
/// Reads a <c>FixedString32Bytes</c> value from the current stream and advances the current position of the stream by the length of the string.
/// </summary>
/// <returns>A <c>FixedString32Bytes</c> value read from the current stream, or 0 if the end of the stream has been reached.</returns>
public unsafe FixedString32Bytes ReadFixedString32()
{
FixedString32Bytes str;
byte* data = ((byte*)&str) + 2;
*(ushort*)&str = ReadFixedStringInternal(data, str.Capacity);
return str;
}
/// <summary>
/// Reads a <c>FixedString64Bytes</c> value from the current stream and advances the current position of the stream by the length of the string.
/// </summary>
/// <returns>A <c>FixedString64Bytes</c> value read from the current stream, or 0 if the end of the stream has been reached.</returns>
public unsafe FixedString64Bytes ReadFixedString64()
{
FixedString64Bytes str;
byte* data = ((byte*)&str) + 2;
*(ushort*)&str = ReadFixedStringInternal(data, str.Capacity);
return str;
}
/// <summary>
/// Reads a <c>FixedString128Bytes</c> value from the current stream and advances the current position of the stream by the length of the string.
/// </summary>
/// <returns>A <c>FixedString128Bytes</c> value read from the current stream, or 0 if the end of the stream has been reached.</returns>
public unsafe FixedString128Bytes ReadFixedString128()
{
FixedString128Bytes str;
byte* data = ((byte*)&str) + 2;
*(ushort*)&str = ReadFixedStringInternal(data, str.Capacity);
return str;
}
/// <summary>
/// Reads a <c>FixedString512Bytes</c> value from the current stream and advances the current position of the stream by the length of the string.
/// </summary>
/// <returns>A <c>FixedString512Bytes</c> value read from the current stream, or 0 if the end of the stream has been reached.</returns>
public unsafe FixedString512Bytes ReadFixedString512()
{
FixedString512Bytes str;
byte* data = ((byte*)&str) + 2;
*(ushort*)&str = ReadFixedStringInternal(data, str.Capacity);
return str;
}
/// <summary>
/// Reads a <c>FixedString4096Bytes</c> value from the current stream and advances the current position of the stream by the length of the string.
/// </summary>
/// <returns>A <c>FixedString4096Bytes</c> value read from the current stream, or 0 if the end of the stream has been reached.</returns>
public unsafe FixedString4096Bytes ReadFixedString4096()
{
FixedString4096Bytes str;
byte* data = ((byte*)&str) + 2;
*(ushort*)&str = ReadFixedStringInternal(data, str.Capacity);
return str;
}
/// <summary>
/// Read and copy data into the given NativeArray of bytes, an error will
/// be logged if not enough bytes are available in the array.
/// </summary>
/// <param name="array">Buffer to write the string bytes to.</param>
/// <returns>Length of data read into byte array, or zero if error occurred.</returns>
public ushort ReadFixedString(NativeArray<byte> array)
{
return ReadFixedStringInternal((byte*)array.GetUnsafePtr(), array.Length);
}
unsafe ushort ReadFixedStringInternal(byte* data, int maxLength)
{
ushort length = ReadUShort();
if (length > maxLength)
{
#if (ENABLE_UNITY_COLLECTIONS_CHECKS || UNITY_DOTS_DEBUG)
UnityEngine.Debug.LogError($"Trying to read a string of length {length} but max length is {maxLength}");
#endif
return 0;
}
ReadBytesInternal(data, length);
return length;
}
/// <summary>
/// Reads a <c>FixedString32Bytes</c> delta value to the data stream using a <see cref="StreamCompressionModel"/>.
/// </summary>
/// <param name="baseline">The previous <c>FixedString32Bytes</c> value, used to compute the diff.</param>
/// <param name="model"><see cref="StreamCompressionModel"/> model for writing value in a packed manner.</param>
/// <returns>A <c>FixedString32Bytes</c> value read from the current stream, or 0 if the end of the stream has been reached.</returns>
public unsafe FixedString32Bytes ReadPackedFixedString32Delta(FixedString32Bytes baseline, in StreamCompressionModel model)
{
FixedString32Bytes str;
byte* data = ((byte*)&str) + 2;
*(ushort*)&str = ReadPackedFixedStringDeltaInternal(data, str.Capacity, ((byte*)&baseline) + 2, *((ushort*)&baseline), model);
return str;
}
/// <summary>
/// Reads a <c>FixedString64Bytes</c> delta value to the data stream using a <see cref="StreamCompressionModel"/>.
/// </summary>
/// <param name="baseline">The previous <c>FixedString64Bytes</c> value, used to compute the diff.</param>
/// <param name="model"><see cref="StreamCompressionModel"/> model for writing value in a packed manner.</param>
/// <returns>A <c>FixedString64Bytes</c> value read from the current stream, or 0 if the end of the stream has been reached.</returns>
public unsafe FixedString64Bytes ReadPackedFixedString64Delta(FixedString64Bytes baseline, in StreamCompressionModel model)
{
FixedString64Bytes str;
byte* data = ((byte*)&str) + 2;
*(ushort*)&str = ReadPackedFixedStringDeltaInternal(data, str.Capacity, ((byte*)&baseline) + 2, *((ushort*)&baseline), model);
return str;
}
/// <summary>
/// Reads a <c>FixedString128Bytes</c> delta value to the data stream using a <see cref="StreamCompressionModel"/>.
/// </summary>
/// <param name="baseline">The previous <c>FixedString128Bytes</c> value, used to compute the diff.</param>
/// <param name="model"><see cref="StreamCompressionModel"/> model for writing value in a packed manner.</param>
/// <returns>A <c>FixedString128Bytes</c> value read from the current stream, or 0 if the end of the stream has been reached.</returns>
public unsafe FixedString128Bytes ReadPackedFixedString128Delta(FixedString128Bytes baseline, in StreamCompressionModel model)
{
FixedString128Bytes str;
byte* data = ((byte*)&str) + 2;
*(ushort*)&str = ReadPackedFixedStringDeltaInternal(data, str.Capacity, ((byte*)&baseline) + 2, *((ushort*)&baseline), model);
return str;
}
/// <summary>
/// Reads a <c>FixedString512Bytes</c> delta value to the data stream using a <see cref="StreamCompressionModel"/>.
/// </summary>
/// <param name="baseline">The previous <c>FixedString512Bytes</c> value, used to compute the diff.</param>
/// <param name="model"><see cref="StreamCompressionModel"/> model for writing value in a packed manner.</param>
/// <returns>A <c>FixedString512Bytes</c> value read from the current stream, or 0 if the end of the stream has been reached.</returns>
public unsafe FixedString512Bytes ReadPackedFixedString512Delta(FixedString512Bytes baseline, in StreamCompressionModel model)
{
FixedString512Bytes str;
byte* data = ((byte*)&str) + 2;
*(ushort*)&str = ReadPackedFixedStringDeltaInternal(data, str.Capacity, ((byte*)&baseline) + 2, *((ushort*)&baseline), model);
return str;
}
/// <summary>
/// Reads a <c>FixedString4096Bytes</c> delta value to the data stream using a <see cref="StreamCompressionModel"/>.
/// </summary>
/// <param name="baseline">The previous <c>FixedString4096Bytes</c> value, used to compute the diff.</param>
/// <param name="model"><see cref="StreamCompressionModel"/> model for writing value in a packed manner.</param>
/// <returns>A <c>FixedString4096Bytes</c> value read from the current stream, or 0 if the end of the stream has been reached.</returns>
public unsafe FixedString4096Bytes ReadPackedFixedString4096Delta(FixedString4096Bytes baseline, in StreamCompressionModel model)
{
FixedString4096Bytes str;
byte* data = ((byte*)&str) + 2;
*(ushort*)&str = ReadPackedFixedStringDeltaInternal(data, str.Capacity, ((byte*)&baseline) + 2, *((ushort*)&baseline), model);
return str;
}
/// <summary>
/// Read and copy data into the given NativeArray of bytes, an error will
/// be logged if not enough bytes are available in the array.
/// </summary>
/// <param name="data">Array for the current fixed string.</param>
/// <param name="baseData">Array containing the previous value, used to compute the diff.</param>
/// <param name="model"><see cref="StreamCompressionModel"/> model for writing value in a packed manner.</param>
/// <returns>Length of data read into byte array, or zero if error occurred.</returns>
public ushort ReadPackedFixedStringDelta(NativeArray<byte> data, NativeArray<byte> baseData, in StreamCompressionModel model)
{
return ReadPackedFixedStringDeltaInternal((byte*)data.GetUnsafePtr(), data.Length, (byte*)baseData.GetUnsafePtr(), (ushort)baseData.Length, model);
}
unsafe ushort ReadPackedFixedStringDeltaInternal(byte* data, int maxLength, byte* baseData, ushort baseLength, in StreamCompressionModel model)
{
uint length = ReadPackedUIntDelta(baseLength, model);
if (length > (uint)maxLength)
{
#if (ENABLE_UNITY_COLLECTIONS_CHECKS || UNITY_DOTS_DEBUG)
UnityEngine.Debug.LogError($"Trying to read a string of length {length} but max length is {maxLength}");
#endif
return 0;
}
if (length <= baseLength)
{
for (int i = 0; i < length; ++i)
data[i] = (byte)ReadPackedUIntDelta(baseData[i], model);
}
else
{
for (int i = 0; i < baseLength; ++i)
data[i] = (byte)ReadPackedUIntDelta(baseData[i], model);
for (int i = baseLength; i < length; ++i)
data[i] = (byte)ReadPackedUInt(model);
}
return (ushort)length;
}
[Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
internal readonly void CheckRead()
{
#if ENABLE_UNITY_COLLECTIONS_CHECKS
AtomicSafetyHandle.CheckReadAndThrow(m_Safety);
#endif
}
[Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")]
static void CheckBits(int numBits)
{
if (numBits < 0 || numBits > 32)
throw new ArgumentOutOfRangeException($"Invalid number of bits specified: {numBits}! Valid range is (0, 32) inclusive.");
}
}
}