using System; using System.Collections.Concurrent; 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; } /// /// End of stream /// public bool EOS => _stream.Position >= _stream.Length; 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; } public MemoryStreamReader(MemoryStreamReader reader) { if (reader == null) throw new ArgumentNullException(nameof(reader)); _stream = reader._stream; } /// /// Flag reading /// public bool ReadBoolean() { if (CheckOutOfRange(1)) throw new OutOfMemoryException("Array index out of bounds"); return BitConverter.ToBoolean(new byte[1] { ReadByte() }, 0); } /// /// Reading byte /// public byte ReadByte() { if (CheckOutOfRange(1)) throw new OutOfMemoryException("Array index out of bounds"); return (byte)_stream.ReadByte(); } public char ReadChar() { if (CheckOutOfRange(2)) throw new OutOfMemoryException("Array index out of bounds"); var buffer = ReadBuffer(2); return BitConverter.ToChar(buffer, 0); } /// /// 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(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 exists = ReadByte(); if (exists == 1) { var addr = ReadBytes(); return new IPAddress(addr); } return null; } public IPEndPoint ReadIPEndpoint() { var exists = ReadByte(); if (exists == 1) { var addr = ReadIP(); var port = ReadInt32(); return new IPEndPoint(addr, port); } return null; } /// /// Check if data reading is outside the stream /// public bool CheckOutOfRange(int offset) { return (_stream.Position + offset) > _stream.Length; } #region Extensions #region Collections 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 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 ReadUInt64Collection() { int count = ReadInt32(); var collection = new List(count); if (count > 0) { for (int i = 0; i < count; i++) { collection.Add(ReadULong()); } } return collection; } public List ReadUInt32Collection() { int count = ReadInt32(); var collection = new List(count); if (count > 0) { for (int i = 0; i < count; i++) { collection.Add(ReadUInt32()); } } return collection; } public List ReadCharCollection() { int count = ReadInt32(); var collection = new List(count); if (count > 0) { for (int i = 0; i < count; i++) { collection.Add(ReadChar()); } } return collection; } public List ReadShortCollection() { int count = ReadInt32(); var collection = new List(count); if (count > 0) { for (int i = 0; i < count; i++) { collection.Add(ReadShort()); } } return collection; } public List ReadUShortCollection() { int count = ReadInt32(); var collection = new List(count); if (count > 0) { for (int i = 0; i < count; i++) { collection.Add(ReadUShort()); } } 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 #region Arrays public T[] ReadArray() where T : IBinarySerializable, new() { int count = ReadInt32(); var array = new T[count]; if (count > 0) { for (int i = 0; i < count; i++) { var item = new T(); item.Deserialize(this); array[i] = item; } } return array; } public string[] ReadStringArray() { int count = ReadInt32(); var array = new string[count]; if (count > 0) { for (int i = 0; i < count; i++) { array[i] = ReadString(); } } return array; } public IPAddress[] ReadIPArray() { int count = ReadInt32(); var array = new IPAddress[count]; if (count > 0) { for (int i = 0; i < count; i++) { array[i] = ReadIP(); } } return array; } public IPEndPoint[] ReadIPEndPointArray() { int count = ReadInt32(); var array = new IPEndPoint[count]; if (count > 0) { for (int i = 0; i < count; i++) { array[i] = ReadIPEndpoint(); } } return array; } public Guid[] ReadGuidArray() { int count = ReadInt32(); var array = new Guid[count]; if (count > 0) { for (int i = 0; i < count; i++) { array[i] = ReadGuid(); } } return array; } public DateTime[] ReadDateTimeArray() { int count = ReadInt32(); var array = new DateTime[count]; if (count > 0) { for (int i = 0; i < count; i++) { array[i] = (ReadDateTime() ?? DateTime.MinValue); } } return array; } public Int64[] ReadInt64Array() { int count = ReadInt32(); var array = new Int64[count]; if (count > 0) { for (int i = 0; i < count; i++) { array[i] = ReadLong(); } } return array; } public Int32[] ReadInt32Array() { int count = ReadInt32(); var array = new Int32[count]; if (count > 0) { for (int i = 0; i < count; i++) { array[i] = ReadInt32(); } } return array; } public UInt64[] ReadUInt64Array() { int count = ReadInt32(); var array = new UInt64[count]; if (count > 0) { for (int i = 0; i < count; i++) { array[i] = ReadULong(); } } return array; } public UInt32[] ReadUInt32Array() { int count = ReadInt32(); var array = new UInt32[count]; if (count > 0) { for (int i = 0; i < count; i++) { array[i] = ReadUInt32(); } } return array; } public char[] ReadCharArray() { int count = ReadInt32(); var array = new char[count]; if (count > 0) { for (int i = 0; i < count; i++) { array[i] = ReadChar(); } } return array; } public short[] ReadShortArray() { int count = ReadInt32(); var array = new short[count]; if (count > 0) { for (int i = 0; i < count; i++) { array[i] = ReadShort(); } } return array; } public ushort[] ReadUShortArray() { int count = ReadInt32(); var array = new ushort[count]; if (count > 0) { for (int i = 0; i < count; i++) { array[i] = ReadUShort(); } } return array; } public float[] ReadFloatArray() { int count = ReadInt32(); var array = new float[count]; if (count > 0) { for (int i = 0; i < count; i++) { array[i] = ReadFloat(); } } return array; } public Double[] ReadDoubleArray() { int count = ReadInt32(); var array = new Double[count]; if (count > 0) { for (int i = 0; i < count; i++) { array[i] = ReadDouble(); } } return array; } public bool[] ReadBooleanArray() { int count = ReadInt32(); var array = new bool[count]; if (count > 0) { for (int i = 0; i < count; i++) { array[i] = ReadBoolean(); } } return array; } public byte[] ReadByteArray() { return ReadBytes(); } public byte[][] ReadByteArrayArray() { int count = ReadInt32(); var array = new byte[count][]; if (count > 0) { for (int i = 0; i < count; i++) { array[i] = ReadBytes(); } } return array; } public decimal[] ReadDecimalArray() { int count = ReadInt32(); var array = new decimal[count]; if (count > 0) { for (int i = 0; i < count; i++) { array[i] = ReadDecimal(); } } return array; } public TimeSpan[] ReadTimeSpanArray() { int count = ReadInt32(); var array = new TimeSpan[count]; if (count > 0) { for (int i = 0; i < count; i++) { array[i] = ReadTimeSpan(); } } return array; } #endregion public Dictionary ReadDictionary() { int count = ReadInt32(); var collection = new Dictionary(count); if (count > 0) { TKey key; TValue value; for (int i = 0; i < count; i++) { key = ReadCompatible(); value = ReadCompatible(); collection.Add(key, value); } } return collection; } public ConcurrentDictionary ReadDictionaryAsConcurrent() { int count = ReadInt32(); var collection = new ConcurrentDictionary(); if (count > 0) { TKey key; TValue value; for (int i = 0; i < count; i++) { key = ReadCompatible(); value = ReadCompatible(); collection.TryAdd(key, value); } } 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 T Read(object arg) where T : IBinarySerializable { byte type = ReadByte(); if (type == 0) return default(T); var item = (T)Activator.CreateInstance(typeof(T), arg); item.Deserialize(this); return item; } #endregion Extensions public void Dispose() { _stream.Dispose(); } public Stream Stream => _stream; } }