using System;
using System.Diagnostics;
using Unity.Collections.LowLevel.Unsafe;
using UnityEngine.Scripting.APIUpdating;
namespace Unity.Collections
{
    /// 
    /// Writes data in an endian format to deserialize data.
    /// 
    /// 
    /// The DataStreamReader class is the counterpart of the
    ///  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.
    /// 
    /// For network byte order use the so named methods.
    /// 
    /// Simple usage example:
    /// 
    /// 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);
    /// }
    /// 
    ///
    /// 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.
    ///
    /// 
    /// 
    /// 
    [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
        /// 
        /// Initializes a new instance of the DataStreamReader struct with a NativeArray<byte>
        /// 
        /// The buffer to attach to the DataStreamReader.
        public DataStreamReader(NativeArray array)
        {
            Initialize(out this, array);
        }
        static void Initialize(out DataStreamReader self, NativeArray 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;
        }
        /// 
        /// 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 { 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));
        }
        /// 
        /// 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.
        /// 
        public readonly bool HasFailedReads => m_Context.m_FailedReads > 0;
        /// 
        /// The total size of the buffer space this reader is working with.
        /// 
        public readonly int Length
        {
            get
            {
                CheckRead();
                return m_Length;
            }
        }
        /// 
        /// True if the reader has been pointed to a valid buffer space. This
        /// would be false if the reader was created with no arguments.
        /// 
        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;
        }
        /// 
        /// 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
        ///  will then be true.
        /// 
        /// Array to copy data into.
        public void ReadBytes(NativeArray array)
        {
            ReadBytesInternal((byte*)array.GetUnsafePtr(), array.Length);
        }
        /// 
        /// Read and copy data into the given Span of bytes. An error will
        /// be logged if not enough bytes are available to fill the array, and
        ///  will then be true.
        /// 
        /// Span to copy data into.
        public void ReadBytes(Span span)
        {
            fixed (byte* ptr = span)
            {
                ReadBytesInternal(ptr, span.Length);
            }
        }
        /// 
        /// Gets the number of bytes read from the data stream.
        /// 
        /// Number of bytes read.
        public int GetBytesRead()
        {
            return m_Context.m_ReadByteIndex - (m_Context.m_BitIndex >> 3);
        }
        /// 
        /// Gets the number of bits read from the data stream.
        /// 
        /// Number of bits read.
        public int GetBitsRead()
        {
            return (m_Context.m_ReadByteIndex << 3) - m_Context.m_BitIndex;
        }
        /// 
        /// Sets the current position of this stream to the given value.
        /// An error will be logged if  is outside the length of the stream.
        ///
        /// In addition this will reset the bit index and the bit buffer.
        /// 
        /// Seek position.
        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;
        }
        /// 
        /// Reads an unsigned byte from the current stream and advances the current position of the stream by one byte.
        /// 
        /// The next byte read from the current stream, or 0 if the end of the stream has been reached.
        public byte ReadByte()
        {
            byte data;
            ReadBytesInternal((byte*)&data, sizeof(byte));
            return data;
        }
        /// 
        /// Reads a 2-byte signed short from the current stream and advances the current position of the stream by two bytes.
        /// 
        /// A 2-byte signed short read from the current stream, or 0 if the end of the stream has been reached.
        public short ReadShort()
        {
            short data;
            ReadBytesInternal((byte*)&data, sizeof(short));
            return data;
        }
        /// 
        /// Reads a 2-byte unsigned short from the current stream and advances the current position of the stream by two bytes.
        /// 
        /// A 2-byte unsigned short read from the current stream, or 0 if the end of the stream has been reached.
        public ushort ReadUShort()
        {
            ushort data;
            ReadBytesInternal((byte*)&data, sizeof(ushort));
            return data;
        }
        /// 
        /// Reads a 4-byte signed integer from the current stream and advances the current position of the stream by four bytes.
        /// 
        /// A 4-byte signed integer read from the current stream, or 0 if the end of the stream has been reached.
        public int ReadInt()
        {
            int data;
            ReadBytesInternal((byte*)&data, sizeof(int));
            return data;
        }
        /// 
        /// Reads a 4-byte unsigned integer from the current stream and advances the current position of the stream by four bytes.
        /// 
        /// A 4-byte unsigned integer read from the current stream, or 0 if the end of the stream has been reached.
        public uint ReadUInt()
        {
            uint data;
            ReadBytesInternal((byte*)&data, sizeof(uint));
            return data;
        }
        /// 
        /// Reads an 8-byte signed long from the stream and advances the current position of the stream by eight bytes.
        /// 
        /// An 8-byte signed long read from the current stream, or 0 if the end of the stream has been reached.
        public long ReadLong()
        {
            long data;
            ReadBytesInternal((byte*)&data, sizeof(long));
            return data;
        }
        /// 
        /// Reads an 8-byte unsigned long from the stream and advances the current position of the stream by eight bytes.
        /// 
        /// An 8-byte unsigned long read from the current stream, or 0 if the end of the stream has been reached.
        public ulong ReadULong()
        {
            ulong data;
            ReadBytesInternal((byte*)&data, sizeof(ulong));
            return data;
        }
        /// 
        /// Aligns the read pointer to the next byte-aligned position. Does nothing if already aligned.
        /// 
        /// If you call , call this to bit-align the reader.
        public void Flush()
        {
            m_Context.m_ReadByteIndex -= (m_Context.m_BitIndex >> 3);
            m_Context.m_BitIndex = 0;
            m_Context.m_BitBuffer = 0;
        }
        /// 
        /// 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.
        /// 
        /// A 2-byte signed short read from the current stream, or 0 if the end of the stream has been reached.
        public short ReadShortNetworkByteOrder()
        {
            short data;
            ReadBytesInternal((byte*)&data, sizeof(short));
            return IsLittleEndian ? ByteSwap(data) : data;
        }
        /// 
        /// 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.
        /// 
        /// A 2-byte unsigned short read from the current stream, or 0 if the end of the stream has been reached.
        public ushort ReadUShortNetworkByteOrder()
        {
            return (ushort)ReadShortNetworkByteOrder();
        }
        /// 
        /// 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.
        /// 
        /// A 4-byte signed integer read from the current stream, or 0 if the end of the stream has been reached.
        public int ReadIntNetworkByteOrder()
        {
            int data;
            ReadBytesInternal((byte*)&data, sizeof(int));
            return IsLittleEndian ? ByteSwap(data) : data;
        }
        /// 
        /// 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.
        /// 
        /// A 4-byte unsigned integer read from the current stream, or 0 if the end of the stream has been reached.
        public uint ReadUIntNetworkByteOrder()
        {
            return (uint)ReadIntNetworkByteOrder();
        }
        /// 
        /// Reads a 4-byte floating point value from the current stream and advances the current position of the stream by four bytes.
        /// 
        /// A 4-byte floating point value read from the current stream, or 0 if the end of the stream has been reached.
        public float ReadFloat()
        {
            UIntFloat uf = new UIntFloat();
            uf.intValue = (uint)ReadInt();
            return uf.floatValue;
        }
        /// 
        /// Reads a 8-byte floating point value from the current stream and advances the current position of the stream by four bytes.
        /// 
        /// A 8-byte floating point value read from the current stream, or 0 if the end of the stream has been reached.
        public double ReadDouble()
        {
            UIntFloat uf = new UIntFloat();
            uf.longValue = (ulong)ReadLong();
            return uf.doubleValue;
        }
        /// 
        /// Reads a 4-byte unsigned integer from the current stream using a  and advances the current position the number of bits depending on the model.
        /// 
        ///  model for reading value in a packed manner.
        /// A 4-byte unsigned integer read from the current stream, or 0 if the end of the stream has been reached.
        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;
        }
        /// 
        /// Reads a specified number of bits from the data stream.
        /// 
        /// A positive number of bytes to write.
        /// A 4-byte unsigned integer read from the current stream, or 0 if the end of the stream has been reached.
        public uint ReadRawBits(int numbits)
        {
            CheckRead();
            FillBitBuffer();
            return ReadRawBitsInternal(numbits);
        }
        /// 
        /// Reads an 8-byte unsigned long value from the data stream using a .
        /// 
        ///  model for reading value in a packed manner.
        /// An 8-byte unsigned long read from the current stream, or 0 if the end of the stream has been reached.
        public ulong ReadPackedULong(in StreamCompressionModel model)
        {
            ulong value;
            ((uint*)&value)[0] = ReadPackedUInt(model);
            ((uint*)&value)[1] = ReadPackedUInt(model);
            return value;
        }
        /// 
        /// Reads a 4-byte signed integer value from the data stream using a .
        /// 
        /// Negative values de-interleaves from positive values before returning, for example (0, -1, 1, -2, 2) -> (-2, -1, 0, 1, 2)
        /// 
        ///  model for reading value in a packed manner.
        /// A 4-byte signed integer read from the current stream, or 0 if the end of the stream has been reached.
        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, ...]
        }
        /// 
        /// Reads an 8-byte signed long value from the data stream using a .
        /// 
        /// Negative values de-interleaves from positive values before returning, for example (0, -1, 1, -2, 2) -> (-2, -1, 0, 1, 2)
        /// 
        ///  model for reading value in a packed manner.
        /// An 8-byte signed long read from the current stream, or 0 if the end of the stream has been reached.
        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, ...]
        }
        /// 
        /// Reads a 4-byte floating point value from the data stream using a .
        /// 
        ///  model for reading value in a packed manner.
        /// A 4-byte floating point value read from the current stream, or 0 if the end of the stream has been reached.
        public float ReadPackedFloat(in StreamCompressionModel model)
        {
            return ReadPackedFloatDelta(0, model);
        }
        /// 
        /// Reads a 8-byte floating point value from the data stream using a .
        /// 
        ///  model for reading value in a packed manner.
        /// A 8-byte floating point value read from the current stream, or 0 if the end of the stream has been reached.
        public double ReadPackedDouble(in StreamCompressionModel model)
        {
            return ReadPackedDoubleDelta(0, model);
        }
        /// 
        /// Reads a 4-byte signed integer delta value from the data stream using a .
        /// 
        /// The previous 4-byte signed integer value, used to compute the diff.
        ///  model for reading value in a packed manner.
        /// 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.
        /// 
        /// See:  to verify if the read failed.
        public int ReadPackedIntDelta(int baseline, in StreamCompressionModel model)
        {
            int delta = ReadPackedInt(model);
            return baseline - delta;
        }
        /// 
        /// Reads a 4-byte unsigned integer delta value from the data stream using a .
        /// 
        /// The previous 4-byte unsigned integer value, used to compute the diff.
        ///  model for reading value in a packed manner.
        /// 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.
        /// 
        /// See:  to verify if the read failed.
        public uint ReadPackedUIntDelta(uint baseline, in StreamCompressionModel model)
        {
            uint delta = (uint)ReadPackedInt(model);
            return baseline - delta;
        }
        /// 
        /// Reads an 8-byte signed long delta value from the data stream using a .
        /// 
        /// The previous 8-byte signed long value, used to compute the diff.
        ///  model for reading value in a packed manner.
        /// 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.
        /// 
        /// See:  to verify if the read failed.
        public long ReadPackedLongDelta(long baseline, in StreamCompressionModel model)
        {
            long delta = ReadPackedLong(model);
            return baseline - delta;
        }
        /// 
        /// Reads an 8-byte unsigned long delta value from the data stream using a .
        /// 
        /// The previous 8-byte unsigned long value, used to compute the diff.
        ///  model for reading value in a packed manner.
        /// 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.
        /// 
        /// See:  to verify if the read failed.
        public ulong ReadPackedULongDelta(ulong baseline, in StreamCompressionModel model)
        {
            ulong delta = (ulong)ReadPackedLong(model);
            return baseline - delta;
        }
        /// 
        /// Reads a 4-byte floating point value from the data stream.
        ///
        /// If the first bit is 0, the data did not change and  will be returned.
        /// 
        /// The previous 4-byte floating point value.
        /// Not currently used.
        /// A 4-byte floating point value read from the current stream, or  if there are no changes to the value.
        /// 
        /// See:  to verify if the read failed.
        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;
        }
        /// 
        /// Reads a 8-byte floating point value from the data stream.
        ///
        /// If the first bit is 0, the data did not change and  will be returned.
        /// 
        /// The previous 8-byte floating point value.
        /// Not currently used.
        /// A 8-byte floating point value read from the current stream, or  if there are no changes to the value.
        /// 
        /// See:  to verify if the read failed.
        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;
        }
        /// 
        /// Reads a FixedString32Bytes value from the current stream and advances the current position of the stream by the length of the string.
        /// 
        /// A FixedString32Bytes value read from the current stream, or 0 if the end of the stream has been reached.
        public unsafe FixedString32Bytes ReadFixedString32()
        {
            FixedString32Bytes str;
            byte* data = ((byte*)&str) + 2;
            *(ushort*)&str = ReadFixedStringInternal(data, str.Capacity);
            return str;
        }
        /// 
        /// Reads a FixedString64Bytes value from the current stream and advances the current position of the stream by the length of the string.
        /// 
        /// A FixedString64Bytes value read from the current stream, or 0 if the end of the stream has been reached.
        public unsafe FixedString64Bytes ReadFixedString64()
        {
            FixedString64Bytes str;
            byte* data = ((byte*)&str) + 2;
            *(ushort*)&str = ReadFixedStringInternal(data, str.Capacity);
            return str;
        }
        /// 
        /// Reads a FixedString128Bytes value from the current stream and advances the current position of the stream by the length of the string.
        /// 
        /// A FixedString128Bytes value read from the current stream, or 0 if the end of the stream has been reached.
        public unsafe FixedString128Bytes ReadFixedString128()
        {
            FixedString128Bytes str;
            byte* data = ((byte*)&str) + 2;
            *(ushort*)&str = ReadFixedStringInternal(data, str.Capacity);
            return str;
        }
        /// 
        /// Reads a FixedString512Bytes value from the current stream and advances the current position of the stream by the length of the string.
        /// 
        /// A FixedString512Bytes value read from the current stream, or 0 if the end of the stream has been reached.
        public unsafe FixedString512Bytes ReadFixedString512()
        {
            FixedString512Bytes str;
            byte* data = ((byte*)&str) + 2;
            *(ushort*)&str = ReadFixedStringInternal(data, str.Capacity);
            return str;
        }
        /// 
        /// Reads a FixedString4096Bytes value from the current stream and advances the current position of the stream by the length of the string.
        /// 
        /// A FixedString4096Bytes value read from the current stream, or 0 if the end of the stream has been reached.
        public unsafe FixedString4096Bytes ReadFixedString4096()
        {
            FixedString4096Bytes str;
            byte* data = ((byte*)&str) + 2;
            *(ushort*)&str = ReadFixedStringInternal(data, str.Capacity);
            return str;
        }
        /// 
        /// Read and copy data into the given NativeArray of bytes, an error will
        /// be logged if not enough bytes are available in the array.
        /// 
        /// Buffer to write the string bytes to.
        /// Length of data read into byte array, or zero if error occurred.
        public ushort ReadFixedString(NativeArray 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;
        }
        /// 
        /// Reads a FixedString32Bytes delta value to the data stream using a .
        /// 
        /// The previous FixedString32Bytes value, used to compute the diff.
        ///  model for writing value in a packed manner.
        /// A FixedString32Bytes value read from the current stream, or 0 if the end of the stream has been reached.
        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;
        }
        /// 
        /// Reads a FixedString64Bytes delta value to the data stream using a .
        /// 
        /// The previous FixedString64Bytes value, used to compute the diff.
        ///  model for writing value in a packed manner.
        /// A FixedString64Bytes value read from the current stream, or 0 if the end of the stream has been reached.
        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;
        }
        /// 
        /// Reads a FixedString128Bytes delta value to the data stream using a .
        /// 
        /// The previous FixedString128Bytes value, used to compute the diff.
        ///  model for writing value in a packed manner.
        /// A FixedString128Bytes value read from the current stream, or 0 if the end of the stream has been reached.
        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;
        }
        /// 
        /// Reads a FixedString512Bytes delta value to the data stream using a .
        /// 
        /// The previous FixedString512Bytes value, used to compute the diff.
        ///  model for writing value in a packed manner.
        /// A FixedString512Bytes value read from the current stream, or 0 if the end of the stream has been reached.
        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;
        }
        /// 
        /// Reads a FixedString4096Bytes delta value to the data stream using a .
        /// 
        /// The previous FixedString4096Bytes value, used to compute the diff.
        ///  model for writing value in a packed manner.
        /// A FixedString4096Bytes value read from the current stream, or 0 if the end of the stream has been reached.
        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;
        }
        /// 
        /// Read and copy data into the given NativeArray of bytes, an error will
        /// be logged if not enough bytes are available in the array.
        /// 
        /// Array for the current fixed string.
        /// Array containing the previous value, used to compute the diff.
        ///  model for writing value in a packed manner.
        /// Length of data read into byte array, or zero if error occurred.
        public ushort ReadPackedFixedStringDelta(NativeArray data, NativeArray 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.");
        }
    }
}