Skip to content

Commit

Permalink
Temp.
Browse files Browse the repository at this point in the history
  • Loading branch information
GGG-KILLER committed Jan 22, 2024
1 parent 81fa68f commit fd0e3b2
Show file tree
Hide file tree
Showing 7 changed files with 361 additions and 0 deletions.
114 changes: 114 additions & 0 deletions Tsu.BinaryParser/src/Builder/AbstractParser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
// Copyright © 2021 GGG KILLER <[email protected]>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software
// and associated documentation files (the “Software”), to deal in the Software without
// restriction, including without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom
// the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
// BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using Tsu.BinaryParser.Parsers;

namespace Tsu.BinaryParser.Builder
{
/// <summary>
/// A parser builder for complex objects.
/// </summary>
/// <typeparam name="T">The type of object being parsed.</typeparam>
public abstract class AbstractParser<[DynamicallyAccessedMembers(
DynamicallyAccessedMemberTypes.PublicParameterlessConstructor
| DynamicallyAccessedMemberTypes.PublicFields
| DynamicallyAccessedMemberTypes.PublicProperties)] T> : IBinaryParser<T>
{
private readonly List<ParserStep> _steps = new();

/// <summary>
/// Initializes a new parser builder
/// </summary>
public AbstractParser()
{
if (!typeof(T).GetConstructors().Any(ctor => ctor.IsPublic && ctor.GetParameters().Length == 0))
throw new InvalidOperationException("Parsed types must have a public parameterless constructor.");
}

/// <summary>
/// All steps currently registered in this parser.
/// </summary>
public IEnumerable<ParserStep> Steps => _steps;

public abstract Option<int> MinimumByteCount { get; }
public abstract Option<int> MaxmiumByteCount { get; }
public abstract bool IsFixedSize { get; }

/// <summary>
/// Adds a new <see cref="MemberBindingStep" /> to the parser.
/// </summary>
/// <typeparam name="TMember">The type of the member being read/written to.</typeparam>
/// <param name="expression">
/// The member to be used in this step.
/// </param>
/// <param name="parser">
/// The parser that is able to handle the type of this member.
/// </param>
/// <returns>
/// The parser itself.
/// </returns>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ArgumentException"></exception>
protected AbstractParser<T> Add<TMember>(Expression<Func<T, TMember>> expression, IBinaryParser<T> parser)
{
if (expression is null)
throw new ArgumentNullException(nameof(expression));
if (expression.Body.NodeType != ExpressionType.MemberAccess)
throw new ArgumentException("Provided expression is not a member access expression.", nameof(expression));

var memberExpression = (MemberExpression) expression.Body;
if (memberExpression.Expression?.NodeType != ExpressionType.Parameter)
throw new ArgumentException("Only direct members of the class can be used.", nameof(expression));

if (memberExpression.Member is FieldInfo fieldInfo)
{
if (fieldInfo.IsInitOnly)
throw new ArgumentException("The provided field cannot be written to.", nameof(expression));
_steps.Add(new MemberBindingStep(fieldInfo, parser));
}
else if (memberExpression.Member is PropertyInfo propertyInfo)
{
if (!propertyInfo.CanRead)
throw new ArgumentException("The provided property cannot be read from.");
if (!propertyInfo.CanWrite)
throw new ArgumentException("The provided property cannot be written to.");
_steps.Add(new MemberBindingStep(propertyInfo, parser));
}
else
{
throw new ArgumentException("Member being accessed is not a field nor property.");
}

return this;
}

public abstract long CalculateSize(T value);
public abstract T Deserialize(IBinaryReader reader, IBinaryParsingContext context);
public abstract ValueTask<T> DeserializeAsync(IBinaryReader reader, IBinaryParsingContext context, CancellationToken cancellationToken = default);
public abstract void Serialize(Stream stream, IBinaryParsingContext context, T value);
public abstract ValueTask SerializeAsync(Stream stream, IBinaryParsingContext context, T value, CancellationToken cancellationToken = default);
}
}
54 changes: 54 additions & 0 deletions Tsu.BinaryParser/src/Builder/ParserSteps.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright © 2021 GGG KILLER <[email protected]>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software
// and associated documentation files (the “Software”), to deal in the Software without
// restriction, including without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom
// the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
// BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

using System.Reflection;

namespace Tsu.BinaryParser.Builder
{
/// <summary>
/// Represents a base parser step.
/// </summary>
public abstract class ParserStep
{
private protected ParserStep() { }
}

/// <summary>
/// Represents a parser step that binds the a parser to a field.
/// </summary>
public sealed class MemberBindingStep : ParserStep
{
internal MemberBindingStep(MemberInfo member, object parser)
{
InternalDebug.AssertNotNull(member);
InternalDebug.AssertNotNull(parser);

Member = member;
Parser = parser;
}

/// <summary>
/// The field's reflection info.
/// </summary>
public MemberInfo Member { get; }

/// <summary>
/// The parser to use for the field.
/// </summary>
public object Parser { get; }
}
}
1 change: 1 addition & 0 deletions Tsu.BinaryParser/src/Tsu.BinaryParser.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

<PropertyGroup>
<TsuProjectType>Library</TsuProjectType>
<TargetFrameworks>net7.0</TargetFrameworks>
<TargetFrameworks>$(LibraryTargetFrameworks)</TargetFrameworks>

<LangVersion>preview</LangVersion>
Expand Down
176 changes: 176 additions & 0 deletions Tsu.BinaryParser/test/Parsers/ChangeEndianessParserTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@

using System.IO;
using System.Threading.Tasks;
using Tsu.BinaryParser.Parsers;

namespace Tsu.BinaryParser.Tests.Parsers;

public class ChangeEndianessParserTests
{
[Fact]
public void ChangeEndianessParser_HasZeroByteCount()
{
// Given
var parser = new ChangeEndianessParser(Endianess.LittleEndian);

// When
var min = parser.MinimumByteCount;
var max = parser.MaxmiumByteCount;

// Then
Assert.True(min.IsSome);
Assert.Equal(0, min.Value);
Assert.True(max.IsSome);
Assert.Equal(0, max.Value);
}

[Fact]
public void ChangeEndianessParser_HasFixedSize()
{
// Given
var parser = new ChangeEndianessParser(Endianess.LittleEndian);

// When
var isFixedSize = parser.IsFixedSize;

// Then
Assert.True(isFixedSize);
}

[Fact]
public void ChangeEndianessParser_CalculateSize_AlwaysReturnsZero()
{
// Given
var parser = new ChangeEndianessParser(Endianess.LittleEndian);

// When
var size = parser.CalculateSize(Unit.Value);

// Then
Assert.Equal(0, size);
}

[Fact]
public void ChangeEndianessParser_Deserialize_ChangesContext()
{
// Given
using var stream = new MemoryStream();
using var reader = new BinaryReader(stream);
var context = new BinaryParsingContext(Endianess.BigEndian);
var parser = new ChangeEndianessParser(Endianess.LittleEndian);

// When
parser.Deserialize(reader, context);

// Then
Assert.Equal(Endianess.LittleEndian, context.Endianess);
}

[Fact]
public void ChangeEndianessParser_Deserialize_DoesNotConsumeFromStream()
{
// Given
using var stream = TestData.RandomStream(128);
using var reader = new BinaryReader(stream);
var context = new BinaryParsingContext(Endianess.BigEndian);
var parser = new ChangeEndianessParser(Endianess.LittleEndian);

// When
parser.Deserialize(reader, context);

// Then
Assert.Equal(0, stream.Position);
}

[Fact]
public async Task ChangeEndianessParser_DeserializeAsync_ChangesContext()
{
// Given
using var stream = new MemoryStream();
using var reader = new BinaryReader(stream);
var context = new BinaryParsingContext(Endianess.BigEndian);
var parser = new ChangeEndianessParser(Endianess.LittleEndian);

// When
await parser.DeserializeAsync(reader, context);

// Then
Assert.Equal(Endianess.LittleEndian, context.Endianess);
}

[Fact]
public async Task ChangeEndianessParser_DeserializeAsync_DoesNotConsumeFromStream()
{
// Given
using var stream = TestData.RandomStream(128);
using var reader = new BinaryReader(stream);
var context = new BinaryParsingContext(Endianess.BigEndian);
var parser = new ChangeEndianessParser(Endianess.LittleEndian);

// When
await parser.DeserializeAsync(reader, context);

// Then
Assert.Equal(0, stream.Position);
}

[Fact]
public void ChangeEndianessParser_Serialize_ChangesContext()
{
// Given
using var stream = new MemoryStream();
var context = new BinaryParsingContext(Endianess.BigEndian);
var parser = new ChangeEndianessParser(Endianess.LittleEndian);

// When
parser.Serialize(stream, context, Unit.Value);

// Then
Assert.Equal(Endianess.LittleEndian, context.Endianess);
}

[Fact]
public void ChangeEndianessParser_Serialize_DoesNotConsumeFromStream()
{
// Given
using var stream = new MemoryStream();
var context = new BinaryParsingContext(Endianess.BigEndian);
var parser = new ChangeEndianessParser(Endianess.LittleEndian);

// When
parser.Serialize(stream, context, Unit.Value);

// Then
Assert.Equal(0, stream.Position);
}

[Fact]
public async Task ChangeEndianessParser_SerializeAsync_ChangesContext()
{
// Given
using var stream = new MemoryStream();
var context = new BinaryParsingContext(Endianess.BigEndian);
var parser = new ChangeEndianessParser(Endianess.LittleEndian);

// When
await parser.SerializeAsync(stream, context, Unit.Value);

// Then
Assert.Equal(Endianess.LittleEndian, context.Endianess);
}

[Fact]
public async Task ChangeEndianessParser_SerializeAsync_DoesNotConsumeFromStream()
{
// Given
using var stream = TestData.RandomStream(128);
var context = new BinaryParsingContext(Endianess.BigEndian);
var parser = new ChangeEndianessParser(Endianess.LittleEndian);

// When
await parser.SerializeAsync(stream, context, Unit.Value);

// Then
Assert.Equal(0, stream.Position);
}
}
Empty file.
15 changes: 15 additions & 0 deletions Tsu.BinaryParser/test/TestData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System;
using System.IO;

namespace Tsu.BinaryParser.Tests;

internal static class TestData
{
public static MemoryStream RandomStream(int length)
{
var buffer = new byte[length];
var random = new Random(0xA23B6FA);
random.NextBytes(buffer);
return new MemoryStream(buffer);
}
}
1 change: 1 addition & 0 deletions Tsu.BinaryParser/test/Tsu.BinaryParser.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
<ItemGroup>
<ProjectReference Include="$(RepositoryRootDirectory)Tsu.Testing/src/Tsu.Testing.csproj" />
<ProjectReference Include="$(RepositoryRootDirectory)Tsu/src/Tsu.csproj" />
<ProjectReference Include="$(RepositoryRootDirectory)Tsu.BinaryParser/src/Tsu.BinaryParser.csproj" />
</ItemGroup>

</Project>

0 comments on commit fd0e3b2

Please sign in to comment.