Browse Source

Cleaned up ETFWriter

tags/docs-0.9
RogueException 9 years ago
parent
commit
7cbefe6fa2
1 changed files with 30 additions and 27 deletions
  1. +30
    -27
      src/Discord.Net/ETF/ETFWriter.cs

+ 30
- 27
src/Discord.Net/ETF/ETFWriter.cs View File

@@ -12,21 +12,23 @@ namespace Discord.ETF
{ {
public unsafe class ETFWriter : IDisposable public unsafe class ETFWriter : IDisposable
{ {
private readonly static byte[] _nilBytes = new byte[] { (byte)ETFType.SMALL_ATOM_EXT, 3, (byte)'n', (byte)'i', (byte)'l' };
private readonly static byte[] _falseBytes = new byte[] { (byte)ETFType.SMALL_ATOM_EXT, 5, (byte)'f', (byte)'a', (byte)'l', (byte)'s', (byte)'e' };
private readonly static byte[] _trueBytes = new byte[] { (byte)ETFType.SMALL_ATOM_EXT, 4, (byte)'t', (byte)'r', (byte)'u', (byte)'e' };
private static readonly ConcurrentDictionary<Type, Delegate> _serializers = new ConcurrentDictionary<Type, Delegate>();
private static readonly ConcurrentDictionary<Type, Delegate> _indirectSerializers = new ConcurrentDictionary<Type, Delegate>();


private readonly static MethodInfo _writeTMethod = GetGenericWriteMethod(null);
private readonly static MethodInfo _writeNullableTMethod = GetGenericWriteMethod(typeof(Nullable<>));
private readonly static MethodInfo _writeDictionaryTMethod = GetGenericWriteMethod(typeof(IDictionary<,>));
private readonly static MethodInfo _writeEnumerableTMethod = GetGenericWriteMethod(typeof(IEnumerable<>));
private readonly static DateTime _epochTime = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
private static readonly byte[] _nilBytes = new byte[] { (byte)ETFType.SMALL_ATOM_EXT, 3, (byte)'n', (byte)'i', (byte)'l' };
private static readonly byte[] _falseBytes = new byte[] { (byte)ETFType.SMALL_ATOM_EXT, 5, (byte)'f', (byte)'a', (byte)'l', (byte)'s', (byte)'e' };
private static readonly byte[] _trueBytes = new byte[] { (byte)ETFType.SMALL_ATOM_EXT, 4, (byte)'t', (byte)'r', (byte)'u', (byte)'e' };

private static readonly MethodInfo _writeTMethod = GetGenericWriteMethod(null);
private static readonly MethodInfo _writeNullableTMethod = GetGenericWriteMethod(typeof(Nullable<>));
private static readonly MethodInfo _writeDictionaryTMethod = GetGenericWriteMethod(typeof(IDictionary<,>));
private static readonly MethodInfo _writeEnumerableTMethod = GetGenericWriteMethod(typeof(IEnumerable<>));
private static readonly DateTime _epochTime = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);


private readonly Stream _stream; private readonly Stream _stream;
private readonly byte[] _buffer; private readonly byte[] _buffer;
private readonly bool _leaveOpen; private readonly bool _leaveOpen;
private readonly Encoding _encoding; private readonly Encoding _encoding;
private readonly ConcurrentDictionary<Type, Delegate> _serializers, _indirectSerializers;


public virtual Stream BaseStream public virtual Stream BaseStream
{ {
@@ -45,8 +47,6 @@ namespace Discord.ETF
_leaveOpen = leaveOpen; _leaveOpen = leaveOpen;
_buffer = new byte[11]; _buffer = new byte[11];
_encoding = Encoding.UTF8; _encoding = Encoding.UTF8;
_serializers = new ConcurrentDictionary<Type, Delegate>();
_indirectSerializers = new ConcurrentDictionary<Type, Delegate>();
} }
public void Write(bool value) public void Write(bool value)
@@ -72,6 +72,7 @@ namespace Discord.ETF
} }
else if (value >= int.MinValue && value <= int.MaxValue) else if (value >= int.MinValue && value <= int.MaxValue)
{ {
//TODO: Does this encode negatives correctly?
_buffer[0] = (byte)ETFType.INTEGER_EXT; _buffer[0] = (byte)ETFType.INTEGER_EXT;
_buffer[1] = (byte)(value >> 24); _buffer[1] = (byte)(value >> 24);
_buffer[2] = (byte)(value >> 16); _buffer[2] = (byte)(value >> 16);
@@ -145,7 +146,7 @@ namespace Discord.ETF


public void Write(DateTime value) => Write((ulong)((value.Ticks - _epochTime.Ticks) / TimeSpan.TicksPerSecond)); public void Write(DateTime value) => Write((ulong)((value.Ticks - _epochTime.Ticks) / TimeSpan.TicksPerSecond));


public void Write(bool? value) { if (value.HasValue) Write((bool)value.Value); else WriteNil(); }
public void Write(bool? value) { if (value.HasValue) Write(value.Value); else WriteNil(); }
public void Write(sbyte? value) { if (value.HasValue) Write((long)value.Value); else WriteNil(); } public void Write(sbyte? value) { if (value.HasValue) Write((long)value.Value); else WriteNil(); }
public void Write(byte? value) { if (value.HasValue) Write((ulong)value.Value); else WriteNil(); } public void Write(byte? value) { if (value.HasValue) Write((ulong)value.Value); else WriteNil(); }
public void Write(short? value) { if (value.HasValue) Write((long)value.Value); else WriteNil(); } public void Write(short? value) { if (value.HasValue) Write((long)value.Value); else WriteNil(); }
@@ -158,35 +159,35 @@ namespace Discord.ETF
public void Write(float? value) { if (value.HasValue) Write((double)value.Value); else WriteNil(); } public void Write(float? value) { if (value.HasValue) Write((double)value.Value); else WriteNil(); }
public void Write(DateTime? value) { if (value.HasValue) Write(value.Value); else WriteNil(); } public void Write(DateTime? value) { if (value.HasValue) Write(value.Value); else WriteNil(); }


public void Write(byte[] value)
public void Write(string value)
{ {
if (value != null) if (value != null)
{ {
int count = value.Length;
var bytes = _encoding.GetBytes(value);
int count = bytes.Length;
_buffer[0] = (byte)ETFType.BINARY_EXT; _buffer[0] = (byte)ETFType.BINARY_EXT;
_buffer[1] = (byte)(count >> 24); _buffer[1] = (byte)(count >> 24);
_buffer[2] = (byte)(count >> 16); _buffer[2] = (byte)(count >> 16);
_buffer[3] = (byte)(count >> 8); _buffer[3] = (byte)(count >> 8);
_buffer[4] = (byte)count; _buffer[4] = (byte)count;
_stream.Write(_buffer, 0, 5); _stream.Write(_buffer, 0, 5);
_stream.Write(value, 0, value.Length);
_stream.Write(bytes, 0, bytes.Length);
} }
else else
WriteNil(); WriteNil();
} }
public void Write(string value)
public void Write(byte[] value)
{ {
if (value != null) if (value != null)
{ {
var bytes = _encoding.GetBytes(value);
int count = bytes.Length;
int count = value.Length;
_buffer[0] = (byte)ETFType.BINARY_EXT; _buffer[0] = (byte)ETFType.BINARY_EXT;
_buffer[1] = (byte)(count >> 24); _buffer[1] = (byte)(count >> 24);
_buffer[2] = (byte)(count >> 16); _buffer[2] = (byte)(count >> 16);
_buffer[3] = (byte)(count >> 8); _buffer[3] = (byte)(count >> 8);
_buffer[4] = (byte)count; _buffer[4] = (byte)count;
_stream.Write(_buffer, 0, 5); _stream.Write(_buffer, 0, 5);
_stream.Write(bytes, 0, bytes.Length);
_stream.Write(value, 0, value.Length);
} }
else else
WriteNil(); WriteNil();
@@ -269,7 +270,7 @@ namespace Discord.ETF
public virtual long Seek(int offset, SeekOrigin origin) => _stream.Seek(offset, origin); public virtual long Seek(int offset, SeekOrigin origin) => _stream.Seek(offset, origin);


#region Emit #region Emit
private Action<ETFWriter, T> CreateSerializer<T>(Type type, TypeInfo typeInfo, bool isDirect)
private static Action<ETFWriter, T> CreateSerializer<T>(Type type, TypeInfo typeInfo, bool isDirect)
{ {
var method = new DynamicMethod(isDirect ? "SerializeETF" : "SerializeIndirectETF", var method = new DynamicMethod(isDirect ? "SerializeETF" : "SerializeIndirectETF",
null, new[] { typeof(ETFWriter), isDirect ? type : typeof(object) }, true); null, new[] { typeof(ETFWriter), isDirect ? type : typeof(object) }, true);
@@ -291,7 +292,7 @@ namespace Discord.ETF
generator.Emit(OpCodes.Ret); generator.Emit(OpCodes.Ret);
return method.CreateDelegate(typeof(Action<ETFWriter, T>)) as Action<ETFWriter, T>; return method.CreateDelegate(typeof(Action<ETFWriter, T>)) as Action<ETFWriter, T>;
} }
private void EmitWriteValue(ILGenerator generator, Type type, TypeInfo typeInfo, bool isTop)
private static void EmitWriteValue(ILGenerator generator, Type type, TypeInfo typeInfo, bool isTop)
{ {
//Convert enum types to their base type //Convert enum types to their base type
if (typeInfo.IsEnum) if (typeInfo.IsEnum)
@@ -335,18 +336,21 @@ namespace Discord.ETF
//Dictionaries //Dictionaries
else if (!typeInfo.IsValueType && typeInfo.ImplementedInterfaces else if (!typeInfo.IsValueType && typeInfo.ImplementedInterfaces
.Any(x => x.IsConstructedGenericType && x.GetGenericTypeDefinition() == typeof(IDictionary<,>))) .Any(x => x.IsConstructedGenericType && x.GetGenericTypeDefinition() == typeof(IDictionary<,>)))
{
generator.EmitCall(OpCodes.Call, _writeDictionaryTMethod.MakeGenericMethod(typeInfo.GenericTypeParameters), null); generator.EmitCall(OpCodes.Call, _writeDictionaryTMethod.MakeGenericMethod(typeInfo.GenericTypeParameters), null);
}
//Enumerable //Enumerable
else if (!typeInfo.IsValueType && typeInfo.ImplementedInterfaces else if (!typeInfo.IsValueType && typeInfo.ImplementedInterfaces
.Any(x => x.IsConstructedGenericType && x.GetGenericTypeDefinition() == typeof(IEnumerable<>))) .Any(x => x.IsConstructedGenericType && x.GetGenericTypeDefinition() == typeof(IEnumerable<>)))
{
generator.EmitCall(OpCodes.Call, _writeEnumerableTMethod.MakeGenericMethod(typeInfo.GenericTypeParameters), null); generator.EmitCall(OpCodes.Call, _writeEnumerableTMethod.MakeGenericMethod(typeInfo.GenericTypeParameters), null);
}
//Nullable Structs //Nullable Structs
else if (typeInfo.IsGenericType && typeInfo.GetGenericTypeDefinition() == typeof(Nullable<>) &&
else if (typeInfo.IsGenericType && typeInfo.GetGenericTypeDefinition() == typeof(Nullable<>) &&
typeInfo.GenericTypeParameters[0].GetTypeInfo().IsValueType) typeInfo.GenericTypeParameters[0].GetTypeInfo().IsValueType)
{
generator.EmitCall(OpCodes.Call, _writeNullableTMethod.MakeGenericMethod(typeInfo.GenericTypeParameters), null); generator.EmitCall(OpCodes.Call, _writeNullableTMethod.MakeGenericMethod(typeInfo.GenericTypeParameters), null);
}
//Structs/Classes //Structs/Classes
else if (typeInfo.IsClass || (typeInfo.IsValueType && !typeInfo.IsPrimitive)) else if (typeInfo.IsClass || (typeInfo.IsValueType && !typeInfo.IsPrimitive))
{ {
@@ -387,7 +391,6 @@ namespace Discord.ETF
generator.EmitCall(OpCodes.Call, _writeTMethod.MakeGenericMethod(typeInfo.GenericTypeParameters), null); generator.EmitCall(OpCodes.Call, _writeTMethod.MakeGenericMethod(typeInfo.GenericTypeParameters), null);
} }
} }

//Unsupported (decimal, char) //Unsupported (decimal, char)
else else
throw new InvalidOperationException($"Serializing {type.Name} is not supported."); throw new InvalidOperationException($"Serializing {type.Name} is not supported.");
@@ -429,7 +432,7 @@ namespace Discord.ETF
{ {
return typeof(ETFWriter).GetTypeInfo().GetDeclaredMethods(nameof(Write)) return typeof(ETFWriter).GetTypeInfo().GetDeclaredMethods(nameof(Write))
.Where(x => x.GetParameters()[0].ParameterType == paramType) .Where(x => x.GetParameters()[0].ParameterType == paramType)
.FirstOrDefault();
.Single();
} }
private static MethodInfo GetGenericWriteMethod(Type genericType) private static MethodInfo GetGenericWriteMethod(Type genericType)
{ {


Loading…
Cancel
Save