diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 3212949..409291d 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -10,10 +10,11 @@ concurrency: jobs: unit: - name: Unit tests (${{ matrix.config }}) + name: Unit tests (${{ matrix.project }} - ${{ matrix.config }}) runs-on: ubuntu-latest strategy: matrix: + project: [RyuSocks.Test, RyuSocks.Generator.Test] config: [Debug, Release] steps: - uses: actions/checkout@v4 @@ -29,7 +30,7 @@ jobs: uses: TSRBerry/unstable-commands@v1 with: commands: dotnet test -c "${{ matrix.config }}" - working-directory: RyuSocks.Test + working-directory: ${{ matrix.project }} timeout-minutes: 10 retry-codes: 139 diff --git a/Directory.Packages.props b/Directory.Packages.props index 13a50d5..4296486 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -8,6 +8,7 @@ + diff --git a/RyuSocks.Generator.Test/PacketGeneratorTests.cs b/RyuSocks.Generator.Test/PacketGeneratorTests.cs new file mode 100644 index 0000000..baaa333 --- /dev/null +++ b/RyuSocks.Generator.Test/PacketGeneratorTests.cs @@ -0,0 +1,162 @@ +using Microsoft.CodeAnalysis.Testing; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Threading.Tasks; +using Xunit; +using Verify = RyuSocks.Generator.Test.SourceGeneratorVerifier; + +namespace RyuSocks.Generator.Test +{ + [ExcludeFromCodeCoverage] + public class PacketGeneratorTests + { + private const string DataDirectory = "data/PacketGenerator/"; + private const int DefaultSourcesAmount = 3; + + private static string GetSourceFromFile(string path) => File.ReadAllText(DataDirectory + path); + private static (string filename, string content) GetGeneratedSourceFromFile(string path) => + (Path.GetFileName(path), File.ReadAllText(DataDirectory + path)); + + private static (string, string)[] GetDefaultGeneratedSources(int otherExpectedSourcesLength) + { + + (string, string)[] generatedSources = new (string, string)[DefaultSourcesAmount + otherExpectedSourcesLength]; + + generatedSources[0] = GetGeneratedSourceFromFile("StringEncoding.g.cs"); + generatedSources[1] = GetGeneratedSourceFromFile("PacketFieldAttribute.g.cs"); + generatedSources[2] = GetGeneratedSourceFromFile("Packet.g.cs"); + + return generatedSources; + } + + [Theory] + [InlineData("SimpleProps", "TestPacket.cs", + "TestPacket.Byte1.g.cs", "TestPacket.SByte1.g.cs", + "TestPacket.UShort1.g.cs", "TestPacket.Short1.g.cs", + "TestPacket.UInt1.g.cs", "TestPacket.Int1.g.cs", + "TestPacket.ULong1.g.cs", "TestPacket.Long1.g.cs", + "TestPacket.String1.g.cs", "TestPacket.String2.g.cs", "TestPacket.String3.g.cs")] + [InlineData("SimplePropsOffsetMember", "TestPacket.cs", + "TestPacket.Byte1.g.cs", "TestPacket.SByte1.g.cs", + "TestPacket.UShort1.g.cs", "TestPacket.Short1.g.cs", + "TestPacket.UInt1.g.cs", "TestPacket.Int1.g.cs", + "TestPacket.ULong1.g.cs", "TestPacket.Long1.g.cs", + "TestPacket.String1.g.cs", "TestPacket.String2.g.cs", "TestPacket.String3.g.cs")] + [InlineData("Namespaces", "TestPacket.cs", + "TestSpace.TestPacket.TestField.g.cs", "TestSpace.TestPacket1.FirstField.g.cs", + "AnotherSpace.TestPacket.TestField.g.cs", "AnotherSpace.TestPacket2.SecondField.g.cs")] + [InlineData("StringEncodings", "TestPacket.cs", + "LittleEndian.ASCIIPacket.String1.g.cs", "LittleEndian.ASCIIPacket.String2.g.cs", "LittleEndian.ASCIIPacket.String3.g.cs", "LittleEndian.ASCIIPacket.String4.g.cs", + "LittleEndian.UnicodePacket.String1.g.cs", "LittleEndian.UnicodePacket.String2.g.cs", "LittleEndian.UnicodePacket.String3.g.cs", "LittleEndian.UnicodePacket.String4.g.cs", + "LittleEndian.UTF7Packet.String1.g.cs", "LittleEndian.UTF7Packet.String2.g.cs", "LittleEndian.UTF7Packet.String3.g.cs", "LittleEndian.UTF7Packet.String4.g.cs", + "LittleEndian.UTF8Packet.String1.g.cs", "LittleEndian.UTF8Packet.String2.g.cs", "LittleEndian.UTF8Packet.String3.g.cs", "LittleEndian.UTF8Packet.String4.g.cs", + "LittleEndian.UTF32Packet.String1.g.cs", "LittleEndian.UTF32Packet.String2.g.cs", "LittleEndian.UTF32Packet.String3.g.cs", "LittleEndian.UTF32Packet.String4.g.cs", + "BigEndian.ASCIIPacket.String1.g.cs", "BigEndian.ASCIIPacket.String2.g.cs", "BigEndian.ASCIIPacket.String3.g.cs", "BigEndian.ASCIIPacket.String4.g.cs", + "BigEndian.UnicodePacket.String1.g.cs", "BigEndian.UnicodePacket.String2.g.cs", "BigEndian.UnicodePacket.String3.g.cs", "BigEndian.UnicodePacket.String4.g.cs", + "BigEndian.UTF7Packet.String1.g.cs", "BigEndian.UTF7Packet.String2.g.cs", "BigEndian.UTF7Packet.String3.g.cs", "BigEndian.UTF7Packet.String4.g.cs", + "BigEndian.UTF8Packet.String1.g.cs", "BigEndian.UTF8Packet.String2.g.cs", "BigEndian.UTF8Packet.String3.g.cs", "BigEndian.UTF8Packet.String4.g.cs", + "BigEndian.UTF32Packet.String1.g.cs", "BigEndian.UTF32Packet.String2.g.cs", "BigEndian.UTF32Packet.String3.g.cs", "BigEndian.UTF32Packet.String4.g.cs")] + public async Task GeneratedSources_AsExpected(string directory, string sourcePath, params string[] expectedGeneratedSourcePath) + { + (string, string)[] generatedSources = GetDefaultGeneratedSources(expectedGeneratedSourcePath.Length); + directory += "/"; + + for (int i = 0; i < expectedGeneratedSourcePath.Length; i++) + { + generatedSources[DefaultSourcesAmount + i] = GetGeneratedSourceFromFile(directory + expectedGeneratedSourcePath[i]); + } + + await Verify.VerifyGeneratedSources( + GetSourceFromFile(directory + sourcePath), + generatedSources + ); + } + + [Fact] + public async Task Generator_Fails_BadConstructorArgs() + { + const string Directory = "Exceptions/"; + (string, string)[] generatedSources = GetDefaultGeneratedSources(0); + DiagnosticResult[] expectedDiagnostics = [ + // Generator failed to generate source + DiagnosticResult.CompilerWarning("CS8785"), + // Argument 1: Cannot convert from float to int + DiagnosticResult.CompilerError("CS1503").WithSpan(7, 18, 7, 22).WithArguments("1", "float", "int"), + // Partial property must have an implementation part + DiagnosticResult.CompilerError("CS9248").WithSpan(8, 25, 8, 30).WithArguments("TestPacket.Byte1"), + ]; + + await Verify.VerifyGeneratedSources( + GetSourceFromFile(Directory + "BadConstructorArgs.cs"), + generatedSources, + expectedDiagnostics + ); + } + + [Fact] + public async Task GeneratedSources_WorksWithGeneratedTypes() + { + const int ExpectedGeneratedSourcesLength = 11; + const string Directory = "GeneratedTypes/"; + (string, string)[] generatedSources = GetDefaultGeneratedSources(ExpectedGeneratedSourcesLength); + DiagnosticResult[] expectedDiagnostics = [ + // Type not found + DiagnosticResult.CompilerError("CS0246").WithSpan(9, 20, 9, 37).WithArguments("GeneratedEnumByte"), + DiagnosticResult.CompilerError("CS0246").WithSpan("RyuSocks.Generator/RyuSocks.Generator.PacketGenerator/TestPacket.Byte1.g.cs", 8, 20, 8, 37).WithArguments("GeneratedEnumByte"), + DiagnosticResult.CompilerError("CS0246").WithSpan("RyuSocks.Generator/RyuSocks.Generator.PacketGenerator/TestPacket.Byte1.g.cs", 12, 21, 12, 38).WithArguments("GeneratedEnumByte"), + DiagnosticResult.CompilerError("CS0246").WithSpan(12, 22, 12, 40).WithArguments("GeneratedEnumSByte"), + DiagnosticResult.CompilerError("CS0246").WithSpan("RyuSocks.Generator/RyuSocks.Generator.PacketGenerator/TestPacket.SByte1.g.cs", 8, 22, 8, 40).WithArguments("GeneratedEnumSByte"), + DiagnosticResult.CompilerError("CS0246").WithSpan("RyuSocks.Generator/RyuSocks.Generator.PacketGenerator/TestPacket.SByte1.g.cs", 12, 21, 12, 39).WithArguments("GeneratedEnumSByte"), + DiagnosticResult.CompilerError("CS0246").WithSpan(15, 23, 15, 42).WithArguments("GeneratedEnumUShort"), + DiagnosticResult.CompilerError("CS0246").WithSpan("RyuSocks.Generator/RyuSocks.Generator.PacketGenerator/TestPacket.UShort1.g.cs", 8, 23, 8, 42).WithArguments("GeneratedEnumUShort"), + DiagnosticResult.CompilerError("CS0246").WithSpan("RyuSocks.Generator/RyuSocks.Generator.PacketGenerator/TestPacket.UShort1.g.cs", 12, 21, 12, 40).WithArguments("GeneratedEnumUShort"), + DiagnosticResult.CompilerError("CS0246").WithSpan(18, 21, 18, 39).WithArguments("GeneratedEnumShort"), + DiagnosticResult.CompilerError("CS0246").WithSpan("RyuSocks.Generator/RyuSocks.Generator.PacketGenerator/TestPacket.Short1.g.cs", 8, 21, 8, 39).WithArguments("GeneratedEnumShort"), + DiagnosticResult.CompilerError("CS0246").WithSpan("RyuSocks.Generator/RyuSocks.Generator.PacketGenerator/TestPacket.Short1.g.cs", 12, 21, 12, 39).WithArguments("GeneratedEnumShort"), + DiagnosticResult.CompilerError("CS0246").WithSpan(21, 31, 21, 48).WithArguments("GeneratedEnumUInt"), + DiagnosticResult.CompilerError("CS0246").WithSpan("RyuSocks.Generator/RyuSocks.Generator.PacketGenerator/TestPacket.UInt1.g.cs", 8, 31, 8, 48).WithArguments("GeneratedEnumUInt"), + DiagnosticResult.CompilerError("CS0246").WithSpan("RyuSocks.Generator/RyuSocks.Generator.PacketGenerator/TestPacket.UInt1.g.cs", 12, 21, 12, 38).WithArguments("GeneratedEnumUInt"), + DiagnosticResult.CompilerError("CS0246").WithSpan(24, 13, 24, 29).WithArguments("GeneratedEnumInt"), + DiagnosticResult.CompilerError("CS0246").WithSpan("RyuSocks.Generator/RyuSocks.Generator.PacketGenerator/TestPacket.Int1.g.cs", 8, 13, 8, 29).WithArguments("GeneratedEnumInt"), + DiagnosticResult.CompilerError("CS0246").WithSpan("RyuSocks.Generator/RyuSocks.Generator.PacketGenerator/TestPacket.Int1.g.cs", 12, 21, 12, 37).WithArguments("GeneratedEnumInt"), + DiagnosticResult.CompilerError("CS0246").WithSpan(27, 13, 27, 31).WithArguments("GeneratedEnumULong"), + DiagnosticResult.CompilerError("CS0246").WithSpan("RyuSocks.Generator/RyuSocks.Generator.PacketGenerator/TestPacket.ULong1.g.cs", 8, 13, 8, 31).WithArguments("GeneratedEnumULong"), + DiagnosticResult.CompilerError("CS0246").WithSpan("RyuSocks.Generator/RyuSocks.Generator.PacketGenerator/TestPacket.ULong1.g.cs", 12, 21, 12, 39).WithArguments("GeneratedEnumULong"), + DiagnosticResult.CompilerError("CS0246").WithSpan(30, 20, 30, 37).WithArguments("GeneratedEnumLong"), + DiagnosticResult.CompilerError("CS0246").WithSpan("RyuSocks.Generator/RyuSocks.Generator.PacketGenerator/TestPacket.Long1.g.cs", 8, 20, 8, 37).WithArguments("GeneratedEnumLong"), + DiagnosticResult.CompilerError("CS0246").WithSpan("RyuSocks.Generator/RyuSocks.Generator.PacketGenerator/TestPacket.Long1.g.cs", 12, 21, 12, 38).WithArguments("GeneratedEnumLong"), + DiagnosticResult.CompilerError("CS0246").WithSpan(33, 20, 33, 37).WithArguments("GeneratedEnumByte"), + DiagnosticResult.CompilerError("CS0246").WithSpan("RyuSocks.Generator/RyuSocks.Generator.PacketGenerator/TestPacket.Byte2.g.cs", 8, 20, 8, 37).WithArguments("GeneratedEnumByte"), + DiagnosticResult.CompilerError("CS0246").WithSpan("RyuSocks.Generator/RyuSocks.Generator.PacketGenerator/TestPacket.Byte2.g.cs", 12, 21, 12, 38).WithArguments("GeneratedEnumByte"), + DiagnosticResult.CompilerError("CS0246").WithSpan(36, 13, 36, 29).WithArguments("GeneratedEnumInt"), + DiagnosticResult.CompilerError("CS0246").WithSpan("RyuSocks.Generator/RyuSocks.Generator.PacketGenerator/TestPacket.Int2.g.cs", 8, 13, 8, 29).WithArguments("GeneratedEnumInt"), + DiagnosticResult.CompilerError("CS0246").WithSpan("RyuSocks.Generator/RyuSocks.Generator.PacketGenerator/TestPacket.Int2.g.cs", 12, 13, 12, 29).WithArguments("GeneratedEnumInt"), + DiagnosticResult.CompilerError("CS0246").WithSpan("RyuSocks.Generator/RyuSocks.Generator.PacketGenerator/TestPacket.Int2.g.cs", 12, 45, 12, 61).WithArguments("GeneratedEnumInt"), + DiagnosticResult.CompilerError("CS0246").WithSpan("RyuSocks.Generator/RyuSocks.Generator.PacketGenerator/TestPacket.Int2.g.cs", 16, 30, 16, 46).WithArguments("GeneratedEnumInt"), + DiagnosticResult.CompilerError("CS0246").WithSpan(39, 23, 39, 40).WithArguments("GeneratedEnumByte"), + DiagnosticResult.CompilerError("CS0246").WithSpan("RyuSocks.Generator/RyuSocks.Generator.PacketGenerator/TestPacket.Byte3.g.cs", 8, 23, 8, 40).WithArguments("GeneratedEnumByte"), + DiagnosticResult.CompilerError("CS0246").WithSpan("RyuSocks.Generator/RyuSocks.Generator.PacketGenerator/TestPacket.Byte3.g.cs", 12, 13, 12, 30).WithArguments("GeneratedEnumByte"), + DiagnosticResult.CompilerError("CS0246").WithSpan("RyuSocks.Generator/RyuSocks.Generator.PacketGenerator/TestPacket.Byte3.g.cs", 12, 46, 12, 63).WithArguments("GeneratedEnumByte"), + DiagnosticResult.CompilerError("CS0246").WithSpan("RyuSocks.Generator/RyuSocks.Generator.PacketGenerator/TestPacket.Byte3.g.cs", 16, 30, 16, 47).WithArguments("GeneratedEnumByte"), + ]; + + generatedSources[DefaultSourcesAmount] = GetGeneratedSourceFromFile(Directory + "TestPacket.Byte1.g.cs"); + generatedSources[DefaultSourcesAmount + 1] = GetGeneratedSourceFromFile(Directory + "TestPacket.SByte1.g.cs"); + generatedSources[DefaultSourcesAmount + 2] = GetGeneratedSourceFromFile(Directory + "TestPacket.UShort1.g.cs"); + generatedSources[DefaultSourcesAmount + 3] = GetGeneratedSourceFromFile(Directory + "TestPacket.Short1.g.cs"); + generatedSources[DefaultSourcesAmount + 4] = GetGeneratedSourceFromFile(Directory + "TestPacket.UInt1.g.cs"); + generatedSources[DefaultSourcesAmount + 5] = GetGeneratedSourceFromFile(Directory + "TestPacket.Int1.g.cs"); + generatedSources[DefaultSourcesAmount + 6] = GetGeneratedSourceFromFile(Directory + "TestPacket.ULong1.g.cs"); + generatedSources[DefaultSourcesAmount + 7] = GetGeneratedSourceFromFile(Directory + "TestPacket.Long1.g.cs"); + generatedSources[DefaultSourcesAmount + 8] = GetGeneratedSourceFromFile(Directory + "TestPacket.Byte2.g.cs"); + generatedSources[DefaultSourcesAmount + 9] = GetGeneratedSourceFromFile(Directory + "TestPacket.Int2.g.cs"); + generatedSources[DefaultSourcesAmount + 10] = GetGeneratedSourceFromFile(Directory + "TestPacket.Byte3.g.cs"); + + await Verify.VerifyGeneratedSources( + GetSourceFromFile(Directory + "TestPacket.cs"), + generatedSources, + expectedDiagnostics + ); + } + } +} diff --git a/RyuSocks.Generator.Test/RyuSocks.Generator.Test.csproj b/RyuSocks.Generator.Test/RyuSocks.Generator.Test.csproj new file mode 100644 index 0000000..33b867f --- /dev/null +++ b/RyuSocks.Generator.Test/RyuSocks.Generator.Test.csproj @@ -0,0 +1,29 @@ + + + net9.0 + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + + + diff --git a/RyuSocks.Generator.Test/SourceGeneratorVerifier.cs b/RyuSocks.Generator.Test/SourceGeneratorVerifier.cs new file mode 100644 index 0000000..b5afe13 --- /dev/null +++ b/RyuSocks.Generator.Test/SourceGeneratorVerifier.cs @@ -0,0 +1,53 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Testing; +using Microsoft.CodeAnalysis.Testing; +using System; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using System.Threading.Tasks; + +namespace RyuSocks.Generator.Test +{ + [ExcludeFromCodeCoverage] + public static class SourceGeneratorVerifier + where TSourceGenerator : new() + { + public static Task VerifyGeneratedSources(string source, (string filename, string content)[] generatedSources, DiagnosticResult[] expectedDiagnostics = null) + { + var test = new Test + { + TestCode = source + }; + + foreach ((string filename, string content) in generatedSources) + { + test.TestState.GeneratedSources.Add((typeof(TSourceGenerator), filename, content)); + } + + if (expectedDiagnostics != null) + { + test.ExpectedDiagnostics.AddRange(expectedDiagnostics); + } + + return test.RunAsync(); + } + + public class Test : CSharpSourceGeneratorTest + { + protected override CompilationOptions CreateCompilationOptions() + { + var compilationOptions = base.CreateCompilationOptions(); + return compilationOptions.WithSpecificDiagnosticOptions( + compilationOptions.SpecificDiagnosticOptions.SetItems(GetNullableWarningsFromCompiler())); + } + + private static ImmutableDictionary GetNullableWarningsFromCompiler() + { + string[] args = ["/warnaserror:nullable"]; + var commandLineArguments = CSharpCommandLineParser.Default.Parse(args, baseDirectory: Environment.CurrentDirectory, sdkDirectory: Environment.CurrentDirectory); + return commandLineArguments.CompilationOptions.SpecificDiagnosticOptions; + } + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/Exceptions/BadConstructorArgs.cs b/RyuSocks.Generator.Test/data/PacketGenerator/Exceptions/BadConstructorArgs.cs new file mode 100644 index 0000000..2dc9507 --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/Exceptions/BadConstructorArgs.cs @@ -0,0 +1,9 @@ +using RyuSocks.Packets; + +public partial class TestPacket : Packet +{ + private int SecondOffset => 1; + + [PacketField(1.5f)] + public partial byte Byte1 { get; set; } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/GeneratedTypes/TestPacket.Byte1.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/GeneratedTypes/TestPacket.Byte1.g.cs new file mode 100644 index 0000000..8462704 --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/GeneratedTypes/TestPacket.Byte1.g.cs @@ -0,0 +1,19 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +partial class TestPacket +{ + public partial GeneratedEnumByte Byte1 + { + get + { + return (GeneratedEnumByte)this[0]; + } + set + { + this[0] = (byte)value; + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/GeneratedTypes/TestPacket.Byte2.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/GeneratedTypes/TestPacket.Byte2.g.cs new file mode 100644 index 0000000..ddb869a --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/GeneratedTypes/TestPacket.Byte2.g.cs @@ -0,0 +1,19 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +partial class TestPacket +{ + public partial GeneratedEnumByte Byte2 + { + get + { + return (GeneratedEnumByte)this[this.AnOffset]; + } + set + { + this[this.AnOffset] = (byte)value; + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/GeneratedTypes/TestPacket.Byte3.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/GeneratedTypes/TestPacket.Byte3.g.cs new file mode 100644 index 0000000..090ade7 --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/GeneratedTypes/TestPacket.Byte3.g.cs @@ -0,0 +1,34 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +partial class TestPacket +{ + protected partial GeneratedEnumByte[] Byte3 + { + get + { + GeneratedEnumByte[] result = new GeneratedEnumByte[this.ALength]; + + for (int i = 0; i < this.ALength; i++) + { + result[i] = (GeneratedEnumByte)this[this.AnOffset + i]; + } + + return result; + } + set + { + if (value.Length != this.ALength) + { + throw new ArgumentOutOfRangeException(nameof(value.Length), value.Length, $"{nameof(value.Length)} must be equal to: {this.ALength}"); + } + + for (int i = 0; i < this.ALength; i++) + { + this[this.AnOffset + i] = (byte)value[i]; + } + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/GeneratedTypes/TestPacket.Int1.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/GeneratedTypes/TestPacket.Int1.g.cs new file mode 100644 index 0000000..bef5b7a --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/GeneratedTypes/TestPacket.Int1.g.cs @@ -0,0 +1,19 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +partial class TestPacket +{ + partial GeneratedEnumInt Int1 + { + get + { + return (GeneratedEnumInt)BinaryPrimitives.ReadInt32LittleEndian(this.AsSpan(5, 4)); + } + set + { + BinaryPrimitives.WriteInt32LittleEndian(this.AsSpan(5, 4), (int)value); + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/GeneratedTypes/TestPacket.Int2.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/GeneratedTypes/TestPacket.Int2.g.cs new file mode 100644 index 0000000..72766eb --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/GeneratedTypes/TestPacket.Int2.g.cs @@ -0,0 +1,34 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +partial class TestPacket +{ + partial GeneratedEnumInt[] Int2 + { + get + { + GeneratedEnumInt[] result = new GeneratedEnumInt[this.ALength]; + + for (int i = 0; i < this.ALength; i++) + { + result[i] = (GeneratedEnumInt)BinaryPrimitives.ReadInt32LittleEndian(this.AsSpan(9 + (i * 4), 4)); + } + + return result; + } + set + { + if (value.Length != this.ALength) + { + throw new ArgumentOutOfRangeException(nameof(value.Length), value.Length, $"{nameof(value.Length)} must be equal to: {this.ALength}"); + } + + for (int i = 0; i < this.ALength; i++) + { + BinaryPrimitives.WriteInt32LittleEndian(this.AsSpan(9 + (i * 4), 4), (int)value[i]); + } + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/GeneratedTypes/TestPacket.Long1.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/GeneratedTypes/TestPacket.Long1.g.cs new file mode 100644 index 0000000..664a33d --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/GeneratedTypes/TestPacket.Long1.g.cs @@ -0,0 +1,19 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +partial class TestPacket +{ + public partial GeneratedEnumLong Long1 + { + get + { + return (GeneratedEnumLong)BinaryPrimitives.ReadInt64LittleEndian(this.AsSpan(7, 8)); + } + set + { + BinaryPrimitives.WriteInt64LittleEndian(this.AsSpan(7, 8), (long)value); + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/GeneratedTypes/TestPacket.SByte1.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/GeneratedTypes/TestPacket.SByte1.g.cs new file mode 100644 index 0000000..1a35637 --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/GeneratedTypes/TestPacket.SByte1.g.cs @@ -0,0 +1,19 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +partial class TestPacket +{ + internal partial GeneratedEnumSByte SByte1 + { + get + { + return (GeneratedEnumSByte)(sbyte)this[1]; + } + set + { + this[1] = (byte)value; + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/GeneratedTypes/TestPacket.Short1.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/GeneratedTypes/TestPacket.Short1.g.cs new file mode 100644 index 0000000..fc0c710 --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/GeneratedTypes/TestPacket.Short1.g.cs @@ -0,0 +1,19 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +partial class TestPacket +{ + private partial GeneratedEnumShort Short1 + { + get + { + return (GeneratedEnumShort)BinaryPrimitives.ReadInt16LittleEndian(this.AsSpan(3, 2)); + } + set + { + BinaryPrimitives.WriteInt16LittleEndian(this.AsSpan(3, 2), (short)value); + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/GeneratedTypes/TestPacket.UInt1.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/GeneratedTypes/TestPacket.UInt1.g.cs new file mode 100644 index 0000000..aff640e --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/GeneratedTypes/TestPacket.UInt1.g.cs @@ -0,0 +1,19 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +partial class TestPacket +{ + protected private partial GeneratedEnumUInt UInt1 + { + get + { + return (GeneratedEnumUInt)BinaryPrimitives.ReadUInt32LittleEndian(this.AsSpan(4, 4)); + } + set + { + BinaryPrimitives.WriteUInt32LittleEndian(this.AsSpan(4, 4), (uint)value); + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/GeneratedTypes/TestPacket.ULong1.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/GeneratedTypes/TestPacket.ULong1.g.cs new file mode 100644 index 0000000..c870820 --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/GeneratedTypes/TestPacket.ULong1.g.cs @@ -0,0 +1,19 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +partial class TestPacket +{ + partial GeneratedEnumULong ULong1 + { + get + { + return (GeneratedEnumULong)BinaryPrimitives.ReadUInt64LittleEndian(this.AsSpan(6, 8)); + } + set + { + BinaryPrimitives.WriteUInt64LittleEndian(this.AsSpan(6, 8), (ulong)value); + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/GeneratedTypes/TestPacket.UShort1.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/GeneratedTypes/TestPacket.UShort1.g.cs new file mode 100644 index 0000000..5524d45 --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/GeneratedTypes/TestPacket.UShort1.g.cs @@ -0,0 +1,19 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +partial class TestPacket +{ + protected partial GeneratedEnumUShort UShort1 + { + get + { + return (GeneratedEnumUShort)BinaryPrimitives.ReadUInt16LittleEndian(this.AsSpan(2, 2)); + } + set + { + BinaryPrimitives.WriteUInt16LittleEndian(this.AsSpan(2, 2), (ushort)value); + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/GeneratedTypes/TestPacket.cs b/RyuSocks.Generator.Test/data/PacketGenerator/GeneratedTypes/TestPacket.cs new file mode 100644 index 0000000..b0d62ac --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/GeneratedTypes/TestPacket.cs @@ -0,0 +1,40 @@ +using RyuSocks.Packets; + +public partial class TestPacket : Packet +{ + private int AnOffset => 2; + protected int ALength => 3; + + [PacketField(0, AssumeGeneratedEnumType="byte")] + public partial GeneratedEnumByte Byte1 { get; set; } + + [PacketField(1, AssumeGeneratedEnumType="SBYTE")] + internal partial GeneratedEnumSByte SByte1 { get; set; } + + [PacketField(2, AssumeGeneratedEnumType="UsHoRt")] + protected partial GeneratedEnumUShort UShort1 { get; set; } + + [PacketField(3, AssumeGeneratedEnumType="sHoRt")] + private partial GeneratedEnumShort Short1 { get; set; } + + [PacketField(4, AssumeGeneratedEnumType="uint")] + protected private partial GeneratedEnumUInt UInt1 { get; set; } + + [PacketField(5, AssumeGeneratedEnumType="int")] + partial GeneratedEnumInt Int1 { get; set; } + + [PacketField(6, AssumeGeneratedEnumType="ulong")] + partial GeneratedEnumULong ULong1 { get; set; } + + [PacketField(7, AssumeGeneratedEnumType="long")] + public partial GeneratedEnumLong Long1 { get; set; } + + [PacketField(nameof(AnOffset), AssumeGeneratedEnumType="byte")] + public partial GeneratedEnumByte Byte2 { get; set; } + + [PacketField(9, LengthMember = nameof(ALength), AssumeGeneratedEnumType="int")] + partial GeneratedEnumInt[] Int2 { get; set; } + + [PacketField(nameof(AnOffset), LengthMember = nameof(ALength), AssumeGeneratedEnumType="byte")] + protected partial GeneratedEnumByte[] Byte3 { get; set; } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/Namespaces/AnotherSpace.TestPacket.TestField.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/Namespaces/AnotherSpace.TestPacket.TestField.g.cs new file mode 100644 index 0000000..dde2b3b --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/Namespaces/AnotherSpace.TestPacket.TestField.g.cs @@ -0,0 +1,22 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +namespace AnotherSpace +{ + partial class TestPacket + { + public partial byte TestField + { + get + { + return this[4]; + } + set + { + this[4] = value; + } + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/Namespaces/AnotherSpace.TestPacket2.SecondField.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/Namespaces/AnotherSpace.TestPacket2.SecondField.g.cs new file mode 100644 index 0000000..4374a95 --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/Namespaces/AnotherSpace.TestPacket2.SecondField.g.cs @@ -0,0 +1,22 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +namespace AnotherSpace +{ + partial class TestPacket2 + { + private partial byte SecondField + { + get + { + return this[2]; + } + set + { + this[2] = value; + } + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/Namespaces/TestPacket.cs b/RyuSocks.Generator.Test/data/PacketGenerator/Namespaces/TestPacket.cs new file mode 100644 index 0000000..c259ded --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/Namespaces/TestPacket.cs @@ -0,0 +1,31 @@ +using RyuSocks.Packets; + +namespace TestSpace +{ + public partial class TestPacket : Packet + { + [PacketField(0)] + internal partial byte TestField { get; set; } + } + + partial class TestPacket1 : Packet + { + [PacketField(1)] + public partial byte FirstField { get; set; } + } +} + +namespace AnotherSpace +{ + partial class TestPacket : Packet + { + [PacketField(4)] + public partial byte TestField { get; set; } + } + + internal partial class TestPacket2 : Packet + { + [PacketField(2)] + private partial byte SecondField { get; set; } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/Namespaces/TestSpace.TestPacket.TestField.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/Namespaces/TestSpace.TestPacket.TestField.g.cs new file mode 100644 index 0000000..df4afcc --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/Namespaces/TestSpace.TestPacket.TestField.g.cs @@ -0,0 +1,22 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +namespace TestSpace +{ + partial class TestPacket + { + internal partial byte TestField + { + get + { + return this[0]; + } + set + { + this[0] = value; + } + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/Namespaces/TestSpace.TestPacket1.FirstField.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/Namespaces/TestSpace.TestPacket1.FirstField.g.cs new file mode 100644 index 0000000..88fb785 --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/Namespaces/TestSpace.TestPacket1.FirstField.g.cs @@ -0,0 +1,22 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +namespace TestSpace +{ + partial class TestPacket1 + { + public partial byte FirstField + { + get + { + return this[1]; + } + set + { + this[1] = value; + } + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/Packet.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/Packet.g.cs new file mode 100644 index 0000000..58897d9 --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/Packet.g.cs @@ -0,0 +1,42 @@ +using System; + +namespace RyuSocks.Packets +{ + public abstract partial class Packet + { + /// + /// The contents of the packet. + /// + public byte[] Bytes { get; protected set; } + + /// + public byte this[int i] + { + get => Bytes[i]; + set => Bytes[i] = value; + } + + /// + /// Creates a new span over the packet. + /// + /// + /// + public Span AsSpan() => Bytes; + + /// + /// Creates a new Span over the portion of the packet beginning + /// at 'start' index and ending at 'end' index (exclusive). + /// + /// The index at which to begin the Span. + /// The number of items in the Span. + /// + public Span AsSpan(int start, int length) => Bytes.AsSpan(start, length); + + protected Packet() { } + + protected Packet(byte[] bytes) + { + Bytes = bytes; + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/PacketFieldAttribute.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/PacketFieldAttribute.g.cs new file mode 100644 index 0000000..76922c3 --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/PacketFieldAttribute.g.cs @@ -0,0 +1,83 @@ +using System; + +namespace RyuSocks.Packets +{ + [AttributeUsage(AttributeTargets.Property, Inherited = false)] + [System.Diagnostics.Conditional("RyuSocks_PacketGenerator_DEBUG")] + public sealed class PacketFieldAttribute : Attribute + { + private int offset = -1; + private string offsetMember = string.Empty; + + /// + /// Marks this property as a field of the packet. + /// The containing class needs to extend . + /// + /// The offset of this field in the packet. + public PacketFieldAttribute([System.ComponentModel.DataAnnotations.Range(0, int.MaxValue)] int offset) + { + this.offset = offset; + } + + /// + /// Marks this property as a field of the packet. + /// The containing class needs to extend . + /// + /// The name of the member which specifies the offset of this field in the packet. + public PacketFieldAttribute(string offsetMember) + { + this.offsetMember = offsetMember; + } + + /// + /// The length of this field in bytes. + /// Only required if it can't be determined from the property type. + /// + public int Length { get; set; } = -1; + + /// + /// The name of the member which specifies the length of this field in bytes. + /// Only required if it can't be determined from the property type. + /// + public string LengthMember { get; set; } = string.Empty; + + /// + /// Whether this field is big endian. + /// + public bool IsBigEndian { get; set; } = false; + + /// + /// The minimum length of the array or chars allowed in this field. + /// + public int MinLength { get; set; } = -1; + + /// + /// The maximum length of the array or chars allowed in this field. + /// + public int MaxLength { get; set; } = -1; + + /// + /// The encoding of the underlying string. + /// Only required for string properties. + /// + public StringEncoding StringEncoding = StringEncoding.ASCII; + + /// + /// The name of the underlying type of the property type. + /// Only required if the type of the property is source generated. + /// + public string AssumeGeneratedEnumType { get; set; } = string.Empty; + + /// + /// The name of the method which should be invoked before the getter/setter is executed. + /// + /// + /// The method type must be void and have one optional parameter with the same type as the property. + /// If verification fails an exception should be thrown. + /// + public string ValidationMethod { get; set; } = string.Empty; + + public int Offset => offset; + public string OffsetMember => offsetMember; + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/SimpleProps/TestPacket.Byte1.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/SimpleProps/TestPacket.Byte1.g.cs new file mode 100644 index 0000000..e963d9e --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/SimpleProps/TestPacket.Byte1.g.cs @@ -0,0 +1,19 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +partial class TestPacket +{ + public partial byte Byte1 + { + get + { + return this[0]; + } + set + { + this[0] = value; + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/SimpleProps/TestPacket.Int1.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/SimpleProps/TestPacket.Int1.g.cs new file mode 100644 index 0000000..a00916a --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/SimpleProps/TestPacket.Int1.g.cs @@ -0,0 +1,19 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +partial class TestPacket +{ + partial int Int1 + { + get + { + return BinaryPrimitives.ReadInt32LittleEndian(this.AsSpan(5, 4)); + } + set + { + BinaryPrimitives.WriteInt32LittleEndian(this.AsSpan(5, 4), value); + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/SimpleProps/TestPacket.Long1.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/SimpleProps/TestPacket.Long1.g.cs new file mode 100644 index 0000000..8737c17 --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/SimpleProps/TestPacket.Long1.g.cs @@ -0,0 +1,19 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +partial class TestPacket +{ + public partial long Long1 + { + get + { + return BinaryPrimitives.ReadInt64LittleEndian(this.AsSpan(7, 8)); + } + set + { + BinaryPrimitives.WriteInt64LittleEndian(this.AsSpan(7, 8), value); + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/SimpleProps/TestPacket.SByte1.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/SimpleProps/TestPacket.SByte1.g.cs new file mode 100644 index 0000000..e2bdc20 --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/SimpleProps/TestPacket.SByte1.g.cs @@ -0,0 +1,19 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +partial class TestPacket +{ + internal partial sbyte SByte1 + { + get + { + return (sbyte)this[1]; + } + set + { + this[1] = (byte)value; + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/SimpleProps/TestPacket.Short1.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/SimpleProps/TestPacket.Short1.g.cs new file mode 100644 index 0000000..b758aef --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/SimpleProps/TestPacket.Short1.g.cs @@ -0,0 +1,19 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +partial class TestPacket +{ + private partial short Short1 + { + get + { + return BinaryPrimitives.ReadInt16LittleEndian(this.AsSpan(3, 2)); + } + set + { + BinaryPrimitives.WriteInt16LittleEndian(this.AsSpan(3, 2), value); + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/SimpleProps/TestPacket.String1.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/SimpleProps/TestPacket.String1.g.cs new file mode 100644 index 0000000..b6aef48 --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/SimpleProps/TestPacket.String1.g.cs @@ -0,0 +1,19 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +partial class TestPacket +{ + private partial string String1 + { + get + { + return Encoding.ASCII.GetString(this.AsSpan(8, 10)); + } + set + { + Encoding.ASCII.GetBytes(value, this.AsSpan(8, 10)); + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/SimpleProps/TestPacket.String2.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/SimpleProps/TestPacket.String2.g.cs new file mode 100644 index 0000000..2a09440 --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/SimpleProps/TestPacket.String2.g.cs @@ -0,0 +1,23 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +partial class TestPacket +{ + partial string String2 + { + get + { + return Encoding.ASCII.GetString(this.AsSpan(9, this.AStringByteLength)); + } + set + { + if (Encoding.ASCII.GetByteCount(value) != this.AStringByteLength) + { + throw new ArgumentOutOfRangeException(nameof(value), Encoding.ASCII.GetByteCount(value), $"byte length of value must be equal to: {this.AStringByteLength}"); + } + Encoding.ASCII.GetBytes(value, this.AsSpan(9, this.AStringByteLength)); + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/SimpleProps/TestPacket.String3.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/SimpleProps/TestPacket.String3.g.cs new file mode 100644 index 0000000..6e998b0 --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/SimpleProps/TestPacket.String3.g.cs @@ -0,0 +1,20 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +partial class TestPacket +{ + protected partial string String3 + { + get + { + return Encoding.ASCII.GetString(this.AsSpan(10, this.ReadWriteByteLength)); + } + set + { + this.ReadWriteByteLength = Encoding.ASCII.GetByteCount(value); + Encoding.ASCII.GetBytes(value, this.AsSpan(10, this.ReadWriteByteLength)); + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/SimpleProps/TestPacket.UInt1.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/SimpleProps/TestPacket.UInt1.g.cs new file mode 100644 index 0000000..51cce17 --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/SimpleProps/TestPacket.UInt1.g.cs @@ -0,0 +1,19 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +partial class TestPacket +{ + protected private partial uint UInt1 + { + get + { + return BinaryPrimitives.ReadUInt32LittleEndian(this.AsSpan(4, 4)); + } + set + { + BinaryPrimitives.WriteUInt32LittleEndian(this.AsSpan(4, 4), value); + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/SimpleProps/TestPacket.ULong1.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/SimpleProps/TestPacket.ULong1.g.cs new file mode 100644 index 0000000..6c8cfd8 --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/SimpleProps/TestPacket.ULong1.g.cs @@ -0,0 +1,19 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +partial class TestPacket +{ + partial ulong ULong1 + { + get + { + return BinaryPrimitives.ReadUInt64LittleEndian(this.AsSpan(6, 8)); + } + set + { + BinaryPrimitives.WriteUInt64LittleEndian(this.AsSpan(6, 8), value); + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/SimpleProps/TestPacket.UShort1.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/SimpleProps/TestPacket.UShort1.g.cs new file mode 100644 index 0000000..1676984 --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/SimpleProps/TestPacket.UShort1.g.cs @@ -0,0 +1,19 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +partial class TestPacket +{ + protected partial ushort UShort1 + { + get + { + return BinaryPrimitives.ReadUInt16LittleEndian(this.AsSpan(2, 2)); + } + set + { + BinaryPrimitives.WriteUInt16LittleEndian(this.AsSpan(2, 2), value); + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/SimpleProps/TestPacket.cs b/RyuSocks.Generator.Test/data/PacketGenerator/SimpleProps/TestPacket.cs new file mode 100644 index 0000000..f3a6c85 --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/SimpleProps/TestPacket.cs @@ -0,0 +1,45 @@ +using RyuSocks.Packets; + +public partial class TestPacket : Packet +{ + private int SecondOffset => 1; + + protected int AStringByteLength => 2; + + public int ReadWriteByteLength { get; set; } = 34; + + public byte Byte0 { get; set; } + + [PacketField(0)] + public partial byte Byte1 { get; set; } + + [PacketField(1)] + internal partial sbyte SByte1 { get; set; } + + [PacketField(2)] + protected partial ushort UShort1 { get; set; } + + [PacketField(3)] + private partial short Short1 { get; set; } + + [PacketField(4)] + protected private partial uint UInt1 { get; set; } + + [PacketField(5)] + partial int Int1 { get; set; } + + [PacketField(6)] + partial ulong ULong1 { get; set; } + + [PacketField(7)] + public partial long Long1 { get; set; } + + [PacketField(8, Length = 10)] + private partial string String1 { get; set; } + + [PacketField(9, LengthMember = nameof(AStringByteLength))] + partial string String2 { get; set; } + + [PacketField(10, LengthMember = nameof(ReadWriteByteLength))] + protected partial string String3 { get; set; } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/SimplePropsOffsetMember/TestPacket.Byte1.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/SimplePropsOffsetMember/TestPacket.Byte1.g.cs new file mode 100644 index 0000000..ea17cab --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/SimplePropsOffsetMember/TestPacket.Byte1.g.cs @@ -0,0 +1,19 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +partial class TestPacket +{ + public partial byte Byte1 + { + get + { + return this[this.AnOffset]; + } + set + { + this[this.AnOffset] = value; + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/SimplePropsOffsetMember/TestPacket.Int1.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/SimplePropsOffsetMember/TestPacket.Int1.g.cs new file mode 100644 index 0000000..7a1da33 --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/SimplePropsOffsetMember/TestPacket.Int1.g.cs @@ -0,0 +1,19 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +partial class TestPacket +{ + partial int Int1 + { + get + { + return BinaryPrimitives.ReadInt32LittleEndian(this.AsSpan(this.AnOffset, 4)); + } + set + { + BinaryPrimitives.WriteInt32LittleEndian(this.AsSpan(this.AnOffset, 4), value); + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/SimplePropsOffsetMember/TestPacket.Long1.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/SimplePropsOffsetMember/TestPacket.Long1.g.cs new file mode 100644 index 0000000..733cbc9 --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/SimplePropsOffsetMember/TestPacket.Long1.g.cs @@ -0,0 +1,19 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +partial class TestPacket +{ + public partial long Long1 + { + get + { + return BinaryPrimitives.ReadInt64LittleEndian(this.AsSpan(this.AnOffset, 8)); + } + set + { + BinaryPrimitives.WriteInt64LittleEndian(this.AsSpan(this.AnOffset, 8), value); + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/SimplePropsOffsetMember/TestPacket.SByte1.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/SimplePropsOffsetMember/TestPacket.SByte1.g.cs new file mode 100644 index 0000000..0323082 --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/SimplePropsOffsetMember/TestPacket.SByte1.g.cs @@ -0,0 +1,19 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +partial class TestPacket +{ + internal partial sbyte SByte1 + { + get + { + return (sbyte)this[this.AnOffset]; + } + set + { + this[this.AnOffset] = (byte)value; + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/SimplePropsOffsetMember/TestPacket.Short1.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/SimplePropsOffsetMember/TestPacket.Short1.g.cs new file mode 100644 index 0000000..385fa58 --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/SimplePropsOffsetMember/TestPacket.Short1.g.cs @@ -0,0 +1,19 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +partial class TestPacket +{ + private partial short Short1 + { + get + { + return BinaryPrimitives.ReadInt16LittleEndian(this.AsSpan(this.AnOffset, 2)); + } + set + { + BinaryPrimitives.WriteInt16LittleEndian(this.AsSpan(this.AnOffset, 2), value); + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/SimplePropsOffsetMember/TestPacket.String1.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/SimplePropsOffsetMember/TestPacket.String1.g.cs new file mode 100644 index 0000000..e50e4ee --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/SimplePropsOffsetMember/TestPacket.String1.g.cs @@ -0,0 +1,19 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +partial class TestPacket +{ + private partial string String1 + { + get + { + return Encoding.ASCII.GetString(this.AsSpan(this.AnOffset, 10)); + } + set + { + Encoding.ASCII.GetBytes(value, this.AsSpan(this.AnOffset, 10)); + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/SimplePropsOffsetMember/TestPacket.String2.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/SimplePropsOffsetMember/TestPacket.String2.g.cs new file mode 100644 index 0000000..6f7a4d2 --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/SimplePropsOffsetMember/TestPacket.String2.g.cs @@ -0,0 +1,23 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +partial class TestPacket +{ + partial string String2 + { + get + { + return Encoding.ASCII.GetString(this.AsSpan(this.AnOffset, this.AStringByteLength)); + } + set + { + if (Encoding.ASCII.GetByteCount(value) != this.AStringByteLength) + { + throw new ArgumentOutOfRangeException(nameof(value), Encoding.ASCII.GetByteCount(value), $"byte length of value must be equal to: {this.AStringByteLength}"); + } + Encoding.ASCII.GetBytes(value, this.AsSpan(this.AnOffset, this.AStringByteLength)); + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/SimplePropsOffsetMember/TestPacket.String3.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/SimplePropsOffsetMember/TestPacket.String3.g.cs new file mode 100644 index 0000000..84fef44 --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/SimplePropsOffsetMember/TestPacket.String3.g.cs @@ -0,0 +1,20 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +partial class TestPacket +{ + protected partial string String3 + { + get + { + return Encoding.ASCII.GetString(this.AsSpan(this.AnOffset, this.ReadWriteByteLength)); + } + set + { + this.ReadWriteByteLength = Encoding.ASCII.GetByteCount(value); + Encoding.ASCII.GetBytes(value, this.AsSpan(this.AnOffset, this.ReadWriteByteLength)); + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/SimplePropsOffsetMember/TestPacket.UInt1.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/SimplePropsOffsetMember/TestPacket.UInt1.g.cs new file mode 100644 index 0000000..ff385ce --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/SimplePropsOffsetMember/TestPacket.UInt1.g.cs @@ -0,0 +1,19 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +partial class TestPacket +{ + protected private partial uint UInt1 + { + get + { + return BinaryPrimitives.ReadUInt32LittleEndian(this.AsSpan(this.AnOffset, 4)); + } + set + { + BinaryPrimitives.WriteUInt32LittleEndian(this.AsSpan(this.AnOffset, 4), value); + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/SimplePropsOffsetMember/TestPacket.ULong1.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/SimplePropsOffsetMember/TestPacket.ULong1.g.cs new file mode 100644 index 0000000..2cabde1 --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/SimplePropsOffsetMember/TestPacket.ULong1.g.cs @@ -0,0 +1,19 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +partial class TestPacket +{ + partial ulong ULong1 + { + get + { + return BinaryPrimitives.ReadUInt64LittleEndian(this.AsSpan(this.AnOffset, 8)); + } + set + { + BinaryPrimitives.WriteUInt64LittleEndian(this.AsSpan(this.AnOffset, 8), value); + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/SimplePropsOffsetMember/TestPacket.UShort1.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/SimplePropsOffsetMember/TestPacket.UShort1.g.cs new file mode 100644 index 0000000..53e7093 --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/SimplePropsOffsetMember/TestPacket.UShort1.g.cs @@ -0,0 +1,19 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +partial class TestPacket +{ + protected partial ushort UShort1 + { + get + { + return BinaryPrimitives.ReadUInt16LittleEndian(this.AsSpan(this.AnOffset, 2)); + } + set + { + BinaryPrimitives.WriteUInt16LittleEndian(this.AsSpan(this.AnOffset, 2), value); + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/SimplePropsOffsetMember/TestPacket.cs b/RyuSocks.Generator.Test/data/PacketGenerator/SimplePropsOffsetMember/TestPacket.cs new file mode 100644 index 0000000..92f76aa --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/SimplePropsOffsetMember/TestPacket.cs @@ -0,0 +1,43 @@ +using RyuSocks.Packets; + +internal partial class TestPacket : Packet +{ + private int AnOffset => 2; + + protected int AStringByteLength => 2; + + public int ReadWriteByteLength { get; set; } = 34; + + [PacketField(nameof(AnOffset))] + public partial byte Byte1 { get; set; } + + [PacketField(nameof(AnOffset))] + internal partial sbyte SByte1 { get; set; } + + [PacketField(nameof(AnOffset))] + protected partial ushort UShort1 { get; set; } + + [PacketField(nameof(AnOffset))] + private partial short Short1 { get; set; } + + [PacketField(nameof(AnOffset))] + protected private partial uint UInt1 { get; set; } + + [PacketField(nameof(AnOffset))] + partial int Int1 { get; set; } + + [PacketField(nameof(AnOffset))] + partial ulong ULong1 { get; set; } + + [PacketField(nameof(AnOffset))] + public partial long Long1 { get; set; } + + [PacketField(nameof(AnOffset), Length = 10)] + private partial string String1 { get; set; } + + [PacketField(nameof(AnOffset), LengthMember = nameof(AStringByteLength))] + partial string String2 { get; set; } + + [PacketField(nameof(AnOffset), LengthMember = nameof(ReadWriteByteLength))] + protected partial string String3 { get; set; } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/StringEncoding.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncoding.g.cs new file mode 100644 index 0000000..d0779c3 --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncoding.g.cs @@ -0,0 +1,11 @@ +namespace RyuSocks.Packets +{ + public enum StringEncoding : byte + { + ASCII, + Unicode, + UTF7, + UTF8, + UTF32, + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/BigEndian.ASCIIPacket.String1.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/BigEndian.ASCIIPacket.String1.g.cs new file mode 100644 index 0000000..e323721 --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/BigEndian.ASCIIPacket.String1.g.cs @@ -0,0 +1,22 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +namespace BigEndian +{ + partial class ASCIIPacket + { + private partial string String1 + { + get + { + return Encoding.ASCII.GetString(this.AsSpan(2, 10)); + } + set + { + Encoding.ASCII.GetBytes(value, this.AsSpan(2, 10)); + } + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/BigEndian.ASCIIPacket.String2.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/BigEndian.ASCIIPacket.String2.g.cs new file mode 100644 index 0000000..fed5363 --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/BigEndian.ASCIIPacket.String2.g.cs @@ -0,0 +1,26 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +namespace BigEndian +{ + partial class ASCIIPacket + { + partial string String2 + { + get + { + return Encoding.ASCII.GetString(this.AsSpan(12, this.AStringByteLength)); + } + set + { + if (Encoding.ASCII.GetByteCount(value) != this.AStringByteLength) + { + throw new ArgumentOutOfRangeException(nameof(value), Encoding.ASCII.GetByteCount(value), $"byte length of value must be equal to: {this.AStringByteLength}"); + } + Encoding.ASCII.GetBytes(value, this.AsSpan(12, this.AStringByteLength)); + } + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/BigEndian.ASCIIPacket.String3.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/BigEndian.ASCIIPacket.String3.g.cs new file mode 100644 index 0000000..a4df373 --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/BigEndian.ASCIIPacket.String3.g.cs @@ -0,0 +1,23 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +namespace BigEndian +{ + partial class ASCIIPacket + { + protected partial string String3 + { + get + { + return Encoding.ASCII.GetString(this.AsSpan(14, this.ReadWriteByteLength)); + } + set + { + this.ReadWriteByteLength = Encoding.ASCII.GetByteCount(value); + Encoding.ASCII.GetBytes(value, this.AsSpan(14, this.ReadWriteByteLength)); + } + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/BigEndian.ASCIIPacket.String4.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/BigEndian.ASCIIPacket.String4.g.cs new file mode 100644 index 0000000..e2085a6 --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/BigEndian.ASCIIPacket.String4.g.cs @@ -0,0 +1,30 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +namespace BigEndian +{ + partial class ASCIIPacket + { + public partial string String4 + { + get + { + return Encoding.ASCII.GetString(this.AsSpan(48, 6)); + } + set + { + if (value.Length < 2) + { + throw new ArgumentOutOfRangeException(nameof(value.Length), value.Length, $"{nameof(value.Length)} must be larger or equal to: {2}"); + } + if (value.Length > 6) + { + throw new ArgumentOutOfRangeException(nameof(value.Length), value.Length, $"{nameof(value.Length)} must be smaller or equal to: {6}"); + } + Encoding.ASCII.GetBytes(value, this.AsSpan(48, 6)); + } + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/BigEndian.UTF32Packet.String1.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/BigEndian.UTF32Packet.String1.g.cs new file mode 100644 index 0000000..b78f1a0 --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/BigEndian.UTF32Packet.String1.g.cs @@ -0,0 +1,26 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +namespace BigEndian +{ + partial class UTF32Packet + { + private partial string String1 + { + get + { + byte[] stringArray = this.AsSpan(2, 10).ToArray(); + Array.Reverse(stringArray); + return Encoding.UTF32.GetString(stringArray); + } + set + { + byte[] byteArray = Encoding.UTF32.GetBytes(value); + Array.Reverse(byteArray); + byteArray.CopyTo(this.AsSpan(2, 10)); + } + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/BigEndian.UTF32Packet.String2.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/BigEndian.UTF32Packet.String2.g.cs new file mode 100644 index 0000000..1247f7c --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/BigEndian.UTF32Packet.String2.g.cs @@ -0,0 +1,30 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +namespace BigEndian +{ + partial class UTF32Packet + { + partial string String2 + { + get + { + byte[] stringArray = this.AsSpan(12, this.AStringByteLength).ToArray(); + Array.Reverse(stringArray); + return Encoding.UTF32.GetString(stringArray); + } + set + { + if (Encoding.UTF32.GetByteCount(value) != this.AStringByteLength) + { + throw new ArgumentOutOfRangeException(nameof(value), Encoding.UTF32.GetByteCount(value), $"byte length of value must be equal to: {this.AStringByteLength}"); + } + byte[] byteArray = Encoding.UTF32.GetBytes(value); + Array.Reverse(byteArray); + byteArray.CopyTo(this.AsSpan(12, this.AStringByteLength)); + } + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/BigEndian.UTF32Packet.String3.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/BigEndian.UTF32Packet.String3.g.cs new file mode 100644 index 0000000..d601008 --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/BigEndian.UTF32Packet.String3.g.cs @@ -0,0 +1,27 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +namespace BigEndian +{ + partial class UTF32Packet + { + protected partial string String3 + { + get + { + byte[] stringArray = this.AsSpan(14, this.ReadWriteByteLength).ToArray(); + Array.Reverse(stringArray); + return Encoding.UTF32.GetString(stringArray); + } + set + { + this.ReadWriteByteLength = Encoding.UTF32.GetByteCount(value); + byte[] byteArray = Encoding.UTF32.GetBytes(value); + Array.Reverse(byteArray); + byteArray.CopyTo(this.AsSpan(14, this.ReadWriteByteLength)); + } + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/BigEndian.UTF32Packet.String4.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/BigEndian.UTF32Packet.String4.g.cs new file mode 100644 index 0000000..1e8c6d3 --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/BigEndian.UTF32Packet.String4.g.cs @@ -0,0 +1,34 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +namespace BigEndian +{ + partial class UTF32Packet + { + public partial string String4 + { + get + { + byte[] stringArray = this.AsSpan(48, 6).ToArray(); + Array.Reverse(stringArray); + return Encoding.UTF32.GetString(stringArray); + } + set + { + if (value.Length < 2) + { + throw new ArgumentOutOfRangeException(nameof(value.Length), value.Length, $"{nameof(value.Length)} must be larger or equal to: {2}"); + } + if (value.Length > 6) + { + throw new ArgumentOutOfRangeException(nameof(value.Length), value.Length, $"{nameof(value.Length)} must be smaller or equal to: {6}"); + } + byte[] byteArray = Encoding.UTF32.GetBytes(value); + Array.Reverse(byteArray); + byteArray.CopyTo(this.AsSpan(48, 6)); + } + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/BigEndian.UTF7Packet.String1.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/BigEndian.UTF7Packet.String1.g.cs new file mode 100644 index 0000000..377ad77 --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/BigEndian.UTF7Packet.String1.g.cs @@ -0,0 +1,22 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +namespace BigEndian +{ + partial class UTF7Packet + { + private partial string String1 + { + get + { + return Encoding.UTF7.GetString(this.AsSpan(2, 10)); + } + set + { + Encoding.UTF7.GetBytes(value, this.AsSpan(2, 10)); + } + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/BigEndian.UTF7Packet.String2.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/BigEndian.UTF7Packet.String2.g.cs new file mode 100644 index 0000000..b5647b6 --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/BigEndian.UTF7Packet.String2.g.cs @@ -0,0 +1,26 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +namespace BigEndian +{ + partial class UTF7Packet + { + partial string String2 + { + get + { + return Encoding.UTF7.GetString(this.AsSpan(12, this.AStringByteLength)); + } + set + { + if (Encoding.UTF7.GetByteCount(value) != this.AStringByteLength) + { + throw new ArgumentOutOfRangeException(nameof(value), Encoding.UTF7.GetByteCount(value), $"byte length of value must be equal to: {this.AStringByteLength}"); + } + Encoding.UTF7.GetBytes(value, this.AsSpan(12, this.AStringByteLength)); + } + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/BigEndian.UTF7Packet.String3.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/BigEndian.UTF7Packet.String3.g.cs new file mode 100644 index 0000000..9f91800 --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/BigEndian.UTF7Packet.String3.g.cs @@ -0,0 +1,23 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +namespace BigEndian +{ + partial class UTF7Packet + { + protected partial string String3 + { + get + { + return Encoding.UTF7.GetString(this.AsSpan(14, this.ReadWriteByteLength)); + } + set + { + this.ReadWriteByteLength = Encoding.UTF7.GetByteCount(value); + Encoding.UTF7.GetBytes(value, this.AsSpan(14, this.ReadWriteByteLength)); + } + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/BigEndian.UTF7Packet.String4.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/BigEndian.UTF7Packet.String4.g.cs new file mode 100644 index 0000000..ba839a3 --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/BigEndian.UTF7Packet.String4.g.cs @@ -0,0 +1,30 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +namespace BigEndian +{ + partial class UTF7Packet + { + public partial string String4 + { + get + { + return Encoding.UTF7.GetString(this.AsSpan(48, 6)); + } + set + { + if (value.Length < 2) + { + throw new ArgumentOutOfRangeException(nameof(value.Length), value.Length, $"{nameof(value.Length)} must be larger or equal to: {2}"); + } + if (value.Length > 6) + { + throw new ArgumentOutOfRangeException(nameof(value.Length), value.Length, $"{nameof(value.Length)} must be smaller or equal to: {6}"); + } + Encoding.UTF7.GetBytes(value, this.AsSpan(48, 6)); + } + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/BigEndian.UTF8Packet.String1.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/BigEndian.UTF8Packet.String1.g.cs new file mode 100644 index 0000000..03ce374 --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/BigEndian.UTF8Packet.String1.g.cs @@ -0,0 +1,22 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +namespace BigEndian +{ + partial class UTF8Packet + { + private partial string String1 + { + get + { + return Encoding.UTF8.GetString(this.AsSpan(2, 10)); + } + set + { + Encoding.UTF8.GetBytes(value, this.AsSpan(2, 10)); + } + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/BigEndian.UTF8Packet.String2.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/BigEndian.UTF8Packet.String2.g.cs new file mode 100644 index 0000000..74100ee --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/BigEndian.UTF8Packet.String2.g.cs @@ -0,0 +1,26 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +namespace BigEndian +{ + partial class UTF8Packet + { + partial string String2 + { + get + { + return Encoding.UTF8.GetString(this.AsSpan(12, this.AStringByteLength)); + } + set + { + if (Encoding.UTF8.GetByteCount(value) != this.AStringByteLength) + { + throw new ArgumentOutOfRangeException(nameof(value), Encoding.UTF8.GetByteCount(value), $"byte length of value must be equal to: {this.AStringByteLength}"); + } + Encoding.UTF8.GetBytes(value, this.AsSpan(12, this.AStringByteLength)); + } + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/BigEndian.UTF8Packet.String3.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/BigEndian.UTF8Packet.String3.g.cs new file mode 100644 index 0000000..178c41d --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/BigEndian.UTF8Packet.String3.g.cs @@ -0,0 +1,23 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +namespace BigEndian +{ + partial class UTF8Packet + { + protected partial string String3 + { + get + { + return Encoding.UTF8.GetString(this.AsSpan(14, this.ReadWriteByteLength)); + } + set + { + this.ReadWriteByteLength = Encoding.UTF8.GetByteCount(value); + Encoding.UTF8.GetBytes(value, this.AsSpan(14, this.ReadWriteByteLength)); + } + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/BigEndian.UTF8Packet.String4.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/BigEndian.UTF8Packet.String4.g.cs new file mode 100644 index 0000000..4a85877 --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/BigEndian.UTF8Packet.String4.g.cs @@ -0,0 +1,30 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +namespace BigEndian +{ + partial class UTF8Packet + { + public partial string String4 + { + get + { + return Encoding.UTF8.GetString(this.AsSpan(48, 6)); + } + set + { + if (value.Length < 2) + { + throw new ArgumentOutOfRangeException(nameof(value.Length), value.Length, $"{nameof(value.Length)} must be larger or equal to: {2}"); + } + if (value.Length > 6) + { + throw new ArgumentOutOfRangeException(nameof(value.Length), value.Length, $"{nameof(value.Length)} must be smaller or equal to: {6}"); + } + Encoding.UTF8.GetBytes(value, this.AsSpan(48, 6)); + } + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/BigEndian.UnicodePacket.String1.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/BigEndian.UnicodePacket.String1.g.cs new file mode 100644 index 0000000..7e2d0d5 --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/BigEndian.UnicodePacket.String1.g.cs @@ -0,0 +1,22 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +namespace BigEndian +{ + partial class UnicodePacket + { + private partial string String1 + { + get + { + return Encoding.BigEndianUnicode.GetString(this.AsSpan(2, 10)); + } + set + { + Encoding.BigEndianUnicode.GetBytes(value, this.AsSpan(2, 10)); + } + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/BigEndian.UnicodePacket.String2.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/BigEndian.UnicodePacket.String2.g.cs new file mode 100644 index 0000000..71ed10b --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/BigEndian.UnicodePacket.String2.g.cs @@ -0,0 +1,26 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +namespace BigEndian +{ + partial class UnicodePacket + { + partial string String2 + { + get + { + return Encoding.BigEndianUnicode.GetString(this.AsSpan(12, this.AStringByteLength)); + } + set + { + if (Encoding.Unicode.GetByteCount(value) != this.AStringByteLength) + { + throw new ArgumentOutOfRangeException(nameof(value), Encoding.Unicode.GetByteCount(value), $"byte length of value must be equal to: {this.AStringByteLength}"); + } + Encoding.BigEndianUnicode.GetBytes(value, this.AsSpan(12, this.AStringByteLength)); + } + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/BigEndian.UnicodePacket.String3.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/BigEndian.UnicodePacket.String3.g.cs new file mode 100644 index 0000000..ecbd7c8 --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/BigEndian.UnicodePacket.String3.g.cs @@ -0,0 +1,23 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +namespace BigEndian +{ + partial class UnicodePacket + { + protected partial string String3 + { + get + { + return Encoding.BigEndianUnicode.GetString(this.AsSpan(14, this.ReadWriteByteLength)); + } + set + { + this.ReadWriteByteLength = Encoding.Unicode.GetByteCount(value); + Encoding.BigEndianUnicode.GetBytes(value, this.AsSpan(14, this.ReadWriteByteLength)); + } + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/BigEndian.UnicodePacket.String4.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/BigEndian.UnicodePacket.String4.g.cs new file mode 100644 index 0000000..71b7846 --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/BigEndian.UnicodePacket.String4.g.cs @@ -0,0 +1,30 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +namespace BigEndian +{ + partial class UnicodePacket + { + public partial string String4 + { + get + { + return Encoding.BigEndianUnicode.GetString(this.AsSpan(48, 6)); + } + set + { + if (value.Length < 2) + { + throw new ArgumentOutOfRangeException(nameof(value.Length), value.Length, $"{nameof(value.Length)} must be larger or equal to: {2}"); + } + if (value.Length > 6) + { + throw new ArgumentOutOfRangeException(nameof(value.Length), value.Length, $"{nameof(value.Length)} must be smaller or equal to: {6}"); + } + Encoding.BigEndianUnicode.GetBytes(value, this.AsSpan(48, 6)); + } + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/LittleEndian.ASCIIPacket.String1.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/LittleEndian.ASCIIPacket.String1.g.cs new file mode 100644 index 0000000..a435828 --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/LittleEndian.ASCIIPacket.String1.g.cs @@ -0,0 +1,22 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +namespace LittleEndian +{ + partial class ASCIIPacket + { + private partial string String1 + { + get + { + return Encoding.ASCII.GetString(this.AsSpan(2, 10)); + } + set + { + Encoding.ASCII.GetBytes(value, this.AsSpan(2, 10)); + } + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/LittleEndian.ASCIIPacket.String2.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/LittleEndian.ASCIIPacket.String2.g.cs new file mode 100644 index 0000000..c1cc8e2 --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/LittleEndian.ASCIIPacket.String2.g.cs @@ -0,0 +1,26 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +namespace LittleEndian +{ + partial class ASCIIPacket + { + partial string String2 + { + get + { + return Encoding.ASCII.GetString(this.AsSpan(12, this.AStringByteLength)); + } + set + { + if (Encoding.ASCII.GetByteCount(value) != this.AStringByteLength) + { + throw new ArgumentOutOfRangeException(nameof(value), Encoding.ASCII.GetByteCount(value), $"byte length of value must be equal to: {this.AStringByteLength}"); + } + Encoding.ASCII.GetBytes(value, this.AsSpan(12, this.AStringByteLength)); + } + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/LittleEndian.ASCIIPacket.String3.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/LittleEndian.ASCIIPacket.String3.g.cs new file mode 100644 index 0000000..419b0f8 --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/LittleEndian.ASCIIPacket.String3.g.cs @@ -0,0 +1,23 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +namespace LittleEndian +{ + partial class ASCIIPacket + { + protected partial string String3 + { + get + { + return Encoding.ASCII.GetString(this.AsSpan(14, this.ReadWriteByteLength)); + } + set + { + this.ReadWriteByteLength = Encoding.ASCII.GetByteCount(value); + Encoding.ASCII.GetBytes(value, this.AsSpan(14, this.ReadWriteByteLength)); + } + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/LittleEndian.ASCIIPacket.String4.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/LittleEndian.ASCIIPacket.String4.g.cs new file mode 100644 index 0000000..3580a23 --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/LittleEndian.ASCIIPacket.String4.g.cs @@ -0,0 +1,30 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +namespace LittleEndian +{ + partial class ASCIIPacket + { + public partial string String4 + { + get + { + return Encoding.ASCII.GetString(this.AsSpan(48, 6)); + } + set + { + if (value.Length < 2) + { + throw new ArgumentOutOfRangeException(nameof(value.Length), value.Length, $"{nameof(value.Length)} must be larger or equal to: {2}"); + } + if (value.Length > 6) + { + throw new ArgumentOutOfRangeException(nameof(value.Length), value.Length, $"{nameof(value.Length)} must be smaller or equal to: {6}"); + } + Encoding.ASCII.GetBytes(value, this.AsSpan(48, 6)); + } + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/LittleEndian.UTF32Packet.String1.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/LittleEndian.UTF32Packet.String1.g.cs new file mode 100644 index 0000000..f8fe2b7 --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/LittleEndian.UTF32Packet.String1.g.cs @@ -0,0 +1,22 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +namespace LittleEndian +{ + partial class UTF32Packet + { + private partial string String1 + { + get + { + return Encoding.UTF32.GetString(this.AsSpan(2, 10)); + } + set + { + Encoding.UTF32.GetBytes(value, this.AsSpan(2, 10)); + } + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/LittleEndian.UTF32Packet.String2.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/LittleEndian.UTF32Packet.String2.g.cs new file mode 100644 index 0000000..3d6d753 --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/LittleEndian.UTF32Packet.String2.g.cs @@ -0,0 +1,26 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +namespace LittleEndian +{ + partial class UTF32Packet + { + partial string String2 + { + get + { + return Encoding.UTF32.GetString(this.AsSpan(12, this.AStringByteLength)); + } + set + { + if (Encoding.UTF32.GetByteCount(value) != this.AStringByteLength) + { + throw new ArgumentOutOfRangeException(nameof(value), Encoding.UTF32.GetByteCount(value), $"byte length of value must be equal to: {this.AStringByteLength}"); + } + Encoding.UTF32.GetBytes(value, this.AsSpan(12, this.AStringByteLength)); + } + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/LittleEndian.UTF32Packet.String3.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/LittleEndian.UTF32Packet.String3.g.cs new file mode 100644 index 0000000..721c873 --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/LittleEndian.UTF32Packet.String3.g.cs @@ -0,0 +1,23 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +namespace LittleEndian +{ + partial class UTF32Packet + { + protected partial string String3 + { + get + { + return Encoding.UTF32.GetString(this.AsSpan(14, this.ReadWriteByteLength)); + } + set + { + this.ReadWriteByteLength = Encoding.UTF32.GetByteCount(value); + Encoding.UTF32.GetBytes(value, this.AsSpan(14, this.ReadWriteByteLength)); + } + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/LittleEndian.UTF32Packet.String4.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/LittleEndian.UTF32Packet.String4.g.cs new file mode 100644 index 0000000..adcefd0 --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/LittleEndian.UTF32Packet.String4.g.cs @@ -0,0 +1,30 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +namespace LittleEndian +{ + partial class UTF32Packet + { + public partial string String4 + { + get + { + return Encoding.UTF32.GetString(this.AsSpan(48, 6)); + } + set + { + if (value.Length < 2) + { + throw new ArgumentOutOfRangeException(nameof(value.Length), value.Length, $"{nameof(value.Length)} must be larger or equal to: {2}"); + } + if (value.Length > 6) + { + throw new ArgumentOutOfRangeException(nameof(value.Length), value.Length, $"{nameof(value.Length)} must be smaller or equal to: {6}"); + } + Encoding.UTF32.GetBytes(value, this.AsSpan(48, 6)); + } + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/LittleEndian.UTF7Packet.String1.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/LittleEndian.UTF7Packet.String1.g.cs new file mode 100644 index 0000000..7b13be0 --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/LittleEndian.UTF7Packet.String1.g.cs @@ -0,0 +1,22 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +namespace LittleEndian +{ + partial class UTF7Packet + { + private partial string String1 + { + get + { + return Encoding.UTF7.GetString(this.AsSpan(2, 10)); + } + set + { + Encoding.UTF7.GetBytes(value, this.AsSpan(2, 10)); + } + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/LittleEndian.UTF7Packet.String2.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/LittleEndian.UTF7Packet.String2.g.cs new file mode 100644 index 0000000..cece67b --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/LittleEndian.UTF7Packet.String2.g.cs @@ -0,0 +1,26 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +namespace LittleEndian +{ + partial class UTF7Packet + { + partial string String2 + { + get + { + return Encoding.UTF7.GetString(this.AsSpan(12, this.AStringByteLength)); + } + set + { + if (Encoding.UTF7.GetByteCount(value) != this.AStringByteLength) + { + throw new ArgumentOutOfRangeException(nameof(value), Encoding.UTF7.GetByteCount(value), $"byte length of value must be equal to: {this.AStringByteLength}"); + } + Encoding.UTF7.GetBytes(value, this.AsSpan(12, this.AStringByteLength)); + } + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/LittleEndian.UTF7Packet.String3.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/LittleEndian.UTF7Packet.String3.g.cs new file mode 100644 index 0000000..2dc995b --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/LittleEndian.UTF7Packet.String3.g.cs @@ -0,0 +1,23 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +namespace LittleEndian +{ + partial class UTF7Packet + { + protected partial string String3 + { + get + { + return Encoding.UTF7.GetString(this.AsSpan(14, this.ReadWriteByteLength)); + } + set + { + this.ReadWriteByteLength = Encoding.UTF7.GetByteCount(value); + Encoding.UTF7.GetBytes(value, this.AsSpan(14, this.ReadWriteByteLength)); + } + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/LittleEndian.UTF7Packet.String4.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/LittleEndian.UTF7Packet.String4.g.cs new file mode 100644 index 0000000..331fe27 --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/LittleEndian.UTF7Packet.String4.g.cs @@ -0,0 +1,30 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +namespace LittleEndian +{ + partial class UTF7Packet + { + public partial string String4 + { + get + { + return Encoding.UTF7.GetString(this.AsSpan(48, 6)); + } + set + { + if (value.Length < 2) + { + throw new ArgumentOutOfRangeException(nameof(value.Length), value.Length, $"{nameof(value.Length)} must be larger or equal to: {2}"); + } + if (value.Length > 6) + { + throw new ArgumentOutOfRangeException(nameof(value.Length), value.Length, $"{nameof(value.Length)} must be smaller or equal to: {6}"); + } + Encoding.UTF7.GetBytes(value, this.AsSpan(48, 6)); + } + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/LittleEndian.UTF8Packet.String1.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/LittleEndian.UTF8Packet.String1.g.cs new file mode 100644 index 0000000..a5e94ca --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/LittleEndian.UTF8Packet.String1.g.cs @@ -0,0 +1,22 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +namespace LittleEndian +{ + partial class UTF8Packet + { + private partial string String1 + { + get + { + return Encoding.UTF8.GetString(this.AsSpan(2, 10)); + } + set + { + Encoding.UTF8.GetBytes(value, this.AsSpan(2, 10)); + } + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/LittleEndian.UTF8Packet.String2.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/LittleEndian.UTF8Packet.String2.g.cs new file mode 100644 index 0000000..b7ee39e --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/LittleEndian.UTF8Packet.String2.g.cs @@ -0,0 +1,26 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +namespace LittleEndian +{ + partial class UTF8Packet + { + partial string String2 + { + get + { + return Encoding.UTF8.GetString(this.AsSpan(12, this.AStringByteLength)); + } + set + { + if (Encoding.UTF8.GetByteCount(value) != this.AStringByteLength) + { + throw new ArgumentOutOfRangeException(nameof(value), Encoding.UTF8.GetByteCount(value), $"byte length of value must be equal to: {this.AStringByteLength}"); + } + Encoding.UTF8.GetBytes(value, this.AsSpan(12, this.AStringByteLength)); + } + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/LittleEndian.UTF8Packet.String3.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/LittleEndian.UTF8Packet.String3.g.cs new file mode 100644 index 0000000..eeef44f --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/LittleEndian.UTF8Packet.String3.g.cs @@ -0,0 +1,23 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +namespace LittleEndian +{ + partial class UTF8Packet + { + protected partial string String3 + { + get + { + return Encoding.UTF8.GetString(this.AsSpan(14, this.ReadWriteByteLength)); + } + set + { + this.ReadWriteByteLength = Encoding.UTF8.GetByteCount(value); + Encoding.UTF8.GetBytes(value, this.AsSpan(14, this.ReadWriteByteLength)); + } + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/LittleEndian.UTF8Packet.String4.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/LittleEndian.UTF8Packet.String4.g.cs new file mode 100644 index 0000000..87d82fd --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/LittleEndian.UTF8Packet.String4.g.cs @@ -0,0 +1,30 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +namespace LittleEndian +{ + partial class UTF8Packet + { + public partial string String4 + { + get + { + return Encoding.UTF8.GetString(this.AsSpan(48, 6)); + } + set + { + if (value.Length < 2) + { + throw new ArgumentOutOfRangeException(nameof(value.Length), value.Length, $"{nameof(value.Length)} must be larger or equal to: {2}"); + } + if (value.Length > 6) + { + throw new ArgumentOutOfRangeException(nameof(value.Length), value.Length, $"{nameof(value.Length)} must be smaller or equal to: {6}"); + } + Encoding.UTF8.GetBytes(value, this.AsSpan(48, 6)); + } + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/LittleEndian.UnicodePacket.String1.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/LittleEndian.UnicodePacket.String1.g.cs new file mode 100644 index 0000000..ba79c02 --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/LittleEndian.UnicodePacket.String1.g.cs @@ -0,0 +1,22 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +namespace LittleEndian +{ + partial class UnicodePacket + { + private partial string String1 + { + get + { + return Encoding.Unicode.GetString(this.AsSpan(2, 10)); + } + set + { + Encoding.Unicode.GetBytes(value, this.AsSpan(2, 10)); + } + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/LittleEndian.UnicodePacket.String2.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/LittleEndian.UnicodePacket.String2.g.cs new file mode 100644 index 0000000..fea8e56 --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/LittleEndian.UnicodePacket.String2.g.cs @@ -0,0 +1,26 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +namespace LittleEndian +{ + partial class UnicodePacket + { + partial string String2 + { + get + { + return Encoding.Unicode.GetString(this.AsSpan(12, this.AStringByteLength)); + } + set + { + if (Encoding.Unicode.GetByteCount(value) != this.AStringByteLength) + { + throw new ArgumentOutOfRangeException(nameof(value), Encoding.Unicode.GetByteCount(value), $"byte length of value must be equal to: {this.AStringByteLength}"); + } + Encoding.Unicode.GetBytes(value, this.AsSpan(12, this.AStringByteLength)); + } + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/LittleEndian.UnicodePacket.String3.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/LittleEndian.UnicodePacket.String3.g.cs new file mode 100644 index 0000000..103e358 --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/LittleEndian.UnicodePacket.String3.g.cs @@ -0,0 +1,23 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +namespace LittleEndian +{ + partial class UnicodePacket + { + protected partial string String3 + { + get + { + return Encoding.Unicode.GetString(this.AsSpan(14, this.ReadWriteByteLength)); + } + set + { + this.ReadWriteByteLength = Encoding.Unicode.GetByteCount(value); + Encoding.Unicode.GetBytes(value, this.AsSpan(14, this.ReadWriteByteLength)); + } + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/LittleEndian.UnicodePacket.String4.g.cs b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/LittleEndian.UnicodePacket.String4.g.cs new file mode 100644 index 0000000..5d7ee9a --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/LittleEndian.UnicodePacket.String4.g.cs @@ -0,0 +1,30 @@ +using RyuSocks.Packets; +using System; +using System.Buffers.Binary; +using System.Text; + +namespace LittleEndian +{ + partial class UnicodePacket + { + public partial string String4 + { + get + { + return Encoding.Unicode.GetString(this.AsSpan(48, 6)); + } + set + { + if (value.Length < 2) + { + throw new ArgumentOutOfRangeException(nameof(value.Length), value.Length, $"{nameof(value.Length)} must be larger or equal to: {2}"); + } + if (value.Length > 6) + { + throw new ArgumentOutOfRangeException(nameof(value.Length), value.Length, $"{nameof(value.Length)} must be smaller or equal to: {6}"); + } + Encoding.Unicode.GetBytes(value, this.AsSpan(48, 6)); + } + } + } +} diff --git a/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/TestPacket.cs b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/TestPacket.cs new file mode 100644 index 0000000..d8a04a8 --- /dev/null +++ b/RyuSocks.Generator.Test/data/PacketGenerator/StringEncodings/TestPacket.cs @@ -0,0 +1,198 @@ +using RyuSocks.Packets; + +namespace LittleEndian +{ + public partial class ASCIIPacket : Packet + { + protected int AStringByteLength => 2; + + public int ReadWriteByteLength { get; set; } = 34; + + [PacketField(2, Length = 10)] + private partial string String1 { get; set; } + + [PacketField(12, LengthMember = nameof(AStringByteLength))] + partial string String2 { get; set; } + + [PacketField(14, LengthMember = nameof(ReadWriteByteLength))] + protected partial string String3 { get; set; } + + [PacketField(48, Length = 6, MinLength = 2, MaxLength = 6)] + public partial string String4 { get; set; } + } + + public partial class UnicodePacket : Packet + { + protected int AStringByteLength => 2; + + public int ReadWriteByteLength { get; set; } = 34; + + [PacketField(2, Length = 10, StringEncoding = StringEncoding.Unicode)] + private partial string String1 { get; set; } + + [PacketField(12, LengthMember = nameof(AStringByteLength), StringEncoding = StringEncoding.Unicode)] + partial string String2 { get; set; } + + [PacketField(14, LengthMember = nameof(ReadWriteByteLength), StringEncoding = StringEncoding.Unicode)] + protected partial string String3 { get; set; } + + [PacketField(48, Length = 6, MinLength = 2, MaxLength = 6, StringEncoding = StringEncoding.Unicode)] + public partial string String4 { get; set; } + } + + public partial class UTF7Packet : Packet + { + protected int AStringByteLength => 2; + + public int ReadWriteByteLength { get; set; } = 34; + + [PacketField(2, Length = 10, StringEncoding = StringEncoding.UTF7)] + private partial string String1 { get; set; } + + [PacketField(12, LengthMember = nameof(AStringByteLength), StringEncoding = StringEncoding.UTF7)] + partial string String2 { get; set; } + + [PacketField(14, LengthMember = nameof(ReadWriteByteLength), StringEncoding = StringEncoding.UTF7)] + protected partial string String3 { get; set; } + + [PacketField(48, Length = 6, MinLength = 2, MaxLength = 6, StringEncoding = StringEncoding.UTF7)] + public partial string String4 { get; set; } + } + + public partial class UTF8Packet : Packet + { + protected int AStringByteLength => 2; + + public int ReadWriteByteLength { get; set; } = 34; + + [PacketField(2, Length = 10, StringEncoding = StringEncoding.UTF8)] + private partial string String1 { get; set; } + + [PacketField(12, LengthMember = nameof(AStringByteLength), StringEncoding = StringEncoding.UTF8)] + partial string String2 { get; set; } + + [PacketField(14, LengthMember = nameof(ReadWriteByteLength), StringEncoding = StringEncoding.UTF8)] + protected partial string String3 { get; set; } + + [PacketField(48, Length = 6, MinLength = 2, MaxLength = 6, StringEncoding = StringEncoding.UTF8)] + public partial string String4 { get; set; } + } + + public partial class UTF32Packet : Packet + { + protected int AStringByteLength => 2; + + public int ReadWriteByteLength { get; set; } = 34; + + [PacketField(2, Length = 10, StringEncoding = StringEncoding.UTF32)] + private partial string String1 { get; set; } + + [PacketField(12, LengthMember = nameof(AStringByteLength), StringEncoding = StringEncoding.UTF32)] + partial string String2 { get; set; } + + [PacketField(14, LengthMember = nameof(ReadWriteByteLength), StringEncoding = StringEncoding.UTF32)] + protected partial string String3 { get; set; } + + [PacketField(48, Length = 6, MinLength = 2, MaxLength = 6, StringEncoding = StringEncoding.UTF32)] + public partial string String4 { get; set; } + } +} + +namespace BigEndian +{ + public partial class ASCIIPacket : Packet + { + protected int AStringByteLength => 2; + + public int ReadWriteByteLength { get; set; } = 34; + + [PacketField(2, Length = 10, IsBigEndian = true)] + private partial string String1 { get; set; } + + [PacketField(12, LengthMember = nameof(AStringByteLength), IsBigEndian = true)] + partial string String2 { get; set; } + + [PacketField(14, LengthMember = nameof(ReadWriteByteLength), IsBigEndian = true)] + protected partial string String3 { get; set; } + + [PacketField(48, Length = 6, MinLength = 2, MaxLength = 6, IsBigEndian = true)] + public partial string String4 { get; set; } + } + + public partial class UnicodePacket : Packet + { + protected int AStringByteLength => 2; + + public int ReadWriteByteLength { get; set; } = 34; + + [PacketField(2, Length = 10, StringEncoding = StringEncoding.Unicode, IsBigEndian = true)] + private partial string String1 { get; set; } + + [PacketField(12, LengthMember = nameof(AStringByteLength), StringEncoding = StringEncoding.Unicode, IsBigEndian = true)] + partial string String2 { get; set; } + + [PacketField(14, LengthMember = nameof(ReadWriteByteLength), StringEncoding = StringEncoding.Unicode, IsBigEndian = true)] + protected partial string String3 { get; set; } + + [PacketField(48, Length = 6, MinLength = 2, MaxLength = 6, StringEncoding = StringEncoding.Unicode, IsBigEndian = true)] + public partial string String4 { get; set; } + } + + public partial class UTF7Packet : Packet + { + protected int AStringByteLength => 2; + + public int ReadWriteByteLength { get; set; } = 34; + + [PacketField(2, Length = 10, StringEncoding = StringEncoding.UTF7, IsBigEndian = true)] + private partial string String1 { get; set; } + + [PacketField(12, LengthMember = nameof(AStringByteLength), StringEncoding = StringEncoding.UTF7, IsBigEndian = true)] + partial string String2 { get; set; } + + [PacketField(14, LengthMember = nameof(ReadWriteByteLength), StringEncoding = StringEncoding.UTF7, IsBigEndian = true)] + protected partial string String3 { get; set; } + + [PacketField(48, Length = 6, MinLength = 2, MaxLength = 6, StringEncoding = StringEncoding.UTF7, IsBigEndian = true)] + public partial string String4 { get; set; } + } + + public partial class UTF8Packet : Packet + { + protected int AStringByteLength => 2; + + public int ReadWriteByteLength { get; set; } = 34; + + [PacketField(2, Length = 10, StringEncoding = StringEncoding.UTF8, IsBigEndian = true)] + private partial string String1 { get; set; } + + [PacketField(12, LengthMember = nameof(AStringByteLength), StringEncoding = StringEncoding.UTF8, IsBigEndian = true)] + partial string String2 { get; set; } + + [PacketField(14, LengthMember = nameof(ReadWriteByteLength), StringEncoding = StringEncoding.UTF8, IsBigEndian = true)] + protected partial string String3 { get; set; } + + [PacketField(48, Length = 6, MinLength = 2, MaxLength = 6, StringEncoding = StringEncoding.UTF8, IsBigEndian = true)] + public partial string String4 { get; set; } + } + + public partial class UTF32Packet : Packet + { + protected int AStringByteLength => 2; + + public int ReadWriteByteLength { get; set; } = 34; + + [PacketField(2, Length = 10, StringEncoding = StringEncoding.UTF32, IsBigEndian = true)] + private partial string String1 { get; set; } + + [PacketField(12, LengthMember = nameof(AStringByteLength), StringEncoding = StringEncoding.UTF32, IsBigEndian = true)] + partial string String2 { get; set; } + + [PacketField(14, LengthMember = nameof(ReadWriteByteLength), StringEncoding = StringEncoding.UTF32, IsBigEndian = true)] + protected partial string String3 { get; set; } + + [PacketField(48, Length = 6, MinLength = 2, MaxLength = 6, StringEncoding = StringEncoding.UTF32, IsBigEndian = true)] + public partial string String4 { get; set; } + } +} + diff --git a/RyuSocks.Generator/Builder/AbstractBuilder.cs b/RyuSocks.Generator/Builder/AbstractBuilder.cs index 6c44ce2..bbe3638 100644 --- a/RyuSocks.Generator/Builder/AbstractBuilder.cs +++ b/RyuSocks.Generator/Builder/AbstractBuilder.cs @@ -1,5 +1,8 @@ +using System.Diagnostics.CodeAnalysis; + namespace RyuSocks.Generator.Builder { + [ExcludeFromCodeCoverage] abstract class AbstractBuilder { protected const int IndentLength = 4; diff --git a/RyuSocks.Generator/Builder/BlockBuilder.cs b/RyuSocks.Generator/Builder/BlockBuilder.cs index 8b3d144..4577e43 100644 --- a/RyuSocks.Generator/Builder/BlockBuilder.cs +++ b/RyuSocks.Generator/Builder/BlockBuilder.cs @@ -1,7 +1,9 @@ using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; namespace RyuSocks.Generator.Builder { + [ExcludeFromCodeCoverage] class BlockBuilder : AbstractBuilder { private readonly List _block = []; diff --git a/RyuSocks.Generator/Builder/CodeBuilder.cs b/RyuSocks.Generator/Builder/CodeBuilder.cs index 7186ca8..2db7438 100644 --- a/RyuSocks.Generator/Builder/CodeBuilder.cs +++ b/RyuSocks.Generator/Builder/CodeBuilder.cs @@ -1,8 +1,10 @@ +using System.Diagnostics.CodeAnalysis; using System.Text; namespace RyuSocks.Generator.Builder { // Original source: https://github.com/Ryujinx/Ryujinx/blob/1df6c07f78c4c3b8c7fc679d7466f79a10c2d496/src/Ryujinx.Horizon.Generators/CodeGenerator.cs + [ExcludeFromCodeCoverage] class CodeBuilder : AbstractBuilder { private readonly StringBuilder _sb = new(); diff --git a/RyuSocks.Generator/Packet/AccessorGenerator.Array.cs b/RyuSocks.Generator/Packet/AccessorGenerator.Array.cs new file mode 100644 index 0000000..3896b5a --- /dev/null +++ b/RyuSocks.Generator/Packet/AccessorGenerator.Array.cs @@ -0,0 +1,181 @@ +/* + * Copyright (C) RyuSOCKS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using RyuSocks.Generator.Builder; +using System; +using System.Diagnostics.CodeAnalysis; + +namespace RyuSocks.Generator.Packet +{ + [SuppressMessage("ReSharper", "RedundantIfElseBlock")] + internal static partial class AccessorGenerator + { + private static string GenerateArrayByteAccessor(PacketFieldModel packetField, bool isGetter) + { + if (isGetter) + { + string fieldTypeName = packetField.FieldType.Name.Extract(0, -2); + string maybeCast = packetField.FieldType.IsEnum ? $"({fieldTypeName})" : string.Empty; + + return $"result[i] = {maybeCast}this[{packetField.GetOffset()} + i];"; + } + else + { + string maybeCastValue = packetField.FieldType.IsEnum ? "(byte)" : string.Empty; + + return $"this[{packetField.GetOffset()} + i] = {maybeCastValue}value[i];"; + } + } + + private static string GenerateArraySByteAccessor(PacketFieldModel packetField, bool isGetter) + { + if (isGetter) + { + string fieldTypeName = packetField.FieldType.Name.Extract(0, -2); + + return $"result[i] = ({fieldTypeName})this[{packetField.GetOffset()} + i];"; + } + else + { + return $"this[{packetField.GetOffset()} + i] = (byte)value[i];"; + } + } + + private static string GenerateArrayIntegralAccessor(PacketFieldModel packetField, TypeConverterModel converter, bool isGetter) + { + if (isGetter) + { + string fieldTypeName = packetField.FieldType.Name.Extract(0, -2); + string maybeCast = packetField.FieldType.IsEnum ? $"({fieldTypeName})" : string.Empty; + + return $"result[i] = {maybeCast}{converter.GetReaderName(packetField.IsBigEndian)}(this.AsSpan({packetField.GetOffset()} + (i * {converter.Length}), {converter.Length}));"; + } + else + { + string valueParameter = packetField.FieldType.IsEnum ? $"({packetField.FieldType.ActualType.ToTypeString()})value" : "value"; + + return $"{converter.GetWriterName(packetField.IsBigEndian)}(this.AsSpan({packetField.GetOffset()} + (i * {converter.Length}), {converter.Length}), {valueParameter}[i]);"; + } + } + + private static string[] GenerateSimpleArrayAccessor(PacketFieldModel packetField, bool isGetter) + { + BlockBuilder source = new(); + + if (isGetter) + { + string fieldTypeName = packetField.FieldType.Name.Extract(0, -2); + + source.AppendLine($"{fieldTypeName}[] result = new {fieldTypeName}[{packetField.GetLength()}];"); + source.AppendLine(); + } + + source.EnterScope($"for (int i = 0; i < {packetField.GetLength()}; i++)"); + + switch (packetField.FieldType.ActualType) + { + case ActualType.Byte: + source.AppendLine(GenerateArrayByteAccessor(packetField, isGetter)); + break; + case ActualType.SByte: + source.AppendLine(GenerateArraySByteAccessor(packetField, isGetter)); + break; + case ActualType.Int16: + case ActualType.UInt16: + case ActualType.Int32: + case ActualType.UInt32: + case ActualType.Int64: + case ActualType.UInt64: + var converter = TypeConverter.Map[packetField.FieldType.ActualType]; + source.AppendLine(GenerateArrayIntegralAccessor(packetField, converter, isGetter)); + break; + case ActualType.NamedType: + // Only deal with strings here + // TODO: Deal with strings + // return GenerateArrayStringAccessor(packetField, isGetter); + default: + throw new InvalidOperationException($"Unable to generate array accessor for type: {packetField.FieldType.Name}({packetField.FieldType.ActualType})"); + } + + source.LeaveScope(); + + if (isGetter) + { + source.AppendLine(); + source.AppendLine("return result;"); + } + + return source.GetLines(); + } + + private static string[] GenerateArrayAccessor(PacketFieldModel packetField, bool isGetter) + { + BlockBuilder source = new(); + + if (!isGetter) + { + if (packetField.MinLength > 0) + { + ExceptionHelper.ArgumentOutOfRange.GenerateThrowIfLessThan(source, "value.Length", packetField.MinLength.ToString()); + } + if (packetField.MaxLength > 0) + { + ExceptionHelper.ArgumentOutOfRange.GenerateThrowIfGreaterThan(source, "value.Length", packetField.MaxLength.ToString()); + } + } + + AddVerificationMethodIfNecessary(source, packetField, isGetter); + + if (!isGetter) + { + if (packetField is { Length: <= 0, LengthMemberPermissions: Permissions.ReadWrite }) + { + string maybeLengthMemberCast = packetField.LengthMemberType != ActualType.Int32 ? $"({packetField.LengthMemberType.ToTypeString()})" : string.Empty; + source.AppendLine($"this.{packetField.LengthMember} = {maybeLengthMemberCast}value.Length;"); + } + else if (packetField is { Length: <= 0, LengthMemberPermissions: Permissions.ReadOnly }) + { + ExceptionHelper.ArgumentOutOfRange.GenerateThrowIfNotEqual(source, "value.Length", $"this.{packetField.LengthMember}"); + } + + source.AppendLine(); + } + + if (packetField.FieldType.IsStruct) + { + // TODO: Deal with struct arrays + source.AppendLine("// TODO: struct arrays"); + } + // Deal with simple types: enums, strings and integral numeric types + else if (packetField.FieldType is { ActualType: ActualType.NamedType, Name: "string" } + || packetField.FieldType.ActualType != ActualType.NamedType) + { + source.AppendBlock(GenerateSimpleArrayAccessor(packetField, isGetter)); + } + else if (packetField.FieldType.ActualType == ActualType.NamedType) + { + // TODO: Deal with class arrays + source.AppendLine("// TODO: class arrays"); + } + else + { + throw new InvalidOperationException($"Unknown property type. Unable to generate array accessor for: {packetField.FieldType.Name}({packetField.FieldType.ActualType})"); + } + + return source.GetLines(); + } + } +} diff --git a/RyuSocks.Generator/Packet/AccessorGenerator.Class.cs b/RyuSocks.Generator/Packet/AccessorGenerator.Class.cs new file mode 100644 index 0000000..ae61b08 --- /dev/null +++ b/RyuSocks.Generator/Packet/AccessorGenerator.Class.cs @@ -0,0 +1,58 @@ +/* + * Copyright (C) RyuSOCKS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using RyuSocks.Generator.Builder; +using System; +using System.Diagnostics.CodeAnalysis; + +namespace RyuSocks.Generator.Packet +{ + [SuppressMessage("ReSharper", "RedundantIfElseBlock")] + internal static partial class AccessorGenerator + { + private static string[] GenerateIPAddressAccessor(PacketFieldModel packetField, bool isGetter) + { + BlockBuilder source = new(); + + AddVerificationMethodIfNecessary(source, packetField, isGetter); + + if (isGetter) + { + source.AppendLine($"return new System.Net.IPAddress(this.AsSpan({packetField.GetOffset()}, {packetField.GetLength()}));"); + } + else + { + source.AppendLine($"value.GetAddressBytes().CopyTo(this.AsSpan({packetField.GetOffset()}, {packetField.GetLength()}));"); + } + + return source.GetLines(); + } + + private static string[] GenerateClassAccessor(PacketFieldModel packetField, bool isGetter) + { + switch (packetField.FieldType.Name) + { + case "System.Net.IPAddress": + return GenerateIPAddressAccessor(packetField, isGetter); + default: + // TODO: Deal with classes without relying on special handling + // - getter: Require a specific constructor? + // - setter: Require a specific method/property to get access to a Span or byte[]? + throw new InvalidOperationException($"Unknown property class type. Unable to generate accessor for: {packetField.FieldType.Name}({packetField.FieldType.ActualType})"); + } + } + } +} diff --git a/RyuSocks.Generator/Packet/AccessorGenerator.Simple.cs b/RyuSocks.Generator/Packet/AccessorGenerator.Simple.cs new file mode 100644 index 0000000..b96bc54 --- /dev/null +++ b/RyuSocks.Generator/Packet/AccessorGenerator.Simple.cs @@ -0,0 +1,153 @@ +/* + * Copyright (C) RyuSOCKS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using RyuSocks.Generator.Builder; +using System; +using System.Diagnostics.CodeAnalysis; + +namespace RyuSocks.Generator.Packet +{ + [SuppressMessage("ReSharper", "RedundantIfElseBlock")] + internal static partial class AccessorGenerator + { + private static string GenerateSimpleByteAccessor(PacketFieldModel packetField, bool isGetter) + { + if (isGetter) + { + string maybeCast = packetField.FieldType.IsEnum ? $"({packetField.FieldType.Name})" : string.Empty; + + return $"return {maybeCast}this[{packetField.GetOffset()}];"; + } + else + { + string maybeCastValue = packetField.FieldType.IsEnum ? "(byte)" : string.Empty; + + return $"this[{packetField.GetOffset()}] = {maybeCastValue}value;"; + } + } + + private static string GenerateSimpleSByteAccessor(PacketFieldModel packetField, bool isGetter) + { + if (isGetter) + { + string maybeCast = packetField.FieldType.IsEnum ? $"({packetField.FieldType.Name})" : string.Empty; + + return $"return {maybeCast}(sbyte)this[{packetField.GetOffset()}];"; + } + else + { + return $"this[{packetField.GetOffset()}] = (byte)value;"; + } + } + + private static string GenerateSimpleIntegralAccessor(PacketFieldModel packetField, TypeConverterModel converter, bool isGetter) + { + if (isGetter) + { + string maybeCast = packetField.FieldType.IsEnum ? $"({packetField.FieldType.Name})" : string.Empty; + + return $"return {maybeCast}{converter.GetReaderName(packetField.IsBigEndian)}(this.AsSpan({packetField.GetOffset()}, {converter.Length}));"; + } + else + { + string valueParameter = packetField.FieldType.IsEnum ? $"({packetField.FieldType.ActualType.ToTypeString()})value" : "value"; + + return $"{converter.GetWriterName(packetField.IsBigEndian)}(this.AsSpan({packetField.GetOffset()}, {converter.Length}), {valueParameter});"; + } + } + + private static string[] GenerateSimpleStringAccessor(PacketFieldModel packetField, bool isGetter) + { + BlockBuilder source = new(); + + if (isGetter) + { + AddVerificationMethodIfNecessary(source, packetField, true); + + source.AppendBlock(packetField.FieldStringEncoding.GetStringText($"this.AsSpan({packetField.GetOffset()}, {packetField.GetLength()})", packetField.IsBigEndian)); + } + else + { + if (packetField.MinLength > 0) + { + ExceptionHelper.ArgumentOutOfRange.GenerateThrowIfLessThan(source, "value.Length", packetField.MinLength.ToString()); + } + if (packetField.MaxLength > 0) + { + ExceptionHelper.ArgumentOutOfRange.GenerateThrowIfGreaterThan(source, "value.Length", packetField.MaxLength.ToString()); + } + + AddVerificationMethodIfNecessary(source, packetField, false); + + if (packetField is { Length: <= 0, LengthMemberPermissions: Permissions.ReadWrite }) + { + string maybeLengthMemberCast = packetField.LengthMemberType != ActualType.Int32 ? $"({packetField.LengthMemberType.ToTypeString()})" : string.Empty; + + source.AppendLine($"this.{packetField.LengthMember} = {maybeLengthMemberCast}{packetField.FieldStringEncoding.GetByteCountText("value")};"); + source.AppendBlock(packetField.FieldStringEncoding.GetBytesText("value", $"this.AsSpan({packetField.GetOffset()}, this.{packetField.LengthMember})", packetField.IsBigEndian)); + } + else + { + if (packetField is { Length: <= 0, LengthMemberPermissions: Permissions.ReadOnly }) + { + ExceptionHelper.ArgumentOutOfRange.GenerateThrowIfNotEqual(source, packetField.FieldStringEncoding.GetByteCountText("value"), packetField.GetLength(), "value", "byte length of value"); + } + + source.AppendBlock(packetField.FieldStringEncoding.GetBytesText("value", $"this.AsSpan({packetField.GetOffset()}, {packetField.GetLength()})", packetField.IsBigEndian)); + } + } + + return source.GetLines(); + } + + private static string[] GenerateSimpleAccessor(PacketFieldModel packetField, bool isGetter) + { + BlockBuilder source = new(); + + if (packetField.FieldType.ActualType != ActualType.NamedType) + { + AddVerificationMethodIfNecessary(source, packetField, isGetter); + } + + switch (packetField.FieldType.ActualType) + { + case ActualType.Byte: + source.AppendLine(GenerateSimpleByteAccessor(packetField, isGetter)); + break; + case ActualType.SByte: + source.AppendLine(GenerateSimpleSByteAccessor(packetField, isGetter)); + break; + case ActualType.Int16: + case ActualType.UInt16: + case ActualType.Int32: + case ActualType.UInt32: + case ActualType.Int64: + case ActualType.UInt64: + var converter = TypeConverter.Map[packetField.FieldType.ActualType]; + source.AppendLine(GenerateSimpleIntegralAccessor(packetField, converter, isGetter)); + break; + case ActualType.NamedType: + // Only deal with strings here + source.AppendBlock(GenerateSimpleStringAccessor(packetField, isGetter)); + break; + default: + throw new InvalidOperationException($"Unable to generate simple accessor for type: {packetField.FieldType.Name}({packetField.FieldType.ActualType})"); + } + + return source.GetLines(); + } + } +} diff --git a/RyuSocks.Generator/Packet/AccessorGenerator.cs b/RyuSocks.Generator/Packet/AccessorGenerator.cs new file mode 100644 index 0000000..75149ff --- /dev/null +++ b/RyuSocks.Generator/Packet/AccessorGenerator.cs @@ -0,0 +1,73 @@ +/* + * Copyright (C) RyuSOCKS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using RyuSocks.Generator.Builder; +using System; + +namespace RyuSocks.Generator.Packet +{ + internal static partial class AccessorGenerator + { + private static void AddVerificationMethodIfNecessary(BlockBuilder source, PacketFieldModel packetField, bool isGetter) + { + if (packetField.ValidationMethodName.Length > 0) + { + string maybeValueParam = isGetter ? string.Empty : "value"; + + source.AppendLine($"this.{packetField.ValidationMethodName}({maybeValueParam});"); + } + } + + private static string[] Generate(PacketFieldModel packetField, bool isGetter) + { + BlockBuilder source = new(); + source.EnterScope(isGetter ? $"{packetField.PropertyGetterModifiers}get" : $"{packetField.PropertySetterModifiers}set"); + + if (packetField.FieldType.IsArray) + { + // TODO: Deal with arrays + // Arrays could contain structs, enums or classes + source.AppendBlock(GenerateArrayAccessor(packetField, isGetter)); + } + else if (packetField.FieldType.IsStruct) + { + // TODO: Deal with structs + source.AppendLine("// TODO: structs"); + } + // Deal with simple types: enums, strings and integral numeric types + else if (packetField.FieldType is { ActualType: ActualType.NamedType, Name: "string" } + || packetField.FieldType.ActualType != ActualType.NamedType) + { + source.AppendBlock(GenerateSimpleAccessor(packetField, isGetter)); + } + else if (packetField.FieldType.ActualType == ActualType.NamedType) + { + source.AppendBlock(GenerateClassAccessor(packetField, isGetter)); + } + else + { + throw new InvalidOperationException($"Unknown property type. Unable to generate accessor for: {packetField.FieldType.Name}({packetField.FieldType.ActualType})"); + } + + source.LeaveScope(); + return source.GetLines(); + } + + public static string[] GenerateGetter(PacketFieldModel packetField) => Generate(packetField, true); + + public static string[] GenerateSetter(PacketFieldModel packetField) => Generate(packetField, false); + } +} diff --git a/RyuSocks.Generator/Packet/ActualType.cs b/RyuSocks.Generator/Packet/ActualType.cs new file mode 100644 index 0000000..0ab71c4 --- /dev/null +++ b/RyuSocks.Generator/Packet/ActualType.cs @@ -0,0 +1,32 @@ +/* + * Copyright (C) RyuSOCKS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +namespace RyuSocks.Generator.Packet +{ + internal enum ActualType + { + // class or struct + NamedType, + SByte, + Byte, + Int16, + UInt16, + Int32, + UInt32, + Int64, + UInt64, + } +} diff --git a/RyuSocks.Generator/Packet/ActualTypeExtensions.cs b/RyuSocks.Generator/Packet/ActualTypeExtensions.cs new file mode 100644 index 0000000..0d08be1 --- /dev/null +++ b/RyuSocks.Generator/Packet/ActualTypeExtensions.cs @@ -0,0 +1,55 @@ +/* + * Copyright (C) RyuSOCKS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; + +namespace RyuSocks.Generator.Packet +{ + internal static class ActualTypeExtensions + { + public static string ToTypeString(this ActualType actualType) + { + return actualType switch + { + ActualType.SByte => "sbyte", + ActualType.Byte => "byte", + ActualType.Int16 => "short", + ActualType.UInt16 => "ushort", + ActualType.Int32 => "int", + ActualType.UInt32 => "uint", + ActualType.Int64 => "long", + ActualType.UInt64 => "ulong", + _ => throw new InvalidOperationException($"Couldn't get type string from {nameof(ActualType)}: {actualType}"), + }; + } + + public static ActualType ToActualType(this string typeString) + { + return typeString.Trim().ToLowerInvariant() switch + { + "sbyte" => ActualType.SByte, + "byte" => ActualType.Byte, + "short" => ActualType.Int16, + "ushort" => ActualType.UInt16, + "int" => ActualType.Int32, + "uint" => ActualType.UInt32, + "long" => ActualType.Int64, + "ulong" => ActualType.UInt64, + _ => (ActualType)Enum.Parse(typeof(ActualType), typeString, true), + }; + } + } +} diff --git a/RyuSocks.Generator/Packet/ExceptionHelper.cs b/RyuSocks.Generator/Packet/ExceptionHelper.cs new file mode 100644 index 0000000..8a98c8e --- /dev/null +++ b/RyuSocks.Generator/Packet/ExceptionHelper.cs @@ -0,0 +1,54 @@ +/* + * Copyright (C) RyuSOCKS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using RyuSocks.Generator.Builder; +using System.Runtime.CompilerServices; + +namespace RyuSocks.Generator.Packet +{ + // NOTE: Required for compatability with .NET Standard and .NET < 8 + internal static class ExceptionHelper + { + public static class ArgumentOutOfRange + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void GenerateException(BlockBuilder source, string value, string rationalOperator, string other, string operatorMessageText, string overrideValueName, string overrideValueText) + { + string nameofText = overrideValueName.Length > 0 ? overrideValueName : value; + string valueText = overrideValueText.Length > 0 ? overrideValueText : $"{{nameof({value})}}"; + string message = $"$\"{valueText} must be {operatorMessageText} to: {{{other}}}\""; + source.EnterScope($"if ({value} {rationalOperator} {other})"); + source.AppendLine($"throw new ArgumentOutOfRangeException(nameof({nameofText}), {value}, {message});"); + source.LeaveScope(); + } + + public static void GenerateThrowIfNotEqual(BlockBuilder source, string value, string other, string overrideValueName = "", string overrideValueText = "") + { + GenerateException(source, value, "!=", other, "equal", overrideValueName, overrideValueText); + } + + public static void GenerateThrowIfLessThan(BlockBuilder source, string value, string other, string overrideValueName = "", string overrideValueText = "") + { + GenerateException(source, value, "<", other, "larger or equal", overrideValueName, overrideValueText); + } + + public static void GenerateThrowIfGreaterThan(BlockBuilder source, string value, string other, string overrideValueName = "", string overrideValueText = "") + { + GenerateException(source, value, ">", other, "smaller or equal", overrideValueName, overrideValueText); + } + } + } +} diff --git a/RyuSocks.Generator/Packet/ModelExtensions.cs b/RyuSocks.Generator/Packet/ModelExtensions.cs new file mode 100644 index 0000000..4759e8a --- /dev/null +++ b/RyuSocks.Generator/Packet/ModelExtensions.cs @@ -0,0 +1,53 @@ +/* + * Copyright (C) RyuSOCKS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; + +namespace RyuSocks.Generator.Packet +{ + internal static class ModelExtensions + { + public static string GetOffset(this PacketFieldModel model) + { + if (model.Offset > -1) + { + return model.Offset.ToString(); + } + + if (model.OffsetMember.Length == 0) + { + throw new InvalidOperationException($"No {nameof(model.OffsetMember)} found."); + } + + return $"this.{model.OffsetMember}"; + } + + public static string GetLength(this PacketFieldModel model) + { + if (model.Length > -1) + { + return model.Length.ToString(); + } + + if (model.LengthMember.Length == 0) + { + throw new InvalidOperationException($"No {nameof(model.LengthMember)} found."); + } + + return $"this.{model.LengthMember}"; + } + } +} diff --git a/RyuSocks.Generator/Packet/Models.cs b/RyuSocks.Generator/Packet/Models.cs new file mode 100644 index 0000000..dc565d7 --- /dev/null +++ b/RyuSocks.Generator/Packet/Models.cs @@ -0,0 +1,47 @@ +/* + * Copyright (C) RyuSOCKS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +namespace RyuSocks.Generator.Packet +{ + internal record struct FieldTypeModel( + string Name, + bool IsArray, + bool IsEnum, + bool IsStruct, + ActualType ActualType + ); + + internal record struct PacketFieldModel( + int Offset, + string OffsetMember, + int Length, + string LengthMember, + ActualType LengthMemberType, + Permissions LengthMemberPermissions, + int MinLength, + int MaxLength, + bool IsBigEndian, + FieldTypeModel FieldType, + StringEncoding FieldStringEncoding, + string PropertyName, + string PropertyAccessModifiers, + string PropertyGetterModifiers, + string PropertySetterModifiers, + string ClassName, + string[] Imports, + string ValidationMethodName + ); +} diff --git a/RyuSocks.Generator/Packet/PacketFieldAttributeData.cs b/RyuSocks.Generator/Packet/PacketFieldAttributeData.cs new file mode 100644 index 0000000..66cf71d --- /dev/null +++ b/RyuSocks.Generator/Packet/PacketFieldAttributeData.cs @@ -0,0 +1,85 @@ +/* + * Copyright (C) RyuSOCKS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using Microsoft.CodeAnalysis; +using System; +using System.Linq; + +namespace RyuSocks.Generator.Packet +{ + // NOTE: The default values should be the same as the ones specified in PacketFieldAttribute. + // See: PacketGenerator.cs + internal class PacketFieldAttributeData + { + public int Offset { get; } = -1; + public string OffsetMember { get; } = string.Empty; + public int Length { get; } + public string LengthMember { get; } + public bool IsBigEndian { get; } + public int MinLength { get; } + public int MaxLength { get; } + public StringEncoding StringEncoding { get; } + public string AssumeGeneratedEnumType { get; } + public string ValidationMethod { get; } + + public PacketFieldAttributeData(AttributeData attributeData) + { + if (attributeData.ConstructorArguments.Length == 0) + { + throw new InvalidOperationException($"Attribute constructor doesn't have arguments: {attributeData.AttributeClass?.Name}"); + } + + // Parse constructor arguments + switch (attributeData.ConstructorArguments[0].Type!.Name) + { + case nameof(String): + OffsetMember = (string)attributeData.ConstructorArguments[0].Value!; + break; + case nameof(Int32): + Offset = (int)attributeData.ConstructorArguments[0].Value!; + break; + default: + throw new InvalidOperationException($"Attribute constructor argument type is not valid: {attributeData.ConstructorArguments[0].Type!.Name}"); + } + + // Parse named arguments + TypedConstant lengthArg = attributeData.NamedArguments.SingleOrDefault(kvp => kvp.Key == nameof(Length)).Value; + TypedConstant lengthMemberArg = attributeData.NamedArguments.SingleOrDefault(kvp => kvp.Key == nameof(LengthMember)).Value; + TypedConstant isBigEndianArg = attributeData.NamedArguments.SingleOrDefault(kvp => kvp.Key == nameof(IsBigEndian)).Value; + TypedConstant minLengthArg = attributeData.NamedArguments.SingleOrDefault(kvp => kvp.Key == nameof(MinLength)).Value; + TypedConstant maxLengthArg = attributeData.NamedArguments.SingleOrDefault(kvp => kvp.Key == nameof(MaxLength)).Value; + TypedConstant stringEncodingArg = attributeData.NamedArguments.SingleOrDefault(kvp => kvp.Key == nameof(StringEncoding)).Value; + TypedConstant assumeGeneratedEnumTypeArg = attributeData.NamedArguments.SingleOrDefault(kvp => kvp.Key == nameof(AssumeGeneratedEnumType)).Value; + TypedConstant validationMethodArg = attributeData.NamedArguments.SingleOrDefault(kvp => kvp.Key == nameof(ValidationMethod)).Value; + + // Get the actual value of every named argument which was specified + Length = !lengthArg.IsNull ? (int)lengthArg.Value! : -1; + LengthMember = !lengthMemberArg.IsNull ? (string)lengthMemberArg.Value! : string.Empty; + IsBigEndian = !isBigEndianArg.IsNull && (bool)isBigEndianArg.Value!; + MinLength = !minLengthArg.IsNull ? (int)minLengthArg.Value! : -1; + MaxLength = !maxLengthArg.IsNull ? (int)maxLengthArg.Value! : -1; + StringEncoding = !stringEncodingArg.IsNull + ? (StringEncoding)stringEncodingArg.Value! + : StringEncoding.ASCII; + AssumeGeneratedEnumType = !assumeGeneratedEnumTypeArg.IsNull + ? (string)assumeGeneratedEnumTypeArg.Value! + : string.Empty; + ValidationMethod = !validationMethodArg.IsNull + ? (string)validationMethodArg.Value! + : string.Empty; + } + } +} diff --git a/RyuSocks.Generator/Packet/Permissions.cs b/RyuSocks.Generator/Packet/Permissions.cs new file mode 100644 index 0000000..2aa4633 --- /dev/null +++ b/RyuSocks.Generator/Packet/Permissions.cs @@ -0,0 +1,26 @@ +/* + * Copyright (C) RyuSOCKS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +namespace RyuSocks.Generator.Packet +{ + internal enum Permissions + { + Unknown, + ReadOnly, + WriteOnly, + ReadWrite, + } +} diff --git a/RyuSocks.Generator/Packet/StringEncoding.cs b/RyuSocks.Generator/Packet/StringEncoding.cs new file mode 100644 index 0000000..7ffc973 --- /dev/null +++ b/RyuSocks.Generator/Packet/StringEncoding.cs @@ -0,0 +1,28 @@ +/* + * Copyright (C) RyuSOCKS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +namespace RyuSocks.Generator.Packet +{ + // NOTE: Keep this in sync with the generated string encoding enum in PacketGenerator.cs + internal enum StringEncoding : byte + { + ASCII, + Unicode, + UTF7, + UTF8, + UTF32, + } +} diff --git a/RyuSocks.Generator/Packet/StringEncodingExtensions.cs b/RyuSocks.Generator/Packet/StringEncodingExtensions.cs new file mode 100644 index 0000000..a8e5755 --- /dev/null +++ b/RyuSocks.Generator/Packet/StringEncodingExtensions.cs @@ -0,0 +1,77 @@ +/* + * Copyright (C) RyuSOCKS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using RyuSocks.Generator.Builder; + +namespace RyuSocks.Generator.Packet +{ + internal static class StringEncodingExtensions + { + public static string GetByteCountText(this StringEncoding stringEncoding, string sourceArgText) + { + return $"Encoding.{stringEncoding}.GetByteCount({sourceArgText})"; + } + + public static string[] GetBytesText(this StringEncoding stringEncoding, string sourceArgText, string destinationArgText, bool isBigEndian) + { + if (isBigEndian) + { + switch (stringEncoding) + { + case StringEncoding.Unicode: + return [$"Encoding.BigEndianUnicode.GetBytes({sourceArgText}, {destinationArgText});"]; + case StringEncoding.UTF32: + BlockBuilder source = new(); + + // Write bytes in little endian to temp array + source.AppendLine($"byte[] byteArray = Encoding.UTF32.GetBytes({sourceArgText});"); + source.AppendLine("Array.Reverse(byteArray);"); + // Write bytes with correct endianness to the packet + source.AppendLine($"byteArray.CopyTo({destinationArgText});"); + + return source.GetLines(); + } + } + + return [$"Encoding.{stringEncoding}.GetBytes({sourceArgText}, {destinationArgText});"]; + } + + public static string[] GetStringText(this StringEncoding stringEncoding, string sourceArgText, bool isBigEndian, bool returnValue = true) + { + string returnOrAssignText = returnValue ? "return" : "string valueString ="; + + if (isBigEndian) + { + switch (stringEncoding) + { + case StringEncoding.Unicode: + return [$"{returnOrAssignText} Encoding.BigEndianUnicode.GetString({sourceArgText});"]; + case StringEncoding.UTF32: + BlockBuilder source = new(); + + // Get a temp array, so the original packet doesn't get modified. + source.AppendLine($"byte[] stringArray = {sourceArgText}.ToArray();"); + source.AppendLine("Array.Reverse(stringArray);"); + source.AppendLine($"{returnOrAssignText} Encoding.UTF32.GetString(stringArray);"); + + return source.GetLines(); + } + } + + return [$"{returnOrAssignText} Encoding.{stringEncoding}.GetString({sourceArgText});"]; + } + } +} diff --git a/RyuSocks.Generator/Packet/TypeConverter.cs b/RyuSocks.Generator/Packet/TypeConverter.cs new file mode 100644 index 0000000..74d9e5f --- /dev/null +++ b/RyuSocks.Generator/Packet/TypeConverter.cs @@ -0,0 +1,58 @@ +/* + * Copyright (C) RyuSOCKS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System.Collections.Generic; +using System.Collections.Immutable; + +namespace RyuSocks.Generator.Packet +{ + internal static class TypeConverter + { + public static readonly ImmutableDictionary Map = new Dictionary + { + { ActualType.Int16, new TypeConverterModel(sizeof(short), "BinaryPrimitives.ReadInt16", "BinaryPrimitives.WriteInt16") }, + { ActualType.UInt16, new TypeConverterModel(sizeof(ushort), "BinaryPrimitives.ReadUInt16", "BinaryPrimitives.WriteUInt16") }, + { ActualType.Int32, new TypeConverterModel(sizeof(int), "BinaryPrimitives.ReadInt32", "BinaryPrimitives.WriteInt32") }, + { ActualType.UInt32, new TypeConverterModel(sizeof(uint), "BinaryPrimitives.ReadUInt32", "BinaryPrimitives.WriteUInt32") }, + { ActualType.Int64, new TypeConverterModel(sizeof(long), "BinaryPrimitives.ReadInt64", "BinaryPrimitives.WriteInt64") }, + { ActualType.UInt64, new TypeConverterModel(sizeof(ulong), "BinaryPrimitives.ReadUInt64", "BinaryPrimitives.WriteUInt64") }, + }.ToImmutableDictionary(); + } + + internal readonly record struct TypeConverterModel + { + public int Length { get; } + private string ReaderMethodName { get; } + private string WriterMethodName { get; } + + public TypeConverterModel(int length, string readerMethodName, string writerMethodName) + { + Length = length; + ReaderMethodName = readerMethodName; + WriterMethodName = writerMethodName; + } + + public string GetReaderName(bool isBigEndian) + { + return isBigEndian ? $"{ReaderMethodName}BigEndian" : $"{ReaderMethodName}LittleEndian"; + } + + public string GetWriterName(bool isBigEndian) + { + return isBigEndian ? $"{WriterMethodName}BigEndian" : $"{WriterMethodName}LittleEndian"; + } + } +} diff --git a/RyuSocks.Generator/PacketGenerator.cs b/RyuSocks.Generator/PacketGenerator.cs new file mode 100644 index 0000000..43920e2 --- /dev/null +++ b/RyuSocks.Generator/PacketGenerator.cs @@ -0,0 +1,511 @@ +/* + * Copyright (C) RyuSOCKS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using RyuSocks.Generator.Builder; +using RyuSocks.Generator.Packet; +using System; +using System.Linq; +using System.Threading; + +namespace RyuSocks.Generator +{ + // TODO: Write an analyzer for PacketFieldAttribute + // - make sure (Offset and OffsetMember) and (Length and LengthMember) aren't used at the same time + // - check whether the attribute is used on a property within a class/subclass that extends Packet + + [Generator] + public class PacketGenerator : IIncrementalGenerator + { + private const string Namespace = "RyuSocks.Packets"; + private const string AbstractClassName = "Packet"; + private const string PacketFieldAttributeName = "PacketFieldAttribute"; + private const string StringEncodingEnumName = "StringEncoding"; + + #region SourceText + private const string EncodingEnumText = @" +namespace %NAMESPACE% +{ + public enum %ENUM_NAME% : byte + { + ASCII, + Unicode, + UTF7, + UTF8, + UTF32, + } +} +"; + + private const string PacketFieldAttributeText = @" +using System; + +namespace %NAMESPACE% +{ + [AttributeUsage(AttributeTargets.Property, Inherited = false)] + [System.Diagnostics.Conditional(""RyuSocks_PacketGenerator_DEBUG"")] + public sealed class %ATTRIBUTE_NAME% : Attribute + { + private int offset = -1; + private string offsetMember = string.Empty; + + /// + /// Marks this property as a field of the packet. + /// The containing class needs to extend . + /// + /// The offset of this field in the packet. + public %ATTRIBUTE_NAME%([System.ComponentModel.DataAnnotations.Range(0, int.MaxValue)] int offset) + { + this.offset = offset; + } + + /// + /// Marks this property as a field of the packet. + /// The containing class needs to extend . + /// + /// The name of the member which specifies the offset of this field in the packet. + public %ATTRIBUTE_NAME%(string offsetMember) + { + this.offsetMember = offsetMember; + } + + /// + /// The length of this field in bytes. + /// Only required if it can't be determined from the property type. + /// + public int Length { get; set; } = -1; + + /// + /// The name of the member which specifies the length of this field in bytes. + /// Only required if it can't be determined from the property type. + /// + public string LengthMember { get; set; } = string.Empty; + + /// + /// Whether this field is big endian. + /// + public bool IsBigEndian { get; set; } = false; + + /// + /// The minimum length of the array or chars allowed in this field. + /// + public int MinLength { get; set; } = -1; + + /// + /// The maximum length of the array or chars allowed in this field. + /// + public int MaxLength { get; set; } = -1; + + /// + /// The encoding of the underlying string. + /// Only required for string properties. + /// + public %STRING_ENCODING_ENUM_NAME% StringEncoding = %STRING_ENCODING_ENUM_NAME%.ASCII; + + /// + /// The name of the underlying type of the property type. + /// Only required if the type of the property is source generated. + /// + public string AssumeGeneratedEnumType { get; set; } = string.Empty; + + /// + /// The name of the method which should be invoked before the getter/setter is executed. + /// + /// + /// The method type must be void and have one optional parameter with the same type as the property. + /// If verification fails an exception should be thrown. + /// + public string ValidationMethod { get; set; } = string.Empty; + + public int Offset => offset; + public string OffsetMember => offsetMember; + } +} +"; + + private const string AbstractPacketClassText = @" +using System; + +namespace %NAMESPACE% +{ + public abstract partial class %CLASS_NAME% + { + /// + /// The contents of the packet. + /// + public byte[] Bytes { get; protected set; } + + /// + public byte this[int i] + { + get => Bytes[i]; + set => Bytes[i] = value; + } + + /// + /// Creates a new span over the packet. + /// + /// + /// + public Span AsSpan() => Bytes; + + /// + /// Creates a new Span over the portion of the packet beginning + /// at 'start' index and ending at 'end' index (exclusive). + /// + /// The index at which to begin the Span. + /// The number of items in the Span. + /// + public Span AsSpan(int start, int length) => Bytes.AsSpan(start, length); + + protected %CLASS_NAME%() { } + + protected %CLASS_NAME%(byte[] bytes) + { + Bytes = bytes; + } + } +} +"; + #endregion + + public void Initialize(IncrementalGeneratorInitializationContext context) + { + // Register the string encoding enum source + context.RegisterPostInitializationOutput(static postContext => postContext.AddSource( + $"{StringEncodingEnumName}.g.cs", + EncodingEnumText + .Replace("%NAMESPACE%", Namespace) + .Replace("%ENUM_NAME%", StringEncodingEnumName) + .TrimStart() + ) + ); + // Register the attribute source + context.RegisterPostInitializationOutput(static postContext => postContext.AddSource( + $"{PacketFieldAttributeName}.g.cs", + PacketFieldAttributeText + .Replace("%NAMESPACE%", Namespace) + .Replace("%ATTRIBUTE_NAME%", PacketFieldAttributeName) + .Replace("%ABSTRACT_CLASS_NAME%", AbstractClassName) + .Replace("%STRING_ENCODING_ENUM_NAME%", StringEncodingEnumName) + .TrimStart() + ) + ); + // Register the abstract packet class source + context.RegisterPostInitializationOutput(static postContext => postContext.AddSource( + $"{AbstractClassName}.g.cs", + AbstractPacketClassText + .Replace("%NAMESPACE%", Namespace) + .Replace("%CLASS_NAME%", AbstractClassName) + .TrimStart() + ) + ); + + // Create an incremental value provider using the generated attribute + var packetFieldProvider = context.SyntaxProvider.ForAttributeWithMetadataName( + $"{Namespace}.{PacketFieldAttributeName}", + IsValidPacketFieldProperty, + TransformPacketFieldProperty + ); + + context.RegisterImplementationSourceOutput(packetFieldProvider, ProduceSourceCode); + } + + /// + /// Check whether is a valid packet field property. + /// + /// The syntax node to check. + /// The cancellation token. + /// True if is a valid packet field property. + private static bool IsValidPacketFieldProperty(SyntaxNode syntaxNode, CancellationToken cancellationToken) => + syntaxNode is PropertyDeclarationSyntax { Parent: ClassDeclarationSyntax { BaseList.Types.Count: > 0 } classDeclaration } propertyDeclaration + && propertyDeclaration.Modifiers.Any(modifier => modifier.IsKind(SyntaxKind.PartialKeyword)) + && classDeclaration.Modifiers.Any(modifier => modifier.IsKind(SyntaxKind.PartialKeyword)); + + private static PacketFieldModel TransformPacketFieldProperty(GeneratorAttributeSyntaxContext context, CancellationToken cancellationToken) + { + var propertySymbol = (context.TargetSymbol as IPropertySymbol)!; + var attributeData = new PacketFieldAttributeData(context.Attributes[0]); + var classSymbol = (context.TargetSymbol.ContainingSymbol as INamedTypeSymbol)!; + + // Get information about the type of the property + + ITypeSymbol actualTypeSymbol = propertySymbol.Type; + bool isArray = propertySymbol.Type.TypeKind == TypeKind.Array; + bool isEnum = propertySymbol.Type.TypeKind == TypeKind.Enum; + bool isStruct = IsActualStruct(propertySymbol.Type); + if (!Enum.TryParse(actualTypeSymbol.Name, out ActualType actualType)) + { + actualType = ActualType.NamedType; + } + + if (isArray) + { + // Get the type of the array + actualTypeSymbol = (propertySymbol.Type as IArrayTypeSymbol)!.ElementType; + isEnum = actualTypeSymbol.TypeKind == TypeKind.Enum; + isStruct = IsActualStruct(actualTypeSymbol); + + // Parse the ActualType value if the array type is neither an enum, a struct, a class or a generated type + if (!isEnum && !isStruct && actualTypeSymbol.TypeKind != TypeKind.Class && actualTypeSymbol.TypeKind != TypeKind.Error) + { + actualType = (ActualType)Enum.Parse(typeof(ActualType), actualTypeSymbol.Name, true); + } + } + + if (isEnum) + { + // Parse the underlying type for enums + actualType = (ActualType)Enum.Parse(typeof(ActualType), (actualTypeSymbol as INamedTypeSymbol)!.EnumUnderlyingType!.Name, true); + } + + // Get information about length member if applicable + ActualType lengthMemberType = ActualType.NamedType; + Permissions lengthMemberPermissions = Permissions.Unknown; + if (attributeData.LengthMember.Length > 0) + { + ISymbol lengthMemberSymbol = classSymbol.GetMembers(attributeData.LengthMember).Single(); + + switch (lengthMemberSymbol) + { + case IPropertySymbol lengthProperty: + lengthMemberType = (ActualType)Enum.Parse(typeof(ActualType), lengthProperty.Type.Name, true); + if (lengthProperty.IsReadOnly) + { + lengthMemberPermissions = Permissions.ReadOnly; + } + else if (lengthProperty.IsWriteOnly) + { + lengthMemberPermissions = Permissions.WriteOnly; + } + else + { + lengthMemberPermissions = Permissions.ReadWrite; + } + break; + case IFieldSymbol lengthField: + lengthMemberType = (ActualType)Enum.Parse(typeof(ActualType), lengthField.Type.Name, true); + if (lengthField.IsReadOnly || lengthField.IsConst) + { + lengthMemberPermissions = Permissions.ReadOnly; + } + else + { + lengthMemberPermissions = Permissions.ReadWrite; + } + break; + default: + throw new InvalidOperationException( + $"Can't determine member type of LengthMember: {lengthMemberSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}"); + } + + if (lengthMemberPermissions == Permissions.WriteOnly) + { + throw new InvalidOperationException("Can't generate property accessors, LengthMember is write-only."); + } + } + + // NOTE: This is only necessary because property types could be generated by other source generators, + // which aren't available at this point. + + // Process auto generated property types + + // Assume generated enum type + if (actualTypeSymbol.TypeKind == TypeKind.Error && attributeData.AssumeGeneratedEnumType.Length > 0) + { + isEnum = true; + actualType = attributeData.AssumeGeneratedEnumType.ToActualType(); + } + + // Get imports + + CompilationUnitSyntax rootNode = context.TargetNode.SyntaxTree.GetCompilationUnitRoot(); + string[] imports = rootNode.Usings.ToString().Split('\n'); + for (int i = 0; i < imports.Length; i++) + { + imports[i] = imports[i].Trim(); + } + + // Create model from data + return new PacketFieldModel( + attributeData.Offset, + attributeData.OffsetMember, + attributeData.Length, + attributeData.LengthMember, + lengthMemberType, + lengthMemberPermissions, + attributeData.MinLength, + attributeData.MaxLength, + attributeData.IsBigEndian, + new FieldTypeModel( + RemoveGlobalAlias(propertySymbol.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)), + isArray, + isEnum, + isStruct, + actualType + ), + attributeData.StringEncoding, + propertySymbol.Name, + GetAccessModifierString(propertySymbol), + GetAccessorModifierString(propertySymbol, true), + GetAccessorModifierString(propertySymbol, false), + RemoveGlobalAlias(classSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)), + imports, + attributeData.ValidationMethod + ); + } + + private static void ProduceSourceCode(SourceProductionContext context, PacketFieldModel packetField) + { + string namespaceName = string.Empty; + string className = packetField.ClassName; + + if (className.Contains(".")) + { + namespaceName = className.Substring(0, className.LastIndexOf(".", StringComparison.Ordinal)); + className = className.Substring(className.LastIndexOf(".", StringComparison.Ordinal) + 1); + } + + // Begin building the generated source + CodeBuilder source = new(); + string[] requiredImports = ["using System;", "using System.Buffers.Binary;", "using System.Text;"]; + + // Add imports + + foreach (var importString in packetField.Imports) + { + source.AppendLine(importString); + } + + foreach (var requiredImportString in requiredImports) + { + if (!packetField.Imports.Contains(requiredImportString)) + { + source.AppendLine(requiredImportString); + } + } + + source.AppendLine(); + + // Add namespace if necessary + + if (namespaceName.Length > 0) + { + source.EnterScope($"namespace {namespaceName}"); + } + + // Add class and property + + source.EnterScope($"partial class {className}"); + source.EnterScope($"{packetField.PropertyAccessModifiers}partial {packetField.FieldType.Name} {packetField.PropertyName}"); + + // Add code for getter + source.AppendBlock(AccessorGenerator.GenerateGetter(packetField)); + // Add code for setter + source.AppendBlock(AccessorGenerator.GenerateSetter(packetField)); + + // Leave property and class scope + source.LeaveScope(); + source.LeaveScope(); + + // Leave namespace scope if necessary + if (namespaceName.Length > 0) + { + source.LeaveScope(); + } + + // Add the generated source + string fullyQualifiedName = namespaceName.Length > 0 ? $"{namespaceName}.{className}" : className; + context.AddSource($"{fullyQualifiedName}.{packetField.PropertyName}.g.cs", source.ToString()); + } + + private static string RemoveGlobalAlias(string classOrNamespaceName) => + classOrNamespaceName.StartsWith("global::") ? classOrNamespaceName.Substring(8) : classOrNamespaceName; + + private static bool IsActualStruct(ITypeSymbol typeSymbol) + { + if (typeSymbol.TypeKind != TypeKind.Struct) + { + return false; + } + + return typeSymbol.SpecialType is < SpecialType.System_Boolean or > SpecialType.System_String; + } + + private static string GetAccessModifierString(IPropertySymbol propertySymbol) + { + if (propertySymbol.DeclaringSyntaxReferences.Length != 1) + { + throw new InvalidOperationException($"PropertySymbol contains wrong amount of declaring syntax references: {propertySymbol.DeclaringSyntaxReferences.Length}"); + } + + if (propertySymbol.DeclaringSyntaxReferences[0].GetSyntax() is not PropertyDeclarationSyntax propertyDeclarationSyntax) + { + throw new InvalidOperationException($"DeclaringSyntaxReference is not {nameof(PropertyDeclarationSyntax)}: {propertySymbol.DeclaringSyntaxReferences[0].GetSyntax().Kind()}"); + } + + string result = ""; + + foreach (var modifier in propertyDeclarationSyntax.Modifiers) + { + if (modifier.IsKind(SyntaxKind.PartialKeyword)) + { + continue; + } + + result += $"{modifier} "; + } + + // NOTE: Keep the last space to make working with it easier. + return result; + } + + private static string GetAccessorModifierString(IPropertySymbol propertySymbol, bool isGetter) + { + if (propertySymbol.DeclaringSyntaxReferences.Length != 1) + { + throw new InvalidOperationException($"PropertySymbol contains wrong amount of declaring syntax references: {propertySymbol.DeclaringSyntaxReferences.Length}"); + } + + if (propertySymbol.DeclaringSyntaxReferences[0].GetSyntax() is not PropertyDeclarationSyntax propertyDeclarationSyntax) + { + throw new InvalidOperationException($"DeclaringSyntaxReference is not {nameof(PropertyDeclarationSyntax)}: {propertySymbol.DeclaringSyntaxReferences[0].GetSyntax().Kind()}"); + } + + SyntaxKind accessorKind = isGetter ? SyntaxKind.GetAccessorDeclaration : SyntaxKind.SetAccessorDeclaration; + + foreach (var accessor in propertyDeclarationSyntax.AccessorList!.Accessors) + { + if (!accessor.IsKind(accessorKind)) + { + continue; + } + + if (accessor.Modifiers.Count > 0) + { + // NOTE: Add a space to the end to make working with it easier. + return $"{accessor.Modifiers} "; + } + + return string.Empty; + } + + throw new InvalidOperationException($"Couldn't find property accessor for: {propertySymbol.Name}"); + } + } +} diff --git a/RyuSocks.Test/Auth/AuthMethodExtensionsTests.cs b/RyuSocks.Test/Auth/AuthMethodExtensionsTests.cs index 6e07d1e..3adddef 100644 --- a/RyuSocks.Test/Auth/AuthMethodExtensionsTests.cs +++ b/RyuSocks.Test/Auth/AuthMethodExtensionsTests.cs @@ -19,11 +19,13 @@ using RyuSocks.Test.Utils; using RyuSocks.Types; using System; +using System.Diagnostics.CodeAnalysis; using Xunit; namespace RyuSocks.Test.Auth { + [ExcludeFromCodeCoverage] public class AuthMethodExtensionsTests { public static readonly TheoryData AuthImplObjects = new() @@ -96,6 +98,7 @@ public void GetAuth_ThrowsOnUnknownImpl() } } + [ExcludeFromCodeCoverage] internal class UnknownAuth : IProxyAuth { public int WrapperLength => throw new NotImplementedException(); diff --git a/RyuSocks.Test/Auth/NoAuthTests.cs b/RyuSocks.Test/Auth/NoAuthTests.cs index e9a31b6..754e0e8 100644 --- a/RyuSocks.Test/Auth/NoAuthTests.cs +++ b/RyuSocks.Test/Auth/NoAuthTests.cs @@ -18,10 +18,12 @@ using RyuSocks.Auth.Extensions; using RyuSocks.Types; using System; +using System.Diagnostics.CodeAnalysis; using Xunit; namespace RyuSocks.Test.Auth { + [ExcludeFromCodeCoverage] public class NoAuthTests { [Theory] diff --git a/RyuSocks.Test/Auth/UsernameAndPasswordRequestTests.cs b/RyuSocks.Test/Auth/UsernameAndPasswordRequestTests.cs index 384c2c5..8cb0066 100644 --- a/RyuSocks.Test/Auth/UsernameAndPasswordRequestTests.cs +++ b/RyuSocks.Test/Auth/UsernameAndPasswordRequestTests.cs @@ -1,9 +1,11 @@ using RyuSocks.Auth.Packets; using System; +using System.Diagnostics.CodeAnalysis; using Xunit; namespace RyuSocks.Test.Auth { + [ExcludeFromCodeCoverage] public class UsernameAndPasswordRequestTests { private const string LongWord = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; diff --git a/RyuSocks.Test/Auth/UsernameAndPasswordResponseTest.cs b/RyuSocks.Test/Auth/UsernameAndPasswordResponseTest.cs index 42215d7..bb44c3b 100644 --- a/RyuSocks.Test/Auth/UsernameAndPasswordResponseTest.cs +++ b/RyuSocks.Test/Auth/UsernameAndPasswordResponseTest.cs @@ -1,10 +1,12 @@ using RyuSocks.Auth; using RyuSocks.Auth.Packets; using System; +using System.Diagnostics.CodeAnalysis; using Xunit; namespace RyuSocks.Test.Auth { + [ExcludeFromCodeCoverage] public class UsernameAndPasswordResponseTest { [Theory] diff --git a/RyuSocks.Test/Auth/UsernameAndPasswordTests.cs b/RyuSocks.Test/Auth/UsernameAndPasswordTests.cs index 7b83644..062173b 100644 --- a/RyuSocks.Test/Auth/UsernameAndPasswordTests.cs +++ b/RyuSocks.Test/Auth/UsernameAndPasswordTests.cs @@ -18,10 +18,12 @@ using RyuSocks.Auth.Packets; using RyuSocks.Types; using System; +using System.Diagnostics.CodeAnalysis; using Xunit; namespace RyuSocks.Test.Auth { + [ExcludeFromCodeCoverage] public class UsernameAndPasswordTests { private static readonly UsernameAndPasswordResponse _expectedUsernameAndPasswordResponse = new() diff --git a/RyuSocks.Test/Packets/CommandPacketTests.cs b/RyuSocks.Test/Packets/CommandPacketTests.cs index 5df2fb3..c5215a5 100644 --- a/RyuSocks.Test/Packets/CommandPacketTests.cs +++ b/RyuSocks.Test/Packets/CommandPacketTests.cs @@ -18,6 +18,7 @@ using RyuSocks.Types; using RyuSocks.Utils; using System; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Net; using System.Text; @@ -25,6 +26,7 @@ namespace RyuSocks.Test.Packets { + [ExcludeFromCodeCoverage] public abstract class CommandPacketTests where T : CommandPacket { private const string VeryLongInvalidTestDomainName = "abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc.local"; diff --git a/RyuSocks.Test/Packets/CommandRequestTests.cs b/RyuSocks.Test/Packets/CommandRequestTests.cs index 84088b7..444fef9 100644 --- a/RyuSocks.Test/Packets/CommandRequestTests.cs +++ b/RyuSocks.Test/Packets/CommandRequestTests.cs @@ -20,6 +20,7 @@ using RyuSocks.Types; using RyuSocks.Utils; using System; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Net; using System.Net.Sockets; @@ -28,6 +29,7 @@ namespace RyuSocks.Test.Packets { + [ExcludeFromCodeCoverage] public class CommandRequestTests : CommandPacketTests { #pragma warning disable IDE0055 // Disable formatting diff --git a/RyuSocks.Test/Packets/CommandResponseTests.cs b/RyuSocks.Test/Packets/CommandResponseTests.cs index 5e41bbb..0f8c642 100644 --- a/RyuSocks.Test/Packets/CommandResponseTests.cs +++ b/RyuSocks.Test/Packets/CommandResponseTests.cs @@ -19,6 +19,7 @@ using RyuSocks.Types; using RyuSocks.Utils; using System; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Net; using System.Net.Sockets; @@ -27,6 +28,7 @@ namespace RyuSocks.Test.Packets { + [ExcludeFromCodeCoverage] public class CommandResponseTests : CommandPacketTests { #pragma warning disable IDE0055 // Disable formatting diff --git a/RyuSocks.Test/Packets/MethodSelectionRequestTests.cs b/RyuSocks.Test/Packets/MethodSelectionRequestTests.cs index 79eaac3..a5604af 100644 --- a/RyuSocks.Test/Packets/MethodSelectionRequestTests.cs +++ b/RyuSocks.Test/Packets/MethodSelectionRequestTests.cs @@ -19,11 +19,13 @@ using RyuSocks.Test.Utils; using RyuSocks.Utils; using System; +using System.Diagnostics.CodeAnalysis; using System.Linq; using Xunit; namespace RyuSocks.Test.Packets { + [ExcludeFromCodeCoverage] public class MethodSelectionRequestTests { [Theory] diff --git a/RyuSocks.Test/Packets/MethodSelectionResponseTests.cs b/RyuSocks.Test/Packets/MethodSelectionResponseTests.cs index ac0ab4e..e0f073a 100644 --- a/RyuSocks.Test/Packets/MethodSelectionResponseTests.cs +++ b/RyuSocks.Test/Packets/MethodSelectionResponseTests.cs @@ -18,10 +18,12 @@ using RyuSocks.Packets; using RyuSocks.Utils; using System; +using System.Diagnostics.CodeAnalysis; using Xunit; namespace RyuSocks.Test.Packets { + [ExcludeFromCodeCoverage] public class MethodSelectionResponseTests { [Fact] diff --git a/RyuSocks.Test/SocksClientTests.cs b/RyuSocks.Test/SocksClientTests.cs index 1d1334d..38476b6 100644 --- a/RyuSocks.Test/SocksClientTests.cs +++ b/RyuSocks.Test/SocksClientTests.cs @@ -21,6 +21,7 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Net; using System.Threading; @@ -28,6 +29,7 @@ namespace RyuSocks.Test { + [ExcludeFromCodeCoverage] public class SocksClientTests : IDisposable, IClassFixture { private readonly SocksServerFixture _fixture; @@ -80,6 +82,7 @@ public void Authenticate_Succeeds() } // ReSharper disable once ClassNeverInstantiated.Global + [ExcludeFromCodeCoverage] public class SocksServerFixture : IDisposable { public TestSocksServer Server { get; } @@ -112,6 +115,7 @@ public void Dispose() } } + [ExcludeFromCodeCoverage] public class TestSocksServer : SocksServer { public TestSocksServer(IPAddress address, ushort port = ProxyConsts.DefaultPort) : base(address, port) { } diff --git a/RyuSocks.Test/Utils/EnumDataAttribute.cs b/RyuSocks.Test/Utils/EnumDataAttribute.cs index b95e632..927b202 100644 --- a/RyuSocks.Test/Utils/EnumDataAttribute.cs +++ b/RyuSocks.Test/Utils/EnumDataAttribute.cs @@ -14,6 +14,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Threading.Tasks; using Xunit; @@ -22,6 +23,7 @@ namespace RyuSocks.Test.Utils { + [ExcludeFromCodeCoverage] public class EnumDataAttribute : DataAttribute where T : struct, Enum { diff --git a/RyuSocks.Test/Utils/RangeDataAttribute.cs b/RyuSocks.Test/Utils/RangeDataAttribute.cs index 47faa48..0b39302 100644 --- a/RyuSocks.Test/Utils/RangeDataAttribute.cs +++ b/RyuSocks.Test/Utils/RangeDataAttribute.cs @@ -13,6 +13,7 @@ // along with this program. If not, see . using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Numerics; using System.Reflection; using System.Threading.Tasks; @@ -22,6 +23,7 @@ namespace RyuSocks.Test.Utils { + [ExcludeFromCodeCoverage] public class RangeDataAttribute : DataAttribute where T : INumber { diff --git a/RyuSocks.Test/Utils/StringDataAttribute.cs b/RyuSocks.Test/Utils/StringDataAttribute.cs index 6c5a629..947c76b 100644 --- a/RyuSocks.Test/Utils/StringDataAttribute.cs +++ b/RyuSocks.Test/Utils/StringDataAttribute.cs @@ -15,6 +15,7 @@ */ using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Text; using System.Threading.Tasks; @@ -24,6 +25,7 @@ namespace RyuSocks.Test.Utils { + [ExcludeFromCodeCoverage] public class StringDataAttribute : DataAttribute { private const char Char = 'a'; diff --git a/RyuSocks.sln b/RyuSocks.sln index 63af2ad..eadf7f0 100644 --- a/RyuSocks.sln +++ b/RyuSocks.sln @@ -8,6 +8,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RyuSocks.Test", "RyuSocks.T EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RyuSocks.Test.Integration", "RyuSocks.Test.Integration\RyuSocks.Test.Integration.csproj", "{C0D6DEF0-9544-454A-A009-227EC1BEDCE3}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RyuSocks.Generator.Test", "RyuSocks.Generator.Test\RyuSocks.Generator.Test.csproj", "{6B63A81D-99FF-4DFF-BA12-5BB173E4E36F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -30,5 +32,9 @@ Global {C0D6DEF0-9544-454A-A009-227EC1BEDCE3}.Debug|Any CPU.Build.0 = Debug|Any CPU {C0D6DEF0-9544-454A-A009-227EC1BEDCE3}.Release|Any CPU.ActiveCfg = Release|Any CPU {C0D6DEF0-9544-454A-A009-227EC1BEDCE3}.Release|Any CPU.Build.0 = Release|Any CPU + {6B63A81D-99FF-4DFF-BA12-5BB173E4E36F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6B63A81D-99FF-4DFF-BA12-5BB173E4E36F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6B63A81D-99FF-4DFF-BA12-5BB173E4E36F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6B63A81D-99FF-4DFF-BA12-5BB173E4E36F}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/RyuSocks/Auth/Packets/UsernameAndPasswordRequest.cs b/RyuSocks/Auth/Packets/UsernameAndPasswordRequest.cs index 8b70c81..af0772d 100644 --- a/RyuSocks/Auth/Packets/UsernameAndPasswordRequest.cs +++ b/RyuSocks/Auth/Packets/UsernameAndPasswordRequest.cs @@ -16,78 +16,31 @@ using RyuSocks.Packets; using System; -using System.Text; namespace RyuSocks.Auth.Packets { - public class UsernameAndPasswordRequest : Packet + public partial class UsernameAndPasswordRequest : Packet { private const int MinimumPacketLength = 4; private const int MaximumPacketLength = 513; - public byte Version - { - get - { - return Bytes[0]; - } - set - { - Bytes[0] = value; - } - } + [PacketField(0)] + public partial byte Version { get; set; } - public byte UsernameLength - { - get - { - return Bytes[1]; - } - set - { - Bytes[1] = value; - } - } + [PacketField(1)] + public partial byte UsernameLength { get; set; } - public string Username - { - get - { - return Encoding.ASCII.GetString(Bytes.AsSpan(2, UsernameLength)); - } - set - { - ArgumentOutOfRangeException.ThrowIfGreaterThan(value.Length, 0xFF); - UsernameLength = (byte)value.Length; - Encoding.ASCII.GetBytes(value).CopyTo(Bytes.AsSpan(2, UsernameLength)); - } - } + [PacketField(2, LengthMember = nameof(UsernameLength), MaxLength = 0xFF)] + public partial string Username { get; set; } - public byte PasswordLength - { - get - { - return Bytes[2 + UsernameLength]; - } - set - { - Bytes[2 + UsernameLength] = value; - } - } + [PacketField(nameof(PasswordLengthOffset))] + public partial byte PasswordLength { get; set; } - public string Password - { - get - { - return Encoding.ASCII.GetString(Bytes.AsSpan(3 + UsernameLength, PasswordLength)); - } - set - { - ArgumentOutOfRangeException.ThrowIfGreaterThan(value.Length, 0xFF); - PasswordLength = (byte)value.Length; - Encoding.ASCII.GetBytes(value).CopyTo(Bytes.AsSpan(3 + UsernameLength, PasswordLength)); - } - } + [PacketField(nameof(PasswordOffset), LengthMember = nameof(PasswordLength), MaxLength = 0xFF)] + public partial string Password { get; set; } + + private int PasswordLengthOffset => 2 + UsernameLength; + private int PasswordOffset => PasswordLengthOffset + 1; public UsernameAndPasswordRequest(byte[] packetBytes) : base(packetBytes) { diff --git a/RyuSocks/Auth/Packets/UsernameAndPasswordResponse.cs b/RyuSocks/Auth/Packets/UsernameAndPasswordResponse.cs index 2a95be1..7bd02ee 100644 --- a/RyuSocks/Auth/Packets/UsernameAndPasswordResponse.cs +++ b/RyuSocks/Auth/Packets/UsernameAndPasswordResponse.cs @@ -19,31 +19,13 @@ namespace RyuSocks.Auth.Packets { - public class UsernameAndPasswordResponse : Packet + public partial class UsernameAndPasswordResponse : Packet { - public byte Version - { - get - { - return Bytes[0]; - } - set - { - Bytes[0] = value; - } - } + [PacketField(0)] + public partial byte Version { get; set; } - public byte Status - { - get - { - return Bytes[1]; - } - set - { - Bytes[1] = value; - } - } + [PacketField(1)] + public partial byte Status { get; set; } public UsernameAndPasswordResponse() { diff --git a/RyuSocks/Packets/CommandPacket.cs b/RyuSocks/Packets/CommandPacket.cs index 6e8c117..802cca2 100644 --- a/RyuSocks/Packets/CommandPacket.cs +++ b/RyuSocks/Packets/CommandPacket.cs @@ -21,33 +21,15 @@ namespace RyuSocks.Packets { - public abstract class CommandPacket : EndpointPacket + public abstract partial class CommandPacket : EndpointPacket { - public byte Version - { - get - { - return Bytes[0]; - } - set - { - Bytes[0] = value; - } - } + [PacketField(0)] + public partial byte Version { get; set; } // ProxyCommand or ReplyField - public byte Reserved - { - get - { - return Bytes[2]; - } - set - { - Bytes[2] = value; - } - } + [PacketField(2)] + public partial byte Reserved { get; set; } // AddressType @@ -56,9 +38,13 @@ public byte Reserved // Port protected CommandPacket(byte[] bytes) : base(bytes) { } + protected CommandPacket(IPEndPoint endpoint) : base(endpoint) { } + protected CommandPacket(DnsEndPoint endpoint) : base(endpoint) { } + protected CommandPacket(ProxyEndpoint endpoint) : base(endpoint) { } + protected CommandPacket() { } public override void Validate() diff --git a/RyuSocks/Packets/CommandRequest.cs b/RyuSocks/Packets/CommandRequest.cs index aca1644..4a5bd20 100644 --- a/RyuSocks/Packets/CommandRequest.cs +++ b/RyuSocks/Packets/CommandRequest.cs @@ -16,25 +16,17 @@ using RyuSocks.Commands; using RyuSocks.Types; +using System; using System.Net; namespace RyuSocks.Packets { - public class CommandRequest : CommandPacket + public partial class CommandRequest : CommandPacket { // Version - public ProxyCommand Command - { - get - { - return (ProxyCommand)Bytes[1]; - } - set - { - Bytes[1] = (byte)value; - } - } + [PacketField(1, AssumeGeneratedEnumType = nameof(Byte))] + public partial ProxyCommand Command { get; set; } // Reserved @@ -59,9 +51,13 @@ public ushort DestinationPort } public CommandRequest(byte[] bytes) : base(bytes) { } + public CommandRequest(IPEndPoint endpoint) : base(endpoint) { } + public CommandRequest(DnsEndPoint endpoint) : base(endpoint) { } + public CommandRequest(ProxyEndpoint endpoint) : base(endpoint) { } + public CommandRequest() { } } } diff --git a/RyuSocks/Packets/CommandResponse.cs b/RyuSocks/Packets/CommandResponse.cs index 3f9ac80..bd5f59d 100644 --- a/RyuSocks/Packets/CommandResponse.cs +++ b/RyuSocks/Packets/CommandResponse.cs @@ -19,21 +19,12 @@ namespace RyuSocks.Packets { - public class CommandResponse : CommandPacket + public partial class CommandResponse : CommandPacket { // Version - public ReplyField ReplyField - { - get - { - return (ReplyField)Bytes[1]; - } - set - { - Bytes[1] = (byte)value; - } - } + [PacketField(1)] + public partial ReplyField ReplyField { get; set; } // Reserved @@ -58,9 +49,13 @@ public ushort BoundPort } public CommandResponse(byte[] bytes) : base(bytes) { } + public CommandResponse(IPEndPoint endpoint) : base(endpoint) { } + public CommandResponse(DnsEndPoint endpoint) : base(endpoint) { } + public CommandResponse(ProxyEndpoint endpoint) : base(endpoint) { } + public CommandResponse() { } } } diff --git a/RyuSocks/Packets/EndpointPacket.cs b/RyuSocks/Packets/EndpointPacket.cs index 3be0588..b679ad9 100644 --- a/RyuSocks/Packets/EndpointPacket.cs +++ b/RyuSocks/Packets/EndpointPacket.cs @@ -19,11 +19,10 @@ using System.Linq; using System.Net; using System.Net.Sockets; -using System.Text; namespace RyuSocks.Packets { - public abstract class EndpointPacket : Packet + public abstract partial class EndpointPacket : Packet { private const byte MinimumPacketLength = 8; private const byte Ipv4PacketLength = 10; @@ -31,136 +30,48 @@ public abstract class EndpointPacket : Packet private const byte MinimumDomainNameLength = byte.MinValue + 1; private const byte MaximumDomainNameLength = byte.MaxValue; - public AddressType AddressType - { - get - { - return (AddressType)Bytes[3]; - } - set - { - Bytes[3] = (byte)value; + [PacketField(3, ValidationMethod = nameof(ResizeIfNecessary))] + public partial AddressType AddressType { get; set; } - if (AddressType is AddressType.Ipv4Address or AddressType.Ipv6Address) - { - int requiredLength = GetEndpointPacketLength(); - if (Bytes.Length != requiredLength) - { - byte[] resizeBytes = Bytes; - Array.Resize(ref resizeBytes, requiredLength); - Bytes = resizeBytes; - } - } - } - } + [PacketField(4, LengthMember = nameof(AddressSize))] + protected partial IPAddress Address { get; set; } - protected IPAddress Address - { - get - { - return AddressType switch - { - AddressType.Ipv4Address => new IPAddress(Bytes[4..8]), - AddressType.Ipv6Address => new IPAddress(Bytes[4..20]), - _ => throw new InvalidOperationException( - $"Can't get {nameof(Address)} for {nameof(Types.AddressType)} {AddressType}."), - }; - } - set - { - switch (AddressType) - { - case AddressType.Ipv4Address: - value.GetAddressBytes().CopyTo(Bytes.AsSpan(4, 4)); - return; - case AddressType.Ipv6Address: - value.GetAddressBytes().CopyTo(Bytes.AsSpan(4, 16)); - return; - default: - throw new InvalidOperationException( - $"Can't set {nameof(Address)} for {nameof(Types.AddressType)} {AddressType}."); - } - } - } + [PacketField(4, ValidationMethod = nameof(ResizeForDomainNameIfNecessary))] + protected partial byte DomainNameLength { get; set; } - protected byte DomainNameLength - { - get - { - return AddressType switch - { - AddressType.DomainName => Bytes[4], - _ => throw new InvalidOperationException( - $"Can't get {nameof(DomainNameLength)} for {nameof(Types.AddressType)} {AddressType}."), - }; - } - set - { - if (AddressType != AddressType.DomainName) - { - throw new InvalidOperationException( - $"Can't set {nameof(DomainNameLength)} for {nameof(Types.AddressType)} {AddressType}."); - } + [PacketField(5, LengthMember = nameof(DomainNameLength), MinLength = MinimumDomainNameLength, MaxLength = MaximumDomainNameLength, ValidationMethod = nameof(EnsureDomainNameType))] + protected partial string DomainName { get; set; } - ArgumentOutOfRangeException.ThrowIfLessThan(value, MinimumDomainNameLength); - Bytes[4] = value; + [PacketField(nameof(PortOffset), IsBigEndian = true)] + protected partial ushort Port { get; set; } - int requiredLength = GetEndpointPacketLength(); - if (Bytes.Length != requiredLength) - { - byte[] resizeBytes = Bytes; - Array.Resize(ref resizeBytes, requiredLength); - Bytes = resizeBytes; - } - } - } + public ProxyEndpoint ProxyEndpoint => AddressType == AddressType.DomainName + ? new ProxyEndpoint(new DnsEndPoint(DomainName, Port)) + : new ProxyEndpoint(new IPEndPoint(Address, Port)); - protected string DomainName + protected int PacketLength => AddressType switch { - get - { - return AddressType switch - { - AddressType.DomainName => Encoding.ASCII.GetString(Bytes, 5, DomainNameLength), - _ => throw new InvalidOperationException( - $"Can't get {nameof(DomainName)} for {nameof(Types.AddressType)} {AddressType}.") - }; - } - set - { - switch (AddressType) - { - case AddressType.DomainName: - ArgumentOutOfRangeException.ThrowIfGreaterThan(value.Length, MaximumDomainNameLength); - DomainNameLength = (byte)value.Length; - Encoding.ASCII.GetBytes(value, Bytes.AsSpan(5, DomainNameLength)); - return; - default: - throw new InvalidOperationException( - $"Can't set {nameof(DomainName)} for {nameof(Types.AddressType)} {AddressType}."); - } - } - } + AddressType.Ipv4Address => Ipv4PacketLength, + AddressType.DomainName => 7 + DomainNameLength, + AddressType.Ipv6Address => Ipv6PacketLength, + _ => throw new ArgumentOutOfRangeException(nameof(AddressType)), + }; - protected ushort Port + private int AddressSize => AddressType switch { - get - { - Span portSpan = GetPortSpan(); - portSpan.Reverse(); - return BitConverter.ToUInt16(portSpan); - } - set - { - byte[] portBytes = BitConverter.GetBytes(value); - Array.Reverse(portBytes); - portBytes.CopyTo(GetPortSpan()); - } - } + AddressType.Ipv4Address => 4, + AddressType.Ipv6Address => 16, + _ => throw new InvalidOperationException( + $"Can't get address size for {nameof(Types.AddressType)} {AddressType}."), + }; - public ProxyEndpoint ProxyEndpoint => AddressType == AddressType.DomainName - ? new ProxyEndpoint(new DnsEndPoint(DomainName, Port)) - : new ProxyEndpoint(new IPEndPoint(Address, Port)); + private int PortOffset => AddressType switch + { + AddressType.Ipv4Address => Ipv4PacketLength - 2, + AddressType.DomainName => 5 + DomainNameLength, + AddressType.Ipv6Address => Ipv6PacketLength - 2, + _ => throw new ArgumentOutOfRangeException(nameof(AddressType)), + }; protected EndpointPacket(byte[] bytes) : base(bytes) { @@ -234,26 +145,51 @@ protected EndpointPacket() AddressType = AddressType.Ipv4Address; } - protected int GetEndpointPacketLength() + // ReSharper disable once UnusedParameter.Local + private void EnsureDomainNameType(string value = "") { - return AddressType switch + if (AddressType != AddressType.DomainName) { - AddressType.Ipv4Address => Ipv4PacketLength, - AddressType.DomainName => 7 + DomainNameLength, - AddressType.Ipv6Address => Ipv6PacketLength, - _ => throw new ArgumentOutOfRangeException(nameof(AddressType)), - }; + throw new InvalidOperationException( + $"Can't get {nameof(DomainNameLength)} for {nameof(Types.AddressType)} {AddressType}."); + } } - private Span GetPortSpan() + private void ResizeIfNecessary(AddressType value = 0) { - return AddressType switch + if (value == 0) { - AddressType.Ipv4Address => Bytes.AsSpan(Ipv4PacketLength - 2, 2), - AddressType.DomainName => Bytes.AsSpan(5 + DomainNameLength, 2), - AddressType.Ipv6Address => Bytes.AsSpan(Ipv6PacketLength - 2, 2), - _ => throw new ArgumentOutOfRangeException(nameof(AddressType)), - }; + return; + } + + if (AddressType is AddressType.Ipv4Address or AddressType.Ipv6Address) + { + int requiredLength = PacketLength; + if (Bytes.Length != requiredLength) + { + byte[] resizeBytes = Bytes; + Array.Resize(ref resizeBytes, requiredLength); + Bytes = resizeBytes; + } + } + } + + private void ResizeForDomainNameIfNecessary(byte value = 0) + { + EnsureDomainNameType(); + + if (value == 0) + { + return; + } + + int requiredLength = 7 + value; + if (Bytes.Length != requiredLength) + { + byte[] resizeBytes = Bytes; + Array.Resize(ref resizeBytes, requiredLength); + Bytes = resizeBytes; + } } public override void Validate() diff --git a/RyuSocks/Packets/MethodSelectionRequest.cs b/RyuSocks/Packets/MethodSelectionRequest.cs index 091b438..1132724 100644 --- a/RyuSocks/Packets/MethodSelectionRequest.cs +++ b/RyuSocks/Packets/MethodSelectionRequest.cs @@ -21,46 +21,16 @@ namespace RyuSocks.Packets { - public class MethodSelectionRequest : Packet + public partial class MethodSelectionRequest : Packet { - public byte Version - { - get - { - return Bytes[0]; - } - set - { - Bytes[0] = value; - } - } + [PacketField(0)] + public partial byte Version { get; set; } - public byte NumOfMethods - { - get - { - return Bytes[1]; - } - set - { - Bytes[1] = value; - } - } - - public AuthMethod[] Methods - { - get - { - return Bytes[2..(2 + NumOfMethods)].Cast().ToArray(); + [PacketField(1)] + public partial byte NumOfMethods { get; set; } - } - set - { - ArgumentOutOfRangeException.ThrowIfGreaterThan(value.Length, 0xFF); - NumOfMethods = (byte)value.Length; - value.Cast().ToArray().CopyTo(Bytes.AsSpan(2, value.Length)); - } - } + [PacketField(2, LengthMember = nameof(NumOfMethods), MaxLength = 0xFF, AssumeGeneratedEnumType = nameof(Byte))] + public partial AuthMethod[] Methods { get; set; } public MethodSelectionRequest(byte[] bytes) : base(bytes) { } diff --git a/RyuSocks/Packets/MethodSelectionResponse.cs b/RyuSocks/Packets/MethodSelectionResponse.cs index 95eff39..3ff310b 100644 --- a/RyuSocks/Packets/MethodSelectionResponse.cs +++ b/RyuSocks/Packets/MethodSelectionResponse.cs @@ -20,31 +20,13 @@ namespace RyuSocks.Packets { - public class MethodSelectionResponse : Packet + public partial class MethodSelectionResponse : Packet { - public byte Version - { - get - { - return Bytes[0]; - } - set - { - Bytes[0] = value; - } - } + [PacketField(0)] + public partial byte Version { get; set; } - public AuthMethod Method - { - get - { - return (AuthMethod)Bytes[1]; - } - set - { - Bytes[1] = (byte)value; - } - } + [PacketField(1, AssumeGeneratedEnumType = nameof(Byte))] + public partial AuthMethod Method { get; set; } public MethodSelectionResponse(byte[] bytes) : base(bytes) { } diff --git a/RyuSocks/Packets/Packet.cs b/RyuSocks/Packets/Packet.cs index 8d0282e..781893b 100644 --- a/RyuSocks/Packets/Packet.cs +++ b/RyuSocks/Packets/Packet.cs @@ -14,20 +14,10 @@ * along with this program. If not, see . */ -using System; - namespace RyuSocks.Packets { - public abstract class Packet + public abstract partial class Packet { - /// - /// The contents of the packet. - /// - public byte[] Bytes { get; protected set; } - - /// - public Span AsSpan() => Bytes; - /// /// Validate the structure of the packet. /// This method is not supposed to verify the contents of the packet in depth. @@ -52,12 +42,5 @@ public bool IsValid() return true; } - - protected Packet() { } - - protected Packet(byte[] bytes) - { - Bytes = bytes; - } } } diff --git a/RyuSocks/Packets/UdpPacket.cs b/RyuSocks/Packets/UdpPacket.cs index 5bd4120..093d6fa 100644 --- a/RyuSocks/Packets/UdpPacket.cs +++ b/RyuSocks/Packets/UdpPacket.cs @@ -20,33 +20,15 @@ namespace RyuSocks.Packets { - public class UdpPacket : EndpointPacket + public partial class UdpPacket : EndpointPacket { - public int HeaderLength => GetEndpointPacketLength(); + public int HeaderLength => PacketLength; - public ushort Reserved - { - get - { - return BitConverter.ToUInt16(Bytes.AsSpan(0, 2)); - } - set - { - BitConverter.GetBytes(value).CopyTo(Bytes.AsSpan(0, 2)); - } - } + [PacketField(0)] + public partial ushort Reserved { get; set; } - public byte Fragment - { - get - { - return Bytes[2]; - } - set - { - Bytes[2] = value; - } - } + [PacketField(2)] + public partial byte Fragment { get; set; } // AddressType @@ -68,7 +50,7 @@ public ushort DestinationPort set => Port = value; } - public Span UserData => Bytes.AsSpan(GetEndpointPacketLength()); + public Span UserData => Bytes.AsSpan(PacketLength); public UdpPacket(byte[] bytes) : base(bytes) { }