using System; using System.Collections; using System.Text; namespace XGame.Framework.Serialization { public class Reader : IReader { private byte[] _buffer; private int _offset; public Reader(byte[] buffer) { _buffer = buffer; _offset = 0; } public bool ReadBool() { Assert.IsTrue(_offset < _buffer.Length, $"Reader::ReadBool: Out of bound of byte array."); return _buffer[_offset++] != 0; } public sbyte ReadSbyte() { Assert.IsTrue(_offset < _buffer.Length, $"Reader::ReadSbyte: Out of bound of byte array."); return (sbyte)_buffer[_offset++]; } public byte ReadByte() { Assert.IsTrue(_offset < _buffer.Length, $"Reader::ReadByte: Out of bound of byte array."); return _buffer[_offset++]; } public short ReadShort() { Assert.IsTrue(_offset + 2 <= _buffer.Length, $"Reader::ReadShort: Out of bound of byte array."); var value = BitConverter.ToInt16(_buffer, _offset); _offset += 2; return value; } public ushort ReadUShort() { Assert.IsTrue(_offset + 2 <= _buffer.Length, $"Reader::ReadShort: Out of bound of byte array."); var value = BitConverter.ToUInt16(_buffer, _offset); _offset += 2; return value; } public int ReadInt() { Assert.IsTrue(_offset + 4 <= _buffer.Length, $"Reader::ReadInt: Out of bound of byte array."); var value = BitConverter.ToInt32(_buffer, _offset); _offset += 4; return value; } public uint ReadUInt() { Assert.IsTrue(_offset + 4 <= _buffer.Length, $"Reader::ReadInt: Out of bound of byte array."); var value = BitConverter.ToUInt32(_buffer, _offset); _offset += 4; return value; } public long ReadLong() { Assert.IsTrue(_offset + 8 <= _buffer.Length, $"Reader::ReadLong: Out of bound of byte array."); var value = BitConverter.ToInt64(_buffer, _offset); _offset += 8; return value; } public ulong ReadULong() { Assert.IsTrue(_offset + 8 <= _buffer.Length, $"Reader::ReadLong: Out of bound of byte array."); var value = BitConverter.ToUInt64(_buffer, _offset); _offset += 8; return value; } public float ReadFloat() { Assert.IsTrue(_offset + 4 <= _buffer.Length, $"Reader::ReadFloat: Out of bound of byte array."); var value = BitConverter.ToSingle(_buffer, _offset); _offset += 4; return value; } public double ReadDouble() { Assert.IsTrue(_offset + 8 <= _buffer.Length, $"Reader::ReadDouble: Out of bound of byte array."); var value = BitConverter.ToDouble(_buffer, _offset); _offset += 8; return value; } public char ReadChar() { Assert.IsTrue(_offset + 2 <= _buffer.Length, $"Reader::ReadChar: Out of bound of byte array."); var value = BitConverter.ToChar(_buffer, _offset); _offset += 2; return value; } public string ReadString() { Assert.IsTrue(_offset < _buffer.Length, $"Reader::ReadString: Out of bound of byte array."); var size = ReadInt(); if (size < 0) return null; if (size == 0) return string.Empty; Assert.IsTrue(_offset + size <= _buffer.Length, $"Reader::ReadString: Out of bound of byte array."); var value = Encoding.UTF8.GetString(_buffer, _offset, size); _offset += size; return value; } public byte[] ReadBytes() { Assert.IsTrue(_offset < _buffer.Length, $"Reader::ReadBytes: Out of bound of byte array."); var size = ReadInt(); if (size < 0) return null; Assert.IsTrue(_offset + size <= _buffer.Length, $"Reader::ReadBytes: Out of bound of byte array."); var blob = new byte[size]; Buffer.BlockCopy(_buffer, _offset, blob, 0, size); _offset += size; return blob; } public T ReadSerializable() where T : ISerializable, new() { return (T)ReadSerializable(typeof(T)); } public T ReadEnumerable() where T : class, IEnumerable { var type = typeof(T); return ReadEnumerable(type) as T; } private object ReadEnumerable(Type type) { Assert.IsTrue(_offset < _buffer.Length, $"Reader::ReadEnumerable: Out of bound of byte array."); int length = ReadInt(); if (length < 0) return null; if (type.IsArray) return ReadArray(type, length); var instance = Activator.CreateInstance(type); switch (instance) { case IList ls: ReadList(ls, type, length); break; case IDictionary dic: ReadDictionary(dic, type, length); break; default: throw new Exception($"Reader::ReadEnumerable: {type.Name} is unsupported."); } return instance; } private void ReadDictionary(IDictionary dic, Type type, int length) { var genericTypes = type.GetGenericArguments(); Assert.IsTrue(genericTypes.Length == 2, "Reader::ReadDictionary: the count of generic arguments must be 2"); if (genericTypes[0].IsEnum) genericTypes[0] = genericTypes[0].GetEnumUnderlyingType(); if (genericTypes[1].IsEnum) genericTypes[1] = genericTypes[1].GetEnumUnderlyingType(); for (int i = 0; i < length; i++) { object key = ReadObject(genericTypes[0]); object value = ReadObject(genericTypes[1]); dic.Add(key, value); } } private void ReadList(IList list, Type type, int length) { var genericType = type.GetGenericArguments()[0]; if (genericType.IsEnum) genericType = genericType.GetEnumUnderlyingType(); for (int i = 0; i < length; i++) { list.Add(ReadObject(genericType)); } } private object ReadArray(System.Type type, int length) { var elementType = type.GetElementType(); Array array = Array.CreateInstance(elementType, length); Assert.IsTrue(array.Rank == 1, "Reader::ReadArray: the count of array rank must be 1"); if (elementType.IsEnum) elementType = elementType.GetEnumUnderlyingType(); for (int i = 0; i < length; i++) { object value = ReadObject(elementType); array.SetValue(value, i); } return array; } private object ReadObject(Type type) { if (TypeHelper.Bool == type) return ReadBool(); if (TypeHelper.SByte == type) return ReadSbyte(); if (TypeHelper.Byte == type) return ReadByte(); if (TypeHelper.Short == type) return ReadShort(); if (TypeHelper.UShort == type) return ReadUShort(); if (TypeHelper.Int == type) return ReadInt(); if (TypeHelper.UInt == type) return ReadUInt(); if (TypeHelper.Long == type) return ReadLong(); if (TypeHelper.ULong == type) return ReadULong(); if (TypeHelper.Float == type) return ReadFloat(); if (TypeHelper.Double == type) return ReadDouble(); if (TypeHelper.Char == type) return ReadChar(); if (TypeHelper.String == type) return ReadString(); if (TypeHelper.Bytes == type) return ReadBytes(); if (TypeHelper.IEnumerable.IsAssignableFrom(type)) return ReadEnumerable(type); if (TypeHelper.ISerializable.IsAssignableFrom(type)) return ReadSerializable(type); throw new NotSupportedException($"Reader::ReadEnumerable: {type.Name} is unsupported."); } private object ReadSerializable(Type type) { Assert.IsTrue(_offset < _buffer.Length, $"Reader::ReadSerializable: Out of bound of byte array."); var isNull = ReadBool(); if (isNull) return null; var value = Activator.CreateInstance(type); (value as ISerializable).Deserialize(this); return value; } } }