diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java index bf1d13f955..ef3e38d5a2 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/java/JavaGenerator.java @@ -981,7 +981,7 @@ private void generateVarDataWrapDecoder( indent + " }\n", propertyName, readOnlyBuffer, - generateVarWrapFieldNotPresentCondition(token.version(), indent), + generateWrapFieldNotPresentCondition(token.version(), indent), sizeOfLengthField, PrimitiveType.UINT32 == lengthType ? "(int)" : "", generateGet(lengthType, "limit", byteOrderStr)); @@ -1860,7 +1860,7 @@ private CharSequence generatePrimitivePropertyEncode( generatePut(encoding.primitiveType(), "offset + " + offset, "value", byteOrderStr)); } - private CharSequence generateVarWrapFieldNotPresentCondition(final int sinceVersion, final String indent) + private CharSequence generateWrapFieldNotPresentCondition(final int sinceVersion, final String indent) { if (0 == sinceVersion) { @@ -1871,6 +1871,7 @@ private CharSequence generateVarWrapFieldNotPresentCondition(final int sinceVers indent + " if (parentMessage.actingVersion < " + sinceVersion + ")\n" + indent + " {\n" + indent + " wrapBuffer.wrap(buffer, offset, 0);\n" + + indent + " return;\n" + indent + " }\n\n"; } @@ -2060,6 +2061,51 @@ private CharSequence generatePrimitiveArrayPropertyDecode( offset); } } + else if (encoding.primitiveType() == PrimitiveType.UINT8) + { + new Formatter(sb).format("\n" + + indent + " public int get%s(final byte[] dst, final int dstOffset, final int length)\n" + + indent + " {\n" + + "%s" + + indent + " final int bytesCopied = Math.min(length, %d);\n" + + indent + " buffer.getBytes(offset + %d, dst, dstOffset, bytesCopied);\n" + + indent + " return bytesCopied;\n" + + indent + " }\n", + Generators.toUpperFirstChar(propertyName), + generateArrayFieldNotPresentCondition(propertyToken.version(), indent), + fieldLength, + offset + ); + + new Formatter(sb).format("\n" + + indent + " public int get%s(final %s dst, final int dstOffset, final int length)\n" + + indent + " {\n" + + "%s" + + indent + " final int bytesCopied = Math.min(length, %d);\n" + + indent + " buffer.getBytes(offset + %d, dst, dstOffset, bytesCopied);\n" + + indent + " return bytesCopied;\n" + + indent + " }\n", + Generators.toUpperFirstChar(propertyName), + fqMutableBuffer, + generateArrayFieldNotPresentCondition(propertyToken.version(), indent), + fieldLength, + offset + ); + + new Formatter(sb).format("\n" + + indent + " public void wrap%s(final %s wrapBuffer)\n" + + indent + " {\n" + + indent + " final int length = %d;\n" + + "%s" + + indent + " wrapBuffer.wrap(buffer, offset + %d, length);\n" + + indent + " }\n", + Generators.toUpperFirstChar(propertyName), + readOnlyBuffer, + fieldLength, + generateWrapFieldNotPresentCondition(propertyToken.version(), indent), + offset + ); + } return sb; } @@ -2150,6 +2196,11 @@ private CharSequence generatePrimitiveArrayPropertyEncode( generateCharArrayEncodeMethods( containingClassName, propertyName, indent, encoding, offset, arrayLength, sb); } + else if (primitiveType == PrimitiveType.UINT8) + { + generateByteArrayEncodeMethods( + containingClassName, propertyName, indent, offset, arrayLength, sb); + } return sb; } @@ -2261,6 +2312,60 @@ private void generateCharArrayEncodeMethods( } } + private void generateByteArrayEncodeMethods( + final String containingClassName, + final String propertyName, + final String indent, + final int offset, + final int fieldLength, + final StringBuilder sb) + { + new Formatter(sb).format("\n" + + indent + " public %s put%s(final byte[] src, final int srcOffset, final int length)\n" + + indent + " {\n" + + indent + " if (length > %d)\n" + + indent + " {\n" + + indent + " throw new IllegalStateException(" + + "\"length > maxValue for type: \" + length);" + + indent + " }\n\n" + + indent + " buffer.putBytes(offset + %d, src, srcOffset, length);\n" + + indent + " for (int i = length; i < %d; i++)\n" + + indent + " {\n" + + indent + " buffer.putByte(offset + %d + i, (byte)0);\n" + + indent + " }\n" + + indent + " return this;\n" + + indent + " }\n", + formatClassName(containingClassName), + Generators.toUpperFirstChar(propertyName), + fieldLength, + offset, + fieldLength, + offset); + + new Formatter(sb).format("\n" + + indent + " public %s put%s(final %s src, final int srcOffset, final int length)\n" + + indent + " {\n" + + indent + " if (length > %d)\n" + + indent + " {\n" + + indent + " throw new IllegalStateException(" + + "\"length > maxValue for type: \" + length);" + + indent + " }\n\n" + + indent + " buffer.putBytes(offset + %d, src, srcOffset, length);\n" + + indent + " for (int i = length; i < %d; i++)\n" + + indent + " {\n" + + indent + " buffer.putByte(offset + %d + i, (byte)0);\n" + + indent + " }\n" + + indent + " return this;\n" + + indent + " }\n", + formatClassName(containingClassName), + Generators.toUpperFirstChar(propertyName), + fqReadOnlyBuffer, + fieldLength, + offset, + fieldLength, + offset); + } + private static int sizeOfPrimitive(final Encoding encoding) { return encoding.primitiveType().size(); diff --git a/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/FixedSizeDataGeneratorTest.java b/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/FixedSizeDataGeneratorTest.java new file mode 100644 index 0000000000..3a052473b0 --- /dev/null +++ b/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/FixedSizeDataGeneratorTest.java @@ -0,0 +1,200 @@ +/* + * Copyright 2013-2020 Real Logic Limited. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package uk.co.real_logic.sbe.generation.java; + +import org.agrona.DirectBuffer; +import org.agrona.MutableDirectBuffer; +import org.agrona.concurrent.UnsafeBuffer; +import org.agrona.generation.CompilerUtil; +import org.agrona.generation.StringWriterOutputManager; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import uk.co.real_logic.sbe.Tests; +import uk.co.real_logic.sbe.ir.Ir; +import uk.co.real_logic.sbe.xml.IrGenerator; +import uk.co.real_logic.sbe.xml.MessageSchema; +import uk.co.real_logic.sbe.xml.ParserOptions; + +import java.nio.charset.StandardCharsets; +import java.util.Map; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.*; +import static uk.co.real_logic.sbe.generation.java.ReflectionUtil.*; +import static uk.co.real_logic.sbe.xml.XmlSchemaParser.parse; + +public class FixedSizeDataGeneratorTest +{ + private static final Class BUFFER_CLASS = MutableDirectBuffer.class; + private static final String BUFFER_NAME = BUFFER_CLASS.getName(); + private static final Class READ_ONLY_BUFFER_CLASS = DirectBuffer.class; + private static final String READ_ONLY_BUFFER_NAME = READ_ONLY_BUFFER_CLASS.getName(); + + private final StringWriterOutputManager outputManager = new StringWriterOutputManager(); + + private Ir ir; + + @BeforeEach + public void setUp() throws Exception + { + final ParserOptions options = ParserOptions.builder().stopOnError(true).build(); + final MessageSchema schema = parse(Tests.getLocalResource("extension-schema.xml"), options); + final IrGenerator irg = new IrGenerator(); + ir = irg.generate(schema); + + outputManager.clear(); + outputManager.setPackageName(ir.applicableNamespace()); + + generator().generate(); + } + + @Test + public void shouldGeneratePutAndGetByteArrayForFixedLengthBlob() throws Exception + { + final UnsafeBuffer buffer = new UnsafeBuffer(new byte[4096]); + + final Object encoder = getTestMessage2Encoder(buffer); + final Object decoder = getTestMessage2Decoder(buffer, encoder); + + final byte[] encodedData = " **DATA** ".getBytes(StandardCharsets.US_ASCII); + byte[] decodedData; + int decodedDataLength; + + // Every byte written, every byte read + putByteArray(encoder, "putTag6", encodedData, 2, 8); + decodedData = " ".getBytes(StandardCharsets.US_ASCII); + decodedDataLength = getByteArray(decoder, "getTag6", decodedData, 1, 8); + assertThat(decodedDataLength, is(8)); + assertThat(new String(decodedData, StandardCharsets.US_ASCII), is(" **DATA** ")); + + // Every byte written, less bytes read + putByteArray(encoder, "putTag6", encodedData, 2, 8); + decodedData = " ".getBytes(StandardCharsets.US_ASCII); + decodedDataLength = getByteArray(decoder, "getTag6", decodedData, 1, 6); + assertThat(decodedDataLength, is(6)); + assertThat(new String(decodedData, StandardCharsets.US_ASCII), is(" **DATA ")); + + // Less bytes written (padding), every byte read + putByteArray(encoder, "putTag6", encodedData, 2, 6); + decodedData = " ".getBytes(StandardCharsets.US_ASCII); + decodedDataLength = getByteArray(decoder, "getTag6", decodedData, 1, 8); + assertThat(decodedDataLength, is(8)); + assertThat(new String(decodedData, StandardCharsets.US_ASCII), is(" **DATA\u0000\u0000 ")); + } + + @Test + public void shouldGeneratePutAndGetDirectBufferForFixedLengthBlob() throws Exception + { + final UnsafeBuffer buffer = new UnsafeBuffer(new byte[4096]); + + final Object encoder = getTestMessage2Encoder(buffer); + final Object decoder = getTestMessage2Decoder(buffer, encoder); + + final UnsafeBuffer encodedData = new UnsafeBuffer(" **DATA** ".getBytes(StandardCharsets.US_ASCII)); + UnsafeBuffer decodedData; + int decodedDataLength; + + // Every byte written, every byte read + putDirectBuffer(encoder, "putTag6", encodedData, 2, 8); + decodedData = new UnsafeBuffer(" ".getBytes(StandardCharsets.US_ASCII)); + decodedDataLength = getDirectBuffer(decoder, "getTag6", decodedData, 1, 8); + assertThat(decodedDataLength, is(8)); + assertThat(decodedData.getStringWithoutLengthAscii(0, 12), is(" **DATA** ")); + + // Every byte written, less bytes read + putDirectBuffer(encoder, "putTag6", encodedData, 2, 8); + decodedData = new UnsafeBuffer(" ".getBytes(StandardCharsets.US_ASCII)); + decodedDataLength = getDirectBuffer(decoder, "getTag6", decodedData, 1, 6); + assertThat(decodedDataLength, is(6)); + assertThat(decodedData.getStringWithoutLengthAscii(0, 12), is(" **DATA ")); + + // Less bytes written (padding), every byte read + putDirectBuffer(encoder, "putTag6", encodedData, 2, 6); + decodedData = new UnsafeBuffer(" ".getBytes(StandardCharsets.US_ASCII)); + decodedDataLength = getDirectBuffer(decoder, "getTag6", decodedData, 1, 8); + assertThat(decodedDataLength, is(8)); + assertThat(decodedData.getStringWithoutLengthAscii(0, 12), is(" **DATA\u0000\u0000 ")); + } + + @Test + public void shouldGenerateWrapForFixedLengthBlob() throws Exception + { + final UnsafeBuffer buffer = new UnsafeBuffer(new byte[4096]); + + final Object encoder = getTestMessage2Encoder(buffer); + final Object decoder = getTestMessage2Decoder(buffer, encoder); + + final UnsafeBuffer encodedData = new UnsafeBuffer(" **DATA** ".getBytes(StandardCharsets.US_ASCII)); + putDirectBuffer(encoder, "putTag6", encodedData, 2, 8); + + final UnsafeBuffer decodedData = new UnsafeBuffer(); + wrapDirectBuffer(decoder, "wrapTag6", decodedData); + assertThat(decodedData.getStringWithoutLengthAscii(0, decodedData.capacity()), is("**DATA**")); + } + + private JavaGenerator generator() + { + return new JavaGenerator(ir, BUFFER_NAME, READ_ONLY_BUFFER_NAME, false, false, false, outputManager); + } + + private Class compile(final String className) throws Exception + { + final String fqClassName = ir.applicableNamespace() + "." + className; + final Map sources = outputManager.getSources(); + final Class aClass = CompilerUtil.compileInMemory(fqClassName, sources); + if (aClass == null) + { + System.out.println(sources); + } + + return aClass; + } + + private Object getTestMessage2Encoder(final UnsafeBuffer buffer) throws Exception + { + final Object encoder = compile("TestMessage2Encoder").getConstructor().newInstance(); + return wrap(0, encoder, buffer, BUFFER_CLASS); + } + + private Object getTestMessage2Decoder(final UnsafeBuffer buffer, final Object encoder) throws Exception + { + final Object decoder = compile("TestMessage2Decoder").getConstructor().newInstance(); + return wrap(buffer, decoder, getSbeBlockLength(encoder), getSbeSchemaVersion(encoder)); + } + + private static Object wrap( + final UnsafeBuffer buffer, final Object decoder, final int blockLength, final int version) throws Exception + { + decoder + .getClass() + .getMethod("wrap", READ_ONLY_BUFFER_CLASS, int.class, int.class, int.class) + .invoke(decoder, buffer, 0, blockLength, version); + + return decoder; + } + + private static Object wrap( + final int bufferOffset, final Object flyweight, final MutableDirectBuffer buffer, final Class bufferClass) + throws Exception + { + flyweight + .getClass() + .getDeclaredMethod("wrap", bufferClass, int.class) + .invoke(flyweight, buffer, bufferOffset); + return flyweight; + } + +} diff --git a/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/JavaGeneratorTest.java b/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/JavaGeneratorTest.java index 39b53cef62..b1b790732c 100644 --- a/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/JavaGeneratorTest.java +++ b/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/JavaGeneratorTest.java @@ -442,6 +442,7 @@ public void shouldGeneratePutCharSequence() throws Exception assertThat(get(decoder, "vehicleCode"), is("R11R12")); } + private Class getModelClass(final Object encoder) throws ClassNotFoundException { final String className = "Model"; diff --git a/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/ReflectionUtil.java b/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/ReflectionUtil.java index 65428b740e..65bb022fee 100644 --- a/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/ReflectionUtil.java +++ b/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/ReflectionUtil.java @@ -15,6 +15,8 @@ */ package uk.co.real_logic.sbe.generation.java; +import org.agrona.DirectBuffer; +import org.agrona.MutableDirectBuffer; import uk.co.real_logic.sbe.generation.Generators; public final class ReflectionUtil @@ -110,6 +112,61 @@ static void set( object.getClass().getMethod(name, type).invoke(object, value); } + static void putByteArray( + final Object encoder, + final String name, + final byte[] value, + final int offset, + final int length) throws Exception + { + encoder.getClass().getDeclaredMethod(name, byte[].class, int.class, int.class) + .invoke(encoder, value, offset, length); + } + + static int getByteArray( + final Object decoder, + final String name, + final byte[] dst, + final int dstOffset, + final int length) throws Exception + { + return (Integer)decoder.getClass().getDeclaredMethod(name, byte[].class, int.class, int.class) + .invoke(decoder, dst, dstOffset, length); + } + + static void putDirectBuffer( + final Object encoder, + final String name, + final DirectBuffer value, + final int offset, + final int length) throws Exception + { + encoder.getClass().getDeclaredMethod(name, DirectBuffer.class, int.class, int.class) + .invoke(encoder, value, offset, length); + } + + static int getDirectBuffer( + final Object decoder, + final String name, + final MutableDirectBuffer dst, + final int dstOffset, + final int length) throws Exception + { + return (Integer)decoder.getClass().getDeclaredMethod(name, MutableDirectBuffer.class, int.class, int.class) + .invoke(decoder, dst, dstOffset, length); + } + + static void wrapDirectBuffer( + final Object decoder, + final String name, + final DirectBuffer dst) throws Exception + { + decoder + .getClass() + .getDeclaredMethod(name, DirectBuffer.class) + .invoke(decoder, dst); + } + static Object getByte(final Class clazz, final byte value) throws Exception { return clazz.getDeclaredMethod("get", byte.class).invoke(null, value); diff --git a/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/SchemaExtensionTest.java b/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/SchemaExtensionTest.java index 335cab9b7c..9d843fb2a1 100644 --- a/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/SchemaExtensionTest.java +++ b/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/SchemaExtensionTest.java @@ -28,13 +28,14 @@ import uk.co.real_logic.sbe.xml.MessageSchema; import uk.co.real_logic.sbe.xml.ParserOptions; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; import java.util.Map; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; import static org.junit.jupiter.api.Assertions.*; -import static uk.co.real_logic.sbe.generation.java.ReflectionUtil.get; -import static uk.co.real_logic.sbe.generation.java.ReflectionUtil.set; +import static uk.co.real_logic.sbe.generation.java.ReflectionUtil.*; import static uk.co.real_logic.sbe.xml.XmlSchemaParser.parse; public class SchemaExtensionTest @@ -184,6 +185,7 @@ public void testMessage1() throws Exception } } + @SuppressWarnings("MethodLength") @Test public void testMessage2() throws Exception { @@ -204,24 +206,45 @@ public void testMessage2() throws Exception final Object setEncoder = encoder.getClass().getMethod("tag5").invoke(encoder); set(setEncoder, "firstChoice", boolean.class, false); set(setEncoder, "secondChoice", boolean.class, true); + + final byte[] data = " **DATA** ".getBytes(StandardCharsets.US_ASCII); + putByteArray(encoder, "putTag6", data, 2, 8); } { // Decode version 0 + final byte[] data = new byte[12]; + Arrays.fill(data, (byte)' '); + final UnsafeBuffer dataBuffer = new UnsafeBuffer(new byte[12]); + dataBuffer.setMemory(0, 12, (byte)' '); + final Object decoderVersion0 = getMessage2Decoder(buffer, 4, 0); assertEquals(100, get(decoderVersion0, "tag1")); assertEquals(Integer.MIN_VALUE, get(decoderVersion0, "tag2")); assertNull(get(decoderVersion0, "tag3")); assertThat(get(decoderVersion0, "tag4").toString(), is("NULL_VAL")); assertNull(get(decoderVersion0, "tag5")); + // tag6 (fixed-size data) + assertEquals(0, getByteArray(decoderVersion0, "getTag6", data, 1, 8)); + assertEquals(" ", new String(data, StandardCharsets.US_ASCII)); + assertEquals(0, getDirectBuffer(decoderVersion0, "getTag6", dataBuffer, 3, 8)); + assertEquals(" ", dataBuffer.getStringWithoutLengthAscii(0, 12)); + wrapDirectBuffer(decoderVersion0, "wrapTag6", dataBuffer); + assertEquals("", dataBuffer.getStringWithoutLengthAscii(0, dataBuffer.capacity())); assertEquals(0, decoderVersion0.getClass().getMethod("tag1SinceVersion").invoke(null)); assertEquals(2, decoderVersion0.getClass().getMethod("tag2SinceVersion").invoke(null)); assertEquals(1, decoderVersion0.getClass().getMethod("tag3SinceVersion").invoke(null)); assertEquals(4, decoderVersion0.getClass().getMethod("tag4SinceVersion").invoke(null)); assertEquals(3, decoderVersion0.getClass().getMethod("tag5SinceVersion").invoke(null)); + assertEquals(6, decoderVersion0.getClass().getMethod("tag6SinceVersion").invoke(null)); } { // Decode version 1 + final byte[] data = new byte[12]; + Arrays.fill(data, (byte)' '); + final UnsafeBuffer dataBuffer = new UnsafeBuffer(new byte[12]); + dataBuffer.setMemory(0, 12, (byte)' '); + final Object decoderVersion1 = getMessage2Decoder(buffer, 8, 1); assertEquals(100, get(decoderVersion1, "tag1")); assertEquals(Integer.MIN_VALUE, get(decoderVersion1, "tag2")); @@ -230,10 +253,22 @@ public void testMessage2() throws Exception assertEquals(300, get(compositeDecoder2, "value")); assertThat(get(decoderVersion1, "tag4").toString(), is("NULL_VAL")); assertNull(get(decoderVersion1, "tag5")); + // tag6 (fixed-size data) + assertEquals(0, getByteArray(decoderVersion1, "getTag6", data, 1, 8)); + assertEquals(" ", new String(data, StandardCharsets.US_ASCII)); + assertEquals(0, getDirectBuffer(decoderVersion1, "getTag6", dataBuffer, 3, 8)); + assertEquals(" ", dataBuffer.getStringWithoutLengthAscii(0, 12)); + wrapDirectBuffer(decoderVersion1, "wrapTag6", dataBuffer); + assertEquals("", dataBuffer.getStringWithoutLengthAscii(0, dataBuffer.capacity())); } { // Decode version 2 - final Object decoderVersion2 = getMessage2Decoder(buffer, 8, 2); + final byte[] data = new byte[12]; + Arrays.fill(data, (byte)' '); + final UnsafeBuffer dataBuffer = new UnsafeBuffer(new byte[12]); + dataBuffer.setMemory(0, 12, (byte)' '); + + final Object decoderVersion2 = getMessage2Decoder(buffer, 12, 2); assertEquals(100, get(decoderVersion2, "tag1")); assertEquals(200, get(decoderVersion2, "tag2")); final Object compositeDecoder2 = get(decoderVersion2, "tag3"); @@ -241,10 +276,22 @@ public void testMessage2() throws Exception assertEquals(300, get(compositeDecoder2, "value")); assertThat(get(decoderVersion2, "tag4").toString(), is("NULL_VAL")); assertNull(get(decoderVersion2, "tag5")); + // tag6 (fixed-size data) + assertEquals(0, getByteArray(decoderVersion2, "getTag6", data, 1, 8)); + assertEquals(" ", new String(data, StandardCharsets.US_ASCII)); + assertEquals(0, getDirectBuffer(decoderVersion2, "getTag6", dataBuffer, 3, 8)); + assertEquals(" ", dataBuffer.getStringWithoutLengthAscii(0, 12)); + wrapDirectBuffer(decoderVersion2, "wrapTag6", dataBuffer); + assertEquals("", dataBuffer.getStringWithoutLengthAscii(0, dataBuffer.capacity())); } { // Decode version 3 - final Object decoderVersion3 = getMessage2Decoder(buffer, 12, 3); + final byte[] data = new byte[12]; + Arrays.fill(data, (byte)' '); + final UnsafeBuffer dataBuffer = new UnsafeBuffer(new byte[12]); + dataBuffer.setMemory(0, 12, (byte)' '); + + final Object decoderVersion3 = getMessage2Decoder(buffer, 13, 3); assertEquals(100, get(decoderVersion3, "tag1")); assertEquals(200, get(decoderVersion3, "tag2")); final Object compositeDecoder3 = get(decoderVersion3, "tag3"); @@ -255,10 +302,22 @@ public void testMessage2() throws Exception assertNotNull(setDecoder); assertEquals(false, get(setDecoder, "firstChoice")); assertEquals(true, get(setDecoder, "secondChoice")); + // tag6 (fixed-size data) + assertEquals(0, getByteArray(decoderVersion3, "getTag6", data, 1, 8)); + assertEquals(" ", new String(data, StandardCharsets.US_ASCII)); + assertEquals(0, getDirectBuffer(decoderVersion3, "getTag6", dataBuffer, 3, 8)); + assertEquals(" ", dataBuffer.getStringWithoutLengthAscii(0, 12)); + wrapDirectBuffer(decoderVersion3, "wrapTag6", dataBuffer); + assertEquals("", dataBuffer.getStringWithoutLengthAscii(0, dataBuffer.capacity())); } { // Decode version 4 - final Object decoderVersion4 = getMessage2Decoder(buffer, 12, 4); + final byte[] data = new byte[12]; + Arrays.fill(data, (byte)' '); + final UnsafeBuffer dataBuffer = new UnsafeBuffer(new byte[12]); + dataBuffer.setMemory(0, 12, (byte)' '); + + final Object decoderVersion4 = getMessage2Decoder(buffer, 14, 4); assertEquals(100, get(decoderVersion4, "tag1")); assertEquals(200, get(decoderVersion4, "tag2")); final Object compositeDecoder4 = get(decoderVersion4, "tag3"); @@ -270,6 +329,67 @@ public void testMessage2() throws Exception assertNotNull(setDecoder); assertEquals(false, get(setDecoder, "firstChoice")); assertEquals(true, get(setDecoder, "secondChoice")); + // tag6 (fixed-size data) + assertEquals(0, getByteArray(decoderVersion4, "getTag6", data, 1, 8)); + assertEquals(" ", new String(data, StandardCharsets.US_ASCII)); + assertEquals(0, getDirectBuffer(decoderVersion4, "getTag6", dataBuffer, 3, 8)); + assertEquals(" ", dataBuffer.getStringWithoutLengthAscii(0, 12)); + wrapDirectBuffer(decoderVersion4, "wrapTag6", dataBuffer); + assertEquals("", dataBuffer.getStringWithoutLengthAscii(0, dataBuffer.capacity())); + } + + { // Decode version 5 + final byte[] data = new byte[12]; + Arrays.fill(data, (byte)' '); + final UnsafeBuffer dataBuffer = new UnsafeBuffer(new byte[12]); + dataBuffer.setMemory(0, 12, (byte)' '); + + final Object decoderVersion5 = getMessage2Decoder(buffer, 14, 5); + assertEquals(100, get(decoderVersion5, "tag1")); + assertEquals(200, get(decoderVersion5, "tag2")); + final Object compositeDecoder4 = get(decoderVersion5, "tag3"); + assertNotNull(compositeDecoder4); + assertEquals(300, get(compositeDecoder4, "value")); + final Object enumConstant = getAEnumConstant(decoderVersion5, "AEnum", 1); + assertEquals(enumConstant, get(decoderVersion5, "tag4")); + final Object setDecoder = get(decoderVersion5, "tag5"); + assertNotNull(setDecoder); + assertEquals(false, get(setDecoder, "firstChoice")); + assertEquals(true, get(setDecoder, "secondChoice")); + // tag6 (fixed-size data) + assertEquals(0, getByteArray(decoderVersion5, "getTag6", data, 1, 8)); + assertEquals(" ", new String(data, StandardCharsets.US_ASCII)); + assertEquals(0, getDirectBuffer(decoderVersion5, "getTag6", dataBuffer, 3, 8)); + assertEquals(" ", dataBuffer.getStringWithoutLengthAscii(0, 12)); + wrapDirectBuffer(decoderVersion5, "wrapTag6", dataBuffer); + assertEquals("", dataBuffer.getStringWithoutLengthAscii(0, dataBuffer.capacity())); + } + + { // Decode version 6 + final byte[] data = new byte[12]; + Arrays.fill(data, (byte)' '); + final UnsafeBuffer dataBuffer = new UnsafeBuffer(new byte[12]); + dataBuffer.setMemory(0, 12, (byte)' '); + + final Object decoderVersion6 = getMessage2Decoder(buffer, 22, 6); + assertEquals(100, get(decoderVersion6, "tag1")); + assertEquals(200, get(decoderVersion6, "tag2")); + final Object compositeDecoder4 = get(decoderVersion6, "tag3"); + assertNotNull(compositeDecoder4); + assertEquals(300, get(compositeDecoder4, "value")); + final Object enumConstant = getAEnumConstant(decoderVersion6, "AEnum", 1); + assertEquals(enumConstant, get(decoderVersion6, "tag4")); + final Object setDecoder = get(decoderVersion6, "tag5"); + assertNotNull(setDecoder); + assertEquals(false, get(setDecoder, "firstChoice")); + assertEquals(true, get(setDecoder, "secondChoice")); + // tag6 (fixed-size data) + assertEquals(8, getByteArray(decoderVersion6, "getTag6", data, 1, 8)); + assertEquals(" **DATA** ", new String(data, StandardCharsets.US_ASCII)); + assertEquals(8, getDirectBuffer(decoderVersion6, "getTag6", dataBuffer, 3, 8)); + assertEquals(" **DATA** ", dataBuffer.getStringWithoutLengthAscii(0, 12)); + wrapDirectBuffer(decoderVersion6, "wrapTag6", dataBuffer); + assertEquals("**DATA**", dataBuffer.getStringWithoutLengthAscii(0, dataBuffer.capacity())); } } diff --git a/sbe-tool/src/test/resources/extension-schema.xml b/sbe-tool/src/test/resources/extension-schema.xml index c50e7d8a6d..5dc79e35fa 100644 --- a/sbe-tool/src/test/resources/extension-schema.xml +++ b/sbe-tool/src/test/resources/extension-schema.xml @@ -2,7 +2,7 @@ @@ -31,6 +31,7 @@ 0 1 + @@ -54,5 +56,6 @@ +