diff options
Diffstat (limited to 'Tools/Hazel-Networking/Hazel/MessageReader.cs')
-rw-r--r-- | Tools/Hazel-Networking/Hazel/MessageReader.cs | 452 |
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; + } + } +} |