Skip to content

Commit

Permalink
Added basic support for fixed-point numbers.
Browse files Browse the repository at this point in the history
  • Loading branch information
MeltyPlayer committed Dec 1, 2024
1 parent 037e660 commit 7d51b01
Show file tree
Hide file tree
Showing 19 changed files with 430 additions and 130 deletions.
50 changes: 50 additions & 0 deletions Schema Build Tests/binary/FixedPointTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
using System.Collections.Generic;
using System.IO;
using System.Numerics;

using NUnit.Framework;

using schema.binary.attributes;
using schema.testing;
using schema.util.asserts;


namespace schema.binary;

public partial class FixedPointTests {
[BinarySchema]
private partial class FixedPointWrapper : IBinaryConvertible {
[FixedPoint(1, 19, 12)]
public float FloatValue { get; set; }

[FixedPoint(1, 19, 12)]
public double DoubleValue { get; set; }
}

[Test]
public void TestReadsAsExpected() {
using var br = SchemaMemoryStream.From([123, 456]).GetBinaryReader();

var wrapper = br.ReadNew<FixedPointWrapper>();
Assert.AreEqual(0.0300292969f, wrapper.FloatValue);
Assert.AreEqual(0.111328125, wrapper.DoubleValue);
}

[Test]
public void TestWritesAsExpected() {
var wrapper = new FixedPointWrapper {
FloatValue = 0.0300292969f, DoubleValue = 0.111328125,
};

var bw = new SchemaBinaryWriter();
wrapper.Write(bw);

using var ms = new MemoryStream();
bw.CompleteAndCopyTo(ms);
ms.Position = 0;

var br = new SchemaBinaryReader(ms);
Assert.AreEqual(123, br.ReadUInt32());
Assert.AreEqual(456, br.ReadUInt32());
}
}
8 changes: 4 additions & 4 deletions Schema Tests/BinarySchemaTestUtil.cs
Original file line number Diff line number Diff line change
Expand Up @@ -102,14 +102,14 @@ var namedTypeSymbol
new WSizeOfMemberInBytesDependencyFixer();
foreach (var structure in structures) {
foreach (var member in structure.Members.OfType<ISchemaValueMember>()) {
if (member.MemberType is IPrimitiveMemberType primitiveMemberType) {
if (primitiveMemberType.AccessChainToSizeOf != null) {
if (member.MemberType is IIntegerMemberType integerMemberType) {
if (integerMemberType.AccessChainToSizeOf != null) {
sizeOfMemberInBytesDependencyFixer.AddDependenciesForContainer(
structureByNamedTypeSymbol,
primitiveMemberType.AccessChainToSizeOf);
integerMemberType.AccessChainToSizeOf);
}

var pointerToAttribute = primitiveMemberType.PointerToAttribute;
var pointerToAttribute = integerMemberType.PointerToAttribute;
if (pointerToAttribute != null) {
sizeOfMemberInBytesDependencyFixer.AddDependenciesForContainer(
structureByNamedTypeSymbol,
Expand Down
106 changes: 106 additions & 0 deletions Schema Tests/binary/generator/FixedPointTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
using NUnit.Framework;


namespace schema.binary.text;

internal class FixedPointTests {
[Test]
public void TestMutableFixedPoint_1_19_12()
=> BinarySchemaTestUtil.AssertGenerated(
"""
using System.Numerics;
using schema.binary;
using schema.binary.attributes;
namespace foo.bar;
[BinarySchema]
public partial class Wrapper {
[FixedPoint(1, 19, 12)]
public float SingleField { get; set; }
[FixedPoint(1, 19, 12)]
public double DoubleField { get; set; }
}
""",
"""
using System;
using schema.binary;
using schema.util;
namespace foo.bar;
public partial class Wrapper {
public void Read(IBinaryReader br) {
this.SingleField = BitLogic.ConvertFixedPointToSingle(br.ReadUInt32(), 1, 19, 12);
this.DoubleField = BitLogic.ConvertFixedPointToDouble(br.ReadUInt32(), 1, 19, 12);
}
}
""",
"""
using System;
using schema.binary;
using schema.util;
namespace foo.bar;
public partial class Wrapper {
public void Write(IBinaryWriter bw) {
bw.WriteUInt32(BitLogic.ConvertSingleToFixedPoint(this.SingleField, 1, 19, 12));
bw.WriteUInt32(BitLogic.ConvertDoubleToFixedPoint(this.DoubleField, 1, 19, 12));
}
}
""");

[Test]
public void TestReadonlyFixedPoint_1_19_12()
=> BinarySchemaTestUtil.AssertGenerated(
"""
using System.Numerics;
using schema.binary;
using schema.binary.attributes;
namespace foo.bar;
[BinarySchema]
public partial class Wrapper {
[FixedPoint(1, 19, 12)]
public float SingleField { get; }
[FixedPoint(1, 19, 12)]
public double DoubleField { get; }
}
""",
"""
using System;
using schema.binary;
using schema.util;
namespace foo.bar;
public partial class Wrapper {
public void Read(IBinaryReader br) {
br.AssertUInt32(BitLogic.ConvertSingleToFixedPoint(this.SingleField, 1, 19, 12));
br.AssertUInt32(BitLogic.ConvertDoubleToFixedPoint(this.DoubleField, 1, 19, 12));
}
}
""",
"""
using System;
using schema.binary;
using schema.util;
namespace foo.bar;
public partial class Wrapper {
public void Write(IBinaryWriter bw) {
bw.WriteUInt32(BitLogic.ConvertSingleToFixedPoint(this.SingleField, 1, 19, 12));
bw.WriteUInt32(BitLogic.ConvertDoubleToFixedPoint(this.DoubleField, 1, 19, 12));
}
}
""");
}
31 changes: 29 additions & 2 deletions Schema Tests/util/BitLogicTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,39 @@ public class BitLogicTests {
public uint TestBytesNeededToContainBits(uint bits)
=> BitLogic.BytesNeededToContainBits(bits);

[Test]
[TestCase((uint) 0, ExpectedResult = 0.0f)]
[TestCase((uint) 1, ExpectedResult = 0.000244140625f)]
[TestCase((uint) 100, ExpectedResult = 0.0244140625f)]
[TestCase(0xF0000000, ExpectedResult = -65536f)]
[TestCase(uint.MaxValue, ExpectedResult = -0.000244140625f)]
public float TestFixedPointToSingle_1_19_12(uint x)
=> BitLogic.ConvertFixedPointToSingle(x, 1, 19, 12);

[Test]
[TestCase((uint) 0, ExpectedResult = 0.0)]
[TestCase((uint) 1, ExpectedResult = 0.000244140625)]
[TestCase((uint) 100, ExpectedResult = 0.0244140625)]
[TestCase(0xF0000000, ExpectedResult = -65536)]
[TestCase(uint.MaxValue, ExpectedResult = -0.000244140625)]
public double TestFixed32_1_19_12(uint x)
=> BitLogic.GetFixedPointDouble(x, 1, 19, 12);
public double TestFixedPointToDouble_1_19_12(uint x)
=> BitLogic.ConvertFixedPointToDouble(x, 1, 19, 12);

[Test]
[TestCase(0.0f, ExpectedResult = (uint) 0)]
[TestCase(0.000244140625f, ExpectedResult = (uint) 1)]
[TestCase(0.0244140625f, ExpectedResult = (uint) 100)]
[TestCase(-65536f, ExpectedResult = 0xF0000000)]
[TestCase(-0.000244140625f, ExpectedResult = uint.MaxValue)]
public uint TestSingleToFixedPoint_1_19_12(float x)
=> BitLogic.ConvertSingleToFixedPoint(x, 1, 19, 12);

[Test]
[TestCase(0.0, ExpectedResult = (uint) 0)]
[TestCase(0.000244140625, ExpectedResult = (uint) 1)]
[TestCase(0.0244140625, ExpectedResult = (uint) 100)]
[TestCase(-65536, ExpectedResult = 0xF0000000)]
[TestCase(-0.000244140625, ExpectedResult = uint.MaxValue)]
public uint TestDoubleToFixedPoint_1_19_12(double x)
=> BitLogic.ConvertDoubleToFixedPoint(x, 1, 19, 12);
}
9 changes: 4 additions & 5 deletions Schema/src/binary/BinarySchemaGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,15 +46,14 @@ public override void PreprocessAllMapped(
foreach (var container in containerByNamedTypeSymbol.Values) {
foreach (var member in
container.Members.OfType<ISchemaValueMember>()) {
if (member.MemberType is IPrimitiveMemberType
primitiveMemberType) {
if (primitiveMemberType.AccessChainToSizeOf != null) {
if (member.MemberType is IIntegerMemberType integerMemberType) {
if (integerMemberType.AccessChainToSizeOf != null) {
sizeOfMemberInBytesDependencyFixer.AddDependenciesForContainer(
containerByNamedTypeSymbol,
primitiveMemberType.AccessChainToSizeOf);
integerMemberType.AccessChainToSizeOf);
}

var pointerToAttribute = primitiveMemberType.PointerToAttribute;
var pointerToAttribute = integerMemberType.PointerToAttribute;
if (pointerToAttribute != null) {
sizeOfMemberInBytesDependencyFixer.AddDependenciesForContainer(
containerByNamedTypeSymbol,
Expand Down
34 changes: 23 additions & 11 deletions Schema/src/binary/BinarySchemaStructureParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,20 @@ public interface IPrimitiveMemberType : IMemberType {
SchemaPrimitiveType PrimitiveType { get; }
bool UseAltFormat { get; }
SchemaNumberType AltFormat { get; }
}

public interface IIntegerMemberType : IPrimitiveMemberType {
bool SizeOfStream { get; }
IMemberReference<string>[]? LengthOfStringMembers { get; }
IMemberReference[]? LengthOfSequenceMembers { get; }
IChain<IAccessChainNode>? AccessChainToSizeOf { get; }
IPointerToAttribute? PointerToAttribute { get; }
}

public interface IFloatMemberType : IPrimitiveMemberType {
FixedPointAttribute? FixedPointAttribute { get; }
}

public interface IContainerMemberType : IMemberType {
bool IsChild { get; }
}
Expand Down Expand Up @@ -278,15 +284,14 @@ public IBinarySchemaContainer ParseContainer(
var sizeOfMemberInBytesDependencyFixer =
new WSizeOfMemberInBytesDependencyFixer();
foreach (var member in members.OfType<ISchemaValueMember>()) {
if (member.MemberType is IPrimitiveMemberType
primitiveMemberType) {
if (primitiveMemberType.AccessChainToSizeOf != null) {
if (member.MemberType is IIntegerMemberType integerMemberType) {
if (integerMemberType.AccessChainToSizeOf != null) {
sizeOfMemberInBytesDependencyFixer.AddDependenciesForContainer(
containerByNamedTypeSymbol,
primitiveMemberType.AccessChainToSizeOf);
integerMemberType.AccessChainToSizeOf);
}

var pointerToAttribute = primitiveMemberType.PointerToAttribute;
var pointerToAttribute = integerMemberType.PointerToAttribute;
if (pointerToAttribute != null) {
sizeOfMemberInBytesDependencyFixer.AddDependenciesForContainer(
containerByNamedTypeSymbol,
Expand Down Expand Up @@ -333,6 +338,7 @@ public IBinarySchemaContainer ParseContainer(
MemberReferenceUtil.WrapTypeInfoWithMemberType(memberTypeInfo);

var attributeParsers = new IAttributeParser[] {
new FixedPointParser(),
new SequenceLengthSourceParser(),
new WLengthOfSequenceParser(),
new WLengthOfStringParser(),
Expand All @@ -354,8 +360,8 @@ public IBinarySchemaContainer ParseContainer(
memberBetterSymbol.GetAttribute<WSizeOfStreamInBytesAttribute>();
if (sizeOfStreamAttribute != null) {
if (memberTypeInfo is IIntegerTypeInfo &&
memberType is PrimitiveMemberType primitiveMemberType) {
primitiveMemberType.SizeOfStream = true;
memberType is IntegerMemberType integerMemberType) {
integerMemberType.SizeOfStream = true;
} else {
memberBetterSymbol.ReportDiagnostic(Rules.NotSupported);
}
Expand Down Expand Up @@ -450,7 +456,7 @@ public IBinarySchemaContainer ParseContainer(

var canPrimitiveTypeBeReadAsNumber =
targetPrimitiveType.CanBeReadAsNumber();
if (!(targetMemberType is PrimitiveMemberType &&
if (!(targetMemberType is IPrimitiveMemberType &&
canPrimitiveTypeBeReadAsNumber)) {
memberBetterSymbol.ReportDiagnostic(
Rules.UnexpectedAttribute);
Expand All @@ -466,7 +472,7 @@ public IBinarySchemaContainer ParseContainer(

var canPrimitiveTypeBeReadAsInteger =
targetPrimitiveType.CanBeReadAsInteger();
if (!(targetMemberType is PrimitiveMemberType &&
if (!(targetMemberType is BPrimitiveMemberType &&
canPrimitiveTypeBeReadAsInteger)) {
memberBetterSymbol.ReportDiagnostic(Rules.UnexpectedAttribute);
}
Expand All @@ -478,7 +484,7 @@ public IBinarySchemaContainer ParseContainer(
formatNumberType = formatIntegerType.AsNumberType();
}

if (targetMemberType is PrimitiveMemberType primitiveMemberType) {
if (targetMemberType is BPrimitiveMemberType primitiveMemberType) {
if (formatNumberType != SchemaNumberType.UNDEFINED) {
primitiveMemberType.UseAltFormat = true;
primitiveMemberType.AltFormat = formatNumberType;
Expand Down Expand Up @@ -620,7 +626,7 @@ public class SchemaValueMember : ISchemaValueMember {
public bool TrackStartAndEnd { get; set; }
}

public class PrimitiveMemberType : IPrimitiveMemberType {
public abstract class BPrimitiveMemberType : IPrimitiveMemberType {
public IPrimitiveTypeInfo PrimitiveTypeInfo { get; set; }
public ITypeInfo TypeInfo => PrimitiveTypeInfo;
public ITypeSymbol TypeSymbol => TypeInfo.TypeSymbol;
Expand All @@ -631,14 +637,20 @@ public SchemaPrimitiveType PrimitiveType

public bool UseAltFormat { get; set; }
public SchemaNumberType AltFormat { get; set; }
}

public class IntegerMemberType : BPrimitiveMemberType, IIntegerMemberType {
public bool SizeOfStream { get; set; }
public IMemberReference<string>[]? LengthOfStringMembers { get; set; }
public IMemberReference[]? LengthOfSequenceMembers { get; set; }
public IChain<IAccessChainNode>? AccessChainToSizeOf { get; set; }
public IPointerToAttribute? PointerToAttribute { get; set; }
}

public class FloatMemberType : BPrimitiveMemberType, IFloatMemberType {
public FixedPointAttribute? FixedPointAttribute { get; set; }
}

public class ContainerMemberType : IContainerMemberType {
public IContainerTypeInfo ContainerTypeInfo { get; set; }
public ITypeInfo TypeInfo => this.ContainerTypeInfo;
Expand Down
Loading

0 comments on commit 7d51b01

Please sign in to comment.