From 27b9188be6d1b24c744b318a849e85de11902df7 Mon Sep 17 00:00:00 2001 From: Rob Purdy Date: Wed, 5 May 2021 21:53:41 -0700 Subject: [PATCH 01/11] [C#] Add more string reading/writing methods to DirectBuffer More, hopefully efficient, mechanisms to read and write strings to and from the DirectBuffer. --- csharp/csharp.sln | 4 +- csharp/sbe-benchmarks/app.config | 2 +- csharp/sbe-benchmarks/sbe-benchmarks.csproj | 2 +- csharp/sbe-dll/DirectBuffer.cs | 122 ++++++++++++++++++ csharp/sbe-dll/sbe-dll.csproj | 4 +- csharp/sbe-generated/sbe-generated.csproj | 2 +- csharp/sbe-samples-car/sbe-samples-car.csproj | 2 +- .../sbe-samples-extension.csproj | 2 +- csharp/sbe-tests/DirectBufferTests.cs | 117 +++++++++++++++++ csharp/sbe-tests/sbe-tests.csproj | 2 +- .../generation/csharp/CSharpGenerator.java | 43 +++++- 11 files changed, 291 insertions(+), 11 deletions(-) diff --git a/csharp/csharp.sln b/csharp/csharp.sln index 2567baba19..e37e5efa35 100644 --- a/csharp/csharp.sln +++ b/csharp/csharp.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26430.6 +# Visual Studio 16 +VisualStudioVersion = 16.0.28803.452 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "sbe-dll", "sbe-dll\sbe-dll.csproj", "{CE883FD9-14A5-409C-AEBC-86BEA4E3C502}" EndProject diff --git a/csharp/sbe-benchmarks/app.config b/csharp/sbe-benchmarks/app.config index 51278a4563..3e0e37cfc8 100644 --- a/csharp/sbe-benchmarks/app.config +++ b/csharp/sbe-benchmarks/app.config @@ -1,3 +1,3 @@ - + diff --git a/csharp/sbe-benchmarks/sbe-benchmarks.csproj b/csharp/sbe-benchmarks/sbe-benchmarks.csproj index 8d10e86c4f..2dcc1d2bbf 100644 --- a/csharp/sbe-benchmarks/sbe-benchmarks.csproj +++ b/csharp/sbe-benchmarks/sbe-benchmarks.csproj @@ -1,7 +1,7 @@  - net461;netcoreapp3.1 + net48;netcoreapp3.1 Org.SbeTool.Sbe.Benchmarks sbe-benchmarks exe diff --git a/csharp/sbe-dll/DirectBuffer.cs b/csharp/sbe-dll/DirectBuffer.cs index e8ef4c012e..b5e6377488 100644 --- a/csharp/sbe-dll/DirectBuffer.cs +++ b/csharp/sbe-dll/DirectBuffer.cs @@ -1,5 +1,6 @@ using System; using System.Runtime.InteropServices; +using System.Text; namespace Org.SbeTool.Sbe.Dll { @@ -654,6 +655,127 @@ public int SetBytes(int index, ReadOnlySpan src) return count; } + /// + /// Writes a into the underlying buffer. + /// + public unsafe int SetBytesFromString(Encoding encoding, string src, int index) + { + + int available = _capacity - index; + + int byteCount = encoding.GetByteCount(src); + + if (byteCount > available) + { + ThrowHelper.ThrowIndexOutOfRangeException(_capacity); + } + fixed (char* ptr = src) + { + encoding.GetBytes(ptr, src.Length, _pBuffer + index, byteCount); + } + return byteCount; + } + + /// + /// Writes a into the underlying buffer. + /// + public unsafe string GetStringFromBytes(Encoding encoding, int index, int byteCount) + { + int num = _capacity - index; + if (byteCount > num) + { + ThrowHelper.ThrowIndexOutOfRangeException(_capacity); + } + + return encoding.GetString(_pBuffer + index, byteCount); + } + + /// + /// Reads a number of bytes from the buffer to create a string. + /// + /// The text encoding to use for string creation + /// index in the underlying buffer to start from + /// The maximum number of bytes to read + /// The null value for this string + /// The created string + public string GetString(Encoding encoding, int index, int length, byte nullByte) + { + int count = Math.Min(length, _capacity - index); + if (count <= 0) + ThrowHelper.ThrowIndexOutOfRangeException(index); + if (*(_pBuffer + index) == nullByte) + return null; + else + return new String((sbyte*) _pBuffer, index, length, encoding); + } + + /// + /// Reads a number of bytes from the buffer to create a string, truncating the string if a null byte is found + /// + /// The text encoding to use for string creation + /// index in the underlying buffer to start from + /// The maximum number of bytes to read + /// The null value for this string + /// The created string + public unsafe string GetStringFromNullTerminatedBytes(Encoding encoding, int index, int maxLength, byte nullByte) + { + int count = Math.Min(maxLength, _capacity - index); + if (count <= 0) + { + ThrowHelper.ThrowIndexOutOfRangeException(index); + } + if (_pBuffer[index] == nullByte) + { + return null; + } + int byteCount2 = 0; + for (byteCount2 = 0; byteCount2 < count && _pBuffer[byteCount2] != nullByte; byteCount2++) + { + } + return encoding.GetString(_pBuffer + index, byteCount2); + + // return new string((sbyte*) _pBuffer, index, byteCount2, encoding); + } + + /// + /// Writes a number of bytes into the buffer using the given encoding and string. + /// Note that pre-computing the bytes to be written, when possible, will be quicker than using the encoder to convert on-the-fly. + /// + /// The text encoding to use + /// String to write + /// + /// Maximum number of bytes to write + /// + /// The number of bytes written. + public unsafe int SetNullTerminatedBytesFromString(Encoding encoding, string src, int index, int maxLength, byte nullByte) + { + int available = Math.Min(maxLength, _capacity - index); + if (available <= 0) + { + ThrowHelper.ThrowIndexOutOfRangeException(index); + } + if (src == null) + { + _pBuffer[index] = nullByte; + return 1; + } + int byteCount = encoding.GetByteCount(src); + if (byteCount > available) + { + ThrowHelper.ThrowIndexOutOfRangeException(index + available); + } + fixed (char* ptr = src) + { + encoding.GetBytes(ptr, src.Length, _pBuffer + index, byteCount); + } + if (byteCount < available) + { + *(_pBuffer + index + byteCount) = nullByte; + return byteCount + 1; + } + return byteCount; + } + /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// diff --git a/csharp/sbe-dll/sbe-dll.csproj b/csharp/sbe-dll/sbe-dll.csproj index 97571f1ae3..1dd9f03a51 100644 --- a/csharp/sbe-dll/sbe-dll.csproj +++ b/csharp/sbe-dll/sbe-dll.csproj @@ -1,6 +1,6 @@  - net45;netstandard2.0 + net48;netstandard2.0 True SBE Org.SbeTool.Sbe.Dll @@ -37,7 +37,7 @@ - + diff --git a/csharp/sbe-generated/sbe-generated.csproj b/csharp/sbe-generated/sbe-generated.csproj index 6b4fc7bdbc..e35f147513 100644 --- a/csharp/sbe-generated/sbe-generated.csproj +++ b/csharp/sbe-generated/sbe-generated.csproj @@ -1,7 +1,7 @@  - net45;netstandard2.0 + net48;netstandard2.0 Org.SbeTool.Sbe.Generated Org.SbeTool.Sbe.Generated Copyright (C) Bill Segall 2018, MarketFactory Inc 2017, Adaptive 2014. All rights reserved. diff --git a/csharp/sbe-samples-car/sbe-samples-car.csproj b/csharp/sbe-samples-car/sbe-samples-car.csproj index 1ed09464dd..49388905b8 100644 --- a/csharp/sbe-samples-car/sbe-samples-car.csproj +++ b/csharp/sbe-samples-car/sbe-samples-car.csproj @@ -1,7 +1,7 @@  - net45;netcoreapp3.1 + net48;netcoreapp3.1 exe Org.SbeTool.Sbe.Example.Car Org.SbeTool.Sbe.Example.Car diff --git a/csharp/sbe-samples-extension/sbe-samples-extension.csproj b/csharp/sbe-samples-extension/sbe-samples-extension.csproj index 97899b3aa4..f17e00caa2 100644 --- a/csharp/sbe-samples-extension/sbe-samples-extension.csproj +++ b/csharp/sbe-samples-extension/sbe-samples-extension.csproj @@ -1,7 +1,7 @@  - net45;netstandard2.0 + net48;netstandard2.0 exe Org.SbeTool.Sbe.Example.Extension Org.SbeTool.Sbe.Example.Extension diff --git a/csharp/sbe-tests/DirectBufferTests.cs b/csharp/sbe-tests/DirectBufferTests.cs index c42e87ddaf..1324625dda 100644 --- a/csharp/sbe-tests/DirectBufferTests.cs +++ b/csharp/sbe-tests/DirectBufferTests.cs @@ -13,6 +13,7 @@ // limitations under the License. using System; +using System.Text; using System.Runtime.InteropServices; using Microsoft.VisualStudio.TestTools.UnitTesting; using Org.SbeTool.Sbe.Dll; @@ -620,5 +621,121 @@ public void ShouldGetDoubleBigEndian() } #endregion + + #region NullTerminatedBytesFromString + const byte Terminator = (byte)0; + readonly static Encoding AsciiEncoding = Encoding.ASCII; + + + [TestMethod] + public void ShouldPutStringWithNullTermination() + { + // the string length is less than the max and less than the capacity - should add null terminator + const string value = "abc123"; + const int index = 0; + var written = _directBuffer.SetNullTerminatedBytesFromString(AsciiEncoding, value, index, 100, Terminator); + var read = new byte[written]; + var numBytesWritten = _directBuffer.GetBytes(index, read, 0, written); + Assert.AreEqual(7, numBytesWritten); + Assert.AreEqual(Terminator, read[read.Length - 1]); // should be terminated with the null value + string result = AsciiEncoding.GetString(read); + Assert.AreEqual(value, result.Substring(0, read.Length - 1)); + } + + [TestMethod] + public void ShouldPutStringWitoutNullTerminator() + { + // the string length is equal to the max legth + const string value = "abc123"; + const int index = 0; + var written = _directBuffer.SetNullTerminatedBytesFromString(AsciiEncoding, value, index, value.Length, Terminator); + var read = new byte[written]; + var numBytesWritten = _directBuffer.GetBytes(index, read, 0, written); + Assert.AreEqual(6, numBytesWritten); + Assert.AreNotEqual(Terminator, read[read.Length - 1]); // should NOT be terminated with the null value + string result = AsciiEncoding.GetString(read); + Assert.AreEqual(value, result.Substring(0, read.Length)); + } + + [TestMethod] + public void ShouldPutStringWitoutNullTerminatorIfBytesToWriteEqualsRemainingCapacity() + { + // the string length is equal to the max legth + const string value = "abc123"; + int index = _directBuffer.Capacity - value.Length; + var written = _directBuffer.SetNullTerminatedBytesFromString(AsciiEncoding, value, index, 100, Terminator); + var read = new byte[written]; + var numBytesWritten = _directBuffer.GetBytes(index, read, 0, written); + Assert.AreEqual(6, numBytesWritten); + Assert.AreNotEqual(Terminator, read[read.Length - 1]); // should NOT be terminated with the null value + string result = AsciiEncoding.GetString(read); + Assert.AreEqual(value, result.Substring(0, read.Length)); + } + + + [TestMethod] + public void ShouldThrowIndexOutOfRangeExceptionWhenStringLengthGreaterThanMax() + { + const string value = "abc123"; + const int index = 0; + int maxLength = value.Length - 1; // max length is less than the value length + Assert.ThrowsException(() => _directBuffer.SetNullTerminatedBytesFromString(AsciiEncoding, value, index, maxLength, Terminator)); + } + + + [TestMethod] + public void ShouldThrowIndexOutOfRangeExceptionWhenStringLengthGreaterThanCapacity() + { + const string value = "abc123"; + int index = _directBuffer.Capacity - value.Length + 1; // remaining capacity will be too short to write the string bytes + Assert.ThrowsException(() => _directBuffer.SetNullTerminatedBytesFromString(AsciiEncoding, value, index, 100, Terminator)); + } + + + [TestMethod] + public void ShouldPutNullStringNullTerminated() + { + const string value = null; + + const int index = 0; + var written = _directBuffer.SetNullTerminatedBytesFromString(AsciiEncoding, value, index, 100, Terminator); + var read = new byte[written]; + var len = _directBuffer.GetBytes(index, read, 0, written); + Assert.AreEqual(1, len); + Assert.AreEqual(Terminator, read[read.Length - 1]); + } + + [TestMethod] + public void ShouldPutEmptyStringNullTerminated() + { + const string value = ""; + + const int index = 0; + var written = _directBuffer.SetNullTerminatedBytesFromString(AsciiEncoding, value, index, 100, Terminator); + var read = new byte[written]; + var len = _directBuffer.GetBytes(index, read, 0, written); + Assert.AreEqual(1, len); + Assert.AreEqual(Terminator, read[read.Length - 1]); + } + + + [TestMethod] + public void ShouldGetStringNullTerminated() + { + var encoding = System.Text.Encoding.ASCII; + const string value = "abc123"; + var bytes = encoding.GetBytes(value); + const int index = 0; + var written = _directBuffer.SetBytes(index, bytes, 0, bytes.Length); + Assert.AreEqual(bytes.Length, written); + var written2 = _directBuffer.SetBytes(index + bytes.Length, new byte[] { (byte)0 }, 0, 1); + Assert.AreEqual(1, written2); + string result = _directBuffer.GetStringFromNullTerminatedBytes(encoding, index, _directBuffer.Capacity - index, (byte)0); + Assert.AreEqual(result, value); + } + + #endregion } + + } diff --git a/csharp/sbe-tests/sbe-tests.csproj b/csharp/sbe-tests/sbe-tests.csproj index 468a1ab8ba..57a41f294e 100644 --- a/csharp/sbe-tests/sbe-tests.csproj +++ b/csharp/sbe-tests/sbe-tests.csproj @@ -1,6 +1,6 @@  - net45;netcoreapp3.1 + net48;netcoreapp3.1 Org.SbeTool.Sbe.Tests Org.SbeTool.Sbe.UnitTests True 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 3a0fbba366..5e56e1d8d2 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 @@ -30,6 +30,8 @@ import java.util.ArrayList; import java.util.List; +import static java.lang.System.lineSeparator; + import static uk.co.real_logic.sbe.generation.Generators.toLowerFirstChar; import static uk.co.real_logic.sbe.generation.Generators.toUpperFirstChar; import static uk.co.real_logic.sbe.generation.csharp.CSharpUtil.*; @@ -478,6 +480,29 @@ private CharSequence generateVarData(final List tokens, final String inde lengthTypePrefix, lengthCSharpType, byteOrderStr)); + + sb.append(lineSeparator()) + .append(String.format( + indent + "public string Get%1$s()\n" + + indent + "{\n" + + indent + INDENT + "const int sizeOfLengthField = %2$d;\n" + + indent + INDENT + "int limit = _parentMessage.Limit;\n" + + indent + INDENT + "_buffer.CheckLimit(limit + sizeOfLengthField);\n" + + indent + INDENT + "int dataLength = (int)_buffer.%3$sGet%4$s(limit);\n" + + indent + INDENT + "return _buffer.GetStringFromBytes(%1$sResolvedCharacterEncoding," + + " limit + sizeOfLengthField, dataLength);\n" + + indent + "}\n\n" + + indent + "public void Set%1$s(string value)\n" + + indent + "{\n" + + indent + INDENT + "var encoding = %1$sResolvedCharacterEncoding;\n" + + indent + INDENT + "const int sizeOfLengthField = %2$d;\n" + + indent + INDENT + "int limit = _parentMessage.Limit;\n" + + indent + INDENT + "int byteCount = _buffer.SetBytesFromString(encoding, value, " + + "limit + sizeOfLengthField);\n" + + indent + INDENT + "_parentMessage.Limit = limit + sizeOfLengthField + byteCount;\n" + + indent + INDENT + "_buffer.%3$sPut%4$s(limit, (ushort)byteCount);\n" + + indent + "}\n", + propertyName, sizeOfLengthField, lengthTypePrefix, byteOrderStr)); } } @@ -638,6 +663,7 @@ private CharSequence generateFileHeader(final String packageName) "// \n\n" + "#pragma warning disable 1591 // disable warning on missing comments\n" + "using System;\n" + + "using System.Text;\n" + "using Org.SbeTool.Sbe.Dll;\n\n" + "namespace %s\n" + "{\n", @@ -978,6 +1004,19 @@ private CharSequence generateArrayProperty( indent + INDENT + "_buffer.SetBytes(_offset + %3$d, src);\n" + indent + "}\n", propName, fieldLength, offset)); + + sb.append(String.format("\n" + + indent + "public void Set%1$s(string value)\n" + + indent + "{\n" + + indent + INDENT + "_buffer.SetNullTerminatedBytesFromString(%1$sResolvedCharacterEncoding, " + + "value, _offset + %2$s, %1$sLength, %1$sNullValue);\n" + + indent + "}\n" + + indent + "public string Get%1$s()\n" + + indent + "{\n" + + indent + INDENT + "return _buffer.GetStringFromNullTerminatedBytes(%1$sResolvedCharacterEncoding, " + + "_offset + %2$s, %1$sLength, %1$sNullValue);\n" + + indent + "}\n", + propName, offset)); } return sb; @@ -990,7 +1029,9 @@ private void generateCharacterEncodingMethod( final String indent) { sb.append(String.format("\n" + - indent + "public const string %sCharacterEncoding = \"%s\";\n\n", + indent + "public const string %1$sCharacterEncoding = \"%2$s\";\n" + + indent + "public static Encoding %1$sResolvedCharacterEncoding = " + + "Encoding.GetEncoding(%1$sCharacterEncoding);\n\n", formatPropertyName(propertyName), encoding)); } From 1a8e321f4022f3f0669bd8af39f582d458e9100b Mon Sep 17 00:00:00 2001 From: Rob Purdy Date: Fri, 7 May 2021 09:46:36 -0700 Subject: [PATCH 02/11] [C#] Reverted to .net45 --- csharp/csharp.sln | 4 ++-- csharp/sbe-benchmarks/sbe-benchmarks.csproj | 2 +- csharp/sbe-dll/DirectBuffer.cs | 7 +++---- csharp/sbe-dll/sbe-dll.csproj | 4 ++-- csharp/sbe-generated/sbe-generated.csproj | 2 +- csharp/sbe-samples-car/sbe-samples-car.csproj | 2 +- csharp/sbe-tests/sbe-tests.csproj | 2 +- 7 files changed, 11 insertions(+), 12 deletions(-) diff --git a/csharp/csharp.sln b/csharp/csharp.sln index e37e5efa35..2567baba19 100644 --- a/csharp/csharp.sln +++ b/csharp/csharp.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 16 -VisualStudioVersion = 16.0.28803.452 +# Visual Studio 15 +VisualStudioVersion = 15.0.26430.6 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "sbe-dll", "sbe-dll\sbe-dll.csproj", "{CE883FD9-14A5-409C-AEBC-86BEA4E3C502}" EndProject diff --git a/csharp/sbe-benchmarks/sbe-benchmarks.csproj b/csharp/sbe-benchmarks/sbe-benchmarks.csproj index 2dcc1d2bbf..8d10e86c4f 100644 --- a/csharp/sbe-benchmarks/sbe-benchmarks.csproj +++ b/csharp/sbe-benchmarks/sbe-benchmarks.csproj @@ -1,7 +1,7 @@  - net48;netcoreapp3.1 + net461;netcoreapp3.1 Org.SbeTool.Sbe.Benchmarks sbe-benchmarks exe diff --git a/csharp/sbe-dll/DirectBuffer.cs b/csharp/sbe-dll/DirectBuffer.cs index b5e6377488..575ea558c4 100644 --- a/csharp/sbe-dll/DirectBuffer.cs +++ b/csharp/sbe-dll/DirectBuffer.cs @@ -687,7 +687,7 @@ public unsafe string GetStringFromBytes(Encoding encoding, int index, int byteCo ThrowHelper.ThrowIndexOutOfRangeException(_capacity); } - return encoding.GetString(_pBuffer + index, byteCount); + return new String((sbyte*)_pBuffer, index, byteCount, encoding); // 4.8+ encoding.GetString(_pBuffer + index, byteCount); } /// @@ -732,9 +732,8 @@ public unsafe string GetStringFromNullTerminatedBytes(Encoding encoding, int ind for (byteCount2 = 0; byteCount2 < count && _pBuffer[byteCount2] != nullByte; byteCount2++) { } - return encoding.GetString(_pBuffer + index, byteCount2); - - // return new string((sbyte*) _pBuffer, index, byteCount2, encoding); + // return encoding.GetString(_pBuffer + index, byteCount2); // .net48+ + return new String((sbyte*) _pBuffer, index, byteCount2, encoding); } /// diff --git a/csharp/sbe-dll/sbe-dll.csproj b/csharp/sbe-dll/sbe-dll.csproj index 1dd9f03a51..97571f1ae3 100644 --- a/csharp/sbe-dll/sbe-dll.csproj +++ b/csharp/sbe-dll/sbe-dll.csproj @@ -1,6 +1,6 @@  - net48;netstandard2.0 + net45;netstandard2.0 True SBE Org.SbeTool.Sbe.Dll @@ -37,7 +37,7 @@ - + diff --git a/csharp/sbe-generated/sbe-generated.csproj b/csharp/sbe-generated/sbe-generated.csproj index e35f147513..6b4fc7bdbc 100644 --- a/csharp/sbe-generated/sbe-generated.csproj +++ b/csharp/sbe-generated/sbe-generated.csproj @@ -1,7 +1,7 @@  - net48;netstandard2.0 + net45;netstandard2.0 Org.SbeTool.Sbe.Generated Org.SbeTool.Sbe.Generated Copyright (C) Bill Segall 2018, MarketFactory Inc 2017, Adaptive 2014. All rights reserved. diff --git a/csharp/sbe-samples-car/sbe-samples-car.csproj b/csharp/sbe-samples-car/sbe-samples-car.csproj index 49388905b8..1ed09464dd 100644 --- a/csharp/sbe-samples-car/sbe-samples-car.csproj +++ b/csharp/sbe-samples-car/sbe-samples-car.csproj @@ -1,7 +1,7 @@  - net48;netcoreapp3.1 + net45;netcoreapp3.1 exe Org.SbeTool.Sbe.Example.Car Org.SbeTool.Sbe.Example.Car diff --git a/csharp/sbe-tests/sbe-tests.csproj b/csharp/sbe-tests/sbe-tests.csproj index 57a41f294e..468a1ab8ba 100644 --- a/csharp/sbe-tests/sbe-tests.csproj +++ b/csharp/sbe-tests/sbe-tests.csproj @@ -1,6 +1,6 @@  - net48;netcoreapp3.1 + net45;netcoreapp3.1 Org.SbeTool.Sbe.Tests Org.SbeTool.Sbe.UnitTests True From 2b494b02cf722f4c61cfcc2e735b38664df73ac5 Mon Sep 17 00:00:00 2001 From: Rob Purdy Date: Fri, 7 May 2021 10:38:06 -0700 Subject: [PATCH 03/11] [C#] Reverted to .net45 --- csharp/sbe-benchmarks/app.config | 2 +- csharp/sbe-samples-extension/sbe-samples-extension.csproj | 2 +- csharp/sbe-tests/DirectBufferTests.cs | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/csharp/sbe-benchmarks/app.config b/csharp/sbe-benchmarks/app.config index 3e0e37cfc8..51278a4563 100644 --- a/csharp/sbe-benchmarks/app.config +++ b/csharp/sbe-benchmarks/app.config @@ -1,3 +1,3 @@ - + diff --git a/csharp/sbe-samples-extension/sbe-samples-extension.csproj b/csharp/sbe-samples-extension/sbe-samples-extension.csproj index f17e00caa2..97899b3aa4 100644 --- a/csharp/sbe-samples-extension/sbe-samples-extension.csproj +++ b/csharp/sbe-samples-extension/sbe-samples-extension.csproj @@ -1,7 +1,7 @@  - net48;netstandard2.0 + net45;netstandard2.0 exe Org.SbeTool.Sbe.Example.Extension Org.SbeTool.Sbe.Example.Extension diff --git a/csharp/sbe-tests/DirectBufferTests.cs b/csharp/sbe-tests/DirectBufferTests.cs index 1324625dda..473ac0df08 100644 --- a/csharp/sbe-tests/DirectBufferTests.cs +++ b/csharp/sbe-tests/DirectBufferTests.cs @@ -623,8 +623,8 @@ public void ShouldGetDoubleBigEndian() #endregion #region NullTerminatedBytesFromString - const byte Terminator = (byte)0; - readonly static Encoding AsciiEncoding = Encoding.ASCII; + const byte Terminator = (byte)0; + readonly static Encoding AsciiEncoding = Encoding.UTF8; [TestMethod] @@ -657,7 +657,7 @@ public void ShouldPutStringWitoutNullTerminator() Assert.AreEqual(value, result.Substring(0, read.Length)); } - [TestMethod] + [TestMethod] public void ShouldPutStringWitoutNullTerminatorIfBytesToWriteEqualsRemainingCapacity() { // the string length is equal to the max legth From 262d2a3eca66ab24de2cb0d23a88fee7e6be28e4 Mon Sep 17 00:00:00 2001 From: Rob Purdy Date: Fri, 7 May 2021 17:19:47 -0700 Subject: [PATCH 04/11] [C#] Updated Samples to use the new String methods --- csharp/sbe-dll/DirectBuffer.cs | 39 +++++------ csharp/sbe-samples-car/CarExample.cs | 39 +++++------ csharp/sbe-samples-car/sbe-samples-car.csproj | 2 +- csharp/sbe-tests/DirectBufferTests.cs | 64 ++++++++++++++++++- .../generation/csharp/CSharpGenerator.java | 3 +- 5 files changed, 95 insertions(+), 52 deletions(-) diff --git a/csharp/sbe-dll/DirectBuffer.cs b/csharp/sbe-dll/DirectBuffer.cs index 575ea558c4..9c5bbb165f 100644 --- a/csharp/sbe-dll/DirectBuffer.cs +++ b/csharp/sbe-dll/DirectBuffer.cs @@ -656,8 +656,13 @@ public int SetBytes(int index, ReadOnlySpan src) } /// - /// Writes a into the underlying buffer. + /// Writes a string into the underlying buffer, encoding using the provided . + /// If there is not enough room in the buffer for the bytes it will throw IndexOutOfRangeExcpetion. /// + /// encoding to use to write the bytes from the string + /// source string + /// index in theunderlying buffer to start writing bytes + /// count of bytes written public unsafe int SetBytesFromString(Encoding encoding, string src, int index) { @@ -677,8 +682,14 @@ public unsafe int SetBytesFromString(Encoding encoding, string src, int index) } /// - /// Writes a into the underlying buffer. + /// Reads a string from the buffer; converting the bytes to characters using + /// the provided . + /// If there are not enough bytes in the buffer it will throw an IndexOutOfRangeException. /// + /// encoding to use to convert the bytes into characters + /// index in theunderlying buffer to start writing bytes + /// the number of bytes to read into the string + /// the string representing the decoded bytes read from the buffer public unsafe string GetStringFromBytes(Encoding encoding, int index, int byteCount) { int num = _capacity - index; @@ -687,26 +698,7 @@ public unsafe string GetStringFromBytes(Encoding encoding, int index, int byteCo ThrowHelper.ThrowIndexOutOfRangeException(_capacity); } - return new String((sbyte*)_pBuffer, index, byteCount, encoding); // 4.8+ encoding.GetString(_pBuffer + index, byteCount); - } - - /// - /// Reads a number of bytes from the buffer to create a string. - /// - /// The text encoding to use for string creation - /// index in the underlying buffer to start from - /// The maximum number of bytes to read - /// The null value for this string - /// The created string - public string GetString(Encoding encoding, int index, int length, byte nullByte) - { - int count = Math.Min(length, _capacity - index); - if (count <= 0) - ThrowHelper.ThrowIndexOutOfRangeException(index); - if (*(_pBuffer + index) == nullByte) - return null; - else - return new String((sbyte*) _pBuffer, index, length, encoding); + return new String((sbyte*)_pBuffer, index, byteCount, encoding); } /// @@ -729,10 +721,9 @@ public unsafe string GetStringFromNullTerminatedBytes(Encoding encoding, int ind return null; } int byteCount2 = 0; - for (byteCount2 = 0; byteCount2 < count && _pBuffer[byteCount2] != nullByte; byteCount2++) + for (byteCount2 = 0; byteCount2 < count && _pBuffer[byteCount2 + index] != nullByte; byteCount2++) { } - // return encoding.GetString(_pBuffer + index, byteCount2); // .net48+ return new String((sbyte*) _pBuffer, index, byteCount2, encoding); } diff --git a/csharp/sbe-samples-car/CarExample.cs b/csharp/sbe-samples-car/CarExample.cs index 287af0ba04..3a51103d8a 100644 --- a/csharp/sbe-samples-car/CarExample.cs +++ b/csharp/sbe-samples-car/CarExample.cs @@ -65,23 +65,20 @@ namespace Baseline { public static class CarExample { - private static readonly byte[] VehicleCode; private static readonly byte[] ManufacturerCode; - private static readonly byte[] Manufacturer; - private static readonly byte[] Model; private static readonly byte[] ActivationCode; + private static readonly string VehicleCode = "abcdef"; + private static readonly string Model = "Civic VTi"; + private static readonly string Manufacturer = "Honda"; static CarExample() { try { // convert some sample strings to the correct encoding for this sample - VehicleCode = Encoding.GetEncoding(Car.VehicleCodeCharacterEncoding).GetBytes("abcdef"); - ManufacturerCode = Encoding.GetEncoding(Engine.ManufacturerCodeCharacterEncoding).GetBytes("123"); - Manufacturer = Encoding.GetEncoding(Car.ManufacturerCharacterEncoding).GetBytes("Honda"); - Model = Encoding.GetEncoding(Car.ModelCharacterEncoding).GetBytes("Civic VTi"); - ActivationCode = Encoding.GetEncoding(Car.ActivationCodeCharacterEncoding).GetBytes("abcdef"); + ManufacturerCode = Engine.ManufacturerCodeResolvedCharacterEncoding.GetBytes("123"); + ActivationCode = Car.ActivationCodeResolvedCharacterEncoding.GetBytes("abcdef"); } catch (Exception ex) { @@ -99,7 +96,7 @@ public static int Encode(Car car, DirectBuffer directBuffer, int bufferOffset) car.ModelYear = 2013; car.Available = BooleanType.T; // enums are directly supported car.Code = Baseline.Model.A; - car.SetVehicleCode(VehicleCode, srcOffset); // we set a constant string + car.SetVehicleCode(VehicleCode); // we set a constant string for (int i = 0, size = Car.SomeNumbersLength; i < size; i++) { @@ -122,17 +119,17 @@ public static int Encode(Car car, DirectBuffer directBuffer, int bufferOffset) fuelFigures.Next(); // move to the first element fuelFigures.Speed = 30; fuelFigures.Mpg = 35.9f; - fuelFigures.SetUsageDescription(Encoding.GetEncoding(Car.FuelFiguresGroup.UsageDescriptionCharacterEncoding).GetBytes("Urban Cycle")); + fuelFigures.SetUsageDescription("Urban Cycle"); fuelFigures.Next(); // second fuelFigures.Speed = 55; fuelFigures.Mpg = 49.0f; - fuelFigures.SetUsageDescription(Encoding.GetEncoding(Car.FuelFiguresGroup.UsageDescriptionCharacterEncoding).GetBytes("Combined Cycle")); + fuelFigures.SetUsageDescription("Combined Cycle"); fuelFigures.Next(); fuelFigures.Speed = 75; fuelFigures.Mpg = 40.0f; - fuelFigures.SetUsageDescription(Encoding.GetEncoding(Car.FuelFiguresGroup.UsageDescriptionCharacterEncoding).GetBytes("Highway Cycle")); + fuelFigures.SetUsageDescription("Highway Cycle"); Car.PerformanceFiguresGroup perfFigures = car.PerformanceFiguresCount(2); // demonstrates how to create a nested group @@ -168,8 +165,8 @@ public static int Encode(Car car, DirectBuffer directBuffer, int bufferOffset) // once we have written all the repeatable groups we can write the variable length properties (you would use that for strings, byte[], etc) - car.SetManufacturer(Manufacturer, srcOffset, Manufacturer.Length); - car.SetModel(Model, srcOffset, Model.Length); + car.SetManufacturer(Manufacturer); + car.SetModel(Model); car.SetActivationCode(ActivationCode, srcOffset, ActivationCode.Length); return car.Size; @@ -202,9 +199,7 @@ public static void Decode(Car car, } sb.Append("\ncar.vehicleCode="); - var vehicleCode = new byte[Car.VehicleCodeLength]; - car.GetVehicleCode(vehicleCode, 0); - sb.Append(Encoding.GetEncoding(Car.VehicleCodeCharacterEncoding).GetString(vehicleCode, 0, Car.VehicleCodeLength)); + sb.Append(car.GetVehicleCode()); OptionalExtras extras = car.Extras; sb.Append("\ncar.extras.cruiseControl=").Append((extras & OptionalExtras.CruiseControl) == OptionalExtras.CruiseControl); // this is how you can find out if a specific flag is set in a flag enum @@ -237,7 +232,7 @@ public static void Decode(Car car, var fuelFigures = fuelFiguresGroup.Next(); sb.Append("\ncar.fuelFigures.speed=").Append(fuelFigures.Speed); sb.Append("\ncar.fuelFigures.mpg=").Append(fuelFigures.Mpg); - sb.Append("\ncar.fuelFigures.UsageDescription=").Append(Encoding.GetEncoding(Car.FuelFiguresGroup.UsageDescriptionCharacterEncoding).GetString(fuelFigures.GetUsageDescriptionBytes())); + sb.Append("\ncar.fuelFigures.UsageDescription=").Append(fuelFigures.GetUsageDescription()); } // The second way to access a repeating group is to use an iterator @@ -258,13 +253,11 @@ public static void Decode(Car car, // variable length fields sb.Append("\ncar.manufacturer.semanticType=").Append(Car.ManufacturerMetaAttribute(MetaAttribute.SemanticType)); length = car.GetManufacturer(buffer, 0, buffer.Length); - sb.Append("\ncar.manufacturer=").Append(Encoding.GetEncoding(Car.ManufacturerCharacterEncoding).GetString(buffer, 0, length)); + sb.Append("\ncar.manufacturer=").Append(Car.ManufacturerResolvedCharacterEncoding.GetString(buffer, 0, length)); - length = car.GetModel(buffer, 0, buffer.Length); - sb.Append("\ncar.model=").Append(Encoding.GetEncoding(Car.ModelCharacterEncoding).GetString(buffer, 0, length)); + sb.Append("\ncar.model=").Append(car.GetModel()); - length = car.GetActivationCode(buffer, 0, buffer.Length); - sb.Append("\ncar.activationcode=").Append(Encoding.GetEncoding(Car.ActivationCodeCharacterEncoding).GetString(buffer, 0, length)); + sb.Append("\ncar.activationcode=").Append(car.GetActivationCode()); sb.Append("\ncar.size=").Append(car.Size); diff --git a/csharp/sbe-samples-car/sbe-samples-car.csproj b/csharp/sbe-samples-car/sbe-samples-car.csproj index 1ed09464dd..8ee29c2329 100644 --- a/csharp/sbe-samples-car/sbe-samples-car.csproj +++ b/csharp/sbe-samples-car/sbe-samples-car.csproj @@ -2,7 +2,7 @@ net45;netcoreapp3.1 - exe + exe Org.SbeTool.Sbe.Example.Car Org.SbeTool.Sbe.Example.Car diff --git a/csharp/sbe-tests/DirectBufferTests.cs b/csharp/sbe-tests/DirectBufferTests.cs index 473ac0df08..ccc04f63ec 100644 --- a/csharp/sbe-tests/DirectBufferTests.cs +++ b/csharp/sbe-tests/DirectBufferTests.cs @@ -728,10 +728,68 @@ public void ShouldGetStringNullTerminated() const int index = 0; var written = _directBuffer.SetBytes(index, bytes, 0, bytes.Length); Assert.AreEqual(bytes.Length, written); - var written2 = _directBuffer.SetBytes(index + bytes.Length, new byte[] { (byte)0 }, 0, 1); + var written2 = _directBuffer.SetBytes(index + bytes.Length, new byte[] { Terminator }, 0, 1); Assert.AreEqual(1, written2); - string result = _directBuffer.GetStringFromNullTerminatedBytes(encoding, index, _directBuffer.Capacity - index, (byte)0); - Assert.AreEqual(result, value); + string result = _directBuffer.GetStringFromNullTerminatedBytes(encoding, index, _directBuffer.Capacity - index, Terminator); + Assert.AreEqual(value, result); + } + + [TestMethod] + public void ShouldGetStringWithoutNullTermination() + { + var encoding = System.Text.Encoding.UTF8; + const string value = "abc123"; + var bytes = encoding.GetBytes(value); + const int index = 10; // pushes the write to the end of the buffer so no terminator will be added + var written = _directBuffer.SetBytes(index, bytes, 0, bytes.Length); + Assert.AreEqual(bytes.Length, written); + string result = _directBuffer.GetStringFromNullTerminatedBytes(encoding, index, 100, Terminator); + Assert.AreEqual(value, result); + } + + #endregion + + #region SetBytesFromString + [TestMethod] + public void ShouldSetBytesFromString() { + const string value = "abc123"; + var written = _directBuffer.SetBytesFromString(AsciiEncoding, value, 0); + Assert.AreEqual(6, written); + string result = _directBuffer.GetStringFromBytes(AsciiEncoding, 0, 6); + Assert.AreEqual(value, result); + } + + [TestMethod] + public void ShouldSetZeroBytesFromEmptyString() { + const string value = ""; + var written = _directBuffer.SetBytesFromString(AsciiEncoding, value, 0); + Assert.AreEqual(0, written); + string result = _directBuffer.GetStringFromBytes(AsciiEncoding, 0, 0); + Assert.AreEqual(value, result); + } + + + [TestMethod] + public void ShouldThrowExceptionIfNotEnoughRoomInBufferForBytes() { + const string value = "a"; + Assert.ThrowsException(() => _directBuffer.SetBytesFromString(AsciiEncoding, value, _directBuffer.Capacity - value.Length + 1)); + } + + + #endregion + + #region GetStringFromBytes + [TestMethod] + public void ShouldGetStringFromBytes() { + const string value = "abc123"; + _directBuffer.SetBytesFromString(AsciiEncoding, value, 0); + string result = _directBuffer.GetStringFromBytes(AsciiEncoding, 0, AsciiEncoding.GetByteCount(value)); + Assert.AreEqual(value, result); + } + + [TestMethod] + public void ShouldThrowExceptionIfNotEnoughBytesToGetStringFromBytes() { + Assert.ThrowsException(() => _directBuffer.GetStringFromBytes(AsciiEncoding, _directBuffer.Capacity, 1)); } #endregion 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 5e56e1d8d2..99e18fff42 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 @@ -489,7 +489,8 @@ private CharSequence generateVarData(final List tokens, final String inde indent + INDENT + "int limit = _parentMessage.Limit;\n" + indent + INDENT + "_buffer.CheckLimit(limit + sizeOfLengthField);\n" + indent + INDENT + "int dataLength = (int)_buffer.%3$sGet%4$s(limit);\n" + - indent + INDENT + "return _buffer.GetStringFromBytes(%1$sResolvedCharacterEncoding," + + indent + INDENT + "_parentMessage.Limit = limit + sizeOfLengthField + dataLength;\n" + + indent + INDENT + "return _buffer.GetStringFromBytes(%1$sResolvedCharacterEncoding," + " limit + sizeOfLengthField, dataLength);\n" + indent + "}\n\n" + indent + "public void Set%1$s(string value)\n" + From 5f54a0d483f611227bcb9ff34bd842c491f22aa6 Mon Sep 17 00:00:00 2001 From: Rob Purdy Date: Fri, 7 May 2021 20:08:46 -0700 Subject: [PATCH 05/11] [C#] Fixed checkstyle --- .../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 99e18fff42..92a39844f5 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 @@ -490,7 +490,7 @@ private CharSequence generateVarData(final List tokens, final String inde indent + INDENT + "_buffer.CheckLimit(limit + sizeOfLengthField);\n" + indent + INDENT + "int dataLength = (int)_buffer.%3$sGet%4$s(limit);\n" + indent + INDENT + "_parentMessage.Limit = limit + sizeOfLengthField + dataLength;\n" + - indent + INDENT + "return _buffer.GetStringFromBytes(%1$sResolvedCharacterEncoding," + + indent + INDENT + "return _buffer.GetStringFromBytes(%1$sResolvedCharacterEncoding," + " limit + sizeOfLengthField, dataLength);\n" + indent + "}\n\n" + indent + "public void Set%1$s(string value)\n" + From 76bf8b620ce0fd5c452d32585e44fcd998601ed3 Mon Sep 17 00:00:00 2001 From: Rob Purdy Date: Sun, 9 May 2021 14:32:03 -0700 Subject: [PATCH 06/11] [C#] Cleaning --- csharp/sbe-dll/DirectBuffer.cs | 7 +++---- csharp/sbe-tests/DirectBufferTests.cs | 6 ++++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/csharp/sbe-dll/DirectBuffer.cs b/csharp/sbe-dll/DirectBuffer.cs index 9c5bbb165f..f478a481eb 100644 --- a/csharp/sbe-dll/DirectBuffer.cs +++ b/csharp/sbe-dll/DirectBuffer.cs @@ -661,7 +661,7 @@ public int SetBytes(int index, ReadOnlySpan src) /// /// encoding to use to write the bytes from the string /// source string - /// index in theunderlying buffer to start writing bytes + /// index in the underlying buffer to start writing bytes /// count of bytes written public unsafe int SetBytesFromString(Encoding encoding, string src, int index) { @@ -676,9 +676,8 @@ public unsafe int SetBytesFromString(Encoding encoding, string src, int index) } fixed (char* ptr = src) { - encoding.GetBytes(ptr, src.Length, _pBuffer + index, byteCount); + return encoding.GetBytes(ptr, src.Length, _pBuffer + index, byteCount); } - return byteCount; } /// @@ -690,7 +689,7 @@ public unsafe int SetBytesFromString(Encoding encoding, string src, int index) /// index in theunderlying buffer to start writing bytes /// the number of bytes to read into the string /// the string representing the decoded bytes read from the buffer - public unsafe string GetStringFromBytes(Encoding encoding, int index, int byteCount) + public string GetStringFromBytes(Encoding encoding, int index, int byteCount) { int num = _capacity - index; if (byteCount > num) diff --git a/csharp/sbe-tests/DirectBufferTests.cs b/csharp/sbe-tests/DirectBufferTests.cs index ccc04f63ec..d189b2917c 100644 --- a/csharp/sbe-tests/DirectBufferTests.cs +++ b/csharp/sbe-tests/DirectBufferTests.cs @@ -645,7 +645,7 @@ public void ShouldPutStringWithNullTermination() [TestMethod] public void ShouldPutStringWitoutNullTerminator() { - // the string length is equal to the max legth + // the string length is equal to the max legth parameter const string value = "abc123"; const int index = 0; var written = _directBuffer.SetNullTerminatedBytesFromString(AsciiEncoding, value, index, value.Length, Terminator); @@ -660,7 +660,7 @@ public void ShouldPutStringWitoutNullTerminator() [TestMethod] public void ShouldPutStringWitoutNullTerminatorIfBytesToWriteEqualsRemainingCapacity() { - // the string length is equal to the max legth + // the string length is equal to the remaining space in the buffer const string value = "abc123"; int index = _directBuffer.Capacity - value.Length; var written = _directBuffer.SetNullTerminatedBytesFromString(AsciiEncoding, value, index, 100, Terminator); @@ -750,6 +750,7 @@ public void ShouldGetStringWithoutNullTermination() #endregion #region SetBytesFromString + [TestMethod] public void ShouldSetBytesFromString() { const string value = "abc123"; @@ -779,6 +780,7 @@ public void ShouldThrowExceptionIfNotEnoughRoomInBufferForBytes() { #endregion #region GetStringFromBytes + [TestMethod] public void ShouldGetStringFromBytes() { const string value = "abc123"; From 01431833346ab6dd1d102d54f2018040edbc9018 Mon Sep 17 00:00:00 2001 From: Rob Purdy Date: Fri, 21 May 2021 13:42:19 -0700 Subject: [PATCH 07/11] Added Modified Benchmarks\nBoth a modified version of the the CarBenchmark which encodes and decodes to/from string; and a version wich uses the new methods to encode and decode strings --- .../SBE/CarBenchmark_with_strings_new.cs | 193 ++++++++++++++++ .../SBE/CarBenchmark_with_strings_original.cs | 213 ++++++++++++++++++ 2 files changed, 406 insertions(+) create mode 100644 csharp/sbe-benchmarks/Bench/SBE/CarBenchmark_with_strings_new.cs create mode 100644 csharp/sbe-benchmarks/Bench/SBE/CarBenchmark_with_strings_original.cs diff --git a/csharp/sbe-benchmarks/Bench/SBE/CarBenchmark_with_strings_new.cs b/csharp/sbe-benchmarks/Bench/SBE/CarBenchmark_with_strings_new.cs new file mode 100644 index 0000000000..d3badedd18 --- /dev/null +++ b/csharp/sbe-benchmarks/Bench/SBE/CarBenchmark_with_strings_new.cs @@ -0,0 +1,193 @@ +using System.Text; +using Baseline; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Diagnosers; +using Org.SbeTool.Sbe.Dll; + +namespace Org.SbeTool.Sbe.Benchmarks.Bench.Benchmarks +{ + [MemoryDiagnoser] + public class CarBenchmark_with_strings_new + { + + private readonly byte[] _eBuffer = new byte[1024]; + private readonly byte[] _dBuffer = new byte[1024]; + private DirectBuffer _encodeBuffer; + private DirectBuffer _decodeBuffer; + private readonly Car _car = new Car(); + private readonly MessageHeader _messageHeader = new MessageHeader(); + + [GlobalSetup] + public void Setup() + { + _encodeBuffer = new DirectBuffer(_eBuffer); + _decodeBuffer = new DirectBuffer(_dBuffer); + Encode(_car, _decodeBuffer, 0); + } + + [Benchmark] + public int Encode() + { + return Encode(_car, _encodeBuffer, 0); + } + + [Benchmark] + public int Decode() + { + return Decode(_car, _decodeBuffer, 0); + } + + public int Encode(Car car, DirectBuffer directBuffer, int bufferOffset) + { + _car.WrapForEncodeAndApplyHeader(directBuffer, bufferOffset, _messageHeader); + + const int srcOffset = 0; + + car.SerialNumber = 1234; + car.ModelYear = 2013; + car.Available = BooleanType.T; + car.Code = Baseline.Model.A; + car.SetVehicleCode("CODE12"); + + for (int i = 0, size = Car.SomeNumbersLength; i < size; i++) + { + car.SetSomeNumbers(i, (uint)i); + } + + car.Extras = OptionalExtras.CruiseControl | OptionalExtras.SportsPack; + car.Engine.Capacity = 2000; + car.Engine.NumCylinders = 4; + car.Engine.SetManufacturerCode("123"); + car.Engine.Efficiency = 35; + car.Engine.BoosterEnabled = BooleanType.T; + car.Engine.Booster.BoostType = BoostType.NITROUS; + car.Engine.Booster.HorsePower = 200; + + var fuelFigures = car.FuelFiguresCount(3); + fuelFigures.Next(); + fuelFigures.Speed = 30; + fuelFigures.Mpg = 35.9f; + fuelFigures.SetUsageDescription("Urban Cycle"); + + fuelFigures.Next(); + fuelFigures.Speed = 55; + fuelFigures.Mpg = 49.0f; + fuelFigures.SetUsageDescription("Combined Cycle"); + + fuelFigures.Next(); + fuelFigures.Speed = 75; + fuelFigures.Mpg = 40.0f; + fuelFigures.SetUsageDescription("Highway Cycle"); + + Car.PerformanceFiguresGroup perfFigures = car.PerformanceFiguresCount(2); + perfFigures.Next(); + perfFigures.OctaneRating = 95; + + Car.PerformanceFiguresGroup.AccelerationGroup acceleration = perfFigures.AccelerationCount(3).Next(); + acceleration.Mph = 30; + acceleration.Seconds = 4.0f; + + acceleration.Next(); + acceleration.Mph = 60; + acceleration.Seconds = 7.5f; + + acceleration.Next(); + acceleration.Mph = 100; + acceleration.Seconds = 12.2f; + + perfFigures.Next(); + perfFigures.OctaneRating = 99; + acceleration = perfFigures.AccelerationCount(3).Next(); + + acceleration.Mph = 30; + acceleration.Seconds = 3.8f; + + acceleration.Next(); + acceleration.Mph = 60; + acceleration.Seconds = 7.1f; + + acceleration.Next(); + acceleration.Mph = 100; + acceleration.Seconds = 11.8f; + + car.SetManufacturer("Honda"); + car.SetModel("Civic VTi"); + car.SetActivationCode("code"); + + return car.Size; + } + + private readonly byte[] _buffer = new byte[128]; + + public int Decode(Car car, DirectBuffer directBuffer, int bufferOffset) + { + _messageHeader.Wrap(directBuffer, bufferOffset, 0); + + car.WrapForDecode(directBuffer, bufferOffset + MessageHeader.Size, _messageHeader.BlockLength, _messageHeader.Version); + + var templateId = Car.TemplateId; + var schemaVersion = Car.SchemaVersion; + var serialNumber = car.SerialNumber; + var modelYear = car.ModelYear; + var available = car.Available; + var code = car.Code; + + for (int i = 0, size = Car.SomeNumbersLength; i < size; i++) + { + var number = car.GetSomeNumbers(i); + } + + var vehicleCode = car.GetVehicleCode(); + + OptionalExtras extras = car.Extras; + var cruiseControl = (extras & OptionalExtras.CruiseControl) == OptionalExtras.CruiseControl; + var sportsPack = (extras & OptionalExtras.SportsPack) == OptionalExtras.SportsPack; + var sunRoof = (extras & OptionalExtras.SunRoof) == OptionalExtras.SunRoof; + + Engine engine = car.Engine; + var capacity = engine.Capacity; + var numCylinders = engine.NumCylinders; + var maxRpm = engine.MaxRpm; + for (int i = 0, size = Engine.ManufacturerCodeLength; i < size; i++) + { + engine.GetManufacturerCode(i); + } + + var length = engine.GetFuel(_buffer, 0, _buffer.Length); + + var efficiency = engine.Efficiency; + var boosterEnabled = engine.BoosterEnabled; + var boostType = engine.Booster.BoostType; + var horsePower = engine.Booster.HorsePower; + + var fuelFiguresGroup = car.FuelFigures; + while (fuelFiguresGroup.HasNext) + { + var fuelFigures = fuelFiguresGroup.Next(); + var speed = fuelFigures.Speed; + var mpg = fuelFigures.Mpg; + var usage = fuelFigures.GetUsageDescription(); + } + + var performanceFiguresGroup = car.PerformanceFigures; + while (performanceFiguresGroup.HasNext) + { + performanceFiguresGroup.Next(); + var octanceRating = performanceFiguresGroup.OctaneRating; + + var accelerationGroup = performanceFiguresGroup.Acceleration; + for (int i = 0; i < accelerationGroup.Count; i++) + { + var acceleration = accelerationGroup.Next(); + var mpg = acceleration.Mph; + var seconds = acceleration.Seconds; + } + } + + var man = car.GetManufacturer(); + var model = car.GetModel(); + var actCode = car.GetActivationCode(); + return car.Size; + } + } +} diff --git a/csharp/sbe-benchmarks/Bench/SBE/CarBenchmark_with_strings_original.cs b/csharp/sbe-benchmarks/Bench/SBE/CarBenchmark_with_strings_original.cs new file mode 100644 index 0000000000..20a94641c4 --- /dev/null +++ b/csharp/sbe-benchmarks/Bench/SBE/CarBenchmark_with_strings_original.cs @@ -0,0 +1,213 @@ +using System.Text; +using Baseline; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Diagnosers; +using Org.SbeTool.Sbe.Dll; + +namespace Org.SbeTool.Sbe.Benchmarks.Bench.Benchmarks +{ + [MemoryDiagnoser] + public class CarBenchmark_with_strings_original + { + + private static readonly Encoding VehicleCodeEncoding = Encoding.GetEncoding(Car.VehicleCodeCharacterEncoding); + private static readonly Encoding ManufacturerCodeEncoding = Encoding.GetEncoding(Engine.ManufacturerCodeCharacterEncoding); + private static readonly Encoding ManufacturerEncoding = Encoding.GetEncoding(Car.ManufacturerCharacterEncoding); + private static readonly Encoding ModelEncoding = Encoding.GetEncoding(Car.ModelCharacterEncoding); + private static readonly Encoding ActivationCodeEncoding = Encoding.GetEncoding(Car.ActivationCodeCharacterEncoding); + private static readonly Encoding UsageDescriptionEncoding = Encoding.GetEncoding(Car.FuelFiguresGroup.UsageDescriptionCharacterEncoding); + + + private readonly byte[] _eBuffer = new byte[1024]; + private readonly byte[] _dBuffer = new byte[1024]; + private DirectBuffer _encodeBuffer; + private DirectBuffer _decodeBuffer; + private readonly Car _car = new Car(); + private readonly MessageHeader _messageHeader = new MessageHeader(); + + [GlobalSetup] + public void Setup() + { + _encodeBuffer = new DirectBuffer(_eBuffer); + _decodeBuffer = new DirectBuffer(_dBuffer); + Encode(_car, _decodeBuffer, 0); + } + + [Benchmark] + public int Encode() + { + return Encode(_car, _encodeBuffer, 0); + } + + [Benchmark] + public int Decode() + { + return Decode(_car, _decodeBuffer, 0); + } + + public int Encode(Car car, DirectBuffer directBuffer, int bufferOffset) + { + _car.WrapForEncodeAndApplyHeader(directBuffer, bufferOffset, _messageHeader); + + const int srcOffset = 0; + + car.SerialNumber = 1234; + car.ModelYear = 2013; + car.Available = BooleanType.T; + car.Code = Baseline.Model.A; + car.SetVehicleCode(VehicleCodeEncoding.GetBytes("CODE12"), 0); + + for (int i = 0, size = Car.SomeNumbersLength; i < size; i++) + { + car.SetSomeNumbers(i, (uint)i); + } + + car.Extras = OptionalExtras.CruiseControl | OptionalExtras.SportsPack; + car.Engine.Capacity = 2000; + car.Engine.NumCylinders = 4; + car.Engine.SetManufacturerCode(ManufacturerCodeEncoding.GetBytes("123"), 0); + car.Engine.Efficiency = 35; + car.Engine.BoosterEnabled = BooleanType.T; + car.Engine.Booster.BoostType = BoostType.NITROUS; + car.Engine.Booster.HorsePower = 200; + + var fuelFigures = car.FuelFiguresCount(3); + fuelFigures.Next(); + fuelFigures.Speed = 30; + fuelFigures.Mpg = 35.9f; + fuelFigures.SetUsageDescription(UsageDescriptionEncoding.GetBytes("Urban Cycle")); + + fuelFigures.Next(); + fuelFigures.Speed = 55; + fuelFigures.Mpg = 49.0f; + fuelFigures.SetUsageDescription(UsageDescriptionEncoding.GetBytes("Combined Cycle")); + + fuelFigures.Next(); + fuelFigures.Speed = 75; + fuelFigures.Mpg = 40.0f; + fuelFigures.SetUsageDescription(UsageDescriptionEncoding.GetBytes("Highway Cycle")); + + Car.PerformanceFiguresGroup perfFigures = car.PerformanceFiguresCount(2); + perfFigures.Next(); + perfFigures.OctaneRating = 95; + + Car.PerformanceFiguresGroup.AccelerationGroup acceleration = perfFigures.AccelerationCount(3).Next(); + acceleration.Mph = 30; + acceleration.Seconds = 4.0f; + + acceleration.Next(); + acceleration.Mph = 60; + acceleration.Seconds = 7.5f; + + acceleration.Next(); + acceleration.Mph = 100; + acceleration.Seconds = 12.2f; + + perfFigures.Next(); + perfFigures.OctaneRating = 99; + acceleration = perfFigures.AccelerationCount(3).Next(); + + acceleration.Mph = 30; + acceleration.Seconds = 3.8f; + + acceleration.Next(); + acceleration.Mph = 60; + acceleration.Seconds = 7.1f; + + acceleration.Next(); + acceleration.Mph = 100; + acceleration.Seconds = 11.8f; + + byte[] Manufacturer = ManufacturerEncoding.GetBytes("Honda"); + byte[] Model = ModelEncoding.GetBytes("Civic VTi"); + byte[] ActivationCode = ActivationCodeEncoding.GetBytes("code"); + + car.SetManufacturer(Manufacturer, srcOffset, Manufacturer.Length); + car.SetModel(Model, srcOffset, Model.Length); + car.SetActivationCode(ActivationCode, srcOffset, ActivationCode.Length); + + return car.Size; + } + + private readonly byte[] _buffer = new byte[128]; + + public int Decode(Car car, DirectBuffer directBuffer, int bufferOffset) + { + _messageHeader.Wrap(directBuffer, bufferOffset, 0); + + car.WrapForDecode(directBuffer, bufferOffset + MessageHeader.Size, _messageHeader.BlockLength, _messageHeader.Version); + + var templateId = Car.TemplateId; + var schemaVersion = Car.SchemaVersion; + var serialNumber = car.SerialNumber; + var modelYear = car.ModelYear; + var available = car.Available; + var code = car.Code; + + for (int i = 0, size = Car.SomeNumbersLength; i < size; i++) + { + var number = car.GetSomeNumbers(i); + } + + var length = car.GetVehicleCode(_buffer, 0); + var vehicleCode = VehicleCodeEncoding.GetString(_buffer, 0, length); + + OptionalExtras extras = car.Extras; + var cruiseControl = (extras & OptionalExtras.CruiseControl) == OptionalExtras.CruiseControl; + var sportsPack = (extras & OptionalExtras.SportsPack) == OptionalExtras.SportsPack; + var sunRoof = (extras & OptionalExtras.SunRoof) == OptionalExtras.SunRoof; + + Engine engine = car.Engine; + var capacity = engine.Capacity; + var numCylinders = engine.NumCylinders; + var maxRpm = engine.MaxRpm; + for (int i = 0, size = Engine.ManufacturerCodeLength; i < size; i++) + { + engine.GetManufacturerCode(i); + } + + length = engine.GetFuel(_buffer, 0, _buffer.Length); + + var efficiency = engine.Efficiency; + var boosterEnabled = engine.BoosterEnabled; + var boostType = engine.Booster.BoostType; + var horsePower = engine.Booster.HorsePower; + + var fuelFiguresGroup = car.FuelFigures; + while (fuelFiguresGroup.HasNext) + { + var fuelFigures = fuelFiguresGroup.Next(); + var speed = fuelFigures.Speed; + var mpg = fuelFigures.Mpg; + length = fuelFigures.GetUsageDescription(_buffer, 0, _buffer.Length); + var fuelUsage = UsageDescriptionEncoding.GetString(_buffer, 0, length); + } + + var performanceFiguresGroup = car.PerformanceFigures; + while (performanceFiguresGroup.HasNext) + { + performanceFiguresGroup.Next(); + var octanceRating = performanceFiguresGroup.OctaneRating; + + var accelerationGroup = performanceFiguresGroup.Acceleration; + for (int i = 0; i < accelerationGroup.Count; i++) + { + var acceleration = accelerationGroup.Next(); + var mpg = acceleration.Mph; + var seconds = acceleration.Seconds; + } + } + + length = car.GetManufacturer(_buffer, 0, _buffer.Length); + var usage = ManufacturerEncoding.GetString(_buffer, 0, length); + + length = car.GetModel(_buffer, 0, _buffer.Length); + usage = ModelEncoding.GetString(_buffer, 0, length); + + length = car.GetActivationCode(_buffer, 0, _buffer.Length); + usage = ActivationCodeEncoding.GetString(_buffer, 0, length); + + return car.Size; + } + } +} From 9af880734e85a12ee3d17a5e47e878b1314eb3ca Mon Sep 17 00:00:00 2001 From: Rob Purdy Date: Mon, 31 May 2021 21:13:01 -0700 Subject: [PATCH 08/11] Added ToString --- .../generation/csharp/CSharpGenerator.java | 306 ++++++++++++++++++ .../sbe/generation/csharp/CSharpUtil.java | 99 ++++++ 2 files changed, 405 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 92a39844f5..4f6438e243 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 @@ -38,6 +38,8 @@ import static uk.co.real_logic.sbe.ir.GenerationUtil.collectVarData; import static uk.co.real_logic.sbe.ir.GenerationUtil.collectGroups; import static uk.co.real_logic.sbe.ir.GenerationUtil.collectFields; +import static uk.co.real_logic.sbe.ir.GenerationUtil.findEndSignal; +import static uk.co.real_logic.sbe.ir.GenerationUtil.getMessageBody; /** * Codec generator for the CSharp programming language. @@ -142,6 +144,8 @@ public void generate() throws IOException collectVarData(messageBody, offset, varData); out.append(generateVarData(varData, BASE_INDENT + INDENT)); + out.append(generateDisplay(toUpperFirstChar(msgToken.name()), fields, groups, varData)); + out.append(INDENT + "}\n"); out.append("}\n"); } @@ -180,6 +184,8 @@ private void generateGroups( i = collectVarData(tokens, i, varData); sb.append(generateVarData(varData, indent + INDENT + INDENT)); + appendGroupInstanceDisplay(sb, fields, groups, varData, indent + INDENT); + sb.append(indent).append(INDENT + "}\n"); } } @@ -525,6 +531,7 @@ private void generateBitSet(final List tokens) throws IOException out.append(generateChoices(tokens.subList(1, tokens.size() - 1))); out.append(INDENT + "}\n"); + out.append(generateChoiceDisplay(enumName, getMessageBody(tokens))); out.append("}\n"); } } @@ -560,6 +567,7 @@ private void generateComposite(final List tokens) throws IOException out.append(generateFixedFlyweightCode(tokens.get(0).encodedLength())); out.append(generateCompositePropertyElements(tokens.subList(1, tokens.size() - 1), BASE_INDENT)); + out.append(generateCompositeDisplay(tokens)); out.append(INDENT + "}\n"); out.append("}\n"); } @@ -1540,4 +1548,302 @@ private String generateLiteral(final PrimitiveType type, final String value) return literal; } + + private void appendGroupInstanceDisplay( + final StringBuilder sb, + final List fields, + final List groups, + final List varData, + final String baseIndent) + { + final String indent = baseIndent + INDENT; + + sb.append('\n'); + append(sb, indent, "internal void BuildString(StringBuilder builder)"); + append(sb, indent, "{"); + append(sb, indent, " if (_buffer == null)"); + append(sb, indent, " return;"); + sb.append('\n'); + Separators.BEGIN_COMPOSITE.appendToGeneratedBuilder(sb, indent + INDENT, "builder"); + appendDisplay(sb, fields, groups, varData, indent + INDENT); + Separators.END_COMPOSITE.appendToGeneratedBuilder(sb, indent + INDENT, "builder"); + sb.append('\n'); + append(sb, indent, "}"); + } + + private void appendDisplay( + final StringBuilder sb, + final List fields, + final List groups, + final List varData, + final String indent) + { + int lengthBeforeLastGeneratedSeparator = -1; + + for (int i = 0, size = fields.size(); i < size;) + { + final Token fieldToken = fields.get(i); + if (fieldToken.signal() == Signal.BEGIN_FIELD) + { + final Token encodingToken = fields.get(i + 1); + final String fieldName = formatPropertyName(fieldToken.name()); + lengthBeforeLastGeneratedSeparator = writeTokenDisplay(fieldName, encodingToken, sb, indent); + + i += fieldToken.componentTokenCount(); + } + else + { + ++i; + } + } + + for (int i = 0, size = groups.size(); i < size; i++) + { + final Token groupToken = groups.get(i); + if (groupToken.signal() != Signal.BEGIN_GROUP) + { + throw new IllegalStateException("tokens must begin with BEGIN_GROUP: token=" + groupToken); + } + + final String groupName = formatPropertyName(groupToken.name()); + final String className = formatClassName(groupToken.name()); + final String varName = formatVariableName(groupToken.name()); + + append(sb, indent, "builder.Append(\"" + groupName + Separators.KEY_VALUE + + Separators.BEGIN_GROUP + "\");"); + append(sb, indent, "var " + varName + " = this." + groupName + ";"); + append(sb, indent, "if (" + varName + ".Count > 0)"); + append(sb, indent, "{"); + append(sb, indent, " var first = true;"); + append(sb, indent, " while (" + varName + ".HasNext)"); + append(sb, indent, " {"); + append(sb, indent, " if (!first) builder.Append(',');"); + append(sb, indent, " first = false;"); + append(sb, indent, " " + varName + ".Next().BuildString(builder);"); + append(sb, indent, " }"); + append(sb, indent, "}"); + append(sb, indent, "builder.Append(\"" + Separators.END_GROUP + "\");"); + + lengthBeforeLastGeneratedSeparator = sb.length(); + Separators.FIELD.appendToGeneratedBuilder(sb, indent, "builder"); + + i = findEndSignal(groups, i, Signal.END_GROUP, groupToken.name()); + } + + for (int i = 0, size = varData.size(); i < size;) + { + final Token varDataToken = varData.get(i); + if (varDataToken.signal() != Signal.BEGIN_VAR_DATA) + { + throw new IllegalStateException("tokens must begin with BEGIN_VAR_DATA: token=" + varDataToken); + } + + final String characterEncoding = varData.get(i + 3).encoding().characterEncoding(); + final String varDataName = formatPropertyName(varDataToken.name()); + final String getterName = formatGetterName(varDataToken.name()); + append(sb, indent, "builder.Append(\"" + varDataName + Separators.KEY_VALUE + "\");"); + if (null == characterEncoding) + { + final String name = Generators.toUpperFirstChar(varDataToken.name()); + append(sb, indent, "builder.Append(skip" + name + "()).Append(\" bytes of raw data\");"); + } + else + { + append(sb, indent, "builder.Append('\\'').Append(" + getterName + "()).Append('\\'');"); + } + + lengthBeforeLastGeneratedSeparator = sb.length(); + Separators.FIELD.appendToGeneratedBuilder(sb, indent, "builder"); + + i += varDataToken.componentTokenCount(); + } + + if (-1 != lengthBeforeLastGeneratedSeparator) + { + sb.setLength(lengthBeforeLastGeneratedSeparator); + } + } + + + private int writeTokenDisplay( + final String fieldName, + final Token typeToken, + final StringBuilder sb, + final String indent) + { + if (typeToken.encodedLength() <= 0 || typeToken.isConstantEncoding()) + { + return -1; + } + + append(sb, indent, "builder.Append(\"" + fieldName + Separators.KEY_VALUE + "\");"); + + switch (typeToken.signal()) + { + case ENCODING: + if (typeToken.arrayLength() > 1) + { + if (typeToken.encoding().primitiveType() == PrimitiveType.CHAR) + { + append(sb, indent, "for (int i = 0; i < " + fieldName + + "Length && this.Get" + fieldName + "(i) > 0; ++i)"); + append(sb, indent, "{"); + append(sb, indent, " builder.Append((char)this.Get" + fieldName + "(i));"); + append(sb, indent, "}"); + } + else + { + Separators.BEGIN_ARRAY.appendToGeneratedBuilder(sb, indent, "builder"); + append(sb, indent, "for (int i = 0; i < " + fieldName + "Length; ++i)"); + append(sb, indent, "{"); + append(sb, indent, " if (i > 0) builder.Append(',');"); + append(sb, indent, " builder.Append(Get" + fieldName + "(i));"); + append(sb, indent, "}"); + Separators.END_ARRAY.appendToGeneratedBuilder(sb, indent, "builder"); + } + } + else + { + // have to duplicate because of checkstyle :/ + append(sb, indent, "builder.Append(this." + fieldName + ");"); + } + break; + + case BEGIN_ENUM: + append(sb, indent, "builder.Append(this." + fieldName + ");"); + break; + + case BEGIN_SET: + append(sb, indent, "this." + fieldName + ".BuildString(builder);"); + break; + + case BEGIN_COMPOSITE: + { + final String varName = formatVariableName(typeToken.applicableTypeName()); + append(sb, indent, "if (this." + fieldName + " != null)"); + append(sb, indent, " this." + fieldName + ".BuildString(builder);"); + append(sb, indent, "else"); + append(sb, indent, " builder.Append(\"null\");"); + break; + } + } + + final int lengthBeforeFieldSeparator = sb.length(); + Separators.FIELD.appendToGeneratedBuilder(sb, indent, "builder"); + + return lengthBeforeFieldSeparator; + } + + private void appendToString(final StringBuilder sb, final String indent) + { + sb.append('\n'); + append(sb, indent, "public override string ToString()"); + append(sb, indent, "{"); + append(sb, indent, " var sb = new StringBuilder(100);"); + append(sb, indent, " this.BuildString(sb);"); + append(sb, indent, " return sb.ToString();"); + append(sb, indent, "}"); + } + + private CharSequence generateChoiceDisplay(final String enumName, final List tokens) + { + final String indent = INDENT; + final String indentTwo = INDENT + INDENT; + final String indentThree = INDENT + INDENT + INDENT; + final StringBuilder sb = new StringBuilder(); + + sb.append('\n'); + append(sb, indent, "static class " + enumName + "Ext"); + append(sb, indent, "{"); + append(sb, indentTwo, "internal static void BuildString(this " + enumName + " val, StringBuilder builder)"); + append(sb, indentTwo, "{"); + Separators.BEGIN_SET.appendToGeneratedBuilder(sb, indentThree, "builder"); + append(sb, indentThree, "builder.Append(val.ToString());"); + Separators.END_SET.appendToGeneratedBuilder(sb, indentThree, "builder"); + append(sb, indentTwo, "}"); + append(sb, indent, "}"); + return sb; + } + + private CharSequence generateDisplay( + final String name, + final List tokens, + final List groups, + final List varData) + { + final StringBuilder sb = new StringBuilder(100); + final String indent = INDENT + INDENT; + + appendToString(sb, indent); + sb.append('\n'); + append(sb, indent, "internal void BuildString(StringBuilder builder)"); + append(sb, indent, "{"); + append(sb, indent, " if (_buffer == null)"); + append(sb, indent, " throw new ArgumentNullException(\"_buffer\");"); + sb.append('\n'); + append(sb, indent, " int originalLimit = this.Limit;"); + append(sb, indent, " this.Limit = _offset + _actingBlockLength;"); + append(sb, indent, " builder.Append(\"[" + name + "](sbeTemplateId=\");"); + append(sb, indent, " builder.Append(" + name + ".TemplateId);"); + append(sb, indent, " builder.Append(\"|sbeSchemaId=\");"); + append(sb, indent, " builder.Append(" + name + ".SchemaId);"); + append(sb, indent, " builder.Append(\"|sbeSchemaVersion=\");"); + append(sb, indent, " if (_parentMessage._actingVersion != " + name + ".SchemaVersion)"); + append(sb, indent, " {"); + append(sb, indent, " builder.Append(_parentMessage._actingVersion);"); + append(sb, indent, " builder.Append('/');"); + append(sb, indent, " }"); + append(sb, indent, " builder.Append(" + name + ".SchemaVersion);"); + append(sb, indent, " builder.Append(\"|sbeBlockLength=\");"); + append(sb, indent, " if (_actingBlockLength != " + name + ".BlockLength)"); + append(sb, indent, " {"); + append(sb, indent, " builder.Append(_actingBlockLength);"); + append(sb, indent, " builder.Append('/');"); + append(sb, indent, " }"); + append(sb, indent, " builder.Append(" + name + ".BlockLength);"); + append(sb, indent, " builder.Append(\"):\");"); + sb.append('\n'); + appendDisplay(sb, tokens, groups, varData, indent + INDENT); + sb.append('\n'); + append(sb, indent, " this.Limit = originalLimit;"); + sb.append('\n'); + append(sb, indent, "}"); + return sb; + } + + private CharSequence generateCompositeDisplay(final List tokens) + { + final String indent = INDENT; + final StringBuilder sb = new StringBuilder(); + + appendToString(sb, indent); + sb.append('\n'); + append(sb, indent, "internal void BuildString(StringBuilder builder)"); + append(sb, indent, "{"); + append(sb, indent, " if (_buffer == null)"); + append(sb, indent, " return;"); + sb.append('\n'); + Separators.BEGIN_COMPOSITE.appendToGeneratedBuilder(sb, indent + INDENT, "builder"); + + int lengthBeforeLastGeneratedSeparator = -1; + + for (int i = 1, end = tokens.size() - 1; i < end;) + { + final Token encodingToken = tokens.get(i); + final String propertyName = formatPropertyName(encodingToken.name()); + lengthBeforeLastGeneratedSeparator = writeTokenDisplay(propertyName, encodingToken, sb, indent + INDENT); + i += encodingToken.componentTokenCount(); + } + + if (-1 != lengthBeforeLastGeneratedSeparator) + { + sb.setLength(lengthBeforeLastGeneratedSeparator); + } + + Separators.END_COMPOSITE.appendToGeneratedBuilder(sb, indent + INDENT, "builder"); + sb.append('\n'); + append(sb, indent, "}"); + + return sb; + } } diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpUtil.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpUtil.java index a43b5ce522..3d521bfdcb 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpUtil.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpUtil.java @@ -28,6 +28,46 @@ */ public class CSharpUtil { + + public enum Separators + { + BEGIN_GROUP('['), + END_GROUP(']'), + BEGIN_COMPOSITE('('), + END_COMPOSITE(')'), + BEGIN_SET('{'), + END_SET('}'), + BEGIN_ARRAY('['), + END_ARRAY(']'), + FIELD('|'), + KEY_VALUE('='), + ENTRY(','); + + public final char symbol; + + Separators(final char symbol) + { + this.symbol = symbol; + } + + /** + * Add separator to a generated append to a {@link StringBuilder}. + * + * @param builder the code generation builder to which information should be added + * @param indent the current generated code indentation + * @param builderName of the generated StringBuilder to which separator should be added + */ + public void appendToGeneratedBuilder(final StringBuilder builder, final String indent, final String builderName) + { + builder.append(indent).append(builderName).append(".Append('").append(symbol).append("');").append('\n'); + } + + public String toString() + { + return String.valueOf(symbol); + } + } + private static final Map PRIMITIVE_TYPE_STRING_ENUM_MAP = new EnumMap<>(PrimitiveType.class); /* @@ -59,6 +99,29 @@ public static String cSharpTypeName(final PrimitiveType primitiveType) return PRIMITIVE_TYPE_STRING_ENUM_MAP.get(primitiveType); } + /** + * Uppercase the first character of a given String. + * + * @param str to have the first character upper cased. + * @return a new String with the first character in uppercase. + */ + public static String toUpperFirstChar(final String str) + { + return Character.toUpperCase(str.charAt(0)) + str.substring(1); + } + + /** + * Lowercase the first character of a given String. + * + * @param str to have the first character upper cased. + * @return a new String with the first character in uppercase. + */ + public static String toLowerFirstChar(final String str) + { + return Character.toLowerCase(str.charAt(0)) + str.substring(1); + } + + /** * Format a String as a property name. * @@ -70,6 +133,18 @@ public static String formatPropertyName(final String str) return toUpperFirstChar(str); } + /** + * Format a String as a variable name. + * + * @param str to be formatted. + * @return the string formatted as a property name. + */ + public static String formatVariableName(final String str) + { + return toLowerFirstChar(str); + } + + /** * Format a String as a class name. * @@ -80,4 +155,28 @@ public static String formatClassName(final String str) { return toUpperFirstChar(str); } + + /** + * Format a Getter name for generated code. + * + * @param propertyName to be formatted. + * @return the property name formatted as a getter name. + */ + public static String formatGetterName(final String propertyName) + { + return "Get" + toUpperFirstChar(propertyName); + } + + /** + * Shortcut to append a line of generated code + * + * @param builder string builder to which to append the line + * @param indent current text indentation + * @param line line to be appended + */ + public static void append(final StringBuilder builder, final String indent, final String line) + { + builder.append(indent).append(line).append('\n'); + } + } From 5d10e9995dc0b9511928fe62d35bf83f649ba229 Mon Sep 17 00:00:00 2001 From: Rob Purdy Date: Wed, 2 Jun 2021 13:38:27 -0700 Subject: [PATCH 09/11] Added support for contant tokens --- .../generation/csharp/CSharpGenerator.java | 91 ++++++++++--------- .../sbe/generation/csharp/CSharpUtil.java | 14 ++- 2 files changed, 56 insertions(+), 49 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 4f6438e243..c3d7648eb2 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 @@ -1593,7 +1593,7 @@ private void appendDisplay( } else { - ++i; + i++; } } @@ -1606,7 +1606,6 @@ private void appendDisplay( } final String groupName = formatPropertyName(groupToken.name()); - final String className = formatClassName(groupToken.name()); final String varName = formatVariableName(groupToken.name()); append(sb, indent, "builder.Append(\"" + groupName + Separators.KEY_VALUE + @@ -1658,73 +1657,77 @@ private void appendDisplay( i += varDataToken.componentTokenCount(); } - if (-1 != lengthBeforeLastGeneratedSeparator) + if (lengthBeforeLastGeneratedSeparator > -1) { sb.setLength(lengthBeforeLastGeneratedSeparator); } } - private int writeTokenDisplay( final String fieldName, final Token typeToken, final StringBuilder sb, final String indent) { - if (typeToken.encodedLength() <= 0 || typeToken.isConstantEncoding()) + if (typeToken.encodedLength() <= 0) { return -1; } append(sb, indent, "builder.Append(\"" + fieldName + Separators.KEY_VALUE + "\");"); - switch (typeToken.signal()) + if (typeToken.isConstantEncoding()) { - case ENCODING: - if (typeToken.arrayLength() > 1) - { - if (typeToken.encoding().primitiveType() == PrimitiveType.CHAR) + append(sb, indent, "builder.Append(\"" + typeToken.encoding().constValue() + "\");"); + } + else + { + switch (typeToken.signal()) + { + case ENCODING: + if (typeToken.arrayLength() > 1) { - append(sb, indent, "for (int i = 0; i < " + fieldName + - "Length && this.Get" + fieldName + "(i) > 0; ++i)"); - append(sb, indent, "{"); - append(sb, indent, " builder.Append((char)this.Get" + fieldName + "(i));"); - append(sb, indent, "}"); + if (typeToken.encoding().primitiveType() == PrimitiveType.CHAR) + { + append(sb, indent, "for (int i = 0; i < " + fieldName + + "Length && this.Get" + fieldName + "(i) > 0; ++i)"); + append(sb, indent, "{"); + append(sb, indent, " builder.Append((char)this.Get" + fieldName + "(i));"); + append(sb, indent, "}"); + } + else + { + Separators.BEGIN_ARRAY.appendToGeneratedBuilder(sb, indent, "builder"); + append(sb, indent, "for (int i = 0; i < " + fieldName + "Length; ++i)"); + append(sb, indent, "{"); + append(sb, indent, " if (i > 0) builder.Append(',');"); + append(sb, indent, " builder.Append(Get" + fieldName + "(i));"); + append(sb, indent, "}"); + Separators.END_ARRAY.appendToGeneratedBuilder(sb, indent, "builder"); + } } else { - Separators.BEGIN_ARRAY.appendToGeneratedBuilder(sb, indent, "builder"); - append(sb, indent, "for (int i = 0; i < " + fieldName + "Length; ++i)"); - append(sb, indent, "{"); - append(sb, indent, " if (i > 0) builder.Append(',');"); - append(sb, indent, " builder.Append(Get" + fieldName + "(i));"); - append(sb, indent, "}"); - Separators.END_ARRAY.appendToGeneratedBuilder(sb, indent, "builder"); + append(sb, indent, "builder.Append(this." + fieldName + ");"); } - } - else - { - // have to duplicate because of checkstyle :/ - append(sb, indent, "builder.Append(this." + fieldName + ");"); - } - break; + break; - case BEGIN_ENUM: - append(sb, indent, "builder.Append(this." + fieldName + ");"); - break; + case BEGIN_ENUM: + append(sb, indent, "builder.Append(this." + fieldName + ");"); + break; - case BEGIN_SET: - append(sb, indent, "this." + fieldName + ".BuildString(builder);"); - break; + case BEGIN_SET: + append(sb, indent, "this." + fieldName + ".BuildString(builder);"); + break; - case BEGIN_COMPOSITE: - { - final String varName = formatVariableName(typeToken.applicableTypeName()); - append(sb, indent, "if (this." + fieldName + " != null)"); - append(sb, indent, " this." + fieldName + ".BuildString(builder);"); - append(sb, indent, "else"); - append(sb, indent, " builder.Append(\"null\");"); - break; + case BEGIN_COMPOSITE: + { + append(sb, indent, "if (this." + fieldName + " != null)"); + append(sb, indent, " this." + fieldName + ".BuildString(builder);"); + append(sb, indent, "else"); + append(sb, indent, " builder.Append(\"null\");"); + break; + } } } @@ -1835,7 +1838,7 @@ private CharSequence generateCompositeDisplay(final List tokens) i += encodingToken.componentTokenCount(); } - if (-1 != lengthBeforeLastGeneratedSeparator) + if (lengthBeforeLastGeneratedSeparator > -1) { sb.setLength(lengthBeforeLastGeneratedSeparator); } diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpUtil.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpUtil.java index 3d521bfdcb..5985470789 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpUtil.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharpUtil.java @@ -21,7 +21,6 @@ import java.util.EnumMap; import java.util.Map; -import static uk.co.real_logic.sbe.generation.Generators.toUpperFirstChar; /** * Utilities for mapping between IR and the C# language. @@ -29,6 +28,9 @@ public class CSharpUtil { + /** + * Useful separator constants for code generation + */ public enum Separators { BEGIN_GROUP('['), @@ -43,7 +45,7 @@ public enum Separators KEY_VALUE('='), ENTRY(','); - public final char symbol; + private final char symbol; Separators(final char symbol) { @@ -55,13 +57,17 @@ public enum Separators * * @param builder the code generation builder to which information should be added * @param indent the current generated code indentation - * @param builderName of the generated StringBuilder to which separator should be added + * @param builderName the generated StringBuilder to which separator should be added */ public void appendToGeneratedBuilder(final StringBuilder builder, final String indent, final String builderName) { builder.append(indent).append(builderName).append(".Append('").append(symbol).append("');").append('\n'); } + /** + * Returns the string value of this separator. + * @return the string value of this separator + */ public String toString() { return String.valueOf(symbol); @@ -121,7 +127,6 @@ public static String toLowerFirstChar(final String str) return Character.toLowerCase(str.charAt(0)) + str.substring(1); } - /** * Format a String as a property name. * @@ -144,7 +149,6 @@ public static String formatVariableName(final String str) return toLowerFirstChar(str); } - /** * Format a String as a class name. * From a8bf7bc38c79fd3fc2471938663d2abf2fbd9c60 Mon Sep 17 00:00:00 2001 From: Rob Purdy Date: Wed, 2 Jun 2021 15:02:31 -0700 Subject: [PATCH 10/11] Fixed issue with ToString of tokens without an encoding - like raw data fields --- .../generation/csharp/CSharpGenerator.java | 71 +++++++++++-------- 1 file changed, 40 insertions(+), 31 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 c3d7648eb2..e09796173e 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 @@ -487,29 +487,32 @@ private CharSequence generateVarData(final List tokens, final String inde lengthCSharpType, byteOrderStr)); - sb.append(lineSeparator()) - .append(String.format( - indent + "public string Get%1$s()\n" + - indent + "{\n" + - indent + INDENT + "const int sizeOfLengthField = %2$d;\n" + - indent + INDENT + "int limit = _parentMessage.Limit;\n" + - indent + INDENT + "_buffer.CheckLimit(limit + sizeOfLengthField);\n" + - indent + INDENT + "int dataLength = (int)_buffer.%3$sGet%4$s(limit);\n" + - indent + INDENT + "_parentMessage.Limit = limit + sizeOfLengthField + dataLength;\n" + - indent + INDENT + "return _buffer.GetStringFromBytes(%1$sResolvedCharacterEncoding," + - " limit + sizeOfLengthField, dataLength);\n" + - indent + "}\n\n" + - indent + "public void Set%1$s(string value)\n" + - indent + "{\n" + - indent + INDENT + "var encoding = %1$sResolvedCharacterEncoding;\n" + - indent + INDENT + "const int sizeOfLengthField = %2$d;\n" + - indent + INDENT + "int limit = _parentMessage.Limit;\n" + - indent + INDENT + "int byteCount = _buffer.SetBytesFromString(encoding, value, " + - "limit + sizeOfLengthField);\n" + - indent + INDENT + "_parentMessage.Limit = limit + sizeOfLengthField + byteCount;\n" + - indent + INDENT + "_buffer.%3$sPut%4$s(limit, (ushort)byteCount);\n" + - indent + "}\n", - propertyName, sizeOfLengthField, lengthTypePrefix, byteOrderStr)); + if (characterEncoding != null) // only generate these string based methods if there is an encoding + { + sb.append(lineSeparator()) + .append(String.format( + indent + "public string Get%1$s()\n" + + indent + "{\n" + + indent + INDENT + "const int sizeOfLengthField = %2$d;\n" + + indent + INDENT + "int limit = _parentMessage.Limit;\n" + + indent + INDENT + "_buffer.CheckLimit(limit + sizeOfLengthField);\n" + + indent + INDENT + "int dataLength = (int)_buffer.%3$sGet%4$s(limit);\n" + + indent + INDENT + "_parentMessage.Limit = limit + sizeOfLengthField + dataLength;\n" + + indent + INDENT + "return _buffer.GetStringFromBytes(%1$sResolvedCharacterEncoding," + + " limit + sizeOfLengthField, dataLength);\n" + + indent + "}\n\n" + + indent + "public void Set%1$s(string value)\n" + + indent + "{\n" + + indent + INDENT + "var encoding = %1$sResolvedCharacterEncoding;\n" + + indent + INDENT + "const int sizeOfLengthField = %2$d;\n" + + indent + INDENT + "int limit = _parentMessage.Limit;\n" + + indent + INDENT + "int byteCount = _buffer.SetBytesFromString(encoding, value, " + + "limit + sizeOfLengthField);\n" + + indent + INDENT + "_parentMessage.Limit = limit + sizeOfLengthField + byteCount;\n" + + indent + INDENT + "_buffer.%3$sPut%4$s(limit, (ushort)byteCount);\n" + + indent + "}\n", + propertyName, sizeOfLengthField, lengthTypePrefix, byteOrderStr)); + } } } @@ -1037,12 +1040,14 @@ private void generateCharacterEncodingMethod( final String encoding, final String indent) { - sb.append(String.format("\n" + - indent + "public const string %1$sCharacterEncoding = \"%2$s\";\n" + - indent + "public static Encoding %1$sResolvedCharacterEncoding = " + - "Encoding.GetEncoding(%1$sCharacterEncoding);\n\n", - formatPropertyName(propertyName), - encoding)); + if (encoding != null) // Raw data fields might not have encodings + { + sb.append(String.format("\n" + + indent + "public const string %1$sCharacterEncoding = \"%2$s\";\n" + + indent + "public static Encoding %1$sResolvedCharacterEncoding = " + + "Encoding.GetEncoding(%1$sCharacterEncoding);\n\n", + formatPropertyName(propertyName), encoding)); + } } private CharSequence generateConstPropertyMethods( @@ -1631,6 +1636,8 @@ private void appendDisplay( for (int i = 0, size = varData.size(); i < size;) { + final Token lengthToken = Generators.findFirst("length", varData, i); + final int sizeOfLengthField = lengthToken.encodedLength(); final Token varDataToken = varData.get(i); if (varDataToken.signal() != Signal.BEGIN_VAR_DATA) { @@ -1641,10 +1648,12 @@ private void appendDisplay( final String varDataName = formatPropertyName(varDataToken.name()); final String getterName = formatGetterName(varDataToken.name()); append(sb, indent, "builder.Append(\"" + varDataName + Separators.KEY_VALUE + "\");"); - if (null == characterEncoding) + if (characterEncoding == null) { final String name = Generators.toUpperFirstChar(varDataToken.name()); - append(sb, indent, "builder.Append(skip" + name + "()).Append(\" bytes of raw data\");"); + append(sb, indent, "builder.Append(" + name + "Length()).Append(\" bytes of raw data\");"); + append(sb, indent, "_parentMessage.Limit = _parentMessage.Limit + " + + sizeOfLengthField + " + " + name + "Length();\n"); } else { From 2ffb6432991ef4ad26118fcc1d16e904252dfc47 Mon Sep 17 00:00:00 2001 From: Rob Purdy Date: Thu, 3 Jun 2021 15:31:35 -0700 Subject: [PATCH 11/11] Fixed bracing issue in generated CSharp code. Also Fixed an indent issue. --- .../generation/csharp/CSharpGenerator.java | 123 ++++++++++-------- 1 file changed, 66 insertions(+), 57 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 e09796173e..02ae2a938f 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 @@ -39,7 +39,6 @@ import static uk.co.real_logic.sbe.ir.GenerationUtil.collectGroups; import static uk.co.real_logic.sbe.ir.GenerationUtil.collectFields; import static uk.co.real_logic.sbe.ir.GenerationUtil.findEndSignal; -import static uk.co.real_logic.sbe.ir.GenerationUtil.getMessageBody; /** * Codec generator for the CSharp programming language. @@ -49,6 +48,8 @@ public class CSharpGenerator implements CodeGenerator { private static final String META_ATTRIBUTE_ENUM = "MetaAttribute"; private static final String INDENT = " "; + private static final String TWO_INDENT = INDENT + INDENT; + private static final String THREE_INDENT = INDENT + INDENT + INDENT; private static final String BASE_INDENT = INDENT; private final Ir ir; @@ -184,7 +185,7 @@ private void generateGroups( i = collectVarData(tokens, i, varData); sb.append(generateVarData(varData, indent + INDENT + INDENT)); - appendGroupInstanceDisplay(sb, fields, groups, varData, indent + INDENT); + appendGroupInstanceDisplay(sb, fields, groups, varData, indent + TWO_INDENT); sb.append(indent).append(INDENT + "}\n"); } @@ -534,7 +535,7 @@ private void generateBitSet(final List tokens) throws IOException out.append(generateChoices(tokens.subList(1, tokens.size() - 1))); out.append(INDENT + "}\n"); - out.append(generateChoiceDisplay(enumName, getMessageBody(tokens))); + out.append(generateChoiceDisplay(enumName)); out.append("}\n"); } } @@ -1567,7 +1568,9 @@ private void appendGroupInstanceDisplay( append(sb, indent, "internal void BuildString(StringBuilder builder)"); append(sb, indent, "{"); append(sb, indent, " if (_buffer == null)"); + append(sb, indent, " {"); append(sb, indent, " return;"); + append(sb, indent, " }"); sb.append('\n'); Separators.BEGIN_COMPOSITE.appendToGeneratedBuilder(sb, indent + INDENT, "builder"); appendDisplay(sb, fields, groups, varData, indent + INDENT); @@ -1621,7 +1624,10 @@ private void appendDisplay( append(sb, indent, " var first = true;"); append(sb, indent, " while (" + varName + ".HasNext)"); append(sb, indent, " {"); - append(sb, indent, " if (!first) builder.Append(',');"); + append(sb, indent, " if (!first)"); + append(sb, indent, " {"); + append(sb, indent, " builder.Append(',');"); + append(sb, indent, " }"); append(sb, indent, " first = false;"); append(sb, indent, " " + varName + ".Next().BuildString(builder);"); append(sb, indent, " }"); @@ -1732,9 +1738,13 @@ private int writeTokenDisplay( case BEGIN_COMPOSITE: { append(sb, indent, "if (this." + fieldName + " != null)"); + append(sb, indent, "{"); append(sb, indent, " this." + fieldName + ".BuildString(builder);"); + append(sb, indent, "}"); append(sb, indent, "else"); + append(sb, indent, "{"); append(sb, indent, " builder.Append(\"null\");"); + append(sb, indent, "}"); break; } } @@ -1757,23 +1767,20 @@ private void appendToString(final StringBuilder sb, final String indent) append(sb, indent, "}"); } - private CharSequence generateChoiceDisplay(final String enumName, final List tokens) + private CharSequence generateChoiceDisplay(final String enumName) { - final String indent = INDENT; - final String indentTwo = INDENT + INDENT; - final String indentThree = INDENT + INDENT + INDENT; final StringBuilder sb = new StringBuilder(); sb.append('\n'); - append(sb, indent, "static class " + enumName + "Ext"); - append(sb, indent, "{"); - append(sb, indentTwo, "internal static void BuildString(this " + enumName + " val, StringBuilder builder)"); - append(sb, indentTwo, "{"); - Separators.BEGIN_SET.appendToGeneratedBuilder(sb, indentThree, "builder"); - append(sb, indentThree, "builder.Append(val.ToString());"); - Separators.END_SET.appendToGeneratedBuilder(sb, indentThree, "builder"); - append(sb, indentTwo, "}"); - append(sb, indent, "}"); + append(sb, INDENT, "static class " + enumName + "Ext"); + append(sb, INDENT, "{"); + append(sb, TWO_INDENT, "internal static void BuildString(this " + enumName + " val, StringBuilder builder)"); + append(sb, TWO_INDENT, "{"); + Separators.BEGIN_SET.appendToGeneratedBuilder(sb, THREE_INDENT, "builder"); + append(sb, THREE_INDENT, "builder.Append(val.ToString());"); + Separators.END_SET.appendToGeneratedBuilder(sb, THREE_INDENT, "builder"); + append(sb, TWO_INDENT, "}"); + append(sb, INDENT, "}"); return sb; } @@ -1784,58 +1791,60 @@ private CharSequence generateDisplay( final List varData) { final StringBuilder sb = new StringBuilder(100); - final String indent = INDENT + INDENT; - appendToString(sb, indent); + appendToString(sb, TWO_INDENT); sb.append('\n'); - append(sb, indent, "internal void BuildString(StringBuilder builder)"); - append(sb, indent, "{"); - append(sb, indent, " if (_buffer == null)"); - append(sb, indent, " throw new ArgumentNullException(\"_buffer\");"); + append(sb, TWO_INDENT, "internal void BuildString(StringBuilder builder)"); + append(sb, TWO_INDENT, "{"); + append(sb, TWO_INDENT, " if (_buffer == null)"); + append(sb, TWO_INDENT, "{"); + append(sb, TWO_INDENT, " throw new ArgumentNullException(\"_buffer\");"); + append(sb, TWO_INDENT, "}"); sb.append('\n'); - append(sb, indent, " int originalLimit = this.Limit;"); - append(sb, indent, " this.Limit = _offset + _actingBlockLength;"); - append(sb, indent, " builder.Append(\"[" + name + "](sbeTemplateId=\");"); - append(sb, indent, " builder.Append(" + name + ".TemplateId);"); - append(sb, indent, " builder.Append(\"|sbeSchemaId=\");"); - append(sb, indent, " builder.Append(" + name + ".SchemaId);"); - append(sb, indent, " builder.Append(\"|sbeSchemaVersion=\");"); - append(sb, indent, " if (_parentMessage._actingVersion != " + name + ".SchemaVersion)"); - append(sb, indent, " {"); - append(sb, indent, " builder.Append(_parentMessage._actingVersion);"); - append(sb, indent, " builder.Append('/');"); - append(sb, indent, " }"); - append(sb, indent, " builder.Append(" + name + ".SchemaVersion);"); - append(sb, indent, " builder.Append(\"|sbeBlockLength=\");"); - append(sb, indent, " if (_actingBlockLength != " + name + ".BlockLength)"); - append(sb, indent, " {"); - append(sb, indent, " builder.Append(_actingBlockLength);"); - append(sb, indent, " builder.Append('/');"); - append(sb, indent, " }"); - append(sb, indent, " builder.Append(" + name + ".BlockLength);"); - append(sb, indent, " builder.Append(\"):\");"); + append(sb, TWO_INDENT, " int originalLimit = this.Limit;"); + append(sb, TWO_INDENT, " this.Limit = _offset + _actingBlockLength;"); + append(sb, TWO_INDENT, " builder.Append(\"[" + name + "](sbeTemplateId=\");"); + append(sb, TWO_INDENT, " builder.Append(" + name + ".TemplateId);"); + append(sb, TWO_INDENT, " builder.Append(\"|sbeSchemaId=\");"); + append(sb, TWO_INDENT, " builder.Append(" + name + ".SchemaId);"); + append(sb, TWO_INDENT, " builder.Append(\"|sbeSchemaVersion=\");"); + append(sb, TWO_INDENT, " if (_parentMessage._actingVersion != " + name + ".SchemaVersion)"); + append(sb, TWO_INDENT, " {"); + append(sb, TWO_INDENT, " builder.Append(_parentMessage._actingVersion);"); + append(sb, TWO_INDENT, " builder.Append('/');"); + append(sb, TWO_INDENT, " }"); + append(sb, TWO_INDENT, " builder.Append(" + name + ".SchemaVersion);"); + append(sb, TWO_INDENT, " builder.Append(\"|sbeBlockLength=\");"); + append(sb, TWO_INDENT, " if (_actingBlockLength != " + name + ".BlockLength)"); + append(sb, TWO_INDENT, " {"); + append(sb, TWO_INDENT, " builder.Append(_actingBlockLength);"); + append(sb, TWO_INDENT, " builder.Append('/');"); + append(sb, TWO_INDENT, " }"); + append(sb, TWO_INDENT, " builder.Append(" + name + ".BlockLength);"); + append(sb, TWO_INDENT, " builder.Append(\"):\");"); sb.append('\n'); - appendDisplay(sb, tokens, groups, varData, indent + INDENT); + appendDisplay(sb, tokens, groups, varData, THREE_INDENT); sb.append('\n'); - append(sb, indent, " this.Limit = originalLimit;"); + append(sb, TWO_INDENT, " this.Limit = originalLimit;"); sb.append('\n'); - append(sb, indent, "}"); + append(sb, TWO_INDENT, "}"); return sb; } private CharSequence generateCompositeDisplay(final List tokens) { - final String indent = INDENT; final StringBuilder sb = new StringBuilder(); - appendToString(sb, indent); + appendToString(sb, TWO_INDENT); sb.append('\n'); - append(sb, indent, "internal void BuildString(StringBuilder builder)"); - append(sb, indent, "{"); - append(sb, indent, " if (_buffer == null)"); - append(sb, indent, " return;"); + append(sb, TWO_INDENT, "internal void BuildString(StringBuilder builder)"); + append(sb, TWO_INDENT, "{"); + append(sb, TWO_INDENT, " if (_buffer == null)"); + append(sb, TWO_INDENT, " {"); + append(sb, TWO_INDENT, " return;"); + append(sb, TWO_INDENT, " }"); sb.append('\n'); - Separators.BEGIN_COMPOSITE.appendToGeneratedBuilder(sb, indent + INDENT, "builder"); + Separators.BEGIN_COMPOSITE.appendToGeneratedBuilder(sb, THREE_INDENT, "builder"); int lengthBeforeLastGeneratedSeparator = -1; @@ -1843,7 +1852,7 @@ private CharSequence generateCompositeDisplay(final List tokens) { final Token encodingToken = tokens.get(i); final String propertyName = formatPropertyName(encodingToken.name()); - lengthBeforeLastGeneratedSeparator = writeTokenDisplay(propertyName, encodingToken, sb, indent + INDENT); + lengthBeforeLastGeneratedSeparator = writeTokenDisplay(propertyName, encodingToken, sb, THREE_INDENT); i += encodingToken.componentTokenCount(); } @@ -1852,9 +1861,9 @@ private CharSequence generateCompositeDisplay(final List tokens) sb.setLength(lengthBeforeLastGeneratedSeparator); } - Separators.END_COMPOSITE.appendToGeneratedBuilder(sb, indent + INDENT, "builder"); + Separators.END_COMPOSITE.appendToGeneratedBuilder(sb, THREE_INDENT, "builder"); sb.append('\n'); - append(sb, indent, "}"); + append(sb, TWO_INDENT, "}"); return sb; }