Added Bool, Integer and Nullable support to ETFReader
This commit is contained in:
@@ -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;
|
||||
|
||||
private void ReadNil(bool allow)
|
||||
|
||||
public ETFReader(Stream stream, bool leaveOpen = false)
|
||||
{
|
||||
if (!allow) throw new InvalidDataException();
|
||||
if (stream == null) throw new ArgumentNullException(nameof(stream));
|
||||
|
||||
_stream = stream;
|
||||
_leaveOpen = leaveOpen;
|
||||
_buffer = new byte[11];
|
||||
_encoding = Encoding.UTF8;
|
||||
}
|
||||
|
||||
private bool ReadNil(bool ignoreLength = false)
|
||||
{
|
||||
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)
|
||||
{
|
||||
_stream.Read(_buffer, 0, 2);
|
||||
switch ((ETFType)_buffer[0])
|
||||
{
|
||||
case ETFType.SMALL_ATOM_EXT:
|
||||
switch (_buffer[1]) //Length
|
||||
{
|
||||
case 3:
|
||||
ReadNil(allowNil);
|
||||
return null;
|
||||
case 4:
|
||||
ReadTrue();
|
||||
return true;
|
||||
case 5:
|
||||
ReadFalse();
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
throw new InvalidDataException();
|
||||
}
|
||||
|
||||
public long? ReadInteger(bool allowNil)
|
||||
public bool? ReadNullableBool()
|
||||
{
|
||||
_stream.Read(_buffer, 0, 1);
|
||||
ETFType type = (ETFType)reader.ReadByte();
|
||||
switch (type)
|
||||
ETFType type = (ETFType)_buffer[0];
|
||||
if (type == ETFType.SMALL_ATOM_EXT)
|
||||
{
|
||||
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]);
|
||||
case ETFType.INTEGER_EXT:
|
||||
_stream.Read(_buffer, 0, 4);
|
||||
return ??;
|
||||
case ETFType.SMALL_BIG_EXT:
|
||||
return ??;
|
||||
_stream.Read(_buffer, 0, 1);
|
||||
switch (_buffer[0]) //Length
|
||||
{
|
||||
case 3:
|
||||
if (ReadNil())
|
||||
return null;
|
||||
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 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 int ReadSByte()
|
||||
{
|
||||
if (value >= byte.MinValue && value <= byte.MaxValue)
|
||||
_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)_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)
|
||||
{
|
||||
_buffer[0] = (byte)ETFType.SMALL_INTEGER_EXT;
|
||||
_buffer[1] = (byte)value;
|
||||
_stream.Write(_buffer, 0, 2);
|
||||
case ETFType.SMALL_INTEGER_EXT:
|
||||
_stream.Read(_buffer, 0, 1);
|
||||
return _buffer[0];
|
||||
case ETFType.INTEGER_EXT:
|
||||
_stream.Read(_buffer, 0, 4);
|
||||
return (_buffer[0] << 24) | (_buffer[1] << 16) | (_buffer[2] << 8) | (_buffer[3]);
|
||||
case ETFType.SMALL_BIG_EXT:
|
||||
_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;
|
||||
}
|
||||
else if (value >= int.MinValue && value <= int.MaxValue)
|
||||
throw new InvalidDataException();
|
||||
}
|
||||
|
||||
public float ReadSingle()
|
||||
{
|
||||
_stream.Read(_buffer, 0, 1);
|
||||
ETFType type = (ETFType)_buffer[0];
|
||||
return (float)ReadDoubleInternal(type);
|
||||
}
|
||||
public float? ReadNullableSingle()
|
||||
{
|
||||
_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.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);
|
||||
type = Enum.GetUnderlyingType(type);
|
||||
typeInfo = type.GetTypeInfo();
|
||||
}
|
||||
else
|
||||
//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.SMALL_BIG_EXT;
|
||||
if (value < 0)
|
||||
//No conversion needed
|
||||
generator.EmitCall(OpCodes.Call, GetReadMethod(type), null);
|
||||
}
|
||||
//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<>)))
|
||||
{
|
||||
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;
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
byte bytes = 0;
|
||||
while (value > 0)
|
||||
_buffer[3 + bytes++] = (byte)(value >>= 8);
|
||||
_buffer[1] = bytes; //Encoded bytes
|
||||
|
||||
_stream.Write(_buffer, 0, 3 + bytes);
|
||||
}
|
||||
}
|
||||
public void Write(ulong value)
|
||||
{
|
||||
if (value <= byte.MaxValue)
|
||||
{
|
||||
_buffer[0] = (byte)ETFType.SMALL_INTEGER_EXT;
|
||||
_buffer[1] = (byte)value;
|
||||
_stream.Write(_buffer, 0, 2);
|
||||
}
|
||||
else if (value <= int.MaxValue)
|
||||
{
|
||||
_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);
|
||||
}
|
||||
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.");
|
||||
}
|
||||
|
||||
private static bool IsType(Type type, params Type[] types)
|
||||
{
|
||||
for (int i = 0; i < types.Length; i++)
|
||||
{
|
||||
_buffer[0] = (byte)ETFType.SMALL_BIG_EXT;
|
||||
_buffer[2] = 0; //Always positive
|
||||
if (type == types[i])
|
||||
return true;
|
||||
}
|
||||
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)
|
||||
{
|
||||
name = attrib.ConstructorArguments.FirstOrDefault().Value as string ?? f.Name;
|
||||
return true;
|
||||
}
|
||||
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)
|
||||
{
|
||||
name = attrib.ConstructorArguments.FirstOrDefault().Value as string ?? p.Name;
|
||||
return true;
|
||||
}
|
||||
name = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
byte bytes = 0;
|
||||
while (value > 0)
|
||||
_buffer[3 + bytes++] = (byte)(value >>= 8);
|
||||
_buffer[1] = bytes; //Encoded bytes
|
||||
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
|
||||
|
||||
_stream.Write(_buffer, 0, 3 + bytes);
|
||||
#region IDisposable
|
||||
private bool _isDisposed = false;
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!_isDisposed)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
if (_leaveOpen)
|
||||
_stream.Flush();
|
||||
else
|
||||
_stream.Dispose();
|
||||
}
|
||||
_isDisposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void Write(float value) => Write((double)value);
|
||||
public unsafe void Write(double value)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
public void Write(DateTime value) => Write((ulong)((value.Ticks - _epochTime.Ticks) / TimeSpan.TicksPerSecond));
|
||||
public void Dispose() => Dispose(true);
|
||||
#endregion
|
||||
}
|
||||
}*/
|
||||
}
|
||||
Reference in New Issue
Block a user