921 lines
		
	
	
		
			40 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
		
		
			
		
	
	
			921 lines
		
	
	
		
			40 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
|  | using System; | ||
|  | using System.Diagnostics; | ||
|  | using System.Runtime.CompilerServices; | ||
|  | using System.Runtime.InteropServices; | ||
|  | using Unity.Burst; | ||
|  | using Unity.Collections.LowLevel.Unsafe; | ||
|  | using UnityEngine.Scripting.APIUpdating; | ||
|  | 
 | ||
|  | namespace Unity.Collections | ||
|  | { | ||
|  |     /// <summary> | ||
|  |     /// Writes data in an endian format to serialize data. | ||
|  |     /// </summary> | ||
|  |     /// <remarks> | ||
|  |     /// Data streams can be used to serialize data (e.g. over the network). The | ||
|  |     /// DataStreamWriter and <see cref="DataStreamReader"/> classes work together | ||
|  |     /// to serialize data for sending and then to deserialize when receiving. | ||
|  |     /// | ||
|  |     /// DataStreamWriter writes data in the endian format native to the current machine architecture. | ||
|  |     /// For network byte order use the so named methods. | ||
|  |     /// <br/> | ||
|  |     /// The reader can be used to deserialize the data from a NativeArray<byte>, writing data | ||
|  |     /// to a NativeArray<byte> and reading it back can be done like this: | ||
|  |     /// <code> | ||
|  |     /// using (var data = new NativeArray<byte>(16, Allocator.Persistent)) | ||
|  |     /// { | ||
|  |     ///     var dataWriter = new DataStreamWriter(data); | ||
|  |     ///     dataWriter.WriteInt(42); | ||
|  |     ///     dataWriter.WriteInt(1234); | ||
|  |     ///     // Length is the actual amount of data inside the writer, | ||
|  |     ///     // Capacity is the total amount. | ||
|  |     ///     var dataReader = new DataStreamReader(nativeArrayOfBytes.GetSubArray(0, dataWriter.Length)); | ||
|  |     ///     var myFirstInt = dataReader.ReadInt(); | ||
|  |     ///     var mySecondInt = dataReader.ReadInt(); | ||
|  |     /// } | ||
|  |     /// </code> | ||
|  |     /// | ||
|  |     /// There are a number of functions for various data types. If a copy of the writer | ||
|  |     /// is stored it can be used to overwrite the data later on. This is particularly useful when | ||
|  |     /// the size of the data is written at the start and you want to write it at | ||
|  |     /// the end when you know the value. | ||
|  |     /// <seealso cref="IsLittleEndian"/> | ||
|  |     /// | ||
|  |     /// <code> | ||
|  |     /// using (var data = new NativeArray<byte>(16, Allocator.Persistent)) | ||
|  |     /// { | ||
|  |     ///     var dataWriter = new DataStreamWriter(data); | ||
|  |     ///     // My header data | ||
|  |     ///     var headerSizeMark = dataWriter; | ||
|  |     ///     dataWriter.WriteUShort((ushort)0); | ||
|  |     ///     var payloadSizeMark = dataWriter; | ||
|  |     ///     dataWriter.WriteUShort((ushort)0); | ||
|  |     ///     dataWriter.WriteInt(42); | ||
|  |     ///     dataWriter.WriteInt(1234); | ||
|  |     ///     var headerSize = data.Length; | ||
|  |     ///     // Update header size to correct value | ||
|  |     ///     headerSizeMark.WriteUShort((ushort)headerSize); | ||
|  |     ///     // My payload data | ||
|  |     ///     byte[] someBytes = Encoding.ASCII.GetBytes("some string"); | ||
|  |     ///     dataWriter.Write(someBytes, someBytes.Length); | ||
|  |     ///     // Update payload size to correct value | ||
|  |     ///     payloadSizeMark.WriteUShort((ushort)(dataWriter.Length - headerSize)); | ||
|  |     /// } | ||
|  |     /// </code> | ||
|  |     /// </remarks> | ||
|  |     [MovedFrom(true, "Unity.Networking.Transport", "Unity.Networking.Transport")] | ||
|  |     [StructLayout(LayoutKind.Sequential)] | ||
|  |     [GenerateTestsForBurstCompatibility] | ||
|  |     public unsafe struct DataStreamWriter | ||
|  |     { | ||
|  |         /// <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 | ||
|  |             { | ||
|  |                 uint test = 1; | ||
|  |                 byte* testPtr = (byte*)&test; | ||
|  |                 return testPtr[0] == 1; | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         struct StreamData | ||
|  |         { | ||
|  |             public byte* buffer; | ||
|  |             public int length; | ||
|  |             public int capacity; | ||
|  |             public ulong bitBuffer; | ||
|  |             public int bitIndex; | ||
|  |             public int failedWrites; | ||
|  |         } | ||
|  | 
 | ||
|  |         [NativeDisableUnsafePtrRestriction] StreamData m_Data; | ||
|  |         /// <summary> | ||
|  |         /// Used for sending data asynchronously. | ||
|  |         /// </summary> | ||
|  |         public IntPtr m_SendHandleData; | ||
|  | 
 | ||
|  | #if ENABLE_UNITY_COLLECTIONS_CHECKS | ||
|  |         AtomicSafetyHandle m_Safety; | ||
|  | #endif | ||
|  | 
 | ||
|  |         /// <summary> | ||
|  |         /// Initializes a new instance of the DataStreamWriter struct. | ||
|  |         /// </summary> | ||
|  |         /// <param name="length">The number of bytes available in the buffer.</param> | ||
|  |         /// <param name="allocator">The <see cref="Allocator"/> used to allocate the memory.</param> | ||
|  |         public DataStreamWriter(int length, AllocatorManager.AllocatorHandle allocator) | ||
|  |         { | ||
|  |             CheckAllocator(allocator); | ||
|  |             Initialize(out this, CollectionHelper.CreateNativeArray<byte>(length, allocator)); | ||
|  |         } | ||
|  | 
 | ||
|  |         /// <summary> | ||
|  |         /// Initializes a new instance of the DataStreamWriter struct with a NativeArray<byte> | ||
|  |         /// </summary> | ||
|  |         /// <param name="data">The buffer to attach to the DataStreamWriter.</param> | ||
|  |         public DataStreamWriter(NativeArray<byte> data) | ||
|  |         { | ||
|  |             Initialize(out this, data); | ||
|  |         } | ||
|  | 
 | ||
|  |         /// <summary> | ||
|  |         /// Initializes a new instance of the DataStreamWriter struct with a memory we don't own | ||
|  |         /// </summary> | ||
|  |         /// <param name="data">Pointer to the data</param> | ||
|  |         /// <param name="length">Length of the data</param> | ||
|  |         public DataStreamWriter(byte* data, int length) | ||
|  |         { | ||
|  |             var na = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray<byte>(data, length, Allocator.Invalid); | ||
|  | #if ENABLE_UNITY_COLLECTIONS_CHECKS | ||
|  |             NativeArrayUnsafeUtility.SetAtomicSafetyHandle(ref na, AtomicSafetyHandle.GetTempMemoryHandle()); | ||
|  | #endif | ||
|  |             Initialize(out this, na); | ||
|  |         } | ||
|  | 
 | ||
|  |         /// <summary> | ||
|  |         /// Convert internal data buffer to NativeArray for use in entities APIs. | ||
|  |         /// </summary> | ||
|  |         /// <returns>NativeArray representation of internal buffer.</returns> | ||
|  |         public NativeArray<byte> AsNativeArray() | ||
|  |         { | ||
|  |             var na = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray<byte>(m_Data.buffer, Length, Allocator.Invalid); | ||
|  | #if ENABLE_UNITY_COLLECTIONS_CHECKS | ||
|  |             NativeArrayUnsafeUtility.SetAtomicSafetyHandle(ref na, m_Safety); | ||
|  | #endif | ||
|  |             return na; | ||
|  |         } | ||
|  | 
 | ||
|  |         static void Initialize(out DataStreamWriter self, NativeArray<byte> data) | ||
|  |         { | ||
|  |             self.m_SendHandleData = IntPtr.Zero; | ||
|  | 
 | ||
|  |             self.m_Data.capacity = data.Length; | ||
|  |             self.m_Data.length = 0; | ||
|  |             self.m_Data.buffer = (byte*)data.GetUnsafePtr(); | ||
|  |             self.m_Data.bitBuffer = 0; | ||
|  |             self.m_Data.bitIndex = 0; | ||
|  |             self.m_Data.failedWrites = 0; | ||
|  | 
 | ||
|  | #if ENABLE_UNITY_COLLECTIONS_CHECKS | ||
|  |             self.m_Safety = NativeArrayUnsafeUtility.GetAtomicSafetyHandle(data); | ||
|  | #endif | ||
|  |         } | ||
|  | 
 | ||
|  |         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> | ||
|  |         /// True if there is a valid data buffer present. This would be false | ||
|  |         /// if the writer was created with no arguments. | ||
|  |         /// </summary> | ||
|  |         public readonly bool IsCreated | ||
|  |         { | ||
|  |             [MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
|  |             get { return m_Data.buffer != null; } | ||
|  |         } | ||
|  | 
 | ||
|  |         /// <summary> | ||
|  |         /// If there is a write failure this returns true. | ||
|  |         /// A failure might happen if an attempt is made to write more than there is capacity for. | ||
|  |         /// </summary> | ||
|  |         public readonly bool HasFailedWrites | ||
|  |         { | ||
|  |             [MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
|  |             get => m_Data.failedWrites > 0; | ||
|  |         } | ||
|  | 
 | ||
|  |         /// <summary> | ||
|  |         /// The total size of the data buffer, see <see cref="Length"/> for | ||
|  |         /// the size of space used in the buffer. | ||
|  |         /// </summary> | ||
|  |         public readonly int Capacity | ||
|  |         { | ||
|  |             [MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
|  |             get | ||
|  |             { | ||
|  |                 CheckRead(); | ||
|  |                 return m_Data.capacity; | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         /// <summary> | ||
|  |         /// The size of the buffer used. See <see cref="Capacity"/> for the total size. | ||
|  |         /// </summary> | ||
|  |         public int Length | ||
|  |         { | ||
|  |             get | ||
|  |             { | ||
|  |                 CheckRead(); | ||
|  |                 SyncBitData(); | ||
|  |                 return m_Data.length + ((m_Data.bitIndex + 7) >> 3); | ||
|  |             } | ||
|  |         } | ||
|  |         /// <summary> | ||
|  |         /// The size of the buffer used in bits. See <see cref="Length"/> for the length in bytes. | ||
|  |         /// </summary> | ||
|  |         public int LengthInBits | ||
|  |         { | ||
|  |             get | ||
|  |             { | ||
|  |                 CheckRead(); | ||
|  |                 SyncBitData(); | ||
|  |                 return m_Data.length * 8 + m_Data.bitIndex; | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         void SyncBitData() | ||
|  |         { | ||
|  |             var bitIndex = m_Data.bitIndex; | ||
|  |             if (bitIndex <= 0) | ||
|  |                 return; | ||
|  |             CheckWrite(); | ||
|  | 
 | ||
|  |             var bitBuffer = m_Data.bitBuffer; | ||
|  |             int offset = 0; | ||
|  |             while (bitIndex > 0) | ||
|  |             { | ||
|  |                 m_Data.buffer[m_Data.length + offset] = (byte)bitBuffer; | ||
|  |                 bitIndex -= 8; | ||
|  |                 bitBuffer >>= 8; | ||
|  |                 ++offset; | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         /// <summary> | ||
|  |         /// Causes any buffered bits to be written to the data buffer. | ||
|  |         /// Note this needs to be invoked after using methods that writes directly to the bit buffer. | ||
|  |         /// </summary> | ||
|  |         public void Flush() | ||
|  |         { | ||
|  |             while (m_Data.bitIndex > 0) | ||
|  |             { | ||
|  |                 m_Data.buffer[m_Data.length++] = (byte)m_Data.bitBuffer; | ||
|  |                 m_Data.bitIndex -= 8; | ||
|  |                 m_Data.bitBuffer >>= 8; | ||
|  |             } | ||
|  | 
 | ||
|  |             m_Data.bitIndex = 0; | ||
|  |         } | ||
|  | 
 | ||
|  |         bool WriteBytesInternal(byte* data, int bytes) | ||
|  |         { | ||
|  |             CheckWrite(); | ||
|  | 
 | ||
|  |             if (m_Data.length + ((m_Data.bitIndex + 7) >> 3) + bytes > m_Data.capacity) | ||
|  |             { | ||
|  |                 ++m_Data.failedWrites; | ||
|  |                 return false; | ||
|  |             } | ||
|  |             Flush(); | ||
|  |             UnsafeUtility.MemCpy(m_Data.buffer + m_Data.length, data, bytes); | ||
|  |             m_Data.length += bytes; | ||
|  |             return true; | ||
|  |         } | ||
|  | 
 | ||
|  |         /// <summary> | ||
|  |         /// Writes an unsigned byte to the current stream and advances the stream position by one byte. | ||
|  |         /// </summary> | ||
|  |         /// <param name="value">The unsigned byte to write.</param> | ||
|  |         /// <returns>Whether the write was successful</returns> | ||
|  |         public bool WriteByte(byte value) | ||
|  |         { | ||
|  |             return WriteBytesInternal((byte*)&value, sizeof(byte)); | ||
|  |         } | ||
|  | 
 | ||
|  |         /// <summary> | ||
|  |         /// Copy NativeArray of bytes into the writer's data buffer. | ||
|  |         /// </summary> | ||
|  |         /// <param name="value">Source byte array</param> | ||
|  |         /// <returns>Whether the write was successful</returns> | ||
|  |         public bool WriteBytes(NativeArray<byte> value) | ||
|  |         { | ||
|  |             return WriteBytesInternal((byte*)value.GetUnsafeReadOnlyPtr(), value.Length); | ||
|  |         } | ||
|  | 
 | ||
|  |         /// <summary> | ||
|  |         /// Copy <c>Span</c> of bytes into the writer's data buffer. | ||
|  |         /// </summary> | ||
|  |         /// <param name="value">Source byte span</param> | ||
|  |         /// <returns>Whether the write was successful</returns> | ||
|  |         public bool WriteBytes(Span<byte> value) | ||
|  |         { | ||
|  |             fixed (byte* data = value) | ||
|  |             { | ||
|  |                 return WriteBytesInternal(data, value.Length); | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         /// <summary> | ||
|  |         /// Writes a 2-byte signed short to the current stream and advances the stream position by two bytes. | ||
|  |         /// </summary> | ||
|  |         /// <param name="value">The 2-byte signed short to write.</param> | ||
|  |         /// <returns>Whether the write was successful</returns> | ||
|  |         public bool WriteShort(short value) | ||
|  |         { | ||
|  |             return WriteBytesInternal((byte*)&value, sizeof(short)); | ||
|  |         } | ||
|  | 
 | ||
|  |         /// <summary> | ||
|  |         /// Writes a 2-byte unsigned short to the current stream and advances the stream position by two bytes. | ||
|  |         /// </summary> | ||
|  |         /// <param name="value">The 2-byte unsigned short to write.</param> | ||
|  |         /// <returns>Whether the write was successful</returns> | ||
|  |         public bool WriteUShort(ushort value) | ||
|  |         { | ||
|  |             return WriteBytesInternal((byte*)&value, sizeof(ushort)); | ||
|  |         } | ||
|  | 
 | ||
|  |         /// <summary> | ||
|  |         /// Writes a 4-byte signed integer from the current stream and advances the current position of the stream by four bytes. | ||
|  |         /// </summary> | ||
|  |         /// <param name="value">The 4-byte signed integer to write.</param> | ||
|  |         /// <returns>Whether the write was successful</returns> | ||
|  |         public bool WriteInt(int value) | ||
|  |         { | ||
|  |             return WriteBytesInternal((byte*)&value, sizeof(int)); | ||
|  |         } | ||
|  | 
 | ||
|  |         /// <summary> | ||
|  |         /// Reads a 4-byte unsigned integer from the current stream and advances the current position of the stream by four bytes. | ||
|  |         /// </summary> | ||
|  |         /// <param name="value">The 4-byte unsigned integer to write.</param> | ||
|  |         /// <returns>Whether the write was successful</returns> | ||
|  |         public bool WriteUInt(uint value) | ||
|  |         { | ||
|  |             return WriteBytesInternal((byte*)&value, sizeof(uint)); | ||
|  |         } | ||
|  | 
 | ||
|  |         /// <summary> | ||
|  |         /// Writes an 8-byte signed long from the stream and advances the current position of the stream by eight bytes. | ||
|  |         /// </summary> | ||
|  |         /// <param name="value">The 8-byte signed long to write.</param> | ||
|  |         /// <returns>Whether the write was successful</returns> | ||
|  |         public bool WriteLong(long value) | ||
|  |         { | ||
|  |             return WriteBytesInternal((byte*)&value, sizeof(long)); | ||
|  |         } | ||
|  | 
 | ||
|  |         /// <summary> | ||
|  |         /// Reads an 8-byte unsigned long from the stream and advances the current position of the stream by eight bytes. | ||
|  |         /// </summary> | ||
|  |         /// <param name="value">The 8-byte unsigned long to write.</param> | ||
|  |         /// <returns>Whether the write was successful</returns> | ||
|  |         public bool WriteULong(ulong value) | ||
|  |         { | ||
|  |             return WriteBytesInternal((byte*)&value, sizeof(ulong)); | ||
|  |         } | ||
|  | 
 | ||
|  |         /// <summary> | ||
|  |         /// Writes a 2-byte signed short to the current stream using Big-endian byte order and advances the stream position by two bytes. | ||
|  |         /// If the stream is in little-endian order, the byte order will be swapped. | ||
|  |         /// </summary> | ||
|  |         /// <param name="value">The 2-byte signed short to write.</param> | ||
|  |         /// <returns>Whether the write was successful</returns> | ||
|  |         public bool WriteShortNetworkByteOrder(short value) | ||
|  |         { | ||
|  |             short netValue = IsLittleEndian ? ByteSwap(value) : value; | ||
|  |             return WriteBytesInternal((byte*)&netValue, sizeof(short)); | ||
|  |         } | ||
|  | 
 | ||
|  | 
 | ||
|  |         /// <summary> | ||
|  |         /// Writes a 2-byte unsigned short to the current stream using Big-endian byte order and advances the stream position by two bytes. | ||
|  |         /// If the stream is in little-endian order, the byte order will be swapped. | ||
|  |         /// </summary> | ||
|  |         /// <param name="value">The 2-byte unsigned short to write.</param> | ||
|  |         /// <returns>Whether the write was successful</returns> | ||
|  |         public bool WriteUShortNetworkByteOrder(ushort value) | ||
|  |         { | ||
|  |             return WriteShortNetworkByteOrder((short)value); | ||
|  |         } | ||
|  | 
 | ||
|  |         /// <summary> | ||
|  |         /// Writes a 4-byte signed integer from the current stream using Big-endian byte order and advances the current position of the stream by four bytes. | ||
|  |         /// If the current machine is in little-endian order, the byte order will be swapped. | ||
|  |         /// </summary> | ||
|  |         /// <param name="value">The 4-byte signed integer to write.</param> | ||
|  |         /// <returns>Whether the write was successful</returns> | ||
|  |         public bool WriteIntNetworkByteOrder(int value) | ||
|  |         { | ||
|  |             int netValue = IsLittleEndian ? ByteSwap(value) : value; | ||
|  |             return WriteBytesInternal((byte*)&netValue, sizeof(int)); | ||
|  |         } | ||
|  | 
 | ||
|  |         /// <summary> | ||
|  |         /// Writes a 4-byte unsigned integer from the current stream using Big-endian byte order and advances the current position of the stream by four bytes. | ||
|  |         /// If the stream is in little-endian order, the byte order will be swapped. | ||
|  |         /// </summary> | ||
|  |         /// <param name="value">The 4-byte unsigned integer to write.</param> | ||
|  |         /// <returns>Whether the write was successful</returns> | ||
|  |         public bool WriteUIntNetworkByteOrder(uint value) | ||
|  |         { | ||
|  |             return WriteIntNetworkByteOrder((int)value); | ||
|  |         } | ||
|  | 
 | ||
|  |         /// <summary> | ||
|  |         /// Writes a 4-byte floating point value to the data stream. | ||
|  |         /// </summary> | ||
|  |         /// <param name="value">The 4-byte floating point value to write.</param> | ||
|  |         /// <returns>Whether the write was successful</returns> | ||
|  |         public bool WriteFloat(float value) | ||
|  |         { | ||
|  |             UIntFloat uf = new UIntFloat(); | ||
|  |             uf.floatValue = value; | ||
|  |             return WriteInt((int)uf.intValue); | ||
|  |         } | ||
|  | 
 | ||
|  |         /// <summary> | ||
|  |         /// Writes a 8-byte floating point value to the data stream. | ||
|  |         /// </summary> | ||
|  |         /// <param name="value">The 8-byte floating point value to write.</param> | ||
|  |         /// <returns>Whether the write was successful</returns> | ||
|  |         public bool WriteDouble(double value) | ||
|  |         { | ||
|  |             UIntFloat uf = new UIntFloat(); | ||
|  |             uf.doubleValue = value; | ||
|  |             return WriteLong((long)uf.longValue); | ||
|  |         } | ||
|  | 
 | ||
|  |         void FlushBits() | ||
|  |         { | ||
|  |             while (m_Data.bitIndex >= 8) | ||
|  |             { | ||
|  |                 m_Data.buffer[m_Data.length++] = (byte)m_Data.bitBuffer; | ||
|  |                 m_Data.bitIndex -= 8; | ||
|  |                 m_Data.bitBuffer >>= 8; | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         void WriteRawBitsInternal(uint value, int numbits) | ||
|  |         { | ||
|  |             CheckBits(value, numbits); | ||
|  | 
 | ||
|  |             m_Data.bitBuffer |= ((ulong)value << m_Data.bitIndex); | ||
|  |             m_Data.bitIndex += numbits; | ||
|  |         } | ||
|  | 
 | ||
|  |         /// <summary> | ||
|  |         /// Appends a specified number of bits to the data stream. | ||
|  |         /// </summary> | ||
|  |         /// <param name="value">The bits to write.</param> | ||
|  |         /// <param name="numbits">A positive number of bytes to write.</param> | ||
|  |         /// <returns>Whether the write was successful</returns> | ||
|  |         public bool WriteRawBits(uint value, int numbits) | ||
|  |         { | ||
|  |             CheckWrite(); | ||
|  | 
 | ||
|  |             if (m_Data.length + ((m_Data.bitIndex + numbits + 7) >> 3) > m_Data.capacity) | ||
|  |             { | ||
|  |                 ++m_Data.failedWrites; | ||
|  |                 return false; | ||
|  |             } | ||
|  |             WriteRawBitsInternal(value, numbits); | ||
|  |             FlushBits(); | ||
|  |             return true; | ||
|  |         } | ||
|  | 
 | ||
|  |         /// <summary> | ||
|  |         /// Writes a 4-byte unsigned integer value to the data stream using a <see cref="StreamCompressionModel"/>. | ||
|  |         /// </summary> | ||
|  |         /// <param name="value">The 4-byte unsigned integer to write.</param> | ||
|  |         /// <param name="model"><see cref="StreamCompressionModel"/> model for writing value in a packed manner.</param> | ||
|  |         /// <returns>Whether the write was successful</returns> | ||
|  |         public bool WritePackedUInt(uint value, in StreamCompressionModel model) | ||
|  |         { | ||
|  |             CheckWrite(); | ||
|  |             int bucket = model.CalculateBucket(value); | ||
|  |             uint offset = model.bucketOffsets[bucket]; | ||
|  |             int bits = model.bucketSizes[bucket]; | ||
|  |             ushort encodeEntry = model.encodeTable[bucket]; | ||
|  | 
 | ||
|  |             if (m_Data.length + ((m_Data.bitIndex + (encodeEntry & 0xff) + bits + 7) >> 3) > m_Data.capacity) | ||
|  |             { | ||
|  |                 ++m_Data.failedWrites; | ||
|  |                 return false; | ||
|  |             } | ||
|  |             WriteRawBitsInternal((uint)(encodeEntry >> 8), encodeEntry & 0xFF); | ||
|  |             WriteRawBitsInternal(value - offset, bits); | ||
|  |             FlushBits(); | ||
|  |             return true; | ||
|  |         } | ||
|  | 
 | ||
|  |         /// <summary> | ||
|  |         /// Writes an 8-byte unsigned long value to the data stream using a <see cref="StreamCompressionModel"/>. | ||
|  |         /// </summary> | ||
|  |         /// <param name="value">The 8-byte unsigned long to write.</param> | ||
|  |         /// <param name="model"><see cref="StreamCompressionModel"/> model for writing value in a packed manner.</param> | ||
|  |         /// <returns>Whether the write was successful</returns> | ||
|  |         public bool WritePackedULong(ulong value, in StreamCompressionModel model) | ||
|  |         { | ||
|  |             var data = (uint*)&value; | ||
|  |             return WritePackedUInt(data[0], model) & | ||
|  |                    WritePackedUInt(data[1], model); | ||
|  |         } | ||
|  | 
 | ||
|  |         /// <summary> | ||
|  |         /// Writes a 4-byte signed integer value to the data stream using a <see cref="StreamCompressionModel"/>. | ||
|  |         /// Negative values are interleaved between positive values, i.e. (0, -1, 1, -2, 2) | ||
|  |         /// </summary> | ||
|  |         /// <param name="value">The 4-byte signed integer to write.</param> | ||
|  |         /// <param name="model"><see cref="StreamCompressionModel"/> model for writing value in a packed manner.</param> | ||
|  |         /// <returns>Whether the write was successful</returns> | ||
|  |         public bool WritePackedInt(int value, in StreamCompressionModel model) | ||
|  |         { | ||
|  |             uint interleaved = (uint)((value >> 31) ^ (value << 1));      // interleave negative values between positive values: 0, -1, 1, -2, 2 | ||
|  |             return WritePackedUInt(interleaved, model); | ||
|  |         } | ||
|  | 
 | ||
|  |         /// <summary> | ||
|  |         /// Writes a 8-byte signed long value to the data stream using a <see cref="StreamCompressionModel"/>. | ||
|  |         /// </summary> | ||
|  |         /// <param name="value">The 8-byte signed long to write.</param> | ||
|  |         /// <param name="model"><see cref="StreamCompressionModel"/> model for writing value in a packed manner.</param> | ||
|  |         /// <returns>Whether the write was successful</returns> | ||
|  |         public bool WritePackedLong(long value, in StreamCompressionModel model) | ||
|  |         { | ||
|  |             ulong interleaved = (ulong)((value >> 63) ^ (value << 1));      // interleave negative values between positive values: 0, -1, 1, -2, 2 | ||
|  |             return WritePackedULong(interleaved, model); | ||
|  |         } | ||
|  | 
 | ||
|  |         /// <summary> | ||
|  |         /// Writes a 4-byte floating point value to the data stream using a <see cref="StreamCompressionModel"/>. | ||
|  |         /// </summary> | ||
|  |         /// <param name="value">The 4-byte floating point value to write.</param> | ||
|  |         /// <param name="model"><see cref="StreamCompressionModel"/> model for writing value in a packed manner.</param> | ||
|  |         /// <returns>Whether the write was successful</returns> | ||
|  |         public bool WritePackedFloat(float value, in StreamCompressionModel model) | ||
|  |         { | ||
|  |             return WritePackedFloatDelta(value, 0, model); | ||
|  |         } | ||
|  | 
 | ||
|  |         /// <summary> | ||
|  |         /// Writes a 8-byte floating point value to the data stream using a <see cref="StreamCompressionModel"/>. | ||
|  |         /// </summary> | ||
|  |         /// <param name="value">The 8-byte floating point value to write.</param> | ||
|  |         /// <param name="model"><see cref="StreamCompressionModel"/> model for writing value in a packed manner.</param> | ||
|  |         /// <returns>Whether the write was successful</returns> | ||
|  |         public bool WritePackedDouble(double value, in StreamCompressionModel model) | ||
|  |         { | ||
|  |             return WritePackedDoubleDelta(value, 0, model); | ||
|  |         } | ||
|  | 
 | ||
|  |         /// <summary> | ||
|  |         /// Writes a delta 4-byte unsigned integer value to the data stream using a <see cref="StreamCompressionModel"/>. | ||
|  |         /// Note that the Uint values are cast to an Int after computing the diff. | ||
|  |         /// </summary> | ||
|  |         /// <param name="value">The current 4-byte unsigned integer value.</param> | ||
|  |         /// <param name="baseline">The previous 4-byte unsigned integer value, used to compute the diff.</param> | ||
|  |         /// <param name="model"><see cref="StreamCompressionModel"/> model for writing value in a packed manner.</param> | ||
|  |         /// <returns>Whether the write was successful</returns> | ||
|  |         public bool WritePackedUIntDelta(uint value, uint baseline, in StreamCompressionModel model) | ||
|  |         { | ||
|  |             int diff = (int)(baseline - value); | ||
|  |             return WritePackedInt(diff, model); | ||
|  |         } | ||
|  | 
 | ||
|  |         /// <summary> | ||
|  |         /// Writes a delta 4-byte signed integer value to the data stream using a <see cref="StreamCompressionModel"/>. | ||
|  |         /// </summary> | ||
|  |         /// <param name="value">The current 4-byte signed integer value.</param> | ||
|  |         /// <param name="baseline">The previous 4-byte signed integer value, used to compute the diff.</param> | ||
|  |         /// <param name="model"><see cref="StreamCompressionModel"/> model for writing value in a packed manner.</param> | ||
|  |         /// <returns>Whether the write was successful</returns> | ||
|  |         public bool WritePackedIntDelta(int value, int baseline, in StreamCompressionModel model) | ||
|  |         { | ||
|  |             int diff = (int)(baseline - value); | ||
|  |             return WritePackedInt(diff, model); | ||
|  |         } | ||
|  | 
 | ||
|  |         /// <summary> | ||
|  |         /// Writes a delta 8-byte signed long value to the data stream using a <see cref="StreamCompressionModel"/>. | ||
|  |         /// </summary> | ||
|  |         /// <param name="value">The current 8-byte signed long value.</param> | ||
|  |         /// <param name="baseline">The previous 8-byte signed long value, used to compute the diff.</param> | ||
|  |         /// <param name="model"><see cref="StreamCompressionModel"/> model for writing value in a packed manner.</param> | ||
|  |         /// <returns>Whether the write was successful</returns> | ||
|  |         public bool WritePackedLongDelta(long value, long baseline, in StreamCompressionModel model) | ||
|  |         { | ||
|  |             long diff = (long)(baseline - value); | ||
|  |             return WritePackedLong(diff, model); | ||
|  |         } | ||
|  | 
 | ||
|  |         /// <summary> | ||
|  |         /// Writes a delta 8-byte unsigned long value to the data stream using a <see cref="StreamCompressionModel"/>. | ||
|  |         /// Note that the unsigned long values are cast to a signed long after computing the diff. | ||
|  |         /// </summary> | ||
|  |         /// <param name="value">The current 8-byte unsigned long value.</param> | ||
|  |         /// <param name="baseline">The previous 8-byte unsigned long, used to compute the diff.</param> | ||
|  |         /// <param name="model"><see cref="StreamCompressionModel"/> model for writing value in a packed manner.</param> | ||
|  |         /// <returns>Whether the write was successful</returns> | ||
|  |         public bool WritePackedULongDelta(ulong value, ulong baseline, in StreamCompressionModel model) | ||
|  |         { | ||
|  |             long diff = (long)(baseline - value); | ||
|  |             return WritePackedLong(diff, model); | ||
|  |         } | ||
|  | 
 | ||
|  |         /// <summary> | ||
|  |         /// Writes a 4-byte floating point value to the data stream. | ||
|  |         /// | ||
|  |         /// If the data did not change a zero bit is prepended, otherwise a 1 bit is prepended. | ||
|  |         /// When reading back the data, the first bit is then checked for whether the data was changed or not. | ||
|  |         /// </summary> | ||
|  |         /// <param name="value">The current 4-byte floating point value.</param> | ||
|  |         /// <param name="baseline">The previous 4-byte floating value, used to compute the diff.</param> | ||
|  |         /// <param name="model">Not currently used.</param> | ||
|  |         /// <returns>Whether the write was successful</returns> | ||
|  |         public bool WritePackedFloatDelta(float value, float baseline, in StreamCompressionModel model) | ||
|  |         { | ||
|  |             CheckWrite(); | ||
|  |             var bits = 0; | ||
|  |             if (value != baseline) | ||
|  |                 bits = 32; | ||
|  |             if (m_Data.length + ((m_Data.bitIndex + 1 + bits + 7) >> 3) > m_Data.capacity) | ||
|  |             { | ||
|  |                 ++m_Data.failedWrites; | ||
|  |                 return false; | ||
|  |             } | ||
|  |             if (bits == 0) | ||
|  |                 WriteRawBitsInternal(0, 1); | ||
|  |             else | ||
|  |             { | ||
|  |                 WriteRawBitsInternal(1, 1); | ||
|  |                 UIntFloat uf = new UIntFloat(); | ||
|  |                 uf.floatValue = value; | ||
|  |                 WriteRawBitsInternal(uf.intValue, bits); | ||
|  |             } | ||
|  |             FlushBits(); | ||
|  |             return true; | ||
|  |         } | ||
|  | 
 | ||
|  |         /// <summary> | ||
|  |         /// Writes a 8-byte floating point value to the data stream. | ||
|  |         /// | ||
|  |         /// If the data did not change a zero bit is prepended, otherwise a 1 bit is prepended. | ||
|  |         /// When reading back the data, the first bit is then checked for whether the data was changed or not. | ||
|  |         /// </summary> | ||
|  |         /// <param name="value">The current 8-byte floating point value.</param> | ||
|  |         /// <param name="baseline">The previous 8-byte floating value, used to compute the diff.</param> | ||
|  |         /// <param name="model">Not currently used.</param> | ||
|  |         /// <returns>Whether the write was successful</returns> | ||
|  |         public bool WritePackedDoubleDelta(double value, double baseline, in StreamCompressionModel model) | ||
|  |         { | ||
|  |             CheckWrite(); | ||
|  |             var bits = 0; | ||
|  |             if (value != baseline) | ||
|  |                 bits = 64; | ||
|  |             if (m_Data.length + ((m_Data.bitIndex + 1 + bits + 7) >> 3) > m_Data.capacity) | ||
|  |             { | ||
|  |                 ++m_Data.failedWrites; | ||
|  |                 return false; | ||
|  |             } | ||
|  |             if (bits == 0) | ||
|  |                 WriteRawBitsInternal(0, 1); | ||
|  |             else | ||
|  |             { | ||
|  |                 WriteRawBitsInternal(1, 1); | ||
|  |                 UIntFloat uf = new UIntFloat(); | ||
|  |                 uf.doubleValue = value; | ||
|  |                 var data = (uint*)&uf.longValue; | ||
|  |                 WriteRawBitsInternal(data[0], 32); | ||
|  |                 FlushBits(); | ||
|  |                 WriteRawBitsInternal(data[1], 32); | ||
|  |             } | ||
|  |             FlushBits(); | ||
|  |             return true; | ||
|  |         } | ||
|  | 
 | ||
|  | 
 | ||
|  |         /// <summary> | ||
|  |         /// Writes a <c>FixedString32Bytes</c> value to the data stream. | ||
|  |         /// </summary> | ||
|  |         /// <param name="str">The <c>FixedString32Bytes</c> to write.</param> | ||
|  |         /// <returns>Whether the write was successful</returns> | ||
|  |         public unsafe bool WriteFixedString32(FixedString32Bytes str) | ||
|  |         { | ||
|  |             int length = (int)*((ushort*)&str) + 2; | ||
|  |             byte* data = ((byte*)&str); | ||
|  |             return WriteBytesInternal(data, length); | ||
|  |         } | ||
|  | 
 | ||
|  |         /// <summary> | ||
|  |         /// Writes a <c>FixedString64Bytes</c> value to the data stream. | ||
|  |         /// </summary> | ||
|  |         /// <param name="str">The <c>FixedString64Bytes</c> to write.</param> | ||
|  |         /// <returns>Whether the write was successful</returns> | ||
|  |         public unsafe bool WriteFixedString64(FixedString64Bytes str) | ||
|  |         { | ||
|  |             int length = (int)*((ushort*)&str) + 2; | ||
|  |             byte* data = ((byte*)&str); | ||
|  |             return WriteBytesInternal(data, length); | ||
|  |         } | ||
|  | 
 | ||
|  |         /// <summary> | ||
|  |         /// Writes a <c>FixedString128Bytes</c> value to the data stream. | ||
|  |         /// </summary> | ||
|  |         /// <param name="str">The <c>FixedString128Bytes</c> to write.</param> | ||
|  |         /// <returns>Whether the write was successful</returns> | ||
|  |         public unsafe bool WriteFixedString128(FixedString128Bytes str) | ||
|  |         { | ||
|  |             int length = (int)*((ushort*)&str) + 2; | ||
|  |             byte* data = ((byte*)&str); | ||
|  |             return WriteBytesInternal(data, length); | ||
|  |         } | ||
|  | 
 | ||
|  |         /// <summary> | ||
|  |         /// Writes a <c>FixedString512Bytes</c> value to the data stream. | ||
|  |         /// </summary> | ||
|  |         /// <param name="str">The <c>FixedString512Bytes</c> to write.</param> | ||
|  |         /// <returns>Whether the write was successful</returns> | ||
|  |         public unsafe bool WriteFixedString512(FixedString512Bytes str) | ||
|  |         { | ||
|  |             int length = (int)*((ushort*)&str) + 2; | ||
|  |             byte* data = ((byte*)&str); | ||
|  |             return WriteBytesInternal(data, length); | ||
|  |         } | ||
|  | 
 | ||
|  |         /// <summary> | ||
|  |         /// Writes a <c>FixedString4096Bytes</c> value to the data stream. | ||
|  |         /// </summary> | ||
|  |         /// <param name="str">The <c>FixedString4096Bytes</c> to write.</param> | ||
|  |         /// <returns>Whether the write was successful</returns> | ||
|  |         public unsafe bool WriteFixedString4096(FixedString4096Bytes str) | ||
|  |         { | ||
|  |             int length = (int)*((ushort*)&str) + 2; | ||
|  |             byte* data = ((byte*)&str); | ||
|  |             return WriteBytesInternal(data, length); | ||
|  |         } | ||
|  | 
 | ||
|  |         /// <summary> | ||
|  |         /// Writes a <c>FixedString32Bytes</c> delta value to the data stream using a <see cref="StreamCompressionModel"/>. | ||
|  |         /// </summary> | ||
|  |         /// <param name="str">The current <c>FixedString32Bytes</c> value.</param> | ||
|  |         /// <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>Whether the write was successful</returns> | ||
|  |         public unsafe bool WritePackedFixedString32Delta(FixedString32Bytes str, FixedString32Bytes baseline, in StreamCompressionModel model) | ||
|  |         { | ||
|  |             ushort length = *((ushort*)&str); | ||
|  |             byte* data = ((byte*)&str) + 2; | ||
|  |             return WritePackedFixedStringDelta(data, length, ((byte*)&baseline) + 2, *((ushort*)&baseline), model); | ||
|  |         } | ||
|  | 
 | ||
|  |         /// <summary> | ||
|  |         /// Writes a delta <c>FixedString64Bytes</c> value to the data stream using a <see cref="StreamCompressionModel"/>. | ||
|  |         /// </summary> | ||
|  |         /// <param name="str">The current <c>FixedString64Bytes</c> value.</param> | ||
|  |         /// <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>Whether the write was successful</returns> | ||
|  |         public unsafe bool WritePackedFixedString64Delta(FixedString64Bytes str, FixedString64Bytes baseline, in StreamCompressionModel model) | ||
|  |         { | ||
|  |             ushort length = *((ushort*)&str); | ||
|  |             byte* data = ((byte*)&str) + 2; | ||
|  |             return WritePackedFixedStringDelta(data, length, ((byte*)&baseline) + 2, *((ushort*)&baseline), model); | ||
|  |         } | ||
|  | 
 | ||
|  |         /// <summary> | ||
|  |         /// Writes a delta <c>FixedString128Bytes</c> value to the data stream using a <see cref="StreamCompressionModel"/>. | ||
|  |         /// </summary> | ||
|  |         /// <param name="str">The current <c>FixedString128Bytes</c> value.</param> | ||
|  |         /// <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>Whether the write was successful</returns> | ||
|  |         public unsafe bool WritePackedFixedString128Delta(FixedString128Bytes str, FixedString128Bytes baseline, in StreamCompressionModel model) | ||
|  |         { | ||
|  |             ushort length = *((ushort*)&str); | ||
|  |             byte* data = ((byte*)&str) + 2; | ||
|  |             return WritePackedFixedStringDelta(data, length, ((byte*)&baseline) + 2, *((ushort*)&baseline), model); | ||
|  |         } | ||
|  | 
 | ||
|  |         /// <summary> | ||
|  |         /// Writes a delta <c>FixedString512Bytes</c> value to the data stream using a <see cref="StreamCompressionModel"/>. | ||
|  |         /// </summary> | ||
|  |         /// <param name="str">The current <c>FixedString512Bytes</c> value.</param> | ||
|  |         /// <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>Whether the write was successful</returns> | ||
|  |         public unsafe bool WritePackedFixedString512Delta(FixedString512Bytes str, FixedString512Bytes baseline, in StreamCompressionModel model) | ||
|  |         { | ||
|  |             ushort length = *((ushort*)&str); | ||
|  |             byte* data = ((byte*)&str) + 2; | ||
|  |             return WritePackedFixedStringDelta(data, length, ((byte*)&baseline) + 2, *((ushort*)&baseline), model); | ||
|  |         } | ||
|  | 
 | ||
|  |         /// <summary> | ||
|  |         /// Writes a delta <c>FixedString4096Bytes</c> value to the data stream using a <see cref="StreamCompressionModel"/>. | ||
|  |         /// </summary> | ||
|  |         /// <param name="str">The current <c>FixedString4096Bytes</c> value.</param> | ||
|  |         /// <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>Whether the write was successful</returns> | ||
|  |         public unsafe bool WritePackedFixedString4096Delta(FixedString4096Bytes str, FixedString4096Bytes baseline, in StreamCompressionModel model) | ||
|  |         { | ||
|  |             ushort length = *((ushort*)&str); | ||
|  |             byte* data = ((byte*)&str) + 2; | ||
|  |             return WritePackedFixedStringDelta(data, length, ((byte*)&baseline) + 2, *((ushort*)&baseline), model); | ||
|  |         } | ||
|  | 
 | ||
|  |         /// <summary> | ||
|  |         /// Writes a delta FixedString value to the data stream using a <see cref="StreamCompressionModel"/>. | ||
|  |         /// | ||
|  |         /// If the value cannot be written <see cref="HasFailedWrites"/> will return true. This state can be cleared by | ||
|  |         /// calling <see cref="Clear"/>. | ||
|  |         /// </summary> | ||
|  |         /// <param name="data">Pointer to a packed fixed string.</param> | ||
|  |         /// <param name="length">The length of the new value.</param> | ||
|  |         /// <param name="baseData">The previous value, used to compute the diff.</param> | ||
|  |         /// <param name="baseLength">The length of the previous value.</param> | ||
|  |         /// <param name="model"><see cref="StreamCompressionModel"/> model for writing value in a packed manner.</param> | ||
|  |         /// <returns>Whether the write was successful</returns> | ||
|  |         unsafe bool WritePackedFixedStringDelta(byte* data, uint length, byte* baseData, uint baseLength, in StreamCompressionModel model) | ||
|  |         { | ||
|  |             var oldData = m_Data; | ||
|  |             if (!WritePackedUIntDelta(length, baseLength, model)) | ||
|  |                 return false; | ||
|  |             bool didFailWrite = false; | ||
|  |             if (length <= baseLength) | ||
|  |             { | ||
|  |                 for (uint i = 0; i < length; ++i) | ||
|  |                     didFailWrite |= !WritePackedUIntDelta(data[i], baseData[i], model); | ||
|  |             } | ||
|  |             else | ||
|  |             { | ||
|  |                 for (uint i = 0; i < baseLength; ++i) | ||
|  |                     didFailWrite |= !WritePackedUIntDelta(data[i], baseData[i], model); | ||
|  |                 for (uint i = baseLength; i < length; ++i) | ||
|  |                     didFailWrite |= !WritePackedUInt(data[i], model); | ||
|  |             } | ||
|  |             // If anything was not written, rewind to the previous position | ||
|  |             if (didFailWrite) | ||
|  |             { | ||
|  |                 m_Data = oldData; | ||
|  |                 ++m_Data.failedWrites; | ||
|  |             } | ||
|  |             return !didFailWrite; | ||
|  |         } | ||
|  | 
 | ||
|  |         /// <summary> | ||
|  |         /// Moves the write position to the start of the data buffer used. | ||
|  |         /// </summary> | ||
|  |         public void Clear() | ||
|  |         { | ||
|  |             m_Data.length = 0; | ||
|  |             m_Data.bitIndex = 0; | ||
|  |             m_Data.bitBuffer = 0; | ||
|  |             m_Data.failedWrites = 0; | ||
|  |         } | ||
|  | 
 | ||
|  |         [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")] | ||
|  |         [MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
|  |         readonly void CheckRead() | ||
|  |         { | ||
|  | #if ENABLE_UNITY_COLLECTIONS_CHECKS | ||
|  |             AtomicSafetyHandle.CheckReadAndThrow(m_Safety); | ||
|  | #endif | ||
|  |         } | ||
|  | 
 | ||
|  |         [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")] | ||
|  |         [MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
|  |         void CheckWrite() | ||
|  |         { | ||
|  | #if ENABLE_UNITY_COLLECTIONS_CHECKS | ||
|  |             AtomicSafetyHandle.CheckWriteAndThrow(m_Safety); | ||
|  | #endif | ||
|  |         } | ||
|  | 
 | ||
|  |         [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")] | ||
|  |         static void CheckAllocator(AllocatorManager.AllocatorHandle allocator) | ||
|  |         { | ||
|  |             if (allocator.ToAllocator != Allocator.Temp) | ||
|  |                 throw new InvalidOperationException("DataStreamWriters can only be created with temp memory"); | ||
|  |         } | ||
|  | 
 | ||
|  |         [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")] | ||
|  |         static void CheckBits(uint value, int numBits) | ||
|  |         { | ||
|  |             if (numBits < 0 || numBits > 32) | ||
|  |                 throw new ArgumentOutOfRangeException($"Invalid number of bits specified: {numBits}! Valid range is (0, 32) inclusive."); | ||
|  |             var errValue = (1UL << numBits); | ||
|  |             if (value >= errValue) | ||
|  |                 throw new ArgumentOutOfRangeException($"Value {value} does not fit in the specified number of bits: {numBits}! Range (inclusive) is (0, {errValue-1})!"); | ||
|  |         } | ||
|  |     } | ||
|  | } |