diff --git a/src/Backdash.Analyzers/SourceGenerationHelper.cs b/src/Backdash.Analyzers/SourceGenerationHelper.cs index 33e2dcc8..3841996e 100644 --- a/src/Backdash.Analyzers/SourceGenerationHelper.cs +++ b/src/Backdash.Analyzers/SourceGenerationHelper.cs @@ -82,10 +82,11 @@ void BuildValueMember(ClassMember member) ); reads.Append(tab2); + reads.AppendLine( member.Type is INamedTypeSymbol { EnumUnderlyingType: { } underTypeRead } - ? $"result.{member.Name} = ({member.Type.Name})binaryReader.Read{underTypeRead.Name}();" - : $"result.{member.Name} = binaryReader.Read{member.Type.Name}();" + ? $"result.{member.Name} = ({member.Type.Name})binaryReader.Read{GetMethodSuffix(underTypeRead)}();" + : $"result.{member.Name} = binaryReader.Read{GetMethodSuffix(member.Type)}();" ); } } @@ -145,8 +146,8 @@ void BuildArrayMember(ITypeSymbol itemType, ClassMember member) reads.Append(tab3); reads.AppendLine( itemType is INamedTypeSymbol { EnumUnderlyingType: { } underTypeRead } - ? $"result.{member.Name}[i] = ({itemType.Name})binaryReader.Read{underTypeRead.Name}();" - : $"result.{member.Name}[i] = binaryReader.Read{itemType.Name}();" + ? $"result.{member.Name}[i] = ({itemType.Name})binaryReader.Read{GetMethodSuffix(underTypeRead)}();" + : $"result.{member.Name}[i] = binaryReader.Read{GetMethodSuffix(itemType)}();" ); } @@ -158,6 +159,14 @@ void BuildArrayMember(ITypeSymbol itemType, ClassMember member) } } + static string GetMethodSuffix(ITypeSymbol type) + { + if (type.Name is "Single") + return "Float"; + + return type.Name; + } + static bool IsArrayLike(ITypeSymbol memberType, out ITypeSymbol elementType) { elementType = memberType; diff --git a/src/Backdash/Network/Endianness.cs b/src/Backdash/Network/Endianness.cs index d046a643..41635560 100644 --- a/src/Backdash/Network/Endianness.cs +++ b/src/Backdash/Network/Endianness.cs @@ -1,7 +1,7 @@ namespace Backdash.Network; /// -/// Defines a endianness value +/// Defines an endianness value /// public enum Endianness : byte { diff --git a/src/Backdash/Serialization/BinaryBufferReader.cs b/src/Backdash/Serialization/BinaryBufferReader.cs index e16ae346..92b175ec 100644 --- a/src/Backdash/Serialization/BinaryBufferReader.cs +++ b/src/Backdash/Serialization/BinaryBufferReader.cs @@ -196,16 +196,6 @@ public bool ReadBoolean() /// public Half? ReadNullableHalf() => ReadBoolean() ? ReadHalf() : null; - /// - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public float ReadSingle() => ReadFloat(); - - /// - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public float? ReadNullableSingle() => ReadNullableFloat(); - /// Reads float 32 from the buffer. public float ReadFloat() => BitConverter.Int32BitsToSingle(ReadInt32()); @@ -353,8 +343,11 @@ public T ReadNumber(bool isUnsigned) where T : unmanaged, IBinaryInteger } /// - public void ReadNumber(ref T value, bool isUnsigned) where T : unmanaged, IBinaryInteger => - value = ReadNumber(isUnsigned); + public void ReadNumber(ref T value, bool isUnsigned) where T : unmanaged, IBinaryInteger + { + numberSerializer.Read(ref value, CurrentBuffer, isUnsigned, out var written); + Advance(written); + } /// public void ReadNumber(ref T? value, bool isUnsigned) where T : unmanaged, IBinaryInteger => @@ -595,13 +588,13 @@ public void Read(in StringBuilder values) public void Read(ref bool? value) => value = ReadNullableBoolean(); /// - public void Read(ref short value) => value = ReadInt16(); + public void Read(ref short value) => ReadNumber(ref value, false); /// public void Read(ref short? value) => value = ReadNullableInt16(); /// - public void Read(ref ushort value) => value = ReadUInt16(); + public void Read(ref ushort value) => ReadNumber(ref value, true); /// public void Read(ref ushort? value) => value = ReadNullableUInt16(); @@ -613,37 +606,37 @@ public void Read(in StringBuilder values) public void Read(ref char? value) => value = ReadNullableChar(); /// - public void Read(ref int value) => value = ReadInt32(); + public void Read(ref int value) => ReadNumber(ref value, false); /// public void Read(ref int? value) => value = ReadNullableInt32(); /// - public void Read(ref uint value) => value = ReadUInt32(); + public void Read(ref uint value) => ReadNumber(ref value, true); /// public void Read(ref uint? value) => value = ReadNullableUInt32(); /// - public void Read(ref long value) => value = ReadInt64(); + public void Read(ref long value) => ReadNumber(ref value, false); /// public void Read(ref long? value) => value = ReadNullableInt64(); /// - public void Read(ref ulong value) => value = ReadUInt64(); + public void Read(ref ulong value) => ReadNumber(ref value, true); /// public void Read(ref ulong? value) => value = ReadNullableUInt64(); /// - public void Read(ref Int128 value) => value = ReadInt128(); + public void Read(ref Int128 value) => ReadNumber(ref value, false); /// public void Read(ref Int128? value) => value = ReadNullableInt128(); /// - public void Read(ref UInt128 value) => value = ReadUInt128(); + public void Read(ref UInt128 value) => ReadNumber(ref value, true); /// public void Read(ref UInt128? value) => value = ReadNullableUInt128(); @@ -963,20 +956,25 @@ public T ReadAsByte() where T : unmanaged /// public void ReadAsByte(ref T value) where T : unmanaged => Read(ref Unsafe.As(ref value)); - /// - public void ReadAsByte(ref T? value) where T : unmanaged => Read(ref Unsafe.As(ref value)); - /// public void ReadAsByte(in Span values) where T : unmanaged => Read(MemoryMarshal.Cast(values)); /// public void ReadAsByte(in List values) where T : unmanaged => ReadAsByte(GetListSpan(in values)); + /// + public void ReadAsByte(ref T? value) where T : unmanaged => value = ReadAsNullableByte(); + /// public T? ReadAsNullableByte() where T : unmanaged { - var value = ReadNullableByte(); - return Unsafe.As(ref value); + if (ReadBoolean()) + { + var value = ReadByte(); + return Unsafe.As(ref value); + } + + return null; } /// Reads a from buffer and reinterprets it as . @@ -989,22 +987,26 @@ public T ReadAsSByte() where T : unmanaged /// public void ReadAsSByte(ref T value) where T : unmanaged => Read(ref Unsafe.As(ref value)); - /// - public void ReadAsSByte(ref T? value) where T : unmanaged => Read(ref Unsafe.As(ref value)); - /// public void ReadAsSByte(in Span values) where T : unmanaged => Read(MemoryMarshal.Cast(values)); /// public void ReadAsSByte(in List values) where T : unmanaged => ReadAsSByte(GetListSpan(in values)); + /// + public void ReadAsSByte(ref T? value) where T : unmanaged => value = ReadAsNullableSByte(); + /// public T? ReadAsNullableSByte() where T : unmanaged { - var value = ReadNullableSByte(); - return Unsafe.As(ref value); - } + if (ReadBoolean()) + { + var value = ReadSByte(); + return Unsafe.As(ref value); + } + return null; + } /// Reads a from buffer and reinterprets it as . public T ReadAsInt16() where T : unmanaged @@ -1016,20 +1018,25 @@ public T ReadAsInt16() where T : unmanaged /// public void ReadAsInt16(ref T value) where T : unmanaged => Read(ref Unsafe.As(ref value)); - /// - public void ReadAsInt16(ref T? value) where T : unmanaged => Read(ref Unsafe.As(ref value)); - /// public void ReadAsInt16(in Span values) where T : unmanaged => Read(MemoryMarshal.Cast(values)); /// public void ReadAsInt16(in List values) where T : unmanaged => ReadAsInt16(GetListSpan(in values)); + /// + public void ReadAsInt16(ref T? value) where T : unmanaged => value = ReadAsNullableInt16(); + /// public T? ReadAsNullableInt16() where T : unmanaged { - var value = ReadNullableInt16(); - return Unsafe.As(ref value); + if (ReadBoolean()) + { + var value = ReadInt16(); + return Unsafe.As(ref value); + } + + return null; } /// Reads a from buffer and reinterprets it as . @@ -1042,20 +1049,25 @@ public T ReadAsUInt16() where T : unmanaged /// public void ReadAsUInt16(ref T value) where T : unmanaged => Read(ref Unsafe.As(ref value)); - /// - public void ReadAsUInt16(ref T? value) where T : unmanaged => Read(ref Unsafe.As(ref value)); - /// public void ReadAsUInt16(in Span values) where T : unmanaged => Read(MemoryMarshal.Cast(values)); /// public void ReadAsUInt16(in List values) where T : unmanaged => ReadAsUInt16(GetListSpan(in values)); + /// + public void ReadAsUInt16(ref T? value) where T : unmanaged => value = ReadAsNullableUInt16(); + /// public T? ReadAsNullableUInt16() where T : unmanaged { - var value = ReadNullableUInt16(); - return Unsafe.As(ref value); + if (ReadBoolean()) + { + var value = ReadUInt16(); + return Unsafe.As(ref value); + } + + return null; } /// Reads a from buffer and reinterprets it as . @@ -1068,20 +1080,25 @@ public T ReadAsInt32() where T : unmanaged /// public void ReadAsInt32(ref T value) where T : unmanaged => Read(ref Unsafe.As(ref value)); - /// - public void ReadAsInt32(ref T? value) where T : unmanaged => Read(ref Unsafe.As(ref value)); - /// public void ReadAsInt32(in Span values) where T : unmanaged => Read(MemoryMarshal.Cast(values)); /// public void ReadAsInt32(in List values) where T : unmanaged => ReadAsInt32(GetListSpan(in values)); + /// + public void ReadAsInt32(ref T? value) where T : unmanaged => value = ReadAsNullableInt32(); + /// public T? ReadAsNullableInt32() where T : unmanaged { - var value = ReadNullableInt32(); - return Unsafe.As(ref value); + if (ReadBoolean()) + { + var value = ReadInt32(); + return Unsafe.As(ref value); + } + + return null; } /// Reads a from buffer and reinterprets it as . @@ -1094,20 +1111,25 @@ public T ReadAsUInt32() where T : unmanaged /// public void ReadAsUInt32(ref T value) where T : unmanaged => Read(ref Unsafe.As(ref value)); - /// - public void ReadAsUInt32(ref T? value) where T : unmanaged => Read(ref Unsafe.As(ref value)); - /// public void ReadAsUInt32(in Span values) where T : unmanaged => Read(MemoryMarshal.Cast(values)); /// public void ReadAsUInt32(in List values) where T : unmanaged => ReadAsUInt32(GetListSpan(in values)); + /// + public void ReadAsUInt32(ref T? value) where T : unmanaged => value = ReadAsNullableUInt32(); + /// public T? ReadAsNullableUInt32() where T : unmanaged { - var value = ReadNullableUInt32(); - return Unsafe.As(ref value); + if (ReadBoolean()) + { + var value = ReadUInt32(); + return Unsafe.As(ref value); + } + + return null; } /// Reads a from buffer and reinterpret it as . @@ -1120,20 +1142,25 @@ public T ReadAsInt64() where T : unmanaged /// public void ReadAsInt64(ref T value) where T : unmanaged => Read(ref Unsafe.As(ref value)); - /// - public void ReadAsInt64(ref T? value) where T : unmanaged => Read(ref Unsafe.As(ref value)); - /// public void ReadAsInt64(in Span values) where T : unmanaged => Read(MemoryMarshal.Cast(values)); /// public void ReadAsInt64(in List values) where T : unmanaged => ReadAsInt64(GetListSpan(in values)); + /// + public void ReadAsInt64(ref T? value) where T : unmanaged => value = ReadAsNullableInt64(); + /// public T? ReadAsNullableInt64() where T : unmanaged { - var value = ReadNullableInt64(); - return Unsafe.As(ref value); + if (ReadBoolean()) + { + var value = ReadInt64(); + return Unsafe.As(ref value); + } + + return null; } /// Reads a from buffer and reinterprets it as . @@ -1146,20 +1173,25 @@ public T ReadAsUInt64() where T : unmanaged /// public void ReadAsUInt64(ref T value) where T : unmanaged => Read(ref Unsafe.As(ref value)); - /// - public void ReadAsUInt64(ref T? value) where T : unmanaged => Read(ref Unsafe.As(ref value)); - /// public void ReadAsUInt64(in Span values) where T : unmanaged => Read(MemoryMarshal.Cast(values)); /// public void ReadAsUInt64(in List values) where T : unmanaged => ReadAsUInt64(GetListSpan(in values)); + /// + public void ReadAsUInt64(ref T? value) where T : unmanaged => value = ReadAsNullableUInt64(); + /// public T? ReadAsNullableUInt64() where T : unmanaged { - var value = ReadNullableUInt64(); - return Unsafe.As(ref value); + if (ReadBoolean()) + { + var value = ReadUInt64(); + return Unsafe.As(ref value); + } + + return null; } /// Reads a from buffer and reinterpret it as . @@ -1172,20 +1204,26 @@ public T ReadAsInt128() where T : unmanaged /// public void ReadAsInt128(ref T value) where T : unmanaged => Read(ref Unsafe.As(ref value)); - /// - public void ReadAsInt128(ref T? value) where T : unmanaged => Read(ref Unsafe.As(ref value)); - /// public void ReadAsInt128(in Span values) where T : unmanaged => Read(MemoryMarshal.Cast(values)); /// public void ReadAsInt128(in List values) where T : unmanaged => ReadAsInt128(GetListSpan(in values)); + + /// + public void ReadAsInt128(ref T? value) where T : unmanaged => value = ReadAsNullableInt128(); + /// public T? ReadAsNullableInt128() where T : unmanaged { - var value = ReadNullableInt128(); - return Unsafe.As(ref value); + if (ReadBoolean()) + { + var value = ReadInt128(); + return Unsafe.As(ref value); + } + + return null; } /// Reads a from buffer and reinterprets it as . @@ -1198,19 +1236,24 @@ public T ReadAsUInt128() where T : unmanaged /// public void ReadAsUInt128(ref T value) where T : unmanaged => Read(ref Unsafe.As(ref value)); - /// - public void ReadAsUInt128(ref T? value) where T : unmanaged => Read(ref Unsafe.As(ref value)); - /// public void ReadAsUInt128(in Span values) where T : unmanaged => Read(MemoryMarshal.Cast(values)); /// public void ReadAsUInt128(in List values) where T : unmanaged => ReadAsUInt128(GetListSpan(in values)); + /// + public void ReadAsUInt128(ref T? value) where T : unmanaged => value = ReadAsNullableUInt128(); + /// public T? ReadAsNullableUInt128() where T : unmanaged { - var value = ReadNullableUInt128(); - return Unsafe.As(ref value); + if (ReadBoolean()) + { + var value = ReadUInt128(); + return Unsafe.As(ref value); + } + + return null; } } diff --git a/src/Backdash/Serialization/Internal/EndiannessSerializer.cs b/src/Backdash/Serialization/Internal/EndiannessSerializer.cs index bceff106..5b29c246 100644 --- a/src/Backdash/Serialization/Internal/EndiannessSerializer.cs +++ b/src/Backdash/Serialization/Internal/EndiannessSerializer.cs @@ -36,6 +36,14 @@ public T Read(ReadOnlySpan buffer, bool isUnsigned, out int bytesRead) return T.ReadBigEndian(buffer[..bytesRead], isUnsigned); } + public void Read(ref T value, ReadOnlySpan buffer, bool isUnsigned, out int bytesRead) + where T : unmanaged, IBinaryInteger + { + bytesRead = Unsafe.SizeOf(); + if (!T.TryReadBigEndian(buffer[..bytesRead], isUnsigned, out value)) + throw new OverflowException(); + } + public bool Write(Span buffer, T value, out int size) where T : unmanaged, IBinaryInteger { @@ -67,6 +75,14 @@ public T Read(ReadOnlySpan buffer, bool isUnsigned, out int bytesRead) return T.ReadLittleEndian(buffer[..bytesRead], isUnsigned); } + public void Read(ref T value, ReadOnlySpan buffer, bool isUnsigned, out int bytesRead) + where T : unmanaged, IBinaryInteger + { + bytesRead = Unsafe.SizeOf(); + if (!T.TryReadLittleEndian(buffer[..bytesRead], isUnsigned, out value)) + throw new OverflowException(); + } + public bool Write(Span buffer, T value, out int size) where T : unmanaged, IBinaryInteger { @@ -112,6 +128,10 @@ int Write(ArrayBufferWriter buffer, T value) where T : unmanaged, IBina /// Reads number from the buffer T Read(ReadOnlySpan buffer, bool isUnsigned, out int bytesRead) where T : unmanaged, IBinaryInteger; + /// Reads number from the buffer + void Read(ref T value, ReadOnlySpan buffer, bool isUnsigned, out int bytesRead) + where T : unmanaged, IBinaryInteger; + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] T Read(in ReadOnlySpan buffer, out int bytesRead) diff --git a/tests/Backdash.Tests/Specs/Unit/Serialization/BinaryBufferReadWriteNullableValues.cs b/tests/Backdash.Tests/Specs/Unit/Serialization/BinaryBufferReadWriteNullableValues.cs index 85ccac93..13c5024e 100644 --- a/tests/Backdash.Tests/Specs/Unit/Serialization/BinaryBufferReadWriteNullableValues.cs +++ b/tests/Backdash.Tests/Specs/Unit/Serialization/BinaryBufferReadWriteNullableValues.cs @@ -873,6 +873,25 @@ public bool TestUInt128(UInt128Enum? value, UInt128Enum? read, Endianness endian return value == read; } + + [PropertyTest] + public void TestBagInt64Ref(Bag.S8? value, Bag.S8? read, Endianness endianness) + { + var size = Setup(value, endianness, out var writer); + writer.WriteAsInt64(in value); + + var reader = GetReader(writer); + reader.ReadAsInt64(ref read); + reader.ReadCount.Should().Be(size); + + ResetRead(); + Bag.S8? otherRead = null; + reader.ReadAsInt64(ref otherRead); + reader.ReadCount.Should().Be(size); + otherRead.Should().Be(read); + + value.Should().BeEquivalentTo(read); + } } public static int Setup(T? value, Endianness endianness, out BinaryBufferWriter writer) where T : unmanaged diff --git a/tests/Backdash.Tests/TestUtils/Types/TestData.cs b/tests/Backdash.Tests/TestUtils/Types/TestData.cs index 8a428cdb..3280cca7 100644 --- a/tests/Backdash.Tests/TestUtils/Types/TestData.cs +++ b/tests/Backdash.Tests/TestUtils/Types/TestData.cs @@ -371,3 +371,8 @@ public enum Int64Enum : long public record struct Int128Enum(Int128 Value); public record struct UInt128Enum(UInt128 Value); + +public static class Bag +{ + public record struct S8(int A, int B); +}