#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.IO; using System.Text; using MLAPI.Reflection; using MLAPI.Logging; using MLAPI.Spawning; using UnityEngine; namespace MLAPI.Serialization { /// /// A BinaryReader that can do bit wise manipulation when backed by a NetworkBuffer /// public class NetworkReader { private Stream m_Source; private NetworkBuffer m_NetworkSource; /// /// Creates a new NetworkReader backed by a given stream /// /// The stream to read from public NetworkReader(Stream stream) { m_Source = stream; m_NetworkSource = stream as NetworkBuffer; } /// /// Changes the underlying stream the reader is reading from /// /// The stream to read from public void SetStream(Stream stream) { m_Source = stream; m_NetworkSource = stream as NetworkBuffer; } /// /// Reads a single byte /// /// The byte read as an integer public int ReadByte() => m_Source.ReadByte(); /// /// Reads a byte /// /// The byte read public byte ReadByteDirect() => (byte)m_Source.ReadByte(); /// /// Reads a single bit /// /// The bit read public bool ReadBit() { if (m_NetworkSource == null) { throw new InvalidOperationException($"Cannot read bits on a non-{nameof(NetworkBuffer)} stream"); } return m_NetworkSource.ReadBit(); } /// /// Reads a single bit /// /// The bit read public bool ReadBool() { if (m_NetworkSource == null) { return m_Source.ReadByte() != 0; } // return ReadBit(); // old (buggy) return ReadByte() != 0; // new (hotfix) } /// /// Skips pad bits and aligns the position to the next byte /// public void SkipPadBits() { if (m_NetworkSource == null) { throw new InvalidOperationException($"Cannot read bits on a non-{nameof(NetworkBuffer)} stream"); } while (!m_NetworkSource.BitAligned) ReadBit(); } /// /// Reads a single boxed object of a given type in a packed format /// /// The type to read /// Returns the boxed read object public object ReadObjectPacked(Type type) { if (type.IsNullable()) { bool isNull = ReadBool(); if (isNull) { return null; } } if (SerializationManager.TryDeserialize(m_Source, type, out object obj)) return obj; if (type.IsArray && type.HasElementType) { int size = ReadInt32Packed(); Array array = Array.CreateInstance(type.GetElementType(), size); for (int i = 0; i < size; i++) { array.SetValue(ReadObjectPacked(type.GetElementType()), i); } return array; } if (type == typeof(byte)) return ReadByteDirect(); if (type == typeof(sbyte)) return ReadSByte(); if (type == typeof(ushort)) return ReadUInt16Packed(); if (type == typeof(short)) return ReadInt16Packed(); if (type == typeof(int)) return ReadInt32Packed(); if (type == typeof(uint)) return ReadUInt32Packed(); if (type == typeof(long)) return ReadInt64Packed(); if (type == typeof(ulong)) return ReadUInt64Packed(); if (type == typeof(float)) return ReadSinglePacked(); if (type == typeof(double)) return ReadDoublePacked(); if (type == typeof(string)) return ReadStringPacked(); if (type == typeof(bool)) return ReadBool(); if (type == typeof(Vector2)) return ReadVector2Packed(); if (type == typeof(Vector3)) return ReadVector3Packed(); if (type == typeof(Vector4)) return ReadVector4Packed(); if (type == typeof(Color)) return ReadColorPacked(); if (type == typeof(Color32)) return ReadColor32(); if (type == typeof(Ray)) return ReadRayPacked(); if (type == typeof(Quaternion)) return ReadRotationPacked(); if (type == typeof(char)) return ReadCharPacked(); if (type.IsEnum) return ReadInt32Packed(); if (type == typeof(GameObject)) { ulong networkObjectId = ReadUInt64Packed(); if (NetworkSpawnManager.SpawnedObjects.ContainsKey(networkObjectId)) { return NetworkSpawnManager.SpawnedObjects[networkObjectId].gameObject; } if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) { NetworkLog.LogWarning($"{nameof(NetworkReader)} cannot find the {nameof(GameObject)} sent in the {nameof(NetworkSpawnManager.SpawnedObjects)} list, it may have been destroyed. {nameof(networkObjectId)}: {networkObjectId}"); } return null; } if (type == typeof(NetworkObject)) { ulong networkObjectId = ReadUInt64Packed(); if (NetworkSpawnManager.SpawnedObjects.ContainsKey(networkObjectId)) { return NetworkSpawnManager.SpawnedObjects[networkObjectId]; } if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) { NetworkLog.LogWarning($"{nameof(NetworkReader)} cannot find the {nameof(NetworkObject)} sent in the {nameof(NetworkSpawnManager.SpawnedObjects)} list, it may have been destroyed. {nameof(networkObjectId)}: {networkObjectId}"); } return null; } if (typeof(NetworkBehaviour).IsAssignableFrom(type)) { ulong networkObjectId = ReadUInt64Packed(); ushort behaviourId = ReadUInt16Packed(); if (NetworkSpawnManager.SpawnedObjects.ContainsKey(networkObjectId)) { return NetworkSpawnManager.SpawnedObjects[networkObjectId].GetNetworkBehaviourAtOrderIndex(behaviourId); } if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) { NetworkLog.LogWarning($"{nameof(NetworkReader)} cannot find the {nameof(NetworkBehaviour)} sent in the {nameof(NetworkSpawnManager.SpawnedObjects)} list, it may have been destroyed. {nameof(networkObjectId)}: {networkObjectId}"); } return null; } if (typeof(INetworkSerializable).IsAssignableFrom(type)) { object instance = Activator.CreateInstance(type); ((INetworkSerializable)instance).NetworkSerialize(new NetworkSerializer(this)); return instance; } Type nullableUnderlyingType = Nullable.GetUnderlyingType(type); if (nullableUnderlyingType != null && SerializationManager.IsTypeSupported(nullableUnderlyingType)) { return ReadObjectPacked(nullableUnderlyingType); } throw new ArgumentException($"{nameof(NetworkReader)} cannot read type {type.Name}"); } /// /// Read a single-precision floating point value from the stream. /// /// The read value public float ReadSingle() => new UIntFloat { UIntValue = ReadUInt32() }.FloatValue; /// /// Read a double-precision floating point value from the stream. /// /// The read value public double ReadDouble() => new UIntFloat { ULongValue = ReadUInt64() }.DoubleValue; /// /// Read a single-precision floating point value from the stream from a varint /// /// The read value public float ReadSinglePacked() => new UIntFloat { UIntValue = ReadUInt32Packed() }.FloatValue; /// /// Read a double-precision floating point value from the stream as a varint /// /// The read value public double ReadDoublePacked() => new UIntFloat { ULongValue = ReadUInt64Packed() }.DoubleValue; /// /// Read a Vector2 from the stream. /// /// The Vector2 read from the stream. public Vector2 ReadVector2() => new Vector2(ReadSingle(), ReadSingle()); /// /// Read a Vector2 from the stream. /// /// The Vector2 read from the stream. public Vector2 ReadVector2Packed() => new Vector2(ReadSinglePacked(), ReadSinglePacked()); /// /// Read a Vector3 from the stream. /// /// The Vector3 read from the stream. public Vector3 ReadVector3() => new Vector3(ReadSingle(), ReadSingle(), ReadSingle()); /// /// Read a Vector3 from the stream. /// /// The Vector3 read from the stream. public Vector3 ReadVector3Packed() => new Vector3(ReadSinglePacked(), ReadSinglePacked(), ReadSinglePacked()); /// /// Read a Vector4 from the stream. /// /// The Vector4 read from the stream. public Vector4 ReadVector4() => new Vector4(ReadSingle(), ReadSingle(), ReadSingle(), ReadSingle()); /// /// Read a Vector4 from the stream. /// /// The Vector4 read from the stream. public Vector4 ReadVector4Packed() => new Vector4(ReadSinglePacked(), ReadSinglePacked(), ReadSinglePacked(), ReadSinglePacked()); /// /// Read a Color from the stream. /// /// The Color read from the stream. public Color ReadColor() => new Color(ReadSingle(), ReadSingle(), ReadSingle(), ReadSingle()); /// /// Read a Color from the stream. /// /// The Color read from the stream. public Color ReadColorPacked() => new Color(ReadSinglePacked(), ReadSinglePacked(), ReadSinglePacked(), ReadSinglePacked()); /// /// Read a Color32 from the stream. /// /// The Color32 read from the stream. public Color32 ReadColor32() => new Color32((byte)ReadByte(), (byte)ReadByte(), (byte)ReadByte(), (byte)ReadByte()); /// /// Read a Ray from the stream. /// /// The Ray read from the stream. public Ray ReadRay() => new Ray(ReadVector3(), ReadVector3()); /// /// Read a Ray from the stream. /// /// The Ray read from the stream. public Ray ReadRayPacked() => new Ray(ReadVector3Packed(), ReadVector3Packed()); /// /// Read a Ray2D from the stream. /// /// The Ray2D read from the stream. public Ray2D ReadRay2D() => new Ray2D(ReadVector2(), ReadVector2()); /// /// Read a Ray2D from the stream. /// /// The Ray2D read from the stream. public Ray2D ReadRay2DPacked() => new Ray2D(ReadVector2Packed(), ReadVector2Packed()); /// /// Read a single-precision floating point value from the stream. The value is between (inclusive) the minValue and maxValue. /// /// Minimum value that this value could be /// Maximum possible value that this could be /// How many bytes the compressed value occupies. Must be between 1 and 4 (inclusive) /// The read value public float ReadRangedSingle(float minValue, float maxValue, int bytes) { if (bytes < 1 || bytes > 4) throw new ArgumentOutOfRangeException("Result must occupy between 1 and 4 bytes!"); uint read = 0; for (int i = 0; i < bytes; ++i) read |= (uint)ReadByte() << (i << 3); return (((float)read / ((0x100 * bytes) - 1)) * (minValue + maxValue)) - minValue; } /// /// read a double-precision floating point value from the stream. The value is between (inclusive) the minValue and maxValue. /// /// Minimum value that this value could be /// Maximum possible value that this could be /// How many bytes the compressed value occupies. Must be between 1 and 8 (inclusive) /// The read value public double ReadRangedDouble(double minValue, double maxValue, int bytes) { if (bytes < 1 || bytes > 8) throw new ArgumentOutOfRangeException("Result must occupy between 1 and 8 bytes!"); ulong read = 0; for (int i = 0; i < bytes; ++i) read |= (ulong)ReadByte() << (i << 3); return (((double)read / ((0x100 * bytes) - 1)) * (minValue + maxValue)) - minValue; } /// /// Reads the rotation from the stream /// /// The rotation read from the stream public Quaternion ReadRotationPacked() { float x = ReadSinglePacked(); float y = ReadSinglePacked(); float z = ReadSinglePacked(); // numerical precision issues can make the remainder very slightly negative. // In this case, use 0 for w as, otherwise, w would be NaN. float remainder = 1f - Mathf.Pow(x, 2) - Mathf.Pow(y, 2) - Mathf.Pow(z, 2); float w = (remainder > 0f) ? Mathf.Sqrt(remainder) : 0.0f; return new Quaternion(x, y, z, w); } /// /// Reads the rotation from the stream /// /// The rotation read from the stream public Quaternion ReadRotation() { float x = ReadSingle(); float y = ReadSingle(); float z = ReadSingle(); float w = ReadSingle(); return new Quaternion(x, y, z, w); } /// /// Read a certain amount of bits from the stream. /// /// How many bits to read. Minimum 0, maximum 8. /// The bits that were read public ulong ReadBits(int bitCount) { if (m_NetworkSource == null) throw new InvalidOperationException($"Cannot read bits on a non-{nameof(NetworkBuffer)} stream"); if (bitCount > 64) throw new ArgumentOutOfRangeException(nameof(bitCount), "Cannot read more than 64 bits into a 64-bit value!"); if (bitCount < 0) throw new ArgumentOutOfRangeException(nameof(bitCount), "Cannot read fewer than 0 bits!"); ulong read = 0; for (int i = 0; i + 8 < bitCount; i += 8) read |= (ulong)ReadByte() << i; read |= (ulong)ReadByteBits(bitCount & 7) << (bitCount & ~7); return read; } /// /// Read a certain amount of bits from the stream. /// /// How many bits to read. Minimum 0, maximum 64. /// The bits that were read public byte ReadByteBits(int bitCount) { if (m_NetworkSource == null) throw new InvalidOperationException($"Cannot read bits on a non-{nameof(NetworkBuffer)} stream"); if (bitCount > 8) throw new ArgumentOutOfRangeException(nameof(bitCount), "Cannot read more than 8 bits into an 8-bit value!"); if (bitCount < 0) throw new ArgumentOutOfRangeException(nameof(bitCount), "Cannot read fewer than 0 bits!"); int result = 0; ByteBool convert = new ByteBool(); for (int i = 0; i < bitCount; ++i) result |= convert.Collapse(ReadBit()) << i; return (byte)result; } /// /// Read a nibble (4 bits) from the stream. /// /// Whether or not the nibble should be left-shifted by 4 bits /// The nibble that was read public byte ReadNibble(bool asUpper) { if (m_NetworkSource == null) throw new InvalidOperationException($"Cannot read bits on a non-{nameof(NetworkBuffer)} stream"); ByteBool convert = new ByteBool(); byte result = (byte) ( convert.Collapse(ReadBit()) | (convert.Collapse(ReadBit()) << 1) | (convert.Collapse(ReadBit()) << 2) | (convert.Collapse(ReadBit()) << 3) ); if (asUpper) result <<= 4; return result; } // Marginally faster than the one that accepts a bool /// /// Read a nibble (4 bits) from the stream. /// /// The nibble that was read public byte ReadNibble() { if (m_NetworkSource == null) throw new InvalidOperationException($"Cannot read bits on a non-{nameof(NetworkBuffer)} stream"); ByteBool convert = new ByteBool(); return (byte) ( convert.Collapse(ReadBit()) | (convert.Collapse(ReadBit()) << 1) | (convert.Collapse(ReadBit()) << 2) | (convert.Collapse(ReadBit()) << 3) ); } /// /// Reads a signed byte /// /// Value read from stream. public sbyte ReadSByte() => (sbyte)ReadByte(); /// /// Read an unsigned short (UInt16) from the stream. /// /// Value read from stream. public ushort ReadUInt16() => (ushort)(ReadByte() | (ReadByte() << 8)); /// /// Read a signed short (Int16) from the stream. /// /// Value read from stream. public short ReadInt16() => (short)ReadUInt16(); /// /// Read a single character from the stream /// /// Value read from stream. public char ReadChar() => (char)ReadUInt16(); /// /// Read an unsigned int (UInt32) from the stream. /// /// Value read from stream. public uint ReadUInt32() => (uint)(ReadByte() | (ReadByte() << 8) | (ReadByte() << 16) | (ReadByte() << 24)); /// /// Read a signed int (Int32) from the stream. /// /// Value read from stream. public int ReadInt32() => (int)ReadUInt32(); /// /// Read an unsigned long (UInt64) from the stream. /// /// Value read from stream. public ulong ReadUInt64() => ( ((uint)ReadByte()) | ((ulong)ReadByte() << 8) | ((ulong)ReadByte() << 16) | ((ulong)ReadByte() << 24) | ((ulong)ReadByte() << 32) | ((ulong)ReadByte() << 40) | ((ulong)ReadByte() << 48) | ((ulong)ReadByte() << 56) ); /// /// Read a signed long (Int64) from the stream. /// /// Value read from stream. public long ReadInt64() => (long)ReadUInt64(); /// /// Read a ZigZag encoded varint signed short (Int16) from the stream. /// /// Decoded un-varinted value. public short ReadInt16Packed() => (short)Arithmetic.ZigZagDecode(ReadUInt64Packed()); /// /// Read a varint unsigned short (UInt16) from the stream. /// /// Un-varinted value. public ushort ReadUInt16Packed() => (ushort)ReadUInt64Packed(); /// /// Read a varint two-byte character from the stream. /// /// Un-varinted value. public char ReadCharPacked() => (char)ReadUInt16Packed(); /// /// Read a ZigZag encoded varint signed int (Int32) from the stream. /// /// Decoded un-varinted value. public int ReadInt32Packed() => (int)Arithmetic.ZigZagDecode(ReadUInt64Packed()); /// /// Read a varint unsigned int (UInt32) from the stream. /// /// Un-varinted value. public uint ReadUInt32Packed() => (uint)ReadUInt64Packed(); /// /// Read a ZigZag encoded varint signed long(Int64) from the stream. /// /// Decoded un-varinted value. public long ReadInt64Packed() => Arithmetic.ZigZagDecode(ReadUInt64Packed()); /// /// Read a varint unsigned long (UInt64) from the stream. /// /// Un-varinted value. public ulong ReadUInt64Packed() { ulong header = ReadByteDirect(); if (header <= 240) return header; if (header <= 248) return 240 + ((header - 241) << 8) + ReadByteDirect(); if (header == 249) return 2288UL + (ulong)(ReadByte() << 8) + ReadByteDirect(); ulong res = ReadByteDirect() | ((ulong)ReadByteDirect() << 8) | ((ulong)ReadByte() << 16); int cmp = 2; int hdr = (int)(header - 247); while (hdr > ++cmp) res |= (ulong)ReadByte() << (cmp << 3); return res; } // Read arrays /// /// Read a string from the stream. /// /// The string that was read. /// If set to true one byte chars are used and only ASCII is supported. public StringBuilder ReadString(bool oneByteChars) => ReadString(null, oneByteChars); /// /// Read a string from the stream. /// /// The string that was read. /// The builder to read the values into or null to use a new builder. /// If set to true one byte chars are used and only ASCII is supported. public StringBuilder ReadString(StringBuilder builder = null, bool oneByteChars = false) { int expectedLength = (int)ReadUInt32Packed(); if (builder == null) builder = new StringBuilder(expectedLength); else if (builder.Capacity + builder.Length < expectedLength) builder.Capacity = expectedLength + builder.Length; for (int i = 0; i < expectedLength; ++i) builder.Insert(i, oneByteChars ? (char)ReadByte() : ReadChar()); return builder; } /// /// Read string encoded as a varint from the stream. /// /// The string that was read. /// The builder to read the string into or null to use a new builder public string ReadStringPacked(StringBuilder builder = null) { int expectedLength = (int)ReadUInt32Packed(); if (builder == null) builder = new StringBuilder(expectedLength); else if (builder.Capacity + builder.Length < expectedLength) builder.Capacity = expectedLength + builder.Length; for (int i = 0; i < expectedLength; ++i) builder.Insert(i, ReadCharPacked()); return builder.ToString(); } /// /// Read string diff from the stream. /// /// The string based on the diff and the old version. /// The version to compare the diff to. /// If set to true one byte chars are used and only ASCII is supported. public StringBuilder ReadStringDiff(string compare, bool oneByteChars = false) => ReadStringDiff(null, compare, oneByteChars); /// /// Read string diff from the stream. /// /// The string based on the diff and the old version /// The builder to read the string into or null to use a new builder. /// The version to compare the diff to. /// If set to true one byte chars are used and only ASCII is supported. public StringBuilder ReadStringDiff(StringBuilder builder, string compare, bool oneByteChars = false) { if (m_NetworkSource == null) throw new InvalidOperationException($"Cannot read bits on a non-{nameof(NetworkBuffer)} stream"); int expectedLength = (int)ReadUInt32Packed(); if (builder == null) builder = new StringBuilder(expectedLength); else if (builder.Capacity < expectedLength) builder.Capacity = expectedLength; ulong dBlockStart = m_NetworkSource.BitPosition + (ulong)(compare == null ? 0 : Math.Min(expectedLength, compare.Length)); ulong mapStart; int compareLength = compare?.Length ?? 0; for (int i = 0; i < expectedLength; ++i) { if (i >= compareLength || ReadBit()) { #if ARRAY_WRITE_PREMAP // Move to data section mapStart = m_NetworkSource.BitPosition; m_NetworkSource.BitPosition = dBlockStart; #endif // Read datum builder.Insert(i, oneByteChars ? (char)ReadByte() : ReadChar()); #if ARRAY_WRITE_PREMAP dBlockStart = m_NetworkSource.BitPosition; // Return to mapping section m_NetworkSource.BitPosition = mapStart; #endif } else if (i < compareLength) builder.Insert(i, compare[i]); } m_NetworkSource.BitPosition = dBlockStart; return builder; } /// /// Read string diff from the stream. /// /// The string based on the diff and the old version. /// The builder containing the current version and that will also be used as the output buffer. /// If set to true one byte chars will be used and only ASCII will be supported. public StringBuilder ReadStringDiff(StringBuilder compareAndBuffer, bool oneByteChars = false) { if (m_NetworkSource == null) throw new InvalidOperationException($"Cannot read bits on a non-{nameof(NetworkBuffer)} stream"); int expectedLength = (int)ReadUInt32Packed(); if (compareAndBuffer == null) throw new ArgumentNullException(nameof(compareAndBuffer), "Buffer cannot be null"); if (compareAndBuffer.Capacity < expectedLength) compareAndBuffer.Capacity = expectedLength; ulong dBlockStart = m_NetworkSource.BitPosition + (ulong)Math.Min(expectedLength, compareAndBuffer.Length); ulong mapStart; for (int i = 0; i < expectedLength; ++i) { if (i >= compareAndBuffer.Length || ReadBit()) { #if ARRAY_WRITE_PREMAP // Move to data section mapStart = m_NetworkSource.BitPosition; m_NetworkSource.BitPosition = dBlockStart; #endif // Read datum compareAndBuffer.Remove(i, 1); compareAndBuffer.Insert(i, oneByteChars ? (char)ReadByte() : ReadChar()); #if ARRAY_WRITE_PREMAP dBlockStart = m_NetworkSource.BitPosition; // Return to mapping section m_NetworkSource.BitPosition = mapStart; #endif } } m_NetworkSource.BitPosition = dBlockStart; return compareAndBuffer; } /// /// Read string diff encoded as varints from the stream. /// /// The string based on the diff and the old version. /// The version to compare the diff to. public StringBuilder ReadStringPackedDiff(string compare) => ReadStringPackedDiff(null, compare); /// /// Read string diff encoded as varints from the stream. /// /// The string based on the diff and the old version /// The builder to read the string into or null to use a new builder. /// The version to compare the diff to. public StringBuilder ReadStringPackedDiff(StringBuilder builder, string compare) { if (m_NetworkSource == null) throw new InvalidOperationException($"Cannot read bits on a non-{nameof(NetworkBuffer)} stream"); int expectedLength = (int)ReadUInt32Packed(); if (builder == null) builder = new StringBuilder(expectedLength); else if (builder.Capacity < expectedLength) builder.Capacity = expectedLength; ulong dBlockStart = m_NetworkSource.BitPosition + (ulong)(compare == null ? 0 : Math.Min(expectedLength, compare.Length)); ulong mapStart; int compareLength = compare?.Length ?? 0; for (int i = 0; i < expectedLength; ++i) { if (i >= compareLength || ReadBit()) { #if ARRAY_WRITE_PREMAP // Move to data section mapStart = m_NetworkSource.BitPosition; m_NetworkSource.BitPosition = dBlockStart; #endif // Read datum builder.Insert(i, ReadCharPacked()); #if ARRAY_WRITE_PREMAP dBlockStart = m_NetworkSource.BitPosition; // Return to mapping section m_NetworkSource.BitPosition = mapStart; #endif } else if (i < compareLength) builder.Insert(i, compare[i]); } m_NetworkSource.BitPosition = dBlockStart; return builder; } /// /// Read string diff encoded as varints from the stream. /// /// The string based on the diff and the old version. /// The builder containing the current version and that will also be used as the output buffer. public StringBuilder ReadStringPackedDiff(StringBuilder compareAndBuffer) { if (m_NetworkSource == null) throw new InvalidOperationException($"Cannot read bits on a non-{nameof(NetworkBuffer)} stream"); int expectedLength = (int)ReadUInt32Packed(); if (compareAndBuffer == null) throw new ArgumentNullException(nameof(compareAndBuffer), "Buffer cannot be null"); if (compareAndBuffer.Capacity < expectedLength) compareAndBuffer.Capacity = expectedLength; ulong dBlockStart = m_NetworkSource.BitPosition + (ulong)Math.Min(expectedLength, compareAndBuffer.Length); ulong mapStart; for (int i = 0; i < expectedLength; ++i) { if (i >= compareAndBuffer.Length || ReadBit()) { #if ARRAY_WRITE_PREMAP // Move to data section mapStart = m_NetworkSource.BitPosition; m_NetworkSource.BitPosition = dBlockStart; #endif // Read datum compareAndBuffer.Remove(i, 1); compareAndBuffer.Insert(i, ReadCharPacked()); #if ARRAY_WRITE_PREMAP dBlockStart = m_NetworkSource.BitPosition; // Return to mapping section m_NetworkSource.BitPosition = mapStart; #endif } } m_NetworkSource.BitPosition = dBlockStart; return compareAndBuffer; } /// /// Read byte array into an optional buffer from the stream. /// /// The byte array that has been read. /// The array to read into. If the array is not large enough or if it's null. A new array is created. /// The length of the array if it's known. Otherwise -1 public byte[] ReadByteArray(byte[] readTo = null, long knownLength = -1) { if (knownLength < 0) knownLength = (long)ReadUInt64Packed(); if (readTo == null || readTo.LongLength != knownLength) readTo = new byte[knownLength]; for (long i = 0; i < knownLength; ++i) readTo[i] = ReadByteDirect(); return readTo; } /// /// CreateArraySegment /// Creates an array segment from the size and offset values passed in. /// If none are passed in, then it creates an array segment of the entire buffer. /// /// size to copy /// offset within the stream buffer to start copying /// ArraySegment<byte> public ArraySegment CreateArraySegment(int sizeToCopy = -1, int offset = -1) { if (m_NetworkSource != null) { //If no offset was passed, used the current position int Offset = offset == -1 ? (int)m_NetworkSource.Position : offset; int CopySize = sizeToCopy == -1 && offset == -1 ? (int)m_NetworkSource.Length : sizeToCopy; if (CopySize > 0) { //Check to make sure we won't be copying beyond our bounds if ((m_NetworkSource.Length - Offset) >= CopySize) { return new ArraySegment(m_NetworkSource.GetBuffer(), Offset, CopySize); } //If we didn't pass anything in or passed the length of the buffer if (CopySize == m_NetworkSource.Length) { Offset = 0; } else { Debug.LogError($"{nameof(CopySize)} ({CopySize}) exceeds bounds with an {nameof(Offset)} of ({Offset})! "); return new ArraySegment(); } //Return the request array segment return new ArraySegment(m_NetworkSource.GetBuffer(), Offset, CopySize); } Debug.LogError($"{nameof(CopySize)} ({CopySize}) is zero or less! "); } else { Debug.LogError("Reader has no stream assigned to it! "); } return new ArraySegment(); } /// /// Read byte array diff into an optional buffer from the stream. /// /// The byte array created from the diff and original. /// The buffer containing the old version or null. /// The length of the array if it's known. Otherwise -1 public byte[] ReadByteArrayDiff(byte[] readTo = null, long knownLength = -1) { if (m_NetworkSource == null) throw new InvalidOperationException($"Cannot read bits on a non-{nameof(NetworkBuffer)} stream"); if (knownLength < 0) knownLength = (long)ReadUInt64Packed(); byte[] writeTo = readTo == null || readTo.LongLength != knownLength ? new byte[knownLength] : readTo; ulong dBlockStart = m_NetworkSource.BitPosition + (ulong)(readTo == null ? 0 : Math.Min(knownLength, readTo.LongLength)); ulong mapStart; long readToLength = readTo?.LongLength ?? 0; for (long i = 0; i < knownLength; ++i) { if (i >= readToLength || ReadBit()) { #if ARRAY_WRITE_PREMAP // Move to data section mapStart = m_NetworkSource.BitPosition; m_NetworkSource.BitPosition = dBlockStart; #endif // Read datum writeTo[i] = ReadByteDirect(); #if ARRAY_WRITE_PREMAP dBlockStart = m_NetworkSource.BitPosition; // Return to mapping section m_NetworkSource.BitPosition = mapStart; #endif } else if (i < readTo.LongLength) writeTo[i] = readTo[i]; } m_NetworkSource.BitPosition = dBlockStart; return writeTo; } /// /// Read short array from the stream. /// /// The array read from the stream. /// The buffer to read into or null to create a new array /// The known length or -1 if unknown public short[] ReadShortArray(short[] readTo = null, long knownLength = -1) { if (knownLength < 0) knownLength = (long)ReadUInt64Packed(); if (readTo == null || readTo.LongLength != knownLength) readTo = new short[knownLength]; for (long i = 0; i < knownLength; ++i) readTo[i] = ReadInt16(); return readTo; } /// /// Read short array in a packed format from the stream. /// /// The array read from the stream. /// The buffer to read into or null to create a new array /// The known length or -1 if unknown public short[] ReadShortArrayPacked(short[] readTo = null, long knownLength = -1) { if (knownLength < 0) knownLength = (long)ReadUInt64Packed(); if (readTo == null || readTo.LongLength != knownLength) readTo = new short[knownLength]; for (long i = 0; i < knownLength; ++i) readTo[i] = ReadInt16Packed(); return readTo; } /// /// Read short array diff from the stream. /// /// The array created from the diff and the current version. /// The buffer containing the old version or null. /// The known length or -1 if unknown public short[] ReadShortArrayDiff(short[] readTo = null, long knownLength = -1) { if (m_NetworkSource == null) throw new InvalidOperationException($"Cannot read bits on a non-{nameof(NetworkBuffer)} stream"); if (knownLength < 0) knownLength = (long)ReadUInt64Packed(); short[] writeTo = readTo == null || readTo.LongLength != knownLength ? new short[knownLength] : readTo; ulong dBlockStart = m_NetworkSource.BitPosition + (ulong)(readTo == null ? 0 : Math.Min(knownLength, readTo.LongLength)); ulong mapStart; long readToLength = readTo?.LongLength ?? 0; for (long i = 0; i < knownLength; ++i) { if (i >= readToLength || ReadBit()) { #if ARRAY_WRITE_PREMAP // Move to data section mapStart = m_NetworkSource.BitPosition; m_NetworkSource.BitPosition = dBlockStart; #endif // Read datum writeTo[i] = ReadInt16(); #if ARRAY_WRITE_PREMAP dBlockStart = m_NetworkSource.BitPosition; // Return to mapping section m_NetworkSource.BitPosition = mapStart; #endif } else if (i < readTo.LongLength) writeTo[i] = readTo[i]; } m_NetworkSource.BitPosition = dBlockStart; return writeTo; } /// /// Read short array diff in a packed format from the stream. /// /// The array created from the diff and the current version. /// The buffer containing the old version or null. /// The known length or -1 if unknown public short[] ReadShortArrayPackedDiff(short[] readTo = null, long knownLength = -1) { if (m_NetworkSource == null) throw new InvalidOperationException($"Cannot read bits on a non-{nameof(NetworkBuffer)} stream"); if (knownLength < 0) knownLength = (long)ReadUInt64Packed(); short[] writeTo = readTo == null || readTo.LongLength != knownLength ? new short[knownLength] : readTo; ulong data = m_NetworkSource.BitPosition + (ulong)(readTo == null ? 0 : Math.Min(knownLength, readTo.LongLength)); ulong rset; long readToLength = readTo?.LongLength ?? 0; for (long i = 0; i < knownLength; ++i) { if (i >= readToLength || ReadBit()) { #if ARRAY_WRITE_PREMAP // Move to data section rset = m_NetworkSource.BitPosition; m_NetworkSource.BitPosition = data; #endif // Read datum writeTo[i] = ReadInt16Packed(); #if ARRAY_WRITE_PREMAP // Return to mapping section data = m_NetworkSource.BitPosition; m_NetworkSource.BitPosition = rset; #endif } else if (i < readTo.LongLength) writeTo[i] = readTo[i]; } m_NetworkSource.BitPosition = data; return writeTo; } /// /// Read ushort array from the stream. /// /// The array read from the stream. /// The buffer to read into or null to create a new array /// The known length or -1 if unknown public ushort[] ReadUShortArray(ushort[] readTo = null, long knownLength = -1) { if (knownLength < 0) knownLength = (long)ReadUInt64Packed(); if (readTo == null || readTo.LongLength != knownLength) readTo = new ushort[knownLength]; for (long i = 0; i < knownLength; ++i) readTo[i] = ReadUInt16(); return readTo; } /// /// Read ushort array in a packed format from the stream. /// /// The array read from the stream. /// The buffer to read into or null to create a new array /// The known length or -1 if unknown public ushort[] ReadUShortArrayPacked(ushort[] readTo = null, long knownLength = -1) { if (knownLength < 0) knownLength = (long)ReadUInt64Packed(); if (readTo == null || readTo.LongLength != knownLength) readTo = new ushort[knownLength]; for (long i = 0; i < knownLength; ++i) readTo[i] = ReadUInt16Packed(); return readTo; } /// /// Read ushort array diff from the stream. /// /// The array created from the diff and the current version. /// The buffer containing the old version or null. /// The known length or -1 if unknown public ushort[] ReadUShortArrayDiff(ushort[] readTo = null, long knownLength = -1) { if (m_NetworkSource == null) throw new InvalidOperationException($"Cannot read bits on a non-{nameof(NetworkBuffer)} stream"); if (knownLength < 0) knownLength = (long)ReadUInt64Packed(); ushort[] writeTo = readTo == null || readTo.LongLength != knownLength ? new ushort[knownLength] : readTo; ulong dBlockStart = m_NetworkSource.BitPosition + (ulong)(readTo == null ? 0 : Math.Min(knownLength, readTo.LongLength)); ulong mapStart; long readToLength = readTo?.LongLength ?? 0; for (long i = 0; i < knownLength; ++i) { if (i >= readToLength || ReadBit()) { #if ARRAY_WRITE_PREMAP // Move to data section mapStart = m_NetworkSource.BitPosition; m_NetworkSource.BitPosition = dBlockStart; #endif // Read datum writeTo[i] = ReadUInt16(); #if ARRAY_WRITE_PREMAP dBlockStart = m_NetworkSource.BitPosition; // Return to mapping section m_NetworkSource.BitPosition = mapStart; #endif } else if (i < readTo.LongLength) writeTo[i] = readTo[i]; } m_NetworkSource.BitPosition = dBlockStart; return writeTo; } /// /// Read ushort array diff in a packed format from the stream. /// /// The array created from the diff and the current version. /// The buffer containing the old version or null. /// The known length or -1 if unknown public ushort[] ReadUShortArrayPackedDiff(ushort[] readTo = null, long knownLength = -1) { if (m_NetworkSource == null) throw new InvalidOperationException($"Cannot read bits on a non-{nameof(NetworkBuffer)} stream"); if (knownLength < 0) knownLength = (long)ReadUInt64Packed(); ushort[] writeTo = readTo == null || readTo.LongLength != knownLength ? new ushort[knownLength] : readTo; ulong data = m_NetworkSource.BitPosition + (ulong)(readTo == null ? 0 : Math.Min(knownLength, readTo.LongLength)); ulong rset; long readToLength = readTo?.LongLength ?? 0; for (long i = 0; i < knownLength; ++i) { if (i >= readToLength || ReadBit()) { #if ARRAY_WRITE_PREMAP // Move to data section rset = m_NetworkSource.BitPosition; m_NetworkSource.BitPosition = data; #endif // Read datum writeTo[i] = ReadUInt16Packed(); #if ARRAY_WRITE_PREMAP // Return to mapping section data = m_NetworkSource.BitPosition; m_NetworkSource.BitPosition = rset; #endif } else if (i < readTo.LongLength) writeTo[i] = readTo[i]; } m_NetworkSource.BitPosition = data; return writeTo; } /// /// Read int array from the stream. /// /// The array read from the stream. /// The buffer to read into or null to create a new array /// The known length or -1 if unknown public int[] ReadIntArray(int[] readTo = null, long knownLength = -1) { if (knownLength < 0) knownLength = (long)ReadUInt64Packed(); if (readTo == null || readTo.LongLength != knownLength) readTo = new int[knownLength]; for (long i = 0; i < knownLength; ++i) readTo[i] = ReadInt32(); return readTo; } /// /// Read int array in a packed format from the stream. /// /// The array read from the stream. /// The buffer to read into or null to create a new array /// The known length or -1 if unknown public int[] ReadIntArrayPacked(int[] readTo = null, long knownLength = -1) { if (knownLength < 0) knownLength = (long)ReadUInt64Packed(); if (readTo == null || readTo.LongLength != knownLength) readTo = new int[knownLength]; for (long i = 0; i < knownLength; ++i) readTo[i] = ReadInt32Packed(); return readTo; } /// /// Read int array diff from the stream. /// /// The array created from the diff and the current version. /// The buffer containing the old version or null. /// The known length or -1 if unknown public int[] ReadIntArrayDiff(int[] readTo = null, long knownLength = -1) { if (m_NetworkSource == null) throw new InvalidOperationException($"Cannot read bits on a non-{nameof(NetworkBuffer)} stream"); if (knownLength < 0) knownLength = (long)ReadUInt64Packed(); int[] writeTo = readTo == null || readTo.LongLength != knownLength ? new int[knownLength] : readTo; ulong dBlockStart = m_NetworkSource.BitPosition + (ulong)(readTo == null ? 0 : Math.Min(knownLength, readTo.LongLength)); ulong mapStart; long readToLength = readTo?.LongLength ?? 0; for (long i = 0; i < knownLength; ++i) { if (i >= readToLength || ReadBit()) { #if ARRAY_WRITE_PREMAP // Move to data section mapStart = m_NetworkSource.BitPosition; m_NetworkSource.BitPosition = dBlockStart; #endif // Read datum writeTo[i] = ReadInt32(); #if ARRAY_WRITE_PREMAP dBlockStart = m_NetworkSource.BitPosition; // Return to mapping section m_NetworkSource.BitPosition = mapStart; #endif } else if (i < readTo.LongLength) writeTo[i] = readTo[i]; } m_NetworkSource.BitPosition = dBlockStart; return writeTo; } /// /// Read int array diff in a packed format from the stream. /// /// The array created from the diff and the current version. /// The buffer containing the old version or null. /// The known length or -1 if unknown public int[] ReadIntArrayPackedDiff(int[] readTo = null, long knownLength = -1) { if (m_NetworkSource == null) throw new InvalidOperationException($"Cannot read bits on a non-{nameof(NetworkBuffer)} stream"); if (knownLength < 0) knownLength = (long)ReadUInt64Packed(); int[] writeTo = readTo == null || readTo.LongLength != knownLength ? new int[knownLength] : readTo; ulong data = m_NetworkSource.BitPosition + (ulong)(readTo == null ? 0 : Math.Min(knownLength, readTo.LongLength)); ulong rset; long readToLength = readTo?.LongLength ?? 0; for (long i = 0; i < knownLength; ++i) { if (i >= readToLength || ReadBit()) { #if ARRAY_WRITE_PREMAP // Move to data section rset = m_NetworkSource.BitPosition; m_NetworkSource.BitPosition = data; #endif // Read datum writeTo[i] = ReadInt32Packed(); #if ARRAY_WRITE_PREMAP // Return to mapping section data = m_NetworkSource.BitPosition; m_NetworkSource.BitPosition = rset; #endif } else if (i < readTo.LongLength) writeTo[i] = readTo[i]; } m_NetworkSource.BitPosition = data; return writeTo; } /// /// Read uint array from the stream. /// /// The array read from the stream. /// The buffer to read into or null to create a new array /// The known length or -1 if unknown public uint[] ReadUIntArray(uint[] readTo = null, long knownLength = -1) { if (knownLength < 0) knownLength = (long)ReadUInt64Packed(); if (readTo == null || readTo.LongLength != knownLength) readTo = new uint[knownLength]; for (long i = 0; i < knownLength; ++i) readTo[i] = ReadUInt32(); return readTo; } /// /// Read uint array in a packed format from the stream. /// /// The array read from the stream. /// The buffer to read into or null to create a new array /// The known length or -1 if unknown public uint[] ReadUIntArrayPacked(uint[] readTo = null, long knownLength = -1) { if (knownLength < 0) knownLength = (long)ReadUInt64Packed(); if (readTo == null || readTo.LongLength != knownLength) readTo = new uint[knownLength]; for (long i = 0; i < knownLength; ++i) readTo[i] = ReadUInt32Packed(); return readTo; } /// /// Read uint array diff from the stream. /// /// The array created from the diff and the current version. /// The buffer containing the old version or null. /// The known length or -1 if unknown public uint[] ReadUIntArrayDiff(uint[] readTo = null, long knownLength = -1) { if (m_NetworkSource == null) throw new InvalidOperationException($"Cannot read bits on a non-{nameof(NetworkBuffer)} stream"); if (knownLength < 0) knownLength = (long)ReadUInt64Packed(); uint[] writeTo = readTo == null || readTo.LongLength != knownLength ? new uint[knownLength] : readTo; ulong dBlockStart = m_NetworkSource.BitPosition + (ulong)(readTo == null ? 0 : Math.Min(knownLength, readTo.LongLength)); ulong mapStart; long readToLength = readTo?.LongLength ?? 0; for (long i = 0; i < knownLength; ++i) { if (i >= readToLength || ReadBit()) { #if ARRAY_WRITE_PREMAP // Move to data section mapStart = m_NetworkSource.BitPosition; m_NetworkSource.BitPosition = dBlockStart; #endif // Read datum writeTo[i] = ReadUInt32(); #if ARRAY_WRITE_PREMAP dBlockStart = m_NetworkSource.BitPosition; // Return to mapping section m_NetworkSource.BitPosition = mapStart; #endif } else if (i < readTo.LongLength) writeTo[i] = readTo[i]; } m_NetworkSource.BitPosition = dBlockStart; return writeTo; } /// /// Read long array from the stream. /// /// The array read from the stream. /// The buffer to read into or null to create a new array /// The known length or -1 if unknown public long[] ReadLongArray(long[] readTo = null, long knownLength = -1) { if (knownLength < 0) knownLength = (long)ReadUInt64Packed(); if (readTo == null || readTo.LongLength != knownLength) readTo = new long[knownLength]; for (long i = 0; i < knownLength; ++i) readTo[i] = ReadInt64(); return readTo; } /// /// Read long array in a packed format from the stream. /// /// The array read from the stream. /// The buffer to read into or null to create a new array /// The known length or -1 if unknown public long[] ReadLongArrayPacked(long[] readTo = null, long knownLength = -1) { if (knownLength < 0) knownLength = (long)ReadUInt64Packed(); if (readTo == null || readTo.LongLength != knownLength) readTo = new long[knownLength]; for (long i = 0; i < knownLength; ++i) readTo[i] = ReadInt64Packed(); return readTo; } /// /// Read long array diff from the stream. /// /// The array created from the diff and the current version. /// The buffer containing the old version or null. /// The known length or -1 if unknown public long[] ReadLongArrayDiff(long[] readTo = null, long knownLength = -1) { if (m_NetworkSource == null) throw new InvalidOperationException($"Cannot read bits on a non-{nameof(NetworkBuffer)} stream"); if (knownLength < 0) knownLength = (long)ReadUInt64Packed(); long[] writeTo = readTo == null || readTo.LongLength != knownLength ? new long[knownLength] : readTo; ulong dBlockStart = m_NetworkSource.BitPosition + (ulong)(readTo == null ? 0 : Math.Min(knownLength, readTo.LongLength)); ulong mapStart; long readToLength = readTo?.LongLength ?? 0; for (long i = 0; i < knownLength; ++i) { if (i >= readToLength || ReadBit()) { #if ARRAY_WRITE_PREMAP // Move to data section mapStart = m_NetworkSource.BitPosition; m_NetworkSource.BitPosition = dBlockStart; #endif // Read datum writeTo[i] = ReadInt64(); #if ARRAY_WRITE_PREMAP dBlockStart = m_NetworkSource.BitPosition; // Return to mapping section m_NetworkSource.BitPosition = mapStart; #endif } else if (i < readTo.LongLength) writeTo[i] = readTo[i]; } m_NetworkSource.BitPosition = dBlockStart; return writeTo; } /// /// Read long array diff in a packed format from the stream. /// /// The array created from the diff and the current version. /// The buffer containing the old version or null. /// The known length or -1 if unknown public long[] ReadLongArrayPackedDiff(long[] readTo = null, long knownLength = -1) { if (m_NetworkSource == null) throw new InvalidOperationException($"Cannot read bits on a non-{nameof(NetworkBuffer)} stream"); if (knownLength < 0) knownLength = (long)ReadUInt64Packed(); long[] writeTo = readTo == null || readTo.LongLength != knownLength ? new long[knownLength] : readTo; ulong data = m_NetworkSource.BitPosition + (ulong)(readTo == null ? 0 : Math.Min(knownLength, readTo.LongLength)); ulong rset; long readToLength = readTo?.LongLength ?? 0; for (long i = 0; i < knownLength; ++i) { if (i >= readToLength || ReadBit()) { #if ARRAY_WRITE_PREMAP // Move to data section rset = m_NetworkSource.BitPosition; m_NetworkSource.BitPosition = data; #endif // Read datum writeTo[i] = ReadInt64Packed(); #if ARRAY_WRITE_PREMAP // Return to mapping section data = m_NetworkSource.BitPosition; m_NetworkSource.BitPosition = rset; #endif } else if (i < readTo.LongLength) writeTo[i] = readTo[i]; } m_NetworkSource.BitPosition = data; return writeTo; } /// /// Read ulong array from the stream. /// /// The array read from the stream. /// The buffer to read into or null to create a new array /// The known length or -1 if unknown public ulong[] ReadULongArray(ulong[] readTo = null, long knownLength = -1) { if (knownLength < 0) knownLength = (long)ReadUInt64Packed(); if (readTo == null || readTo.LongLength != knownLength) readTo = new ulong[knownLength]; for (long i = 0; i < knownLength; ++i) readTo[i] = ReadUInt64(); return readTo; } /// /// Read ulong array in a packed format from the stream. /// /// The array read from the stream. /// The buffer to read into or null to create a new array /// The known length or -1 if unknown public ulong[] ReadULongArrayPacked(ulong[] readTo = null, long knownLength = -1) { if (knownLength < 0) knownLength = (long)ReadUInt64Packed(); if (readTo == null || readTo.LongLength != knownLength) readTo = new ulong[knownLength]; for (long i = 0; i < knownLength; ++i) readTo[i] = ReadUInt64Packed(); return readTo; } /// /// Read ulong array diff from the stream. /// /// The array created from the diff and the current version. /// The buffer containing the old version or null. /// The known length or -1 if unknown public ulong[] ReadULongArrayDiff(ulong[] readTo = null, long knownLength = -1) { if (m_NetworkSource == null) throw new InvalidOperationException($"Cannot read bits on a non-{nameof(NetworkBuffer)} stream"); if (knownLength < 0) knownLength = (long)ReadUInt64Packed(); ulong[] writeTo = readTo == null || readTo.LongLength != knownLength ? new ulong[knownLength] : readTo; ulong dBlockStart = m_NetworkSource.BitPosition + (ulong)(readTo == null ? 0 : Math.Min(knownLength, readTo.LongLength)); ulong mapStart; long readToLength = readTo?.LongLength ?? 0; for (long i = 0; i < knownLength; ++i) { if (i >= readToLength || ReadBit()) { #if ARRAY_WRITE_PREMAP // Move to data section mapStart = m_NetworkSource.BitPosition; m_NetworkSource.BitPosition = dBlockStart; #endif // Read datum writeTo[i] = ReadUInt64(); #if ARRAY_WRITE_PREMAP dBlockStart = m_NetworkSource.BitPosition; // Return to mapping section m_NetworkSource.BitPosition = mapStart; #endif } else if (i < readTo.LongLength) writeTo[i] = readTo[i]; } m_NetworkSource.BitPosition = dBlockStart; return writeTo; } /// /// Read ulong array diff in a packed format from the stream. /// /// The array created from the diff and the current version. /// The buffer containing the old version or null. /// The known length or -1 if unknown public ulong[] ReadULongArrayPackedDiff(ulong[] readTo = null, long knownLength = -1) { if (m_NetworkSource == null) throw new InvalidOperationException($"Cannot read bits on a non-{nameof(NetworkBuffer)} stream"); if (knownLength < 0) knownLength = (long)ReadUInt64Packed(); ulong[] writeTo = readTo == null || readTo.LongLength != knownLength ? new ulong[knownLength] : readTo; ulong data = m_NetworkSource.BitPosition + (ulong)(readTo == null ? 0 : Math.Min(knownLength, readTo.LongLength)); ulong rset; long readToLength = readTo?.LongLength ?? 0; for (long i = 0; i < knownLength; ++i) { if (i >= readToLength || ReadBit()) { #if ARRAY_WRITE_PREMAP // Move to data section rset = m_NetworkSource.BitPosition; m_NetworkSource.BitPosition = data; #endif // Read datum writeTo[i] = ReadUInt64Packed(); #if ARRAY_WRITE_PREMAP // Return to mapping section data = m_NetworkSource.BitPosition; m_NetworkSource.BitPosition = rset; #endif } else if (i < readTo.LongLength) writeTo[i] = readTo[i]; } m_NetworkSource.BitPosition = data; return writeTo; } /// /// Read float array from the stream. /// /// The array read from the stream. /// The buffer to read into or null to create a new array /// The known length or -1 if unknown public float[] ReadFloatArray(float[] readTo = null, long knownLength = -1) { if (knownLength < 0) knownLength = (long)ReadUInt64Packed(); if (readTo == null || readTo.LongLength != knownLength) readTo = new float[knownLength]; for (long i = 0; i < knownLength; ++i) readTo[i] = ReadSingle(); return readTo; } /// /// Read float array in a packed format from the stream. /// /// The array read from the stream. /// The buffer to read into or null to create a new array /// The known length or -1 if unknown public float[] ReadFloatArrayPacked(float[] readTo = null, long knownLength = -1) { if (knownLength < 0) knownLength = (long)ReadUInt64Packed(); if (readTo == null || readTo.LongLength != knownLength) readTo = new float[knownLength]; for (long i = 0; i < knownLength; ++i) readTo[i] = ReadSinglePacked(); return readTo; } /// /// Read float array diff from the stream. /// /// The array created from the diff and the current version. /// The buffer containing the old version or null. /// The known length or -1 if unknown public float[] ReadFloatArrayDiff(float[] readTo = null, long knownLength = -1) { if (m_NetworkSource == null) throw new InvalidOperationException($"Cannot read bits on a non-{nameof(NetworkBuffer)} stream"); if (knownLength < 0) knownLength = (long)ReadUInt64Packed(); float[] writeTo = readTo == null || readTo.LongLength != knownLength ? new float[knownLength] : readTo; ulong dBlockStart = m_NetworkSource.BitPosition + (ulong)(readTo == null ? 0 : Math.Min(knownLength, readTo.LongLength)); ulong mapStart; long readToLength = readTo?.LongLength ?? 0; for (long i = 0; i < knownLength; ++i) { if (i >= readToLength || ReadBit()) { #if ARRAY_WRITE_PREMAP // Move to data section mapStart = m_NetworkSource.BitPosition; m_NetworkSource.BitPosition = dBlockStart; #endif // Read datum writeTo[i] = ReadSingle(); #if ARRAY_WRITE_PREMAP dBlockStart = m_NetworkSource.BitPosition; // Return to mapping section m_NetworkSource.BitPosition = mapStart; #endif } else if (i < readTo.LongLength) writeTo[i] = readTo[i]; } m_NetworkSource.BitPosition = dBlockStart; return writeTo; } /// /// Read float array diff in a packed format from the stream. /// /// The array created from the diff and the current version. /// The buffer containing the old version or null. /// The known length or -1 if unknown public float[] ReadFloatArrayPackedDiff(float[] readTo = null, long knownLength = -1) { if (m_NetworkSource == null) throw new InvalidOperationException($"Cannot read bits on a non-{nameof(NetworkBuffer)} stream"); if (knownLength < 0) knownLength = (long)ReadUInt64Packed(); float[] writeTo = readTo == null || readTo.LongLength != knownLength ? new float[knownLength] : readTo; ulong data = m_NetworkSource.BitPosition + (ulong)(readTo == null ? 0 : Math.Min(knownLength, readTo.LongLength)); ulong rset; long readToLength = readTo?.LongLength ?? 0; for (long i = 0; i < knownLength; ++i) { if (i >= readToLength || ReadBit()) { #if ARRAY_WRITE_PREMAP // Move to data section rset = m_NetworkSource.BitPosition; m_NetworkSource.BitPosition = data; #endif // Read datum readTo[i] = ReadSinglePacked(); #if ARRAY_WRITE_PREMAP // Return to mapping section data = m_NetworkSource.BitPosition; m_NetworkSource.BitPosition = rset; #endif } else if (i < readTo.LongLength) writeTo[i] = readTo[i]; } m_NetworkSource.BitPosition = data; return writeTo; } /// /// Read double array from the stream. /// /// The array read from the stream. /// The buffer to read into or null to create a new array /// The known length or -1 if unknown public double[] ReadDoubleArray(double[] readTo = null, long knownLength = -1) { if (knownLength < 0) knownLength = (long)ReadUInt64Packed(); if (readTo == null || readTo.LongLength != knownLength) readTo = new double[knownLength]; for (long i = 0; i < knownLength; ++i) readTo[i] = ReadDouble(); return readTo; } /// /// Read double array in a packed format from the stream. /// /// The array read from the stream. /// The buffer to read into or null to create a new array /// The known length or -1 if unknown public double[] ReadDoubleArrayPacked(double[] readTo = null, long knownLength = -1) { if (knownLength < 0) knownLength = (long)ReadUInt64Packed(); if (readTo == null || readTo.LongLength != knownLength) readTo = new double[knownLength]; for (long i = 0; i < knownLength; ++i) readTo[i] = ReadDoublePacked(); return readTo; } /// /// Read double array diff from the stream. /// /// The array created from the diff and the current version. /// The buffer containing the old version or null. /// The known length or -1 if unknown public double[] ReadDoubleArrayDiff(double[] readTo = null, long knownLength = -1) { if (m_NetworkSource == null) throw new InvalidOperationException($"Cannot read bits on a non-{nameof(NetworkBuffer)} stream"); if (knownLength < 0) knownLength = (long)ReadUInt64Packed(); double[] writeTo = readTo == null || readTo.LongLength != knownLength ? new double[knownLength] : readTo; ulong dBlockStart = m_NetworkSource.BitPosition + (ulong)(readTo == null ? 0 : Math.Min(knownLength, readTo.LongLength)); ulong mapStart; long readToLength = readTo?.LongLength ?? 0; for (long i = 0; i < knownLength; ++i) { if (i >= readToLength || ReadBit()) { #if ARRAY_WRITE_PREMAP // Move to data section mapStart = m_NetworkSource.BitPosition; m_NetworkSource.BitPosition = dBlockStart; #endif // Read datum writeTo[i] = ReadDouble(); #if ARRAY_WRITE_PREMAP dBlockStart = m_NetworkSource.BitPosition; // Return to mapping section m_NetworkSource.BitPosition = mapStart; #endif } else if (i < readTo.LongLength) writeTo[i] = readTo[i]; } m_NetworkSource.BitPosition = dBlockStart; return writeTo; } /// /// Read double array diff in a packed format from the stream. /// /// The array created from the diff and the current version. /// The buffer containing the old version or null. /// The known length or -1 if unknown public double[] ReadDoubleArrayPackedDiff(double[] readTo = null, long knownLength = -1) { if (m_NetworkSource == null) throw new InvalidOperationException($"Cannot read bits on a non-{nameof(NetworkBuffer)} stream"); if (knownLength < 0) knownLength = (long)ReadUInt64Packed(); double[] writeTo = readTo == null || readTo.LongLength != knownLength ? new double[knownLength] : readTo; ulong data = m_NetworkSource.BitPosition + (ulong)(readTo == null ? 0 : Math.Min(knownLength, readTo.LongLength)); ulong rset; long readToLength = readTo?.LongLength ?? 0; for (long i = 0; i < knownLength; ++i) { if (i >= readToLength || ReadBit()) { #if ARRAY_WRITE_PREMAP // Move to data section rset = m_NetworkSource.BitPosition; m_NetworkSource.BitPosition = data; #endif // Read datum writeTo[i] = ReadDoublePacked(); #if ARRAY_WRITE_PREMAP // Return to mapping section data = m_NetworkSource.BitPosition; m_NetworkSource.BitPosition = rset; #endif } else if (i < readTo.LongLength) writeTo[i] = readTo[i]; } m_NetworkSource.BitPosition = data; return writeTo; } } }