polygone/Library/PackageCache/com.unity.multiplayer.mlapi@3e3aef6aa0/Runtime/Serialization/NetworkReader.cs
2021-08-02 05:44:37 -04:00

1667 lines
No EOL
78 KiB
C#

#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
{
/// <summary>
/// A BinaryReader that can do bit wise manipulation when backed by a NetworkBuffer
/// </summary>
public class NetworkReader
{
private Stream m_Source;
private NetworkBuffer m_NetworkSource;
/// <summary>
/// Creates a new NetworkReader backed by a given stream
/// </summary>
/// <param name="stream">The stream to read from</param>
public NetworkReader(Stream stream)
{
m_Source = stream;
m_NetworkSource = stream as NetworkBuffer;
}
/// <summary>
/// Changes the underlying stream the reader is reading from
/// </summary>
/// <param name="stream">The stream to read from</param>
public void SetStream(Stream stream)
{
m_Source = stream;
m_NetworkSource = stream as NetworkBuffer;
}
/// <summary>
/// Reads a single byte
/// </summary>
/// <returns>The byte read as an integer</returns>
public int ReadByte() => m_Source.ReadByte();
/// <summary>
/// Reads a byte
/// </summary>
/// <returns>The byte read</returns>
public byte ReadByteDirect() => (byte)m_Source.ReadByte();
/// <summary>
/// Reads a single bit
/// </summary>
/// <returns>The bit read</returns>
public bool ReadBit()
{
if (m_NetworkSource == null)
{
throw new InvalidOperationException($"Cannot read bits on a non-{nameof(NetworkBuffer)} stream");
}
return m_NetworkSource.ReadBit();
}
/// <summary>
/// Reads a single bit
/// </summary>
/// <returns>The bit read</returns>
public bool ReadBool()
{
if (m_NetworkSource == null)
{
return m_Source.ReadByte() != 0;
}
// return ReadBit(); // old (buggy)
return ReadByte() != 0; // new (hotfix)
}
/// <summary>
/// Skips pad bits and aligns the position to the next byte
/// </summary>
public void SkipPadBits()
{
if (m_NetworkSource == null)
{
throw new InvalidOperationException($"Cannot read bits on a non-{nameof(NetworkBuffer)} stream");
}
while (!m_NetworkSource.BitAligned) ReadBit();
}
/// <summary>
/// Reads a single boxed object of a given type in a packed format
/// </summary>
/// <param name="type">The type to read</param>
/// <returns>Returns the boxed read object</returns>
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}");
}
/// <summary>
/// Read a single-precision floating point value from the stream.
/// </summary>
/// <returns>The read value</returns>
public float ReadSingle() => new UIntFloat { UIntValue = ReadUInt32() }.FloatValue;
/// <summary>
/// Read a double-precision floating point value from the stream.
/// </summary>
/// <returns>The read value</returns>
public double ReadDouble() => new UIntFloat { ULongValue = ReadUInt64() }.DoubleValue;
/// <summary>
/// Read a single-precision floating point value from the stream from a varint
/// </summary>
/// <returns>The read value</returns>
public float ReadSinglePacked() => new UIntFloat { UIntValue = ReadUInt32Packed() }.FloatValue;
/// <summary>
/// Read a double-precision floating point value from the stream as a varint
/// </summary>
/// <returns>The read value</returns>
public double ReadDoublePacked() => new UIntFloat { ULongValue = ReadUInt64Packed() }.DoubleValue;
/// <summary>
/// Read a Vector2 from the stream.
/// </summary>
/// <returns>The Vector2 read from the stream.</returns>
public Vector2 ReadVector2() => new Vector2(ReadSingle(), ReadSingle());
/// <summary>
/// Read a Vector2 from the stream.
/// </summary>
/// <returns>The Vector2 read from the stream.</returns>
public Vector2 ReadVector2Packed() => new Vector2(ReadSinglePacked(), ReadSinglePacked());
/// <summary>
/// Read a Vector3 from the stream.
/// </summary>
/// <returns>The Vector3 read from the stream.</returns>
public Vector3 ReadVector3() => new Vector3(ReadSingle(), ReadSingle(), ReadSingle());
/// <summary>
/// Read a Vector3 from the stream.
/// </summary>
/// <returns>The Vector3 read from the stream.</returns>
public Vector3 ReadVector3Packed() => new Vector3(ReadSinglePacked(), ReadSinglePacked(), ReadSinglePacked());
/// <summary>
/// Read a Vector4 from the stream.
/// </summary>
/// <returns>The Vector4 read from the stream.</returns>
public Vector4 ReadVector4() => new Vector4(ReadSingle(), ReadSingle(), ReadSingle(), ReadSingle());
/// <summary>
/// Read a Vector4 from the stream.
/// </summary>
/// <returns>The Vector4 read from the stream.</returns>
public Vector4 ReadVector4Packed() => new Vector4(ReadSinglePacked(), ReadSinglePacked(), ReadSinglePacked(), ReadSinglePacked());
/// <summary>
/// Read a Color from the stream.
/// </summary>
/// <returns>The Color read from the stream.</returns>
public Color ReadColor() => new Color(ReadSingle(), ReadSingle(), ReadSingle(), ReadSingle());
/// <summary>
/// Read a Color from the stream.
/// </summary>
/// <returns>The Color read from the stream.</returns>
public Color ReadColorPacked() => new Color(ReadSinglePacked(), ReadSinglePacked(), ReadSinglePacked(), ReadSinglePacked());
/// <summary>
/// Read a Color32 from the stream.
/// </summary>
/// <returns>The Color32 read from the stream.</returns>
public Color32 ReadColor32() => new Color32((byte)ReadByte(), (byte)ReadByte(), (byte)ReadByte(), (byte)ReadByte());
/// <summary>
/// Read a Ray from the stream.
/// </summary>
/// <returns>The Ray read from the stream.</returns>
public Ray ReadRay() => new Ray(ReadVector3(), ReadVector3());
/// <summary>
/// Read a Ray from the stream.
/// </summary>
/// <returns>The Ray read from the stream.</returns>
public Ray ReadRayPacked() => new Ray(ReadVector3Packed(), ReadVector3Packed());
/// <summary>
/// Read a Ray2D from the stream.
/// </summary>
/// <returns>The Ray2D read from the stream.</returns>
public Ray2D ReadRay2D() => new Ray2D(ReadVector2(), ReadVector2());
/// <summary>
/// Read a Ray2D from the stream.
/// </summary>
/// <returns>The Ray2D read from the stream.</returns>
public Ray2D ReadRay2DPacked() => new Ray2D(ReadVector2Packed(), ReadVector2Packed());
/// <summary>
/// Read a single-precision floating point value from the stream. The value is between (inclusive) the minValue and maxValue.
/// </summary>
/// <param name="minValue">Minimum value that this value could be</param>
/// <param name="maxValue">Maximum possible value that this could be</param>
/// <param name="bytes">How many bytes the compressed value occupies. Must be between 1 and 4 (inclusive)</param>
/// <returns>The read value</returns>
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;
}
/// <summary>
/// read a double-precision floating point value from the stream. The value is between (inclusive) the minValue and maxValue.
/// </summary>
/// <param name="minValue">Minimum value that this value could be</param>
/// <param name="maxValue">Maximum possible value that this could be</param>
/// <param name="bytes">How many bytes the compressed value occupies. Must be between 1 and 8 (inclusive)</param>
/// <returns>The read value</returns>
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;
}
/// <summary>
/// Reads the rotation from the stream
/// </summary>
/// <returns>The rotation read from the stream</returns>
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);
}
/// <summary>
/// Reads the rotation from the stream
/// </summary>
/// <returns>The rotation read from the stream</returns>
public Quaternion ReadRotation()
{
float x = ReadSingle();
float y = ReadSingle();
float z = ReadSingle();
float w = ReadSingle();
return new Quaternion(x, y, z, w);
}
/// <summary>
/// Read a certain amount of bits from the stream.
/// </summary>
/// <param name="bitCount">How many bits to read. Minimum 0, maximum 8.</param>
/// <returns>The bits that were read</returns>
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;
}
/// <summary>
/// Read a certain amount of bits from the stream.
/// </summary>
/// <param name="bitCount">How many bits to read. Minimum 0, maximum 64.</param>
/// <returns>The bits that were read</returns>
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;
}
/// <summary>
/// Read a nibble (4 bits) from the stream.
/// </summary>
/// <param name="asUpper">Whether or not the nibble should be left-shifted by 4 bits</param>
/// <returns>The nibble that was read</returns>
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
/// <summary>
/// Read a nibble (4 bits) from the stream.
/// </summary>
/// <returns>The nibble that was read</returns>
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)
);
}
/// <summary>
/// Reads a signed byte
/// </summary>
/// <returns>Value read from stream.</returns>
public sbyte ReadSByte() => (sbyte)ReadByte();
/// <summary>
/// Read an unsigned short (UInt16) from the stream.
/// </summary>
/// <returns>Value read from stream.</returns>
public ushort ReadUInt16() => (ushort)(ReadByte() | (ReadByte() << 8));
/// <summary>
/// Read a signed short (Int16) from the stream.
/// </summary>
/// <returns>Value read from stream.</returns>
public short ReadInt16() => (short)ReadUInt16();
/// <summary>
/// Read a single character from the stream
/// </summary>
/// <returns>Value read from stream.</returns>
public char ReadChar() => (char)ReadUInt16();
/// <summary>
/// Read an unsigned int (UInt32) from the stream.
/// </summary>
/// <returns>Value read from stream.</returns>
public uint ReadUInt32() => (uint)(ReadByte() | (ReadByte() << 8) | (ReadByte() << 16) | (ReadByte() << 24));
/// <summary>
/// Read a signed int (Int32) from the stream.
/// </summary>
/// <returns>Value read from stream.</returns>
public int ReadInt32() => (int)ReadUInt32();
/// <summary>
/// Read an unsigned long (UInt64) from the stream.
/// </summary>
/// <returns>Value read from stream.</returns>
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)
);
/// <summary>
/// Read a signed long (Int64) from the stream.
/// </summary>
/// <returns>Value read from stream.</returns>
public long ReadInt64() => (long)ReadUInt64();
/// <summary>
/// Read a ZigZag encoded varint signed short (Int16) from the stream.
/// </summary>
/// <returns>Decoded un-varinted value.</returns>
public short ReadInt16Packed() => (short)Arithmetic.ZigZagDecode(ReadUInt64Packed());
/// <summary>
/// Read a varint unsigned short (UInt16) from the stream.
/// </summary>
/// <returns>Un-varinted value.</returns>
public ushort ReadUInt16Packed() => (ushort)ReadUInt64Packed();
/// <summary>
/// Read a varint two-byte character from the stream.
/// </summary>
/// <returns>Un-varinted value.</returns>
public char ReadCharPacked() => (char)ReadUInt16Packed();
/// <summary>
/// Read a ZigZag encoded varint signed int (Int32) from the stream.
/// </summary>
/// <returns>Decoded un-varinted value.</returns>
public int ReadInt32Packed() => (int)Arithmetic.ZigZagDecode(ReadUInt64Packed());
/// <summary>
/// Read a varint unsigned int (UInt32) from the stream.
/// </summary>
/// <returns>Un-varinted value.</returns>
public uint ReadUInt32Packed() => (uint)ReadUInt64Packed();
/// <summary>
/// Read a ZigZag encoded varint signed long(Int64) from the stream.
/// </summary>
/// <returns>Decoded un-varinted value.</returns>
public long ReadInt64Packed() => Arithmetic.ZigZagDecode(ReadUInt64Packed());
/// <summary>
/// Read a varint unsigned long (UInt64) from the stream.
/// </summary>
/// <returns>Un-varinted value.</returns>
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
/// <summary>
/// Read a string from the stream.
/// </summary>
/// <returns>The string that was read.</returns>
/// <param name="oneByteChars">If set to <c>true</c> one byte chars are used and only ASCII is supported.</param>
public StringBuilder ReadString(bool oneByteChars) => ReadString(null, oneByteChars);
/// <summary>
/// Read a string from the stream.
/// </summary>
/// <returns>The string that was read.</returns>
/// <param name="builder">The builder to read the values into or null to use a new builder.</param>
/// <param name="oneByteChars">If set to <c>true</c> one byte chars are used and only ASCII is supported.</param>
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;
}
/// <summary>
/// Read string encoded as a varint from the stream.
/// </summary>
/// <returns>The string that was read.</returns>
/// <param name="builder">The builder to read the string into or null to use a new builder</param>
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();
}
/// <summary>
/// Read string diff from the stream.
/// </summary>
/// <returns>The string based on the diff and the old version.</returns>
/// <param name="compare">The version to compare the diff to.</param>
/// <param name="oneByteChars">If set to <c>true</c> one byte chars are used and only ASCII is supported.</param>
public StringBuilder ReadStringDiff(string compare, bool oneByteChars = false) => ReadStringDiff(null, compare, oneByteChars);
/// <summary>
/// Read string diff from the stream.
/// </summary>
/// <returns>The string based on the diff and the old version</returns>
/// <param name="builder">The builder to read the string into or null to use a new builder.</param>
/// <param name="compare">The version to compare the diff to.</param>
/// <param name="oneByteChars">If set to <c>true</c> one byte chars are used and only ASCII is supported.</param>
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;
}
/// <summary>
/// Read string diff from the stream.
/// </summary>
/// <returns>The string based on the diff and the old version.</returns>
/// <param name="compareAndBuffer">The builder containing the current version and that will also be used as the output buffer.</param>
/// <param name="oneByteChars">If set to <c>true</c> one byte chars will be used and only ASCII will be supported.</param>
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;
}
/// <summary>
/// Read string diff encoded as varints from the stream.
/// </summary>
/// <returns>The string based on the diff and the old version.</returns>
/// <param name="compare">The version to compare the diff to.</param>
public StringBuilder ReadStringPackedDiff(string compare) => ReadStringPackedDiff(null, compare);
/// <summary>
/// Read string diff encoded as varints from the stream.
/// </summary>
/// <returns>The string based on the diff and the old version</returns>
/// <param name="builder">The builder to read the string into or null to use a new builder.</param>
/// <param name="compare">The version to compare the diff to.</param>
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;
}
/// <summary>
/// Read string diff encoded as varints from the stream.
/// </summary>
/// <returns>The string based on the diff and the old version.</returns>
/// <param name="compareAndBuffer">The builder containing the current version and that will also be used as the output buffer.</param>
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;
}
/// <summary>
/// Read byte array into an optional buffer from the stream.
/// </summary>
/// <returns>The byte array that has been read.</returns>
/// <param name="readTo">The array to read into. If the array is not large enough or if it's null. A new array is created.</param>
/// <param name="knownLength">The length of the array if it's known. Otherwise -1</param>
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;
}
/// <summary>
/// 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.
/// </summary>
/// <param name="sizeToCopy">size to copy</param>
/// <param name="offset">offset within the stream buffer to start copying</param>
/// <returns>ArraySegment&lt;byte&gt;</returns>
public ArraySegment<byte> 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<byte>(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})! <returning empty array segment>");
return new ArraySegment<byte>();
}
//Return the request array segment
return new ArraySegment<byte>(m_NetworkSource.GetBuffer(), Offset, CopySize);
}
Debug.LogError($"{nameof(CopySize)} ({CopySize}) is zero or less! <returning empty array segment>");
}
else
{
Debug.LogError("Reader has no stream assigned to it! <returning empty array segment>");
}
return new ArraySegment<byte>();
}
/// <summary>
/// Read byte array diff into an optional buffer from the stream.
/// </summary>
/// <returns>The byte array created from the diff and original.</returns>
/// <param name="readTo">The buffer containing the old version or null.</param>
/// <param name="knownLength">The length of the array if it's known. Otherwise -1</param>
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;
}
/// <summary>
/// Read short array from the stream.
/// </summary>
/// <returns>The array read from the stream.</returns>
/// <param name="readTo">The buffer to read into or null to create a new array</param>
/// <param name="knownLength">The known length or -1 if unknown</param>
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;
}
/// <summary>
/// Read short array in a packed format from the stream.
/// </summary>
/// <returns>The array read from the stream.</returns>
/// <param name="readTo">The buffer to read into or null to create a new array</param>
/// <param name="knownLength">The known length or -1 if unknown</param>
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;
}
/// <summary>
/// Read short array diff from the stream.
/// </summary>
/// <returns>The array created from the diff and the current version.</returns>
/// <param name="readTo">The buffer containing the old version or null.</param>
/// <param name="knownLength">The known length or -1 if unknown</param>
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;
}
/// <summary>
/// Read short array diff in a packed format from the stream.
/// </summary>
/// <returns>The array created from the diff and the current version.</returns>
/// <param name="readTo">The buffer containing the old version or null.</param>
/// <param name="knownLength">The known length or -1 if unknown</param>
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;
}
/// <summary>
/// Read ushort array from the stream.
/// </summary>
/// <returns>The array read from the stream.</returns>
/// <param name="readTo">The buffer to read into or null to create a new array</param>
/// <param name="knownLength">The known length or -1 if unknown</param>
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;
}
/// <summary>
/// Read ushort array in a packed format from the stream.
/// </summary>
/// <returns>The array read from the stream.</returns>
/// <param name="readTo">The buffer to read into or null to create a new array</param>
/// <param name="knownLength">The known length or -1 if unknown</param>
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;
}
/// <summary>
/// Read ushort array diff from the stream.
/// </summary>
/// <returns>The array created from the diff and the current version.</returns>
/// <param name="readTo">The buffer containing the old version or null.</param>
/// <param name="knownLength">The known length or -1 if unknown</param>
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;
}
/// <summary>
/// Read ushort array diff in a packed format from the stream.
/// </summary>
/// <returns>The array created from the diff and the current version.</returns>
/// <param name="readTo">The buffer containing the old version or null.</param>
/// <param name="knownLength">The known length or -1 if unknown</param>
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;
}
/// <summary>
/// Read int array from the stream.
/// </summary>
/// <returns>The array read from the stream.</returns>
/// <param name="readTo">The buffer to read into or null to create a new array</param>
/// <param name="knownLength">The known length or -1 if unknown</param>
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;
}
/// <summary>
/// Read int array in a packed format from the stream.
/// </summary>
/// <returns>The array read from the stream.</returns>
/// <param name="readTo">The buffer to read into or null to create a new array</param>
/// <param name="knownLength">The known length or -1 if unknown</param>
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;
}
/// <summary>
/// Read int array diff from the stream.
/// </summary>
/// <returns>The array created from the diff and the current version.</returns>
/// <param name="readTo">The buffer containing the old version or null.</param>
/// <param name="knownLength">The known length or -1 if unknown</param>
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;
}
/// <summary>
/// Read int array diff in a packed format from the stream.
/// </summary>
/// <returns>The array created from the diff and the current version.</returns>
/// <param name="readTo">The buffer containing the old version or null.</param>
/// <param name="knownLength">The known length or -1 if unknown</param>
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;
}
/// <summary>
/// Read uint array from the stream.
/// </summary>
/// <returns>The array read from the stream.</returns>
/// <param name="readTo">The buffer to read into or null to create a new array</param>
/// <param name="knownLength">The known length or -1 if unknown</param>
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;
}
/// <summary>
/// Read uint array in a packed format from the stream.
/// </summary>
/// <returns>The array read from the stream.</returns>
/// <param name="readTo">The buffer to read into or null to create a new array</param>
/// <param name="knownLength">The known length or -1 if unknown</param>
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;
}
/// <summary>
/// Read uint array diff from the stream.
/// </summary>
/// <returns>The array created from the diff and the current version.</returns>
/// <param name="readTo">The buffer containing the old version or null.</param>
/// <param name="knownLength">The known length or -1 if unknown</param>
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;
}
/// <summary>
/// Read long array from the stream.
/// </summary>
/// <returns>The array read from the stream.</returns>
/// <param name="readTo">The buffer to read into or null to create a new array</param>
/// <param name="knownLength">The known length or -1 if unknown</param>
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;
}
/// <summary>
/// Read long array in a packed format from the stream.
/// </summary>
/// <returns>The array read from the stream.</returns>
/// <param name="readTo">The buffer to read into or null to create a new array</param>
/// <param name="knownLength">The known length or -1 if unknown</param>
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;
}
/// <summary>
/// Read long array diff from the stream.
/// </summary>
/// <returns>The array created from the diff and the current version.</returns>
/// <param name="readTo">The buffer containing the old version or null.</param>
/// <param name="knownLength">The known length or -1 if unknown</param>
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;
}
/// <summary>
/// Read long array diff in a packed format from the stream.
/// </summary>
/// <returns>The array created from the diff and the current version.</returns>
/// <param name="readTo">The buffer containing the old version or null.</param>
/// <param name="knownLength">The known length or -1 if unknown</param>
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;
}
/// <summary>
/// Read ulong array from the stream.
/// </summary>
/// <returns>The array read from the stream.</returns>
/// <param name="readTo">The buffer to read into or null to create a new array</param>
/// <param name="knownLength">The known length or -1 if unknown</param>
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;
}
/// <summary>
/// Read ulong array in a packed format from the stream.
/// </summary>
/// <returns>The array read from the stream.</returns>
/// <param name="readTo">The buffer to read into or null to create a new array</param>
/// <param name="knownLength">The known length or -1 if unknown</param>
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;
}
/// <summary>
/// Read ulong array diff from the stream.
/// </summary>
/// <returns>The array created from the diff and the current version.</returns>
/// <param name="readTo">The buffer containing the old version or null.</param>
/// <param name="knownLength">The known length or -1 if unknown</param>
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;
}
/// <summary>
/// Read ulong array diff in a packed format from the stream.
/// </summary>
/// <returns>The array created from the diff and the current version.</returns>
/// <param name="readTo">The buffer containing the old version or null.</param>
/// <param name="knownLength">The known length or -1 if unknown</param>
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;
}
/// <summary>
/// Read float array from the stream.
/// </summary>
/// <returns>The array read from the stream.</returns>
/// <param name="readTo">The buffer to read into or null to create a new array</param>
/// <param name="knownLength">The known length or -1 if unknown</param>
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;
}
/// <summary>
/// Read float array in a packed format from the stream.
/// </summary>
/// <returns>The array read from the stream.</returns>
/// <param name="readTo">The buffer to read into or null to create a new array</param>
/// <param name="knownLength">The known length or -1 if unknown</param>
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;
}
/// <summary>
/// Read float array diff from the stream.
/// </summary>
/// <returns>The array created from the diff and the current version.</returns>
/// <param name="readTo">The buffer containing the old version or null.</param>
/// <param name="knownLength">The known length or -1 if unknown</param>
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;
}
/// <summary>
/// Read float array diff in a packed format from the stream.
/// </summary>
/// <returns>The array created from the diff and the current version.</returns>
/// <param name="readTo">The buffer containing the old version or null.</param>
/// <param name="knownLength">The known length or -1 if unknown</param>
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;
}
/// <summary>
/// Read double array from the stream.
/// </summary>
/// <returns>The array read from the stream.</returns>
/// <param name="readTo">The buffer to read into or null to create a new array</param>
/// <param name="knownLength">The known length or -1 if unknown</param>
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;
}
/// <summary>
/// Read double array in a packed format from the stream.
/// </summary>
/// <returns>The array read from the stream.</returns>
/// <param name="readTo">The buffer to read into or null to create a new array</param>
/// <param name="knownLength">The known length or -1 if unknown</param>
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;
}
/// <summary>
/// Read double array diff from the stream.
/// </summary>
/// <returns>The array created from the diff and the current version.</returns>
/// <param name="readTo">The buffer containing the old version or null.</param>
/// <param name="knownLength">The known length or -1 if unknown</param>
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;
}
/// <summary>
/// Read double array diff in a packed format from the stream.
/// </summary>
/// <returns>The array created from the diff and the current version.</returns>
/// <param name="readTo">The buffer containing the old version or null.</param>
/// <param name="knownLength">The known length or -1 if unknown</param>
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;
}
}
}