#define ARRAY_WRITE_PERMISSIVE // Allow attempt to write "packed" byte array (calls WriteByteArray()) #define ARRAY_RESOLVE_IMPLICIT // Include WriteArray() method with automatic type resolution #define ARRAY_WRITE_PREMAP // Create a prefixed array diff mapping #define ARRAY_DIFF_ALLOW_RESIZE // Whether or not to permit writing diffs of differently sized arrays using System; using System.Diagnostics; using System.IO; using MLAPI.Reflection; using UnityEngine; namespace MLAPI.Serialization { // Improved version of NetworkWriter /// /// A BinaryWriter that can do bit wise manipulation when backed by a NetworkBuffer /// public class NetworkWriter { private Stream m_Sink; private NetworkBuffer m_NetworkSink; /// /// Creates a new NetworkWriter backed by a given stream /// /// The stream to use for writing public NetworkWriter(Stream stream) { m_Sink = stream; m_NetworkSink = stream as NetworkBuffer; } /// /// Changes the underlying stream the writer is writing to /// /// The stream to write to public void SetStream(Stream stream) { m_Sink = stream; m_NetworkSink = stream as NetworkBuffer; } internal Stream GetStream() { return m_Sink; } /// /// Writes a boxed object in a packed format /// /// The object to write public void WriteObjectPacked(object value) { // Check unitys custom null checks bool isNull = value == null || (value is UnityEngine.Object && ((UnityEngine.Object)value) == null); if (isNull || value.GetType().IsNullable()) { WriteBool(isNull); if (isNull) { return; } } if (SerializationManager.TrySerialize(m_Sink, value)) { return; } if (value is Array array) { var elementType = value.GetType().GetElementType(); if (SerializationManager.IsTypeSupported(elementType)) { WriteInt32Packed(array.Length); for (int i = 0; i < array.Length; i++) { WriteObjectPacked(array.GetValue(i)); } return; } } else if (value is byte) { WriteByte((byte)value); return; } else if (value is sbyte) { WriteSByte((sbyte)value); return; } else if (value is ushort) { WriteUInt16Packed((ushort)value); return; } else if (value is short) { WriteInt16Packed((short)value); return; } else if (value is int) { WriteInt32Packed((int)value); return; } else if (value is uint) { WriteUInt32Packed((uint)value); return; } else if (value is long) { WriteInt64Packed((long)value); return; } else if (value is ulong) { WriteUInt64Packed((ulong)value); return; } else if (value is float) { WriteSinglePacked((float)value); return; } else if (value is double) { WriteDoublePacked((double)value); return; } else if (value is string) { WriteStringPacked((string)value); return; } else if (value is bool) { WriteBool((bool)value); return; } else if (value is Vector2) { WriteVector2Packed((Vector2)value); return; } else if (value is Vector3) { WriteVector3Packed((Vector3)value); return; } else if (value is Vector4) { WriteVector4Packed((Vector4)value); return; } else if (value is Color) { WriteColorPacked((Color)value); return; } else if (value is Color32) { WriteColor32((Color32)value); return; } else if (value is Ray) { WriteRayPacked((Ray)value); return; } else if (value is Quaternion) { WriteRotationPacked((Quaternion)value); return; } else if (value is char) { WriteCharPacked((char)value); return; } else if (value.GetType().IsEnum) { WriteInt32Packed((int)value); return; } else if (value is GameObject) { var networkObject = ((GameObject)value).GetComponent(); if (networkObject == null) { throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(GameObject)} types that does not has a {nameof(NetworkObject)} component attached. {nameof(GameObject)}: {((GameObject)value).name}"); } if (!networkObject.IsSpawned) { throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {((GameObject)value).name}"); } WriteUInt64Packed(networkObject.NetworkObjectId); return; } else if (value is NetworkObject) { if (!((NetworkObject)value).IsSpawned) { throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {((GameObject)value).name}"); } WriteUInt64Packed(((NetworkObject)value).NetworkObjectId); return; } else if (value is NetworkBehaviour) { if (!((NetworkBehaviour)value).HasNetworkObject || !((NetworkBehaviour)value).NetworkObject.IsSpawned) { throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(NetworkBehaviour)} types that are not spawned. {nameof(GameObject)}: {((GameObject)value).name}"); } WriteUInt64Packed(((NetworkBehaviour)value).NetworkObjectId); WriteUInt16Packed(((NetworkBehaviour)value).NetworkBehaviourId); return; } else if (value is INetworkSerializable) { ((INetworkSerializable)value).NetworkSerialize(new NetworkSerializer(this)); return; } throw new ArgumentException($"{nameof(NetworkWriter)} cannot write type {value.GetType().Namespace}"); } /// /// Write single-precision floating point value to the stream /// /// Value to write public void WriteSingle(float value) { WriteUInt32(new UIntFloat { FloatValue = value }.UIntValue); } /// /// Write double-precision floating point value to the stream /// /// Value to write public void WriteDouble(double value) { WriteUInt64(new UIntFloat { DoubleValue = value }.ULongValue); } /// /// Write single-precision floating point value to the stream as a varint /// /// Value to write public void WriteSinglePacked(float value) { WriteUInt32Packed(new UIntFloat { FloatValue = value }.UIntValue); } /// /// Write double-precision floating point value to the stream as a varint /// /// Value to write public void WriteDoublePacked(double value) { WriteUInt64Packed(new UIntFloat { DoubleValue = value }.ULongValue); } /// /// Convenience method that writes two non-packed Vector3 from the ray to the stream /// /// Ray to write public void WriteRay(Ray ray) { WriteVector3(ray.origin); WriteVector3(ray.direction); } /// /// Convenience method that writes two packed Vector3 from the ray to the stream /// /// Ray to write public void WriteRayPacked(Ray ray) { WriteVector3Packed(ray.origin); WriteVector3Packed(ray.direction); } /// /// Convenience method that writes two non-packed Vector2 from the ray to the stream /// /// Ray2D to write public void WriteRay2D(Ray2D ray2d) { WriteVector2(ray2d.origin); WriteVector2(ray2d.direction); } /// /// Convenience method that writes two packed Vector2 from the ray to the stream /// /// Ray2D to write public void WriteRay2DPacked(Ray2D ray2d) { WriteVector2Packed(ray2d.origin); WriteVector2Packed(ray2d.direction); } /// /// Convenience method that writes four non-varint floats from the color to the stream /// /// Color to write public void WriteColor(Color color) { WriteSingle(color.r); WriteSingle(color.g); WriteSingle(color.b); WriteSingle(color.a); } /// /// Convenience method that writes four varint floats from the color to the stream /// /// Color to write public void WriteColorPacked(Color color) { WriteSinglePacked(color.r); WriteSinglePacked(color.g); WriteSinglePacked(color.b); WriteSinglePacked(color.a); } /// /// Convenience method that writes four non-varint floats from the color to the stream /// /// Color32 to write public void WriteColor32(Color32 color32) { WriteByte(color32.r); WriteByte(color32.g); WriteByte(color32.b); WriteByte(color32.a); } /// /// Convenience method that writes two non-varint floats from the vector to the stream /// /// Vector to write public void WriteVector2(Vector2 vector2) { WriteSingle(vector2.x); WriteSingle(vector2.y); } /// /// Convenience method that writes two varint floats from the vector to the stream /// /// Vector to write public void WriteVector2Packed(Vector2 vector2) { WriteSinglePacked(vector2.x); WriteSinglePacked(vector2.y); } /// /// Convenience method that writes three non-varint floats from the vector to the stream /// /// Vector to write public void WriteVector3(Vector3 vector3) { WriteSingle(vector3.x); WriteSingle(vector3.y); WriteSingle(vector3.z); } /// /// Convenience method that writes three varint floats from the vector to the stream /// /// Vector to write public void WriteVector3Packed(Vector3 vector3) { WriteSinglePacked(vector3.x); WriteSinglePacked(vector3.y); WriteSinglePacked(vector3.z); } /// /// Convenience method that writes four non-varint floats from the vector to the stream /// /// Vector to write public void WriteVector4(Vector4 vector4) { WriteSingle(vector4.x); WriteSingle(vector4.y); WriteSingle(vector4.z); WriteSingle(vector4.w); } /// /// Convenience method that writes four varint floats from the vector to the stream /// /// Vector to write public void WriteVector4Packed(Vector4 vector4) { WriteSinglePacked(vector4.x); WriteSinglePacked(vector4.y); WriteSinglePacked(vector4.z); WriteSinglePacked(vector4.w); } /// /// Write a single-precision floating point value to the stream. The value is between (inclusive) the minValue and maxValue. /// /// Value to write /// Minimum value that this value could be /// Maximum possible value that this could be /// How many bytes the compressed result should occupy. Must be between 1 and 4 (inclusive) public void WriteRangedSingle(float value, float minValue, float maxValue, int bytes) { if (bytes < 1 || bytes > 4) throw new ArgumentOutOfRangeException("Result must occupy between 1 and 4 bytes!"); if (value < minValue || value > maxValue) throw new ArgumentOutOfRangeException("Given value does not match the given constraints!"); uint result = (uint)(((value + minValue) / (maxValue + minValue)) * ((0x100 * bytes) - 1)); for (int i = 0; i < bytes; ++i) m_Sink.WriteByte((byte)(result >> (i << 3))); } /// /// Write a double-precision floating point value to the stream. The value is between (inclusive) the minValue and maxValue. /// /// Value to write /// Minimum value that this value could be /// Maximum possible value that this could be /// How many bytes the compressed result should occupy. Must be between 1 and 8 (inclusive) public void WriteRangedDouble(double value, double minValue, double maxValue, int bytes) { if (bytes < 1 || bytes > 8) throw new ArgumentOutOfRangeException("Result must occupy between 1 and 8 bytes!"); if (value < minValue || value > maxValue) throw new ArgumentOutOfRangeException("Given value does not match the given constraints!"); ulong result = (ulong)(((value + minValue) / (maxValue + minValue)) * ((0x100 * bytes) - 1)); for (int i = 0; i < bytes; ++i) WriteByte((byte)(result >> (i << 3))); } /// /// Writes the rotation to the stream. /// /// Rotation to write public void WriteRotationPacked(Quaternion rotation) { if (Mathf.Sign(rotation.w) < 0) { WriteSinglePacked(-rotation.x); WriteSinglePacked(-rotation.y); WriteSinglePacked(-rotation.z); } else { WriteSinglePacked(rotation.x); WriteSinglePacked(rotation.y); WriteSinglePacked(rotation.z); } } /// /// Writes the rotation to the stream. /// /// Rotation to write public void WriteRotation(Quaternion rotation) { WriteSingle(rotation.x); WriteSingle(rotation.y); WriteSingle(rotation.z); WriteSingle(rotation.w); } /// /// Writes a single bit /// /// public void WriteBit(bool bit) { if (m_NetworkSink == null) throw new InvalidOperationException($"Cannot write bits on a non-{nameof(NetworkBuffer)} stream"); m_NetworkSink.WriteBit(bit); } /// /// Writes a bool as a single bit /// /// public void WriteBool(bool value) { if (m_NetworkSink == null) { m_Sink.WriteByte(value ? (byte)1 : (byte)0); } else { // WriteBit(value); // old (buggy) WriteByte(value ? (byte)1 : (byte)0); // new (hotfix) } } /// /// Writes pad bits to make the underlying stream aligned /// public void WritePadBits() { while (!m_NetworkSink.BitAligned) WriteBit(false); } /// /// Write the lower half (lower nibble) of a byte. /// /// Value containing nibble to write. public void WriteNibble(byte value) => WriteBits(value, 4); /// /// Write either the upper or lower nibble of a byte to the stream. /// /// Value holding the nibble /// Whether or not the upper nibble should be written. True to write the four high bits, else writes the four low bits. public void WriteNibble(byte value, bool upper) => WriteNibble((byte)(value >> (upper ? 4 : 0))); /// /// Write s certain amount of bits to the stream. /// /// Value to get bits from. /// Amount of bits to write public void WriteBits(ulong value, int bitCount) { if (m_NetworkSink == null) throw new InvalidOperationException($"Cannot write bits on a non-{nameof(NetworkBuffer)} stream"); if (bitCount > 64) throw new ArgumentOutOfRangeException(nameof(bitCount), "Cannot read more than 64 bits from a 64-bit value!"); if (bitCount < 0) throw new ArgumentOutOfRangeException(nameof(bitCount), "Cannot read fewer than 0 bits!"); int count = 0; for (; count + 8 < bitCount; count += 8) m_NetworkSink.WriteByte((byte)(value >> count)); for (; count < bitCount; ++count) m_NetworkSink.WriteBit((value & (1UL << count)) != 0); } /// /// Write bits to stream. /// /// Value to get bits from. /// Amount of bits to write. public void WriteBits(byte value, int bitCount) { if (m_NetworkSink == null) throw new InvalidOperationException($"Cannot write bits on a non-{nameof(NetworkBuffer)} stream"); for (int i = 0; i < bitCount; ++i) m_NetworkSink.WriteBit(((value >> i) & 1) != 0); } /// /// Write a signed byte to the stream. /// /// Value to write public void WriteSByte(sbyte value) => WriteByte((byte)value); /// /// Write a single character to the stream. /// /// Character to write public void WriteChar(char c) => WriteUInt16(c); /// /// Write an unsigned short (UInt16) to the stream. /// /// Value to write public void WriteUInt16(ushort value) { m_Sink.WriteByte((byte)value); m_Sink.WriteByte((byte)(value >> 8)); } /// /// Write a signed short (Int16) to the stream. /// /// Value to write public void WriteInt16(short value) => WriteUInt16((ushort)value); /// /// Write an unsigned int (UInt32) to the stream. /// /// Value to write public void WriteUInt32(uint value) { m_Sink.WriteByte((byte)value); m_Sink.WriteByte((byte)(value >> 8)); m_Sink.WriteByte((byte)(value >> 16)); m_Sink.WriteByte((byte)(value >> 24)); } /// /// Write a signed int (Int32) to the stream. /// /// Value to write public void WriteInt32(int value) => WriteUInt32((uint)value); /// /// Write an unsigned long (UInt64) to the stream. /// /// Value to write public void WriteUInt64(ulong value) { m_Sink.WriteByte((byte)value); m_Sink.WriteByte((byte)(value >> 8)); m_Sink.WriteByte((byte)(value >> 16)); m_Sink.WriteByte((byte)(value >> 24)); m_Sink.WriteByte((byte)(value >> 32)); m_Sink.WriteByte((byte)(value >> 40)); m_Sink.WriteByte((byte)(value >> 48)); m_Sink.WriteByte((byte)(value >> 56)); } /// /// Write a signed long (Int64) to the stream. /// /// Value to write public void WriteInt64(long value) => WriteUInt64((ulong)value); /// /// Write a signed short (Int16) as a ZigZag encoded varint to the stream. /// /// Value to write public void WriteInt16Packed(short value) => WriteInt64Packed(value); /// /// Write an unsigned short (UInt16) as a varint to the stream. /// /// Value to write public void WriteUInt16Packed(ushort value) => WriteUInt64Packed(value); /// /// Write a two-byte character as a varint to the stream. /// /// Value to write public void WriteCharPacked(char c) => WriteUInt16Packed(c); /// /// Write a signed int (Int32) as a ZigZag encoded varint to the stream. /// /// Value to write public void WriteInt32Packed(int value) => WriteInt64Packed(value); /// /// Write an unsigned int (UInt32) as a varint to the stream. /// /// Value to write public void WriteUInt32Packed(uint value) => WriteUInt64Packed(value); /// /// Write a signed long (Int64) as a ZigZag encoded varint to the stream. /// /// Value to write public void WriteInt64Packed(long value) => WriteUInt64Packed(Arithmetic.ZigZagEncode(value)); /// /// Write an unsigned long (UInt64) as a varint to the stream. /// /// Value to write public void WriteUInt64Packed(ulong value) { if (value <= 240) WriteULongByte(value); else if (value <= 2287) { WriteULongByte(((value - 240) >> 8) + 241); WriteULongByte(value - 240); } else if (value <= 67823) { WriteULongByte(249); WriteULongByte((value - 2288) >> 8); WriteULongByte(value - 2288); } else { ulong header = 255; ulong match = 0x00FF_FFFF_FFFF_FFFFUL; while (value <= match) { --header; match >>= 8; } WriteULongByte(header); int max = (int)(header - 247); for (int i = 0; i < max; ++i) WriteULongByte(value >> (i << 3)); } } /// /// Write a byte (in an int format) to the stream. /// /// Value to write private void WriteIntByte(int value) => WriteByte((byte)value); /// /// Write a byte (in a ulong format) to the stream. /// /// Value to write private void WriteULongByte(ulong byteValue) => WriteByte((byte)byteValue); /// /// Write a byte to the stream. /// /// Value to write public void WriteByte(byte value) { m_Sink.WriteByte(value); } // As it turns out, strings cannot be treated as char arrays, since strings use pointers to store data rather than C# arrays /// /// Writes a string /// /// The string to write /// Whether or not to use one byte per character. This will only allow ASCII public void WriteString(string s, bool oneByteChars = false) { WriteUInt32Packed((uint)s.Length); int target = s.Length; for (int i = 0; i < target; ++i) { if (oneByteChars) WriteByte((byte)s[i]); else WriteChar(s[i]); } } /// /// Writes a string in a packed format /// /// public void WriteStringPacked(string s) { WriteUInt32Packed((uint)s.Length); int target = s.Length; for (int i = 0; i < target; ++i) WriteCharPacked(s[i]); } /// /// Writes the diff between two strings /// /// The new array /// The previous array to use for diff /// Whether or not to use single byte chars. This will only allow ASCII characters public void WriteStringDiff(string write, string compare, bool oneByteChars = false) { #if !ARRAY_DIFF_ALLOW_RESIZE if (write.Length != compare.Length) throw new ArgumentException("Mismatched string lengths"); #endif WriteUInt32Packed((uint)write.Length); // Premapping int target; #if ARRAY_WRITE_PREMAP #if ARRAY_DIFF_ALLOW_RESIZE target = Math.Min(write.Length, compare.Length); #else target = a1.Length; #endif for (int i = 0; i < target; ++i) WriteBit(write[i] != compare[i]); #else target = write.Length; #endif for (int i = 0; i < target; ++i) { bool b = write[i] != compare[i]; #if !ARRAY_WRITE_PREMAP WriteBit(!b); #endif if (b) { if (oneByteChars) WriteByte((byte)write[i]); else WriteChar(write[i]); } } } /// /// Writes the diff between two strings in a packed format /// /// The new string /// The previous string to use for diff public void WriteStringPackedDiff(string write, string compare) { #if !ARRAY_DIFF_ALLOW_RESIZE if (write.Length != compare.Length) throw new ArgumentException("Mismatched string lengths"); #endif WriteUInt32Packed((uint)write.Length); // Premapping int target; #if ARRAY_WRITE_PREMAP #if ARRAY_DIFF_ALLOW_RESIZE target = Math.Min(write.Length, compare.Length); #else target = a1.Length; #endif for (int i = 0; i < target; ++i) WriteBit(write[i] != compare[i]); #else target = write.Length; #endif for (int i = 0; i < target; ++i) { bool b = write[i] != compare[i]; #if !ARRAY_WRITE_PREMAP WriteBit(!b); #endif if (b) WriteCharPacked(write[i]); } } private void CheckLengths(Array a1, Array a2) { } [Conditional("ARRAY_WRITE_PREMAP")] private void WritePremap(Array a1, Array a2) { long target; target = Math.Min(a1.LongLength, a2.LongLength); for (long i = 0; i < target; ++i) WriteBit(!a1.GetValue(i).Equals(a2.GetValue(i))); // TODO: Byte-align here } private ulong WriteArraySize(Array a1, Array a2, long length) { ulong write = (ulong)(length >= 0 ? length : a1.LongLength); if (length < 0) { if (length > a1.LongLength) throw new IndexOutOfRangeException("Cannot write more data than is available"); WriteUInt64Packed(write); } return write; } /// /// Writes a byte array /// /// The array to write /// The amount of elements to write public void WriteByteArray(byte[] b, long count = -1) { ulong target = WriteArraySize(b, null, count); for (ulong i = 0; i < target; ++i) m_Sink.WriteByte(b[i]); } /// /// WriteBytes /// Takes a byte array buffer and writes the bytes into the currently assigned stream at its current position /// This reduces the iterations required to write (n) bytes by a factor of up to 8x less iterations. /// for blocks of memory that exceed 8 bytes in size. It also doesn't require passing arrays over the stack. /// Ex: /// 256 bytes iterates 32 times vs 256 times ------------------------- 8x less iterations /// 64 bytes iterates 8 times vs 64 times----------------------------- 8x less iterations /// 22 bytes iterates 5 times ( 2-Int64 1-Int32 2-Byte) vs 22 times -- 4x less iterations /// /// /// public void WriteBytes(byte[] buffer, long targetSize, int offset = 0) { long TargetSize = targetSize; long LargeInt64Blocks = TargetSize >> 3; //Divide by 8 int IndexOffset = offset; //8 Byte blocks for (long i = 0; i < LargeInt64Blocks; i++) { WriteInt64(BitConverter.ToInt64(buffer, IndexOffset)); IndexOffset += 8; } long Offset = LargeInt64Blocks * 8; long Remainder = TargetSize - Offset; //4 byte block if (Remainder >= 4) { WriteInt32(BitConverter.ToInt32(buffer, IndexOffset)); IndexOffset += 4; Offset += 4; } //Remainder of bytes < 4 if (TargetSize - Offset > 0) { for (long i = 0; i < (TargetSize - Offset); i++) { WriteByte(buffer[IndexOffset + i]); } } } /// /// ReadAndWrite /// Uses a NetworkReader to read (targetSize) bytes and will write (targetSize) bytes to current stream. /// This reduces the iterations required to write (n) bytes by a factor of up to 8x less iterations. /// for blocks of memory that exceed 8 bytes in size. It also doesn't require passing arrays over the stack. /// Ex: /// 256 bytes iterates 32 times vs 256 times ------------------------- 8x less iterations /// 64 bytes iterates 8 times vs 64 times----------------------------- 8x less iterations /// 22 bytes iterates 5 times ( 2-Int64 1-Int32 2-Byte) vs 22 times -- 4x less iterations /// /// /// public void ReadAndWrite(NetworkReader sourceReader, long targetSize) { long TargetSize = targetSize; long LargeInt64Blocks = TargetSize >> 3; //Divide by 8 if (targetSize > 131072 || targetSize < 0) { return; } //8 Byte blocks for (long i = 0; i < LargeInt64Blocks; i++) { WriteInt64(sourceReader.ReadInt64()); } long Offset = LargeInt64Blocks * 8; long Remainder = TargetSize - Offset; //4 byte block if (Remainder >= 4) { WriteInt32(sourceReader.ReadInt32()); Offset += 4; } //Remainder of bytes < 4 if (TargetSize - Offset > 0) { for (long i = 0; i < (TargetSize - Offset); i++) { WriteByte(sourceReader.ReadByteDirect()); } } } /// /// Writes the diff between two byte arrays /// /// The new array /// The previous array to use for diff /// The amount of elements to write public void WriteByteArrayDiff(byte[] write, byte[] compare, long count = -1) { CheckLengths(write, compare); long target = (long)WriteArraySize(write, compare, count); WritePremap(write, compare); for (long i = 0; i < target; ++i) { bool b = i >= compare.LongLength || write[i] != compare[i]; #if !ARRAY_WRITE_PREMAP WriteBit(b); #endif if (b) WriteByte(write[i]); } } /// /// Writes a short array /// /// The array to write /// The amount of elements to write public void WriteShortArray(short[] b, long count = -1) { ulong target = WriteArraySize(b, null, count); for (ulong i = 0; i < target; ++i) WriteInt16(b[i]); } /// /// Writes the diff between two short arrays /// /// The new array /// The previous array to use for diff /// The amount of elements to write public void WriteShortArrayDiff(short[] write, short[] compare, long count = -1) { CheckLengths(write, compare); long target = (long)WriteArraySize(write, compare, count); WritePremap(write, compare); for (long i = 0; i < target; ++i) { bool b = i >= compare.LongLength || write[i] != compare[i]; #if !ARRAY_WRITE_PREMAP WriteBit(!b); #endif if (b) WriteInt16(write[i]); } } /// /// Writes a ushort array /// /// The array to write /// The amount of elements to write public void WriteUShortArray(ushort[] b, long count = -1) { ulong target = WriteArraySize(b, null, count); for (ulong i = 0; i < target; ++i) WriteUInt16(b[i]); } /// /// Writes the diff between two ushort arrays /// /// The new array /// The previous array to use for diff /// The amount of elements to write public void WriteUShortArrayDiff(ushort[] write, ushort[] compare, long count = -1) { CheckLengths(write, compare); long target = (long)WriteArraySize(write, compare, count); WritePremap(write, compare); for (long i = 0; i < target; ++i) { bool b = i >= compare.LongLength || write[i] != compare[i]; #if !ARRAY_WRITE_PREMAP WriteBit(!b); #endif if (b) WriteUInt16(write[i]); } } /// /// Writes a char array /// /// The array to write /// The amount of elements to write public void WriteCharArray(char[] b, long count = -1) { ulong target = WriteArraySize(b, null, count); for (ulong i = 0; i < target; ++i) WriteChar(b[i]); } /// /// Writes the diff between two char arrays /// /// The new array /// The previous array to use for diff /// The amount of elements to write public void WriteCharArrayDiff(char[] write, char[] compare, long count = -1) { CheckLengths(write, compare); long target = (long)WriteArraySize(write, compare, count); WritePremap(write, compare); for (long i = 0; i < target; ++i) { bool b = i >= compare.LongLength || write[i] != compare[i]; #if !ARRAY_WRITE_PREMAP WriteBit(!b); #endif if (b) WriteChar(write[i]); } } /// /// Writes a int array /// /// The array to write /// The amount of elements to write public void WriteIntArray(int[] b, long count = -1) { ulong target = WriteArraySize(b, null, count); for (ulong i = 0; i < target; ++i) WriteInt32(b[i]); } /// /// Writes the diff between two int arrays /// /// The new array /// The previous array to use for diff /// The amount of elements to write public void WriteIntArrayDiff(int[] write, int[] compare, long count = -1) { CheckLengths(write, compare); long target = (long)WriteArraySize(write, compare, count); WritePremap(write, compare); for (long i = 0; i < target; ++i) { bool b = i >= compare.LongLength || write[i] != compare[i]; #if !ARRAY_WRITE_PREMAP WriteBit(!b); #endif if (b) WriteInt32(write[i]); } } /// /// Writes a uint array /// /// The array to write /// The amount of elements to write public void WriteUIntArray(uint[] b, long count = -1) { ulong target = WriteArraySize(b, null, count); for (ulong i = 0; i < target; ++i) WriteUInt32(b[i]); } /// /// Writes the diff between two uint arrays /// /// The new array /// The previous array to use for diff /// The amount of elements to write public void WriteUIntArrayDiff(uint[] write, uint[] compare, long count = -1) { CheckLengths(write, compare); long target = (long)WriteArraySize(write, compare, count); WritePremap(write, compare); for (long i = 0; i < target; ++i) { bool b = i >= compare.LongLength || write[i] != compare[i]; #if !ARRAY_WRITE_PREMAP WriteBit(!b); #endif if (b) WriteUInt32(write[i]); } } /// /// Writes a long array /// /// The array to write /// The amount of elements to write public void WriteLongArray(long[] b, long count = -1) { ulong target = WriteArraySize(b, null, count); for (ulong i = 0; i < target; ++i) WriteInt64(b[i]); } /// /// Writes the diff between two long arrays /// /// The new array /// The previous array to use for diff /// The amount of elements to write public void WriteLongArrayDiff(long[] write, long[] compare, long count = -1) { CheckLengths(write, compare); long target = (long)WriteArraySize(write, compare, count); WritePremap(write, compare); for (long i = 0; i < target; ++i) { bool b = i >= compare.LongLength || write[i] != compare[i]; #if !ARRAY_WRITE_PREMAP WriteBit(!b); #endif if (b) WriteInt64(write[i]); } } /// /// Writes a ulong array /// /// The array to write /// The amount of elements to write public void WriteULongArray(ulong[] b, long count = -1) { ulong target = WriteArraySize(b, null, count); for (ulong i = 0; i < target; ++i) WriteUInt64(b[i]); } /// /// Writes the diff between two ulong arrays /// /// The new array /// The previous array to use for diff /// The amount of elements to write public void WriteULongArrayDiff(ulong[] write, ulong[] compare, long count = -1) { CheckLengths(write, compare); long target = (long)WriteArraySize(write, compare, count); WritePremap(write, compare); for (long i = 0; i < target; ++i) { bool b = i >= compare.LongLength || write[i] != compare[i]; #if !ARRAY_WRITE_PREMAP WriteBit(!b); #endif if (b) WriteUInt64(write[i]); } } /// /// Writes a float array /// /// The array to write /// The amount of elements to write public void WriteFloatArray(float[] b, long count = -1) { ulong target = WriteArraySize(b, null, count); for (ulong i = 0; i < target; ++i) WriteSingle(b[i]); } /// /// Writes the diff between two float arrays /// /// The new array /// The previous array to use for diff /// The amount of elements to write public void WriteFloatArrayDiff(float[] write, float[] compare, long count = -1) { CheckLengths(write, compare); long target = (long)WriteArraySize(write, compare, count); WritePremap(write, compare); for (long i = 0; i < target; ++i) { bool b = i >= compare.LongLength || write[i] != compare[i]; #if !ARRAY_WRITE_PREMAP WriteBit(!b); #endif if (b) WriteSingle(write[i]); } } /// /// Writes a double array /// /// The array to write /// The amount of elements to write public void WriteDoubleArray(double[] b, long count = -1) { ulong target = WriteArraySize(b, null, count); for (ulong i = 0; i < target; ++i) WriteDouble(b[i]); } /// /// Writes the diff between two double arrays /// /// The new array /// The previous array to use for diff /// The amount of elements to write public void WriteDoubleArrayDiff(double[] write, double[] compare, long count = -1) { CheckLengths(write, compare); long target = (long)WriteArraySize(write, compare, count); WritePremap(write, compare); for (long i = 0; i < target; ++i) { bool b = i >= compare.LongLength || write[i] != compare[i]; #if !ARRAY_WRITE_PREMAP WriteBit(!b); #endif if (b) WriteDouble(write[i]); } } // Packed arrays #if ARRAY_RESOLVE_IMPLICIT /// /// Writes an array in a packed format /// /// The array to write /// The amount of elements to write public void WriteArrayPacked(Array a, long count = -1) { var arrayType = a.GetType(); #if ARRAY_WRITE_PERMISSIVE if (arrayType == typeof(byte[])) WriteByteArray(a as byte[], count); else #endif if (arrayType == typeof(short[])) WriteShortArrayPacked(a as short[], count); else if (arrayType == typeof(ushort[])) WriteUShortArrayPacked(a as ushort[], count); else if (arrayType == typeof(char[])) WriteCharArrayPacked(a as char[], count); else if (arrayType == typeof(int[])) WriteIntArrayPacked(a as int[], count); else if (arrayType == typeof(uint[])) WriteUIntArrayPacked(a as uint[], count); else if (arrayType == typeof(long[])) WriteLongArrayPacked(a as long[], count); else if (arrayType == typeof(ulong[])) WriteULongArrayPacked(a as ulong[], count); else if (arrayType == typeof(float[])) WriteFloatArrayPacked(a as float[], count); else if (arrayType == typeof(double[])) WriteDoubleArrayPacked(a as double[], count); else throw new InvalidDataException("Unknown array type! Please serialize manually!"); } /// /// Writes the diff between two arrays in a packed format /// /// The new array /// The previous array to use for diff /// The amount of elements to write public void WriteArrayPackedDiff(Array write, Array compare, long count = -1) { var arrayType = write.GetType(); if (arrayType != compare.GetType()) throw new ArrayTypeMismatchException("Cannot write diff of two differing array types"); #if ARRAY_WRITE_PERMISSIVE if (arrayType == typeof(byte[])) WriteByteArrayDiff(write as byte[], compare as byte[], count); else #endif if (arrayType == typeof(short[])) WriteShortArrayPackedDiff(write as short[], compare as short[], count); else if (arrayType == typeof(ushort[])) WriteUShortArrayPackedDiff(write as ushort[], compare as ushort[], count); else if (arrayType == typeof(char[])) WriteCharArrayPackedDiff(write as char[], compare as char[], count); else if (arrayType == typeof(int[])) WriteIntArrayPackedDiff(write as int[], compare as int[], count); else if (arrayType == typeof(uint[])) WriteUIntArrayPackedDiff(write as uint[], compare as uint[], count); else if (arrayType == typeof(long[])) WriteLongArrayPackedDiff(write as long[], compare as long[], count); else if (arrayType == typeof(ulong[])) WriteULongArrayPackedDiff(write as ulong[], compare as ulong[], count); else if (arrayType == typeof(float[])) WriteFloatArrayPackedDiff(write as float[], compare as float[], count); else if (arrayType == typeof(double[])) WriteDoubleArrayPackedDiff(write as double[], compare as double[], count); else throw new InvalidDataException("Unknown array type! Please serialize manually!"); } #endif /// /// Writes a short array in a packed format /// /// The array to write /// The amount of elements to write public void WriteShortArrayPacked(short[] b, long count = -1) { ulong target = WriteArraySize(b, null, count); for (ulong i = 0; i < target; ++i) WriteInt16Packed(b[i]); } /// /// Writes the diff between two short arrays in a packed format /// /// The new array /// The previous array to use for diff /// The amount of elements to write public void WriteShortArrayPackedDiff(short[] write, short[] compare, long count = -1) { CheckLengths(write, compare); long target = (long)WriteArraySize(write, compare, count); WritePremap(write, compare); for (long i = 0; i < target; ++i) { bool b = i >= compare.LongLength || write[i] != compare[i]; #if !ARRAY_WRITE_PREMAP WriteBit(!b); #endif if (b) WriteInt16Packed(write[i]); } } /// /// Writes a ushort array in a packed format /// /// The array to write /// The amount of elements to write public void WriteUShortArrayPacked(ushort[] b, long count = -1) { ulong target = WriteArraySize(b, null, count); for (ulong i = 0; i < target; ++i) WriteUInt16Packed(b[i]); } /// /// Writes the diff between two ushort arrays in a packed format /// /// The new array /// The previous array to use for diff /// The amount of elements to write public void WriteUShortArrayPackedDiff(ushort[] write, ushort[] compare, long count = -1) { CheckLengths(write, compare); long target = (long)WriteArraySize(write, compare, count); WritePremap(write, compare); for (long i = 0; i < target; ++i) { bool b = i >= compare.LongLength || write[i] != compare[i]; #if !ARRAY_WRITE_PREMAP WriteBit(!b); #endif if (b) WriteUInt16Packed(write[i]); } } /// /// Writes a char array in a packed format /// /// The array to write /// The amount of elements to write public void WriteCharArrayPacked(char[] b, long count = -1) { ulong target = WriteArraySize(b, null, count); for (ulong i = 0; i < target; ++i) WriteCharPacked(b[i]); } /// /// Writes the diff between two char arrays in a packed format /// /// The new array /// The previous array to use for diff /// The amount of elements to write public void WriteCharArrayPackedDiff(char[] write, char[] compare, long count = -1) { CheckLengths(write, compare); long target = (long)WriteArraySize(write, compare, count); WritePremap(write, compare); for (long i = 0; i < target; ++i) { bool b = i >= compare.LongLength || write[i] != compare[i]; #if !ARRAY_WRITE_PREMAP WriteBit(!b); #endif if (b) WriteCharPacked(write[i]); } } /// /// Writes a int array in a packed format /// /// The array to write /// The amount of elements to write public void WriteIntArrayPacked(int[] b, long count = -1) { ulong target = WriteArraySize(b, null, count); for (ulong i = 0; i < target; ++i) WriteInt32Packed(b[i]); } /// /// Writes the diff between two int arrays /// /// The new array /// The previous array to use for diff /// The amount of elements to write public void WriteIntArrayPackedDiff(int[] write, int[] compare, long count = -1) { CheckLengths(write, compare); long target = (long)WriteArraySize(write, compare, count); WritePremap(write, compare); for (long i = 0; i < target; ++i) { bool b = i >= compare.LongLength || write[i] != compare[i]; #if !ARRAY_WRITE_PREMAP WriteBit(!b); #endif if (b) WriteInt32Packed(write[i]); } } /// /// Writes a uint array in a packed format /// /// The array to write /// The amount of elements to write public void WriteUIntArrayPacked(uint[] b, long count = -1) { ulong target = WriteArraySize(b, null, count); for (ulong i = 0; i < target; ++i) WriteUInt32Packed(b[i]); } /// /// Writes the diff between two uing arrays in a packed format /// /// The new array /// The previous array to use for diff /// The amount of elements to write public void WriteUIntArrayPackedDiff(uint[] write, uint[] compare, long count = -1) { CheckLengths(write, compare); long target = (long)WriteArraySize(write, compare, count); WritePremap(write, compare); for (long i = 0; i < target; ++i) { bool b = i >= compare.LongLength || write[i] != compare[i]; #if !ARRAY_WRITE_PREMAP WriteBit(!b); #endif if (b) WriteUInt32Packed(write[i]); } } /// /// Writes a long array in a packed format /// /// The array to write /// The amount of elements to write public void WriteLongArrayPacked(long[] b, long count = -1) { ulong target = WriteArraySize(b, null, count); for (ulong i = 0; i < target; ++i) WriteInt64Packed(b[i]); } /// /// Writes the diff between two long arrays in a packed format /// /// The new array /// The previous array to use for diff /// The amount of elements to write public void WriteLongArrayPackedDiff(long[] write, long[] compare, long count = -1) { CheckLengths(write, compare); long target = (long)WriteArraySize(write, compare, count); WritePremap(write, compare); for (long i = 0; i < target; ++i) { bool b = i >= compare.LongLength || write[i] != compare[i]; #if !ARRAY_WRITE_PREMAP WriteBit(!b); #endif if (b) WriteInt64Packed(write[i]); } } /// /// Writes a ulong array in a packed format /// /// The array to write /// The amount of elements to write public void WriteULongArrayPacked(ulong[] b, long count = -1) { ulong target = WriteArraySize(b, null, count); for (ulong i = 0; i < target; ++i) WriteUInt64Packed(b[i]); } /// /// Writes the diff between two ulong arrays in a packed format /// /// The new array /// The previous array to use for diff /// The amount of elements to write public void WriteULongArrayPackedDiff(ulong[] write, ulong[] compare, long count = -1) { CheckLengths(write, compare); long target = (long)WriteArraySize(write, compare, count); WritePremap(write, compare); for (long i = 0; i < target; ++i) { bool b = i >= compare.LongLength || write[i] != compare[i]; #if !ARRAY_WRITE_PREMAP WriteBit(!b); #endif if (b) WriteUInt64Packed(write[i]); } } /// /// Writes a float array in a packed format /// /// The array to write /// The amount of elements to write public void WriteFloatArrayPacked(float[] b, long count = -1) { ulong target = WriteArraySize(b, null, count); for (ulong i = 0; i < target; ++i) WriteSinglePacked(b[i]); } /// /// Writes the diff between two float arrays in a packed format /// /// The new array /// The previous array to use for diff /// The amount of elements to write public void WriteFloatArrayPackedDiff(float[] write, float[] compare, long count = -1) { CheckLengths(write, compare); long target = (long)WriteArraySize(write, compare, count); WritePremap(write, compare); for (long i = 0; i < target; ++i) { bool b = i >= compare.LongLength || write[i] != compare[i]; #if !ARRAY_WRITE_PREMAP WriteBit(!b); #endif if (b) WriteSinglePacked(write[i]); } } /// /// Writes a double array in a packed format /// /// The array to write /// The amount of elements to write public void WriteDoubleArrayPacked(double[] b, long count = -1) { ulong target = WriteArraySize(b, null, count); for (ulong i = 0; i < target; ++i) WriteDoublePacked(b[i]); } /// /// Writes the diff between two double arrays in a packed format /// /// The new array /// The previous array to use for diff /// The amount of elements to write public void WriteDoubleArrayPackedDiff(double[] write, double[] compare, long count = -1) { CheckLengths(write, compare); long target = (long)WriteArraySize(write, compare, count); WritePremap(write, compare); for (long i = 0; i < target; ++i) { bool b = i >= compare.LongLength || write[i] != compare[i]; #if !ARRAY_WRITE_PREMAP WriteBit(!b); #endif if (b) WriteDoublePacked(write[i]); } } } }