using System; using System.Collections.Generic; using System.IO; using System.Net; using System.Text; using ZeroLevel.Services.Extensions; namespace ZeroLevel.Services.Serialization { /// /// A wrapper over a MemoryStream for reading, with a check for overflow /// public sealed class MemoryStreamReader : IBinaryReader { private readonly Stream _stream; private bool _reverseByteOrder = false; public void ReverseByteOrder(bool use_reverse_byte_order) { _reverseByteOrder = use_reverse_byte_order; } public MemoryStreamReader(byte[] data) { if (data == null) throw new ArgumentNullException(nameof(data)); _stream = new MemoryStream(data); } public MemoryStreamReader(Stream stream) { if (stream == null) throw new ArgumentNullException(nameof(stream)); _stream = stream; } /// /// Flag reading /// public bool ReadBoolean() { if (CheckOutOfRange(_stream, 1)) throw new OutOfMemoryException("Array index out of bounds"); return BitConverter.ToBoolean(new byte[1] { ReadByte() }, 0); } /// /// Reading byte /// public byte ReadByte() { if (CheckOutOfRange(_stream, 1)) throw new OutOfMemoryException("Array index out of bounds"); return (byte)_stream.ReadByte(); } /// /// Reading bytes /// /// public byte[] ReadBytes() { var length = BitConverter.ToInt32(ReadBuffer(4), 0); if (length == 0) return new byte[0]; return ReadBuffer(length); } public short ReadShort() { var buffer = ReadBuffer(2); return BitConverter.ToInt16(buffer, 0); } public ushort ReadUShort() { var buffer = ReadBuffer(2); return BitConverter.ToUInt16(buffer, 0); } /// /// Read 32-bit integer (4 bytes) /// public Int32 ReadInt32() { var buffer = ReadBuffer(4); return BitConverter.ToInt32(buffer, 0); } public UInt32 ReadUInt32() { var buffer = ReadBuffer(4); return BitConverter.ToUInt32(buffer, 0); } public decimal ReadDecimal() { var p1 = ReadInt32(); var p2 = ReadInt32(); var p3 = ReadInt32(); var p4 = ReadInt32(); return BitConverterExt.ToDecimal(new int[] { p1, p2, p3, p4 }); } /// /// Read integer 64-bit number (8 bytes) /// public Int64 ReadLong() { var buffer = ReadBuffer(8); return BitConverter.ToInt64(buffer, 0); } public UInt64 ReadULong() { var buffer = ReadBuffer(8); return BitConverter.ToUInt64(buffer, 0); } public TimeSpan ReadTimeSpan() { return new TimeSpan(ReadLong()); } public float ReadFloat() { var buffer = ReadBuffer(4); return BitConverter.ToSingle(buffer, 0); } public double ReadDouble() { var buffer = ReadBuffer(8); return BitConverter.ToDouble(buffer, 0); } /// /// Read string (4 bytes per length + Length bytes) /// public string ReadString() { var length = BitConverter.ToInt32(ReadBuffer(4), 0); if (length == 0) return null; var buffer = ReadBuffer(length); return Encoding.UTF8.GetString(buffer); } /// /// Read GUID (16 bytes) /// public Guid ReadGuid() { var buffer = ReadBuffer(16); return new Guid(buffer); } /// /// Reading byte-package (read the size of the specified number of bytes, and then the packet itself read size) /// public byte[] ReadBuffer(int count) { if (count == 0) return null; if (CheckOutOfRange(_stream, count)) throw new OutOfMemoryException("Array index out of bounds"); var buffer = new byte[count]; var readedCount = _stream.Read(buffer, 0, count); if (count != readedCount) throw new InvalidOperationException($"The stream returned less data ({count} bytes) than expected ({readedCount} bytes)"); if (_reverseByteOrder && count > 1) { byte b; for (int i = 0; i < (count >> 1); i++) { b = buffer[i]; buffer[i] = buffer[count - i - 1]; buffer[count - i - 1] = b; } } return buffer; } /// /// Reading the datetime /// /// public DateTime? ReadDateTime() { var is_null = ReadByte(); if (is_null == 0) return null; var buffer = ReadBuffer(8); long deserialized = BitConverter.ToInt64(buffer, 0); return DateTime.FromBinary(deserialized); } public IPAddress ReadIP() { var addr = ReadBytes(); return new IPAddress(addr); } public IPEndPoint ReadIPEndpoint() { var addr = ReadIP(); var port = ReadInt32(); return new IPEndPoint(addr, port); } /// /// Check if data reading is outside the stream /// private bool CheckOutOfRange(Stream stream, int offset) { return (stream.Position + offset) > stream.Length; } #region Extensions public List ReadCollection() where T : IBinarySerializable, new() { int count = ReadInt32(); var collection = new List(count); if (count > 0) { for (int i = 0; i < count; i++) { var item = new T(); item.Deserialize(this); collection.Add(item); } } return collection; } public T ReadCompatible() { return MessageSerializer.DeserializeCompatible(this); } public T Read() where T : IBinarySerializable { byte type = ReadByte(); if (type == 0) return default(T); var item = (T)Activator.CreateInstance(); item.Deserialize(this); return item; } public List ReadStringCollection() { int count = ReadInt32(); var collection = new List(count); if (count > 0) { for (int i = 0; i < count; i++) { collection.Add(ReadString()); } } return collection; } public List ReadIPCollection() { int count = ReadInt32(); var collection = new List(count); if (count > 0) { for (int i = 0; i < count; i++) { collection.Add(ReadIP()); } } return collection; } public List ReadIPEndPointCollection() { int count = ReadInt32(); var collection = new List(count); if (count > 0) { for (int i = 0; i < count; i++) { collection.Add(ReadIPEndpoint()); } } return collection; } public List ReadGuidCollection() { int count = ReadInt32(); var collection = new List(count); if (count > 0) { for (int i = 0; i < count; i++) { collection.Add(ReadGuid()); } } return collection; } public List ReadDateTimeCollection() { int count = ReadInt32(); var collection = new List(count); if (count > 0) { for (int i = 0; i < count; i++) { collection.Add(ReadDateTime() ?? DateTime.MinValue); } } return collection; } public List ReadInt64Collection() { int count = ReadInt32(); var collection = new List(count); if (count > 0) { for (int i = 0; i < count; i++) { collection.Add(ReadLong()); } } return collection; } public List ReadInt32Collection() { int count = ReadInt32(); var collection = new List(count); if (count > 0) { for (int i = 0; i < count; i++) { collection.Add(ReadInt32()); } } return collection; } public List ReadFloatCollection() { int count = ReadInt32(); var collection = new List(count); if (count > 0) { for (int i = 0; i < count; i++) { collection.Add(ReadFloat()); } } return collection; } public List ReadDoubleCollection() { int count = ReadInt32(); var collection = new List(count); if (count > 0) { for (int i = 0; i < count; i++) { collection.Add(ReadDouble()); } } return collection; } public List ReadBooleanCollection() { int count = ReadInt32(); var collection = new List(count); if (count > 0) { for (int i = 0; i < count; i++) { collection.Add(ReadBoolean()); } } return collection; } public List ReadByteCollection() { int count = ReadInt32(); var collection = new List(count); if (count > 0) { for (int i = 0; i < count; i++) { collection.Add(ReadByte()); } } return collection; } public List ReadByteArrayCollection() { int count = ReadInt32(); var collection = new List(count); if (count > 0) { for (int i = 0; i < count; i++) { collection.Add(ReadBytes()); } } return collection; } public List ReadDecimalCollection() { int count = ReadInt32(); var collection = new List(count); if (count > 0) { for (int i = 0; i < count; i++) { collection.Add(ReadDecimal()); } } return collection; } public List ReadTimeSpanCollection() { int count = ReadInt32(); var collection = new List(count); if (count > 0) { for (int i = 0; i < count; i++) { collection.Add(ReadTimeSpan()); } } return collection; } #endregion Extensions public void Dispose() { _stream.Dispose(); } } }