using System;
-using System.IO;
-using System.Linq;
+using System.Buffers.Binary;
+using System.Numerics;
using System.Runtime.CompilerServices;
using System.Text;
-
-namespace Hazel
+using Impostor.Api;
+using Impostor.Api.Games;
+using Impostor.Api.Net.Inner;
+using Impostor.Api.Net.Messages;
+using Impostor.Api.Unity;
+using Microsoft.Extensions.ObjectPool;
+
+namespace Impostor.Hazel
{
- public class MessageReader : IRecyclable
+ public class MessageReader : IMessageReader
{
- public static readonly ObjectPool<MessageReader> ReaderPool = new ObjectPool<MessageReader>(() => new MessageReader());
-
- public byte[] Buffer;
- public byte Tag;
+ private readonly ObjectPool<MessageReader> _pool;
+ private bool _inUse;
- public int Length;
- public int Offset;
-
- public int BytesRemaining => this.Length - this.Position;
-
- private MessageReader Parent;
-
- public int Position
+ internal MessageReader(ObjectPool<MessageReader> pool)
{
- get { return this._position; }
- set
- {
- this._position = value;
- this.readHead = value + Offset;
- }
+ _pool = pool;
}
- private int _position;
- private int readHead;
+ public byte[] Buffer { get; private set; }
- public static MessageReader GetSized(int minSize)
- {
- var output = ReaderPool.GetObject();
+ public int Offset { get; internal set; }
- if (output.Buffer == null || output.Buffer.Length < minSize)
- {
- output.Buffer = new byte[minSize];
- }
- else
- {
- Array.Clear(output.Buffer, 0, output.Buffer.Length);
- }
+ public int Position { get; internal set; }
- output.Offset = 0;
- output.Position = 0;
- output.Tag = byte.MaxValue;
- return output;
- }
-
- public static MessageReader Get(byte[] buffer)
- {
- var output = ReaderPool.GetObject();
-
- output.Buffer = buffer;
- output.Offset = 0;
- output.Position = 0;
- output.Length = buffer.Length;
- output.Tag = byte.MaxValue;
-
- return output;
- }
-
- public static MessageReader CopyMessageIntoParent(MessageReader source)
- {
- var output = MessageReader.GetSized(source.Length + 3);
- System.Buffer.BlockCopy(source.Buffer, source.Offset - 3, output.Buffer, 0, source.Length + 3);
+ public int Length { get; internal set; }
+
+ public int BytesRemaining => this.Length - this.Position;
- output.Offset = 0;
- output.Position = 0;
- output.Length = source.Length + 3;
+ public byte Tag { get; private set; }
- return output;
- }
+ public MessageReader Parent { get; private set; }
- public static MessageReader Get(MessageReader source)
+ private int ReadPosition => Offset + Position;
+ public void Update(byte[] buffer, int offset = 0, int position = 0, int? length = null, byte tag = byte.MaxValue, MessageReader parent = null)
{
- var output = MessageReader.GetSized(source.Buffer.Length);
- System.Buffer.BlockCopy(source.Buffer, 0, output.Buffer, 0, source.Buffer.Length);
-
- output.Offset = source.Offset;
-
- output._position = source._position;
- output.readHead = source.readHead;
-
- output.Length = source.Length;
- output.Tag = source.Tag;
-
- return output;
+ _inUse = true;
+
+ Buffer = buffer;
+ Offset = offset;
+ Position = position;
+ Length = length ?? buffer.Length;
+ Tag = tag;
+ Parent = parent;
}
- public static MessageReader Get(byte[] buffer, int offset)
+ internal void Reset()
{
- // Ensure there is at least a header
- if (offset + 3 > buffer.Length) return null;
-
- var output = ReaderPool.GetObject();
-
- output.Buffer = buffer;
- output.Offset = offset;
- output.Position = 0;
-
- output.Length = output.ReadUInt16();
- output.Tag = output.ReadByte();
-
- output.Offset += 3;
- output.Position = 0;
-
- return output;
+ _inUse = false;
+
+ Tag = byte.MaxValue;
+ Buffer = null;
+ Offset = 0;
+ Position = 0;
+ Length = 0;
+ Parent = null;
}
- /// <summary>
- /// Produces a MessageReader using the parent's buffer. This MessageReader should **NOT** be recycled.
- /// </summary>
- public MessageReader ReadMessage()
+ public IMessageReader ReadMessage()
{
- // Ensure there is at least a header
- if (this.BytesRemaining < 3) throw new InvalidDataException($"ReadMessage header is longer than message length: 3 of {this.BytesRemaining}");
-
- var output = new MessageReader();
-
- output.Parent = this;
- output.Buffer = this.Buffer;
- output.Offset = this.readHead;
- output.Position = 0;
-
- output.Length = output.ReadUInt16();
- output.Tag = output.ReadByte();
-
- output.Offset += 3;
- output.Position = 0;
+ var length = ReadUInt16();
+ var tag = FastByte();
+ var pos = ReadPosition;
- if (this.BytesRemaining < output.Length + 3) throw new InvalidDataException($"Message Length at Position {this.readHead} is longer than message length: {output.Length + 3} of {this.BytesRemaining}");
+ Position += length;
- this.Position += output.Length + 3;
- return output;
+ var reader = _pool.Get();
+ reader.Update(Buffer, pos, 0, length, tag, this);
+ return reader;
}
- /// <summary>
- /// Produces a MessageReader with a new buffer. This MessageReader should be recycled.
- /// </summary>
- public MessageReader ReadMessageAsNewBuffer()
+ public void RemoveMessage(IMessageReader message)
{
- if (this.BytesRemaining < 3) throw new InvalidDataException($"ReadMessage header is longer than message length: 3 of {this.BytesRemaining}");
-
- var len = this.ReadUInt16();
- var tag = this.ReadByte();
+ if (message.Buffer != Buffer)
+ {
+ throw new ImpostorProtocolException("Tried to remove message from a message that does not have the same buffer.");
+ }
- if (this.BytesRemaining < len) throw new InvalidDataException($"Message Length at Position {this.readHead} is longer than message length: {len} of {this.BytesRemaining}");
+ // Offset of where to start removing.
+ var offsetStart = message.Offset - 3;
- var output = MessageReader.GetSized(len);
+ // Offset of where to end removing.
+ var offsetEnd = message.Offset + message.Length;
- output.Parent = this;
- Array.Copy(this.Buffer, this.readHead, output.Buffer, 0, len);
+ // The amount of bytes to copy over ourselves.
+ var lengthToCopy = message.Buffer.Length - offsetEnd;
- output.Length = len;
- output.Tag = tag;
+ System.Buffer.BlockCopy(Buffer, offsetEnd, Buffer, offsetStart, lengthToCopy);
- this.Position += output.Length;
- return output;
+ ((MessageReader) message).Parent.AdjustLength(message.Offset, message.Length + 3);
}
- public MessageWriter StartWriter()
++ public void InsertMessage(IMessageReader reader, IMessageWriter writer)
+ {
- var output = new MessageWriter(this.Buffer);
- output.Position = this.readHead;
- return output;
- }
-
- public void RemoveMessage(MessageReader reader)
- {
- var temp = MessageReader.GetSized(reader.Buffer.Length);
- try
- {
- var headerOffset = reader.Offset - 3;
- var endOfMessage = reader.Offset + reader.Length;
- var len = reader.Buffer.Length - endOfMessage;
-
- Array.Copy(reader.Buffer, endOfMessage, temp.Buffer, 0, len);
- Array.Copy(temp.Buffer, 0, this.Buffer, headerOffset, len);
-
- this.AdjustLength(reader.Offset, reader.Length + 3);
- }
- finally
- {
- temp.Recycle();
- }
- }
-
- public void InsertMessage(MessageReader reader, MessageWriter writer)
- {
- var temp = MessageReader.GetSized(reader.Buffer.Length);
- try
- {
- var headerOffset = reader.Offset - 3;
- var startOfMessage = reader.Offset;
- var len = reader.Buffer.Length - startOfMessage;
- int writerOffset = 3;
- switch (writer.SendOption)
- {
- case SendOption.Reliable:
- writerOffset = 3;
- break;
- case SendOption.None:
- writerOffset = 1;
- break;
- }
-
- //store the original buffer in temp
- Array.Copy(reader.Buffer, headerOffset, temp.Buffer, 0, len);
-
- //put the contents of writer in at headerOffset
- Array.Copy(writer.Buffer, writerOffset, this.Buffer, headerOffset, writer.Length-writerOffset);
-
- //put the original buffer in after that
- Array.Copy(temp.Buffer, 0, this.Buffer, headerOffset + (writer.Length-writerOffset), len - writer.Length);
-
- this.AdjustLength(-1 * reader.Offset , -1 * (writer.Length - writerOffset));
- }
- finally
- {
- temp.Recycle();
- }
++ throw new NotImplementedException();
+ }
+
private void AdjustLength(int offset, int amount)
{
- if (this.readHead > offset)
+ this.Length -= amount;
+
+ if (this.ReadPosition > offset)
{
this.Position -= amount;
}
return output;
}
- ulong output = (ulong)this.FastByte()
- | (ulong)this.FastByte() << 8
- | (ulong)this.FastByte() << 16
- | (ulong)this.FastByte() << 24
- | (ulong)this.FastByte() << 32
- | (ulong)this.FastByte() << 40
- | (ulong)this.FastByte() << 48
- | (ulong)this.FastByte() << 56;
-
+ public ulong ReadUInt64()
+ {
- long output = (long)this.FastByte()
- | (long)this.FastByte() << 8
- | (long)this.FastByte() << 16
- | (long)this.FastByte() << 24
- | (long)this.FastByte() << 32
- | (long)this.FastByte() << 40
- | (long)this.FastByte() << 48
- | (long)this.FastByte() << 56;
-
++ var output = BinaryPrimitives.ReadUInt64LittleEndian(Buffer.AsSpan(ReadPosition));
++ Position += sizeof(ulong);
+ return output;
+ }
+
+ public long ReadInt64()
+ {
++ var output = BinaryPrimitives.ReadInt64LittleEndian(Buffer.AsSpan(ReadPosition));
++ Position += sizeof(long);
+ return output;
+ }
+
public unsafe float ReadSingle()
{
- float output = 0;
- fixed (byte* bufPtr = &this.Buffer[this.readHead])
- {
- byte* outPtr = (byte*)&output;
-
- *outPtr = *bufPtr;
- *(outPtr + 1) = *(bufPtr + 1);
- *(outPtr + 2) = *(bufPtr + 2);
- *(outPtr + 3) = *(bufPtr + 3);
- }
-
- this.Position += 4;
+ var output = BinaryPrimitives.ReadSingleLittleEndian(Buffer.AsSpan(ReadPosition));
+ Position += sizeof(float);
return output;
}