Browse Source

Added Bool, Integer and Nullable support to ETFReader

tags/docs-0.9
RogueException 9 years ago
parent
commit
1f1e6e6ed4
1 changed files with 392 additions and 100 deletions
  1. +392
    -100
      src/Discord.Net/ETF/ETFReader.cs

+ 392
- 100
src/Discord.Net/ETF/ETFReader.cs View File

@@ -1,26 +1,49 @@
/*using System;
using Newtonsoft.Json;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;

namespace Discord.ETF
{
public class ETFReader
public class ETFReader : IDisposable
{
private static readonly ConcurrentDictionary<Type, Delegate> _deserializers;
private static readonly Dictionary<Type, MethodInfo> _readMethods = GetPrimitiveReadMethods();

private readonly Stream _stream;
private readonly byte[] _buffer;
private readonly bool _leaveOpen;
private readonly Encoding _encoding;
private readonly ConcurrentDictionary<Type, Delegate> _serializers, _indirectSerializers;
public ETFReader(Stream stream, bool leaveOpen = false)
{
if (stream == null) throw new ArgumentNullException(nameof(stream));

private void ReadNil(bool allow)
_stream = stream;
_leaveOpen = leaveOpen;
_buffer = new byte[11];
_encoding = Encoding.UTF8;
}

private bool ReadNil(bool ignoreLength = false)
{
if (!allow) throw new InvalidDataException();
if (!ignoreLength)
{
_stream.Read(_buffer, 0, 1);
byte length = _buffer[0];
if (length != 3) return false;
}

_stream.Read(_buffer, 0, 3);
if (_buffer[0] != 'n' || _buffer[1] != 'i' || _buffer[2] != 'l')
throw new InvalidDataException();
if (_buffer[0] == 'n' && _buffer[1] == 'i' && _buffer[2] == 'l')
return true;

return false;
}
private void ReadTrue()
{
@@ -35,138 +58,407 @@ namespace Discord.ETF
throw new InvalidDataException();
}

public bool? ReadBool(bool allowNil)
public bool? ReadNullableBool()
{
_stream.Read(_buffer, 0, 2);
switch ((ETFType)_buffer[0])
_stream.Read(_buffer, 0, 1);
ETFType type = (ETFType)_buffer[0];
if (type == ETFType.SMALL_ATOM_EXT)
{
case ETFType.SMALL_ATOM_EXT:
switch (_buffer[1]) //Length
{
case 3:
ReadNil(allowNil);
_stream.Read(_buffer, 0, 1);
switch (_buffer[0]) //Length
{
case 3:
if (ReadNil())
return null;
case 4:
ReadTrue();
return true;
case 5:
ReadFalse();
return false;
}
break;
break;
case 4:
ReadTrue();
return true;
case 5:
ReadFalse();
return false;
}
}
throw new InvalidDataException();
}
public bool ReadBool()
{
_stream.Read(_buffer, 0, 1);
ETFType type = (ETFType)_buffer[0];
if (type == ETFType.SMALL_ATOM_EXT)
{
_stream.Read(_buffer, 0, 1);
switch (_buffer[0]) //Length
{
case 4:
ReadTrue();
return true;
case 5:
ReadFalse();
return false;
}
}
throw new InvalidDataException();
}

public long? ReadInteger(bool allowNil)
public int ReadSByte()
{
_stream.Read(_buffer, 0, 1);
ETFType type = (ETFType)_buffer[0];
return (sbyte)ReadLongInternal(type);
}
public int? ReadNullableSByte()
{
_stream.Read(_buffer, 0, 1);
ETFType type = (ETFType)_buffer[0];
if (type == ETFType.SMALL_ATOM_EXT && ReadNil()) return null;
return (sbyte)ReadLongInternal(type);
}
public uint ReadByte()
{
_stream.Read(_buffer, 0, 1);
ETFType type = (ETFType)_buffer[0];
return (byte)ReadLongInternal(type);
}
public uint? ReadNullableByte()
{
_stream.Read(_buffer, 0, 1);
ETFType type = (ETFType)_buffer[0];
if (type == ETFType.SMALL_ATOM_EXT && ReadNil()) return null;
return (byte)ReadLongInternal(type);
}
public int ReadShort()
{
_stream.Read(_buffer, 0, 1);
ETFType type = (ETFType)_buffer[0];
return (short)ReadLongInternal(type);
}
public int? ReadNullableShort()
{
_stream.Read(_buffer, 0, 1);
ETFType type = (ETFType)_buffer[0];
if (type == ETFType.SMALL_ATOM_EXT && ReadNil()) return null;
return (short)ReadLongInternal(type);
}
public uint ReadUShort()
{
_stream.Read(_buffer, 0, 1);
ETFType type = (ETFType)_buffer[0];
return (ushort)ReadLongInternal(type);
}
public uint? ReadNullableUShort()
{
_stream.Read(_buffer, 0, 1);
ETFType type = (ETFType)_buffer[0];
if (type == ETFType.SMALL_ATOM_EXT && ReadNil()) return null;
return (ushort)ReadLongInternal(type);
}
public int ReadInt()
{
_stream.Read(_buffer, 0, 1);
ETFType type = (ETFType)_buffer[0];
return (int)ReadLongInternal(type);
}
public int? ReadNullableInt()
{
_stream.Read(_buffer, 0, 1);
ETFType type = (ETFType)_buffer[0];
if (type == ETFType.SMALL_ATOM_EXT && ReadNil()) return null;
return (int)ReadLongInternal(type);
}
public uint ReadUInt()
{
_stream.Read(_buffer, 0, 1);
ETFType type = (ETFType)_buffer[0];
return (uint)ReadLongInternal(type);
}
public uint? ReadNullableUInt()
{
_stream.Read(_buffer, 0, 1);
ETFType type = (ETFType)_buffer[0];
if (type == ETFType.SMALL_ATOM_EXT && ReadNil()) return null;
return (uint)ReadLongInternal(type);
}
public long ReadLong()
{
_stream.Read(_buffer, 0, 1);
ETFType type = (ETFType)_buffer[0];
return ReadLongInternal(type);
}
public long? ReadNullableLong()
{
_stream.Read(_buffer, 0, 1);
ETFType type = (ETFType)_buffer[0];
if (type == ETFType.SMALL_ATOM_EXT && ReadNil()) return null;
return ReadLongInternal(type);
}
public ulong ReadULong()
{
_stream.Read(_buffer, 0, 1);
ETFType type = (ETFType)reader.ReadByte();
ETFType type = (ETFType)_buffer[0];
return (ulong)ReadLongInternal(type);
}
public ulong? ReadNullableULong()
{
_stream.Read(_buffer, 0, 1);
ETFType type = (ETFType)_buffer[0];
if (type == ETFType.SMALL_ATOM_EXT && ReadNil()) return null;
return (ulong)ReadLongInternal(type);
}
public long ReadLongInternal(ETFType type)
{
switch (type)
{
case ETFType.SMALL_ATOM_EXT:
ReadNil(allowNil);
return null;
case ETFType.SMALL_INTEGER_EXT:
_stream.Read(_buffer, 0, 1);
return (_buffer[0] << 24) | (_buffer[1] << 16) |
(_buffer[2] << 8) | (_buffer[3]);
return _buffer[0];
case ETFType.INTEGER_EXT:
_stream.Read(_buffer, 0, 4);
return ??;
return (_buffer[0] << 24) | (_buffer[1] << 16) | (_buffer[2] << 8) | (_buffer[3]);
case ETFType.SMALL_BIG_EXT:
return ??;
_stream.Read(_buffer, 0, 2);
bool isPositive = _buffer[0] == 0;
byte count = _buffer[1];

int shiftValue = (count - 1) * 8;
ulong value = 0;
_stream.Read(_buffer, 0, count);
for (int i = 0; i < count; i++, shiftValue -= 8)
value = value + _buffer[i] << shiftValue;
if (!isPositive)
return -(long)value;
else
return (long)value;
}
throw new InvalidDataException();
}

public void Write(sbyte value) => Write((long)value);
public void Write(byte value) => Write((ulong)value);
public void Write(short value) => Write((long)value);
public void Write(ushort value) => Write((ulong)value);
public void Write(int value) => Write((long)value);
public void Write(uint value) => Write((ulong)value);
public void Write(long value)
public float ReadSingle()
{
_stream.Read(_buffer, 0, 1);
ETFType type = (ETFType)_buffer[0];
return (float)ReadDoubleInternal(type);
}
public float? ReadNullableSingle()
{
if (value >= byte.MinValue && value <= byte.MaxValue)
_stream.Read(_buffer, 0, 1);
ETFType type = (ETFType)_buffer[0];
if (type == ETFType.SMALL_ATOM_EXT && ReadNil()) return null;
return (float)ReadDoubleInternal(type);
}
public double ReadDouble()
{
_stream.Read(_buffer, 0, 1);
ETFType type = (ETFType)_buffer[0];
return ReadDoubleInternal(type);
}
public double? ReadNullableDouble()
{
_stream.Read(_buffer, 0, 1);
ETFType type = (ETFType)_buffer[0];
if (type == ETFType.SMALL_ATOM_EXT && ReadNil()) return null;
return ReadDoubleInternal(type);
}
public double ReadDoubleInternal(ETFType type)
{
throw new NotImplementedException();
}

public string ReadString()
{
throw new NotImplementedException();
}
public byte[] ReadByteArray()
{
throw new NotImplementedException();
}

#region Emit
private static Func<ETFReader, T> CreateDeserializer<T>(Type type, TypeInfo typeInfo)
where T : new()
{
var method = new DynamicMethod("DeserializeETF", type, new[] { typeof(ETFReader) }, true);
var generator = method.GetILGenerator();

generator.Emit(OpCodes.Ldarg_0); //ETFReader(this)
EmitReadValue(generator, type, typeInfo, true);

generator.Emit(OpCodes.Ret);
return method.CreateDelegate(typeof(Func<ETFReader, T>)) as Func<ETFReader, T>;
}
private static void EmitReadValue(ILGenerator generator, Type type, TypeInfo typeInfo, bool isTop)
{
//Convert enum types to their base type
if (typeInfo.IsEnum)
{
_buffer[0] = (byte)ETFType.SMALL_INTEGER_EXT;
_buffer[1] = (byte)value;
_stream.Write(_buffer, 0, 2);
type = Enum.GetUnderlyingType(type);
typeInfo = type.GetTypeInfo();
}
else if (value >= int.MinValue && value <= int.MaxValue)
//Primitives/Enums
if (!typeInfo.IsEnum && IsType(type, typeof(sbyte), typeof(byte), typeof(short),
typeof(ushort), typeof(int), typeof(uint), typeof(long),
typeof(ulong), typeof(double), typeof(bool), typeof(string),
typeof(sbyte?), typeof(byte?), typeof(short?), typeof(ushort?),
typeof(int?), typeof(uint?), typeof(long?), typeof(ulong?),
typeof(bool?), typeof(float?), typeof(double?)
/*typeof(object), typeof(DateTime)*/))
{
_buffer[0] = (byte)ETFType.INTEGER_EXT;
_buffer[1] = (byte)(value >> 24);
_buffer[2] = (byte)(value >> 16);
_buffer[3] = (byte)(value >> 8);
_buffer[4] = (byte)value;
_stream.Write(_buffer, 0, 5);
//No conversion needed
generator.EmitCall(OpCodes.Call, GetReadMethod(type), null);
}
else
//Dictionaries
/*else if (!typeInfo.IsValueType && typeInfo.ImplementedInterfaces
.Any(x => x.IsConstructedGenericType && x.GetGenericTypeDefinition() == typeof(IDictionary<,>)))
{
generator.EmitCall(OpCodes.Call, _writeDictionaryTMethod.MakeGenericMethod(typeInfo.GenericTypeParameters), null);
}
//Enumerable
else if (!typeInfo.IsValueType && typeInfo.ImplementedInterfaces
.Any(x => x.IsConstructedGenericType && x.GetGenericTypeDefinition() == typeof(IEnumerable<>)))
{
_buffer[0] = (byte)ETFType.SMALL_BIG_EXT;
if (value < 0)
generator.EmitCall(OpCodes.Call, _writeEnumerableTMethod.MakeGenericMethod(typeInfo.GenericTypeParameters), null);
}
//Nullable Structs
else if (typeInfo.IsGenericType && typeInfo.GetGenericTypeDefinition() == typeof(Nullable<>) &&
typeInfo.GenericTypeParameters[0].GetTypeInfo().IsValueType)
{
generator.EmitCall(OpCodes.Call, _writeNullableTMethod.MakeGenericMethod(typeInfo.GenericTypeParameters), null);
}
//Structs/Classes
else if (typeInfo.IsClass || (typeInfo.IsValueType && !typeInfo.IsPrimitive))
{
if (isTop)
{
_buffer[2] = 1; //Is negative
value = -value;
}
typeInfo.ForEachField(f =>
{
string name;
if (!f.IsPublic || !IsETFProperty(f, out name)) return;

byte bytes = 0;
while (value > 0)
_buffer[3 + bytes++] = (byte)(value >>= 8);
_buffer[1] = bytes; //Encoded bytes
generator.Emit(OpCodes.Ldarg_0); //ETFReader(this)
generator.Emit(OpCodes.Ldstr, name); //ETFReader(this), name
generator.EmitCall(OpCodes.Call, GetWriteMethod(typeof(string)), null);
generator.Emit(OpCodes.Ldarg_0); //ETFReader(this)
generator.Emit(OpCodes.Ldarg_1); //ETFReader(this), obj
generator.Emit(OpCodes.Ldfld, f); //ETFReader(this), obj.fieldValue
EmitWriteValue(generator, f.FieldType, f.FieldType.GetTypeInfo(), false);
});

_stream.Write(_buffer, 0, 3 + bytes);
}
typeInfo.ForEachProperty(p =>
{
string name;
if (!p.CanRead || !p.GetMethod.IsPublic || !IsETFProperty(p, out name)) return;

generator.Emit(OpCodes.Ldarg_0); //ETFReader(this)
generator.Emit(OpCodes.Ldstr, name); //ETFReader(this), name
generator.EmitCall(OpCodes.Call, GetWriteMethod(typeof(string)), null);
generator.Emit(OpCodes.Ldarg_0); //ETFReader(this)
generator.Emit(OpCodes.Ldarg_1); //ETFReader(this), obj
generator.EmitCall(OpCodes.Callvirt, p.GetMethod, null); //ETFReader(this), obj.propValue
EmitWriteValue(generator, p.PropertyType, p.PropertyType.GetTypeInfo(), false);
});
}
else
{
//While we could drill deeper and make a large serializer that also serializes all subclasses,
//it's more efficient to serialize on a per-type basis via another Write<T> call.
generator.EmitCall(OpCodes.Call, _writeTMethod.MakeGenericMethod(typeInfo.GenericTypeParameters), null);
}
}*/
//Unsupported (decimal, char)
else
throw new InvalidOperationException($"Deserializing {type.Name} is not supported.");
}
public void Write(ulong value)

private static bool IsType(Type type, params Type[] types)
{
if (value <= byte.MaxValue)
for (int i = 0; i < types.Length; i++)
{
_buffer[0] = (byte)ETFType.SMALL_INTEGER_EXT;
_buffer[1] = (byte)value;
_stream.Write(_buffer, 0, 2);
if (type == types[i])
return true;
}
else if (value <= int.MaxValue)
return false;
}
private static bool IsETFProperty(FieldInfo f, out string name)
{
var attrib = f.CustomAttributes.Where(x => x.AttributeType == typeof(JsonPropertyAttribute)).FirstOrDefault();
if (attrib != null)
{
_buffer[0] = (byte)ETFType.INTEGER_EXT;
_buffer[1] = (byte)(value >> 24);
_buffer[2] = (byte)(value >> 16);
_buffer[3] = (byte)(value >> 8);
_buffer[4] = (byte)value;
_stream.Write(_buffer, 0, 5);
name = attrib.ConstructorArguments.FirstOrDefault().Value as string ?? f.Name;
return true;
}
else
name = null;
return false;
}
private static bool IsETFProperty(PropertyInfo p, out string name)
{
var attrib = p.CustomAttributes.Where(x => x.AttributeType == typeof(JsonPropertyAttribute)).FirstOrDefault();
if (attrib != null)
{
_buffer[0] = (byte)ETFType.SMALL_BIG_EXT;
_buffer[2] = 0; //Always positive

byte bytes = 0;
while (value > 0)
_buffer[3 + bytes++] = (byte)(value >>= 8);
_buffer[1] = bytes; //Encoded bytes

_stream.Write(_buffer, 0, 3 + bytes);
name = attrib.ConstructorArguments.FirstOrDefault().Value as string ?? p.Name;
return true;
}
name = null;
return false;
}

private static MethodInfo GetReadMethod(string name)
=> typeof(ETFReader).GetTypeInfo().GetDeclaredMethods(name).Single();
private static MethodInfo GetReadMethod(Type type)
{
MethodInfo method;
if (_readMethods.TryGetValue(type, out method))
return method;
return null;
}
private static Dictionary<Type, MethodInfo> GetPrimitiveReadMethods()
{
return new Dictionary<Type, MethodInfo>
{
{ typeof(bool), GetReadMethod(nameof(ReadBool)) },
{ typeof(bool?), GetReadMethod(nameof(ReadNullableBool)) },
{ typeof(byte), GetReadMethod(nameof(ReadByte)) },
{ typeof(byte?), GetReadMethod(nameof(ReadNullableByte)) },
{ typeof(sbyte), GetReadMethod(nameof(ReadSByte)) },
{ typeof(sbyte?), GetReadMethod(nameof(ReadNullableSByte)) },
{ typeof(short), GetReadMethod(nameof(ReadShort)) },
{ typeof(short?), GetReadMethod(nameof(ReadNullableShort)) },
{ typeof(ushort), GetReadMethod(nameof(ReadUShort)) },
{ typeof(ushort?), GetReadMethod(nameof(ReadNullableUShort)) },
{ typeof(int), GetReadMethod(nameof(ReadInt)) },
{ typeof(int?), GetReadMethod(nameof(ReadNullableInt)) },
{ typeof(uint), GetReadMethod(nameof(ReadUInt)) },
{ typeof(uint?), GetReadMethod(nameof(ReadNullableUInt)) },
{ typeof(long), GetReadMethod(nameof(ReadLong)) },
{ typeof(long?), GetReadMethod(nameof(ReadNullableLong)) },
{ typeof(ulong), GetReadMethod(nameof(ReadULong)) },
{ typeof(ulong?), GetReadMethod(nameof(ReadNullableULong)) },
{ typeof(float), GetReadMethod(nameof(ReadSingle)) },
{ typeof(float?), GetReadMethod(nameof(ReadNullableSingle)) },
{ typeof(double), GetReadMethod(nameof(ReadDouble)) },
{ typeof(double?), GetReadMethod(nameof(ReadNullableDouble)) },
};
}
#endregion

#region IDisposable
private bool _isDisposed = false;

public void Write(float value) => Write((double)value);
public unsafe void Write(double value)
protected virtual void Dispose(bool disposing)
{
ulong value2 = *(ulong*)&value;
_buffer[0] = (byte)ETFType.NEW_FLOAT_EXT;
_buffer[1] = (byte)(value2 >> 56);
_buffer[2] = (byte)(value2 >> 48);
_buffer[3] = (byte)(value2 >> 40);
_buffer[4] = (byte)(value2 >> 32);
_buffer[5] = (byte)(value2 >> 24);
_buffer[6] = (byte)(value2 >> 16);
_buffer[7] = (byte)(value2 >> 8);
_buffer[8] = (byte)value2;
_stream.Write(_buffer, 0, 9);
if (!_isDisposed)
{
if (disposing)
{
if (_leaveOpen)
_stream.Flush();
else
_stream.Dispose();
}
_isDisposed = true;
}
}

public void Write(DateTime value) => Write((ulong)((value.Ticks - _epochTime.Ticks) / TimeSpan.TicksPerSecond));
public void Dispose() => Dispose(true);
#endregion
}
}*/
}

Loading…
Cancel
Save