Compare commits
2 commits
0d4015b07e
...
42d572d857
Author | SHA1 | Date | |
---|---|---|---|
42d572d857 | |||
39ca103842 |
5 changed files with 272 additions and 16 deletions
|
@ -1,4 +1,5 @@
|
||||||
using System.Buffers.Binary;
|
using System.Buffers.Binary;
|
||||||
|
using System.Data;
|
||||||
|
|
||||||
namespace Nyanbyte.Sysnya.Binary;
|
namespace Nyanbyte.Sysnya.Binary;
|
||||||
|
|
||||||
|
@ -15,6 +16,10 @@ public class BinaryEnumerator
|
||||||
private readonly ReadOnlyMemory<byte> _fullData;
|
private readonly ReadOnlyMemory<byte> _fullData;
|
||||||
private int _position = 0;
|
private int _position = 0;
|
||||||
|
|
||||||
|
public int Position => _position;
|
||||||
|
public int Remaining => _fullData.Length - _position;
|
||||||
|
public int Size => _fullData.Length;
|
||||||
|
|
||||||
public BinaryEnumerator Skip(int length)
|
public BinaryEnumerator Skip(int length)
|
||||||
{
|
{
|
||||||
_position += length;
|
_position += length;
|
||||||
|
@ -34,6 +39,19 @@ public class BinaryEnumerator
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public uint ReadUInt32()
|
||||||
|
{
|
||||||
|
uint value = _bigEndian ? BinaryPrimitives.ReadUInt32BigEndian(_fullData.Span[_position..(_position + 4)]) : BinaryPrimitives.ReadUInt32LittleEndian(_fullData.Span[_position..(_position + 4)]);
|
||||||
|
_position += 4;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BinaryEnumerator ReadUInt32(out uint value)
|
||||||
|
{
|
||||||
|
value = ReadUInt32();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public long ReadInt64()
|
public long ReadInt64()
|
||||||
{
|
{
|
||||||
long value = _bigEndian ? BinaryPrimitives.ReadInt64BigEndian(_fullData.Span[_position..(_position + 8)]) : BinaryPrimitives.ReadInt64LittleEndian(_fullData.Span[_position..(_position + 8)]);
|
long value = _bigEndian ? BinaryPrimitives.ReadInt64BigEndian(_fullData.Span[_position..(_position + 8)]) : BinaryPrimitives.ReadInt64LittleEndian(_fullData.Span[_position..(_position + 8)]);
|
||||||
|
@ -46,4 +64,87 @@ public class BinaryEnumerator
|
||||||
value = ReadInt64();
|
value = ReadInt64();
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ulong ReadUInt64()
|
||||||
|
{
|
||||||
|
ulong value = _bigEndian ? BinaryPrimitives.ReadUInt64BigEndian(_fullData.Span[_position..(_position + 8)]) : BinaryPrimitives.ReadUInt64LittleEndian(_fullData.Span[_position..(_position + 8)]);
|
||||||
|
_position += 8;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BinaryEnumerator ReadUInt64(out ulong value)
|
||||||
|
{
|
||||||
|
value = ReadUInt64();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public short ReadInt16()
|
||||||
|
{
|
||||||
|
short value = _bigEndian ? BinaryPrimitives.ReadInt16BigEndian(_fullData.Span[_position..(_position + 2)]) : BinaryPrimitives.ReadInt16LittleEndian(_fullData.Span[_position..(_position + 2)]);
|
||||||
|
_position += 2;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BinaryEnumerator ReadInt16(out short value)
|
||||||
|
{
|
||||||
|
value = ReadInt16();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ushort ReadUInt16()
|
||||||
|
{
|
||||||
|
ushort value = _bigEndian ? BinaryPrimitives.ReadUInt16BigEndian(_fullData.Span[_position..(_position + 2)]) : BinaryPrimitives.ReadUInt16LittleEndian(_fullData.Span[_position..(_position + 2)]);
|
||||||
|
_position += 2;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BinaryEnumerator ReadUInt16(out ushort value)
|
||||||
|
{
|
||||||
|
value = ReadUInt16();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float ReadF32()
|
||||||
|
{
|
||||||
|
float value = _bigEndian ? BinaryPrimitives.ReadSingleBigEndian(_fullData.Span.Slice(_position)) : BinaryPrimitives.ReadSingleLittleEndian(_fullData.Span.Slice(_position));
|
||||||
|
_position += 4;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BinaryEnumerator ReadF32(out float value)
|
||||||
|
{
|
||||||
|
value = ReadF32();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double ReadF64()
|
||||||
|
{
|
||||||
|
double value = _bigEndian ? BinaryPrimitives.ReadDoubleBigEndian(_fullData.Span.Slice(_position)) : BinaryPrimitives.ReadDoubleLittleEndian(_fullData.Span.Slice(_position));
|
||||||
|
_position += 8;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BinaryEnumerator ReadF64(out double value)
|
||||||
|
{
|
||||||
|
value = ReadF64();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TNumeric ReadNumeric<TNumeric>() where TNumeric : struct, IConvertible
|
||||||
|
{
|
||||||
|
//TODO Refactor
|
||||||
|
object val = typeof(TNumeric).Name switch
|
||||||
|
{
|
||||||
|
"Int16" => ReadInt16(),
|
||||||
|
"UInt16" => ReadUInt16(),
|
||||||
|
"Int32" => ReadInt32(),
|
||||||
|
"UInt32" => ReadUInt32(),
|
||||||
|
"Int64" => ReadInt64(),
|
||||||
|
"UInt64" => ReadUInt64(),
|
||||||
|
|
||||||
|
_ => throw new DataException($"Invalid type: {typeof(TNumeric).FullName}")
|
||||||
|
};
|
||||||
|
|
||||||
|
return (TNumeric)val;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,23 +3,105 @@ using System.Linq.Expressions;
|
||||||
|
|
||||||
namespace Nyanbyte.Sysnya.Binary;
|
namespace Nyanbyte.Sysnya.Binary;
|
||||||
|
|
||||||
public class BinaryStructMapperBuilder<T>
|
public class BinaryStructMapperBuilder<T> where T : notnull, new()
|
||||||
{
|
{
|
||||||
public BinaryStructMapperBuilder(T obj)
|
public BinaryStructMapperBuilder(int minMemoryLen = 0, bool bigEndian = true)
|
||||||
{
|
{
|
||||||
ArgumentNullException.ThrowIfNull(obj);
|
_minMemoryLen = minMemoryLen;
|
||||||
_obj = obj;
|
_bigEndian = bigEndian;
|
||||||
}
|
}
|
||||||
|
private readonly int _minMemoryLen;
|
||||||
|
private readonly bool _bigEndian;
|
||||||
|
private readonly LinkedList<Action<T, BinaryEnumerator>> _funcPipeline = new();
|
||||||
|
|
||||||
[NotNull]
|
public BinaryStructMapperBuilder<T> BindI16<M>(Expression<Func<T, M>> property)
|
||||||
private readonly T _obj;
|
|
||||||
private readonly Stack<Action<Memory<byte>>> _funcStack = new();
|
|
||||||
|
|
||||||
public BinaryStructMapperBuilder<T> BindU32<M>(Expression<Func<M, T>> property)
|
|
||||||
{
|
{
|
||||||
var member = property.Body as MemberExpression;
|
Bind<M, short>(property);
|
||||||
ArgumentNullException.ThrowIfNull(member);
|
|
||||||
_funcStack.Push(m => _obj.GetType().GetProperty(member.Member.Name)!.SetValue(_obj, m));
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public BinaryStructMapperBuilder<T> BindU16<M>(Expression<Func<T, M>> property)
|
||||||
|
{
|
||||||
|
Bind<M, ushort>(property);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BinaryStructMapperBuilder<T> BindI32<M>(Expression<Func<T, M>> property)
|
||||||
|
{
|
||||||
|
Bind<M, int>(property);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BinaryStructMapperBuilder<T> BindU32<M>(Expression<Func<T, M>> property)
|
||||||
|
{
|
||||||
|
Bind<M, uint>(property);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BinaryStructMapperBuilder<T> BindI64<M>(Expression<Func<T, M>> property)
|
||||||
|
{
|
||||||
|
Bind<M, long>(property);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BinaryStructMapperBuilder<T> BindU64<M>(Expression<Func<T, M>> property)
|
||||||
|
{
|
||||||
|
Bind<M, ulong>(property);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Bind<M, TNumeric>(Expression<Func<T, M>> p) where TNumeric : struct, IConvertible
|
||||||
|
{
|
||||||
|
var member = p.Body as MemberExpression;
|
||||||
|
ArgumentNullException.ThrowIfNull(member);
|
||||||
|
var prop = typeof(T).GetProperty(member.Member.Name)!;
|
||||||
|
_funcPipeline.AddLast((obj, enu) =>
|
||||||
|
{
|
||||||
|
TNumeric value = enu.ReadNumeric<TNumeric>();
|
||||||
|
prop.SetValue(obj, value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public BinaryStructMapper<T> Build()
|
||||||
|
{
|
||||||
|
return new BinaryStructMapper<T>(_funcPipeline, _minMemoryLen, _bigEndian);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class BinaryStructMapper<T> where T : notnull, new()
|
||||||
|
{
|
||||||
|
internal BinaryStructMapper(LinkedList<Action<T, BinaryEnumerator>> funcPipeline, int minMemoryLen, bool bigEndian)
|
||||||
|
{
|
||||||
|
_funcPipeline = funcPipeline;
|
||||||
|
_minMemoryLen = minMemoryLen;
|
||||||
|
_bigEndian = bigEndian;
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly int _minMemoryLen;
|
||||||
|
private readonly bool _bigEndian;
|
||||||
|
private readonly LinkedList<Action<T, BinaryEnumerator>> _funcPipeline;
|
||||||
|
|
||||||
|
public T Map(byte[] bytes)
|
||||||
|
{
|
||||||
|
return Map(bytes.AsMemory());
|
||||||
|
}
|
||||||
|
|
||||||
|
public T Map(Memory<byte> bytes)
|
||||||
|
{
|
||||||
|
BinaryEnumerator enu = new(bytes, _bigEndian);
|
||||||
|
if (bytes.Length < _minMemoryLen)
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"Data shorter than minimum expected: expected {_minMemoryLen} bytes; got: {bytes.Length} bytes");
|
||||||
|
}
|
||||||
|
|
||||||
|
T obj = Activator.CreateInstance<T>();
|
||||||
|
var enumerator = _funcPipeline.GetEnumerator();
|
||||||
|
while (enumerator.MoveNext())
|
||||||
|
{
|
||||||
|
var func = enumerator.Current;
|
||||||
|
func.Invoke(obj, enu);
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
73
test/Nyanbyte.Sysnya.CoreTests/Binary/BinaryMapperTests.cs
Normal file
73
test/Nyanbyte.Sysnya.CoreTests/Binary/BinaryMapperTests.cs
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
using System;
|
||||||
|
using System.Buffers.Binary;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection.Metadata;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Nyanbyte.Sysnya.Binary;
|
||||||
|
|
||||||
|
namespace Nyanbyte.Sysnya.CoreTests.Binary;
|
||||||
|
|
||||||
|
public class BinaryMapperTests
|
||||||
|
{
|
||||||
|
|
||||||
|
private class Dummy
|
||||||
|
{
|
||||||
|
public short Int16 { get; set; }
|
||||||
|
public ushort UInt16 { get; set; }
|
||||||
|
public int Int32 { get; set; }
|
||||||
|
public uint UInt32 { get; set; }
|
||||||
|
public long Int64 { get; set; }
|
||||||
|
public ulong UInt64 { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Map32Bit()
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
Span<byte> bytes = stackalloc byte[8];
|
||||||
|
BinaryPrimitives.WriteUInt32BigEndian(bytes, 0xfeedbeef);
|
||||||
|
BinaryPrimitives.WriteInt32BigEndian(bytes[4..], 0xbeef);
|
||||||
|
|
||||||
|
BinaryStructMapperBuilder<Dummy> b = new(0, true);
|
||||||
|
|
||||||
|
// When
|
||||||
|
var mapper = b.BindU32(p => p.UInt32).BindI32(p => p.Int32).Build();
|
||||||
|
var dummy = mapper.Map(bytes.ToArray());
|
||||||
|
|
||||||
|
// Then
|
||||||
|
Assert.Equal(0xfeedbeef, dummy.UInt32);
|
||||||
|
Assert.Equal(0xbeef, dummy.Int32);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MapAll()
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
Span<byte> bytes = stackalloc byte[28];
|
||||||
|
BinaryPrimitives.WriteUInt16BigEndian(bytes[0..], 0xbabe);
|
||||||
|
BinaryPrimitives.WriteInt16BigEndian(bytes[2..], 0xbf);
|
||||||
|
BinaryPrimitives.WriteUInt32BigEndian(bytes[4..], 0xfeedbeef);
|
||||||
|
BinaryPrimitives.WriteInt32BigEndian(bytes[8..], 0xbeef);
|
||||||
|
BinaryPrimitives.WriteUInt64BigEndian(bytes[12..], 0xbad1dea0babebebe);
|
||||||
|
BinaryPrimitives.WriteInt64BigEndian(bytes[20..], 0x1dea0babebebe);
|
||||||
|
|
||||||
|
BinaryStructMapperBuilder<Dummy> b = new(0, true);
|
||||||
|
|
||||||
|
// When
|
||||||
|
var mapper = b
|
||||||
|
.BindU16(p => p.UInt16).BindI16(p => p.Int16)
|
||||||
|
.BindU32(p => p.UInt32).BindI32(p => p.Int32)
|
||||||
|
.BindU64(p => p.UInt64).BindI64(p => p.Int64)
|
||||||
|
.Build();
|
||||||
|
var dummy = mapper.Map(bytes.ToArray());
|
||||||
|
|
||||||
|
// Then
|
||||||
|
Assert.Equal(0xbabe, dummy.UInt16);
|
||||||
|
Assert.Equal(0xbf, dummy.Int16);
|
||||||
|
Assert.Equal(0xfeedbeef, dummy.UInt32);
|
||||||
|
Assert.Equal(0xbeef, dummy.Int32);
|
||||||
|
Assert.Equal(0xbad1dea0babebebe, dummy.UInt64);
|
||||||
|
Assert.Equal(0x1dea0babebebe, dummy.Int64);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
|
|
||||||
|
@ -21,8 +21,8 @@
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\..\src\Nyanbyte.Sysnya\Nyanbyte.Sysnya.csproj" />
|
<ProjectReference Include="..\..\src\Nyanbyte.Sysnya\Nyanbyte.Sysnya.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
Loading…
Reference in a new issue