Skip to content

Commit

Permalink
Merge pull request #6 from ndelta0/dev
Browse files Browse the repository at this point in the history
Merge dev into main
  • Loading branch information
ndelta0 authored May 27, 2022
2 parents 75c0c03 + 8f75d6b commit 4014de1
Show file tree
Hide file tree
Showing 6 changed files with 114 additions and 1 deletion.
10 changes: 10 additions & 0 deletions BinData.Tests/Deserializer/ValueTypeDeserializerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,16 @@ public void TypedCharTest()
Assert.Equal(expected, actual);
}

[Fact]
public void TypedDateTimeTest()
{
var value = new byte[] { 0x00, 0x1a, 0x26, 0xaa, 0xc4, 0x95, 0x1e, 0x01 };
var actual = BinaryConvert.Deserialize<DateTime>(value);
var expected = new DateTime(256, 8, 16, 0, 32, 4);

Assert.Equal(expected, actual);
}

[Fact]
public void TypedStringTest()
{
Expand Down
10 changes: 10 additions & 0 deletions BinData.Tests/Serializer/ValueTypeSerializerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,16 @@ public void CharTest()
Assert.Equal(expected, actual);
}

[Fact]
public void DateTimeTest()
{
var value = new DateTime(256, 8, 16, 0, 32, 4);
var actual = BinaryConvert.Serialize(value);
var expected = new byte[] { 0x00, 0x1a, 0x26, 0xaa, 0xc4, 0x95, 0x1e, 0x01 };

Assert.Equal(expected, actual);
}

public static IEnumerable<object[]> GetValueTupleData()
{
yield return new object[]
Expand Down
14 changes: 14 additions & 0 deletions BinData/BinaryStreamReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -107,4 +107,18 @@ public static byte[] ReadBytes(Stream stream)

return bytes;
}

public static unsafe T ReadStructure<T>(Stream stream) where T : unmanaged
{
Span<byte> buffer = stackalloc byte[sizeof(T)];
if (stream.Read(buffer) < sizeof(T))
ThrowHelper.ThrowEndOfStreamException();

if (!BitConverter.IsLittleEndian)
{
buffer.Reverse();
}

return MemoryMarshal.Read<T>(buffer);
}
}
14 changes: 14 additions & 0 deletions BinData/BinaryStreamWriter.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Buffers.Binary;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;

namespace BinData;
Expand Down Expand Up @@ -81,4 +82,17 @@ public static void WriteString(string value, Stream stream)
stream.Write(buffer);
stream.Write(bytes);
}

public static unsafe void WriteStructure<T>(T value, Stream stream) where T : unmanaged
{
Span<byte> buffer = stackalloc byte[sizeof(T)];
MemoryMarshal.Write(buffer, ref value);

if (!BitConverter.IsLittleEndian)
{
buffer.Reverse();
}

stream.Write(buffer);
}
}
35 changes: 34 additions & 1 deletion BinData/DeserializationContext.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Collections.Concurrent;
using System.Diagnostics.CodeAnalysis;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.CompilerServices;
Expand Down Expand Up @@ -28,6 +29,8 @@ internal sealed class DeserializationContext
.GetMethod(nameof(BinaryStreamReader.ReadString))!;
private static readonly MethodInfo _readBytes = typeof(BinaryStreamReader)
.GetMethod(nameof(BinaryStreamReader.ReadBytes))!;
private static readonly MethodInfo _readStructure = typeof(BinaryStreamReader)
.GetMethod(nameof(BinaryStreamReader.ReadStructure))!;

public DeserializationContext(Type type, ReadMethod read)
{
Expand Down Expand Up @@ -120,6 +123,10 @@ private static void AddReadExpression(BuildingInfo info)
{
AddReadNullableObjectExpression(info, AddReadClassExpression);
}
else if (IsReadableStructure(info.Type, out MethodInfo? readMethod))
{
AddReadStructureExpression(info, readMethod);
}
else
{
throw new NotSupportedException($"Unsupported type '{info.Type.FullName}'.");
Expand Down Expand Up @@ -175,10 +182,11 @@ private static void AddReadArrayExpression(BuildingInfo info)
private static void AddReadListExpression(BuildingInfo info)
{
PropertyInfo indexer = info.Type.GetProperties().FirstOrDefault(prop => prop.GetIndexParameters().Length == 1)!;
ConstructorInfo constructor = info.Type.GetConstructor(new[] { typeof(int) })!;
Type elementType = info.Type.GetGenericArguments()[0];

info.Add(Expression.Assign(info.IteratorEnd, Expression.Call(null, _readInt, info.Stream)));
info.Add(Expression.Assign(info.Value, Expression.New(info.Type)));
info.Add(Expression.Assign(info.Value, Expression.New(constructor, info.IteratorEnd)));

AddReadForLoopExpression(info, info =>
{
Expand Down Expand Up @@ -289,6 +297,11 @@ x.GetMethod is not null &&
}
}

private static void AddReadStructureExpression(BuildingInfo info, MethodInfo readMethod)
{
info.Add(Expression.Assign(info.Value, Expression.Call(null, readMethod, info.Stream)));
}

private static Expression ReadToTemporary(BuildingInfo info, Type type)
{
ParameterExpression temp = Expression.Variable(type);
Expand All @@ -307,4 +320,24 @@ private static Type ShadowInterfaces(Type type)

return type;
}

private static bool IsReadableStructure(Type type, [NotNullWhen(true)] out MethodInfo? readMethod)
{
if (!type.IsValueType)
{
readMethod = null;
return false;
}

try
{
readMethod = _readStructure.MakeGenericMethod(new[] { type });
return true;
}
catch
{
readMethod = null;
return false;
}
}
}
32 changes: 32 additions & 0 deletions BinData/SerializationContext.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Collections.Concurrent;
using System.Diagnostics.CodeAnalysis;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.CompilerServices;
Expand Down Expand Up @@ -27,6 +28,8 @@ internal sealed class SerializationContext
.GetMethod(nameof(BinaryStreamWriter.WritePrimitive))!.MakeGenericMethod(new[] { typeof(int) });
private static readonly MethodInfo _writeSpan = typeof(Stream)
.GetMethod(nameof(Stream.Write), new[] { typeof(ReadOnlySpan<byte>) })!;
private static readonly MethodInfo _writeStructure = typeof(BinaryStreamWriter)
.GetMethod(nameof(BinaryStreamWriter.WriteStructure))!;

public SerializationContext(Type type, WriteMethod write)
{
Expand Down Expand Up @@ -111,6 +114,10 @@ private static void AddWriteExpression(BuildingInfo info)
{
AddWriteNullableObjectExpression(info, AddWriteClassExpression);
}
else if (IsWritableStruct(info.Type, out MethodInfo? writeMethod))
{
AddWriteStructureExpression(info, writeMethod);
}
else
{
throw new NotSupportedException($"Unsupported type '{info.Type.FullName}'.");
Expand Down Expand Up @@ -258,4 +265,29 @@ private static void AddWriteNullableObjectExpression(BuildingInfo info, Action<B
info.Add(Expression.Call(info.Stream, _streamWriteByte, _zeroByteExpression));
info.Add(Expression.Label(endLabel));
}

private static void AddWriteStructureExpression(BuildingInfo info, MethodInfo writeMethod)
{
info.Add(Expression.Call(null, writeMethod, info.Value, info.Stream));
}

private static bool IsWritableStruct(Type type, [NotNullWhen(true)] out MethodInfo? writeMethod)
{
if (!type.IsValueType)
{
writeMethod = null;
return false;
}

try
{
writeMethod = _writeStructure.MakeGenericMethod(new[] { type });
return true;
}
catch
{
writeMethod = null;
return false;
}
}
}

0 comments on commit 4014de1

Please sign in to comment.