using System; using Unity.Collections; using UnityEngine; using UnityEngine.Assertions; using UnityEngine.Pool; namespace RebootKit.Engine.Network { public struct NetworkBufferReader : IDisposable { class ReaderHandle { public NativeSlice Data; public int Position; public bool IsBigEndian; } static readonly IObjectPool s_ReaderPool = new ObjectPool( () => new ReaderHandle(), _ => { }, handle => { handle.Data = default; handle.Position = 0; handle.IsBigEndian = false; }, _ => { }, true, 256 ); ReaderHandle m_Handle; public NetworkBufferReader(NativeSlice data, int position = 0) { Assert.IsTrue(position >= 0 && position <= data.Length, "Position must be within the bounds of the data array."); m_Handle = s_ReaderPool.Get(); m_Handle.Data = data; m_Handle.Position = position; m_Handle.IsBigEndian = !BitConverter.IsLittleEndian; } public void Dispose() { if (m_Handle != null) { s_ReaderPool.Release(m_Handle); m_Handle = null; } } public bool HasNext(int size) { return m_Handle.Position + size <= m_Handle.Data.Length; } public bool Read(out NativeArray value, int size, Allocator allocator = Allocator.Temp) { Assert.IsTrue(HasNext(size), $"Not enough data to read the requested size. Requested: {size}, Available: {m_Handle.Data.Length - m_Handle.Position}"); if (size <= 0) { value = default; return false; } value = new NativeArray(size, allocator); for (int i = 0; i < size; i++) { value[i] = m_Handle.Data[m_Handle.Position++]; } return true; } public bool Read(out NativeSlice value, int size) { if (!HasNext(size)) { value = default; return false; } value = m_Handle.Data.Slice(m_Handle.Position, size); m_Handle.Position += size; return true; } public bool Read(out byte value) { if (!HasNext(1)) { value = 0; return false; } Assert.IsTrue(HasNext(1), "Not enough data to read a byte."); value = m_Handle.Data[m_Handle.Position++]; return true; } public bool Read(out bool value) { if (!HasNext(1)) { value = false; return false; } value = m_Handle.Data[m_Handle.Position++] != 0; return true; } public bool Read(out int value) { value = 0; if (!HasNext(4)) { return false; } if (m_Handle.IsBigEndian) { value |= m_Handle.Data[m_Handle.Position++] << 24; value |= m_Handle.Data[m_Handle.Position++] << 16; value |= m_Handle.Data[m_Handle.Position++] << 8; value |= m_Handle.Data[m_Handle.Position++]; } else { value |= m_Handle.Data[m_Handle.Position++]; value |= m_Handle.Data[m_Handle.Position++] << 8; value |= m_Handle.Data[m_Handle.Position++] << 16; value |= m_Handle.Data[m_Handle.Position++] << 24; } return true; } public bool Read(out short value) { value = 0; if (!HasNext(2)) { return false; } if (m_Handle.IsBigEndian) { value |= (short) (m_Handle.Data[m_Handle.Position++] << 8); value |= (short) (m_Handle.Data[m_Handle.Position++]); } else { value |= (short) (m_Handle.Data[m_Handle.Position++]); value |= (short) (m_Handle.Data[m_Handle.Position++] << 8); } return true; } public bool Read(out ushort value) { value = 0; if (!HasNext(2)) { return false; } if (m_Handle.IsBigEndian) { value |= (ushort) (m_Handle.Data[m_Handle.Position++] << 8); value |= (ushort) (m_Handle.Data[m_Handle.Position++]); } else { value |= (ushort) (m_Handle.Data[m_Handle.Position++]); value |= (ushort) (m_Handle.Data[m_Handle.Position++] << 8); } return true; } public bool Read(out long value) { value = 0; if (!HasNext(8)) { return false; } if (m_Handle.IsBigEndian) { value |= (long) m_Handle.Data[m_Handle.Position++] << 56; value |= (long) m_Handle.Data[m_Handle.Position++] << 48; value |= (long) m_Handle.Data[m_Handle.Position++] << 40; value |= (long) m_Handle.Data[m_Handle.Position++] << 32; value |= (long) m_Handle.Data[m_Handle.Position++] << 24; value |= (long) m_Handle.Data[m_Handle.Position++] << 16; value |= (long) m_Handle.Data[m_Handle.Position++] << 8; value |= (long) m_Handle.Data[m_Handle.Position++]; } else { value |= (long) m_Handle.Data[m_Handle.Position++]; value |= (long) m_Handle.Data[m_Handle.Position++] << 8; value |= (long) m_Handle.Data[m_Handle.Position++] << 16; value |= (long) m_Handle.Data[m_Handle.Position++] << 24; value |= (long) m_Handle.Data[m_Handle.Position++] << 32; value |= (long) m_Handle.Data[m_Handle.Position++] << 40; value |= (long) m_Handle.Data[m_Handle.Position++] << 48; value |= (long) m_Handle.Data[m_Handle.Position++] << 56; } return true; } public bool Read(out ulong value) { value = 0; if (!HasNext(8)) { return false; } if (m_Handle.IsBigEndian) { value |= (ulong) m_Handle.Data[m_Handle.Position++] << 56; value |= (ulong) m_Handle.Data[m_Handle.Position++] << 48; value |= (ulong) m_Handle.Data[m_Handle.Position++] << 40; value |= (ulong) m_Handle.Data[m_Handle.Position++] << 32; value |= (ulong) m_Handle.Data[m_Handle.Position++] << 24; value |= (ulong) m_Handle.Data[m_Handle.Position++] << 16; value |= (ulong) m_Handle.Data[m_Handle.Position++] << 8; value |= (ulong) m_Handle.Data[m_Handle.Position++]; } else { value |= (ulong) m_Handle.Data[m_Handle.Position++]; value |= (ulong) m_Handle.Data[m_Handle.Position++] << 8; value |= (ulong) m_Handle.Data[m_Handle.Position++] << 16; value |= (ulong) m_Handle.Data[m_Handle.Position++] << 24; value |= (ulong) m_Handle.Data[m_Handle.Position++] << 32; value |= (ulong) m_Handle.Data[m_Handle.Position++] << 40; value |= (ulong) m_Handle.Data[m_Handle.Position++] << 48; value |= (ulong) m_Handle.Data[m_Handle.Position++] << 56; } return true; } public bool Read(out float value) { if (Read(out int intValue)) { value = System.BitConverter.Int32BitsToSingle(intValue); return true; } value = 0.0f; return false; } public bool Read(out Vector2 value) { Assert.IsTrue(HasNext(sizeof(float) * 2), "Not enough data to read a Vector2."); if (Read(out float x) && Read(out float y)) { value = new Vector2(x, y); return true; } value = Vector2.zero; return false; } public bool Read(out Vector3 value) { Assert.IsTrue(HasNext(sizeof(float) * 3), "Not enough data to read a Vector3."); if (Read(out float x) && Read(out float y) && Read(out float z)) { value = new Vector3(x, y, z); return true; } value = Vector3.zero; return false; } public bool Read(out Vector4 value) { Assert.IsTrue(HasNext(sizeof(float) * 4), "Not enough data to read a Vector4."); if (Read(out float x) && Read(out float y) && Read(out float z) && Read(out float w)) { value = new Vector4(x, y, z, w); return true; } value = Vector4.zero; return false; } public bool Read(out Quaternion value) { Assert.IsTrue(HasNext(sizeof(float) * 4), "Not enough data to read a Quaternion."); if (Read(out float x) && Read(out float y) && Read(out float z) && Read(out float w)) { value = new Quaternion(x, y, z, w); return true; } value = Quaternion.identity; return false; } public bool Read(out FixedString32Bytes value) { Assert.IsTrue(HasNext(32), "Not enough data to read a FixedString32Bytes."); NativeArray tempData = new NativeArray(32, Allocator.Temp); value = new FixedString32Bytes(); int length = 0; for (int i = 0; i < 32; i++) { Read(out byte byteValue); tempData[i] = byteValue; if (byteValue != 0) { length++; } } value.Length = length; for (int i = 0; i < length; i++) { value[i] = tempData[i]; } tempData.Dispose(); return true; } public bool Read(out FixedString64Bytes value) { Assert.IsTrue(HasNext(64), "Not enough data to read a FixedString64Bytes."); NativeArray tempData = new NativeArray(64, Allocator.Temp); value = new FixedString64Bytes(); int length = 0; for (int i = 0; i < 64; i++) { Read(out byte byteValue); tempData[i] = byteValue; if (byteValue != 0) { length++; } } value.Length = length; for (int i = 0; i < length; i++) { value[i] = tempData[i]; } tempData.Dispose(); return true; } public bool Read(out FixedString128Bytes value) { Assert.IsTrue(HasNext(128), "Not enough data to read a FixedString128Bytes."); NativeArray tempData = new NativeArray(128, Allocator.Temp); value = new FixedString128Bytes(); int length = 0; for (int i = 0; i < 128; i++) { Read(out byte byteValue); tempData[i] = byteValue; if (byteValue != 0) { length++; } } value.Length = length; for (int i = 0; i < length; i++) { value[i] = tempData[i]; } tempData.Dispose(); return true; } public bool Read(out FixedString512Bytes value) { Assert.IsTrue(HasNext(512), "Not enough data to read a FixedString512Bytes."); NativeArray tempData = new NativeArray(512, Allocator.Temp); value = new FixedString512Bytes(); int length = 0; for (int i = 0; i < 512; i++) { Read(out byte byteValue); tempData[i] = byteValue; if (byteValue != 0) { length++; } } value.Length = length; for (int i = 0; i < length; i++) { value[i] = tempData[i]; } tempData.Dispose(); return true; } public bool Read(out FixedString4096Bytes value) { Assert.IsTrue(HasNext(4096), "Not enough data to read a FixedString4096Bytes."); NativeArray tempData = new NativeArray(4096, Allocator.Temp); value = new FixedString4096Bytes(); int length = 0; for (int i = 0; i < 4096; i++) { Read(out byte byteValue); tempData[i] = byteValue; if (byteValue != 0) { length++; } } value.Length = length; for (int i = 0; i < length; i++) { value[i] = tempData[i]; } tempData.Dispose(); return true; } } }