123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919 |
- 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");
- if (value >= (1UL << numbits))
- throw new ArgumentOutOfRangeException("Value does not fit in the specified number of bits");
- }
- }
- }
|