diff --git a/.github/workflows/build-runtime-cs.yml b/.github/workflows/build-runtime-cs.yml index f79663fa..f5be8955 100644 --- a/.github/workflows/build-runtime-cs.yml +++ b/.github/workflows/build-runtime-cs.yml @@ -25,29 +25,19 @@ jobs: uses: actions/setup-dotnet@v3 with: dotnet-version: | - 5.0.x - 6.0.x - 7.0.x 8.0.x + 9.0.x dotnet-quality: 'preview' - name: Generate Output.g.cs working-directory: "./Laboratory/C#" run: dotnet run --project ../../Compiler/ --include $(ls -p ../Schemas/Valid/*.bop | tr '\n' ' ') build --generator "cs:./GeneratedTestCode/Output.g.cs,namespace=Bebop.Codegen" - - name: Run Test .NET 6 - run: dotnet test -c Release -f net6.0 + - name: Run Test .NET 8 + run: dotnet test -c Release -f net8.0 working-directory: ${{env.TEST_ROOT}} - - name: Run Test .NET 5 - run: dotnet test -c Release -f net5.0 - working-directory: ${{env.TEST_ROOT}} - - - name: Run Test .NET Framework 4.7.2 - run: dotnet test -c Release -f net472 - working-directory: ${{env.TEST_ROOT}} - - - name: Run Test .NET Framework 4.8 - run: dotnet test -c Release -f net48 + - name: Run Test .NET 9 + run: dotnet test -c Release -f net9.0 working-directory: ${{env.TEST_ROOT}} - name: Restore Project diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 068c6ab9..d9547f56 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -17,7 +17,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v3 with: - dotnet-version: 8.0.x + dotnet-version: 9.0.x - name: Setup Rust uses: actions-rs/toolchain@v1 with: diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index d1f0721e..e238f83f 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -192,8 +192,10 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v3 with: - dotnet-version: "8.0.x" - dotnet-quality: "preview" + dotnet-version: | + 8.0.x + 9.0.x + dotnet-quality: 'preview' - name: Test .NET Runtime run: | diff --git a/.github/workflows/test-csharp.yml b/.github/workflows/test-csharp.yml index 7c77c685..2ca703b6 100644 --- a/.github/workflows/test-csharp.yml +++ b/.github/workflows/test-csharp.yml @@ -12,10 +12,8 @@ jobs: - uses: actions/setup-dotnet@v3 with: dotnet-version: | - 5.0.x - 6.0.x - 7.0.x 8.0.x + 9.0.x dotnet-quality: 'preview' - name: Build and run tests shell: bash diff --git a/Core/Generators/CSharp/CSharpGenerator.cs b/Core/Generators/CSharp/CSharpGenerator.cs index 0bd84929..f59e75b3 100644 --- a/Core/Generators/CSharp/CSharpGenerator.cs +++ b/Core/Generators/CSharp/CSharpGenerator.cs @@ -121,7 +121,7 @@ public override ValueTask Compile(BebopSchema schema, GeneratorConfig co _ => string.Empty }; builder.AppendLine(recordAttribute); - builder.AppendLine($"public partial class {definition.ClassName()} : {BebopRecord}, global::System.IEquatable<{definition.ClassName()}> {{"); + builder.AppendLine($"public partial class {definition.ClassName()} : {BebopRecord}, {IDecodable}<{definition.ClassName()}>, global::System.IEquatable<{definition.ClassName()}> {{"); builder.Indent(indentStep); if (fd is MessageDefinition) @@ -646,7 +646,7 @@ void CompileUnionConcreteClass() builder.AppendLine("/// "); builder.AppendLine(GeneratedAttribute); builder.AppendLine(recordAttribute); - builder.AppendLine($"public partial class {ud.ClassName()} : {PrefixNamespace(ud.BaseClassName())}<{genericTypeArguments}> {{").Indent(indentStep).AppendLine(); + builder.AppendLine($"public partial class {ud.ClassName()} : {PrefixNamespace(ud.BaseClassName())}<{genericTypeArguments}>, {IDecodable}<{ud.ClassName()}> {{").Indent(indentStep).AppendLine(); if (ud.OpcodeDecorator is not null && ud.OpcodeDecorator.Arguments.TryGetValue("fourcc", out var fourcc)) { builder.AppendLine($"public const uint OpCode = {fourcc};"); @@ -1346,6 +1346,7 @@ public override void WriteAuxiliaryFile(string outputPath) private const string StringGetByteCount = "global::System.Text.Encoding.UTF8.GetByteCount"; private const string StringGetMaxByteCount = "global::System.Text.Encoding.UTF8.GetMaxByteCount"; private const string BebopRecord = "global::Bebop.Runtime.BaseBebopRecord"; + private const string IDecodable = "global::Bebop.Runtime.IDecodable"; private static readonly string[] DecodeBufferTypes = { "byte[]", "global::System.ReadOnlySpan", "global::System.ReadOnlyMemory", "global::System.ArraySegment", ImmutableByteArrayType }; /// diff --git a/Laboratory/C#/Benchmarks/Benchmarks.csproj b/Laboratory/C#/Benchmarks/Benchmarks.csproj index 04d69d1a..1c456a51 100644 --- a/Laboratory/C#/Benchmarks/Benchmarks.csproj +++ b/Laboratory/C#/Benchmarks/Benchmarks.csproj @@ -1,9 +1,9 @@  Exe - net6.0;net5.0;net472 + net8.0;net9.0 enable - 9.0 + preview diff --git a/Laboratory/C#/Test/Test.csproj b/Laboratory/C#/Test/Test.csproj index 5460f256..f4f70f80 100644 --- a/Laboratory/C#/Test/Test.csproj +++ b/Laboratory/C#/Test/Test.csproj @@ -1,9 +1,9 @@  - net6.0;net5.0;net472 + net8.0;net9.0 enable - 9.0 + preview false diff --git a/Laboratory/Integration/IntegrationTesting.csproj b/Laboratory/Integration/IntegrationTesting.csproj index 2abea591..a25ca925 100644 --- a/Laboratory/Integration/IntegrationTesting.csproj +++ b/Laboratory/Integration/IntegrationTesting.csproj @@ -3,7 +3,7 @@ Exe - 12 + preview net8.0 false $(NoWarn);CS9193 diff --git a/Runtime/C#/Bebop.csproj b/Runtime/C#/Bebop.csproj index 7ea9858f..f6399891 100644 --- a/Runtime/C#/Bebop.csproj +++ b/Runtime/C#/Bebop.csproj @@ -1,9 +1,9 @@  - net472;netstandard2.0;netstandard2.1;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0 + net8.0;net9.0 enable - 10.0 + preview The .NET runtime for Bebop, a schema-based binary serialization format. bebop The Bebop Authors @@ -13,52 +13,18 @@ 0.0.1 $([System.DateTime]::UtcNow.ToString(`yyyyMMdd-HHmm`)) Apache-2.0 - https://github.com/RainwayApp/bebop + https://github.com/betwixt-labs/bebop true ./bin/$(AssemblyName).xml true 128.png - https://github.com/RainwayApp/bebop.git + https://github.com/betwixt-labs/bebop.git git binary serialization bebop encoding decoding false - - - - none - none - all - - - - - - - - - - - - - - - - - none - none - all - - - @@ -71,7 +37,6 @@ - True @@ -79,11 +44,10 @@ - + TRACE;AGGRESSIVE_OPTIMIZE - true @@ -92,4 +56,4 @@ false - \ No newline at end of file + diff --git a/Runtime/C#/Runtime/BebopRecord.cs b/Runtime/C#/Runtime/BebopRecord.cs index cb235a13..09bd18eb 100644 --- a/Runtime/C#/Runtime/BebopRecord.cs +++ b/Runtime/C#/Runtime/BebopRecord.cs @@ -10,6 +10,51 @@ namespace Bebop.Runtime { + + /// + /// Provides a contract for decoding various byte-based data representations into instances of Bebop records of type . + /// + /// The type of Bebop record that implements the decoding methods, constrained to . + public interface IDecodable where T : BaseBebopRecord + { + /// + /// Decodes the specified byte array into an instance of the Bebop record . + /// + /// The byte array containing the encoded Bebop record data. + /// An instance of the Bebop record decoded from the byte array. + static abstract T Decode(byte[] record); + + /// + /// Decodes the specified read-only span of bytes into an instance of the Bebop record . + /// + /// The read-only span of bytes containing the encoded Bebop record data. + /// An instance of the Bebop record decoded from the read-only span of bytes. + static abstract T Decode(ReadOnlySpan record); + + /// + /// Decodes the specified read-only memory of bytes into an instance of the Bebop record . + /// + /// The read-only memory of bytes containing the encoded Bebop record data. + /// An instance of the Bebop record decoded from the read-only memory of bytes. + static abstract T Decode(ReadOnlyMemory record); + + /// + /// Decodes the specified array segment of bytes into an instance of the Bebop record . + /// + /// The array segment of bytes containing the encoded Bebop record data. + /// An instance of the Bebop record decoded from the array segment of bytes. + static abstract T Decode(ArraySegment record); + + /// + /// Decodes the specified immutable array of bytes into an instance of the Bebop record . + /// + /// The immutable array of bytes containing the encoded Bebop record data. + /// An instance of the Bebop record decoded from the immutable array of bytes. + static abstract T Decode(ImmutableArray record); + } + + + /// /// A base class which is implemented by all bebopc generated classes. /// @@ -39,6 +84,7 @@ protected BaseBebopRecord() { } /// calculates the exact number of bytes that will be produced when the current record is encoded. /// public abstract int ByteCount { get; } + /// /// Encodes the current record. /// @@ -281,7 +327,7 @@ internal override void AssignHandler(MethodInfo methodInfo, object handlerInstan if (methodInfo.IsValueTask()) { _handlerValueTaskDelegate = - (Func) (isStaticHandler + (Func)(isStaticHandler ? Delegate.CreateDelegate(delegateType, methodInfo) : Delegate.CreateDelegate(delegateType, handlerInstance, @@ -290,7 +336,7 @@ internal override void AssignHandler(MethodInfo methodInfo, object handlerInstan else if (methodInfo.IsTask()) { _handlerTaskDelegate = - (Func) (isStaticHandler + (Func)(isStaticHandler ? Delegate.CreateDelegate(delegateType, methodInfo) : Delegate.CreateDelegate(delegateType, handlerInstance, methodInfo)); @@ -298,7 +344,7 @@ internal override void AssignHandler(MethodInfo methodInfo, object handlerInstan else { _handlerVoidDelegate = - (Action) (isStaticHandler + (Action)(isStaticHandler ? Delegate.CreateDelegate(delegateType, methodInfo) : Delegate.CreateDelegate(delegateType, handlerInstance, methodInfo)); diff --git a/Runtime/C#/Runtime/BebopSerializer.cs b/Runtime/C#/Runtime/BebopSerializer.cs new file mode 100644 index 00000000..02cacb74 --- /dev/null +++ b/Runtime/C#/Runtime/BebopSerializer.cs @@ -0,0 +1,128 @@ +using System; +using System.Collections.Immutable; + +namespace Bebop.Runtime +{ + /// + /// Provides methods for encoding and decoding Bebop records. + /// + public static class BebopSerializer + { + // Encoding methods + + /// + /// Encodes the specified Bebop record into a byte array. + /// + /// The type of Bebop record to encode, which must inherit from . + /// The Bebop record instance to encode. + /// An array of bytes which contain the encoded Bebop record. + public static byte[] Encode(T record) where T : BaseBebopRecord + { + return record.Encode(); + } + + /// + /// Encodes the specified Bebop record into a byte array with an initial capacity. + /// + /// The type of Bebop record to encode, which must inherit from . + /// The Bebop record instance to encode. + /// The initial capacity of the byte array to use for encoding. + /// An array of bytes which contain the encoded Bebop record. + public static byte[] Encode(T record, int initialCapacity) where T : BaseBebopRecord + { + return record.Encode(initialCapacity); + } + + /// + /// Encodes the specified Bebop record into an immutable array of bytes. + /// + /// The type of Bebop record to encode, which must inherit from . + /// The Bebop record instance to encode. + /// An immutable array of bytes which contain the encoded Bebop record. + public static ImmutableArray EncodeImmutably(T record) where T : BaseBebopRecord + { + return record.EncodeImmutably(); + } + + /// + /// Encodes the specified Bebop record into an immutable array of bytes with an initial capacity. + /// + /// The type of Bebop record to encode, which must inherit from . + /// The Bebop record instance to encode. + /// The initial capacity of the immutable array to use for encoding. + /// An immutable array of bytes which contain the encoded Bebop record. + public static ImmutableArray EncodeImmutably(T record, int initialCapacity) where T : BaseBebopRecord + { + return record.EncodeImmutably(initialCapacity); + } + + /// + /// Encodes the specified Bebop record into the provided buffer. + /// + /// The type of Bebop record to encode, which must inherit from . + /// The Bebop record instance to encode. + /// The buffer to encode the record into. + /// The number of bytes written into the buffer. + public static int EncodeIntoBuffer(T record, byte[] outBuffer) where T : BaseBebopRecord + { + return record.EncodeIntoBuffer(outBuffer); + } + + // Decoding methods + + /// + /// Decodes the specified byte array into an instance of the Bebop record . + /// + /// The type of Bebop record to decode, which must implement and . + /// The byte array containing the encoded Bebop record data. + /// An instance of the Bebop record decoded from the byte array. + public static T Decode(byte[] data) where T : BaseBebopRecord, IDecodable, new() + { + return T.Decode(data); + } + + /// + /// Decodes the specified read-only span of bytes into an instance of the Bebop record . + /// + /// The type of Bebop record to decode, which must implement and . + /// The read-only span of bytes containing the encoded Bebop record data. + /// An instance of the Bebop record decoded from the read-only span of bytes. + public static T Decode(ReadOnlySpan data) where T : BaseBebopRecord, IDecodable, new() + { + return T.Decode(data); + } + + /// + /// Decodes the specified read-only memory of bytes into an instance of the Bebop record . + /// + /// The type of Bebop record to decode, which must implement and . + /// The read-only memory of bytes containing the encoded Bebop record data. + /// An instance of the Bebop record decoded from the read-only memory of bytes. + public static T Decode(ReadOnlyMemory data) where T : BaseBebopRecord, IDecodable, new() + { + return T.Decode(data); + } + + /// + /// Decodes the specified array segment of bytes into an instance of the Bebop record . + /// + /// The type of Bebop record to decode, which must implement and . + /// The array segment of bytes containing the encoded Bebop record data. + /// An instance of the Bebop record decoded from the array segment of bytes. + public static T Decode(ArraySegment data) where T : BaseBebopRecord, IDecodable, new() + { + return T.Decode(data); + } + + /// + /// Decodes the specified immutable array of bytes into an instance of the Bebop record . + /// + /// The type of Bebop record to decode, which must implement and . + /// The immutable array of bytes containing the encoded Bebop record data. + /// An instance of the Bebop record decoded from the immutable array of bytes. + public static T Decode(ImmutableArray data) where T : BaseBebopRecord, IDecodable, new() + { + return T.Decode(data); + } + } +}