From b34653fc1d32dc411ad8d4a870d596d99922cd84 Mon Sep 17 00:00:00 2001 From: James Gate <355273+gatesy@users.noreply.github.com> Date: Thu, 22 Aug 2019 17:46:49 +0100 Subject: [PATCH 1/7] Added a rough outline of my C# value type encoder/decoder. --- .../csharp/CSharpStructGenerator.java | 137 ++++++++++++++++++ .../generation/csharp/CSharpValueTypes.java | 13 ++ 2 files changed, 150 insertions(+) create mode 100644 sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpStructGenerator.java create mode 100644 sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpValueTypes.java diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpStructGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpStructGenerator.java new file mode 100644 index 0000000000..de3d469a04 --- /dev/null +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpStructGenerator.java @@ -0,0 +1,137 @@ +package uk.co.real_logic.sbe.generation.csharp; + +import org.agrona.Verify; +import org.agrona.generation.OutputManager; +import uk.co.real_logic.sbe.generation.CodeGenerator; +import uk.co.real_logic.sbe.ir.Ir; +import uk.co.real_logic.sbe.ir.Signal; +import uk.co.real_logic.sbe.ir.Token; + +import java.io.IOException; +import java.io.Writer; +import java.util.List; + +public class CSharpStructGenerator implements CodeGenerator +{ + private static final String INDENT = " "; + + private final Ir ir; + private final OutputManager outputManager; + + public CSharpStructGenerator(final Ir ir, final OutputManager outputManager) + { + Verify.notNull(ir, "ir"); + Verify.notNull(outputManager, "outputManager"); + + this.ir = ir; + this.outputManager = outputManager; + } + + @Override + public void generate() throws IOException + { + writeComposite(ir.headerStructure().tokens()); + } + + public void writeComposite(final List tokens) throws IOException + { + final String compositeName = CSharpUtil.formatClassName(tokens.get(0).applicableTypeName()); + final String structName = compositeName + "Value"; + + try (Writer out = outputManager.createOutput(structName)) + { + out.append(generateFileHeader(ir.packageName())); + out.append(generateStructDeclaration(structName, compositeName)); + out.append(generateStructConstructor(structName, tokens, INDENT + INDENT)); + out.append(generateStructMembers(tokens, INDENT + INDENT)); + out.append(INDENT + "}\n"); + out.append("}\n"); + } + } + + private CharSequence generateFileHeader(final String packageName) + { + return String.format( + "/* Generated SBE (Simple Binary Encoding) message codec */\n\n" + + "using System;\n" + + "using System.Runtime.InteropServices;\n\n" + + "namespace %s\n" + + "{\n", packageName); + } + + private CharSequence generateStructDeclaration(final String structName, final String compositeName) + { + return String.format( + INDENT + "[StructLayout(LayoutKind.Explicit, Size = %s.Size)]\n" + + INDENT + "public struct %s\n" + + INDENT + "{\n", + compositeName, structName); + } + + private CharSequence generateStructConstructor(final String structName, final List tokens, + final String baseIndent) + { + final StringBuilder sb = new StringBuilder(); + + sb.append(baseIndent); + sb.append("public "); + sb.append(structName); + sb.append("("); + + boolean firstTokenDone = false; + + for (final Token token : tokens) + { + switch (token.signal()) + { + case ENCODING: + if (firstTokenDone) + { + sb.append(","); + } + sb.append("\n"); + sb.append(baseIndent + INDENT + CSharpUtil.cSharpTypeName(token.encoding().primitiveType())); + sb.append(" "); + sb.append(token.name()); + firstTokenDone = true; + break; + } + } + + sb.append(")\n"); + sb.append(baseIndent + "{\n"); // start of constructor body + + // Generate field assignments + for (final Token token : tokens) + { + if (token.signal() == Signal.ENCODING) + { + sb.append(String.format(baseIndent + INDENT + "%s = %s;\n", + CSharpUtil.toUpperFirstChar(token.name()), token.name())); + } + } + + sb.append(baseIndent + "}\n"); // end of constructor + + return sb; + } + + private CharSequence generateStructMembers(final List tokens, final String baseIndent) + { + final StringBuilder sb = new StringBuilder(); + + for (final Token token : tokens) + { + if (token.signal() == Signal.ENCODING) + { + sb.append(String.format("\n" + baseIndent + "[FieldOffset(%s)]\n", token.offset())); + sb.append(String.format(baseIndent + "public readonly %s %s;\n", + CSharpUtil.cSharpTypeName(token.encoding().primitiveType()), + CSharpUtil.toUpperFirstChar(token.name()))); + } + } + + return sb; + } + +} diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpValueTypes.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpValueTypes.java new file mode 100644 index 0000000000..ef6242fe8b --- /dev/null +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpValueTypes.java @@ -0,0 +1,13 @@ +package uk.co.real_logic.sbe.generation.csharp; + +import uk.co.real_logic.sbe.generation.CodeGenerator; +import uk.co.real_logic.sbe.generation.TargetCodeGenerator; +import uk.co.real_logic.sbe.ir.Ir; + +public class CSharpValueTypes implements TargetCodeGenerator +{ + public CodeGenerator newInstance(final Ir ir, final String outputDir) + { + return new CSharpStructGenerator(ir, new CSharpNamespaceOutputManager(outputDir, ir.applicableNamespace())); + } +} From 0ec52341011baafdedab2fe4a88a53e99f89df12 Mon Sep 17 00:00:00 2001 From: James Gate Date: Wed, 13 May 2020 20:52:10 +0100 Subject: [PATCH 2/7] Revert "Added a rough outline of my C# value type encoder/decoder." This reverts commit b34653fc1d32dc411ad8d4a870d596d99922cd84. --- .../csharp/CSharpStructGenerator.java | 137 ------------------ .../generation/csharp/CSharpValueTypes.java | 13 -- 2 files changed, 150 deletions(-) delete mode 100644 sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpStructGenerator.java delete mode 100644 sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpValueTypes.java diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpStructGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpStructGenerator.java deleted file mode 100644 index de3d469a04..0000000000 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpStructGenerator.java +++ /dev/null @@ -1,137 +0,0 @@ -package uk.co.real_logic.sbe.generation.csharp; - -import org.agrona.Verify; -import org.agrona.generation.OutputManager; -import uk.co.real_logic.sbe.generation.CodeGenerator; -import uk.co.real_logic.sbe.ir.Ir; -import uk.co.real_logic.sbe.ir.Signal; -import uk.co.real_logic.sbe.ir.Token; - -import java.io.IOException; -import java.io.Writer; -import java.util.List; - -public class CSharpStructGenerator implements CodeGenerator -{ - private static final String INDENT = " "; - - private final Ir ir; - private final OutputManager outputManager; - - public CSharpStructGenerator(final Ir ir, final OutputManager outputManager) - { - Verify.notNull(ir, "ir"); - Verify.notNull(outputManager, "outputManager"); - - this.ir = ir; - this.outputManager = outputManager; - } - - @Override - public void generate() throws IOException - { - writeComposite(ir.headerStructure().tokens()); - } - - public void writeComposite(final List tokens) throws IOException - { - final String compositeName = CSharpUtil.formatClassName(tokens.get(0).applicableTypeName()); - final String structName = compositeName + "Value"; - - try (Writer out = outputManager.createOutput(structName)) - { - out.append(generateFileHeader(ir.packageName())); - out.append(generateStructDeclaration(structName, compositeName)); - out.append(generateStructConstructor(structName, tokens, INDENT + INDENT)); - out.append(generateStructMembers(tokens, INDENT + INDENT)); - out.append(INDENT + "}\n"); - out.append("}\n"); - } - } - - private CharSequence generateFileHeader(final String packageName) - { - return String.format( - "/* Generated SBE (Simple Binary Encoding) message codec */\n\n" + - "using System;\n" + - "using System.Runtime.InteropServices;\n\n" + - "namespace %s\n" + - "{\n", packageName); - } - - private CharSequence generateStructDeclaration(final String structName, final String compositeName) - { - return String.format( - INDENT + "[StructLayout(LayoutKind.Explicit, Size = %s.Size)]\n" + - INDENT + "public struct %s\n" + - INDENT + "{\n", - compositeName, structName); - } - - private CharSequence generateStructConstructor(final String structName, final List tokens, - final String baseIndent) - { - final StringBuilder sb = new StringBuilder(); - - sb.append(baseIndent); - sb.append("public "); - sb.append(structName); - sb.append("("); - - boolean firstTokenDone = false; - - for (final Token token : tokens) - { - switch (token.signal()) - { - case ENCODING: - if (firstTokenDone) - { - sb.append(","); - } - sb.append("\n"); - sb.append(baseIndent + INDENT + CSharpUtil.cSharpTypeName(token.encoding().primitiveType())); - sb.append(" "); - sb.append(token.name()); - firstTokenDone = true; - break; - } - } - - sb.append(")\n"); - sb.append(baseIndent + "{\n"); // start of constructor body - - // Generate field assignments - for (final Token token : tokens) - { - if (token.signal() == Signal.ENCODING) - { - sb.append(String.format(baseIndent + INDENT + "%s = %s;\n", - CSharpUtil.toUpperFirstChar(token.name()), token.name())); - } - } - - sb.append(baseIndent + "}\n"); // end of constructor - - return sb; - } - - private CharSequence generateStructMembers(final List tokens, final String baseIndent) - { - final StringBuilder sb = new StringBuilder(); - - for (final Token token : tokens) - { - if (token.signal() == Signal.ENCODING) - { - sb.append(String.format("\n" + baseIndent + "[FieldOffset(%s)]\n", token.offset())); - sb.append(String.format(baseIndent + "public readonly %s %s;\n", - CSharpUtil.cSharpTypeName(token.encoding().primitiveType()), - CSharpUtil.toUpperFirstChar(token.name()))); - } - } - - return sb; - } - -} diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpValueTypes.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpValueTypes.java deleted file mode 100644 index ef6242fe8b..0000000000 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpValueTypes.java +++ /dev/null @@ -1,13 +0,0 @@ -package uk.co.real_logic.sbe.generation.csharp; - -import uk.co.real_logic.sbe.generation.CodeGenerator; -import uk.co.real_logic.sbe.generation.TargetCodeGenerator; -import uk.co.real_logic.sbe.ir.Ir; - -public class CSharpValueTypes implements TargetCodeGenerator -{ - public CodeGenerator newInstance(final Ir ir, final String outputDir) - { - return new CSharpStructGenerator(ir, new CSharpNamespaceOutputManager(outputDir, ir.applicableNamespace())); - } -} From 989a9782a0ae9dc66dbe64147da87fb962e969e3 Mon Sep 17 00:00:00 2001 From: James Gate Date: Wed, 13 May 2020 22:46:32 +0100 Subject: [PATCH 3/7] Expose an array property as a span --- .../sbe/generation/csharp/CSharpGenerator.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java index db16531c08..54b27d878e 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java @@ -889,6 +889,17 @@ private CharSequence generateArrayProperty( generateDocumentation(indent, fieldToken), propName, typeName, fieldLength, typePrefix, offset, typeSize, byteOrderStr)); + // Expose the array as a Span + sb.append(String.format("\n" + + "%1$s" + + indent + "public ReadOnlySpan<%2$s> %3$s\n" + + indent + "{\n" + + indent + INDENT + "get => _buffer.AsReadOnlySpan(_offset + %4$s, InstanceLength);\n" + + indent + INDENT + "set => value.CopyTo(_buffer.AsSpan(_offset + %4$s, InstanceLength));\n" + + indent + "}\n", + generateDocumentation(indent, fieldToken), + typeName, propName, offset)); + if (typeToken.encoding().primitiveType() == PrimitiveType.CHAR) { generateCharacterEncodingMethod(sb, propertyName, typeToken.encoding().characterEncoding(), indent); From c98913943fbd9781dcc8812846cbb9b5ec0b4c54 Mon Sep 17 00:00:00 2001 From: James Gate Date: Wed, 13 May 2020 23:46:39 +0100 Subject: [PATCH 4/7] Get the Length property right --- .../co/real_logic/sbe/generation/csharp/CSharpGenerator.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java index 54b27d878e..7641a1ba87 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java @@ -894,8 +894,8 @@ private CharSequence generateArrayProperty( "%1$s" + indent + "public ReadOnlySpan<%2$s> %3$s\n" + indent + "{\n" + - indent + INDENT + "get => _buffer.AsReadOnlySpan(_offset + %4$s, InstanceLength);\n" + - indent + INDENT + "set => value.CopyTo(_buffer.AsSpan(_offset + %4$s, InstanceLength));\n" + + indent + INDENT + "get => _buffer.AsReadOnlySpan(_offset + %4$s, %3$sLength);\n" + + indent + INDENT + "set => value.CopyTo(_buffer.AsSpan(_offset + %4$s, %3$sLength));\n" + indent + "}\n", generateDocumentation(indent, fieldToken), typeName, propName, offset)); From a7b37daf1d590ad9b160162d8026f9ba90607503 Mon Sep 17 00:00:00 2001 From: James Gate Date: Wed, 13 May 2020 23:50:27 +0100 Subject: [PATCH 5/7] Get the type right --- .../co/real_logic/sbe/generation/csharp/CSharpGenerator.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java index 7641a1ba87..4aa8868d98 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java @@ -894,8 +894,8 @@ private CharSequence generateArrayProperty( "%1$s" + indent + "public ReadOnlySpan<%2$s> %3$s\n" + indent + "{\n" + - indent + INDENT + "get => _buffer.AsReadOnlySpan(_offset + %4$s, %3$sLength);\n" + - indent + INDENT + "set => value.CopyTo(_buffer.AsSpan(_offset + %4$s, %3$sLength));\n" + + indent + INDENT + "get => _buffer.AsReadOnlySpan<%2$s>(_offset + %4$s, %3$sLength);\n" + + indent + INDENT + "set => value.CopyTo(_buffer.AsSpan<%2$s>(_offset + %4$s, %3$sLength));\n" + indent + "}\n", generateDocumentation(indent, fieldToken), typeName, propName, offset)); From 7881a8a1bd6ad913e1a88d0fb93f84ac18900026 Mon Sep 17 00:00:00 2001 From: James Gate Date: Thu, 14 May 2020 12:37:55 +0100 Subject: [PATCH 6/7] Add field-as-span method --- .../sbe/generation/csharp/CSharpGenerator.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java index 4aa8868d98..e2efd2decd 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java @@ -889,7 +889,7 @@ private CharSequence generateArrayProperty( generateDocumentation(indent, fieldToken), propName, typeName, fieldLength, typePrefix, offset, typeSize, byteOrderStr)); - // Expose the array as a Span + // Expose the array as a ReadOnlySpan sb.append(String.format("\n" + "%1$s" + indent + "public ReadOnlySpan<%2$s> %3$s\n" + @@ -900,6 +900,16 @@ private CharSequence generateArrayProperty( generateDocumentation(indent, fieldToken), typeName, propName, offset)); + // Expose the array as a Span + sb.append(String.format("\n" + + "%1$s" + + indent + "public Span<%2$s> %3$sAsSpan()\n" + + indent + "{\n" + + indent + INDENT + "return _buffer.AsSpan(_offset + %4$s, %3$sLength);\n" + + indent + "}\n", + generateDocumentation(indent, fieldToken), + typeName, propName, offset)); + if (typeToken.encoding().primitiveType() == PrimitiveType.CHAR) { generateCharacterEncodingMethod(sb, propertyName, typeToken.encoding().characterEncoding(), indent); From fc08ce3009693f35de7c65a5b87383633e3566ad Mon Sep 17 00:00:00 2001 From: James Gate Date: Thu, 14 May 2020 12:44:30 +0100 Subject: [PATCH 7/7] Set the type correctly --- .../uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java index e2efd2decd..8de581714c 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpGenerator.java @@ -905,7 +905,7 @@ private CharSequence generateArrayProperty( "%1$s" + indent + "public Span<%2$s> %3$sAsSpan()\n" + indent + "{\n" + - indent + INDENT + "return _buffer.AsSpan(_offset + %4$s, %3$sLength);\n" + + indent + INDENT + "return _buffer.AsSpan<%2$s>(_offset + %4$s, %3$sLength);\n" + indent + "}\n", generateDocumentation(indent, fieldToken), typeName, propName, offset));