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 { /// /// Writes data in an endian format to serialize data. /// /// /// Data streams can be used to serialize data (e.g. over the network). The /// DataStreamWriter and 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. ///
/// 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: /// /// 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(); /// } /// /// /// 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. /// /// /// /// 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)); /// } /// ///
[MovedFrom(true, "Unity.Networking.Transport", "Unity.Networking.Transport")] [StructLayout(LayoutKind.Sequential)] [GenerateTestsForBurstCompatibility] public unsafe struct DataStreamWriter { /// /// Show the byte order in which the current computer architecture stores data. /// /// /// Different computer architectures store data using different byte orders. /// /// Big-endian: the most significant byte is at the left end of a word. /// Little-endian: means the most significant byte is at the right end of a word. /// /// 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; /// /// Used for sending data asynchronously. /// public IntPtr m_SendHandleData; #if ENABLE_UNITY_COLLECTIONS_CHECKS AtomicSafetyHandle m_Safety; #endif /// /// Initializes a new instance of the DataStreamWriter struct. /// /// The number of bytes available in the buffer. /// The used to allocate the memory. public DataStreamWriter(int length, AllocatorManager.AllocatorHandle allocator) { CheckAllocator(allocator); Initialize(out this, CollectionHelper.CreateNativeArray(length, allocator)); } /// /// Initializes a new instance of the DataStreamWriter struct with a NativeArray<byte> /// /// The buffer to attach to the DataStreamWriter. public DataStreamWriter(NativeArray data) { Initialize(out this, data); } /// /// Initializes a new instance of the DataStreamWriter struct with a memory we don't own /// /// Pointer to the data /// Length of the data public DataStreamWriter(byte* data, int length) { var na = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray(data, length, Allocator.Invalid); #if ENABLE_UNITY_COLLECTIONS_CHECKS NativeArrayUnsafeUtility.SetAtomicSafetyHandle(ref na, AtomicSafetyHandle.GetTempMemoryHandle()); #endif Initialize(out this, na); } /// /// Convert internal data buffer to NativeArray for use in entities APIs. /// /// NativeArray representation of internal buffer. public NativeArray AsNativeArray() { var na = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray(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 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)); } /// /// True if there is a valid data buffer present. This would be false /// if the writer was created with no arguments. /// public readonly bool IsCreated { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return m_Data.buffer != null; } } /// /// 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. /// public readonly bool HasFailedWrites { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => m_Data.failedWrites > 0; } /// /// The total size of the data buffer, see for /// the size of space used in the buffer. /// public readonly int Capacity { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { CheckRead(); return m_Data.capacity; } } /// /// The size of the buffer used. See for the total size. /// public int Length { get { CheckRead(); SyncBitData(); return m_Data.length + ((m_Data.bitIndex + 7) >> 3); } } /// /// The size of the buffer used in bits. See for the length in bytes. /// 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; } } /// /// 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. /// 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; } /// /// Writes an unsigned byte to the current stream and advances the stream position by one byte. /// /// The unsigned byte to write. /// Whether the write was successful public bool WriteByte(byte value) { return WriteBytesInternal((byte*)&value, sizeof(byte)); } /// /// Copy NativeArray of bytes into the writer's data buffer. /// /// Source byte array /// Whether the write was successful public bool WriteBytes(NativeArray value) { return WriteBytesInternal((byte*)value.GetUnsafeReadOnlyPtr(), value.Length); } /// /// Copy Span of bytes into the writer's data buffer. /// /// Source byte span /// Whether the write was successful public bool WriteBytes(Span value) { fixed (byte* data = value) { return WriteBytesInternal(data, value.Length); } } /// /// Writes a 2-byte signed short to the current stream and advances the stream position by two bytes. /// /// The 2-byte signed short to write. /// Whether the write was successful public bool WriteShort(short value) { return WriteBytesInternal((byte*)&value, sizeof(short)); } /// /// Writes a 2-byte unsigned short to the current stream and advances the stream position by two bytes. /// /// The 2-byte unsigned short to write. /// Whether the write was successful public bool WriteUShort(ushort value) { return WriteBytesInternal((byte*)&value, sizeof(ushort)); } /// /// Writes a 4-byte signed integer from the current stream and advances the current position of the stream by four bytes. /// /// The 4-byte signed integer to write. /// Whether the write was successful public bool WriteInt(int value) { return WriteBytesInternal((byte*)&value, sizeof(int)); } /// /// Reads a 4-byte unsigned integer from the current stream and advances the current position of the stream by four bytes. /// /// The 4-byte unsigned integer to write. /// Whether the write was successful public bool WriteUInt(uint value) { return WriteBytesInternal((byte*)&value, sizeof(uint)); } /// /// Writes an 8-byte signed long from the stream and advances the current position of the stream by eight bytes. /// /// The 8-byte signed long to write. /// Whether the write was successful public bool WriteLong(long value) { return WriteBytesInternal((byte*)&value, sizeof(long)); } /// /// Reads an 8-byte unsigned long from the stream and advances the current position of the stream by eight bytes. /// /// The 8-byte unsigned long to write. /// Whether the write was successful public bool WriteULong(ulong value) { return WriteBytesInternal((byte*)&value, sizeof(ulong)); } /// /// 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. /// /// The 2-byte signed short to write. /// Whether the write was successful public bool WriteShortNetworkByteOrder(short value) { short netValue = IsLittleEndian ? ByteSwap(value) : value; return WriteBytesInternal((byte*)&netValue, sizeof(short)); } /// /// 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. /// /// The 2-byte unsigned short to write. /// Whether the write was successful public bool WriteUShortNetworkByteOrder(ushort value) { return WriteShortNetworkByteOrder((short)value); } /// /// 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. /// /// The 4-byte signed integer to write. /// Whether the write was successful public bool WriteIntNetworkByteOrder(int value) { int netValue = IsLittleEndian ? ByteSwap(value) : value; return WriteBytesInternal((byte*)&netValue, sizeof(int)); } /// /// 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. /// /// The 4-byte unsigned integer to write. /// Whether the write was successful public bool WriteUIntNetworkByteOrder(uint value) { return WriteIntNetworkByteOrder((int)value); } /// /// Writes a 4-byte floating point value to the data stream. /// /// The 4-byte floating point value to write. /// Whether the write was successful public bool WriteFloat(float value) { UIntFloat uf = new UIntFloat(); uf.floatValue = value; return WriteInt((int)uf.intValue); } /// /// Writes a 8-byte floating point value to the data stream. /// /// The 8-byte floating point value to write. /// Whether the write was successful 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; } /// /// Appends a specified number of bits to the data stream. /// /// The bits to write. /// A positive number of bytes to write. /// Whether the write was successful 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; } /// /// Writes a 4-byte unsigned integer value to the data stream using a . /// /// The 4-byte unsigned integer to write. /// model for writing value in a packed manner. /// Whether the write was successful 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; } /// /// Writes an 8-byte unsigned long value to the data stream using a . /// /// The 8-byte unsigned long to write. /// model for writing value in a packed manner. /// Whether the write was successful public bool WritePackedULong(ulong value, in StreamCompressionModel model) { var data = (uint*)&value; return WritePackedUInt(data[0], model) & WritePackedUInt(data[1], model); } /// /// Writes a 4-byte signed integer value to the data stream using a . /// Negative values are interleaved between positive values, i.e. (0, -1, 1, -2, 2) /// /// The 4-byte signed integer to write. /// model for writing value in a packed manner. /// Whether the write was successful 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); } /// /// Writes a 8-byte signed long value to the data stream using a . /// /// The 8-byte signed long to write. /// model for writing value in a packed manner. /// Whether the write was successful 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); } /// /// Writes a 4-byte floating point value to the data stream using a . /// /// The 4-byte floating point value to write. /// model for writing value in a packed manner. /// Whether the write was successful public bool WritePackedFloat(float value, in StreamCompressionModel model) { return WritePackedFloatDelta(value, 0, model); } /// /// Writes a 8-byte floating point value to the data stream using a . /// /// The 8-byte floating point value to write. /// model for writing value in a packed manner. /// Whether the write was successful public bool WritePackedDouble(double value, in StreamCompressionModel model) { return WritePackedDoubleDelta(value, 0, model); } /// /// Writes a delta 4-byte unsigned integer value to the data stream using a . /// Note that the Uint values are cast to an Int after computing the diff. /// /// The current 4-byte unsigned integer value. /// The previous 4-byte unsigned integer value, used to compute the diff. /// model for writing value in a packed manner. /// Whether the write was successful public bool WritePackedUIntDelta(uint value, uint baseline, in StreamCompressionModel model) { int diff = (int)(baseline - value); return WritePackedInt(diff, model); } /// /// Writes a delta 4-byte signed integer value to the data stream using a . /// /// The current 4-byte signed integer value. /// The previous 4-byte signed integer value, used to compute the diff. /// model for writing value in a packed manner. /// Whether the write was successful public bool WritePackedIntDelta(int value, int baseline, in StreamCompressionModel model) { int diff = (int)(baseline - value); return WritePackedInt(diff, model); } /// /// Writes a delta 8-byte signed long value to the data stream using a . /// /// The current 8-byte signed long value. /// The previous 8-byte signed long value, used to compute the diff. /// model for writing value in a packed manner. /// Whether the write was successful public bool WritePackedLongDelta(long value, long baseline, in StreamCompressionModel model) { long diff = (long)(baseline - value); return WritePackedLong(diff, model); } /// /// Writes a delta 8-byte unsigned long value to the data stream using a . /// Note that the unsigned long values are cast to a signed long after computing the diff. /// /// The current 8-byte unsigned long value. /// The previous 8-byte unsigned long, used to compute the diff. /// model for writing value in a packed manner. /// Whether the write was successful public bool WritePackedULongDelta(ulong value, ulong baseline, in StreamCompressionModel model) { long diff = (long)(baseline - value); return WritePackedLong(diff, model); } /// /// 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. /// /// The current 4-byte floating point value. /// The previous 4-byte floating value, used to compute the diff. /// Not currently used. /// Whether the write was successful 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; } /// /// 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. /// /// The current 8-byte floating point value. /// The previous 8-byte floating value, used to compute the diff. /// Not currently used. /// Whether the write was successful 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; } /// /// Writes a FixedString32Bytes value to the data stream. /// /// The FixedString32Bytes to write. /// Whether the write was successful public unsafe bool WriteFixedString32(FixedString32Bytes str) { int length = (int)*((ushort*)&str) + 2; byte* data = ((byte*)&str); return WriteBytesInternal(data, length); } /// /// Writes a FixedString64Bytes value to the data stream. /// /// The FixedString64Bytes to write. /// Whether the write was successful public unsafe bool WriteFixedString64(FixedString64Bytes str) { int length = (int)*((ushort*)&str) + 2; byte* data = ((byte*)&str); return WriteBytesInternal(data, length); } /// /// Writes a FixedString128Bytes value to the data stream. /// /// The FixedString128Bytes to write. /// Whether the write was successful public unsafe bool WriteFixedString128(FixedString128Bytes str) { int length = (int)*((ushort*)&str) + 2; byte* data = ((byte*)&str); return WriteBytesInternal(data, length); } /// /// Writes a FixedString512Bytes value to the data stream. /// /// The FixedString512Bytes to write. /// Whether the write was successful public unsafe bool WriteFixedString512(FixedString512Bytes str) { int length = (int)*((ushort*)&str) + 2; byte* data = ((byte*)&str); return WriteBytesInternal(data, length); } /// /// Writes a FixedString4096Bytes value to the data stream. /// /// The FixedString4096Bytes to write. /// Whether the write was successful public unsafe bool WriteFixedString4096(FixedString4096Bytes str) { int length = (int)*((ushort*)&str) + 2; byte* data = ((byte*)&str); return WriteBytesInternal(data, length); } /// /// Writes a FixedString32Bytes delta value to the data stream using a . /// /// The current FixedString32Bytes value. /// The previous FixedString32Bytes value, used to compute the diff. /// model for writing value in a packed manner. /// Whether the write was successful 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); } /// /// Writes a delta FixedString64Bytes value to the data stream using a . /// /// The current FixedString64Bytes value. /// The previous FixedString64Bytes value, used to compute the diff. /// model for writing value in a packed manner. /// Whether the write was successful 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); } /// /// Writes a delta FixedString128Bytes value to the data stream using a . /// /// The current FixedString128Bytes value. /// The previous FixedString128Bytes value, used to compute the diff. /// model for writing value in a packed manner. /// Whether the write was successful 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); } /// /// Writes a delta FixedString512Bytes value to the data stream using a . /// /// The current FixedString512Bytes value. /// The previous FixedString512Bytes value, used to compute the diff. /// model for writing value in a packed manner. /// Whether the write was successful 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); } /// /// Writes a delta FixedString4096Bytes value to the data stream using a . /// /// The current FixedString4096Bytes value. /// The previous FixedString4096Bytes value, used to compute the diff. /// model for writing value in a packed manner. /// Whether the write was successful 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); } /// /// Writes a delta FixedString value to the data stream using a . /// /// If the value cannot be written will return true. This state can be cleared by /// calling . /// /// Pointer to a packed fixed string. /// The length of the new value. /// The previous value, used to compute the diff. /// The length of the previous value. /// model for writing value in a packed manner. /// Whether the write was successful 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; } /// /// Moves the write position to the start of the data buffer used. /// 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})!"); } } }