aboutsummaryrefslogtreecommitdiff
path: root/Tools/Hazel-Networking/Hazel/MessageReader.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Tools/Hazel-Networking/Hazel/MessageReader.cs')
-rw-r--r--Tools/Hazel-Networking/Hazel/MessageReader.cs452
1 files changed, 452 insertions, 0 deletions
diff --git a/Tools/Hazel-Networking/Hazel/MessageReader.cs b/Tools/Hazel-Networking/Hazel/MessageReader.cs
new file mode 100644
index 0000000..bd3b0d8
--- /dev/null
+++ b/Tools/Hazel-Networking/Hazel/MessageReader.cs
@@ -0,0 +1,452 @@
+using System;
+using System.IO;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Text;
+
+namespace Hazel
+{
+ public class MessageReader : IRecyclable
+ {
+ public static readonly ObjectPool<MessageReader> ReaderPool = new ObjectPool<MessageReader>(() => new MessageReader());
+
+ public byte[] Buffer;
+ public byte Tag;
+
+ public int Length; // 总长度
+ public int Offset; // length和tag后面
+
+ public int BytesRemaining => this.Length - this.Position;
+
+ private MessageReader Parent;
+
+ public int Position
+ {
+ get { return this._position; }
+ set
+ {
+ this._position = value;
+ this.readHead = value + Offset;
+ }
+ }
+
+ private int _position;
+ private int readHead;
+
+ public static MessageReader GetSized(int minSize)
+ {
+ var output = ReaderPool.GetObject();
+
+ if (output.Buffer == null || output.Buffer.Length < minSize)
+ {
+ output.Buffer = new byte[minSize];
+ }
+ else
+ {
+ Array.Clear(output.Buffer, 0, output.Buffer.Length);
+ }
+
+ 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);
+
+ output.Offset = 0;
+ output.Position = 0;
+ output.Length = source.Length + 3;
+
+ return output;
+ }
+
+ public static MessageReader Get(MessageReader source)
+ {
+ 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;
+ }
+
+ public static MessageReader Get(byte[] buffer, int offset)
+ {
+ // 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;
+ }
+
+ /// <summary>
+ /// Produces a MessageReader using the parent's buffer. This MessageReader should **NOT** be recycled.
+ /// </summary>
+ public MessageReader 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;
+
+ 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}");
+
+ this.Position += output.Length + 3;
+ return output;
+ }
+
+ /// <summary>
+ /// Produces a MessageReader with a new buffer. This MessageReader should be recycled.
+ /// </summary>
+ public MessageReader ReadMessageAsNewBuffer()
+ {
+ 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 (this.BytesRemaining < len) throw new InvalidDataException($"Message Length at Position {this.readHead} is longer than message length: {len} of {this.BytesRemaining}");
+
+ var output = MessageReader.GetSized(len);
+
+ Array.Copy(this.Buffer, this.readHead, output.Buffer, 0, len);
+
+ output.Length = len;
+ output.Tag = tag;
+
+ this.Position += output.Length;
+ return output;
+ }
+
+ public MessageWriter StartWriter()
+ {
+ var output = new MessageWriter(this.Buffer);
+ output.Position = this.readHead;
+ return output;
+ }
+
+ public MessageReader Duplicate()
+ {
+ var output = GetSized(this.Length);
+ Array.Copy(this.Buffer, this.Offset, output.Buffer, 0, this.Length);
+ output.Length = this.Length;
+ output.Offset = 0;
+ output.Position = 0;
+
+ 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();
+ }
+ }
+
+ private void AdjustLength(int offset, int amount)
+ {
+ if (this.readHead > offset)
+ {
+ this.Position -= amount;
+ }
+
+ if (Parent != null)
+ {
+ var lengthOffset = this.Offset - 3;
+ var curLen = this.Buffer[lengthOffset]
+ | (this.Buffer[lengthOffset + 1] << 8);
+
+ curLen -= amount;
+ this.Length -= amount;
+
+ this.Buffer[lengthOffset] = (byte)curLen;
+ this.Buffer[lengthOffset + 1] = (byte)(this.Buffer[lengthOffset + 1] >> 8);
+
+ Parent.AdjustLength(offset, amount);
+ }
+ }
+
+ public void Recycle()
+ {
+ this.Parent = null;
+ ReaderPool.PutObject(this);
+ }
+
+ #region Read Methods
+ public bool ReadBoolean()
+ {
+ byte val = this.FastByte();
+ return val != 0;
+ }
+
+ public sbyte ReadSByte()
+ {
+ return (sbyte)this.FastByte();
+ }
+
+ public byte ReadByte()
+ {
+ return this.FastByte();
+ }
+
+ public ushort ReadUInt16()
+ {
+ ushort output =
+ (ushort)(this.FastByte()
+ | this.FastByte() << 8);
+ return output;
+ }
+
+ public short ReadInt16()
+ {
+ short output =
+ (short)(this.FastByte()
+ | this.FastByte() << 8);
+ return output;
+ }
+
+ public uint ReadUInt32()
+ {
+ uint output = this.FastByte()
+ | (uint)this.FastByte() << 8
+ | (uint)this.FastByte() << 16
+ | (uint)this.FastByte() << 24;
+
+ return output;
+ }
+
+ public int ReadInt32()
+ {
+ int output = this.FastByte()
+ | this.FastByte() << 8
+ | this.FastByte() << 16
+ | this.FastByte() << 24;
+
+ return output;
+ }
+
+ public ulong ReadUInt64()
+ {
+ 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;
+
+ return output;
+ }
+
+ public long ReadInt64()
+ {
+ 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;
+
+ 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;
+ return output;
+ }
+
+ public string ReadString()
+ {
+ int len = this.ReadPackedInt32();
+ if (this.BytesRemaining < len) throw new InvalidDataException($"Read length is longer than message length: {len} of {this.BytesRemaining}");
+
+ string output = UTF8Encoding.UTF8.GetString(this.Buffer, this.readHead, len);
+
+ this.Position += len;
+ return output;
+ }
+
+ public byte[] ReadBytesAndSize()
+ {
+ int len = this.ReadPackedInt32();
+ if (this.BytesRemaining < len) throw new InvalidDataException($"Read length is longer than message length: {len} of {this.BytesRemaining}");
+
+ return this.ReadBytes(len);
+ }
+
+ public byte[] ReadBytes(int length)
+ {
+ if (this.BytesRemaining < length) throw new InvalidDataException($"Read length is longer than message length: {length} of {this.BytesRemaining}");
+
+ byte[] output = new byte[length];
+ Array.Copy(this.Buffer, this.readHead, output, 0, output.Length);
+ this.Position += output.Length;
+ return output;
+ }
+
+ ///
+ public int ReadPackedInt32()
+ {
+ return (int)this.ReadPackedUInt32();
+ }
+
+ ///
+ public uint ReadPackedUInt32()
+ {
+ bool readMore = true;
+ int shift = 0;
+ uint output = 0;
+
+ while (readMore)
+ {
+ if (this.BytesRemaining < 1) throw new InvalidDataException($"Read length is longer than message length.");
+
+ byte b = this.ReadByte();
+ if (b >= 0x80)
+ {
+ readMore = true;
+ b ^= 0x80;
+ }
+ else
+ {
+ readMore = false;
+ }
+
+ output |= (uint)(b << shift);
+ shift += 7;
+ }
+
+ return output;
+ }
+ #endregion
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private byte FastByte()
+ {
+ this._position++;
+ return this.Buffer[this.readHead++];
+ }
+
+ public unsafe static bool IsLittleEndian()
+ {
+ byte b;
+ unsafe
+ {
+ int i = 1;
+ byte* bp = (byte*)&i;
+ b = *bp;
+ }
+
+ return b == 1;
+ }
+ }
+}