From dd3b4d1a8b2685ab8be258c120b013bc61f9ebef Mon Sep 17 00:00:00 2001 From: Forest Date: Thu, 19 Jul 2018 19:08:19 -0700 Subject: [PATCH] Add a message reader. It's a tiny bit wonk, but it works --- Hazel.UnitTests/Hazel.UnitTests.csproj | 2 + Hazel.UnitTests/MessageReaderTests.cs | 119 +++++++++++++++++ Hazel/BinaryReaderExtensions.cs | 48 ------- Hazel/Hazel.csproj | 5 +- Hazel/MessageReader.cs | 176 +++++++++++++++++++++++++ Hazel/MessageWriter.cs | 8 +- 6 files changed, 307 insertions(+), 51 deletions(-) create mode 100644 Hazel.UnitTests/MessageReaderTests.cs delete mode 100644 Hazel/BinaryReaderExtensions.cs create mode 100644 Hazel/MessageReader.cs diff --git a/Hazel.UnitTests/Hazel.UnitTests.csproj b/Hazel.UnitTests/Hazel.UnitTests.csproj index b14bdd5..4cd3d36 100644 --- a/Hazel.UnitTests/Hazel.UnitTests.csproj +++ b/Hazel.UnitTests/Hazel.UnitTests.csproj @@ -26,6 +26,7 @@ DEBUG;TRACE prompt 4 + false pdbonly @@ -54,6 +55,7 @@ + diff --git a/Hazel.UnitTests/MessageReaderTests.cs b/Hazel.UnitTests/MessageReaderTests.cs new file mode 100644 index 0000000..80f54eb --- /dev/null +++ b/Hazel.UnitTests/MessageReaderTests.cs @@ -0,0 +1,119 @@ +using System; +using System.IO; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Hazel.UnitTests +{ + [TestClass] + public class MessageReaderTests + { + [TestMethod] + public void ReadProperInt() + { + const int Test1 = int.MaxValue; + const int Test2 = int.MinValue; + + var msg = new MessageWriter(128); + msg.StartMessage(1); + msg.Write(Test1); + msg.Write(Test2); + msg.EndMessage(); + + Assert.AreEqual(11, msg.Length); + Assert.AreEqual(msg.Length, msg.Position); + + MessageReader reader = MessageReader.Get(msg.Buffer, 0); + Assert.AreEqual(Test1, reader.ReadInt32()); + Assert.AreEqual(Test2, reader.ReadInt32()); + } + + [TestMethod] + public void ReadProperBool() + { + const bool Test1 = true; + const bool Test2 = false; + + var msg = new MessageWriter(128); + msg.StartMessage(1); + msg.Write(Test1); + msg.Write(Test2); + msg.EndMessage(); + + Assert.AreEqual(5, msg.Length); + Assert.AreEqual(msg.Length, msg.Position); + + MessageReader reader = MessageReader.Get(msg.Buffer, 0); + + Assert.AreEqual(Test1, reader.ReadBoolean()); + Assert.AreEqual(Test2, reader.ReadBoolean()); + + } + + [TestMethod] + public void ReadProperString() + { + const string Test1 = "Hello"; + string Test2 = new string(' ', 1024); + var msg = new MessageWriter(2048); + msg.StartMessage(1); + msg.Write(Test1); + msg.Write(Test2); + msg.EndMessage(); + + Assert.AreEqual(msg.Length, msg.Position); + + MessageReader reader = MessageReader.Get(msg.Buffer, 0); + + Assert.AreEqual(Test1, reader.ReadString()); + Assert.AreEqual(Test2, reader.ReadString()); + + } + + [TestMethod] + public void ReadProperFloat() + { + const float Test1 = 12.34f; + + var msg = new MessageWriter(2048); + msg.StartMessage(1); + msg.Write(Test1); + msg.EndMessage(); + + Assert.AreEqual(7, msg.Length); + Assert.AreEqual(msg.Length, msg.Position); + + MessageReader reader = MessageReader.Get(msg.Buffer, 0); + + Assert.AreEqual(Test1, reader.ReadSingle()); + + } + + [TestMethod] + public void ReadMessageLength() + { + var msg = new MessageWriter(2048); + msg.StartMessage(1); + msg.Write(65534); + msg.StartMessage(2); + msg.Write("HO"); + msg.EndMessage(); + msg.EndMessage(); + + Assert.AreEqual(msg.Length, msg.Position); + + MessageReader reader = MessageReader.Get(msg.Buffer, 0); + Assert.AreEqual(1, reader.Tag); + Assert.AreEqual(65534, reader.ReadInt32()); // Content + var sub = reader.ReadMessage(); + Assert.AreEqual(2, sub.Tag); + Assert.AreEqual("HO", sub.ReadString()); + + } + + [TestMethod] + public void GetLittleEndian() + { + Assert.IsTrue(MessageWriter.IsLittleEndian()); + } + } +} \ No newline at end of file diff --git a/Hazel/BinaryReaderExtensions.cs b/Hazel/BinaryReaderExtensions.cs deleted file mode 100644 index 332f236..0000000 --- a/Hazel/BinaryReaderExtensions.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System.IO; - -namespace Hazel -{ - /// - public static class BinaryReaderExtensions - { - /// - public static uint ReadPackedUInt32(this BinaryReader reader) - { - bool readMore = true; - int shift = 0; - uint output = 0; - - while (readMore) - { - byte b = reader.ReadByte(); - if (b >= 0x80) - { - readMore = true; - b ^= 0x80; - } - else - { - readMore = false; - } - - output |= (uint)(b << shift); - shift += 7; - } - - return output; - } - - /// - public static int ReadPackedInt32(this BinaryReader reader) - { - return (int)reader.ReadPackedUInt32(); - } - - /// - public static byte[] ReadBytesAndSize(this BinaryReader reader) - { - int len = (int)reader.ReadPackedUInt32(); - return reader.ReadBytes(len); - } - } -} \ No newline at end of file diff --git a/Hazel/Hazel.csproj b/Hazel/Hazel.csproj index 1172bb0..236a7b1 100644 --- a/Hazel/Hazel.csproj +++ b/Hazel/Hazel.csproj @@ -32,7 +32,8 @@ TRACE prompt 4 - bin\Release\Hazel.XML + + true @@ -51,7 +52,6 @@ - @@ -61,6 +61,7 @@ + diff --git a/Hazel/MessageReader.cs b/Hazel/MessageReader.cs new file mode 100644 index 0000000..7e4fc03 --- /dev/null +++ b/Hazel/MessageReader.cs @@ -0,0 +1,176 @@ +using System; +using System.Text; + +namespace Hazel +{ + /// + public class MessageReader : IRecyclable + { + private static readonly ObjectPool objectPool = new ObjectPool(() => new MessageReader()); + + private byte[] Buffer; + public byte Tag; + public int End; + + public int Position; + + public static MessageReader Get(byte[] buffer, int offset, int length) + { + var output = objectPool.GetObject(); + output.Buffer = buffer; + output.Position = offset; + output.End = length + offset; + output.Tag = output.ReadByte(); + + return output; + } + + public static MessageReader Get(byte[] buffer, int offset) + { + var output = objectPool.GetObject(); + output.Buffer = buffer; + output.Position = offset; + output.End = output.ReadUInt16() + offset; + output.Tag = output.ReadByte(); + + return output; + } + + /// + public MessageReader ReadMessage() + { + var output = MessageReader.Get(this.Buffer, this.Position); + this.Position += output.End; + return output; + } + + /// + public void Recycle() + { + this.Position = this.End = 0; + objectPool.PutObject(this); + } + + #region Read Methods + public bool ReadBoolean() + { + byte val = this.Buffer[this.Position++]; + return val != 0; + } + + public sbyte ReadSByte() + { + return (sbyte)this.Buffer[this.Position++]; + } + + public byte ReadByte() + { + return this.Buffer[this.Position++]; + } + + public ushort ReadUInt16() + { + ushort output = + (ushort)(this.Buffer[Position++] + | this.Buffer[Position++] << 8); + return output; + } + + public int ReadInt32() + { + int output = this.Buffer[Position++] + | this.Buffer[Position++] << 8 + | this.Buffer[Position++] << 16 + | this.Buffer[Position++] << 24; + + return output; + } + + public unsafe float ReadSingle() + { + float output = 0; + fixed (byte* bufPtr = &this.Buffer[this.Position]) + { + 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(); + string output = UTF8Encoding.UTF8.GetString(this.Buffer, this.Position, len); + + this.Position += len; + return output; + } + + public byte[] ReadBytesAndSize() + { + int len = this.ReadPackedInt32(); + return this.ReadBytes(len); + } + + public byte[] ReadBytes(int length) + { + byte[] output = new byte[length]; + Array.Copy(this.Buffer, this.Position, 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) + { + byte b = this.ReadByte(); + if (b >= 0x80) + { + readMore = true; + b ^= 0x80; + } + else + { + readMore = false; + } + + output |= (uint)(b << shift); + shift += 7; + } + + return output; + } + #endregion + + public unsafe static bool IsLittleEndian() + { + byte b; + unsafe + { + int i = 1; + byte* bp = (byte*)&i; + b = *bp; + } + + return b == 1; + } + } +} diff --git a/Hazel/MessageWriter.cs b/Hazel/MessageWriter.cs index 3508688..22640e6 100644 --- a/Hazel/MessageWriter.cs +++ b/Hazel/MessageWriter.cs @@ -101,13 +101,19 @@ namespace Hazel if (this.Position > this.Length) this.Length = this.Position; } + public void Write(sbyte value) + { + this.Buffer[this.Position++] = (byte)value; + if (this.Position > this.Length) this.Length = this.Position; + } + public void Write(byte value) { this.Buffer[this.Position++] = value; if (this.Position > this.Length) this.Length = this.Position; } - public void Write(short value) + public void Write(ushort value) { this.Buffer[this.Position++] = (byte)value; this.Buffer[this.Position++] = (byte)(value >> 8); -- 2.39.5