From 4b56197d7e62ae096501c665935a4376960b3136 Mon Sep 17 00:00:00 2001 From: Michael Barker Date: Tue, 17 Aug 2021 15:39:47 +1200 Subject: [PATCH 1/3] [Java] Partial implementation of sbeSkip. --- .../sbe/generation/java/JavaGenerator.java | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) 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 1bcd4798c0..bb2039bb4b 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 @@ -31,6 +31,8 @@ import java.util.function.Function; import static uk.co.real_logic.sbe.SbeTool.JAVA_INTERFACE_PACKAGE; +import static uk.co.real_logic.sbe.generation.cpp.CppUtil.formatClassName; +import static uk.co.real_logic.sbe.generation.cpp.CppUtil.formatPropertyName; import static uk.co.real_logic.sbe.generation.java.JavaGenerator.CodecType.DECODER; import static uk.co.real_logic.sbe.generation.java.JavaGenerator.CodecType.ENCODER; import static uk.co.real_logic.sbe.generation.java.JavaUtil.*; @@ -233,6 +235,7 @@ private void generateDecoder( generateDecoderVarData(sb, varData, BASE_INDENT); generateDecoderDisplay(sb, msgToken.name(), fields, groups, varData); + generateMessageLength(sb, className, groups, varData, BASE_INDENT); out.append(sb); out.append("}\n"); @@ -3655,6 +3658,57 @@ private void appendMessageToString(final StringBuilder sb, final String decoderN append(sb, INDENT, "}"); } + private void generateMessageLength( + final StringBuilder sb, + final String className, final List groups, + final List varData, + final String baseIndent) + { + final String methodIndent = baseIndent + INDENT; + final String bodyIndent = methodIndent + INDENT; + append(sb, methodIndent, ""); + append(sb, methodIndent, "public " + className + " sbeSkip()"); + append(sb, methodIndent, "{"); + 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 groupDecoderName = decoderName(groupToken.name()); + + append(sb, bodyIndent, groupDecoderName + " " + groupName + " = " + groupName + "();"); + append(sb, bodyIndent, "if (" + groupName + ".count() > 0)"); + append(sb, bodyIndent, "{"); + append(sb, bodyIndent, " while (" + groupName + ".hasNext())"); + append(sb, bodyIndent, " {"); + append(sb, bodyIndent, " " + groupName + ".skip();"); + append(sb, bodyIndent, " }"); + append(sb, bodyIndent, "}"); + 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 varDataName = formatPropertyName(varDataToken.name()); + append(sb, bodyIndent, "skip" + Generators.toUpperFirstChar(varDataName) + "();"); + + i += varDataToken.componentTokenCount(); + } + + append(sb, bodyIndent, "return this;"); + append(sb, methodIndent, "}"); + } + private static String validateBufferImplementation( final String fullyQualifiedBufferImplementation, final Class bufferClass) { From 3ff3217fb46fc1a013ac374629f70af1b44726ba Mon Sep 17 00:00:00 2001 From: Michael Barker Date: Wed, 18 Aug 2021 15:09:13 +1200 Subject: [PATCH 2/3] [Java] Complete implementation of sbeSkip and sbeDecodedLength. Pull out common test utility methods. --- .../sbe/generation/java/JavaGenerator.java | 23 +++- .../generation/java/CarDecodeTestUtil.java | 126 ++++++++++++++++++ .../sbe/generation/java/RewindTest.java | 111 +-------------- .../java/SkipAndDecodedLengthTest.java | 67 ++++++++++ 4 files changed, 218 insertions(+), 109 deletions(-) create mode 100644 sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/CarDecodeTestUtil.java create mode 100644 sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/SkipAndDecodedLengthTest.java 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 bb2039bb4b..fd17ae3c4d 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 @@ -235,7 +235,7 @@ private void generateDecoder( generateDecoderVarData(sb, varData, BASE_INDENT); generateDecoderDisplay(sb, msgToken.name(), fields, groups, varData); - generateMessageLength(sb, className, groups, varData, BASE_INDENT); + generateMessageLength(sb, className, true, groups, varData, BASE_INDENT); out.append(sb); out.append("}\n"); @@ -287,6 +287,7 @@ private void generateDecoderGroups( generateDecoderVarData(sb, varData, indent + INDENT); appendGroupInstanceDecoderDisplay(sb, fields, groups, varData, indent + INDENT); + generateMessageLength(sb, groupName, false, groups, varData, indent + INDENT); sb.append(indent).append(" }\n"); } @@ -2582,6 +2583,15 @@ private CharSequence generateDecoderFlyweightCode(final String className, final " public " + className + " sbeRewind()\n" + " {\n" + " return wrap(buffer, initialOffset, actingBlockLength, actingVersion);\n" + + " }\n\n" + + + " public int sbeDecodedLength()\n" + + " {\n" + + " final int currentLimit = limit();\n" + + " sbeSkip();\n" + + " final int decodedLength = encodedLength();\n" + + " limit(currentLimit);\n" + + " return decodedLength;\n" + " }\n\n"; return generateFlyweightCode(DECODER, className, token, methods, readOnlyBuffer); @@ -3660,7 +3670,9 @@ private void appendMessageToString(final StringBuilder sb, final String decoderN private void generateMessageLength( final StringBuilder sb, - final String className, final List groups, + final String className, + final boolean isParent, + final List groups, final List varData, final String baseIndent) { @@ -3669,6 +3681,10 @@ private void generateMessageLength( append(sb, methodIndent, ""); append(sb, methodIndent, "public " + className + " sbeSkip()"); append(sb, methodIndent, "{"); + if (isParent) + { + append(sb, bodyIndent, "sbeRewind();"); + } for (int i = 0, size = groups.size(); i < size; i++) { final Token groupToken = groups.get(i); @@ -3685,7 +3701,8 @@ private void generateMessageLength( append(sb, bodyIndent, "{"); append(sb, bodyIndent, " while (" + groupName + ".hasNext())"); append(sb, bodyIndent, " {"); - append(sb, bodyIndent, " " + groupName + ".skip();"); + append(sb, bodyIndent, " " + groupName + ".next();"); + append(sb, bodyIndent, " " + groupName + ".sbeSkip();"); append(sb, bodyIndent, " }"); append(sb, bodyIndent, "}"); i = findEndSignal(groups, i, Signal.END_GROUP, groupToken.name()); diff --git a/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/CarDecodeTestUtil.java b/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/CarDecodeTestUtil.java new file mode 100644 index 0000000000..87dd7279cc --- /dev/null +++ b/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/CarDecodeTestUtil.java @@ -0,0 +1,126 @@ +/* + * Copyright 2013-2021 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 baseline.CarDecoder; +import baseline.EngineDecoder; +import baseline.OptionalExtrasDecoder; + +import java.util.ArrayList; + +public class CarDecodeTestUtil +{ + static ArrayList getValues(final CarDecoder carDecoder) + { + final ArrayList values = new ArrayList<>(); + + values.add(carDecoder.serialNumber()); + values.add(carDecoder.modelYear()); + values.add(carDecoder.available()); + values.add(carDecoder.code()); + values.add(CarDecoder.someNumbersLength()); + for (int i = 0, n = CarDecoder.someNumbersLength(); i < n; i++) + { + values.add(carDecoder.someNumbers(i)); + } + values.add(carDecoder.vehicleCode()); + final OptionalExtrasDecoder extras = carDecoder.extras(); + values.add(extras.sunRoof()); + values.add(extras.sportsPack()); + values.add(extras.cruiseControl()); + final EngineDecoder engine = carDecoder.engine(); + values.add(engine.capacity()); + values.add(engine.numCylinders()); + values.add(engine.maxRpm()); + values.add(engine.manufacturerCode()); + values.add(engine.fuel()); + final CarDecoder.FuelFiguresDecoder fuelFigures = carDecoder.fuelFigures(); + while (fuelFigures.hasNext()) + { + fuelFigures.next(); + values.add(fuelFigures.speed()); + values.add(fuelFigures.mpg()); + } + final CarDecoder.PerformanceFiguresDecoder performanceFigures = carDecoder.performanceFigures(); + while (performanceFigures.hasNext()) + { + performanceFigures.next(); + values.add(performanceFigures.octaneRating()); + final CarDecoder.PerformanceFiguresDecoder.AccelerationDecoder acceleration = + performanceFigures.acceleration(); + while (acceleration.hasNext()) + { + acceleration.next(); + values.add(acceleration.mph()); + values.add(acceleration.seconds()); + } + } + values.add(carDecoder.manufacturer()); + values.add(carDecoder.model()); + values.add(carDecoder.activationCode()); + return values; + } + + static ArrayList getPartialValues(final CarDecoder carDecoder) + { + final ArrayList values = new ArrayList<>(); + + values.add(carDecoder.serialNumber()); + values.add(carDecoder.modelYear()); + values.add(carDecoder.available()); + values.add(carDecoder.code()); + values.add(CarDecoder.someNumbersLength()); + for (int i = 0, n = CarDecoder.someNumbersLength(); i < n; i++) + { + values.add(carDecoder.someNumbers(i)); + } + values.add(carDecoder.vehicleCode()); + final OptionalExtrasDecoder extras = carDecoder.extras(); + values.add(extras.sunRoof()); + values.add(extras.sportsPack()); + values.add(extras.cruiseControl()); + final EngineDecoder engine = carDecoder.engine(); + values.add(engine.capacity()); + values.add(engine.numCylinders()); + values.add(engine.maxRpm()); + values.add(engine.manufacturerCode()); + values.add(engine.fuel()); + final CarDecoder.FuelFiguresDecoder fuelFigures = carDecoder.fuelFigures(); + while (fuelFigures.hasNext()) + { + fuelFigures.next(); + values.add(fuelFigures.speed()); + values.add(fuelFigures.mpg()); + } + final CarDecoder.PerformanceFiguresDecoder performanceFigures = carDecoder.performanceFigures(); + + // Stop decoding part way through the message. + if (performanceFigures.hasNext()) + { + performanceFigures.next(); + values.add(performanceFigures.octaneRating()); + final CarDecoder.PerformanceFiguresDecoder.AccelerationDecoder acceleration = + performanceFigures.acceleration(); + if (acceleration.hasNext()) + { + acceleration.next(); + values.add(acceleration.mph()); + } + } + + return values; + } +} diff --git a/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/RewindTest.java b/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/RewindTest.java index 487dd441f1..9385da574c 100644 --- a/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/RewindTest.java +++ b/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/RewindTest.java @@ -16,9 +16,7 @@ package uk.co.real_logic.sbe.generation.java; import baseline.CarDecoder; -import baseline.EngineDecoder; import baseline.MessageHeaderDecoder; -import baseline.OptionalExtrasDecoder; import org.agrona.concurrent.UnsafeBuffer; import org.junit.jupiter.api.Test; import uk.co.real_logic.sbe.EncodedCarTestBase; @@ -43,120 +41,21 @@ void shouldRewindAfterReadingFullMessage() final CarDecoder carDecoder = new CarDecoder(); carDecoder.wrapAndApplyHeader(new UnsafeBuffer(encodedMsgBuffer), 0, header); - final ArrayList passOne = getValues(carDecoder); + final ArrayList passOne = CarDecodeTestUtil.getValues(carDecoder); carDecoder.sbeRewind(); - final ArrayList passTwo = getValues(carDecoder); + final ArrayList passTwo = CarDecodeTestUtil.getValues(carDecoder); assertEquals(passOne, passTwo); carDecoder.sbeRewind(); - final ArrayList partialPassOne = getPartialValues(carDecoder); + final ArrayList partialPassOne = CarDecodeTestUtil.getPartialValues(carDecoder); carDecoder.sbeRewind(); - final ArrayList partialPassTwo = getPartialValues(carDecoder); + final ArrayList partialPassTwo = CarDecodeTestUtil.getPartialValues(carDecoder); assertNotEquals(passOne, partialPassOne); assertEquals(partialPassOne, partialPassTwo); carDecoder.sbeRewind(); - final ArrayList passThree = getValues(carDecoder); + final ArrayList passThree = CarDecodeTestUtil.getValues(carDecoder); assertEquals(passOne, passThree); } - private ArrayList getValues(final CarDecoder carDecoder) - { - final ArrayList values = new ArrayList<>(); - - values.add(carDecoder.serialNumber()); - values.add(carDecoder.modelYear()); - values.add(carDecoder.available()); - values.add(carDecoder.code()); - values.add(CarDecoder.someNumbersLength()); - for (int i = 0, n = CarDecoder.someNumbersLength(); i < n; i++) - { - values.add(carDecoder.someNumbers(i)); - } - values.add(carDecoder.vehicleCode()); - final OptionalExtrasDecoder extras = carDecoder.extras(); - values.add(extras.sunRoof()); - values.add(extras.sportsPack()); - values.add(extras.cruiseControl()); - final EngineDecoder engine = carDecoder.engine(); - values.add(engine.capacity()); - values.add(engine.numCylinders()); - values.add(engine.maxRpm()); - values.add(engine.manufacturerCode()); - values.add(engine.fuel()); - final CarDecoder.FuelFiguresDecoder fuelFigures = carDecoder.fuelFigures(); - while (fuelFigures.hasNext()) - { - fuelFigures.next(); - values.add(fuelFigures.speed()); - values.add(fuelFigures.mpg()); - } - final CarDecoder.PerformanceFiguresDecoder performanceFigures = carDecoder.performanceFigures(); - while (performanceFigures.hasNext()) - { - performanceFigures.next(); - values.add(performanceFigures.octaneRating()); - final CarDecoder.PerformanceFiguresDecoder.AccelerationDecoder acceleration = - performanceFigures.acceleration(); - while (acceleration.hasNext()) - { - acceleration.next(); - values.add(acceleration.mph()); - values.add(acceleration.seconds()); - } - } - values.add(carDecoder.manufacturer()); - values.add(carDecoder.model()); - return values; - } - - private ArrayList getPartialValues(final CarDecoder carDecoder) - { - final ArrayList values = new ArrayList<>(); - - values.add(carDecoder.serialNumber()); - values.add(carDecoder.modelYear()); - values.add(carDecoder.available()); - values.add(carDecoder.code()); - values.add(CarDecoder.someNumbersLength()); - for (int i = 0, n = CarDecoder.someNumbersLength(); i < n; i++) - { - values.add(carDecoder.someNumbers(i)); - } - values.add(carDecoder.vehicleCode()); - final OptionalExtrasDecoder extras = carDecoder.extras(); - values.add(extras.sunRoof()); - values.add(extras.sportsPack()); - values.add(extras.cruiseControl()); - final EngineDecoder engine = carDecoder.engine(); - values.add(engine.capacity()); - values.add(engine.numCylinders()); - values.add(engine.maxRpm()); - values.add(engine.manufacturerCode()); - values.add(engine.fuel()); - final CarDecoder.FuelFiguresDecoder fuelFigures = carDecoder.fuelFigures(); - while (fuelFigures.hasNext()) - { - fuelFigures.next(); - values.add(fuelFigures.speed()); - values.add(fuelFigures.mpg()); - } - final CarDecoder.PerformanceFiguresDecoder performanceFigures = carDecoder.performanceFigures(); - - // Stop decoding part way through the message. - if (performanceFigures.hasNext()) - { - performanceFigures.next(); - values.add(performanceFigures.octaneRating()); - final CarDecoder.PerformanceFiguresDecoder.AccelerationDecoder acceleration = - performanceFigures.acceleration(); - if (acceleration.hasNext()) - { - acceleration.next(); - values.add(acceleration.mph()); - } - } - - return values; - } } diff --git a/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/SkipAndDecodedLengthTest.java b/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/SkipAndDecodedLengthTest.java new file mode 100644 index 0000000000..8f69440b8c --- /dev/null +++ b/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/java/SkipAndDecodedLengthTest.java @@ -0,0 +1,67 @@ +/* + * Copyright 2013-2021 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 baseline.CarDecoder; +import baseline.MessageHeaderDecoder; +import org.agrona.concurrent.UnsafeBuffer; +import org.junit.jupiter.api.Test; +import uk.co.real_logic.sbe.EncodedCarTestBase; + +import java.nio.ByteBuffer; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class SkipAndDecodedLengthTest extends EncodedCarTestBase +{ + private static final int MSG_BUFFER_CAPACITY = 4 * 1024; + + @Test + void shouldRewindAfterReadingFullMessage() + { + final ByteBuffer encodedMsgBuffer = ByteBuffer.allocate(MSG_BUFFER_CAPACITY); + encodeTestMessage(encodedMsgBuffer); + + final int encodedLength = CAR.encodedLength(); + + final MessageHeaderDecoder header = new MessageHeaderDecoder(); + final CarDecoder carDecoder = new CarDecoder(); + carDecoder.wrapAndApplyHeader(new UnsafeBuffer(encodedMsgBuffer), 0, header); + final int decodedLengthNoRead = carDecoder.sbeDecodedLength(); + + final int initialLimit = carDecoder.limit(); + CarDecodeTestUtil.getValues(carDecoder); + final int readLimit = carDecoder.limit(); + + carDecoder.sbeRewind(); + final int rewindLimit = carDecoder.limit(); + carDecoder.sbeSkip(); + + final int skipLimit = carDecoder.limit(); + final int decodedLengthFullSkip = carDecoder.sbeDecodedLength(); + carDecoder.sbeRewind(); + final int decodedLengthAfterRewind = carDecoder.sbeDecodedLength(); + CarDecodeTestUtil.getPartialValues(carDecoder); + final int decodedLengthPartialRead = carDecoder.sbeDecodedLength(); + + assertEquals(initialLimit, rewindLimit); + assertEquals(readLimit, skipLimit); + assertEquals(encodedLength, decodedLengthNoRead); + assertEquals(encodedLength, decodedLengthFullSkip); + assertEquals(encodedLength, decodedLengthAfterRewind); + assertEquals(encodedLength, decodedLengthPartialRead); + } +} From 88e2d936698a009dafcd9643331db2750574ece2 Mon Sep 17 00:00:00 2001 From: Michael Barker Date: Wed, 18 Aug 2021 16:11:03 +1200 Subject: [PATCH 3/3] [Java] Regenerate IR codecs. --- .../sbe/ir/generated/FrameCodecDecoder.java | 18 +++++++++++++ .../sbe/ir/generated/TokenCodecDecoder.java | 26 +++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecDecoder.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecDecoder.java index efb7c33192..ce7cd2a13c 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecDecoder.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecDecoder.java @@ -109,6 +109,15 @@ public FrameCodecDecoder sbeRewind() return wrap(buffer, initialOffset, actingBlockLength, actingVersion); } + public int sbeDecodedLength() + { + final int currentLimit = limit(); + sbeSkip(); + final int decodedLength = encodedLength(); + limit(currentLimit); + return decodedLength; + } + public int encodedLength() { return limit - offset; @@ -664,4 +673,13 @@ public StringBuilder appendTo(final StringBuilder builder) return builder; } + + public FrameCodecDecoder sbeSkip() + { + sbeRewind(); + skipPackageName(); + skipNamespaceName(); + skipSemanticVersion(); + return this; + } } diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecDecoder.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecDecoder.java index 98a58e85d4..bc1953b583 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecDecoder.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecDecoder.java @@ -109,6 +109,15 @@ public TokenCodecDecoder sbeRewind() return wrap(buffer, initialOffset, actingBlockLength, actingVersion); } + public int sbeDecodedLength() + { + final int currentLimit = limit(); + sbeSkip(); + final int decodedLength = encodedLength(); + limit(currentLimit); + return decodedLength; + } + public int encodedLength() { return limit - offset; @@ -1890,4 +1899,21 @@ public StringBuilder appendTo(final StringBuilder builder) return builder; } + + public TokenCodecDecoder sbeSkip() + { + sbeRewind(); + skipName(); + skipConstValue(); + skipMinValue(); + skipMaxValue(); + skipNullValue(); + skipCharacterEncoding(); + skipEpoch(); + skipTimeUnit(); + skipSemanticType(); + skipDescription(); + skipReferencedName(); + return this; + } }