topLevelBlockFields)
+ {
+
+ this.groupPathsByField = groupPathsByField;
+ this.topLevelBlockFields = topLevelBlockFields;
+ }
+
+ /**
+ * Find or create a {@link CodecInteraction} to represent wrapping a codec around
+ * the given version of data.
+ * @param version the version of data to wrap
+ * @return the {@link CodecInteraction} instance
+ */
+ public CodecInteraction wrap(final int version)
+ {
+ return wrapInteractions.computeIfAbsent(version, Wrap::new);
+ }
+
+ /**
+ * Find or create a {@link CodecInteraction} to represent accessing the field identified
+ * by the given token.
+ *
+ * The supplied token must carry a {@link Signal#BEGIN_FIELD}
+ * or {@link Signal#BEGIN_VAR_DATA} signal.
+ *
+ * @param token the token identifying the field
+ * @return the {@link CodecInteraction} instance
+ */
+ public CodecInteraction accessField(final Token token)
+ {
+ return accessFieldInteractions.computeIfAbsent(token,
+ t -> new AccessField(groupPathsByField.get(t), t, topLevelBlockFields.contains(t)));
+ }
+
+ /**
+ * Find or create a {@link CodecInteraction} to represent determining a
+ * repeating group is empty.
+ *
+ *
For encoding, this will be when the group count is supplied, e.g.,
+ * {@code encoder.myGroupCount(0)}.
+ *
+ *
For decoding, this will be when the group is read, e.g.,
+ * {@code decoder.myGroup()}.
+ *
+ *
The supplied token must carry a {@link Signal#BEGIN_GROUP} signal.
+ *
+ * @param token the token identifying the group
+ * @return the {@link CodecInteraction} instance
+ */
+ public CodecInteraction determineGroupIsEmpty(final Token token)
+ {
+ return determineGroupIsEmptyInteractions.computeIfAbsent(token,
+ t -> new DetermineGroupIsEmpty(groupPathsByField.get(t), t));
+ }
+
+ /**
+ * Find or create a {@link CodecInteraction} to represent determining a
+ * repeating group has elements.
+ *
+ *
For encoding, this will be when the group count is supplied, e.g.,
+ * {@code encoder.myGroupCount(1)}.
+ *
+ *
For decoding, this will be when the group is read, e.g.,
+ * {@code decoder.myGroup()}.
+ *
+ *
The supplied token must carry a {@link Signal#BEGIN_GROUP} signal.
+ *
+ * @param token the token identifying the group
+ * @return the {@link CodecInteraction} instance
+ */
+ public CodecInteraction determineGroupHasElements(final Token token)
+ {
+ return determineGroupHasElementsInteractions.computeIfAbsent(token,
+ t -> new DetermineGroupHasElements(groupPathsByField.get(t), t));
+ }
+
+ /**
+ * Find or create a {@link CodecInteraction} to represent moving to the next
+ * element in a repeating group.
+ *
+ *
For encoders, decoders, and codecs, this will be when the next element
+ * is accessed, e.g., {@code myGroup.next()} when {@code myGroup.count - myGroup.index > 1}.
+ *
+ *
The supplied token must carry a {@link Signal#BEGIN_GROUP} signal.
+ *
+ * @param token the token identifying the group
+ * @return the {@link CodecInteraction} instance
+ */
+ public CodecInteraction moveToNextElement(final Token token)
+ {
+ return moveToNextElementInteractions.computeIfAbsent(token,
+ t -> new MoveToNextElement(groupPathsByField.get(t), t));
+ }
+
+ /**
+ * Find or create a {@link CodecInteraction} to represent moving to the last
+ * element in a repeating group.
+ *
+ *
For encoders, decoders, and codecs, this will be when the last element
+ * is accessed, e.g., {@code myGroup.next()} when {@code myGroup.count - myGroup.index == 1}.
+ *
+ * @param token the token identifying the group
+ * @return the {@link CodecInteraction} instance
+ */
+ public CodecInteraction moveToLastElement(final Token token)
+ {
+ return moveToLastElementInteractions.computeIfAbsent(token,
+ t -> new MoveToLastElement(groupPathsByField.get(t), t));
+ }
+
+ /**
+ * Find or create a {@link CodecInteraction} to represent resetting the count
+ * of a repeating group to the current index.
+ *
+ *
For encoders, decoders, and codecs, this will be when the {@code resetCountToIndex}
+ * method is called, e.g., {@code myGroup.resetCountToIndex()}.
+ *
+ *
The supplied token must carry a {@link Signal#BEGIN_GROUP} signal.
+ *
+ * @param token the token identifying the group
+ * @return the {@link CodecInteraction} instance
+ */
+ public CodecInteraction resetCountToIndex(final Token token)
+ {
+ return resetCountToIndexInteractions.computeIfAbsent(token,
+ t -> new ResetCountToIndex(groupPathsByField.get(t), t));
+ }
+
+ /**
+ * Find or create a {@link CodecInteraction} to represent accessing the length
+ * of a variable-length data field without advancing the codec position.
+ *
+ *
For decoders and codecs, this will be when the length is accessed, e.g.,
+ * {@code decoder.myVarDataLength()}.
+ *
+ *
The supplied token must carry a {@link Signal#BEGIN_VAR_DATA} signal.
+ *
+ * @param token the token identifying the field
+ * @return the {@link CodecInteraction} instance
+ */
+ public CodecInteraction accessVarDataLength(final Token token)
+ {
+ return accessVarDataLengthInteractions.computeIfAbsent(token,
+ t -> new AccessVarDataLength(groupPathsByField.get(t), t));
+ }
+ }
+ }
+}
diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/common/PrecedenceChecks.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/common/PrecedenceChecks.java
new file mode 100644
index 0000000000..9825a3e6b7
--- /dev/null
+++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/common/PrecedenceChecks.java
@@ -0,0 +1,253 @@
+/*
+ * Copyright 2013-2023 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.common;
+
+import uk.co.real_logic.sbe.ir.Token;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.OptionalInt;
+import java.util.function.Function;
+import java.util.stream.IntStream;
+
+import static java.util.Objects.requireNonNull;
+import static uk.co.real_logic.sbe.ir.GenerationUtil.collectFields;
+import static uk.co.real_logic.sbe.ir.GenerationUtil.collectGroups;
+import static uk.co.real_logic.sbe.ir.GenerationUtil.collectVarData;
+
+/**
+ * A factory for creating a {@link FieldPrecedenceModel} for a message from its IR tokens.
+ */
+public final class PrecedenceChecks
+{
+ private static final Function SELECT_LATEST_VERSION_ONLY = versions ->
+ {
+ final OptionalInt max = versions.max();
+ return max.isPresent() ? IntStream.of(max.getAsInt()) : IntStream.empty();
+ };
+
+ private final Context context;
+
+ private PrecedenceChecks(final Context context)
+ {
+ context.conclude();
+ this.context = context;
+ }
+
+ /**
+ * Creates a {@link FieldPrecedenceModel} for the given message tokens,
+ * unless precedence checks are disabled, in which case {@code null} is returned.
+ *
+ * Only the latest version of the message is considered when creating the model.
+ *
+ *
+ * @param stateClassName the name of the generated class that models the state of the encoder.
+ * @param msgTokens the tokens of the message.
+ * @return a {@link FieldPrecedenceModel} for the given message tokens or {@code null} if precedence checks
+ * are disabled.
+ */
+ public FieldPrecedenceModel createEncoderModel(
+ final String stateClassName,
+ final List msgTokens)
+ {
+ return createModel(stateClassName, msgTokens, SELECT_LATEST_VERSION_ONLY);
+ }
+
+ /**
+ * Creates a {@link FieldPrecedenceModel} for the given message tokens,
+ * unless precedence checks are disabled, in which case {@code null} is returned.
+ *
+ * All versions of the message are considered when creating the model.
+ *
+ *
+ * @param stateClassName the name of the generated class that models the state of the decoder.
+ * @param msgTokens the tokens of the message.
+ * @return a {@link FieldPrecedenceModel} for the given message tokens or {@code null} if precedence checks
+ * are disabled.
+ */
+ public FieldPrecedenceModel createDecoderModel(
+ final String stateClassName,
+ final List msgTokens)
+ {
+ return createModel(stateClassName, msgTokens, Function.identity());
+ }
+
+ /**
+ * Creates a {@link FieldPrecedenceModel} for the given message tokens,
+ * unless precedence checks are disabled, in which case {@code null} is returned.
+ *
+ * All versions of the message are considered when creating the model.
+ *
+ *
+ * @param stateClassName the name of the generated class that models the state of the codec.
+ * @param msgTokens the tokens of the message.
+ * @return a {@link FieldPrecedenceModel} for the given message tokens or {@code null} if precedence checks
+ * are disabled.
+ */
+ public FieldPrecedenceModel createCodecModel(
+ final String stateClassName,
+ final List msgTokens
+ )
+ {
+ return createModel(stateClassName, msgTokens, Function.identity());
+ }
+
+ /**
+ * Returns the {@link Context} describing how precedence checks should be generated.
+ *
+ * @return the {@link Context} describing how precedence checks should be generated.
+ */
+ public Context context()
+ {
+ return context;
+ }
+
+ /**
+ * Creates a new {@link PrecedenceChecks} instance.
+ *
+ * @param context the {@link Context} describing how precedence checks should be generated.
+ * @return a new {@link PrecedenceChecks} instance.
+ */
+ public static PrecedenceChecks newInstance(final Context context)
+ {
+ return new PrecedenceChecks(context);
+ }
+
+ private FieldPrecedenceModel createModel(
+ final String stateClassName,
+ final List tokens,
+ final Function versionsSelector
+ )
+ {
+ if (context.shouldGeneratePrecedenceChecks())
+ {
+ final Token msgToken = tokens.get(0);
+
+ final List messageBody = tokens.subList(1, tokens.size() - 1);
+ int i = 0;
+
+ final List fields = new ArrayList<>();
+ i = collectFields(messageBody, i, fields);
+
+ final List groups = new ArrayList<>();
+ i = collectGroups(messageBody, i, groups);
+
+ final List varData = new ArrayList<>();
+ collectVarData(messageBody, i, varData);
+
+ return FieldPrecedenceModel.newInstance(
+ stateClassName,
+ msgToken,
+ fields,
+ groups,
+ varData,
+ versionsSelector);
+ }
+
+ return null;
+ }
+
+ /**
+ * The context describing how precedence checks should be generated.
+ */
+ public static final class Context
+ {
+ private boolean shouldGeneratePrecedenceChecks;
+ private String precedenceChecksFlagName = "SBE_ENABLE_PRECEDENCE_CHECKS";
+ private String precedenceChecksPropName = "sbe.enable.precedence.checks";
+
+ /**
+ * Returns {@code true} if precedence checks should be generated; {@code false} otherwise.
+ *
+ * @return {@code true} if precedence checks should be generated; {@code false} otherwise.
+ */
+ public boolean shouldGeneratePrecedenceChecks()
+ {
+ return shouldGeneratePrecedenceChecks;
+ }
+
+ /**
+ * Sets whether field precedence checks should be generated.
+ *
+ * @param shouldGeneratePrecedenceChecks {@code true} if precedence checks should be generated;
+ * {@code false} otherwise.
+ * @return this {@link Context} instance.
+ */
+ public Context shouldGeneratePrecedenceChecks(final boolean shouldGeneratePrecedenceChecks)
+ {
+ this.shouldGeneratePrecedenceChecks = shouldGeneratePrecedenceChecks;
+ return this;
+ }
+
+ /**
+ * Returns the name of the flag that can be used to enable precedence checks at runtime.
+ *
+ * @return the name of the flag that can be used to enable precedence checks at runtime.
+ */
+ public String precedenceChecksFlagName()
+ {
+ return precedenceChecksFlagName;
+ }
+
+ /**
+ * Sets the name of the flag that can be used to enable precedence checks at runtime.
+ *
+ * @param precedenceChecksFlagName the name of the flag that can be used to enable
+ * precedence checks at runtime.
+ * @return this {@link Context} instance.
+ */
+ public Context precedenceChecksFlagName(final String precedenceChecksFlagName)
+ {
+ this.precedenceChecksFlagName = precedenceChecksFlagName;
+ return this;
+ }
+
+ /**
+ * Returns the name of the system property that can be used to enable precedence checks
+ * at runtime.
+ *
+ * @return the name of the system property that can be used to enable precedence checks
+ * at runtime.
+ */
+ public String precedenceChecksPropName()
+ {
+ return precedenceChecksPropName;
+ }
+
+ /**
+ * Sets the name of the system property that can be used to enable precedence checks
+ * at runtime.
+ *
+ * @param precedenceChecksPropName the name of the system property that can be used to
+ * enable precedence checks at runtime.
+ * @return this {@link Context} instance.
+ */
+ public Context precedenceChecksPropName(final String precedenceChecksPropName)
+ {
+ this.precedenceChecksPropName = precedenceChecksPropName;
+ return this;
+ }
+
+ /**
+ * Validates this {@link Context} instance.
+ */
+ public void conclude()
+ {
+ requireNonNull(precedenceChecksFlagName, "precedenceChecksFlagName");
+ requireNonNull(precedenceChecksPropName, "precedenceChecksPropName");
+ }
+ }
+}
diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/cpp/CppGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/cpp/CppGenerator.java
index 482335b269..c801c58057 100755
--- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/cpp/CppGenerator.java
+++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/cpp/CppGenerator.java
@@ -15,23 +15,24 @@
*/
package uk.co.real_logic.sbe.generation.cpp;
-import org.agrona.Strings;
-import org.agrona.Verify;
-import org.agrona.generation.OutputManager;
import uk.co.real_logic.sbe.PrimitiveType;
import uk.co.real_logic.sbe.generation.CodeGenerator;
import uk.co.real_logic.sbe.generation.Generators;
+import uk.co.real_logic.sbe.generation.common.FieldPrecedenceModel;
+import uk.co.real_logic.sbe.generation.common.PrecedenceChecks;
import uk.co.real_logic.sbe.ir.Encoding;
import uk.co.real_logic.sbe.ir.Ir;
import uk.co.real_logic.sbe.ir.Signal;
import uk.co.real_logic.sbe.ir.Token;
+import org.agrona.Strings;
+import org.agrona.Verify;
+import org.agrona.collections.MutableBoolean;
+import org.agrona.generation.OutputManager;
import java.io.IOException;
import java.io.Writer;
import java.nio.ByteOrder;
-import java.util.ArrayList;
-import java.util.Formatter;
-import java.util.List;
+import java.util.*;
import static uk.co.real_logic.sbe.generation.Generators.toLowerFirstChar;
import static uk.co.real_logic.sbe.generation.Generators.toUpperFirstChar;
@@ -45,12 +46,18 @@
@SuppressWarnings("MethodLength")
public class CppGenerator implements CodeGenerator
{
+ private static final boolean DISABLE_IMPLICIT_COPYING = Boolean.parseBoolean(
+ System.getProperty("sbe.cpp.disable.implicit.copying", "false"));
private static final String BASE_INDENT = "";
private static final String INDENT = " ";
+ private static final String TWO_INDENT = INDENT + INDENT;
+ private static final String THREE_INDENT = TWO_INDENT + INDENT;
private final Ir ir;
private final OutputManager outputManager;
private final boolean shouldDecodeUnknownEnumValues;
+ private final PrecedenceChecks precedenceChecks;
+ private final String precedenceChecksFlagName;
/**
* Create a new Go language {@link CodeGenerator}.
@@ -60,12 +67,36 @@ public class CppGenerator implements CodeGenerator
* @param outputManager for generating the codecs to.
*/
public CppGenerator(final Ir ir, final boolean shouldDecodeUnknownEnumValues, final OutputManager outputManager)
+ {
+ this(
+ ir,
+ shouldDecodeUnknownEnumValues,
+ PrecedenceChecks.newInstance(new PrecedenceChecks.Context()),
+ outputManager
+ );
+ }
+
+ /**
+ * Create a new Go language {@link CodeGenerator}.
+ *
+ * @param ir for the messages and types.
+ * @param shouldDecodeUnknownEnumValues generate support for unknown enum values when decoding.
+ * @param precedenceChecks whether and how to generate field precedence checks.
+ * @param outputManager for generating the codecs to.
+ */
+ public CppGenerator(
+ final Ir ir,
+ final boolean shouldDecodeUnknownEnumValues,
+ final PrecedenceChecks precedenceChecks,
+ final OutputManager outputManager)
{
Verify.notNull(ir, "ir");
Verify.notNull(outputManager, "outputManager");
this.ir = ir;
this.shouldDecodeUnknownEnumValues = shouldDecodeUnknownEnumValues;
+ this.precedenceChecks = precedenceChecks;
+ this.precedenceChecksFlagName = precedenceChecks.context().precedenceChecksFlagName();
this.outputManager = outputManager;
}
@@ -143,12 +174,11 @@ public void generate() throws IOException
{
final Token msgToken = tokens.get(0);
final String className = formatClassName(msgToken.name());
+ final String stateClassName = className + "::CodecState";
+ final FieldPrecedenceModel fieldPrecedenceModel = precedenceChecks.createCodecModel(stateClassName, tokens);
try (Writer out = outputManager.createOutput(className))
{
- out.append(generateFileHeader(ir.namespaces(), className, typesToInclude));
- out.append(generateClassDeclaration(className));
- out.append(generateMessageFlyweightCode(className, msgToken));
final List messageBody = tokens.subList(1, tokens.size() - 1);
int i = 0;
@@ -162,20 +192,393 @@ public void generate() throws IOException
final List varData = new ArrayList<>();
collectVarData(messageBody, i, varData);
+ out.append(generateFileHeader(ir.namespaces(), className, typesToInclude));
+ out.append(generateClassDeclaration(className));
+ out.append(generateMessageFlyweightCode(className, msgToken, fieldPrecedenceModel));
+ out.append(generateFullyEncodedCheck(fieldPrecedenceModel));
+
final StringBuilder sb = new StringBuilder();
- generateFields(sb, className, fields, BASE_INDENT);
- generateGroups(sb, groups, BASE_INDENT);
- generateVarData(sb, className, varData, BASE_INDENT);
+ generateFields(sb, className, fields, fieldPrecedenceModel, BASE_INDENT);
+ generateGroups(sb, groups, fieldPrecedenceModel, BASE_INDENT);
+ generateVarData(sb, className, varData, fieldPrecedenceModel, BASE_INDENT);
generateDisplay(sb, msgToken.name(), fields, groups, varData);
sb.append(generateMessageLength(groups, varData, BASE_INDENT));
sb.append("};\n");
+ generateLookupTableDefinitions(sb, className, fieldPrecedenceModel);
sb.append(CppUtil.closingBraces(ir.namespaces().length)).append("#endif\n");
out.append(sb);
}
}
}
- private void generateGroups(final StringBuilder sb, final List tokens, final String indent)
+ private CharSequence generateFullyEncodedCheck(final FieldPrecedenceModel fieldPrecedenceModel)
+ {
+ if (null == fieldPrecedenceModel)
+ {
+ return "";
+ }
+
+ final String indent = " ";
+ final StringBuilder sb = new StringBuilder();
+ sb.append("\n");
+
+ sb.append(indent).append("void checkEncodingIsComplete()\n")
+ .append(indent).append("{\n")
+ .append("#if defined(").append(precedenceChecksFlagName).append(")\n")
+ .append(indent).append(INDENT).append("switch (m_codecState)\n")
+ .append(indent).append(INDENT).append("{\n");
+
+ fieldPrecedenceModel.forEachTerminalEncoderState(state ->
+ {
+ sb.append(indent).append(TWO_INDENT).append("case ").append(stateCaseForSwitchCase(state)).append(":\n")
+ .append(indent).append(THREE_INDENT).append("return;\n");
+ });
+
+ sb.append(indent).append(TWO_INDENT).append("default:\n")
+ .append(indent).append(THREE_INDENT)
+ .append("throw AccessOrderError(std::string(\"Not fully encoded, current state: \") +\n")
+ .append(indent).append(THREE_INDENT)
+ .append(INDENT).append("codecStateName(m_codecState) + \", allowed transitions: \" +\n")
+ .append(indent).append(THREE_INDENT)
+ .append(INDENT).append("codecStateTransitions(m_codecState));\n")
+ .append(indent).append(INDENT).append("}\n")
+ .append("#endif\n");
+
+ sb.append(indent).append("}\n\n");
+
+ return sb;
+ }
+
+ private static String accessOrderListenerMethodName(final Token token)
+ {
+ return "on" + Generators.toUpperFirstChar(token.name()) + "Accessed";
+ }
+
+ private static String accessOrderListenerMethodName(final Token token, final String suffix)
+ {
+ return "on" + Generators.toUpperFirstChar(token.name()) + suffix + "Accessed";
+ }
+
+ private static void generateAccessOrderListenerMethod(
+ final StringBuilder sb,
+ final FieldPrecedenceModel fieldPrecedenceModel,
+ final String indent,
+ final Token token)
+ {
+ if (null == fieldPrecedenceModel)
+ {
+ return;
+ }
+
+ final FieldPrecedenceModel.CodecInteraction fieldAccess =
+ fieldPrecedenceModel.interactionFactory().accessField(token);
+
+ final String constDeclaration = canChangeState(fieldPrecedenceModel, fieldAccess) ? "" : " const";
+
+ sb.append("\n")
+ .append(indent).append("private:\n")
+ .append(indent).append(INDENT).append("void ").append(accessOrderListenerMethodName(token)).append("()")
+ .append(constDeclaration).append("\n")
+ .append(indent).append(INDENT).append("{\n");
+
+ generateAccessOrderListener(
+ sb,
+ indent + TWO_INDENT,
+ "access field",
+ fieldPrecedenceModel,
+ fieldAccess);
+
+ sb.append(indent).append(INDENT).append("}\n\n")
+ .append(indent).append("public:");
+ }
+
+ private static boolean canChangeState(
+ final FieldPrecedenceModel fieldPrecedenceModel,
+ final FieldPrecedenceModel.CodecInteraction fieldAccess)
+ {
+ if (fieldAccess.isTopLevelBlockFieldAccess())
+ {
+ return false;
+ }
+
+ final MutableBoolean canChangeState = new MutableBoolean(false);
+ fieldPrecedenceModel.forEachTransition(fieldAccess, transition ->
+ {
+ if (!transition.alwaysEndsInStartState())
+ {
+ canChangeState.set(true);
+ }
+ });
+
+ return canChangeState.get();
+ }
+
+ private CharSequence generateAccessOrderListenerCall(
+ final FieldPrecedenceModel fieldPrecedenceModel,
+ final String indent,
+ final Token token,
+ final String... arguments)
+ {
+ return generateAccessOrderListenerCall(
+ fieldPrecedenceModel,
+ indent,
+ accessOrderListenerMethodName(token),
+ arguments);
+ }
+
+ private CharSequence generateAccessOrderListenerCall(
+ final FieldPrecedenceModel fieldPrecedenceModel,
+ final String indent,
+ final String methodName,
+ final String... arguments)
+ {
+ if (null == fieldPrecedenceModel)
+ {
+ return "";
+ }
+
+ final StringBuilder sb = new StringBuilder();
+ sb.append("#if defined(").append(precedenceChecksFlagName).append(")\n")
+ .append(indent).append(methodName).append("(");
+
+ for (int i = 0; i < arguments.length; i++)
+ {
+ if (i > 0)
+ {
+ sb.append(", ");
+ }
+ sb.append(arguments[i]);
+ }
+ sb.append(");\n");
+
+ sb.append("#endif\n");
+
+ return sb;
+ }
+
+ private static void generateAccessOrderListenerMethodForGroupWrap(
+ final StringBuilder sb,
+ final FieldPrecedenceModel fieldPrecedenceModel,
+ final String indent,
+ final Token token)
+ {
+ if (null == fieldPrecedenceModel)
+ {
+ return;
+ }
+
+ sb.append("\n")
+ .append(indent).append("private:\n")
+ .append(indent).append(INDENT).append("void ").append(accessOrderListenerMethodName(token))
+ .append("(std::uint64_t remaining, std::string action)\n")
+ .append(indent).append(INDENT).append("{\n")
+ .append(indent).append(TWO_INDENT).append("if (0 == remaining)\n")
+ .append(indent).append(TWO_INDENT).append("{\n");
+
+ final FieldPrecedenceModel.CodecInteraction selectEmptyGroup =
+ fieldPrecedenceModel.interactionFactory().determineGroupIsEmpty(token);
+
+ generateAccessOrderListener(
+ sb,
+ indent + THREE_INDENT,
+ "\" + action + \" count of repeating group",
+ fieldPrecedenceModel,
+ selectEmptyGroup);
+
+ sb.append(indent).append(TWO_INDENT).append("}\n")
+ .append(indent).append(TWO_INDENT).append("else\n")
+ .append(indent).append(TWO_INDENT).append("{\n");
+
+ final FieldPrecedenceModel.CodecInteraction selectNonEmptyGroup =
+ fieldPrecedenceModel.interactionFactory().determineGroupHasElements(token);
+
+ generateAccessOrderListener(
+ sb,
+ indent + THREE_INDENT,
+ "\" + action + \" count of repeating group",
+ fieldPrecedenceModel,
+ selectNonEmptyGroup);
+
+ sb.append(indent).append(TWO_INDENT).append("}\n")
+ .append(indent).append(INDENT).append("}\n\n")
+ .append(indent).append("public:");
+ }
+
+ private static void generateAccessOrderListenerMethodForVarDataLength(
+ final StringBuilder sb,
+ final FieldPrecedenceModel fieldPrecedenceModel,
+ final String indent,
+ final Token token)
+ {
+ if (null == fieldPrecedenceModel)
+ {
+ return;
+ }
+
+ sb.append("\n")
+ .append(indent).append("private:\n")
+ .append(indent).append(INDENT).append("void ").append(accessOrderListenerMethodName(token, "Length"))
+ .append("() const\n")
+ .append(indent).append(INDENT).append("{\n");
+
+ final FieldPrecedenceModel.CodecInteraction accessLength =
+ fieldPrecedenceModel.interactionFactory().accessVarDataLength(token);
+
+ generateAccessOrderListener(
+ sb,
+ indent + TWO_INDENT,
+ "decode length of var data",
+ fieldPrecedenceModel,
+ accessLength);
+
+ sb.append(indent).append(INDENT).append("}\n\n")
+ .append(indent).append("public:");
+ }
+
+ private static void generateAccessOrderListener(
+ final StringBuilder sb,
+ final String indent,
+ final String action,
+ final FieldPrecedenceModel fieldPrecedenceModel,
+ final FieldPrecedenceModel.CodecInteraction interaction)
+ {
+ if (interaction.isTopLevelBlockFieldAccess())
+ {
+ sb.append(indent).append("if (codecState() == ")
+ .append(qualifiedStateCase(fieldPrecedenceModel.notWrappedState()))
+ .append(")\n")
+ .append(indent).append("{\n");
+ generateAccessOrderException(sb, indent + INDENT, action, fieldPrecedenceModel, interaction);
+ sb.append(indent).append("}\n");
+ }
+ else
+ {
+ sb.append(indent).append("switch (codecState())\n")
+ .append(indent).append("{\n");
+
+ fieldPrecedenceModel.forEachTransition(interaction, transitionGroup ->
+ {
+
+ transitionGroup.forEachStartState(startState ->
+ {
+ sb.append(indent).append(INDENT)
+ .append("case ").append(stateCaseForSwitchCase(startState)).append(":\n");
+ });
+
+ if (!transitionGroup.alwaysEndsInStartState())
+ {
+ sb.append(indent).append(TWO_INDENT).append("codecState(")
+ .append(qualifiedStateCase(transitionGroup.endState())).append(");\n");
+ }
+
+ sb.append(indent).append(TWO_INDENT).append("break;\n");
+ });
+
+ sb.append(indent).append(INDENT).append("default:\n");
+ generateAccessOrderException(sb, indent + TWO_INDENT, action, fieldPrecedenceModel, interaction);
+ sb.append(indent).append("}\n");
+ }
+ }
+
+ private static void generateAccessOrderException(
+ final StringBuilder sb,
+ final String indent,
+ final String action,
+ final FieldPrecedenceModel fieldPrecedenceModel,
+ final FieldPrecedenceModel.CodecInteraction interaction)
+ {
+ sb.append(indent).append("throw AccessOrderError(")
+ .append("std::string(\"Illegal field access order. \") +\n")
+ .append(indent).append(INDENT)
+ .append("\"Cannot ").append(action).append(" \\\"").append(interaction.groupQualifiedName())
+ .append("\\\" in state: \" + codecStateName(codecState()) +\n")
+ .append(indent).append(INDENT)
+ .append("\". Expected one of these transitions: [\" + codecStateTransitions(codecState()) +\n")
+ .append(indent).append(INDENT)
+ .append("\"]. Please see the diagram in the docs of the enum ")
+ .append(fieldPrecedenceModel.generatedRepresentationClassName()).append(".\");\n");
+ }
+
+ private static void generateAccessOrderListenerMethodForNextGroupElement(
+ final StringBuilder sb,
+ final FieldPrecedenceModel fieldPrecedenceModel,
+ final String indent,
+ final Token token)
+ {
+ if (null == fieldPrecedenceModel)
+ {
+ return;
+ }
+
+ sb.append("\n");
+
+ sb.append(indent).append(INDENT).append("void onNextElementAccessed()\n")
+ .append(indent).append(INDENT).append("{\n")
+ .append(indent).append(TWO_INDENT).append("std::uint64_t remaining = m_count - m_index;\n")
+ .append(indent).append(TWO_INDENT).append("if (remaining > 1)\n")
+ .append(indent).append(TWO_INDENT).append("{\n");
+
+ final FieldPrecedenceModel.CodecInteraction selectNextElementInGroup =
+ fieldPrecedenceModel.interactionFactory().moveToNextElement(token);
+
+ generateAccessOrderListener(
+ sb,
+ indent + THREE_INDENT,
+ "access next element in repeating group",
+ fieldPrecedenceModel,
+ selectNextElementInGroup);
+
+ sb.append(indent).append(TWO_INDENT).append("}\n")
+ .append(indent).append(TWO_INDENT).append("else if (1 == remaining)\n")
+ .append(indent).append(TWO_INDENT).append("{\n");
+
+ final FieldPrecedenceModel.CodecInteraction selectLastElementInGroup =
+ fieldPrecedenceModel.interactionFactory().moveToLastElement(token);
+
+ generateAccessOrderListener(
+ sb,
+ indent + THREE_INDENT,
+ "access next element in repeating group",
+ fieldPrecedenceModel,
+ selectLastElementInGroup);
+
+ sb.append(indent).append(TWO_INDENT).append("}\n")
+ .append(indent).append(INDENT).append("}\n");
+ }
+
+ private static void generateAccessOrderListenerMethodForResetGroupCount(
+ final StringBuilder sb,
+ final FieldPrecedenceModel fieldPrecedenceModel,
+ final String indent,
+ final Token token)
+ {
+ if (null == fieldPrecedenceModel)
+ {
+ return;
+ }
+
+ sb.append("\n")
+ .append(indent).append(INDENT).append("void onResetCountToIndex()\n")
+ .append(indent).append(INDENT).append("{\n");
+
+ final FieldPrecedenceModel.CodecInteraction resetCountToIndex =
+ fieldPrecedenceModel.interactionFactory().resetCountToIndex(token);
+
+ generateAccessOrderListener(
+ sb,
+ indent + TWO_INDENT,
+ "reset count of repeating group",
+ fieldPrecedenceModel,
+ resetCountToIndex);
+
+ sb.append(indent).append(INDENT).append("}\n");
+ }
+
+ private void generateGroups(
+ final StringBuilder sb,
+ final List tokens,
+ final FieldPrecedenceModel fieldPrecedenceModel,
+ final String indent)
{
for (int i = 0, size = tokens.size(); i < size; i++)
{
@@ -189,7 +592,7 @@ private void generateGroups(final StringBuilder sb, final List tokens, fi
final Token numInGroupToken = Generators.findFirst("numInGroup", tokens, i);
final String cppTypeForNumInGroup = cppTypeName(numInGroupToken.encoding().primitiveType());
- generateGroupClassHeader(sb, groupName, tokens, i, indent + INDENT);
+ generateGroupClassHeader(sb, groupName, groupToken, tokens, fieldPrecedenceModel, i, indent + INDENT);
++i;
final int groupHeaderTokenCount = tokens.get(i).componentTokenCount();
@@ -197,26 +600,32 @@ private void generateGroups(final StringBuilder sb, final List tokens, fi
final List fields = new ArrayList<>();
i = collectFields(tokens, i, fields);
- generateFields(sb, formatClassName(groupName), fields, indent + INDENT);
+ generateFields(sb, formatClassName(groupName), fields, fieldPrecedenceModel, indent + INDENT);
final List groups = new ArrayList<>();
i = collectGroups(tokens, i, groups);
- generateGroups(sb, groups, indent + INDENT);
+ generateGroups(sb, groups, fieldPrecedenceModel, indent + INDENT);
final List varData = new ArrayList<>();
i = collectVarData(tokens, i, varData);
- generateVarData(sb, formatClassName(groupName), varData, indent + INDENT);
+ generateVarData(sb, formatClassName(groupName), varData, fieldPrecedenceModel, indent + INDENT);
sb.append(generateGroupDisplay(groupName, fields, groups, varData, indent + INDENT + INDENT));
sb.append(generateMessageLength(groups, varData, indent + INDENT + INDENT));
sb.append(indent).append(" };\n");
- generateGroupProperty(sb, groupName, groupToken, cppTypeForNumInGroup, indent);
+ generateGroupProperty(sb, groupName, groupToken, cppTypeForNumInGroup, fieldPrecedenceModel, indent);
}
}
- private static void generateGroupClassHeader(
- final StringBuilder sb, final String groupName, final List tokens, final int index, final String indent)
+ private void generateGroupClassHeader(
+ final StringBuilder sb,
+ final String groupName,
+ final Token groupToken,
+ final List tokens,
+ final FieldPrecedenceModel fieldPrecedenceModel,
+ final int index,
+ final String indent)
{
final String dimensionsClassName = formatClassName(tokens.get(index + 1).name());
final int dimensionHeaderLength = tokens.get(index + 1).encodedLength();
@@ -226,6 +635,8 @@ private static void generateGroupClassHeader(
final String cppTypeBlockLength = cppTypeName(blockLengthToken.encoding().primitiveType());
final String cppTypeNumInGroup = cppTypeName(numInGroupToken.encoding().primitiveType());
+ final String groupClassName = formatClassName(groupName);
+
new Formatter(sb).format("\n" +
indent + "class %1$s\n" +
indent + "{\n" +
@@ -243,17 +654,50 @@ private static void generateGroupClassHeader(
indent + " SBE_NODISCARD std::uint64_t *sbePositionPtr() SBE_NOEXCEPT\n" +
indent + " {\n" +
indent + " return m_positionPtr;\n" +
- indent + " }\n\n" +
+ indent + " }\n\n",
+ groupClassName);
- indent + "public:\n",
- formatClassName(groupName));
+ if (null != fieldPrecedenceModel)
+ {
+ new Formatter(sb).format(
+ indent + " CodecState *m_codecStatePtr = nullptr;\n\n" +
+
+ indent + " CodecState codecState() const SBE_NOEXCEPT\n" +
+ indent + " {\n" +
+ indent + " return *m_codecStatePtr;\n" +
+ indent + " }\n\n" +
+
+ indent + " CodecState *codecStatePtr()\n" +
+ indent + " {\n" +
+ indent + " return m_codecStatePtr;\n" +
+ indent + " }\n\n" +
+
+ indent + " void codecState(CodecState codecState)\n" +
+ indent + " {\n" +
+ indent + " *m_codecStatePtr = codecState;\n" +
+ indent + " }\n\n"
+ );
+ }
+
+ sb.append(generateHiddenCopyConstructor(indent + " ", groupClassName));
+
+ final String codecStateParameter = null == fieldPrecedenceModel ?
+ ")\n" :
+ ",\n " + indent + " CodecState *codecState)\n";
+
+ final String codecStateAssignment = null == fieldPrecedenceModel ?
+ "" :
+ indent + " m_codecStatePtr = codecState;\n";
new Formatter(sb).format(
+ indent + "public:\n" +
+ indent + " %5$s() = default;\n\n" +
+
indent + " inline void wrapForDecode(\n" +
indent + " char *buffer,\n" +
indent + " std::uint64_t *pos,\n" +
indent + " const std::uint64_t actingVersion,\n" +
- indent + " const std::uint64_t bufferLength)\n" +
+ indent + " const std::uint64_t bufferLength%3$s" +
indent + " {\n" +
indent + " %2$s dimensions(buffer, *pos, bufferLength, actingVersion);\n" +
indent + " m_buffer = buffer;\n" +
@@ -265,8 +709,13 @@ private static void generateGroupClassHeader(
indent + " m_initialPosition = *pos;\n" +
indent + " m_positionPtr = pos;\n" +
indent + " *m_positionPtr = *m_positionPtr + %1$d;\n" +
+ "%4$s" +
indent + " }\n",
- dimensionHeaderLength, dimensionsClassName);
+ dimensionHeaderLength,
+ dimensionsClassName,
+ codecStateParameter,
+ codecStateAssignment,
+ groupClassName);
final long minCount = numInGroupToken.encoding().applicableMinValue().longValue();
final String minCheck = minCount > 0 ? "count < " + minCount + " || " : "";
@@ -277,7 +726,7 @@ private static void generateGroupClassHeader(
indent + " const %3$s count,\n" +
indent + " std::uint64_t *pos,\n" +
indent + " const std::uint64_t actingVersion,\n" +
- indent + " const std::uint64_t bufferLength)\n" +
+ indent + " const std::uint64_t bufferLength%8$s" +
indent + " {\n" +
indent + "#if defined(__GNUG__) && !defined(__clang__)\n" +
indent + "#pragma GCC diagnostic push\n" +
@@ -302,6 +751,7 @@ private static void generateGroupClassHeader(
indent + " m_initialPosition = *pos;\n" +
indent + " m_positionPtr = pos;\n" +
indent + " *m_positionPtr = *m_positionPtr + %4$d;\n" +
+ "%9$s" +
indent + " }\n",
cppTypeBlockLength,
blockLength,
@@ -309,7 +759,43 @@ private static void generateGroupClassHeader(
dimensionHeaderLength,
minCheck,
numInGroupToken.encoding().applicableMaxValue().longValue(),
- dimensionsClassName);
+ dimensionsClassName,
+ codecStateParameter,
+ codecStateAssignment);
+
+ if (groupToken.version() > 0)
+ {
+ final String codecStateNullAssignment = null == fieldPrecedenceModel ?
+ "" :
+ indent + " m_codecStatePtr = nullptr;\n";
+
+ new Formatter(sb).format(
+ indent + " inline void notPresent(std::uint64_t actingVersion)\n" +
+ indent + " {\n" +
+ indent + " m_buffer = nullptr;\n" +
+ indent + " m_bufferLength = 0;\n" +
+ indent + " m_blockLength = 0;\n" +
+ indent + " m_count = 0;\n" +
+ indent + " m_index = 0;\n" +
+ indent + " m_actingVersion = actingVersion;\n" +
+ indent + " m_initialPosition = 0;\n" +
+ indent + " m_positionPtr = nullptr;\n" +
+ "%1$s" +
+ indent + " }\n",
+ codecStateNullAssignment);
+ }
+
+
+ if (null != fieldPrecedenceModel)
+ {
+ sb.append("\n").append(indent).append("private:");
+ generateAccessOrderListenerMethodForNextGroupElement(sb, fieldPrecedenceModel, indent, groupToken);
+ generateAccessOrderListenerMethodForResetGroupCount(sb, fieldPrecedenceModel, indent, groupToken);
+ sb.append("\n").append(indent).append("public:");
+ }
+
+ final CharSequence onNextAccessOrderCall = null == fieldPrecedenceModel ? "" :
+ generateAccessOrderListenerCall(fieldPrecedenceModel, indent + TWO_INDENT, "onNextElementAccessed");
new Formatter(sb).format("\n" +
indent + " static SBE_CONSTEXPR std::uint64_t sbeHeaderSize() SBE_NOEXCEPT\n" +
@@ -358,6 +844,7 @@ private static void generateGroupClassHeader(
indent + " {\n" +
indent + " throw std::runtime_error(\"index >= count [E108]\");\n" +
indent + " }\n" +
+ "%4$s" +
indent + " m_offset = *m_positionPtr;\n" +
indent + " if (SBE_BOUNDS_CHECK_EXPECT(((m_offset + m_blockLength) > m_bufferLength), false))\n" +
indent + " {\n" +
@@ -370,11 +857,13 @@ private static void generateGroupClassHeader(
indent + " }\n",
dimensionHeaderLength,
blockLength,
- formatClassName(groupName));
+ groupClassName,
+ onNextAccessOrderCall);
sb.append("\n")
.append(indent).append(" inline std::uint64_t resetCountToIndex()\n")
.append(indent).append(" {\n")
+ .append(generateAccessOrderListenerCall(fieldPrecedenceModel, indent + TWO_INDENT, "onResetCountToIndex"))
.append(indent).append(" m_count = m_index;\n")
.append(indent).append(" ").append(dimensionsClassName)
.append(" dimensions(m_buffer, m_initialPosition, m_bufferLength, m_actingVersion);\n")
@@ -394,11 +883,12 @@ private static void generateGroupClassHeader(
.append(indent).append(" }\n\n");
}
- private static void generateGroupProperty(
+ private void generateGroupProperty(
final StringBuilder sb,
final String groupName,
final Token token,
final String cppTypeForNumInGroup,
+ final FieldPrecedenceModel fieldPrecedenceModel,
final String indent)
{
final String className = formatClassName(groupName);
@@ -420,25 +910,76 @@ private static void generateGroupProperty(
groupName,
token.id());
- new Formatter(sb).format("\n" +
- indent + " SBE_NODISCARD inline %1$s &%2$s()\n" +
- indent + " {\n" +
- indent + " m_%2$s.wrapForDecode(m_buffer, sbePositionPtr(), m_actingVersion, m_bufferLength);\n" +
- indent + " return m_%2$s;\n" +
- indent + " }\n",
- className,
- propertyName);
+ if (null != fieldPrecedenceModel)
+ {
+ generateAccessOrderListenerMethodForGroupWrap(
+ sb,
+ fieldPrecedenceModel,
+ indent,
+ token
+ );
+ }
+
+ final String codecStateArgument = null == fieldPrecedenceModel ? "" : ", codecStatePtr()";
+
+ final CharSequence onDecodeAccessOrderCall = null == fieldPrecedenceModel ? "" :
+ generateAccessOrderListenerCall(fieldPrecedenceModel, indent + TWO_INDENT, token,
+ "m_" + propertyName + ".count()", "\"decode\"");
+
+ if (token.version() > 0)
+ {
+ new Formatter(sb).format("\n" +
+ indent + " SBE_NODISCARD inline %1$s &%2$s()\n" +
+ indent + " {\n" +
+ indent + " if (m_actingVersion < %5$du)\n" +
+ indent + " {\n" +
+ indent + " m_%2$s.notPresent(m_actingVersion);\n" +
+ indent + " return m_%2$s;\n" +
+ indent + " }\n\n" +
+
+ indent + " m_%2$s.wrapForDecode(" +
+ "m_buffer, sbePositionPtr(), m_actingVersion, m_bufferLength%3$s);\n" +
+ "%4$s" +
+ indent + " return m_%2$s;\n" +
+ indent + " }\n",
+ className,
+ propertyName,
+ codecStateArgument,
+ onDecodeAccessOrderCall,
+ token.version());
+ }
+ else
+ {
+ new Formatter(sb).format("\n" +
+ indent + " SBE_NODISCARD inline %1$s &%2$s()\n" +
+ indent + " {\n" +
+ indent + " m_%2$s.wrapForDecode(" +
+ "m_buffer, sbePositionPtr(), m_actingVersion, m_bufferLength%3$s);\n" +
+ "%4$s" +
+ indent + " return m_%2$s;\n" +
+ indent + " }\n",
+ className,
+ propertyName,
+ codecStateArgument,
+ onDecodeAccessOrderCall);
+ }
+
+ final CharSequence onEncodeAccessOrderCall = null == fieldPrecedenceModel ? "" :
+ generateAccessOrderListenerCall(fieldPrecedenceModel, indent + TWO_INDENT, token, "count", "\"encode\"");
new Formatter(sb).format("\n" +
indent + " %1$s &%2$sCount(const %3$s count)\n" +
indent + " {\n" +
indent + " m_%2$s.wrapForEncode(" +
- "m_buffer, count, sbePositionPtr(), m_actingVersion, m_bufferLength);\n" +
+ "m_buffer, count, sbePositionPtr(), m_actingVersion, m_bufferLength%4$s);\n" +
+ "%5$s" +
indent + " return m_%2$s;\n" +
indent + " }\n",
className,
propertyName,
- cppTypeForNumInGroup);
+ cppTypeForNumInGroup,
+ codecStateArgument,
+ onEncodeAccessOrderCall);
final int version = token.version();
final String versionCheck = 0 == version ?
@@ -458,7 +999,11 @@ private static void generateGroupProperty(
}
private void generateVarData(
- final StringBuilder sb, final String className, final List tokens, final String indent)
+ final StringBuilder sb,
+ final String className,
+ final List tokens,
+ final FieldPrecedenceModel fieldPrecedenceModel,
+ final String indent)
{
for (int i = 0, size = tokens.size(); i < size;)
{
@@ -480,12 +1025,39 @@ private void generateVarData(
generateFieldMetaAttributeMethod(sb, token, indent);
generateVarDataDescriptors(
- sb, token, propertyName, characterEncoding, lengthToken, lengthOfLengthField, lengthCppType, indent);
+ sb, token, propertyName, characterEncoding, lengthOfLengthField, indent);
+
+ generateAccessOrderListenerMethodForVarDataLength(sb, fieldPrecedenceModel, indent, token);
+
+ final CharSequence lengthAccessListenerCall = generateAccessOrderListenerCall(
+ fieldPrecedenceModel, indent + TWO_INDENT,
+ accessOrderListenerMethodName(token, "Length"));
+
+ new Formatter(sb).format("\n" +
+ indent + " SBE_NODISCARD %4$s %1$sLength() const\n" +
+ indent + " {\n" +
+ "%2$s" +
+ "%5$s" +
+ indent + " %4$s length;\n" +
+ indent + " std::memcpy(&length, m_buffer + sbePosition(), sizeof(%4$s));\n" +
+ indent + " return %3$s(length);\n" +
+ indent + " }\n",
+ toLowerFirstChar(propertyName),
+ generateArrayFieldNotPresentCondition(token.version(), BASE_INDENT),
+ formatByteOrderEncoding(lengthToken.encoding().byteOrder(), lengthToken.encoding().primitiveType()),
+ lengthCppType,
+ lengthAccessListenerCall);
+
+ generateAccessOrderListenerMethod(sb, fieldPrecedenceModel, indent, token);
+
+ final CharSequence accessOrderListenerCall =
+ generateAccessOrderListenerCall(fieldPrecedenceModel, indent + TWO_INDENT, token);
new Formatter(sb).format("\n" +
indent + " std::uint64_t skip%1$s()\n" +
indent + " {\n" +
"%2$s" +
+ "%6$s" +
indent + " std::uint64_t lengthOfLengthField = %3$d;\n" +
indent + " std::uint64_t lengthPosition = sbePosition();\n" +
indent + " %5$s lengthFieldValue;\n" +
@@ -498,12 +1070,14 @@ private void generateVarData(
generateArrayFieldNotPresentCondition(token.version(), indent),
lengthOfLengthField,
lengthByteOrderStr,
- lengthCppType);
+ lengthCppType,
+ accessOrderListenerCall);
new Formatter(sb).format("\n" +
indent + " SBE_NODISCARD const char *%1$s()\n" +
indent + " {\n" +
"%2$s" +
+ "%6$s" +
indent + " %4$s lengthFieldValue;\n" +
indent + " std::memcpy(&lengthFieldValue, m_buffer + sbePosition(), sizeof(%4$s));\n" +
indent + " const char *fieldPtr = m_buffer + sbePosition() + %3$d;\n" +
@@ -514,12 +1088,14 @@ private void generateVarData(
generateTypeFieldNotPresentCondition(token.version(), indent),
lengthOfLengthField,
lengthCppType,
- lengthByteOrderStr);
+ lengthByteOrderStr,
+ accessOrderListenerCall);
new Formatter(sb).format("\n" +
indent + " std::uint64_t get%1$s(char *dst, const std::uint64_t length)\n" +
indent + " {\n" +
"%2$s" +
+ "%6$s" +
indent + " std::uint64_t lengthOfLengthField = %3$d;\n" +
indent + " std::uint64_t lengthPosition = sbePosition();\n" +
indent + " sbePosition(lengthPosition + lengthOfLengthField);\n" +
@@ -536,11 +1112,13 @@ private void generateVarData(
generateArrayFieldNotPresentCondition(token.version(), indent),
lengthOfLengthField,
lengthByteOrderStr,
- lengthCppType);
+ lengthCppType,
+ accessOrderListenerCall);
new Formatter(sb).format("\n" +
indent + " %5$s &put%1$s(const char *src, const %3$s length)\n" +
indent + " {\n" +
+ "%6$s" +
indent + " std::uint64_t lengthOfLengthField = %2$d;\n" +
indent + " std::uint64_t lengthPosition = sbePosition();\n" +
indent + " %3$s lengthFieldValue = %4$s(length);\n" +
@@ -558,12 +1136,14 @@ private void generateVarData(
lengthOfLengthField,
lengthCppType,
lengthByteOrderStr,
- className);
+ className,
+ accessOrderListenerCall);
new Formatter(sb).format("\n" +
indent + " std::string get%1$sAsString()\n" +
indent + " {\n" +
"%2$s" +
+ "%6$s" +
indent + " std::uint64_t lengthOfLengthField = %3$d;\n" +
indent + " std::uint64_t lengthPosition = sbePosition();\n" +
indent + " sbePosition(lengthPosition + lengthOfLengthField);\n" +
@@ -579,15 +1159,17 @@ private void generateVarData(
generateStringNotPresentCondition(token.version(), indent),
lengthOfLengthField,
lengthByteOrderStr,
- lengthCppType);
+ lengthCppType,
+ accessOrderListenerCall);
- generateJsonEscapedStringGetter(sb, token, indent, propertyName);
+ generateJsonEscapedStringGetter(sb, token, indent, propertyName, accessOrderListenerCall);
new Formatter(sb).format("\n" +
indent + " #if __cplusplus >= 201703L\n" +
indent + " std::string_view get%1$sAsStringView()\n" +
indent + " {\n" +
"%2$s" +
+ "%6$s" +
indent + " std::uint64_t lengthOfLengthField = %3$d;\n" +
indent + " std::uint64_t lengthPosition = sbePosition();\n" +
indent + " sbePosition(lengthPosition + lengthOfLengthField);\n" +
@@ -604,7 +1186,8 @@ private void generateVarData(
generateStringViewNotPresentCondition(token.version(), indent),
lengthOfLengthField,
lengthByteOrderStr,
- lengthCppType);
+ lengthCppType,
+ accessOrderListenerCall);
new Formatter(sb).format("\n" +
indent + " %1$s &put%2$s(const std::string &str)\n" +
@@ -645,9 +1228,7 @@ private void generateVarDataDescriptors(
final Token token,
final String propertyName,
final String characterEncoding,
- final Token lengthToken,
final Integer sizeOfLengthField,
- final String lengthCppType,
final String indent)
{
new Formatter(sb).format("\n" +
@@ -687,19 +1268,6 @@ private void generateVarDataDescriptors(
indent + " }\n",
toLowerFirstChar(propertyName),
sizeOfLengthField);
-
- new Formatter(sb).format("\n" +
- indent + " SBE_NODISCARD %4$s %1$sLength() const\n" +
- indent + " {\n" +
- "%2$s" +
- indent + " %4$s length;\n" +
- indent + " std::memcpy(&length, m_buffer + sbePosition(), sizeof(%4$s));\n" +
- indent + " return %3$s(length);\n" +
- indent + " }\n",
- toLowerFirstChar(propertyName),
- generateArrayFieldNotPresentCondition(version, BASE_INDENT),
- formatByteOrderEncoding(lengthToken.encoding().byteOrder(), lengthToken.encoding().primitiveType()),
- lengthCppType);
}
private void generateChoiceSet(final List tokens) throws IOException
@@ -1226,19 +1794,21 @@ private CharSequence generateCompositePropertyElements(
switch (fieldToken.signal())
{
case ENCODING:
- generatePrimitiveProperty(sb, containingClassName, propertyName, fieldToken, fieldToken, indent);
+ generatePrimitiveProperty(sb, containingClassName, propertyName, fieldToken, fieldToken,
+ null, indent);
break;
case BEGIN_ENUM:
- generateEnumProperty(sb, containingClassName, fieldToken, propertyName, fieldToken, indent);
+ generateEnumProperty(sb, containingClassName, fieldToken, propertyName, fieldToken,
+ null, indent);
break;
case BEGIN_SET:
- generateBitsetProperty(sb, propertyName, fieldToken, indent);
+ generateBitsetProperty(sb, propertyName, fieldToken, fieldToken, null, indent);
break;
case BEGIN_COMPOSITE:
- generateCompositeProperty(sb, propertyName, fieldToken, indent);
+ generateCompositeProperty(sb, propertyName, fieldToken, fieldToken, null, indent);
break;
default:
@@ -1257,6 +1827,7 @@ private void generatePrimitiveProperty(
final String propertyName,
final Token propertyToken,
final Token encodingToken,
+ final FieldPrecedenceModel fieldPrecedenceModel,
final String indent)
{
generatePrimitiveFieldMetaData(sb, propertyName, encodingToken, indent);
@@ -1268,7 +1839,7 @@ private void generatePrimitiveProperty(
else
{
generatePrimitivePropertyMethods(
- sb, containingClassName, propertyName, propertyToken, encodingToken, indent);
+ sb, containingClassName, propertyName, propertyToken, encodingToken, fieldPrecedenceModel, indent);
}
}
@@ -1278,16 +1849,19 @@ private void generatePrimitivePropertyMethods(
final String propertyName,
final Token propertyToken,
final Token encodingToken,
+ final FieldPrecedenceModel fieldPrecedenceModel,
final String indent)
{
final int arrayLength = encodingToken.arrayLength();
if (arrayLength == 1)
{
- generateSingleValueProperty(sb, containingClassName, propertyName, propertyToken, encodingToken, indent);
+ generateSingleValueProperty(sb, containingClassName, propertyName, propertyToken, encodingToken,
+ fieldPrecedenceModel, indent);
}
else if (arrayLength > 1)
{
- generateArrayProperty(sb, containingClassName, propertyName, propertyToken, encodingToken, indent);
+ generateArrayProperty(sb, containingClassName, propertyName, propertyToken, encodingToken,
+ fieldPrecedenceModel, indent);
}
}
@@ -1415,42 +1989,59 @@ private CharSequence generateStoreValue(
return sb;
}
+ private static String noexceptDeclaration(final FieldPrecedenceModel fieldPrecedenceModel)
+ {
+ return fieldPrecedenceModel == null ? " SBE_NOEXCEPT" : "";
+ }
+
private void generateSingleValueProperty(
final StringBuilder sb,
final String containingClassName,
final String propertyName,
final Token propertyToken,
final Token encodingToken,
+ final FieldPrecedenceModel fieldPrecedenceModel,
final String indent)
{
final PrimitiveType primitiveType = encodingToken.encoding().primitiveType();
final String cppTypeName = cppTypeName(primitiveType);
final int offset = encodingToken.offset();
+ final CharSequence accessOrderListenerCall =
+ generateAccessOrderListenerCall(fieldPrecedenceModel, indent + TWO_INDENT, propertyToken);
+
+ final String noexceptDeclaration = noexceptDeclaration(fieldPrecedenceModel);
+
new Formatter(sb).format("\n" +
- indent + " SBE_NODISCARD %1$s %2$s() const SBE_NOEXCEPT\n" +
+ indent + " SBE_NODISCARD %1$s %2$s() const%6$s\n" +
indent + " {\n" +
"%3$s" +
"%4$s" +
+ "%5$s" +
indent + " }\n",
cppTypeName,
propertyName,
generateFieldNotPresentCondition(propertyToken.version(), encodingToken.encoding(), indent),
- generateLoadValue(primitiveType, Integer.toString(offset), encodingToken.encoding().byteOrder(), indent));
+ accessOrderListenerCall,
+ generateLoadValue(primitiveType, Integer.toString(offset), encodingToken.encoding().byteOrder(), indent),
+ noexceptDeclaration);
final CharSequence storeValue = generateStoreValue(
primitiveType, "", Integer.toString(offset), encodingToken.encoding().byteOrder(), indent);
new Formatter(sb).format("\n" +
- indent + " %1$s &%2$s(const %3$s value) SBE_NOEXCEPT\n" +
+ indent + " %1$s &%2$s(const %3$s value)%6$s\n" +
indent + " {\n" +
"%4$s" +
+ "%5$s" +
indent + " return *this;\n" +
indent + " }\n",
formatClassName(containingClassName),
propertyName,
cppTypeName,
- storeValue);
+ storeValue,
+ accessOrderListenerCall,
+ noexceptDeclaration);
}
private void generateArrayProperty(
@@ -1459,12 +2050,18 @@ private void generateArrayProperty(
final String propertyName,
final Token propertyToken,
final Token encodingToken,
+ final FieldPrecedenceModel fieldPrecedenceModel,
final String indent)
{
final PrimitiveType primitiveType = encodingToken.encoding().primitiveType();
final String cppTypeName = cppTypeName(primitiveType);
final int offset = encodingToken.offset();
+ final CharSequence accessOrderListenerCall =
+ generateAccessOrderListenerCall(fieldPrecedenceModel, indent + TWO_INDENT, propertyToken);
+
+ final String noexceptDeclaration = noexceptDeclaration(fieldPrecedenceModel);
+
final int arrayLength = encodingToken.arrayLength();
new Formatter(sb).format("\n" +
indent + " static SBE_CONSTEXPR std::uint64_t %1$sLength() SBE_NOEXCEPT\n" +
@@ -1475,24 +2072,30 @@ private void generateArrayProperty(
arrayLength);
new Formatter(sb).format("\n" +
- indent + " SBE_NODISCARD const char *%1$s() const SBE_NOEXCEPT\n" +
+ indent + " SBE_NODISCARD const char *%1$s() const%5$s\n" +
indent + " {\n" +
"%2$s" +
+ "%4$s" +
indent + " return m_buffer + m_offset + %3$d;\n" +
indent + " }\n",
propertyName,
generateTypeFieldNotPresentCondition(propertyToken.version(), indent),
- offset);
+ offset,
+ accessOrderListenerCall,
+ noexceptDeclaration);
new Formatter(sb).format("\n" +
- indent + " SBE_NODISCARD char *%1$s() SBE_NOEXCEPT\n" +
+ indent + " SBE_NODISCARD char *%1$s()%5$s\n" +
indent + " {\n" +
"%2$s" +
+ "%4$s" +
indent + " return m_buffer + m_offset + %3$d;\n" +
indent + " }\n",
propertyName,
generateTypeFieldNotPresentCondition(propertyToken.version(), indent),
- offset);
+ offset,
+ accessOrderListenerCall,
+ noexceptDeclaration);
final CharSequence loadValue = generateLoadValue(
primitiveType,
@@ -1508,13 +2111,15 @@ private void generateArrayProperty(
indent + " throw std::runtime_error(\"index out of range for %2$s [E104]\");\n" +
indent + " }\n\n" +
"%4$s" +
+ "%6$s" +
"%5$s" +
indent + " }\n",
cppTypeName,
propertyName,
arrayLength,
generateFieldNotPresentCondition(propertyToken.version(), encodingToken.encoding(), indent),
- loadValue);
+ loadValue,
+ accessOrderListenerCall);
final CharSequence storeValue = generateStoreValue(
primitiveType,
@@ -1531,6 +2136,7 @@ private void generateArrayProperty(
indent + " throw std::runtime_error(\"index out of range for %2$s [E105]\");\n" +
indent + " }\n\n" +
+ "%6$s" +
"%5$s" +
indent + " return *this;\n" +
indent + " }\n",
@@ -1538,7 +2144,8 @@ private void generateArrayProperty(
propertyName,
cppTypeName,
arrayLength,
- storeValue);
+ storeValue,
+ accessOrderListenerCall);
new Formatter(sb).format("\n" +
indent + " std::uint64_t get%1$s(char *const dst, const std::uint64_t length) const\n" +
@@ -1549,6 +2156,7 @@ private void generateArrayProperty(
indent + " }\n\n" +
"%3$s" +
+ "%6$s" +
indent + " std::memcpy(dst, m_buffer + m_offset + %4$d, " +
"sizeof(%5$s) * static_cast(length));\n" +
indent + " return length;\n" +
@@ -1557,11 +2165,13 @@ private void generateArrayProperty(
arrayLength,
generateArrayFieldNotPresentCondition(propertyToken.version(), indent),
offset,
- cppTypeName);
+ cppTypeName,
+ accessOrderListenerCall);
new Formatter(sb).format("\n" +
- indent + " %1$s &put%2$s(const char *const src) SBE_NOEXCEPT\n" +
+ indent + " %1$s &put%2$s(const char *const src)%7$s\n" +
indent + " {\n" +
+ "%6$s" +
indent + " std::memcpy(m_buffer + m_offset + %3$d, src, sizeof(%4$s) * %5$d);\n" +
indent + " return *this;\n" +
indent + " }\n",
@@ -1569,7 +2179,9 @@ private void generateArrayProperty(
toUpperFirstChar(propertyName),
offset,
cppTypeName,
- arrayLength);
+ arrayLength,
+ accessOrderListenerCall,
+ noexceptDeclaration);
if (arrayLength > 1 && arrayLength <= 4)
{
@@ -1588,8 +2200,9 @@ private void generateArrayProperty(
}
}
- sb.append(") SBE_NOEXCEPT\n");
+ sb.append(")").append(noexceptDeclaration).append("\n");
sb.append(indent).append(" {\n");
+ sb.append(accessOrderListenerCall);
for (int i = 0; i < arrayLength; i++)
{
@@ -1611,6 +2224,8 @@ private void generateArrayProperty(
new Formatter(sb).format("\n" +
indent + " SBE_NODISCARD std::string get%1$sAsString() const\n" +
indent + " {\n" +
+ "%4$s" +
+ "%5$s" +
indent + " const char *buffer = m_buffer + m_offset + %2$d;\n" +
indent + " std::size_t length = 0;\n\n" +
@@ -1621,14 +2236,18 @@ private void generateArrayProperty(
indent + " }\n",
toUpperFirstChar(propertyName),
offset,
- arrayLength);
+ arrayLength,
+ generateStringNotPresentCondition(propertyToken.version(), indent),
+ accessOrderListenerCall);
- generateJsonEscapedStringGetter(sb, encodingToken, indent, propertyName);
+ generateJsonEscapedStringGetter(sb, encodingToken, indent, propertyName, accessOrderListenerCall);
new Formatter(sb).format("\n" +
indent + " #if __cplusplus >= 201703L\n" +
- indent + " SBE_NODISCARD std::string_view get%1$sAsStringView() const SBE_NOEXCEPT\n" +
+ indent + " SBE_NODISCARD std::string_view get%1$sAsStringView() const%6$s\n" +
indent + " {\n" +
+ "%4$s" +
+ "%5$s" +
indent + " const char *buffer = m_buffer + m_offset + %2$d;\n" +
indent + " std::size_t length = 0;\n\n" +
@@ -1640,7 +2259,10 @@ private void generateArrayProperty(
indent + " #endif\n",
toUpperFirstChar(propertyName),
offset,
- arrayLength);
+ arrayLength,
+ generateStringViewNotPresentCondition(propertyToken.version(), indent),
+ accessOrderListenerCall,
+ noexceptDeclaration);
new Formatter(sb).format("\n" +
indent + " #if __cplusplus >= 201703L\n" +
@@ -1652,6 +2274,7 @@ private void generateArrayProperty(
indent + " throw std::runtime_error(\"string too large for put%2$s [E106]\");\n" +
indent + " }\n\n" +
+ "%5$s" +
indent + " std::memcpy(m_buffer + m_offset + %3$d, str.data(), srcLength);\n" +
indent + " for (std::size_t start = srcLength; start < %4$d; ++start)\n" +
indent + " {\n" +
@@ -1669,6 +2292,7 @@ private void generateArrayProperty(
indent + " throw std::runtime_error(\"string too large for put%2$s [E106]\");\n" +
indent + " }\n\n" +
+ "%5$s" +
indent + " std::memcpy(m_buffer + m_offset + %3$d, str.c_str(), srcLength);\n" +
indent + " for (std::size_t start = srcLength; start < %4$d; ++start)\n" +
indent + " {\n" +
@@ -1681,17 +2305,23 @@ private void generateArrayProperty(
containingClassName,
toUpperFirstChar(propertyName),
offset,
- arrayLength);
+ arrayLength,
+ accessOrderListenerCall);
}
}
private void generateJsonEscapedStringGetter(
- final StringBuilder sb, final Token token, final String indent, final String propertyName)
+ final StringBuilder sb,
+ final Token token,
+ final String indent,
+ final String propertyName,
+ final CharSequence accessOrderListenerCall)
{
new Formatter(sb).format("\n" +
indent + " std::string get%1$sAsJsonEscapedString()\n" +
indent + " {\n" +
"%2$s" +
+ "%3$s" +
indent + " std::ostringstream oss;\n" +
indent + " std::string s = get%1$sAsString();\n\n" +
indent + " for (const auto c : s)\n" +
@@ -1720,7 +2350,8 @@ private void generateJsonEscapedStringGetter(
indent + " return oss.str();\n" +
indent + " }\n",
toUpperFirstChar(propertyName),
- generateStringNotPresentCondition(token.version(), indent));
+ generateStringNotPresentCondition(token.version(), indent),
+ accessOrderListenerCall);
}
private void generateConstPropertyMethods(
@@ -1807,7 +2438,7 @@ private void generateConstPropertyMethods(
values,
constantValue.length);
- generateJsonEscapedStringGetter(sb, token, indent, propertyName);
+ generateJsonEscapedStringGetter(sb, token, indent, propertyName, "");
}
private CharSequence generateFixedFlyweightCode(final String className, final int size)
@@ -1821,6 +2452,7 @@ private CharSequence generateFixedFlyweightCode(final String className, final in
" std::uint64_t m_bufferLength = 0;\n" +
" std::uint64_t m_offset = 0;\n" +
" std::uint64_t m_actingVersion = 0;\n\n" +
+ "%7$s" +
"public:\n" +
" enum MetaAttribute\n" +
@@ -1879,7 +2511,17 @@ private CharSequence generateFixedFlyweightCode(final String className, final in
" const std::uint64_t actingVersion,\n" +
" const std::uint64_t bufferLength)\n" +
" {\n" +
- " return *this = %1$s(buffer, offset, bufferLength, actingVersion);\n" +
+ " m_buffer = buffer;\n" +
+ " m_bufferLength = bufferLength;\n" +
+ " m_offset = offset;\n" +
+ " m_actingVersion = actingVersion;\n\n" +
+
+ " if (SBE_BOUNDS_CHECK_EXPECT(((m_offset + %2$s) > m_bufferLength), false))\n" +
+ " {\n" +
+ " throw std::runtime_error(\"buffer too short for flyweight [E107]\");\n" +
+ " }\n\n" +
+
+ " return *this;\n" +
" }\n\n" +
" SBE_NODISCARD static SBE_CONSTEXPR std::uint64_t encodedLength() SBE_NOEXCEPT\n" +
@@ -1926,14 +2568,53 @@ private CharSequence generateFixedFlyweightCode(final String className, final in
schemaIdType,
generateLiteral(ir.headerStructure().schemaIdType(), Integer.toString(ir.id())),
schemaVersionType,
- generateLiteral(ir.headerStructure().schemaVersionType(), Integer.toString(ir.version())));
+ generateLiteral(ir.headerStructure().schemaVersionType(), Integer.toString(ir.version())),
+ generateHiddenCopyConstructor(" ", className));
+ }
+
+ private static String generateHiddenCopyConstructor(final String indent, final String className)
+ {
+ final String ctorAndCopyAssignmentDeletion = String.format(
+ "#if __cplusplus >= 201103L\n" +
+ "%1$s%2$s(const %2$s&) = delete;\n" +
+ "%1$s%2$s& operator=(const %2$s&) = delete;\n" +
+ "#else\n" +
+ "%1$s%2$s(const %2$s&);\n" +
+ "%1$s%2$s& operator=(const %2$s&);\n" +
+ "#endif\n\n",
+ indent, className);
+
+ return DISABLE_IMPLICIT_COPYING ? ctorAndCopyAssignmentDeletion : "";
}
- private static CharSequence generateConstructorsAndOperators(final String className)
+ private static CharSequence generateConstructorsAndOperators(
+ final String className,
+ final FieldPrecedenceModel fieldPrecedenceModel)
{
+ final String constructorWithCodecState = null == fieldPrecedenceModel ? "" : String.format(
+ " %1$s(\n" +
+ " char *buffer,\n" +
+ " const std::uint64_t offset,\n" +
+ " const std::uint64_t bufferLength,\n" +
+ " const std::uint64_t actingBlockLength,\n" +
+ " const std::uint64_t actingVersion,\n" +
+ " CodecState codecState) :\n" +
+ " m_buffer(buffer),\n" +
+ " m_bufferLength(bufferLength),\n" +
+ " m_offset(offset),\n" +
+ " m_position(sbeCheckPosition(offset + actingBlockLength)),\n" +
+ " m_actingBlockLength(actingBlockLength),\n" +
+ " m_actingVersion(actingVersion),\n" +
+ " m_codecState(codecState)\n" +
+ " {\n" +
+ " }\n\n",
+ className);
+
return String.format(
" %1$s() = default;\n\n" +
+ "%2$s" +
+
" %1$s(\n" +
" char *buffer,\n" +
" const std::uint64_t offset,\n" +
@@ -1962,10 +2643,14 @@ private static CharSequence generateConstructorsAndOperators(final String classN
" %1$s(buffer, 0, bufferLength, actingBlockLength, actingVersion)\n" +
" {\n" +
" }\n\n",
- className);
+ className,
+ constructorWithCodecState);
}
- private CharSequence generateMessageFlyweightCode(final String className, final Token token)
+ private CharSequence generateMessageFlyweightCode(
+ final String className,
+ final Token token,
+ final FieldPrecedenceModel fieldPrecedenceModel)
{
final String blockLengthType = cppTypeName(ir.headerStructure().blockLengthType());
final String templateIdType = cppTypeName(ir.headerStructure().templateIdType());
@@ -1975,20 +2660,28 @@ private CharSequence generateMessageFlyweightCode(final String className, final
final String headerType = ir.headerStructure().tokens().get(0).name();
final String semanticVersion = ir.semanticVersion() == null ? "" : ir.semanticVersion();
+
+ final String codecStateArgument = null == fieldPrecedenceModel ? "" : ", m_codecState";
+
return String.format(
"private:\n" +
+ "%15$s" +
+ "%16$s" +
" char *m_buffer = nullptr;\n" +
" std::uint64_t m_bufferLength = 0;\n" +
" std::uint64_t m_offset = 0;\n" +
" std::uint64_t m_position = 0;\n" +
" std::uint64_t m_actingBlockLength = 0;\n" +
- " std::uint64_t m_actingVersion = 0;\n\n" +
+ " std::uint64_t m_actingVersion = 0;\n" +
+ "%17$s" +
" inline std::uint64_t *sbePositionPtr() SBE_NOEXCEPT\n" +
" {\n" +
" return &m_position;\n" +
" }\n\n" +
+ "%22$s" +
+
"public:\n" +
" static const %1$s SBE_BLOCK_LENGTH = %2$s;\n" +
" static const %3$s SBE_TEMPLATE_ID = %4$s;\n" +
@@ -2015,7 +2708,9 @@ private CharSequence generateMessageFlyweightCode(final String className, final
" using messageHeader = %12$s;\n\n" +
+ "%18$s" +
"%11$s" +
+
" SBE_NODISCARD static SBE_CONSTEXPR %1$s sbeBlockLength() SBE_NOEXCEPT\n" +
" {\n" +
" return %2$s;\n" +
@@ -2058,7 +2753,14 @@ private CharSequence generateMessageFlyweightCode(final String className, final
" %10$s &wrapForEncode(char *buffer, const std::uint64_t offset, const std::uint64_t bufferLength)\n" +
" {\n" +
- " return *this = %10$s(buffer, offset, bufferLength, sbeBlockLength(), sbeSchemaVersion());\n" +
+ " m_buffer = buffer;\n" +
+ " m_bufferLength = bufferLength;\n" +
+ " m_offset = offset;\n" +
+ " m_actingBlockLength = sbeBlockLength();\n" +
+ " m_actingVersion = sbeSchemaVersion();\n" +
+ " m_position = sbeCheckPosition(m_offset + m_actingBlockLength);\n" +
+ "%19$s" +
+ " return *this;\n" +
" }\n\n" +
" %10$s &wrapAndApplyHeader(" +
@@ -2072,14 +2774,18 @@ private CharSequence generateMessageFlyweightCode(final String className, final
" .schemaId(sbeSchemaId())\n" +
" .version(sbeSchemaVersion());\n\n" +
- " return *this = %10$s(\n" +
- " buffer,\n" +
- " offset + messageHeader::encodedLength(),\n" +
- " bufferLength,\n" +
- " sbeBlockLength(),\n" +
- " sbeSchemaVersion());\n" +
+ " m_buffer = buffer;\n" +
+ " m_bufferLength = bufferLength;\n" +
+ " m_offset = offset + messageHeader::encodedLength();\n" +
+ " m_actingBlockLength = sbeBlockLength();\n" +
+ " m_actingVersion = sbeSchemaVersion();\n" +
+ " m_position = sbeCheckPosition(m_offset + m_actingBlockLength);\n" +
+ "%19$s" +
+ " return *this;\n" +
" }\n\n" +
+ "%20$s" +
+
" %10$s &wrapForDecode(\n" +
" char *buffer,\n" +
" const std::uint64_t offset,\n" +
@@ -2087,7 +2793,14 @@ private CharSequence generateMessageFlyweightCode(final String className, final
" const std::uint64_t actingVersion,\n" +
" const std::uint64_t bufferLength)\n" +
" {\n" +
- " return *this = %10$s(buffer, offset, bufferLength, actingBlockLength, actingVersion);\n" +
+ " m_buffer = buffer;\n" +
+ " m_bufferLength = bufferLength;\n" +
+ " m_offset = offset;\n" +
+ " m_actingBlockLength = actingBlockLength;\n" +
+ " m_actingVersion = actingVersion;\n" +
+ " m_position = sbeCheckPosition(m_offset + m_actingBlockLength);\n" +
+ "%21$s" +
+ " return *this;\n" +
" }\n\n" +
" %10$s &sbeRewind()\n" +
@@ -2123,7 +2836,7 @@ private CharSequence generateMessageFlyweightCode(final String className, final
" SBE_NODISCARD std::uint64_t decodeLength() const\n" +
" {\n" +
- " %10$s skipper(m_buffer, m_offset, m_bufferLength, sbeBlockLength(), m_actingVersion);\n" +
+ " %10$s skipper(m_buffer, m_offset, m_bufferLength, sbeBlockLength(), m_actingVersion%14$s);\n" +
" skipper.skip();\n" +
" return skipper.encodedLength();\n" +
" }\n\n" +
@@ -2157,15 +2870,269 @@ private CharSequence generateMessageFlyweightCode(final String className, final
generateLiteral(ir.headerStructure().schemaVersionType(), Integer.toString(ir.version())),
semanticType,
className,
- generateConstructorsAndOperators(className),
+ generateConstructorsAndOperators(className, fieldPrecedenceModel),
formatClassName(headerType),
- semanticVersion);
+ semanticVersion,
+ codecStateArgument,
+ generateFieldOrderStateEnum(fieldPrecedenceModel),
+ generateLookupTableDeclarations(fieldPrecedenceModel),
+ generateFieldOrderStateMember(fieldPrecedenceModel),
+ generateAccessOrderErrorType(fieldPrecedenceModel),
+ generateEncoderWrapListener(fieldPrecedenceModel),
+ generateDecoderWrapListener(fieldPrecedenceModel),
+ generateDecoderWrapListenerCall(fieldPrecedenceModel),
+ generateHiddenCopyConstructor(" ", className));
+ }
+
+ private CharSequence generateAccessOrderErrorType(final FieldPrecedenceModel fieldPrecedenceModel)
+ {
+ if (null == fieldPrecedenceModel)
+ {
+ return "";
+ }
+
+ final StringBuilder sb = new StringBuilder();
+ sb.append(INDENT).append("class AccessOrderError : public std::logic_error\n")
+ .append(INDENT).append("{\n")
+ .append(INDENT).append("public:\n")
+ .append(INDENT).append(" explicit AccessOrderError(const std::string &msg) : std::logic_error(msg) {}\n")
+ .append(INDENT).append("};\n\n");
+ return sb;
+ }
+
+ private static CharSequence generateLookupTableDeclarations(final FieldPrecedenceModel fieldPrecedenceModel)
+ {
+ if (null == fieldPrecedenceModel)
+ {
+ return "";
+ }
+
+ final StringBuilder sb = new StringBuilder();
+ sb.append(INDENT).append("static const std::string STATE_NAME_LOOKUP[")
+ .append(fieldPrecedenceModel.stateCount())
+ .append("];\n");
+ sb.append(INDENT).append("static const std::string STATE_TRANSITIONS_LOOKUP[")
+ .append(fieldPrecedenceModel.stateCount())
+ .append("];\n\n");
+
+ sb.append(INDENT).append("static std::string codecStateName(CodecState state)\n")
+ .append(INDENT).append("{\n")
+ .append(TWO_INDENT).append("return STATE_NAME_LOOKUP[static_cast(state)];\n")
+ .append(INDENT).append("}\n\n");
+
+ sb.append(INDENT).append("static std::string codecStateTransitions(CodecState state)\n")
+ .append(INDENT).append("{\n")
+ .append(TWO_INDENT).append("return STATE_TRANSITIONS_LOOKUP[static_cast(state)];\n")
+ .append(INDENT).append("}\n\n");
+
+ return sb;
+ }
+
+ private static void generateLookupTableDefinitions(
+ final StringBuilder sb,
+ final String className,
+ final FieldPrecedenceModel fieldPrecedenceModel)
+ {
+ if (null == fieldPrecedenceModel)
+ {
+ return;
+ }
+
+ sb.append("\n").append("const std::string ").append(className).append("::STATE_NAME_LOOKUP[")
+ .append(fieldPrecedenceModel.stateCount()).append("] =\n")
+ .append("{\n");
+ fieldPrecedenceModel.forEachStateOrderedByStateNumber(state ->
+ {
+ sb.append(INDENT).append("\"").append(state.name()).append("\",\n");
+ });
+ sb.append("};\n\n");
+
+ sb.append("const std::string ").append(className).append("::STATE_TRANSITIONS_LOOKUP[")
+ .append(fieldPrecedenceModel.stateCount()).append("] =\n")
+ .append("{\n");
+ fieldPrecedenceModel.forEachStateOrderedByStateNumber(state ->
+ {
+ sb.append(INDENT).append("\"");
+ final MutableBoolean isFirst = new MutableBoolean(true);
+ final Set transitionDescriptions = new HashSet<>();
+ fieldPrecedenceModel.forEachTransitionFrom(state, transitionGroup ->
+ {
+ if (transitionDescriptions.add(transitionGroup.exampleCode()))
+ {
+ if (isFirst.get())
+ {
+ isFirst.set(false);
+ }
+ else
+ {
+ sb.append(", ");
+ }
+
+ sb.append("\\\"").append(transitionGroup.exampleCode()).append("\\\"");
+ }
+ });
+ sb.append("\",\n");
+ });
+ sb.append("};\n\n");
+ }
+
+ private static CharSequence qualifiedStateCase(final FieldPrecedenceModel.State state)
+ {
+ return "CodecState::" + state.name();
+ }
+
+ private static CharSequence stateCaseForSwitchCase(final FieldPrecedenceModel.State state)
+ {
+ return qualifiedStateCase(state);
+ }
+
+ private static CharSequence unqualifiedStateCase(final FieldPrecedenceModel.State state)
+ {
+ return state.name();
+ }
+
+ private static CharSequence generateFieldOrderStateEnum(final FieldPrecedenceModel fieldPrecedenceModel)
+ {
+ if (null == fieldPrecedenceModel)
+ {
+ return "";
+ }
+
+ final StringBuilder sb = new StringBuilder();
+
+ sb.append(" /**\n");
+ sb.append(" * The states in which a encoder/decoder/codec can live.\n");
+ sb.append(" *\n");
+ sb.append(" * The state machine diagram below, encoded in the dot language, describes\n");
+ sb.append(" * the valid state transitions according to the order in which fields may be\n");
+ sb.append(" * accessed safely. Tools such as PlantUML and Graphviz can render it.\n");
+ sb.append(" *\n");
+ sb.append(" *
{@code\n");
+ fieldPrecedenceModel.generateGraph(sb, " * ");
+ sb.append(" * }
\n");
+ sb.append(" */\n");
+ sb.append(INDENT).append("enum class CodecState\n")
+ .append(INDENT).append("{\n");
+ fieldPrecedenceModel.forEachStateOrderedByStateNumber(state ->
+ {
+ sb.append(INDENT).append(INDENT).append(unqualifiedStateCase(state))
+ .append(" = ").append(state.number())
+ .append(",\n");
+ });
+ sb.append(INDENT).append("};\n\n");
+
+ return sb;
+ }
+
+ private static CharSequence generateFieldOrderStateMember(final FieldPrecedenceModel fieldPrecedenceModel)
+ {
+ if (null == fieldPrecedenceModel)
+ {
+ return "\n";
+ }
+
+ final StringBuilder sb = new StringBuilder();
+
+ sb.append(INDENT).append("CodecState m_codecState = ")
+ .append(qualifiedStateCase(fieldPrecedenceModel.notWrappedState()))
+ .append(";\n\n");
+
+ sb.append(INDENT).append("CodecState codecState() const\n")
+ .append(INDENT).append("{\n")
+ .append(INDENT).append(INDENT).append("return m_codecState;\n")
+ .append(INDENT).append("}\n\n");
+
+ sb.append(INDENT).append("CodecState *codecStatePtr()\n")
+ .append(INDENT).append("{\n")
+ .append(INDENT).append(INDENT).append("return &m_codecState;\n")
+ .append(INDENT).append("}\n\n");
+
+ sb.append(INDENT).append("void codecState(CodecState newState)\n")
+ .append(INDENT).append("{\n")
+ .append(INDENT).append(INDENT).append("m_codecState = newState;\n")
+ .append(INDENT).append("}\n\n");
+
+ return sb;
+ }
+
+ private static CharSequence generateDecoderWrapListener(final FieldPrecedenceModel fieldPrecedenceModel)
+ {
+ if (null == fieldPrecedenceModel)
+ {
+ return "";
+ }
+
+ if (fieldPrecedenceModel.versionCount() == 1)
+ {
+ return "";
+ }
+
+ final StringBuilder sb = new StringBuilder();
+ sb.append(INDENT).append("void onWrapForDecode(std::uint64_t actingVersion)\n")
+ .append(INDENT).append("{\n")
+ .append(INDENT).append(INDENT).append("switch(actingVersion)\n")
+ .append(INDENT).append(INDENT).append("{\n");
+
+ fieldPrecedenceModel.forEachWrappedStateByVersion((version, state) ->
+ {
+ sb.append(INDENT).append(TWO_INDENT).append("case ").append(version).append(":\n")
+ .append(INDENT).append(THREE_INDENT).append("codecState(")
+ .append(qualifiedStateCase(state)).append(");\n")
+ .append(INDENT).append(THREE_INDENT).append("break;\n");
+ });
+
+ sb.append(INDENT).append(TWO_INDENT).append("default:\n")
+ .append(INDENT).append(THREE_INDENT).append("codecState(")
+ .append(qualifiedStateCase(fieldPrecedenceModel.latestVersionWrappedState())).append(");\n")
+ .append(INDENT).append(THREE_INDENT).append("break;\n")
+ .append(INDENT).append(INDENT).append("}\n")
+ .append(INDENT).append("}\n\n");
+
+ return sb;
+ }
+
+
+ private CharSequence generateDecoderWrapListenerCall(final FieldPrecedenceModel fieldPrecedenceModel)
+ {
+ if (null == fieldPrecedenceModel)
+ {
+ return "";
+ }
+
+ if (fieldPrecedenceModel.versionCount() == 1)
+ {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("#if defined(").append(precedenceChecksFlagName).append(")\n")
+ .append(TWO_INDENT).append("codecState(")
+ .append(qualifiedStateCase(fieldPrecedenceModel.latestVersionWrappedState())).append(");\n")
+ .append("#endif\n");
+ return sb;
+ }
+
+ return generateAccessOrderListenerCall(fieldPrecedenceModel, TWO_INDENT, "onWrapForDecode", "actingVersion");
+ }
+
+ private CharSequence generateEncoderWrapListener(final FieldPrecedenceModel fieldPrecedenceModel)
+ {
+ if (null == fieldPrecedenceModel)
+ {
+ return "";
+ }
+
+ final StringBuilder sb = new StringBuilder();
+ sb.append("#if defined(").append(precedenceChecksFlagName).append(")\n")
+ .append(TWO_INDENT).append("codecState(")
+ .append(qualifiedStateCase(fieldPrecedenceModel.latestVersionWrappedState()))
+ .append(");\n")
+ .append("#endif\n");
+ return sb;
}
private void generateFields(
final StringBuilder sb,
final String containingClassName,
final List tokens,
+ final FieldPrecedenceModel fieldPrecedenceModel,
final String indent)
{
for (int i = 0, size = tokens.size(); i < size; i++)
@@ -2179,23 +3146,29 @@ private void generateFields(
generateFieldMetaAttributeMethod(sb, signalToken, indent);
generateFieldCommonMethods(indent, sb, signalToken, encodingToken, propertyName);
+ generateAccessOrderListenerMethod(sb, fieldPrecedenceModel, indent, signalToken);
+
switch (encodingToken.signal())
{
case ENCODING:
generatePrimitiveProperty(
- sb, containingClassName, propertyName, signalToken, encodingToken, indent);
+ sb, containingClassName, propertyName, signalToken, encodingToken,
+ fieldPrecedenceModel, indent);
break;
case BEGIN_ENUM:
- generateEnumProperty(sb, containingClassName, signalToken, propertyName, encodingToken, indent);
+ generateEnumProperty(sb, containingClassName, signalToken, propertyName, encodingToken,
+ fieldPrecedenceModel, indent);
break;
case BEGIN_SET:
- generateBitsetProperty(sb, propertyName, encodingToken, indent);
+ generateBitsetProperty(sb, propertyName, signalToken, encodingToken,
+ fieldPrecedenceModel, indent);
break;
case BEGIN_COMPOSITE:
- generateCompositeProperty(sb, propertyName, encodingToken, indent);
+ generateCompositeProperty(sb, propertyName, signalToken, encodingToken,
+ fieldPrecedenceModel, indent);
break;
default:
@@ -2310,6 +3283,7 @@ private void generateEnumProperty(
final Token fieldToken,
final String propertyName,
final Token encodingToken,
+ final FieldPrecedenceModel fieldPrecedenceModel,
final String indent)
{
final String enumName = formatClassName(encodingToken.applicableTypeName());
@@ -2362,21 +3336,31 @@ private void generateEnumProperty(
else
{
final String offsetStr = Integer.toString(offset);
+
+ final CharSequence accessOrderListenerCall = generateAccessOrderListenerCall(
+ fieldPrecedenceModel, indent + TWO_INDENT, fieldToken);
+
+ final String noexceptDeclaration = noexceptDeclaration(fieldPrecedenceModel);
+
new Formatter(sb).format("\n" +
- indent + " SBE_NODISCARD %1$s %2$sRaw() const SBE_NOEXCEPT\n" +
+ indent + " SBE_NODISCARD %1$s %2$sRaw() const%6$s\n" +
indent + " {\n" +
"%3$s" +
"%4$s" +
+ "%5$s" +
indent + " }\n",
typeName,
propertyName,
generateFieldNotPresentCondition(fieldToken.version(), encodingToken.encoding(), indent),
- generateLoadValue(primitiveType, offsetStr, encodingToken.encoding().byteOrder(), indent));
+ accessOrderListenerCall,
+ generateLoadValue(primitiveType, offsetStr, encodingToken.encoding().byteOrder(), indent),
+ noexceptDeclaration);
new Formatter(sb).format("\n" +
indent + " SBE_NODISCARD %1$s::Value %2$s() const\n" +
indent + " {\n" +
"%3$s" +
+ "%7$s" +
indent + " %5$s val;\n" +
indent + " std::memcpy(&val, m_buffer + m_offset + %6$d, sizeof(%5$s));\n" +
indent + " return %1$s::get(%4$s(val));\n" +
@@ -2386,11 +3370,13 @@ private void generateEnumProperty(
generateEnumFieldNotPresentCondition(fieldToken.version(), enumName, indent),
formatByteOrderEncoding(encodingToken.encoding().byteOrder(), primitiveType),
typeName,
- offset);
+ offset,
+ accessOrderListenerCall);
new Formatter(sb).format("\n" +
- indent + " %1$s &%2$s(const %3$s::Value value) SBE_NOEXCEPT\n" +
+ indent + " %1$s &%2$s(const %3$s::Value value)%8$s\n" +
indent + " {\n" +
+ "%7$s" +
indent + " %4$s val = %6$s(value);\n" +
indent + " std::memcpy(m_buffer + m_offset + %5$d, &val, sizeof(%4$s));\n" +
indent + " return *this;\n" +
@@ -2400,15 +3386,22 @@ private void generateEnumProperty(
enumName,
typeName,
offset,
- formatByteOrderEncoding(encodingToken.encoding().byteOrder(), primitiveType));
+ formatByteOrderEncoding(encodingToken.encoding().byteOrder(), primitiveType),
+ accessOrderListenerCall,
+ noexceptDeclaration);
}
}
- private static void generateBitsetProperty(
- final StringBuilder sb, final String propertyName, final Token token, final String indent)
+ private void generateBitsetProperty(
+ final StringBuilder sb,
+ final String propertyName,
+ final Token fieldToken,
+ final Token encodingToken,
+ final FieldPrecedenceModel fieldPrecedenceModel,
+ final String indent)
{
- final String bitsetName = formatClassName(token.applicableTypeName());
- final int offset = token.offset();
+ final String bitsetName = formatClassName(encodingToken.applicableTypeName());
+ final int offset = encodingToken.offset();
new Formatter(sb).format("\n" +
indent + "private:\n" +
@@ -2418,15 +3411,20 @@ private static void generateBitsetProperty(
bitsetName,
propertyName);
+ final CharSequence accessOrderListenerCall = generateAccessOrderListenerCall(
+ fieldPrecedenceModel, indent + TWO_INDENT, fieldToken);
+
new Formatter(sb).format(
indent + " SBE_NODISCARD %1$s &%2$s()\n" +
indent + " {\n" +
+ "%4$s" +
indent + " m_%2$s.wrap(m_buffer, m_offset + %3$d, m_actingVersion, m_bufferLength);\n" +
indent + " return m_%2$s;\n" +
indent + " }\n",
bitsetName,
propertyName,
- offset);
+ offset,
+ accessOrderListenerCall);
new Formatter(sb).format("\n" +
indent + " static SBE_CONSTEXPR std::size_t %1$sEncodingLength() SBE_NOEXCEPT\n" +
@@ -2434,13 +3432,18 @@ private static void generateBitsetProperty(
indent + " return %2$d;\n" +
indent + " }\n",
propertyName,
- token.encoding().primitiveType().size());
+ encodingToken.encoding().primitiveType().size());
}
- private static void generateCompositeProperty(
- final StringBuilder sb, final String propertyName, final Token token, final String indent)
+ private void generateCompositeProperty(
+ final StringBuilder sb,
+ final String propertyName,
+ final Token fieldToken,
+ final Token encodingToken,
+ final FieldPrecedenceModel fieldPrecedenceModel,
+ final String indent)
{
- final String compositeName = formatClassName(token.applicableTypeName());
+ final String compositeName = formatClassName(encodingToken.applicableTypeName());
new Formatter(sb).format("\n" +
"private:\n" +
@@ -2450,15 +3453,20 @@ private static void generateCompositeProperty(
compositeName,
propertyName);
+ final CharSequence accessOrderListenerCall = generateAccessOrderListenerCall(
+ fieldPrecedenceModel, indent + TWO_INDENT, fieldToken);
+
new Formatter(sb).format(
indent + " SBE_NODISCARD %1$s &%2$s()\n" +
indent + " {\n" +
+ "%4$s" +
indent + " m_%2$s.wrap(m_buffer, m_offset + %3$d, m_actingVersion, m_bufferLength);\n" +
indent + " return m_%2$s;\n" +
indent + " }\n",
compositeName,
propertyName,
- token.offset());
+ encodingToken.offset(),
+ accessOrderListenerCall);
}
private CharSequence generateNullValueLiteral(final PrimitiveType primitiveType, final Encoding encoding)
diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharp.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharp.java
index 5b4870a321..ee55d214d5 100644
--- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharp.java
+++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/csharp/CSharp.java
@@ -18,6 +18,7 @@
import uk.co.real_logic.sbe.generation.CodeGenerator;
import uk.co.real_logic.sbe.generation.TargetCodeGenerator;
+import uk.co.real_logic.sbe.generation.TargetCodeGeneratorLoader;
import uk.co.real_logic.sbe.ir.Ir;
/**
@@ -30,6 +31,9 @@ public class CSharp implements TargetCodeGenerator
*/
public CodeGenerator newInstance(final Ir ir, final String outputDir)
{
- return new CSharpGenerator(ir, new CSharpNamespaceOutputManager(outputDir, ir.applicableNamespace()));
+ return new CSharpGenerator(
+ ir,
+ TargetCodeGeneratorLoader.precedenceChecks(),
+ new CSharpNamespaceOutputManager(outputDir, ir.applicableNamespace()));
}
}
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 5a607a8414..8fe17df71a 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
@@ -19,26 +19,30 @@
import uk.co.real_logic.sbe.PrimitiveType;
import uk.co.real_logic.sbe.PrimitiveValue;
import uk.co.real_logic.sbe.generation.CodeGenerator;
-import org.agrona.generation.OutputManager;
import uk.co.real_logic.sbe.generation.Generators;
-import uk.co.real_logic.sbe.ir.*;
+import uk.co.real_logic.sbe.generation.common.FieldPrecedenceModel;
+import uk.co.real_logic.sbe.generation.common.PrecedenceChecks;
+import uk.co.real_logic.sbe.ir.Encoding;
+import uk.co.real_logic.sbe.ir.Ir;
+import uk.co.real_logic.sbe.ir.Signal;
+import uk.co.real_logic.sbe.ir.Token;
import org.agrona.Verify;
+import org.agrona.collections.MutableBoolean;
+import org.agrona.generation.OutputManager;
import java.io.IOException;
import java.io.Writer;
import java.nio.ByteOrder;
import java.util.ArrayList;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
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.*;
-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.*;
/**
* Codec generator for the CSharp programming language.
@@ -54,6 +58,8 @@ public class CSharpGenerator implements CodeGenerator
private final Ir ir;
private final OutputManager outputManager;
+ private final PrecedenceChecks precedenceChecks;
+ private final String precedenceChecksFlagName;
/**
* Create a new C# language {@link CodeGenerator}.
@@ -62,11 +68,32 @@ public class CSharpGenerator implements CodeGenerator
* @param outputManager for generating the codecs to.
*/
public CSharpGenerator(final Ir ir, final OutputManager outputManager)
+ {
+ this(
+ ir,
+ PrecedenceChecks.newInstance(new PrecedenceChecks.Context()),
+ outputManager
+ );
+ }
+
+ /**
+ * Create a new C# language {@link CodeGenerator}.
+ *
+ * @param ir for the messages and types.
+ * @param precedenceChecks whether and how to perform field precedence checks.
+ * @param outputManager for generating the codecs to.
+ */
+ public CSharpGenerator(
+ final Ir ir,
+ final PrecedenceChecks precedenceChecks,
+ final OutputManager outputManager)
{
Verify.notNull(ir, "ir");
Verify.notNull(outputManager, "outputManager");
this.ir = ir;
+ this.precedenceChecks = precedenceChecks;
+ this.precedenceChecksFlagName = precedenceChecks.context().precedenceChecksFlagName();
this.outputManager = outputManager;
}
@@ -123,32 +150,38 @@ public void generate() throws IOException
{
final Token msgToken = tokens.get(0);
final String className = formatClassName(msgToken.name());
+ final String stateClassName = className + ".CodecState";
+ final FieldPrecedenceModel fieldPrecedenceModel = precedenceChecks.createCodecModel(stateClassName, tokens);
try (Writer out = outputManager.createOutput(className))
{
- out.append(generateFileHeader(ir.applicableNamespace()));
- out.append(generateDocumentation(BASE_INDENT, msgToken));
- out.append(generateClassDeclaration(className));
- out.append(generateMessageFlyweightCode(className, msgToken, BASE_INDENT));
-
final List messageBody = tokens.subList(1, tokens.size() - 1);
int offset = 0;
-
final List fields = new ArrayList<>();
offset = collectFields(messageBody, offset, fields);
- out.append(generateFields(fields, BASE_INDENT));
-
final List groups = new ArrayList<>();
offset = collectGroups(messageBody, offset, groups);
+ final List varData = new ArrayList<>();
+ collectVarData(messageBody, offset, varData);
+
+ out.append(generateFileHeader(ir.applicableNamespace()));
+ out.append(generateDocumentation(BASE_INDENT, msgToken));
+ out.append(generateClassDeclaration(className));
+ out.append(generateMessageFlyweightCode(className, msgToken, fieldPrecedenceModel, BASE_INDENT));
+
+ out.append(generateFieldOrderStates(BASE_INDENT + INDENT, fieldPrecedenceModel));
+ out.append(generateFullyEncodedCheck(BASE_INDENT + INDENT, fieldPrecedenceModel));
+
+ out.append(generateFields(fieldPrecedenceModel, fields, BASE_INDENT));
+
final StringBuilder sb = new StringBuilder();
- generateGroups(sb, className, groups, BASE_INDENT);
+ generateGroups(sb, className, groups, fieldPrecedenceModel, BASE_INDENT);
out.append(sb);
- final List varData = new ArrayList<>();
- collectVarData(messageBody, offset, varData);
- out.append(generateVarData(varData, BASE_INDENT + INDENT));
+ out.append(generateVarData(fieldPrecedenceModel, varData, BASE_INDENT + INDENT));
- out.append(generateDisplay(toUpperFirstChar(msgToken.name()), fields, groups, varData));
+ out.append(generateDisplay(toUpperFirstChar(msgToken.name()),
+ fields, groups, varData, fieldPrecedenceModel));
out.append(INDENT + "}\n");
out.append("}\n");
@@ -160,6 +193,7 @@ private void generateGroups(
final StringBuilder sb,
final String parentMessageClassName,
final List tokens,
+ final FieldPrecedenceModel fieldPrecedenceModel,
final String indent)
{
for (int i = 0, size = tokens.size(); i < size; i++)
@@ -170,23 +204,24 @@ private void generateGroups(
throw new IllegalStateException("tokens must begin with BEGIN_GROUP: token=" + groupToken);
}
final String groupName = groupToken.name();
- sb.append(generateGroupProperty(groupName, groupToken, indent + INDENT));
+ sb.append(generateGroupProperty(groupName, fieldPrecedenceModel, groupToken, indent + INDENT));
- generateGroupClassHeader(sb, groupName, parentMessageClassName, tokens, i, indent + INDENT);
+ generateGroupClassHeader(sb, groupName, parentMessageClassName, tokens,
+ fieldPrecedenceModel, i, indent + INDENT);
i++;
i += tokens.get(i).componentTokenCount();
final List fields = new ArrayList<>();
i = collectFields(tokens, i, fields);
- sb.append(generateFields(fields, indent + INDENT));
+ sb.append(generateFields(fieldPrecedenceModel, fields, indent + INDENT));
final List groups = new ArrayList<>();
i = collectGroups(tokens, i, groups);
- generateGroups(sb, parentMessageClassName, groups, indent + INDENT);
+ generateGroups(sb, parentMessageClassName, groups, fieldPrecedenceModel, indent + INDENT);
final List varData = new ArrayList<>();
i = collectVarData(tokens, i, varData);
- sb.append(generateVarData(varData, indent + INDENT + INDENT));
+ sb.append(generateVarData(fieldPrecedenceModel, varData, indent + INDENT + INDENT));
appendGroupInstanceDisplay(sb, fields, groups, varData, indent + TWO_INDENT);
@@ -199,6 +234,7 @@ private void generateGroupClassHeader(
final String groupName,
final String parentMessageClassName,
final List tokens,
+ final FieldPrecedenceModel fieldPrecedenceModel,
final int index,
final String indent)
{
@@ -232,6 +268,16 @@ private void generateGroupClassHeader(
groupName));
}
+ sb.append("\n")
+ // The "file" access modifier is more suitable but only available from C# 11 onwards.
+ .append(indent).append(INDENT).append("internal void NotPresent()\n")
+ .append(indent).append(INDENT).append("{\n")
+ .append(indent).append(TWO_INDENT).append("_count = 0;\n")
+ .append(indent).append(TWO_INDENT).append("_index = 0;\n")
+ .append(indent).append(TWO_INDENT).append("_buffer = null;\n")
+ .append(indent).append(TWO_INDENT).append("_offset = 0;\n")
+ .append(indent).append(INDENT).append("}\n");
+
sb.append(String.format("\n" +
indent + INDENT + "public void WrapForDecode(%s parentMessage, DirectBuffer buffer, int actingVersion)\n" +
indent + INDENT + "{\n" +
@@ -284,27 +330,50 @@ private void generateGroupClassHeader(
blockLength,
dimensionHeaderLength));
- generateGroupEnumerator(sb, groupName, typeForNumInGroup, indent);
+ if (null != fieldPrecedenceModel)
+ {
+ sb.append("\n")
+ .append(indent).append(" private CodecState codecState()\n")
+ .append(indent).append(" {\n")
+ .append(indent).append(" return _parentMessage.codecState();\n")
+ .append(indent).append(" }\n");
+
+ sb.append("\n")
+ .append(indent).append(" private void codecState(CodecState newState)\n")
+ .append(indent).append(" {\n")
+ .append(indent).append(" _parentMessage.codecState(newState);\n")
+ .append(indent).append(" }\n");
+ }
+
+ final Token groupToken = tokens.get(index);
+ generateGroupEnumerator(sb, fieldPrecedenceModel, groupToken, groupName, typeForNumInGroup, indent);
}
private void generateGroupEnumerator(
final StringBuilder sb,
+ final FieldPrecedenceModel fieldPrecedenceModel,
+ final Token groupToken,
final String groupName,
final String typeForNumInGroup,
final String indent)
{
+ generateAccessOrderListenerMethodForNextGroupElement(sb, fieldPrecedenceModel, indent + INDENT, groupToken);
+ generateAccessOrderListenerMethodForResetGroupCount(sb, fieldPrecedenceModel, indent + INDENT, groupToken);
+
sb.append(
indent + INDENT + "public int ActingBlockLength { get { return _blockLength; } }\n\n" +
indent + INDENT + "public int Count { get { return _count; } }\n\n" +
- indent + INDENT + "public bool HasNext { get { return _index < _count; } }\n");
+ indent + INDENT + "public bool HasNext { get { return _index < _count; } }\n\n");
sb.append(String.format("\n" +
indent + INDENT + "public int ResetCountToIndex()\n" +
indent + INDENT + "{\n" +
+ "%s" +
indent + INDENT + INDENT + "_count = _index;\n" +
indent + INDENT + INDENT + "_dimensions.NumInGroup = (%s) _count;\n\n" +
indent + INDENT + INDENT + "return _count;\n" +
indent + INDENT + "}\n",
+ generateAccessOrderListenerCall(fieldPrecedenceModel, indent + TWO_INDENT, "OnResetCountToIndex"),
typeForNumInGroup));
sb.append(String.format("\n" +
@@ -314,6 +383,7 @@ private void generateGroupEnumerator(
indent + INDENT + INDENT + "{\n" +
indent + INDENT + INDENT + INDENT + "ThrowHelper.ThrowInvalidOperationException();\n" +
indent + INDENT + INDENT + "}\n\n" +
+ generateAccessOrderListenerCall(fieldPrecedenceModel, indent + TWO_INDENT, "OnNextElementAccessed") +
indent + INDENT + INDENT + "_offset = _parentMessage.Limit;\n" +
indent + INDENT + INDENT + "_parentMessage.Limit = _offset + _blockLength;\n" +
indent + INDENT + INDENT + "++_index;\n\n" +
@@ -338,7 +408,11 @@ private boolean isRepresentableByInt32(final Encoding encoding)
encoding.applicableMaxValue().longValue() <= Integer.MAX_VALUE;
}
- private CharSequence generateGroupProperty(final String groupName, final Token token, final String indent)
+ private CharSequence generateGroupProperty(
+ final String groupName,
+ final FieldPrecedenceModel fieldPrecedenceModel,
+ final Token token,
+ final String indent)
{
final StringBuilder sb = new StringBuilder();
@@ -355,37 +429,65 @@ private CharSequence generateGroupProperty(final String groupName, final Token t
toUpperFirstChar(groupName),
token.id()));
+ generateAccessOrderListenerMethodForGroupWrap(sb, fieldPrecedenceModel, indent, token);
+
generateSinceActingDeprecated(sb, indent, toUpperFirstChar(groupName), token);
+ final String groupField = "_" + toLowerFirstChar(groupName);
+
+ final CharSequence accessOrderListenerCallOnDecode = generateAccessOrderListenerCall(
+ fieldPrecedenceModel,
+ indent + TWO_INDENT,
+ token,
+ groupField + ".Count",
+ "\"decode\"");
+
sb.append(String.format("\n" +
"%1$s" +
indent + "public %2$sGroup %3$s\n" +
indent + "{\n" +
indent + INDENT + "get\n" +
indent + INDENT + "{\n" +
+ "%5$s" +
+
indent + INDENT + INDENT + "_%4$s.WrapForDecode(_parentMessage, _buffer, _actingVersion);\n" +
+ "%6$s" +
+
indent + INDENT + INDENT + "return _%4$s;\n" +
indent + INDENT + "}\n" +
indent + "}\n",
generateDocumentation(indent, token),
className,
toUpperFirstChar(groupName),
- toLowerFirstChar(groupName)));
-
+ toLowerFirstChar(groupName),
+ generateGroupNotPresentCondition(token.version(), indent + INDENT + INDENT, groupField),
+ accessOrderListenerCallOnDecode));
+
+ final CharSequence accessOrderListenerCallOnEncode = generateAccessOrderListenerCall(
+ fieldPrecedenceModel,
+ indent + INDENT,
+ token,
+ "count",
+ "\"encode\"");
sb.append(String.format("\n" +
indent + "public %1$sGroup %2$sCount(int count)\n" +
indent + "{\n" +
+ "%4$s" +
indent + INDENT + "_%3$s.WrapForEncode(_parentMessage, _buffer, count);\n" +
indent + INDENT + "return _%3$s;\n" +
indent + "}\n",
className,
toUpperFirstChar(groupName),
- toLowerFirstChar(groupName)));
+ toLowerFirstChar(groupName),
+ accessOrderListenerCallOnEncode));
return sb;
}
- private CharSequence generateVarData(final List tokens, final String indent)
+ private CharSequence generateVarData(
+ final FieldPrecedenceModel fieldPrecedenceModel,
+ final List tokens,
+ final String indent)
{
final StringBuilder sb = new StringBuilder();
@@ -412,21 +514,34 @@ private CharSequence generateVarData(final List tokens, final String inde
final ByteOrder byteOrder = lengthEncoding.byteOrder();
final String byteOrderStr = generateByteOrder(byteOrder, lengthEncoding.primitiveType().size());
+ generateAccessOrderListenerMethodForVarDataLength(sb, fieldPrecedenceModel, indent, token);
+ generateAccessOrderListenerMethod(sb, fieldPrecedenceModel, indent, token);
+
sb.append(String.format("\n" +
indent + "public const int %sHeaderSize = %d;\n",
propertyName,
sizeOfLengthField));
+ final CharSequence lengthAccessOrderListenerCall = generateAccessOrderListenerCall(
+ fieldPrecedenceModel, indent + INDENT, accessOrderListenerMethodName(token, "Length"));
+
sb.append(String.format(indent + "\n" +
indent + "public int %1$sLength()\n" +
indent + "{\n" +
+ "%5$s" +
+ "%6$s" +
indent + INDENT + "_buffer.CheckLimit(_parentMessage.Limit + %2$d);\n" +
indent + INDENT + "return (int)_buffer.%3$sGet%4$s(_parentMessage.Limit);\n" +
indent + "}\n",
propertyName,
sizeOfLengthField,
lengthTypePrefix,
- byteOrderStr));
+ byteOrderStr,
+ generateArrayFieldNotPresentCondition(token.version(), indent, "0"),
+ lengthAccessOrderListenerCall));
+
+ final CharSequence accessOrderListenerCall =
+ generateAccessOrderListenerCall(fieldPrecedenceModel, indent + INDENT, token);
sb.append(String.format("\n" +
indent + "public int Get%1$s(byte[] dst, int dstOffset, int length) =>\n" +
@@ -437,6 +552,7 @@ private CharSequence generateVarData(final List tokens, final String inde
indent + "public int Get%1$s(Span dst)\n" +
indent + "{\n" +
"%2$s" +
+ "%6$s" +
indent + INDENT + "const int sizeOfLengthField = %3$d;\n" +
indent + INDENT + "int limit = _parentMessage.Limit;\n" +
indent + INDENT + "_buffer.CheckLimit(limit + sizeOfLengthField);\n" +
@@ -447,15 +563,18 @@ private CharSequence generateVarData(final List tokens, final String inde
indent + INDENT + "return bytesCopied;\n" +
indent + "}\n",
propertyName,
- generateArrayFieldNotPresentCondition(token.version(), indent),
+ generateArrayFieldNotPresentCondition(token.version(), indent, "0"),
sizeOfLengthField,
lengthTypePrefix,
- byteOrderStr));
+ byteOrderStr,
+ accessOrderListenerCall));
sb.append(String.format(indent + "\n" +
indent + "// Allocates and returns a new byte array\n" +
indent + "public byte[] Get%1$sBytes()\n" +
indent + "{\n" +
+ "%5$s" +
+ "%6$s" +
indent + INDENT + "const int sizeOfLengthField = %2$d;\n" +
indent + INDENT + "int limit = _parentMessage.Limit;\n" +
indent + INDENT + "_buffer.CheckLimit(limit + sizeOfLengthField);\n" +
@@ -468,7 +587,9 @@ private CharSequence generateVarData(final List tokens, final String inde
propertyName,
sizeOfLengthField,
lengthTypePrefix,
- byteOrderStr));
+ byteOrderStr,
+ generateArrayFieldNotPresentCondition(token.version(), indent, "new byte[0]"),
+ accessOrderListenerCall));
sb.append(String.format("\n" +
indent + "public int Set%1$s(byte[] src, int srcOffset, int length) =>\n" +
@@ -478,6 +599,7 @@ private CharSequence generateVarData(final List tokens, final String inde
sb.append(String.format("\n" +
indent + "public int Set%1$s(ReadOnlySpan src)\n" +
indent + "{\n" +
+ "%6$s" +
indent + INDENT + "const int sizeOfLengthField = %2$d;\n" +
indent + INDENT + "int limit = _parentMessage.Limit;\n" +
indent + INDENT + "_parentMessage.Limit = limit + sizeOfLengthField + src.Length;\n" +
@@ -489,7 +611,8 @@ private CharSequence generateVarData(final List tokens, final String inde
sizeOfLengthField,
lengthTypePrefix,
lengthCSharpType,
- byteOrderStr));
+ byteOrderStr,
+ accessOrderListenerCall));
if (characterEncoding != null) // only generate these string based methods if there is an encoding
{
@@ -497,6 +620,8 @@ private CharSequence generateVarData(final List tokens, final String inde
.append(String.format(
indent + "public string Get%1$s()\n" +
indent + "{\n" +
+ "%6$s" +
+ "%7$s" +
indent + INDENT + "const int sizeOfLengthField = %2$d;\n" +
indent + INDENT + "int limit = _parentMessage.Limit;\n" +
indent + INDENT + "_buffer.CheckLimit(limit + sizeOfLengthField);\n" +
@@ -507,15 +632,22 @@ private CharSequence generateVarData(final List tokens, final String inde
indent + "}\n\n" +
indent + "public void Set%1$s(string value)\n" +
indent + "{\n" +
+ "%7$s" +
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 + INDENT + "_buffer.%3$sPut%4$s(limit, (%5$s)byteCount);\n" +
indent + "}\n",
- propertyName, sizeOfLengthField, lengthTypePrefix, byteOrderStr));
+ propertyName,
+ sizeOfLengthField,
+ lengthTypePrefix,
+ byteOrderStr,
+ lengthCSharpType,
+ generateArrayFieldNotPresentCondition(token.version(), indent, "\"\""),
+ accessOrderListenerCall));
}
}
}
@@ -588,23 +720,24 @@ private CharSequence generateCompositePropertyElements(final List tokens,
{
final Token token = tokens.get(i);
final String propertyName = formatPropertyName(token.name());
+ final FieldPrecedenceModel fieldPrecedenceModel = null;
switch (token.signal())
{
case ENCODING:
- sb.append(generatePrimitiveProperty(propertyName, token, token, indent));
+ sb.append(generatePrimitiveProperty(propertyName, token, token, fieldPrecedenceModel, indent));
break;
case BEGIN_ENUM:
- sb.append(generateEnumProperty(propertyName, token, token, indent));
+ sb.append(generateEnumProperty(propertyName, token, token, fieldPrecedenceModel, indent));
break;
case BEGIN_SET:
- sb.append(generateBitSetProperty(propertyName, token, token, indent));
+ sb.append(generateBitSetProperty(propertyName, token, token, fieldPrecedenceModel, indent));
break;
case BEGIN_COMPOSITE:
- sb.append(generateCompositeProperty(propertyName, token, token, indent));
+ sb.append(generateCompositeProperty(propertyName, token, token, fieldPrecedenceModel, indent));
break;
default:
@@ -747,7 +880,11 @@ private CharSequence generateEnumDeclaration(
}
private CharSequence generatePrimitiveProperty(
- final String propertyName, final Token fieldToken, final Token typeToken, final String indent)
+ final String propertyName,
+ final Token fieldToken,
+ final Token typeToken,
+ final FieldPrecedenceModel fieldPrecedenceModel,
+ final String indent)
{
final StringBuilder sb = new StringBuilder();
@@ -759,7 +896,8 @@ private CharSequence generatePrimitiveProperty(
}
else
{
- sb.append(generatePrimitivePropertyMethods(propertyName, fieldToken, typeToken, indent));
+ sb.append(generatePrimitivePropertyMethods(propertyName, fieldToken, typeToken,
+ fieldPrecedenceModel, indent));
}
return sb;
@@ -769,17 +907,20 @@ private CharSequence generatePrimitivePropertyMethods(
final String propertyName,
final Token fieldToken,
final Token typeToken,
+ final FieldPrecedenceModel fieldPrecedenceModel,
final String indent)
{
final int arrayLength = typeToken.arrayLength();
if (arrayLength == 1)
{
- return generateSingleValueProperty(propertyName, fieldToken, typeToken, indent + INDENT);
+ return generateSingleValueProperty(propertyName, fieldToken, typeToken,
+ fieldPrecedenceModel, indent + INDENT);
}
else if (arrayLength > 1)
{
- return generateArrayProperty(propertyName, fieldToken, typeToken, indent + INDENT);
+ return generateArrayProperty(propertyName, fieldToken, typeToken,
+ fieldPrecedenceModel, indent + INDENT);
}
return "";
@@ -809,6 +950,7 @@ private CharSequence generateSingleValueProperty(
final String propertyName,
final Token fieldToken,
final Token typeToken,
+ final FieldPrecedenceModel fieldPrecedenceModel,
final String indent)
{
final String typeName = cSharpTypeName(typeToken.encoding().primitiveType());
@@ -817,6 +959,9 @@ private CharSequence generateSingleValueProperty(
final ByteOrder byteOrder = typeToken.encoding().byteOrder();
final String byteOrderStr = generateByteOrder(byteOrder, typeToken.encoding().primitiveType().size());
+ final CharSequence accessOrderListenerCall =
+ generateAccessOrderListenerCall(fieldPrecedenceModel, indent + TWO_INDENT, fieldToken);
+
return String.format("\n" +
"%1$s" +
indent + "public %2$s %3$s\n" +
@@ -824,10 +969,12 @@ private CharSequence generateSingleValueProperty(
indent + INDENT + "get\n" +
indent + INDENT + "{\n" +
"%4$s" +
+ "%8$s" +
indent + INDENT + INDENT + "return _buffer.%5$sGet%7$s(_offset + %6$d);\n" +
indent + INDENT + "}\n" +
indent + INDENT + "set\n" +
indent + INDENT + "{\n" +
+ "%8$s" +
indent + INDENT + INDENT + "_buffer.%5$sPut%7$s(_offset + %6$d, value);\n" +
indent + INDENT + "}\n" +
indent + "}\n\n",
@@ -837,7 +984,8 @@ private CharSequence generateSingleValueProperty(
generateFieldNotPresentCondition(fieldToken.version(), typeToken.encoding(), indent),
typePrefix,
offset,
- byteOrderStr);
+ byteOrderStr,
+ accessOrderListenerCall);
}
private CharSequence generateFieldNotPresentCondition(
@@ -866,16 +1014,34 @@ private CharSequence generateFieldNotPresentCondition(
literal);
}
- private CharSequence generateArrayFieldNotPresentCondition(final int sinceVersion, final String indent)
+ private CharSequence generateGroupNotPresentCondition(
+ final int sinceVersion,
+ final String indent,
+ final String groupInstanceField)
{
if (0 == sinceVersion)
{
return "";
}
- return String.format(
- indent + INDENT + INDENT + "if (_actingVersion < %d) return 0;\n\n",
- sinceVersion);
+ return indent + "if (_actingVersion < " + sinceVersion + ")" +
+ indent + "{\n" +
+ indent + INDENT + groupInstanceField + ".NotPresent();\n" +
+ indent + INDENT + "return " + groupInstanceField + ";\n" +
+ indent + "}\n\n";
+ }
+
+ private CharSequence generateArrayFieldNotPresentCondition(
+ final int sinceVersion,
+ final String indent,
+ final String defaultValue)
+ {
+ if (0 == sinceVersion)
+ {
+ return "";
+ }
+
+ return indent + INDENT + "if (_actingVersion < " + sinceVersion + ") return " + defaultValue + ";\n\n";
}
private CharSequence generateBitSetNotPresentCondition(
@@ -912,7 +1078,7 @@ private CharSequence generateArrayProperty(
final String propertyName,
final Token fieldToken,
final Token typeToken,
- final String indent)
+ final FieldPrecedenceModel fieldPrecedenceModel, final String indent)
{
final String typeName = cSharpTypeName(typeToken.encoding().primitiveType());
final String typePrefix = toUpperFirstChar(typeToken.encoding().primitiveType().primitiveName());
@@ -923,6 +1089,11 @@ private CharSequence generateArrayProperty(
final int typeSize = typeToken.encoding().primitiveType().size();
final String propName = toUpperFirstChar(propertyName);
+ final CharSequence accessOrderListenerCall =
+ generateAccessOrderListenerCall(fieldPrecedenceModel, indent + INDENT, fieldToken);
+ final CharSequence accessOrderListenerCallDoubleIndent =
+ generateAccessOrderListenerCall(fieldPrecedenceModel, indent + TWO_INDENT, fieldToken);
+
final StringBuilder sb = new StringBuilder();
sb.append(String.format("\n" +
@@ -938,12 +1109,14 @@ private CharSequence generateArrayProperty(
indent + INDENT + INDENT + "ThrowHelper.ThrowIndexOutOfRangeException(index);\n" +
indent + INDENT + "}\n\n" +
"%5$s" +
+ "%10$s" +
indent + INDENT + "return _buffer.%6$sGet%9$s(_offset + %7$d + (index * %8$d));\n" +
indent + "}\n",
generateDocumentation(indent, fieldToken),
typeName, propName, fieldLength,
generateFieldNotPresentCondition(fieldToken.version(), typeToken.encoding(), indent),
- typePrefix, offset, typeSize, byteOrderStr));
+ typePrefix, offset, typeSize, byteOrderStr,
+ accessOrderListenerCall));
sb.append(String.format("\n" +
"%1$s" +
@@ -953,29 +1126,48 @@ private CharSequence generateArrayProperty(
indent + INDENT + "{\n" +
indent + INDENT + INDENT + "ThrowHelper.ThrowIndexOutOfRangeException(index);\n" +
indent + INDENT + "}\n\n" +
+ "%9$s" +
indent + INDENT + "_buffer.%5$sPut%8$s(_offset + %6$d + (index * %7$d), value);\n" +
indent + "}\n",
generateDocumentation(indent, fieldToken),
- propName, typeName, fieldLength, typePrefix, offset, typeSize, byteOrderStr));
+ propName, typeName, fieldLength, typePrefix, offset, typeSize, byteOrderStr,
+ accessOrderListenerCall));
sb.append(String.format("\n" +
"%1$s" +
indent + "public ReadOnlySpan<%2$s> %3$s\n" +
indent + "{\n" +
- indent + INDENT + "get => _buffer.AsReadOnlySpan<%2$s>(_offset + %4$s, %3$sLength);\n" +
- indent + INDENT + "set => value.CopyTo(_buffer.AsSpan<%2$s>(_offset + %4$s, %3$sLength));\n" +
+ indent + INDENT + "get\n" +
+ indent + INDENT + "{\n" +
+ "%5$s" +
+ "%6$s" +
+ indent + INDENT + INDENT + "return _buffer.AsReadOnlySpan<%2$s>(_offset + %4$s, %3$sLength);\n" +
+ indent + INDENT + "}\n" +
+ indent + INDENT + "set\n" +
+ indent + INDENT + "{\n" +
+ "%6$s" +
+ indent + INDENT + INDENT + "value.CopyTo(_buffer.AsSpan<%2$s>(_offset + %4$s, %3$sLength));\n" +
+ indent + INDENT + "}\n" +
indent + "}\n",
generateDocumentation(indent, fieldToken),
- typeName, propName, offset));
+ typeName, propName, offset,
+ generateArrayFieldNotPresentCondition(fieldToken.version(),
+ indent + INDENT + INDENT, "new " + typeName + "[0]"),
+ accessOrderListenerCallDoubleIndent));
sb.append(String.format("\n" +
"%1$s" +
indent + "public Span<%2$s> %3$sAsSpan()\n" +
indent + "{\n" +
+ "%5$s" +
+ "%6$s" +
indent + INDENT + "return _buffer.AsSpan<%2$s>(_offset + %4$s, %3$sLength);\n" +
indent + "}\n",
generateDocumentation(indent, fieldToken),
- typeName, propName, offset));
+ typeName, propName, offset,
+ generateArrayFieldNotPresentCondition(fieldToken.version(),
+ indent + INDENT + INDENT, "new " + typeName + "[0]"),
+ accessOrderListenerCall));
if (typeToken.encoding().primitiveType() == PrimitiveType.CHAR)
{
@@ -986,9 +1178,13 @@ private CharSequence generateArrayProperty(
indent + "{\n" +
indent + INDENT + "const int length = %2$d;\n" +
"%3$s" +
+ "%4$s" +
indent + INDENT + "return Get%1$s(new Span(dst, dstOffset, length));\n" +
indent + "}\n",
- propName, fieldLength, generateArrayFieldNotPresentCondition(fieldToken.version(), indent), offset));
+ propName,
+ fieldLength,
+ generateArrayFieldNotPresentCondition(fieldToken.version(), indent, "0"),
+ accessOrderListenerCall));
sb.append(String.format("\n" +
indent + "public int Get%1$s(Span dst)\n" +
@@ -999,10 +1195,15 @@ private CharSequence generateArrayProperty(
indent + INDENT + INDENT + "ThrowHelper.ThrowWhenSpanLengthTooSmall(dst.Length);\n" +
indent + INDENT + "}\n\n" +
"%3$s" +
+ "%5$s" +
indent + INDENT + "_buffer.GetBytes(_offset + %4$d, dst);\n" +
indent + INDENT + "return length;\n" +
indent + "}\n",
- propName, fieldLength, generateArrayFieldNotPresentCondition(fieldToken.version(), indent), offset));
+ propName,
+ fieldLength,
+ generateArrayFieldNotPresentCondition(fieldToken.version(), indent, "0"),
+ offset,
+ accessOrderListenerCall));
sb.append(String.format("\n" +
indent + "public void Set%1$s(byte[] src, int srcOffset)\n" +
@@ -1019,22 +1220,28 @@ private CharSequence generateArrayProperty(
indent + INDENT + "{\n" +
indent + INDENT + INDENT + "ThrowHelper.ThrowWhenSpanLengthTooLarge(src.Length);\n" +
indent + INDENT + "}\n\n" +
+ "%4$s" +
indent + INDENT + "_buffer.SetBytes(_offset + %3$d, src);\n" +
indent + "}\n",
- propName, fieldLength, offset));
+ propName, fieldLength, offset,
+ accessOrderListenerCall));
sb.append(String.format("\n" +
indent + "public void Set%1$s(string value)\n" +
indent + "{\n" +
+ "%3$s" +
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" +
+ "%3$s" +
indent + INDENT + "return _buffer.GetStringFromNullTerminatedBytes(%1$sResolvedCharacterEncoding, " +
"_offset + %2$s, %1$sLength, %1$sNullValue);\n" +
indent + "}\n",
- propName, offset));
+ propName,
+ offset,
+ accessOrderListenerCall));
}
return sb;
@@ -1156,7 +1363,11 @@ private CharSequence generateFixedFlyweightCode(final int size)
size);
}
- private CharSequence generateMessageFlyweightCode(final String className, final Token token, final String indent)
+ private CharSequence generateMessageFlyweightCode(
+ final String className,
+ final Token token,
+ final FieldPrecedenceModel fieldPrecedenceModel,
+ final String indent)
{
final String blockLengthType = cSharpTypeName(ir.headerStructure().blockLengthType());
final String templateIdType = cSharpTypeName(ir.headerStructure().templateIdType());
@@ -1184,16 +1395,18 @@ private CharSequence generateMessageFlyweightCode(final String className, final
indent + INDENT + "{\n" +
indent + INDENT + INDENT + "_parentMessage = this;\n" +
indent + INDENT + "}\n\n" +
- indent + INDENT + "public void WrapForEncode(DirectBuffer buffer, int offset)\n" +
+ indent + INDENT + "public %10$s WrapForEncode(DirectBuffer buffer, int offset)\n" +
indent + INDENT + "{\n" +
+ "%12$s" +
indent + INDENT + INDENT + "_buffer = buffer;\n" +
indent + INDENT + INDENT + "_offset = offset;\n" +
indent + INDENT + INDENT + "_actingBlockLength = BlockLength;\n" +
indent + INDENT + INDENT + "_actingVersion = SchemaVersion;\n" +
indent + INDENT + INDENT + "Limit = offset + _actingBlockLength;\n" +
+ indent + INDENT + INDENT + "return this;\n" +
indent + INDENT + "}\n\n" +
- indent + INDENT + "public void WrapForEncodeAndApplyHeader(DirectBuffer buffer, int offset, " +
- " MessageHeader headerEncoder)\n" +
+ indent + INDENT + "public %10$s WrapForEncodeAndApplyHeader(DirectBuffer buffer, int offset, " +
+ "MessageHeader headerEncoder)\n" +
indent + INDENT + "{\n" +
indent + INDENT + INDENT + "headerEncoder.Wrap(buffer, offset, SchemaVersion);\n" +
indent + INDENT + INDENT + "headerEncoder.BlockLength = BlockLength;\n" +
@@ -1201,16 +1414,27 @@ private CharSequence generateMessageFlyweightCode(final String className, final
indent + INDENT + INDENT + "headerEncoder.SchemaId = SchemaId;\n" +
indent + INDENT + INDENT + "headerEncoder.Version = SchemaVersion;\n" +
indent + INDENT + INDENT + "\n" +
- indent + INDENT + INDENT + "WrapForEncode(buffer, offset + MessageHeader.Size);\n" +
+ indent + INDENT + INDENT + "return WrapForEncode(buffer, offset + MessageHeader.Size);\n" +
indent + INDENT + "}\n\n" +
- indent + INDENT + "public void WrapForDecode(DirectBuffer buffer, int offset, " +
+ "%13$s" +
+ indent + INDENT + "public %10$s WrapForDecode(DirectBuffer buffer, int offset, " +
"int actingBlockLength, int actingVersion)\n" +
indent + INDENT + "{\n" +
+ "%14$s" +
indent + INDENT + INDENT + "_buffer = buffer;\n" +
indent + INDENT + INDENT + "_offset = offset;\n" +
indent + INDENT + INDENT + "_actingBlockLength = actingBlockLength;\n" +
indent + INDENT + INDENT + "_actingVersion = actingVersion;\n" +
indent + INDENT + INDENT + "Limit = offset + _actingBlockLength;\n" +
+ indent + INDENT + INDENT + "return this;\n" +
+ indent + INDENT + "}\n\n" +
+ indent + INDENT + "public %10$s WrapForDecodeAndApplyHeader(DirectBuffer buffer, int offset, " +
+ "MessageHeader headerDecoder)\n" +
+ indent + INDENT + "{\n" +
+ indent + INDENT + INDENT + "headerDecoder.Wrap(buffer, offset, SchemaVersion);\n" +
+ indent + INDENT + INDENT + "\n" +
+ indent + INDENT + INDENT + "return WrapForDecode(buffer, offset + MessageHeader.Size, " +
+ " headerDecoder.BlockLength, headerDecoder.Version);\n" +
indent + INDENT + "}\n\n" +
indent + INDENT + "public int Size\n" +
indent + INDENT + "{\n" +
@@ -1241,10 +1465,507 @@ private CharSequence generateMessageFlyweightCode(final String className, final
generateLiteral(ir.headerStructure().schemaVersionType(), Integer.toString(ir.version())),
semanticType,
className,
- semanticVersion);
+ semanticVersion,
+ generateEncoderWrapListener(fieldPrecedenceModel, indent + TWO_INDENT),
+ generateDecoderWrapListener(fieldPrecedenceModel, indent + INDENT),
+ generateAccessOrderListenerCall(fieldPrecedenceModel, indent + TWO_INDENT,
+ "OnWrapForDecode", "actingVersion"));
+ }
+
+ private static CharSequence qualifiedStateCase(final FieldPrecedenceModel.State state)
+ {
+ return "CodecState." + state.name();
+ }
+
+ private static CharSequence stateCaseForSwitchCase(final FieldPrecedenceModel.State state)
+ {
+ return qualifiedStateCase(state);
+ }
+
+ private static CharSequence unqualifiedStateCase(final FieldPrecedenceModel.State state)
+ {
+ return state.name();
+ }
+
+ private static CharSequence generateFieldOrderStates(
+ final String indent,
+ final FieldPrecedenceModel fieldPrecedenceModel)
+ {
+ if (null == fieldPrecedenceModel)
+ {
+ return "";
+ }
+
+ final StringBuilder sb = new StringBuilder();
+
+ sb.append(indent).append("///\n");
+
+ sb.append(indent).append("/// \n");
+ sb.append(indent).append("/// \n");
+ sb.append(indent).append("/// The states in which a encoder/decoder/codec can live.\n");
+ sb.append(indent).append("/// \n");
+ sb.append(indent).append("/// \n");
+ sb.append(indent).append("/// The state machine diagram below, encoded in the dot language, describes\n");
+ sb.append(indent).append("/// the valid state transitions according to the order in which fields may be\n");
+ sb.append(indent).append("/// accessed safely. Tools such as PlantUML and Graphviz can render it.\n");
+ sb.append(indent).append("/// \n");
+ sb.append(indent).append("/// \n");
+ fieldPrecedenceModel.generateGraph(sb, indent + "/// ");
+ sb.append(indent).append("///
\n");
+ sb.append(indent).append("/// \n");
+ sb.append(indent).append("private enum CodecState\n")
+ .append(indent).append("{\n");
+ fieldPrecedenceModel.forEachStateOrderedByStateNumber(state ->
+ {
+ sb.append(indent).append(INDENT).append(unqualifiedStateCase(state))
+ .append(" = ").append(state.number())
+ .append(",\n");
+ });
+ sb.append(indent).append("}\n\n");
+
+ sb.append("\n").append(indent).append("private static readonly string[] StateNameLookup = new []\n")
+ .append(indent).append("{\n");
+ fieldPrecedenceModel.forEachStateOrderedByStateNumber(state ->
+ {
+ sb.append(indent).append(INDENT).append("\"").append(state.name()).append("\",\n");
+ });
+ sb.append(indent).append("};\n\n");
+
+ sb.append(indent).append("private static readonly string[] StateTransitionsLookup = new []\n")
+ .append(indent).append("{\n");
+ fieldPrecedenceModel.forEachStateOrderedByStateNumber(state ->
+ {
+ sb.append(indent).append(INDENT).append("\"");
+ final MutableBoolean isFirst = new MutableBoolean(true);
+ final Set transitionDescriptions = new HashSet<>();
+ fieldPrecedenceModel.forEachTransitionFrom(state, transitionGroup ->
+ {
+ if (transitionDescriptions.add(transitionGroup.exampleCode()))
+ {
+ if (isFirst.get())
+ {
+ isFirst.set(false);
+ }
+ else
+ {
+ sb.append(", ");
+ }
+
+ sb.append("\\\"").append(transitionGroup.exampleCode()).append("\\\"");
+ }
+ });
+ sb.append("\",\n");
+ });
+ sb.append(indent).append("};\n\n");
+
+ sb.append(indent).append("private static string codecStateName(CodecState state)\n")
+ .append(indent).append("{\n")
+ .append(indent).append(INDENT).append("return StateNameLookup[(int) state];\n")
+ .append(indent).append("}\n\n");
+
+ sb.append(indent).append("private static string codecStateTransitions(CodecState state)\n")
+ .append(indent).append("{\n")
+ .append(indent).append(INDENT).append("return StateTransitionsLookup[(int) state];\n")
+ .append(indent).append("}\n\n");
+
+ sb.append(indent).append("private CodecState _codecState = ")
+ .append(qualifiedStateCase(fieldPrecedenceModel.notWrappedState()))
+ .append(";\n\n");
+
+ sb.append(indent).append("private CodecState codecState()\n")
+ .append(indent).append("{\n")
+ .append(indent).append(INDENT).append("return _codecState;\n")
+ .append(indent).append("}\n\n");
+
+ sb.append(indent).append("private void codecState(CodecState newState)\n")
+ .append(indent).append("{\n")
+ .append(indent).append(INDENT).append("_codecState = newState;\n")
+ .append(indent).append("}\n");
+
+ return sb;
+ }
+
+ private CharSequence generateFullyEncodedCheck(
+ final String indent,
+ final FieldPrecedenceModel fieldPrecedenceModel)
+ {
+ if (null == fieldPrecedenceModel)
+ {
+ return "";
+ }
+
+ final StringBuilder sb = new StringBuilder();
+ sb.append("\n");
+
+ sb.append(indent).append("public void CheckEncodingIsComplete()\n")
+ .append(indent).append("{\n")
+ .append("#if ").append(precedenceChecksFlagName).append("\n")
+ .append(indent).append(INDENT).append("switch (_codecState)\n")
+ .append(indent).append(INDENT).append("{\n");
+
+ fieldPrecedenceModel.forEachTerminalEncoderState(state ->
+ {
+ sb.append(indent).append(TWO_INDENT).append("case ").append(stateCaseForSwitchCase(state)).append(":\n")
+ .append(indent).append(THREE_INDENT).append("return;\n");
+ });
+
+ sb.append(indent).append(TWO_INDENT).append("default:\n")
+ .append(indent).append(THREE_INDENT)
+ .append("throw new InvalidOperationException(\"Not fully encoded, current state: \" +\n")
+ .append(indent).append(THREE_INDENT)
+ .append(INDENT).append("codecStateName(_codecState) + \", allowed transitions: \" +\n")
+ .append(indent).append(THREE_INDENT)
+ .append(INDENT).append("codecStateTransitions(_codecState));\n")
+ .append(indent).append(INDENT).append("}\n")
+ .append("#endif\n")
+ .append(indent).append("}\n\n");
+
+ return sb;
+ }
+
+ private static String accessOrderListenerMethodName(final Token token)
+ {
+ return "On" + Generators.toUpperFirstChar(token.name()) + "Accessed";
}
- private CharSequence generateFields(final List tokens, final String indent)
+ private static String accessOrderListenerMethodName(final Token token, final String suffix)
+ {
+ return "On" + Generators.toUpperFirstChar(token.name()) + suffix + "Accessed";
+ }
+
+ private static void generateAccessOrderListenerMethod(
+ final StringBuilder sb,
+ final FieldPrecedenceModel fieldPrecedenceModel,
+ final String indent,
+ final Token token)
+ {
+ if (null == fieldPrecedenceModel)
+ {
+ return;
+ }
+
+ sb.append("\n")
+ .append(indent).append("private void ").append(accessOrderListenerMethodName(token)).append("()\n")
+ .append(indent).append("{\n");
+
+ final FieldPrecedenceModel.CodecInteraction fieldAccess =
+ fieldPrecedenceModel.interactionFactory().accessField(token);
+
+ generateAccessOrderListener(
+ sb,
+ indent + INDENT,
+ "access field",
+ fieldPrecedenceModel,
+ fieldAccess);
+
+ sb.append(indent).append("}\n");
+ }
+
+ private CharSequence generateAccessOrderListenerCall(
+ final FieldPrecedenceModel fieldPrecedenceModel,
+ final String indent,
+ final Token token,
+ final String... arguments)
+ {
+ return generateAccessOrderListenerCall(
+ fieldPrecedenceModel,
+ indent,
+ accessOrderListenerMethodName(token),
+ arguments);
+ }
+
+ private CharSequence generateAccessOrderListenerCall(
+ final FieldPrecedenceModel fieldPrecedenceModel,
+ final String indent,
+ final String methodName,
+ final String... arguments)
+ {
+ if (null == fieldPrecedenceModel)
+ {
+ return "";
+ }
+
+ final StringBuilder sb = new StringBuilder();
+ sb.append("#if ").append(precedenceChecksFlagName).append("\n")
+ .append(indent).append(methodName).append("(");
+
+ for (int i = 0; i < arguments.length; i++)
+ {
+ if (i > 0)
+ {
+ sb.append(", ");
+ }
+ sb.append(arguments[i]);
+ }
+ sb.append(");\n");
+
+ sb.append("#endif\n");
+
+ return sb;
+ }
+
+ private static void generateAccessOrderListenerMethodForGroupWrap(
+ final StringBuilder sb,
+ final FieldPrecedenceModel fieldPrecedenceModel,
+ final String indent,
+ final Token token)
+ {
+ if (null == fieldPrecedenceModel)
+ {
+ return;
+ }
+
+ sb.append("\n")
+ .append(indent).append("private void ").append(accessOrderListenerMethodName(token))
+ .append("(int remaining, string action)\n")
+ .append(indent).append("{\n")
+ .append(indent).append(INDENT).append("if (remaining == 0)\n")
+ .append(indent).append(INDENT).append("{\n");
+
+ final FieldPrecedenceModel.CodecInteraction selectEmptyGroup =
+ fieldPrecedenceModel.interactionFactory().determineGroupIsEmpty(token);
+
+ generateAccessOrderListener(
+ sb,
+ indent + TWO_INDENT,
+ "\" + action + \" count of repeating group",
+ fieldPrecedenceModel,
+ selectEmptyGroup);
+
+ sb.append(indent).append(INDENT).append("}\n")
+ .append(indent).append(INDENT).append("else\n")
+ .append(indent).append(INDENT).append("{\n");
+
+ final FieldPrecedenceModel.CodecInteraction selectNonEmptyGroup =
+ fieldPrecedenceModel.interactionFactory().determineGroupHasElements(token);
+
+ generateAccessOrderListener(
+ sb,
+ indent + TWO_INDENT,
+ "\" + action + \" count of repeating group",
+ fieldPrecedenceModel,
+ selectNonEmptyGroup);
+
+ sb.append(indent).append(INDENT).append("}\n")
+ .append(indent).append("}\n");
+ }
+
+ private static void generateAccessOrderListener(
+ final StringBuilder sb,
+ final String indent,
+ final String action,
+ final FieldPrecedenceModel fieldPrecedenceModel,
+ final FieldPrecedenceModel.CodecInteraction interaction)
+ {
+ if (interaction.isTopLevelBlockFieldAccess())
+ {
+ sb.append(indent).append("if (codecState() == ")
+ .append(qualifiedStateCase(fieldPrecedenceModel.notWrappedState()))
+ .append(")\n")
+ .append(indent).append("{\n");
+ generateAccessOrderException(sb, indent + INDENT, action, fieldPrecedenceModel, interaction);
+ sb.append(indent).append("}\n");
+ }
+ else
+ {
+ sb.append(indent).append("switch (codecState())\n")
+ .append(indent).append("{\n");
+
+ fieldPrecedenceModel.forEachTransition(interaction, transitionGroup ->
+ {
+ transitionGroup.forEachStartState(startState ->
+ {
+ sb.append(indent).append(INDENT)
+ .append("case ").append(stateCaseForSwitchCase(startState)).append(":\n");
+ });
+ sb.append(indent).append(TWO_INDENT).append("codecState(")
+ .append(qualifiedStateCase(transitionGroup.endState())).append(");\n")
+ .append(indent).append(TWO_INDENT).append("break;\n");
+ });
+
+ sb.append(indent).append(INDENT).append("default:\n");
+ generateAccessOrderException(sb, indent + TWO_INDENT, action, fieldPrecedenceModel, interaction);
+ sb.append(indent).append("}\n");
+ }
+ }
+
+ private static void generateAccessOrderException(
+ final StringBuilder sb,
+ final String indent,
+ final String action,
+ final FieldPrecedenceModel fieldPrecedenceModel,
+ final FieldPrecedenceModel.CodecInteraction interaction)
+ {
+ sb.append(indent).append("throw new InvalidOperationException(")
+ .append("\"Illegal field access order. \" +\n")
+ .append(indent).append(INDENT)
+ .append("\"Cannot ").append(action).append(" \\\"").append(interaction.groupQualifiedName())
+ .append("\\\" in state: \" + codecStateName(codecState()) +\n")
+ .append(indent).append(INDENT)
+ .append("\". Expected one of these transitions: [\" + codecStateTransitions(codecState()) +\n")
+ .append(indent).append(INDENT)
+ .append("\"]. Please see the diagram in the docs of the enum ")
+ .append(fieldPrecedenceModel.generatedRepresentationClassName()).append(".\");\n");
+ }
+
+ private static void generateAccessOrderListenerMethodForNextGroupElement(
+ final StringBuilder sb,
+ final FieldPrecedenceModel fieldPrecedenceModel,
+ final String indent,
+ final Token token)
+ {
+ if (null == fieldPrecedenceModel)
+ {
+ return;
+ }
+
+ sb.append("\n");
+
+ sb.append(indent).append("private void OnNextElementAccessed()\n")
+ .append(indent).append("{\n")
+ .append(indent).append(INDENT).append("int remaining = ").append("_count - _index").append(";\n")
+ .append(indent).append(INDENT).append("if (remaining > 1)\n")
+ .append(indent).append(INDENT).append("{\n");
+
+ final FieldPrecedenceModel.CodecInteraction selectNextElementInGroup =
+ fieldPrecedenceModel.interactionFactory().moveToNextElement(token);
+
+ generateAccessOrderListener(
+ sb,
+ indent + TWO_INDENT,
+ "access next element in repeating group",
+ fieldPrecedenceModel,
+ selectNextElementInGroup);
+
+ sb.append(indent).append(INDENT).append("}\n")
+ .append(indent).append(INDENT).append("else if (remaining == 1)\n")
+ .append(indent).append(INDENT).append("{\n");
+
+ final FieldPrecedenceModel.CodecInteraction selectLastElementInGroup =
+ fieldPrecedenceModel.interactionFactory().moveToLastElement(token);
+
+ generateAccessOrderListener(
+ sb,
+ indent + TWO_INDENT,
+ "access next element in repeating group",
+ fieldPrecedenceModel,
+ selectLastElementInGroup);
+
+ sb.append(indent).append(INDENT).append("}\n")
+ .append(indent).append("}\n");
+ }
+
+ private static void generateAccessOrderListenerMethodForResetGroupCount(
+ final StringBuilder sb,
+ final FieldPrecedenceModel fieldPrecedenceModel,
+ final String indent,
+ final Token token)
+ {
+ if (null == fieldPrecedenceModel)
+ {
+ return;
+ }
+
+ sb.append(indent).append("private void OnResetCountToIndex()\n")
+ .append(indent).append("{\n");
+
+ final FieldPrecedenceModel.CodecInteraction resetCountToIndex =
+ fieldPrecedenceModel.interactionFactory().resetCountToIndex(token);
+
+ generateAccessOrderListener(
+ sb,
+ indent + " ",
+ "reset count of repeating group",
+ fieldPrecedenceModel,
+ resetCountToIndex);
+
+ sb.append(indent).append("}\n");
+ }
+
+ private static void generateAccessOrderListenerMethodForVarDataLength(
+ final StringBuilder sb,
+ final FieldPrecedenceModel fieldPrecedenceModel,
+ final String indent,
+ final Token token)
+ {
+ if (null == fieldPrecedenceModel)
+ {
+ return;
+ }
+
+ sb.append("\n")
+ .append(indent).append("private void ")
+ .append(accessOrderListenerMethodName(token, "Length")).append("()\n")
+ .append(indent).append("{\n");
+
+ final FieldPrecedenceModel.CodecInteraction accessLength =
+ fieldPrecedenceModel.interactionFactory().accessVarDataLength(token);
+
+ generateAccessOrderListener(
+ sb,
+ indent + INDENT,
+ "decode length of var data",
+ fieldPrecedenceModel,
+ accessLength);
+
+ sb.append(indent).append("}\n");
+ }
+
+ private static CharSequence generateDecoderWrapListener(
+ final FieldPrecedenceModel fieldPrecedenceModel,
+ final String indent)
+ {
+ if (null == fieldPrecedenceModel)
+ {
+ return "";
+ }
+
+ final StringBuilder sb = new StringBuilder();
+ sb.append(indent).append("private void OnWrapForDecode(int actingVersion)\n")
+ .append(indent).append("{\n")
+ .append(indent).append(INDENT).append("switch(actingVersion)\n")
+ .append(indent).append(INDENT).append("{\n");
+
+ fieldPrecedenceModel.forEachWrappedStateByVersion((version, state) ->
+ {
+ sb.append(indent).append(TWO_INDENT).append("case ").append(version).append(":\n")
+ .append(indent).append(THREE_INDENT).append("codecState(")
+ .append(qualifiedStateCase(state)).append(");\n")
+ .append(indent).append(THREE_INDENT).append("break;\n");
+ });
+
+ sb.append(indent).append(TWO_INDENT).append("default:\n")
+ .append(indent).append(THREE_INDENT).append("codecState(")
+ .append(qualifiedStateCase(fieldPrecedenceModel.latestVersionWrappedState())).append(");\n")
+ .append(indent).append(THREE_INDENT).append("break;\n")
+ .append(indent).append(INDENT).append("}\n")
+ .append(indent).append("}\n\n");
+
+ return sb;
+ }
+
+ private CharSequence generateEncoderWrapListener(
+ final FieldPrecedenceModel fieldPrecedenceModel,
+ final String indent)
+ {
+ if (null == fieldPrecedenceModel)
+ {
+ return "";
+ }
+
+ final StringBuilder sb = new StringBuilder();
+ sb.append("#if ").append(precedenceChecksFlagName).append("\n")
+ .append(indent).append("codecState(")
+ .append(qualifiedStateCase(fieldPrecedenceModel.latestVersionWrappedState()))
+ .append(");\n")
+ .append("#endif\n");
+ return sb;
+ }
+
+ private CharSequence generateFields(
+ final FieldPrecedenceModel fieldPrecedenceModel,
+ final List tokens,
+ final String indent)
{
final StringBuilder sb = new StringBuilder();
@@ -1258,26 +1979,32 @@ private CharSequence generateFields(final List tokens, final String inden
generateFieldIdMethod(sb, signalToken, indent + INDENT);
generateSinceActingDeprecated(
- sb, indent, CSharpUtil.formatPropertyName(signalToken.name()), signalToken);
+ sb, indent + INDENT, CSharpUtil.formatPropertyName(signalToken.name()), signalToken);
generateOffsetMethod(sb, signalToken, indent + INDENT);
generateFieldMetaAttributeMethod(sb, signalToken, indent + INDENT);
+ generateAccessOrderListenerMethod(sb, fieldPrecedenceModel, indent + INDENT, signalToken);
+
switch (encodingToken.signal())
{
case ENCODING:
- sb.append(generatePrimitiveProperty(propertyName, signalToken, encodingToken, indent));
+ sb.append(generatePrimitiveProperty(propertyName, signalToken, encodingToken,
+ fieldPrecedenceModel, indent));
break;
case BEGIN_ENUM:
- sb.append(generateEnumProperty(propertyName, signalToken, encodingToken, indent));
+ sb.append(generateEnumProperty(propertyName, signalToken, encodingToken,
+ fieldPrecedenceModel, indent));
break;
case BEGIN_SET:
- sb.append(generateBitSetProperty(propertyName, signalToken, encodingToken, indent));
+ sb.append(generateBitSetProperty(propertyName, signalToken, encodingToken,
+ fieldPrecedenceModel, indent));
break;
case BEGIN_COMPOSITE:
- sb.append(generateCompositeProperty(propertyName, signalToken, encodingToken, indent));
+ sb.append(generateCompositeProperty(propertyName, signalToken, encodingToken,
+ fieldPrecedenceModel, indent));
break;
default:
@@ -1352,6 +2079,7 @@ private CharSequence generateEnumProperty(
final String propertyName,
final Token fieldToken,
final Token typeToken,
+ final FieldPrecedenceModel fieldPrecedenceModel,
final String indent)
{
final String enumName = formatClassName(typeToken.applicableTypeName());
@@ -1381,6 +2109,9 @@ private CharSequence generateEnumProperty(
}
else
{
+ final CharSequence accessOrderListenerCall = generateAccessOrderListenerCall(
+ fieldPrecedenceModel, indent + TWO_INDENT, fieldToken);
+
return String.format("\n" +
"%1$s" +
indent + INDENT + "public %2$s %3$s\n" +
@@ -1388,10 +2119,12 @@ private CharSequence generateEnumProperty(
indent + INDENT + INDENT + "get\n" +
indent + INDENT + INDENT + "{\n" +
"%4$s" +
+ "%10$s" +
indent + INDENT + INDENT + INDENT + "return (%5$s)_buffer.%6$sGet%8$s(_offset + %7$d);\n" +
indent + INDENT + INDENT + "}\n" +
indent + INDENT + INDENT + "set\n" +
indent + INDENT + INDENT + "{\n" +
+ "%10$s" +
indent + INDENT + INDENT + INDENT + "_buffer.%6$sPut%8$s(_offset + %7$d, (%9$s)value);\n" +
indent + INDENT + INDENT + "}\n" +
indent + INDENT + "}\n\n",
@@ -1403,12 +2136,17 @@ private CharSequence generateEnumProperty(
typePrefix,
offset,
byteOrderStr,
- enumUnderlyingType);
+ enumUnderlyingType,
+ accessOrderListenerCall);
}
}
private String generateBitSetProperty(
- final String propertyName, final Token fieldToken, final Token typeToken, final String indent)
+ final String propertyName,
+ final Token fieldToken,
+ final Token typeToken,
+ final FieldPrecedenceModel fieldPrecedenceModel,
+ final String indent)
{
final String bitSetName = formatClassName(typeToken.applicableTypeName());
final int offset = typeToken.offset();
@@ -1416,6 +2154,8 @@ private String generateBitSetProperty(
final ByteOrder byteOrder = typeToken.encoding().byteOrder();
final String byteOrderStr = generateByteOrder(byteOrder, typeToken.encoding().primitiveType().size());
final String typeName = cSharpTypeName(typeToken.encoding().primitiveType());
+ final CharSequence accessOrderListenerCall = generateAccessOrderListenerCall(
+ fieldPrecedenceModel, indent + TWO_INDENT, fieldToken);
return String.format("\n" +
"%1$s" +
@@ -1424,10 +2164,12 @@ private String generateBitSetProperty(
indent + INDENT + INDENT + "get\n" +
indent + INDENT + INDENT + "{\n" +
"%4$s" +
+ "%10$s" +
indent + INDENT + INDENT + INDENT + "return (%5$s)_buffer.%6$sGet%8$s(_offset + %7$d);\n" +
indent + INDENT + INDENT + "}\n" +
indent + INDENT + INDENT + "set\n" +
indent + INDENT + INDENT + "{\n" +
+ "%10$s" +
indent + INDENT + INDENT + INDENT + "_buffer.%6$sPut%8$s(_offset + %7$d, (%9$s)value);\n" +
indent + INDENT + INDENT + "}\n" +
indent + INDENT + "}\n",
@@ -1439,14 +2181,21 @@ private String generateBitSetProperty(
typePrefix,
offset,
byteOrderStr,
- typeName);
+ typeName,
+ accessOrderListenerCall);
}
private Object generateCompositeProperty(
- final String propertyName, final Token fieldToken, final Token typeToken, final String indent)
+ final String propertyName,
+ final Token fieldToken,
+ final Token typeToken,
+ final FieldPrecedenceModel fieldPrecedenceModel,
+ final String indent)
{
final String compositeName = CSharpUtil.formatClassName(typeToken.applicableTypeName());
final int offset = typeToken.offset();
+ final CharSequence accessOrderListenerCall = generateAccessOrderListenerCall(
+ fieldPrecedenceModel, indent + THREE_INDENT, fieldToken);
final StringBuilder sb = new StringBuilder();
sb.append(String.format("\n" +
@@ -1462,6 +2211,7 @@ private Object generateCompositeProperty(
indent + INDENT + INDENT + "get\n" +
indent + INDENT + INDENT + "{\n" +
"%4$s" +
+ "%7$s" +
indent + INDENT + INDENT + INDENT + "_%5$s.Wrap(_buffer, _offset + %6$d, _actingVersion);\n" +
indent + INDENT + INDENT + INDENT + "return _%5$s;\n" +
indent + INDENT + INDENT + "}\n" +
@@ -1471,7 +2221,8 @@ private Object generateCompositeProperty(
toUpperFirstChar(propertyName),
generateTypeFieldNotPresentCondition(fieldToken.version(), indent),
toLowerFirstChar(propertyName),
- offset));
+ offset,
+ accessOrderListenerCall));
return sb;
}
@@ -1715,11 +2466,13 @@ private int writeTokenDisplay(
{
if (typeToken.encoding().primitiveType() == PrimitiveType.CHAR)
{
+ append(sb, indent, "builder.Append(\"'\");");
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, "}");
+ append(sb, indent, "builder.Append(\"'\");");
}
else
{
@@ -1804,7 +2557,8 @@ private CharSequence generateDisplay(
final String name,
final List tokens,
final List groups,
- final List varData)
+ final List varData,
+ final FieldPrecedenceModel fieldPrecedenceModel)
{
final StringBuilder sb = new StringBuilder(100);
@@ -1818,6 +2572,15 @@ private CharSequence generateDisplay(
append(sb, TWO_INDENT, " }");
sb.append('\n');
append(sb, TWO_INDENT, " int originalLimit = this.Limit;");
+ if (null != fieldPrecedenceModel)
+ {
+ sb.append("#if ").append(precedenceChecksFlagName).append("\n");
+ append(sb, TWO_INDENT, " CodecState originalState = _codecState;");
+ sb.append(THREE_INDENT).append("_codecState = ")
+ .append(qualifiedStateCase(fieldPrecedenceModel.notWrappedState())).append(";\n");
+ append(sb, TWO_INDENT, " OnWrapForDecode(_actingVersion);");
+ sb.append("#endif\n");
+ }
append(sb, TWO_INDENT, " this.Limit = _offset + _actingBlockLength;");
append(sb, TWO_INDENT, " builder.Append(\"[" + name + "](sbeTemplateId=\");");
append(sb, TWO_INDENT, " builder.Append(" + name + ".TemplateId);");
@@ -1841,6 +2604,12 @@ private CharSequence generateDisplay(
sb.append('\n');
appendDisplay(sb, tokens, groups, varData, THREE_INDENT);
sb.append('\n');
+ if (null != fieldPrecedenceModel)
+ {
+ sb.append("#if ").append(precedenceChecksFlagName).append("\n");
+ append(sb, TWO_INDENT, " _codecState = originalState;");
+ sb.append("#endif\n");
+ }
append(sb, TWO_INDENT, " this.Limit = originalLimit;");
sb.append('\n');
append(sb, TWO_INDENT, "}");
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 e40a02c6a1..7edf2e68c3 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
@@ -15,25 +15,23 @@
*/
package uk.co.real_logic.sbe.generation.java;
+import uk.co.real_logic.sbe.PrimitiveType;
+import uk.co.real_logic.sbe.generation.CodeGenerator;
+import uk.co.real_logic.sbe.generation.Generators;
+import uk.co.real_logic.sbe.generation.common.FieldPrecedenceModel;
+import uk.co.real_logic.sbe.generation.common.PrecedenceChecks;
+import uk.co.real_logic.sbe.ir.*;
import org.agrona.DirectBuffer;
import org.agrona.MutableDirectBuffer;
import org.agrona.Strings;
import org.agrona.Verify;
+import org.agrona.collections.MutableBoolean;
import org.agrona.generation.DynamicPackageOutputManager;
import org.agrona.sbe.*;
-import uk.co.real_logic.sbe.PrimitiveType;
-import uk.co.real_logic.sbe.generation.CodeGenerator;
-import uk.co.real_logic.sbe.generation.Generators;
-import uk.co.real_logic.sbe.ir.*;
import java.io.IOException;
import java.io.Writer;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Formatter;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
+import java.util.*;
import java.util.function.Function;
import static uk.co.real_logic.sbe.SbeTool.JAVA_INTERFACE_PACKAGE;
@@ -73,6 +71,9 @@ enum CodecType
private final boolean shouldGenerateInterfaces;
private final boolean shouldDecodeUnknownEnumValues;
private final boolean shouldSupportTypesPackageNames;
+ private final PrecedenceChecks precedenceChecks;
+ private final String precedenceChecksFlagName;
+ private final String precedenceChecksPropName;
private final Set packageNameByTypes = new HashSet<>();
/**
@@ -120,6 +121,43 @@ public JavaGenerator(
final boolean shouldDecodeUnknownEnumValues,
final boolean shouldSupportTypesPackageNames,
final DynamicPackageOutputManager outputManager)
+ {
+ this(
+ ir,
+ mutableBuffer,
+ readOnlyBuffer,
+ shouldGenerateGroupOrderAnnotation,
+ shouldGenerateInterfaces,
+ shouldDecodeUnknownEnumValues,
+ shouldSupportTypesPackageNames,
+ PrecedenceChecks.newInstance(new PrecedenceChecks.Context()),
+ outputManager
+ );
+ }
+
+ /**
+ * Create a new Java language {@link CodeGenerator}.
+ *
+ * @param ir for the messages and types.
+ * @param mutableBuffer implementation used for mutating underlying buffers.
+ * @param readOnlyBuffer implementation used for reading underlying buffers.
+ * @param shouldGenerateGroupOrderAnnotation in the codecs.
+ * @param shouldGenerateInterfaces for common methods.
+ * @param shouldDecodeUnknownEnumValues generate support for unknown enum values when decoding.
+ * @param shouldSupportTypesPackageNames generator support for types in their own package.
+ * @param precedenceChecks whether and how to generate field precedence checks.
+ * @param outputManager for generating the codecs to.
+ */
+ public JavaGenerator(
+ final Ir ir,
+ final String mutableBuffer,
+ final String readOnlyBuffer,
+ final boolean shouldGenerateGroupOrderAnnotation,
+ final boolean shouldGenerateInterfaces,
+ final boolean shouldDecodeUnknownEnumValues,
+ final boolean shouldSupportTypesPackageNames,
+ final PrecedenceChecks precedenceChecks,
+ final DynamicPackageOutputManager outputManager)
{
Verify.notNull(ir, "ir");
Verify.notNull(outputManager, "outputManager");
@@ -137,6 +175,10 @@ public JavaGenerator(
this.shouldGenerateGroupOrderAnnotation = shouldGenerateGroupOrderAnnotation;
this.shouldGenerateInterfaces = shouldGenerateInterfaces;
this.shouldDecodeUnknownEnumValues = shouldDecodeUnknownEnumValues;
+
+ this.precedenceChecks = precedenceChecks;
+ this.precedenceChecksFlagName = precedenceChecks.context().precedenceChecksFlagName();
+ this.precedenceChecksPropName = precedenceChecks.context().precedenceChecksPropName();
}
/**
@@ -230,20 +272,30 @@ public void generate() throws IOException
final List varData = new ArrayList<>();
collectVarData(messageBody, i, varData);
- generateDecoder(msgToken, fields, groups, varData, hasVarData);
- generateEncoder(msgToken, fields, groups, varData, hasVarData);
+ final String decoderClassName = formatClassName(decoderName(msgToken.name()));
+ final String decoderStateClassName = decoderClassName + "#CodecStates";
+ final FieldPrecedenceModel decoderPrecedenceModel =
+ precedenceChecks.createDecoderModel(decoderStateClassName, tokens);
+ generateDecoder(decoderClassName, msgToken, fields, groups, varData, hasVarData, decoderPrecedenceModel);
+
+ final String encoderClassName = formatClassName(encoderName(msgToken.name()));
+ final String encoderStateClassName = encoderClassName + "#CodecStates";
+ final FieldPrecedenceModel encoderPrecedenceModel =
+ precedenceChecks.createEncoderModel(encoderStateClassName, tokens);
+ generateEncoder(encoderClassName, msgToken, fields, groups, varData, hasVarData, encoderPrecedenceModel);
}
}
private void generateEncoder(
+ final String className,
final Token msgToken,
final List fields,
final List groups,
final List varData,
- final boolean hasVarData)
+ final boolean hasVarData,
+ final FieldPrecedenceModel fieldPrecedenceModel)
throws IOException
{
- final String className = formatClassName(encoderName(msgToken.name()));
final String implementsString = implementsInterface(MessageEncoderFlyweight.class.getSimpleName());
try (Writer out = outputManager.createOutput(className))
@@ -255,29 +307,519 @@ private void generateEncoder(
generateAnnotations(BASE_INDENT, className, groups, out, this::encoderName);
}
out.append(generateDeclaration(className, implementsString, msgToken));
- out.append(generateEncoderFlyweightCode(className, msgToken));
+
+ out.append(generateFieldOrderStates(fieldPrecedenceModel));
+ out.append(generateEncoderFlyweightCode(className, fieldPrecedenceModel, msgToken));
final StringBuilder sb = new StringBuilder();
- generateEncoderFields(sb, className, fields, BASE_INDENT);
- generateEncoderGroups(sb, className, groups, BASE_INDENT, false);
- generateEncoderVarData(sb, className, varData, BASE_INDENT);
+ generateEncoderFields(sb, className, fieldPrecedenceModel, fields, BASE_INDENT);
+ generateEncoderGroups(sb, className, fieldPrecedenceModel, groups, BASE_INDENT, false);
+ generateEncoderVarData(sb, className, fieldPrecedenceModel, varData, BASE_INDENT);
generateEncoderDisplay(sb, decoderName(msgToken.name()));
+ generateFullyEncodedCheck(sb, fieldPrecedenceModel);
out.append(sb);
out.append("}\n");
}
}
+ private static CharSequence qualifiedStateCase(final FieldPrecedenceModel.State state)
+ {
+ return "CodecStates." + state.name();
+ }
+
+ private static CharSequence stateCaseForSwitchCase(final FieldPrecedenceModel.State state)
+ {
+ return qualifiedStateCase(state);
+ }
+
+ private static CharSequence unqualifiedStateCase(final FieldPrecedenceModel.State state)
+ {
+ return state.name();
+ }
+
+ private CharSequence generateFieldOrderStates(final FieldPrecedenceModel fieldPrecedenceModel)
+ {
+ if (null == fieldPrecedenceModel)
+ {
+ return "";
+ }
+
+ final StringBuilder sb = new StringBuilder();
+
+ sb.append(" private static final boolean ENABLE_BOUNDS_CHECKS = ")
+ .append("!Boolean.getBoolean(\"agrona.disable.bounds.checks\");\n\n");
+ sb.append(" private static final boolean ")
+ .append(precedenceChecksFlagName).append(" = ")
+ .append("Boolean.parseBoolean(System.getProperty(\n")
+ .append(" \"").append(precedenceChecksPropName).append("\",\n")
+ .append(" Boolean.toString(ENABLE_BOUNDS_CHECKS)));\n\n");
+
+ sb.append(" /**\n");
+ sb.append(" * The states in which a encoder/decoder/codec can live.\n");
+ sb.append(" *\n");
+ sb.append(" * The state machine diagram below, encoded in the dot language, describes\n");
+ sb.append(" * the valid state transitions according to the order in which fields may be\n");
+ sb.append(" * accessed safely. Tools such as PlantUML and Graphviz can render it.\n");
+ sb.append(" *\n");
+ sb.append(" *
{@code\n");
+ fieldPrecedenceModel.generateGraph(sb, " * ");
+ sb.append(" * }
\n");
+ sb.append(" */\n");
+ sb.append(" private static class CodecStates\n")
+ .append(" {\n");
+ fieldPrecedenceModel.forEachStateOrderedByStateNumber(state ->
+ {
+ sb.append(" private static final int ")
+ .append(unqualifiedStateCase(state))
+ .append(" = ").append(state.number())
+ .append(";\n");
+ });
+
+ sb.append("\n").append(" private static final String[] STATE_NAME_LOOKUP =\n")
+ .append(" {\n");
+ fieldPrecedenceModel.forEachStateOrderedByStateNumber(state ->
+ {
+ sb.append(" \"").append(state.name()).append("\",\n");
+ });
+ sb.append(" };\n\n");
+
+ sb.append(" private static final String[] STATE_TRANSITIONS_LOOKUP =\n")
+ .append(" {\n");
+ fieldPrecedenceModel.forEachStateOrderedByStateNumber(state ->
+ {
+ sb.append(" \"");
+ final MutableBoolean isFirst = new MutableBoolean(true);
+ final Set transitionDescriptions = new HashSet<>();
+ fieldPrecedenceModel.forEachTransitionFrom(state, transitionGroup ->
+ {
+ if (transitionDescriptions.add(transitionGroup.exampleCode()))
+ {
+ if (isFirst.get())
+ {
+ isFirst.set(false);
+ }
+ else
+ {
+ sb.append(", ");
+ }
+
+ sb.append("\\\"").append(transitionGroup.exampleCode()).append("\\\"");
+ }
+ });
+ sb.append("\",\n");
+ });
+ sb.append(" };\n\n");
+
+ sb.append(" private static String name(final int state)\n")
+ .append(" {\n")
+ .append(" return STATE_NAME_LOOKUP[state];\n")
+ .append(" }\n\n");
+
+ sb.append(" private static String transitions(final int state)\n")
+ .append(" {\n")
+ .append(" return STATE_TRANSITIONS_LOOKUP[state];\n")
+ .append(" }\n");
+
+ sb.append(" }\n\n");
+
+ sb.append(" private int codecState = ")
+ .append(qualifiedStateCase(fieldPrecedenceModel.notWrappedState()))
+ .append(";\n\n");
+
+ sb.append(" private int codecState()\n")
+ .append(" {\n")
+ .append(" return codecState;\n")
+ .append(" }\n\n");
+
+ sb.append(" private void codecState(int newState)\n")
+ .append(" {\n")
+ .append(" codecState = newState;\n")
+ .append(" }\n\n");
+
+ return sb;
+ }
+
+ private void generateFullyEncodedCheck(
+ final StringBuilder sb,
+ final FieldPrecedenceModel fieldPrecedenceModel)
+ {
+ if (null == fieldPrecedenceModel)
+ {
+ return;
+ }
+
+ sb.append("\n");
+
+ sb.append(" public void checkEncodingIsComplete()\n")
+ .append(" {\n")
+ .append(" if (").append(precedenceChecksFlagName).append(")\n")
+ .append(" {\n")
+ .append(" switch (codecState)\n")
+ .append(" {\n");
+
+ fieldPrecedenceModel.forEachTerminalEncoderState(state ->
+ {
+ sb.append(" case ").append(stateCaseForSwitchCase(state)).append(":\n")
+ .append(" return;\n");
+ });
+
+ sb.append(" default:\n")
+ .append(" throw new IllegalStateException(\"Not fully encoded, current state: \" +\n")
+ .append(" CodecStates.name(codecState) + \", allowed transitions: \" +\n")
+ .append(" CodecStates.transitions(codecState));\n")
+ .append(" }\n")
+ .append(" }\n")
+ .append(" }\n\n");
+ }
+
+ private static String accessOrderListenerMethodName(final Token token)
+ {
+ return "on" + Generators.toUpperFirstChar(token.name()) + "Accessed";
+ }
+
+ private static String accessOrderListenerMethodName(final Token token, final String suffix)
+ {
+ return "on" + Generators.toUpperFirstChar(token.name()) + suffix + "Accessed";
+ }
+
+ private static void generateAccessOrderListenerMethod(
+ final StringBuilder sb,
+ final FieldPrecedenceModel fieldPrecedenceModel,
+ final String indent,
+ final Token token)
+ {
+ if (null == fieldPrecedenceModel)
+ {
+ return;
+ }
+
+ sb.append("\n")
+ .append(indent).append("private void ").append(accessOrderListenerMethodName(token)).append("()\n")
+ .append(indent).append("{\n");
+
+ final FieldPrecedenceModel.CodecInteraction fieldAccess =
+ fieldPrecedenceModel.interactionFactory().accessField(token);
+
+ generateAccessOrderListener(
+ sb,
+ indent + " ",
+ "access field",
+ fieldPrecedenceModel,
+ fieldAccess);
+
+ sb.append(indent).append("}\n");
+ }
+
+ private CharSequence generateAccessOrderListenerCall(
+ final FieldPrecedenceModel fieldPrecedenceModel,
+ final String indent,
+ final Token token,
+ final String... arguments)
+ {
+ return generateAccessOrderListenerCall(
+ fieldPrecedenceModel,
+ indent,
+ accessOrderListenerMethodName(token),
+ arguments);
+ }
+
+ private CharSequence generateAccessOrderListenerCall(
+ final FieldPrecedenceModel fieldPrecedenceModel,
+ final String indent,
+ final String methodName,
+ final String... arguments)
+ {
+ if (null == fieldPrecedenceModel)
+ {
+ return "";
+ }
+
+ final StringBuilder sb = new StringBuilder();
+ sb.append(indent).append("if (").append(precedenceChecksFlagName).append(")\n")
+ .append(indent).append("{\n")
+ .append(indent).append(" ").append(methodName).append("(");
+
+ for (int i = 0; i < arguments.length; i++)
+ {
+ if (i > 0)
+ {
+ sb.append(", ");
+ }
+ sb.append(arguments[i]);
+ }
+ sb.append(");\n");
+
+ sb.append(indent).append("}\n\n");
+
+ return sb;
+ }
+
+ private static void generateAccessOrderListenerMethodForGroupWrap(
+ final StringBuilder sb,
+ final String action,
+ final FieldPrecedenceModel fieldPrecedenceModel,
+ final String indent,
+ final Token token)
+ {
+ if (null == fieldPrecedenceModel)
+ {
+ return;
+ }
+
+ sb.append("\n")
+ .append(indent).append("private void ").append(accessOrderListenerMethodName(token))
+ .append("(final int count)\n")
+ .append(indent).append("{\n")
+ .append(indent).append(" if (count == 0)\n")
+ .append(indent).append(" {\n");
+
+ final FieldPrecedenceModel.CodecInteraction selectEmptyGroup =
+ fieldPrecedenceModel.interactionFactory().determineGroupIsEmpty(token);
+
+ generateAccessOrderListener(
+ sb,
+ indent + " ",
+ action + " count of repeating group",
+ fieldPrecedenceModel,
+ selectEmptyGroup);
+
+ sb.append(indent).append(" }\n")
+ .append(indent).append(" else\n")
+ .append(indent).append(" {\n");
+
+ final FieldPrecedenceModel.CodecInteraction selectNonEmptyGroup =
+ fieldPrecedenceModel.interactionFactory().determineGroupHasElements(token);
+
+ generateAccessOrderListener(
+ sb,
+ indent + " ",
+ action + " count of repeating group",
+ fieldPrecedenceModel,
+ selectNonEmptyGroup);
+
+ sb.append(indent).append(" }\n")
+ .append(indent).append("}\n");
+ }
+
+ private static void generateAccessOrderListener(
+ final StringBuilder sb,
+ final String indent,
+ final String action,
+ final FieldPrecedenceModel fieldPrecedenceModel,
+ final FieldPrecedenceModel.CodecInteraction interaction)
+ {
+ if (interaction.isTopLevelBlockFieldAccess())
+ {
+ sb.append(indent).append("if (codecState() == ")
+ .append(qualifiedStateCase(fieldPrecedenceModel.notWrappedState()))
+ .append(")\n")
+ .append(indent).append("{\n");
+ generateAccessOrderException(sb, indent + " ", action, fieldPrecedenceModel, interaction);
+ sb.append(indent).append("}\n");
+ }
+ else
+ {
+ sb.append(indent).append("switch (codecState())\n")
+ .append(indent).append("{\n");
+
+ fieldPrecedenceModel.forEachTransition(interaction, transitionGroup ->
+ {
+ transitionGroup.forEachStartState(startState ->
+ sb.append(indent).append(" case ").append(stateCaseForSwitchCase(startState)).append(":\n"));
+ sb.append(indent).append(" codecState(")
+ .append(qualifiedStateCase(transitionGroup.endState())).append(");\n")
+ .append(indent).append(" break;\n");
+ });
+
+ sb.append(indent).append(" default:\n");
+ generateAccessOrderException(sb, indent + " ", action, fieldPrecedenceModel, interaction);
+ sb.append(indent).append("}\n");
+ }
+ }
+
+ private static void generateAccessOrderException(
+ final StringBuilder sb,
+ final String indent,
+ final String action,
+ final FieldPrecedenceModel fieldPrecedenceModel,
+ final FieldPrecedenceModel.CodecInteraction interaction)
+ {
+ sb.append(indent).append("throw new IllegalStateException(")
+ .append("\"Illegal field access order. \" +\n")
+ .append(indent)
+ .append(" \"Cannot ").append(action).append(" \\\"").append(interaction.groupQualifiedName())
+ .append("\\\" in state: \" + CodecStates.name(codecState()) +\n")
+ .append(indent)
+ .append(" \". Expected one of these transitions: [\" + CodecStates.transitions(codecState()) +\n")
+ .append(indent)
+ .append(" \"]. Please see the diagram in the Javadoc of the class ")
+ .append(fieldPrecedenceModel.generatedRepresentationClassName()).append(".\");\n");
+ }
+
+ private static void generateAccessOrderListenerMethodForNextGroupElement(
+ final StringBuilder sb,
+ final FieldPrecedenceModel fieldPrecedenceModel,
+ final String indent,
+ final Token token)
+ {
+ if (null == fieldPrecedenceModel)
+ {
+ return;
+ }
+
+ sb.append(indent).append("private void onNextElementAccessed()\n")
+ .append(indent).append("{\n")
+ .append(indent).append(" final int remaining = ").append("count - index").append(";\n")
+ .append(indent).append(" if (remaining > 1)\n")
+ .append(indent).append(" {\n");
+
+ final FieldPrecedenceModel.CodecInteraction selectNextElementInGroup =
+ fieldPrecedenceModel.interactionFactory().moveToNextElement(token);
+
+ generateAccessOrderListener(
+ sb,
+ indent + " ",
+ "access next element in repeating group",
+ fieldPrecedenceModel,
+ selectNextElementInGroup);
+
+ sb.append(indent).append(" }\n")
+ .append(indent).append(" else if (remaining == 1)\n")
+ .append(indent).append(" {\n");
+
+ final FieldPrecedenceModel.CodecInteraction selectLastElementInGroup =
+ fieldPrecedenceModel.interactionFactory().moveToLastElement(token);
+
+ generateAccessOrderListener(
+ sb,
+ indent + " ",
+ "access next element in repeating group",
+ fieldPrecedenceModel,
+ selectLastElementInGroup);
+
+ sb.append(indent).append(" }\n")
+ .append(indent).append("}\n\n");
+ }
+
+ private static void generateAccessOrderListenerMethodForResetGroupCount(
+ final StringBuilder sb,
+ final FieldPrecedenceModel fieldPrecedenceModel,
+ final String indent,
+ final Token token)
+ {
+ if (null == fieldPrecedenceModel)
+ {
+ return;
+ }
+
+ sb.append(indent).append("private void onResetCountToIndex()\n")
+ .append(indent).append("{\n");
+
+ final FieldPrecedenceModel.CodecInteraction resetCountToIndex =
+ fieldPrecedenceModel.interactionFactory().resetCountToIndex(token);
+
+ generateAccessOrderListener(
+ sb,
+ indent + " ",
+ "reset count of repeating group",
+ fieldPrecedenceModel,
+ resetCountToIndex);
+
+ sb.append(indent).append("}\n\n");
+ }
+
+ private static void generateAccessOrderListenerMethodForVarDataLength(
+ final StringBuilder sb,
+ final FieldPrecedenceModel fieldPrecedenceModel,
+ final String indent,
+ final Token token)
+ {
+ if (null == fieldPrecedenceModel)
+ {
+ return;
+ }
+
+ sb.append("\n")
+ .append(indent).append("void ").append(accessOrderListenerMethodName(token, "Length")).append("()\n")
+ .append(indent).append("{\n");
+
+ final FieldPrecedenceModel.CodecInteraction accessLength =
+ fieldPrecedenceModel.interactionFactory().accessVarDataLength(token);
+
+ generateAccessOrderListener(
+ sb,
+ indent + INDENT,
+ "decode length of var data",
+ fieldPrecedenceModel,
+ accessLength);
+
+ sb.append(indent).append("}\n");
+ }
+
+ private static CharSequence generateDecoderWrapListener(
+ final FieldPrecedenceModel fieldPrecedenceModel,
+ final String indent)
+ {
+ if (null == fieldPrecedenceModel)
+ {
+ return "";
+ }
+
+ final StringBuilder sb = new StringBuilder();
+ sb.append(indent).append("private void onWrap(final int actingVersion)\n")
+ .append(indent).append("{\n")
+ .append(indent).append(" switch(actingVersion)\n")
+ .append(indent).append(" {\n");
+
+ fieldPrecedenceModel.forEachWrappedStateByVersion((version, state) ->
+ {
+ sb.append(indent).append(" case ").append(version).append(":\n")
+ .append(indent).append(" codecState(")
+ .append(qualifiedStateCase(state)).append(");\n")
+ .append(indent).append(" break;\n");
+ });
+
+ sb.append(indent).append(" default:\n")
+ .append(indent).append(" codecState(")
+ .append(qualifiedStateCase(fieldPrecedenceModel.latestVersionWrappedState())).append(");\n")
+ .append(indent).append(" break;\n")
+ .append(indent).append(" }\n")
+ .append(indent).append("}\n\n");
+
+ return sb;
+ }
+
+ private CharSequence generateEncoderWrapListener(
+ final FieldPrecedenceModel fieldPrecedenceModel,
+ final String indent)
+ {
+ if (null == fieldPrecedenceModel)
+ {
+ return "";
+ }
+
+ final StringBuilder sb = new StringBuilder();
+ sb.append(indent).append("if (").append(precedenceChecksFlagName).append(")")
+ .append("\n").append(indent).append("{\n")
+ .append(indent).append(" codecState(")
+ .append(qualifiedStateCase(fieldPrecedenceModel.latestVersionWrappedState()))
+ .append(");\n")
+ .append(indent).append("}\n\n");
+ return sb;
+ }
+
private void generateDecoder(
+ final String className,
final Token msgToken,
final List fields,
final List groups,
final List varData,
- final boolean hasVarData)
+ final boolean hasVarData,
+ final FieldPrecedenceModel fieldPrecedenceModel)
throws IOException
{
- final String className = formatClassName(decoderName(msgToken.name()));
final String implementsString = implementsInterface(MessageDecoderFlyweight.class.getSimpleName());
try (Writer out = outputManager.createOutput(className))
@@ -288,13 +830,15 @@ private void generateDecoder(
{
generateAnnotations(BASE_INDENT, className, groups, out, this::decoderName);
}
+
out.append(generateDeclaration(className, implementsString, msgToken));
- out.append(generateDecoderFlyweightCode(className, msgToken));
+ out.append(generateFieldOrderStates(fieldPrecedenceModel));
+ out.append(generateDecoderFlyweightCode(fieldPrecedenceModel, className, msgToken));
final StringBuilder sb = new StringBuilder();
- generateDecoderFields(sb, fields, BASE_INDENT);
- generateDecoderGroups(sb, className, groups, BASE_INDENT, false);
- generateDecoderVarData(sb, varData, BASE_INDENT);
+ generateDecoderFields(sb, fieldPrecedenceModel, fields, BASE_INDENT);
+ generateDecoderGroups(sb, fieldPrecedenceModel, className, groups, BASE_INDENT, false);
+ generateDecoderVarData(sb, fieldPrecedenceModel, varData, BASE_INDENT);
generateDecoderDisplay(sb, msgToken.name(), fields, groups, varData);
generateMessageLength(sb, className, true, groups, varData, BASE_INDENT);
@@ -306,7 +850,7 @@ private void generateDecoder(
private void generateDecoderGroups(
final StringBuilder sb,
- final String outerClassName,
+ final FieldPrecedenceModel fieldPrecedenceModel, final String outerClassName,
final List tokens,
final String indent,
final boolean isSubGroup) throws IOException
@@ -335,18 +879,19 @@ private void generateDecoderGroups(
final List varData = new ArrayList<>();
i = collectVarData(tokens, i, varData);
- generateGroupDecoderProperty(sb, groupName, groupToken, indent, isSubGroup);
+ generateGroupDecoderProperty(sb, groupName, fieldPrecedenceModel, groupToken, indent, isSubGroup);
generateTypeJavadoc(sb, indent + INDENT, groupToken);
if (shouldGenerateGroupOrderAnnotation)
{
generateAnnotations(indent + INDENT, groupName, groups, sb, this::decoderName);
}
- generateGroupDecoderClassHeader(sb, groupName, outerClassName, tokens, groups, index, indent + INDENT);
+ generateGroupDecoderClassHeader(sb, groupName, outerClassName, fieldPrecedenceModel, groupToken,
+ tokens, groups, index, indent + INDENT);
- generateDecoderFields(sb, fields, indent + INDENT);
- generateDecoderGroups(sb, outerClassName, groups, indent + INDENT, true);
- generateDecoderVarData(sb, varData, indent + INDENT);
+ generateDecoderFields(sb, fieldPrecedenceModel, fields, indent + INDENT);
+ generateDecoderGroups(sb, fieldPrecedenceModel, outerClassName, groups, indent + INDENT, true);
+ generateDecoderVarData(sb, fieldPrecedenceModel, varData, indent + INDENT);
appendGroupInstanceDecoderDisplay(sb, fields, groups, varData, indent + INDENT);
generateMessageLength(sb, groupName, false, groups, varData, indent + INDENT);
@@ -358,6 +903,7 @@ private void generateDecoderGroups(
private void generateEncoderGroups(
final StringBuilder sb,
final String outerClassName,
+ final FieldPrecedenceModel fieldPrecedenceModel,
final List tokens,
final String indent,
final boolean isSubGroup) throws IOException
@@ -387,18 +933,20 @@ private void generateEncoderGroups(
final List varData = new ArrayList<>();
i = collectVarData(tokens, i, varData);
- generateGroupEncoderProperty(sb, groupName, groupToken, indent, isSubGroup);
+ generateGroupEncoderProperty(sb, groupName, fieldPrecedenceModel, groupToken, indent, isSubGroup);
generateTypeJavadoc(sb, indent + INDENT, groupToken);
if (shouldGenerateGroupOrderAnnotation)
{
generateAnnotations(indent + INDENT, groupClassName, groups, sb, this::encoderName);
}
- generateGroupEncoderClassHeader(sb, groupName, outerClassName, tokens, groups, index, indent + INDENT);
+ generateGroupEncoderClassHeader(
+ sb, groupName, outerClassName, fieldPrecedenceModel, groupToken,
+ tokens, groups, index, indent + INDENT);
- generateEncoderFields(sb, groupClassName, fields, indent + INDENT);
- generateEncoderGroups(sb, outerClassName, groups, indent + INDENT, true);
- generateEncoderVarData(sb, groupClassName, varData, indent + INDENT);
+ generateEncoderFields(sb, groupClassName, fieldPrecedenceModel, fields, indent + INDENT);
+ generateEncoderGroups(sb, outerClassName, fieldPrecedenceModel, groups, indent + INDENT, true);
+ generateEncoderVarData(sb, groupClassName, fieldPrecedenceModel, varData, indent + INDENT);
sb.append(indent).append(" }\n");
}
@@ -408,6 +956,8 @@ private void generateGroupDecoderClassHeader(
final StringBuilder sb,
final String groupName,
final String parentMessageClassName,
+ final FieldPrecedenceModel fieldPrecedenceModel,
+ final Token groupToken,
final List tokens,
final List subGroupTokens,
final int index,
@@ -452,15 +1002,18 @@ private void generateGroupDecoderClassHeader(
.append(indent).append(" parentMessage.limit(limit + HEADER_SIZE);\n")
.append(indent).append(" blockLength = ").append(blockLenCast).append(blockLengthGet).append(";\n")
.append(indent).append(" count = ").append(numInGroupCast).append(numInGroupGet).append(";\n")
- .append(indent).append(" }\n");
+ .append(indent).append(" }\n\n");
- sb.append("\n")
- .append(indent).append(" public ").append(className).append(" next()\n")
+
+ generateAccessOrderListenerMethodForNextGroupElement(sb, fieldPrecedenceModel, indent + " ", groupToken);
+
+ sb.append(indent).append(" public ").append(className).append(" next()\n")
.append(indent).append(" {\n")
.append(indent).append(" if (index >= count)\n")
.append(indent).append(" {\n")
.append(indent).append(" throw new java.util.NoSuchElementException();\n")
.append(indent).append(" }\n\n")
+ .append(generateAccessOrderListenerCall(fieldPrecedenceModel, indent + " ", "onNextElementAccessed"))
.append(indent).append(" offset = parentMessage.limit();\n")
.append(indent).append(" parentMessage.limit(offset + blockLength);\n")
.append(indent).append(" ++index;\n\n")
@@ -508,12 +1061,29 @@ private void generateGroupDecoderClassHeader(
.append(indent).append(" {\n")
.append(indent).append(" return index < count;\n")
.append(indent).append(" }\n");
+
+ if (null != fieldPrecedenceModel)
+ {
+ sb.append("\n")
+ .append(indent).append(" private int codecState()\n")
+ .append(indent).append(" {\n")
+ .append(indent).append(" return parentMessage.codecState();\n")
+ .append(indent).append(" }\n");
+
+ sb.append("\n")
+ .append(indent).append(" private void codecState(final int newState)\n")
+ .append(indent).append(" {\n")
+ .append(indent).append(" parentMessage.codecState(newState);\n")
+ .append(indent).append(" }\n");
+ }
}
private void generateGroupEncoderClassHeader(
final StringBuilder sb,
final String groupName,
final String parentMessageClassName,
+ final FieldPrecedenceModel fieldPrecedenceModel,
+ final Token groupToken,
final List tokens,
final List subGroupTokens,
final int index,
@@ -566,7 +1136,7 @@ private void generateGroupEncoderClassHeader(
ind + " parentMessage.limit(limit + HEADER_SIZE);\n" +
ind + " %5$s;\n" +
ind + " %6$s;\n" +
- ind + " }\n",
+ ind + " }\n\n",
parentMessageClassName,
mutableBuffer,
numInGroupToken.encoding().applicableMinValue().longValue(),
@@ -574,9 +1144,12 @@ private void generateGroupEncoderClassHeader(
blockLengthPut,
numInGroupPut);
- sb.append("\n")
- .append(ind).append(" public ").append(encoderName(groupName)).append(" next()\n")
+ generateAccessOrderListenerMethodForNextGroupElement(sb, fieldPrecedenceModel, ind + " ", groupToken);
+ generateAccessOrderListenerMethodForResetGroupCount(sb, fieldPrecedenceModel, ind + " ", groupToken);
+
+ sb.append(ind).append(" public ").append(encoderName(groupName)).append(" next()\n")
.append(ind).append(" {\n")
+ .append(generateAccessOrderListenerCall(fieldPrecedenceModel, ind + " ", "onNextElementAccessed"))
.append(ind).append(" if (index >= count)\n")
.append(ind).append(" {\n")
.append(ind).append(" throw new java.util.NoSuchElementException();\n")
@@ -585,15 +1158,15 @@ private void generateGroupEncoderClassHeader(
.append(ind).append(" parentMessage.limit(offset + sbeBlockLength());\n")
.append(ind).append(" ++index;\n\n")
.append(ind).append(" return this;\n")
- .append(ind).append(" }\n");
+ .append(ind).append(" }\n\n");
final String countOffset = "initialLimit + " + numInGroupToken.offset();
final String resetCountPut = generatePut(
numInGroupTypeCast, countOffset, numInGroupValue, byteOrderString(numInGroupToken.encoding()));
- sb.append("\n")
- .append(ind).append(" public int resetCountToIndex()\n")
+ sb.append(ind).append(" public int resetCountToIndex()\n")
.append(ind).append(" {\n")
+ .append(generateAccessOrderListenerCall(fieldPrecedenceModel, ind + " ", "onResetCountToIndex"))
.append(ind).append(" count = index;\n")
.append(ind).append(" ").append(resetCountPut).append(";\n\n")
.append(ind).append(" return count;\n")
@@ -618,6 +1191,21 @@ private void generateGroupEncoderClassHeader(
.append(ind).append(" {\n")
.append(ind).append(" return ").append(blockLength).append(";\n")
.append(ind).append(" }\n");
+
+ if (null != fieldPrecedenceModel)
+ {
+ sb.append("\n")
+ .append(ind).append(" private int codecState()\n")
+ .append(ind).append(" {\n")
+ .append(ind).append(" return parentMessage.codecState();\n")
+ .append(ind).append(" }\n");
+
+ sb.append("\n")
+ .append(ind).append(" private void codecState(final int newState)\n")
+ .append(ind).append(" {\n")
+ .append(ind).append(" parentMessage.codecState(newState);\n")
+ .append(ind).append(" }\n");
+ }
}
private static String primitiveTypeName(final Token token)
@@ -728,9 +1316,10 @@ private void generateGroupEncoderClassDeclaration(
sb.append(indent).append(" }\n");
}
- private static void generateGroupDecoderProperty(
+ private void generateGroupDecoderProperty(
final StringBuilder sb,
final String groupName,
+ final FieldPrecedenceModel fieldPrecedenceModel,
final Token token,
final String indent,
final boolean isSubGroup)
@@ -772,22 +1361,27 @@ private static void generateGroupDecoderProperty(
indent + " return " + propertyName + ";\n" +
indent + " }\n\n";
+ generateAccessOrderListenerMethodForGroupWrap(sb, "decode", fieldPrecedenceModel, indent + " ", token);
+
generateFlyweightPropertyJavadoc(sb, indent + INDENT, token, className);
new Formatter(sb).format("\n" +
indent + " public %1$s %2$s()\n" +
indent + " {\n" +
"%3$s" +
indent + " %2$s.wrap(buffer);\n" +
+ "%4$s" +
indent + " return %2$s;\n" +
indent + " }\n",
className,
propertyName,
- actingVersionGuard);
+ actingVersionGuard,
+ generateAccessOrderListenerCall(fieldPrecedenceModel, indent + " ", token, propertyName + ".count"));
}
private void generateGroupEncoderProperty(
final StringBuilder sb,
final String groupName,
+ final FieldPrecedenceModel fieldPrecedenceModel,
final Token token,
final String indent,
final boolean isSubGroup)
@@ -812,19 +1406,26 @@ private void generateGroupEncoderProperty(
formatPropertyName(groupName),
token.id());
+ generateAccessOrderListenerMethodForGroupWrap(sb, "encode", fieldPrecedenceModel, indent + " ", token);
+
generateGroupEncodePropertyJavadoc(sb, indent + INDENT, token, className);
new Formatter(sb).format("\n" +
indent + " public %1$s %2$sCount(final int count)\n" +
indent + " {\n" +
+ "%3$s" +
indent + " %2$s.wrap(buffer, count);\n" +
indent + " return %2$s;\n" +
indent + " }\n",
className,
- propertyName);
+ propertyName,
+ generateAccessOrderListenerCall(fieldPrecedenceModel, indent + " ", token, "count"));
}
private void generateDecoderVarData(
- final StringBuilder sb, final List tokens, final String indent)
+ final StringBuilder sb,
+ final FieldPrecedenceModel fieldPrecedenceModel,
+ final List tokens,
+ final String indent)
{
for (int i = 0, size = tokens.size(); i < size;)
{
@@ -855,24 +1456,39 @@ private void generateDecoderVarData(
.append(indent).append(" return ").append(sizeOfLengthField).append(";\n")
.append(indent).append(" }\n");
+ generateAccessOrderListenerMethodForVarDataLength(sb, fieldPrecedenceModel, indent + " ", token);
+
+ final CharSequence lengthAccessOrderListenerCall = generateAccessOrderListenerCall(
+ fieldPrecedenceModel, indent + " ", accessOrderListenerMethodName(token, "Length"));
+
+ generateAccessOrderListenerMethod(sb, fieldPrecedenceModel, indent + " ", token);
+
sb.append("\n")
.append(indent).append(" public int ").append(methodPropName).append("Length()\n")
.append(indent).append(" {\n")
.append(generateArrayFieldNotPresentCondition(token.version(), indent))
+ .append(lengthAccessOrderListenerCall)
.append(indent).append(" final int limit = parentMessage.limit();\n")
.append(indent).append(" return ").append(PrimitiveType.UINT32 == lengthType ? "(int)" : "")
.append(generateGet(lengthType, "limit", byteOrderStr)).append(";\n")
.append(indent).append(" }\n");
- generateDataDecodeMethods(
- sb, token, propertyName, sizeOfLengthField, lengthType, byteOrderStr, characterEncoding, indent);
+ final CharSequence accessOrderListenerCall =
+ generateAccessOrderListenerCall(fieldPrecedenceModel, indent + " ", token);
+
+ generateDataDecodeMethods(sb, token, propertyName, sizeOfLengthField, lengthType,
+ byteOrderStr, characterEncoding, accessOrderListenerCall, indent);
i += token.componentTokenCount();
}
}
private void generateEncoderVarData(
- final StringBuilder sb, final String className, final List tokens, final String indent)
+ final StringBuilder sb,
+ final String className,
+ final FieldPrecedenceModel fieldPrecedenceModel,
+ final List tokens,
+ final String indent)
{
for (int i = 0, size = tokens.size(); i < size;)
{
@@ -903,9 +1519,14 @@ private void generateEncoderVarData(
.append(sizeOfLengthField).append(";\n")
.append(indent).append(" }\n");
+ generateAccessOrderListenerMethod(sb, fieldPrecedenceModel, indent + " ", token);
+ final CharSequence accessOrderListenerCall =
+ generateAccessOrderListenerCall(fieldPrecedenceModel, indent + " ", token);
+
generateDataEncodeMethods(
sb,
propertyName,
+ accessOrderListenerCall,
sizeOfLengthField,
maxLengthValue,
lengthEncoding.primitiveType(),
@@ -926,12 +1547,14 @@ private void generateDataDecodeMethods(
final PrimitiveType lengthType,
final String byteOrderStr,
final String characterEncoding,
+ final CharSequence accessOrderListenerCall,
final String indent)
{
new Formatter(sb).format("\n" +
indent + " public int skip%1$s()\n" +
indent + " {\n" +
"%2$s" +
+ "%6$s" +
indent + " final int headerLength = %3$d;\n" +
indent + " final int limit = parentMessage.limit();\n" +
indent + " final int dataLength = %4$s%5$s;\n" +
@@ -943,7 +1566,8 @@ private void generateDataDecodeMethods(
generateStringNotPresentConditionForAppendable(token.version(), indent),
sizeOfLengthField,
PrimitiveType.UINT32 == lengthType ? "(int)" : "",
- generateGet(lengthType, "limit", byteOrderStr));
+ generateGet(lengthType, "limit", byteOrderStr),
+ accessOrderListenerCall);
generateVarDataTypedDecoder(
sb,
@@ -953,6 +1577,7 @@ private void generateDataDecodeMethods(
mutableBuffer,
lengthType,
byteOrderStr,
+ accessOrderListenerCall,
indent);
generateVarDataTypedDecoder(
@@ -963,9 +1588,18 @@ private void generateDataDecodeMethods(
"byte[]",
lengthType,
byteOrderStr,
+ accessOrderListenerCall,
indent);
- generateVarDataWrapDecoder(sb, token, propertyName, sizeOfLengthField, lengthType, byteOrderStr, indent);
+ generateVarDataWrapDecoder(
+ sb,
+ token,
+ propertyName,
+ sizeOfLengthField,
+ lengthType,
+ byteOrderStr,
+ accessOrderListenerCall,
+ indent);
if (null != characterEncoding)
{
@@ -973,6 +1607,7 @@ private void generateDataDecodeMethods(
indent + " public String %1$s()\n" +
indent + " {\n" +
"%2$s" +
+ "%7$s" +
indent + " final int headerLength = %3$d;\n" +
indent + " final int limit = parentMessage.limit();\n" +
indent + " final int dataLength = %4$s%5$s;\n" +
@@ -990,7 +1625,8 @@ private void generateDataDecodeMethods(
sizeOfLengthField,
PrimitiveType.UINT32 == lengthType ? "(int)" : "",
generateGet(lengthType, "limit", byteOrderStr),
- charset(characterEncoding));
+ charset(characterEncoding),
+ accessOrderListenerCall);
if (isAsciiEncoding(characterEncoding))
{
@@ -998,6 +1634,7 @@ private void generateDataDecodeMethods(
indent + " public int get%1$s(final Appendable appendable)\n" +
indent + " {\n" +
"%2$s" +
+ "%6$s" +
indent + " final int headerLength = %3$d;\n" +
indent + " final int limit = parentMessage.limit();\n" +
indent + " final int dataLength = %4$s%5$s;\n" +
@@ -1010,7 +1647,8 @@ private void generateDataDecodeMethods(
generateStringNotPresentConditionForAppendable(token.version(), indent),
sizeOfLengthField,
PrimitiveType.UINT32 == lengthType ? "(int)" : "",
- generateGet(lengthType, "limit", byteOrderStr));
+ generateGet(lengthType, "limit", byteOrderStr),
+ accessOrderListenerCall);
}
}
}
@@ -1022,12 +1660,14 @@ private void generateVarDataWrapDecoder(
final int sizeOfLengthField,
final PrimitiveType lengthType,
final String byteOrderStr,
+ final CharSequence accessOrderListenerCall,
final String indent)
{
new Formatter(sb).format("\n" +
indent + " public void wrap%s(final %s wrapBuffer)\n" +
indent + " {\n" +
"%s" +
+ "%s" +
indent + " final int headerLength = %d;\n" +
indent + " final int limit = parentMessage.limit();\n" +
indent + " final int dataLength = %s%s;\n" +
@@ -1037,6 +1677,7 @@ private void generateVarDataWrapDecoder(
propertyName,
readOnlyBuffer,
generateWrapFieldNotPresentCondition(token.version(), indent),
+ accessOrderListenerCall,
sizeOfLengthField,
PrimitiveType.UINT32 == lengthType ? "(int)" : "",
generateGet(lengthType, "limit", byteOrderStr));
@@ -1045,6 +1686,7 @@ private void generateVarDataWrapDecoder(
private void generateDataEncodeMethods(
final StringBuilder sb,
final String propertyName,
+ final CharSequence accessOrderListenerCall,
final int sizeOfLengthField,
final int maxLengthValue,
final PrimitiveType lengthType,
@@ -1057,6 +1699,7 @@ private void generateDataEncodeMethods(
sb,
className,
propertyName,
+ accessOrderListenerCall,
sizeOfLengthField,
maxLengthValue,
readOnlyBuffer,
@@ -1068,6 +1711,7 @@ private void generateDataEncodeMethods(
sb,
className,
propertyName,
+ accessOrderListenerCall,
sizeOfLengthField,
maxLengthValue,
"byte[]",
@@ -1080,6 +1724,7 @@ private void generateDataEncodeMethods(
generateCharArrayEncodeMethods(
sb,
propertyName,
+ accessOrderListenerCall,
sizeOfLengthField,
maxLengthValue,
lengthType,
@@ -1093,6 +1738,7 @@ private void generateDataEncodeMethods(
private void generateCharArrayEncodeMethods(
final StringBuilder sb,
final String propertyName,
+ final CharSequence accessOrderListenerCall,
final int sizeOfLengthField,
final int maxLengthValue,
final PrimitiveType lengthType,
@@ -1113,6 +1759,7 @@ private void generateCharArrayEncodeMethods(
indent + " {\n" +
indent + " throw new IllegalStateException(\"length > maxValue for type: \" + length);\n" +
indent + " }\n\n" +
+ "%6$s" +
indent + " final int headerLength = %4$d;\n" +
indent + " final int limit = parentMessage.limit();\n" +
indent + " parentMessage.limit(limit + headerLength + length);\n" +
@@ -1124,7 +1771,8 @@ private void generateCharArrayEncodeMethods(
formatPropertyName(propertyName),
maxLengthValue,
sizeOfLengthField,
- generatePut(lengthPutType, "limit", "length", byteOrderStr));
+ generatePut(lengthPutType, "limit", "length", byteOrderStr),
+ accessOrderListenerCall);
new Formatter(sb).format("\n" +
indent + " public %1$s %2$s(final CharSequence value)\n" +
@@ -1134,6 +1782,7 @@ private void generateCharArrayEncodeMethods(
indent + " {\n" +
indent + " throw new IllegalStateException(\"length > maxValue for type: \" + length);\n" +
indent + " }\n\n" +
+ "%6$s" +
indent + " final int headerLength = %4$d;\n" +
indent + " final int limit = parentMessage.limit();\n" +
indent + " parentMessage.limit(limit + headerLength + length);\n" +
@@ -1145,7 +1794,8 @@ private void generateCharArrayEncodeMethods(
formatPropertyName(propertyName),
maxLengthValue,
sizeOfLengthField,
- generatePut(lengthPutType, "limit", "length", byteOrderStr));
+ generatePut(lengthPutType, "limit", "length", byteOrderStr),
+ accessOrderListenerCall);
}
else
{
@@ -1159,6 +1809,7 @@ private void generateCharArrayEncodeMethods(
indent + " {\n" +
indent + " throw new IllegalStateException(\"length > maxValue for type: \" + length);\n" +
indent + " }\n\n" +
+ "%7$s" +
indent + " final int headerLength = %5$d;\n" +
indent + " final int limit = parentMessage.limit();\n" +
indent + " parentMessage.limit(limit + headerLength + length);\n" +
@@ -1171,7 +1822,8 @@ private void generateCharArrayEncodeMethods(
charset(characterEncoding),
maxLengthValue,
sizeOfLengthField,
- generatePut(lengthPutType, "limit", "length", byteOrderStr));
+ generatePut(lengthPutType, "limit", "length", byteOrderStr),
+ accessOrderListenerCall);
}
}
@@ -1183,12 +1835,14 @@ private void generateVarDataTypedDecoder(
final String exchangeType,
final PrimitiveType lengthType,
final String byteOrderStr,
+ final CharSequence accessOrderListenerCall,
final String indent)
{
new Formatter(sb).format("\n" +
indent + " public int get%s(final %s dst, final int dstOffset, final int length)\n" +
indent + " {\n" +
"%s" +
+ "%s" +
indent + " final int headerLength = %d;\n" +
indent + " final int limit = parentMessage.limit();\n" +
indent + " final int dataLength = %s%s;\n" +
@@ -1200,6 +1854,7 @@ private void generateVarDataTypedDecoder(
propertyName,
exchangeType,
generateArrayFieldNotPresentCondition(token.version(), indent),
+ accessOrderListenerCall,
sizeOfLengthField,
PrimitiveType.UINT32 == lengthType ? "(int)" : "",
generateGet(lengthType, "limit", byteOrderStr));
@@ -1209,6 +1864,7 @@ private void generateDataTypedEncoder(
final StringBuilder sb,
final String className,
final String propertyName,
+ final CharSequence accessOrderListenerCall,
final int sizeOfLengthField,
final int maxLengthValue,
final String exchangeType,
@@ -1225,6 +1881,7 @@ private void generateDataTypedEncoder(
indent + " {\n" +
indent + " throw new IllegalStateException(\"length > maxValue for type: \" + length);\n" +
indent + " }\n\n" +
+ "%7$s" +
indent + " final int headerLength = %5$d;\n" +
indent + " final int limit = parentMessage.limit();\n" +
indent + " parentMessage.limit(limit + headerLength + length);\n" +
@@ -1237,7 +1894,8 @@ private void generateDataTypedEncoder(
exchangeType,
maxLengthValue,
sizeOfLengthField,
- generatePut(lengthPutType, "limit", "length", byteOrderStr));
+ generatePut(lengthPutType, "limit", "length", byteOrderStr),
+ accessOrderListenerCall);
}
private void generateBitSet(final List tokens) throws IOException
@@ -1359,26 +2017,29 @@ private void generateComposite(final List tokens) throws IOException
generateEncodingOffsetMethod(sb, propertyName, encodingToken.offset(), BASE_INDENT);
generateEncodingLengthMethod(sb, propertyName, encodingToken.encodedLength(), BASE_INDENT);
generateFieldSinceVersionMethod(sb, encodingToken, BASE_INDENT);
+ final String accessOrderListenerCall = "";
switch (encodingToken.signal())
{
case ENCODING:
generatePrimitiveDecoder(
- sb, true, encodingToken.name(), encodingToken, encodingToken, BASE_INDENT);
+ sb, true, encodingToken.name(), "", encodingToken, encodingToken, BASE_INDENT);
break;
case BEGIN_ENUM:
- generateEnumDecoder(sb, true, encodingToken, propertyName, encodingToken, BASE_INDENT);
+ generateEnumDecoder(sb, true, "", encodingToken, propertyName, encodingToken, BASE_INDENT);
break;
case BEGIN_SET:
generateBitSetProperty(
- sb, true, DECODER, propertyName, encodingToken, encodingToken, BASE_INDENT, typeName);
+ sb, true, DECODER, propertyName, accessOrderListenerCall,
+ encodingToken, encodingToken, BASE_INDENT, typeName);
break;
case BEGIN_COMPOSITE:
generateCompositeProperty(
- sb, true, DECODER, propertyName, encodingToken, encodingToken, BASE_INDENT, typeName);
+ sb, true, DECODER, propertyName, accessOrderListenerCall,
+ encodingToken, encodingToken, BASE_INDENT, typeName);
break;
default:
@@ -1410,25 +2071,30 @@ private void generateComposite(final List tokens) throws IOException
final StringBuilder sb = new StringBuilder();
generateEncodingOffsetMethod(sb, propertyName, encodingToken.offset(), BASE_INDENT);
generateEncodingLengthMethod(sb, propertyName, encodingToken.encodedLength(), BASE_INDENT);
+ final String accessOrderListenerCall = "";
switch (encodingToken.signal())
{
case ENCODING:
- generatePrimitiveEncoder(sb, encoderName, encodingToken.name(), encodingToken, BASE_INDENT);
+ generatePrimitiveEncoder(sb, encoderName, encodingToken.name(),
+ accessOrderListenerCall, encodingToken, BASE_INDENT);
break;
case BEGIN_ENUM:
- generateEnumEncoder(sb, encoderName, encodingToken, propertyName, encodingToken, BASE_INDENT);
+ generateEnumEncoder(sb, encoderName, accessOrderListenerCall,
+ encodingToken, propertyName, encodingToken, BASE_INDENT);
break;
case BEGIN_SET:
generateBitSetProperty(
- sb, true, ENCODER, propertyName, encodingToken, encodingToken, BASE_INDENT, typeName);
+ sb, true, ENCODER, propertyName, accessOrderListenerCall,
+ encodingToken, encodingToken, BASE_INDENT, typeName);
break;
case BEGIN_COMPOSITE:
generateCompositeProperty(
- sb, true, ENCODER, propertyName, encodingToken, encodingToken, BASE_INDENT, typeName);
+ sb, true, ENCODER, propertyName, accessOrderListenerCall,
+ encodingToken, encodingToken, BASE_INDENT, typeName);
break;
default:
@@ -1855,6 +2521,7 @@ private void generatePrimitiveDecoder(
final StringBuilder sb,
final boolean inComposite,
final String propertyName,
+ final CharSequence accessOrderListenerCall,
final Token propertyToken,
final Token encodingToken,
final String indent)
@@ -1870,7 +2537,7 @@ private void generatePrimitiveDecoder(
else
{
sb.append(generatePrimitivePropertyDecodeMethods(
- inComposite, formattedPropertyName, propertyToken, encodingToken, indent));
+ inComposite, formattedPropertyName, accessOrderListenerCall, propertyToken, encodingToken, indent));
}
}
@@ -1878,43 +2545,52 @@ private void generatePrimitiveEncoder(
final StringBuilder sb,
final String containingClassName,
final String propertyName,
- final Token token,
+ final CharSequence accessOrderListenerCall,
+ final Token typeToken,
final String indent)
{
final String formattedPropertyName = formatPropertyName(propertyName);
- generatePrimitiveFieldMetaMethod(sb, formattedPropertyName, token, indent);
+ generatePrimitiveFieldMetaMethod(sb, formattedPropertyName, typeToken, indent);
- if (!token.isConstantEncoding())
+ if (!typeToken.isConstantEncoding())
{
sb.append(generatePrimitivePropertyEncodeMethods(
- containingClassName, formattedPropertyName, token, indent));
+ containingClassName, formattedPropertyName, accessOrderListenerCall, typeToken, indent));
}
else
{
- generateConstPropertyMethods(sb, formattedPropertyName, token, indent);
+ generateConstPropertyMethods(sb, formattedPropertyName, typeToken, indent);
}
}
private CharSequence generatePrimitivePropertyDecodeMethods(
final boolean inComposite,
final String propertyName,
+ final CharSequence accessOrderListenerCall,
final Token propertyToken,
final Token encodingToken,
final String indent)
{
return encodingToken.matchOnLength(
- () -> generatePrimitivePropertyDecode(inComposite, propertyName, propertyToken, encodingToken, indent),
+ () -> generatePrimitivePropertyDecode(
+ inComposite, propertyName, accessOrderListenerCall, propertyToken, encodingToken, indent),
() -> generatePrimitiveArrayPropertyDecode(
- inComposite, propertyName, propertyToken, encodingToken, indent));
+ inComposite, propertyName, accessOrderListenerCall, propertyToken, encodingToken, indent));
}
private CharSequence generatePrimitivePropertyEncodeMethods(
- final String containingClassName, final String propertyName, final Token token, final String indent)
+ final String containingClassName,
+ final String propertyName,
+ final CharSequence accessOrderListenerCall,
+ final Token typeToken,
+ final String indent)
{
- return token.matchOnLength(
- () -> generatePrimitivePropertyEncode(containingClassName, propertyName, token, indent),
- () -> generatePrimitiveArrayPropertyEncode(containingClassName, propertyName, token, indent));
+ return typeToken.matchOnLength(
+ () -> generatePrimitivePropertyEncode(
+ containingClassName, propertyName, accessOrderListenerCall, typeToken, indent),
+ () -> generatePrimitiveArrayPropertyEncode(
+ containingClassName, propertyName, accessOrderListenerCall, typeToken, indent));
}
private void generatePrimitiveFieldMetaMethod(
@@ -1953,6 +2629,7 @@ private void generatePrimitiveFieldMetaMethod(
private CharSequence generatePrimitivePropertyDecode(
final boolean inComposite,
final String propertyName,
+ final CharSequence accessOrderListenerCall,
final Token propertyToken,
final Token encodingToken,
final String indent)
@@ -1968,32 +2645,40 @@ private CharSequence generatePrimitivePropertyDecode(
indent + " public %s %s()\n" +
indent + " {\n" +
"%s" +
+ "%s" +
indent + " return %s;\n" +
indent + " }\n\n",
javaTypeName,
formatPropertyName(propertyName),
generateFieldNotPresentCondition(inComposite, propertyToken.version(), encoding, indent),
+ accessOrderListenerCall,
generateGet(encoding.primitiveType(), "offset + " + offset, byteOrderStr));
}
private CharSequence generatePrimitivePropertyEncode(
- final String containingClassName, final String propertyName, final Token token, final String indent)
+ final String containingClassName,
+ final String propertyName,
+ final CharSequence accessOrderListenerCall,
+ final Token typeToken,
+ final String indent)
{
- final Encoding encoding = token.encoding();
+ final Encoding encoding = typeToken.encoding();
final String javaTypeName = javaTypeName(encoding.primitiveType());
- final int offset = token.offset();
+ final int offset = typeToken.offset();
final String byteOrderStr = byteOrderString(encoding);
return String.format(
"\n" +
indent + " public %s %s(final %s value)\n" +
indent + " {\n" +
+ "%s" +
indent + " %s;\n" +
indent + " return this;\n" +
indent + " }\n\n",
formatClassName(containingClassName),
formatPropertyName(propertyName),
javaTypeName,
+ accessOrderListenerCall,
generatePut(encoding.primitiveType(), "offset + " + offset, "value", byteOrderStr));
}
@@ -2094,6 +2779,7 @@ private static CharSequence generatePropertyNotPresentCondition(
private CharSequence generatePrimitiveArrayPropertyDecode(
final boolean inComposite,
final String propertyName,
+ final CharSequence accessOrderListenerCall,
final Token propertyToken,
final Token encodingToken,
final String indent)
@@ -2117,6 +2803,7 @@ private CharSequence generatePrimitiveArrayPropertyDecode(
indent + " throw new IndexOutOfBoundsException(\"index out of range: index=\" + index);\n" +
indent + " }\n\n" +
"%s" +
+ "%s" +
indent + " final int pos = offset + %d + (index * %d);\n\n" +
indent + " return %s;\n" +
indent + " }\n\n",
@@ -2124,6 +2811,7 @@ private CharSequence generatePrimitiveArrayPropertyDecode(
propertyName,
fieldLength,
generateFieldNotPresentCondition(inComposite, propertyToken.version(), encoding, indent),
+ accessOrderListenerCall,
offset,
typeSize,
generateGet(encoding.primitiveType(), "pos", byteOrderStr));
@@ -2142,18 +2830,21 @@ private CharSequence generatePrimitiveArrayPropertyDecode(
"\"Copy will go out of range: offset=\" + dstOffset);\n" +
indent + " }\n\n" +
"%s" +
+ "%s" +
indent + " buffer.getBytes(offset + %d, dst, dstOffset, length);\n\n" +
indent + " return length;\n" +
indent + " }\n",
Generators.toUpperFirstChar(propertyName),
fieldLength,
generateArrayFieldNotPresentCondition(propertyToken.version(), indent),
+ accessOrderListenerCall,
offset);
new Formatter(sb).format("\n" +
indent + " public String %s()\n" +
indent + " {\n" +
"%s" +
+ "%s" +
indent + " final byte[] dst = new byte[%d];\n" +
indent + " buffer.getBytes(offset + %d, dst, 0, %d);\n\n" +
indent + " int end = 0;\n" +
@@ -2162,6 +2853,7 @@ private CharSequence generatePrimitiveArrayPropertyDecode(
indent + " }\n\n",
propertyName,
generateStringNotPresentCondition(propertyToken.version(), indent),
+ accessOrderListenerCall,
fieldLength,
offset,
fieldLength,
@@ -2174,6 +2866,7 @@ private CharSequence generatePrimitiveArrayPropertyDecode(
indent + " public int get%1$s(final Appendable value)\n" +
indent + " {\n" +
"%2$s" +
+ "%5$s" +
indent + " for (int i = 0; i < %3$d; ++i)\n" +
indent + " {\n" +
indent + " final int c = buffer.getByte(offset + %4$d + i) & 0xFF;\n" +
@@ -2195,7 +2888,8 @@ private CharSequence generatePrimitiveArrayPropertyDecode(
Generators.toUpperFirstChar(propertyName),
generateStringNotPresentConditionForAppendable(propertyToken.version(), indent),
fieldLength,
- offset);
+ offset,
+ accessOrderListenerCall);
}
}
else if (encoding.primitiveType() == PrimitiveType.UINT8)
@@ -2204,12 +2898,14 @@ else if (encoding.primitiveType() == PrimitiveType.UINT8)
indent + " public int get%s(final byte[] dst, final int dstOffset, final int length)\n" +
indent + " {\n" +
"%s" +
+ "%s" +
indent + " final int bytesCopied = Math.min(length, %d);\n" +
indent + " buffer.getBytes(offset + %d, dst, dstOffset, bytesCopied);\n\n" +
indent + " return bytesCopied;\n" +
indent + " }\n",
Generators.toUpperFirstChar(propertyName),
generateArrayFieldNotPresentCondition(propertyToken.version(), indent),
+ accessOrderListenerCall,
fieldLength,
offset);
@@ -2217,6 +2913,7 @@ else if (encoding.primitiveType() == PrimitiveType.UINT8)
indent + " public int get%s(final %s dst, final int dstOffset, final int length)\n" +
indent + " {\n" +
"%s" +
+ "%s" +
indent + " final int bytesCopied = Math.min(length, %d);\n" +
indent + " buffer.getBytes(offset + %d, dst, dstOffset, bytesCopied);\n\n" +
indent + " return bytesCopied;\n" +
@@ -2224,6 +2921,7 @@ else if (encoding.primitiveType() == PrimitiveType.UINT8)
Generators.toUpperFirstChar(propertyName),
fqMutableBuffer,
generateArrayFieldNotPresentCondition(propertyToken.version(), indent),
+ accessOrderListenerCall,
fieldLength,
offset);
@@ -2231,11 +2929,13 @@ else if (encoding.primitiveType() == PrimitiveType.UINT8)
indent + " public void wrap%s(final %s wrapBuffer)\n" +
indent + " {\n" +
"%s" +
+ "%s" +
indent + " wrapBuffer.wrap(buffer, offset + %d, %d);\n" +
indent + " }\n",
Generators.toUpperFirstChar(propertyName),
readOnlyBuffer,
generateWrapFieldNotPresentCondition(propertyToken.version(), indent),
+ accessOrderListenerCall,
offset,
fieldLength);
}
@@ -2260,14 +2960,18 @@ private String byteOrderString(final Encoding encoding)
}
private CharSequence generatePrimitiveArrayPropertyEncode(
- final String containingClassName, final String propertyName, final Token token, final String indent)
+ final String containingClassName,
+ final String propertyName,
+ final CharSequence accessOrderListenerCall,
+ final Token typeToken,
+ final String indent)
{
- final Encoding encoding = token.encoding();
+ final Encoding encoding = typeToken.encoding();
final PrimitiveType primitiveType = encoding.primitiveType();
final String javaTypeName = javaTypeName(primitiveType);
- final int offset = token.offset();
+ final int offset = typeToken.offset();
final String byteOrderStr = byteOrderString(encoding);
- final int arrayLength = token.arrayLength();
+ final int arrayLength = typeToken.arrayLength();
final int typeSize = sizeOfPrimitive(encoding);
final StringBuilder sb = new StringBuilder();
@@ -2282,6 +2986,7 @@ private CharSequence generatePrimitiveArrayPropertyEncode(
indent + " {\n" +
indent + " throw new IndexOutOfBoundsException(\"index out of range: index=\" + index);\n" +
indent + " }\n\n" +
+ "%s" +
indent + " final int pos = offset + %d + (index * %d);\n" +
indent + " %s;\n\n" +
indent + " return this;\n" +
@@ -2290,6 +2995,7 @@ private CharSequence generatePrimitiveArrayPropertyEncode(
propertyName,
javaTypeName,
arrayLength,
+ accessOrderListenerCall,
offset,
typeSize,
generatePut(primitiveType, "pos", "value", byteOrderStr));
@@ -2310,6 +3016,8 @@ private CharSequence generatePrimitiveArrayPropertyEncode(
sb.append(")\n");
sb.append(indent).append(" {\n");
+ sb.append(accessOrderListenerCall);
+
for (int i = 0; i < arrayLength; i++)
{
final String indexStr = "offset + " + (offset + (typeSize * i));
@@ -2327,12 +3035,25 @@ private CharSequence generatePrimitiveArrayPropertyEncode(
if (primitiveType == PrimitiveType.CHAR)
{
generateCharArrayEncodeMethods(
- containingClassName, propertyName, indent, encoding, offset, arrayLength, sb);
+ containingClassName,
+ propertyName,
+ indent,
+ accessOrderListenerCall,
+ encoding,
+ offset,
+ arrayLength,
+ sb);
}
else if (primitiveType == PrimitiveType.UINT8)
{
generateByteArrayEncodeMethods(
- containingClassName, propertyName, indent, offset, arrayLength, sb);
+ containingClassName,
+ propertyName,
+ indent,
+ accessOrderListenerCall,
+ offset,
+ arrayLength,
+ sb);
}
return sb;
@@ -2342,6 +3063,7 @@ private void generateCharArrayEncodeMethods(
final String containingClassName,
final String propertyName,
final String indent,
+ final CharSequence accessOrderListenerCall,
final Encoding encoding,
final int offset,
final int fieldLength,
@@ -2358,12 +3080,14 @@ private void generateCharArrayEncodeMethods(
indent + " throw new IndexOutOfBoundsException(" +
"\"Copy will go out of range: offset=\" + srcOffset);\n" +
indent + " }\n\n" +
+ "%s" +
indent + " buffer.putBytes(offset + %d, src, srcOffset, length);\n\n" +
indent + " return this;\n" +
indent + " }\n",
formatClassName(containingClassName),
Generators.toUpperFirstChar(propertyName),
fieldLength,
+ accessOrderListenerCall,
offset);
if (isAsciiEncoding(encoding.characterEncoding()))
@@ -2378,6 +3102,7 @@ private void generateCharArrayEncodeMethods(
indent + " throw new IndexOutOfBoundsException(" +
"\"String too large for copy: byte length=\" + srcLength);\n" +
indent + " }\n\n" +
+ "%5$s" +
indent + " buffer.putStringWithoutLengthAscii(offset + %4$d, src);\n\n" +
indent + " for (int start = srcLength; start < length; ++start)\n" +
indent + " {\n" +
@@ -2388,7 +3113,8 @@ private void generateCharArrayEncodeMethods(
formatClassName(containingClassName),
propertyName,
fieldLength,
- offset);
+ offset,
+ accessOrderListenerCall);
new Formatter(sb).format("\n" +
indent + " public %1$s %2$s(final CharSequence src)\n" +
@@ -2400,6 +3126,7 @@ private void generateCharArrayEncodeMethods(
indent + " throw new IndexOutOfBoundsException(" +
"\"CharSequence too large for copy: byte length=\" + srcLength);\n" +
indent + " }\n\n" +
+ "%5$s" +
indent + " buffer.putStringWithoutLengthAscii(offset + %4$d, src);\n\n" +
indent + " for (int start = srcLength; start < length; ++start)\n" +
indent + " {\n" +
@@ -2410,7 +3137,8 @@ private void generateCharArrayEncodeMethods(
formatClassName(containingClassName),
propertyName,
fieldLength,
- offset);
+ offset,
+ accessOrderListenerCall);
}
else
{
@@ -2425,6 +3153,7 @@ private void generateCharArrayEncodeMethods(
indent + " throw new IndexOutOfBoundsException(" +
"\"String too large for copy: byte length=\" + bytes.length);\n" +
indent + " }\n\n" +
+ "%s" +
indent + " buffer.putBytes(offset + %d, bytes, 0, bytes.length);\n\n" +
indent + " for (int start = bytes.length; start < length; ++start)\n" +
indent + " {\n" +
@@ -2436,6 +3165,7 @@ private void generateCharArrayEncodeMethods(
propertyName,
fieldLength,
charset(encoding.characterEncoding()),
+ accessOrderListenerCall,
offset,
offset);
}
@@ -2445,6 +3175,7 @@ private void generateByteArrayEncodeMethods(
final String containingClassName,
final String propertyName,
final String indent,
+ final CharSequence accessOrderListenerCall,
final int offset,
final int fieldLength,
final StringBuilder sb)
@@ -2457,6 +3188,7 @@ private void generateByteArrayEncodeMethods(
indent + " throw new IllegalStateException(" +
"\"length > maxValue for type: \" + length);\n" +
indent + " }\n\n" +
+ "%s" +
indent + " buffer.putBytes(offset + %d, src, srcOffset, length);\n" +
indent + " for (int i = length; i < %d; i++)\n" +
indent + " {\n" +
@@ -2467,6 +3199,7 @@ private void generateByteArrayEncodeMethods(
formatClassName(containingClassName),
Generators.toUpperFirstChar(propertyName),
fieldLength,
+ accessOrderListenerCall,
offset,
fieldLength,
offset);
@@ -2479,6 +3212,7 @@ private void generateByteArrayEncodeMethods(
indent + " throw new IllegalStateException(" +
"\"length > maxValue for type: \" + length);\n" +
indent + " }\n\n" +
+ "%s" +
indent + " buffer.putBytes(offset + %d, src, srcOffset, length);\n" +
indent + " for (int i = length; i < %d; i++)\n" +
indent + " {\n" +
@@ -2490,6 +3224,7 @@ private void generateByteArrayEncodeMethods(
Generators.toUpperFirstChar(propertyName),
fqReadOnlyBuffer,
fieldLength,
+ accessOrderListenerCall,
offset,
fieldLength,
offset);
@@ -2663,67 +3398,90 @@ private CharSequence generateFixedFlyweightCode(
semanticVersion);
}
- private CharSequence generateDecoderFlyweightCode(final String className, final Token token)
+ private CharSequence generateDecoderFlyweightCode(
+ final FieldPrecedenceModel fieldPrecedenceModel,
+ final String className,
+ final Token token)
{
final String headerClassName = formatClassName(ir.headerStructure().tokens().get(0).applicableTypeName());
- final String methods =
- " public " + className + " wrap(\n" +
- " final " + readOnlyBuffer + " buffer,\n" +
- " final int offset,\n" +
- " final int actingBlockLength,\n" +
- " final int actingVersion)\n" +
- " {\n" +
- " if (buffer != this.buffer)\n" +
- " {\n" +
- " this.buffer = buffer;\n" +
- " }\n" +
- " this.initialOffset = offset;\n" +
- " this.offset = offset;\n" +
- " this.actingBlockLength = actingBlockLength;\n" +
- " this.actingVersion = actingVersion;\n" +
- " limit(offset + actingBlockLength);\n\n" +
- " return this;\n" +
- " }\n\n" +
+ final StringBuilder methods = new StringBuilder();
- " public " + className + " wrapAndApplyHeader(\n" +
- " final " + readOnlyBuffer + " buffer,\n" +
- " final int offset,\n" +
- " final " + headerClassName + "Decoder headerDecoder)\n" +
- " {\n" +
- " headerDecoder.wrap(buffer, offset);\n\n" +
- " final int templateId = headerDecoder.templateId();\n" +
- " if (TEMPLATE_ID != templateId)\n" +
- " {\n" +
- " throw new IllegalStateException(\"Invalid TEMPLATE_ID: \" + templateId);\n" +
- " }\n\n" +
- " return wrap(\n" +
- " buffer,\n" +
- " offset + " + headerClassName + "Decoder.ENCODED_LENGTH,\n" +
- " headerDecoder.blockLength(),\n" +
- " headerDecoder.version());\n" +
- " }\n\n" +
+ methods.append(generateDecoderWrapListener(fieldPrecedenceModel, " "));
- " public " + className + " sbeRewind()\n" +
- " {\n" +
- " return wrap(buffer, initialOffset, actingBlockLength, actingVersion);\n" +
- " }\n\n" +
+ methods.append(" public ").append(className).append(" wrap(\n")
+ .append(" final ").append(readOnlyBuffer).append(" buffer,\n")
+ .append(" final int offset,\n")
+ .append(" final int actingBlockLength,\n")
+ .append(" final int actingVersion)\n")
+ .append(" {\n")
+ .append(" if (buffer != this.buffer)\n")
+ .append(" {\n")
+ .append(" this.buffer = buffer;\n")
+ .append(" }\n")
+ .append(" this.initialOffset = offset;\n")
+ .append(" this.offset = offset;\n")
+ .append(" this.actingBlockLength = actingBlockLength;\n")
+ .append(" this.actingVersion = actingVersion;\n")
+ .append(" limit(offset + actingBlockLength);\n\n")
+ .append(generateAccessOrderListenerCall(fieldPrecedenceModel, " ", "onWrap", "actingVersion"))
+ .append(" return this;\n")
+ .append(" }\n\n");
- " public int sbeDecodedLength()\n" +
- " {\n" +
- " final int currentLimit = limit();\n" +
- " sbeSkip();\n" +
- " final int decodedLength = encodedLength();\n" +
- " limit(currentLimit);\n\n" +
- " return decodedLength;\n" +
- " }\n\n" +
+ methods.append(" public ").append(className).append(" wrapAndApplyHeader(\n")
+ .append(" final ").append(readOnlyBuffer).append(" buffer,\n")
+ .append(" final int offset,\n")
+ .append(" final ").append(headerClassName).append("Decoder headerDecoder)\n")
+ .append(" {\n")
+ .append(" headerDecoder.wrap(buffer, offset);\n\n")
+ .append(" final int templateId = headerDecoder.templateId();\n")
+ .append(" if (TEMPLATE_ID != templateId)\n")
+ .append(" {\n")
+ .append(" throw new IllegalStateException(\"Invalid TEMPLATE_ID: \" + templateId);\n")
+ .append(" }\n\n")
+ .append(" return wrap(\n")
+ .append(" buffer,\n")
+ .append(" offset + ").append(headerClassName).append("Decoder.ENCODED_LENGTH,\n")
+ .append(" headerDecoder.blockLength(),\n")
+ .append(" headerDecoder.version());\n")
+ .append(" }\n\n");
+
+ methods.append(" public ").append(className).append(" sbeRewind()\n")
+ .append(" {\n")
+ .append(" return wrap(buffer, initialOffset, actingBlockLength, actingVersion);\n")
+ .append(" }\n\n");
- " public int actingVersion()\n" +
- " {\n" +
- " return actingVersion;\n" +
- " }\n\n";
+ methods.append(" public int sbeDecodedLength()\n")
+ .append(" {\n")
+ .append(" final int currentLimit = limit();\n");
- return generateFlyweightCode(DECODER, className, token, methods, readOnlyBuffer);
+ if (null != fieldPrecedenceModel)
+ {
+ methods.append(" final int currentCodecState = codecState();\n");
+ }
+
+ methods
+ .append(" sbeSkip();\n")
+ .append(" final int decodedLength = encodedLength();\n")
+ .append(" limit(currentLimit);\n\n");
+
+ if (null != fieldPrecedenceModel)
+ {
+ methods.append(" if (").append(precedenceChecksFlagName).append(")\n")
+ .append(" {\n")
+ .append(" codecState(currentCodecState);\n")
+ .append(" }\n\n");
+ }
+
+ methods.append(" return decodedLength;\n")
+ .append(" }\n\n");
+
+ methods.append(" public int actingVersion()\n")
+ .append(" {\n")
+ .append(" return actingVersion;\n")
+ .append(" }\n\n");
+
+ return generateFlyweightCode(DECODER, className, token, methods.toString(), readOnlyBuffer);
}
private CharSequence generateFlyweightCode(
@@ -2829,7 +3587,10 @@ private CharSequence generateFlyweightCode(
semanticVersion);
}
- private CharSequence generateEncoderFlyweightCode(final String className, final Token token)
+ private CharSequence generateEncoderFlyweightCode(
+ final String className,
+ final FieldPrecedenceModel fieldPrecedenceModel,
+ final Token token)
{
final String wrapMethod =
" public " + className + " wrap(final " + mutableBuffer + " buffer, final int offset)\n" +
@@ -2841,6 +3602,7 @@ private CharSequence generateEncoderFlyweightCode(final String className, final
" this.initialOffset = offset;\n" +
" this.offset = offset;\n" +
" limit(offset + BLOCK_LENGTH);\n\n" +
+ generateEncoderWrapListener(fieldPrecedenceModel, " ") +
" return this;\n" +
" }\n\n";
@@ -2889,7 +3651,11 @@ private CharSequence generateEncoderFlyweightCode(final String className, final
}
private void generateEncoderFields(
- final StringBuilder sb, final String containingClassName, final List tokens, final String indent)
+ final StringBuilder sb,
+ final String containingClassName,
+ final FieldPrecedenceModel fieldPrecedenceModel,
+ final List tokens,
+ final String indent)
{
Generators.forEachField(
tokens,
@@ -2903,25 +3669,32 @@ private void generateEncoderFields(
generateEncodingOffsetMethod(sb, propertyName, fieldToken.offset(), indent);
generateEncodingLengthMethod(sb, propertyName, typeToken.encodedLength(), indent);
generateFieldMetaAttributeMethod(sb, fieldToken, indent);
+ generateAccessOrderListenerMethod(sb, fieldPrecedenceModel, indent + " ", fieldToken);
+ final CharSequence accessOrderListenerCall = generateAccessOrderListenerCall(
+ fieldPrecedenceModel, indent + " ", fieldToken);
switch (typeToken.signal())
{
case ENCODING:
- generatePrimitiveEncoder(sb, containingClassName, propertyName, typeToken, indent);
+ generatePrimitiveEncoder(sb, containingClassName, propertyName,
+ accessOrderListenerCall, typeToken, indent);
break;
case BEGIN_ENUM:
- generateEnumEncoder(sb, containingClassName, fieldToken, propertyName, typeToken, indent);
+ generateEnumEncoder(sb, containingClassName,
+ accessOrderListenerCall, fieldToken, propertyName, typeToken, indent);
break;
case BEGIN_SET:
generateBitSetProperty(
- sb, false, ENCODER, propertyName, fieldToken, typeToken, indent, typeName);
+ sb, false, ENCODER, propertyName, accessOrderListenerCall,
+ fieldToken, typeToken, indent, typeName);
break;
case BEGIN_COMPOSITE:
generateCompositeProperty(
- sb, false, ENCODER, propertyName, fieldToken, typeToken, indent, typeName);
+ sb, false, ENCODER, propertyName, accessOrderListenerCall,
+ fieldToken, typeToken, indent, typeName);
break;
default:
@@ -2930,7 +3703,11 @@ private void generateEncoderFields(
});
}
- private void generateDecoderFields(final StringBuilder sb, final List tokens, final String indent)
+ private void generateDecoderFields(
+ final StringBuilder sb,
+ final FieldPrecedenceModel fieldPrecedenceModel,
+ final List tokens,
+ final String indent)
{
Generators.forEachField(
tokens,
@@ -2945,24 +3722,32 @@ private void generateDecoderFields(final StringBuilder sb, final List tok
generateEncodingLengthMethod(sb, propertyName, typeToken.encodedLength(), indent);
generateFieldMetaAttributeMethod(sb, fieldToken, indent);
+ generateAccessOrderListenerMethod(sb, fieldPrecedenceModel, indent + " ", fieldToken);
+ final CharSequence accessOrderListenerCall = generateAccessOrderListenerCall(
+ fieldPrecedenceModel, indent + " ", fieldToken);
+
switch (typeToken.signal())
{
case ENCODING:
- generatePrimitiveDecoder(sb, false, propertyName, fieldToken, typeToken, indent);
+ generatePrimitiveDecoder(
+ sb, false, propertyName, accessOrderListenerCall, fieldToken, typeToken, indent);
break;
case BEGIN_ENUM:
- generateEnumDecoder(sb, false, fieldToken, propertyName, typeToken, indent);
+ generateEnumDecoder(
+ sb, false, accessOrderListenerCall, fieldToken, propertyName, typeToken, indent);
break;
case BEGIN_SET:
generateBitSetProperty(
- sb, false, DECODER, propertyName, fieldToken, typeToken, indent, typeName);
+ sb, false, DECODER, propertyName, accessOrderListenerCall,
+ fieldToken, typeToken, indent, typeName);
break;
case BEGIN_COMPOSITE:
generateCompositeProperty(
- sb, false, DECODER, propertyName, fieldToken, typeToken, indent, typeName);
+ sb, false, DECODER, propertyName, accessOrderListenerCall,
+ fieldToken, typeToken, indent, typeName);
break;
default:
@@ -3063,6 +3848,7 @@ private static void generateFieldMetaAttributeMethod(final StringBuilder sb, fin
private void generateEnumDecoder(
final StringBuilder sb,
final boolean inComposite,
+ final CharSequence accessOrderListenerCall,
final Token fieldToken,
final String propertyName,
final Token typeToken,
@@ -3106,11 +3892,13 @@ private void generateEnumDecoder(
indent + " public %s %sRaw()\n" +
indent + " {\n" +
"%s" +
+ "%s" +
indent + " return %s;\n" +
indent + " }\n",
javaTypeName,
formatPropertyName(propertyName),
generateFieldNotPresentCondition(inComposite, fieldToken.version(), encoding, indent),
+ accessOrderListenerCall,
rawGetStr);
new Formatter(sb).format(
@@ -3118,11 +3906,13 @@ private void generateEnumDecoder(
indent + " public %s %s()\n" +
indent + " {\n" +
"%s" +
+ "%s" +
indent + " return %s.get(%s);\n" +
indent + " }\n\n",
enumName,
propertyName,
generatePropertyNotPresentCondition(inComposite, DECODER, fieldToken, enumName, indent),
+ accessOrderListenerCall,
enumName,
rawGetStr);
}
@@ -3131,6 +3921,7 @@ private void generateEnumDecoder(
private void generateEnumEncoder(
final StringBuilder sb,
final String containingClassName,
+ final CharSequence accessOrderListenerCall,
final Token fieldToken,
final String propertyName,
final Token typeToken,
@@ -3146,12 +3937,14 @@ private void generateEnumEncoder(
new Formatter(sb).format("\n" +
indent + " public %s %s(final %s value)\n" +
indent + " {\n" +
+ "%s" +
indent + " %s;\n" +
indent + " return this;\n" +
indent + " }\n",
formatClassName(containingClassName),
propertyName,
enumName,
+ accessOrderListenerCall,
generatePut(encoding.primitiveType(), "offset + " + offset, "value.value()", byteOrderString));
}
}
@@ -3161,6 +3954,7 @@ private void generateBitSetProperty(
final boolean inComposite,
final CodecType codecType,
final String propertyName,
+ final CharSequence accessOrderListenerCall,
final Token propertyToken,
final Token bitsetToken,
final String indent,
@@ -3177,12 +3971,14 @@ private void generateBitSetProperty(
indent + " public %s %s()\n" +
indent + " {\n" +
"%s" +
+ "%s" +
indent + " %s.wrap(buffer, offset + %d);\n" +
indent + " return %s;\n" +
indent + " }\n",
bitSetName,
propertyName,
generatePropertyNotPresentCondition(inComposite, codecType, propertyToken, null, indent),
+ accessOrderListenerCall,
propertyName,
bitsetToken.offset(),
propertyName);
@@ -3193,6 +3989,7 @@ private void generateCompositeProperty(
final boolean inComposite,
final CodecType codecType,
final String propertyName,
+ final CharSequence accessOrderListenerCall,
final Token propertyToken,
final Token compositeToken,
final String indent,
@@ -3209,12 +4006,14 @@ private void generateCompositeProperty(
indent + " public %s %s()\n" +
indent + " {\n" +
"%s" +
+ "%s" +
indent + " %s.wrap(buffer, offset + %d);\n" +
indent + " return %s;\n" +
indent + " }\n",
compositeName,
propertyName,
generatePropertyNotPresentCondition(inComposite, codecType, propertyToken, null, indent),
+ accessOrderListenerCall,
propertyName,
compositeToken.offset(),
propertyName);
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 8cbdc11954..89962bd61f 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
@@ -11,6 +11,83 @@
@SuppressWarnings("all")
public final class FrameCodecDecoder
{
+ private static final boolean ENABLE_BOUNDS_CHECKS = !Boolean.getBoolean("agrona.disable.bounds.checks");
+
+ private static final boolean SBE_ENABLE_IR_PRECEDENCE_CHECKS = Boolean.parseBoolean(System.getProperty(
+ "sbe.enable.ir.precedence.checks",
+ Boolean.toString(ENABLE_BOUNDS_CHECKS)));
+
+ /**
+ * The states in which a encoder/decoder/codec can live.
+ *
+ * The state machine diagram below, encoded in the dot language, describes
+ * the valid state transitions according to the order in which fields may be
+ * accessed safely. Tools such as PlantUML and Graphviz can render it.
+ *
+ *
{@code
+ * digraph G {
+ * NOT_WRAPPED -> V0_BLOCK [label=" wrap(version=0) "];
+ * V0_BLOCK -> V0_BLOCK [label=" irId(?) "];
+ * V0_BLOCK -> V0_BLOCK [label=" irVersion(?) "];
+ * V0_BLOCK -> V0_BLOCK [label=" schemaVersion(?) "];
+ * V0_BLOCK -> V0_BLOCK [label=" packageNameLength() "];
+ * V0_BLOCK -> V0_PACKAGENAME_DONE [label=" packageName(?) "];
+ * V0_PACKAGENAME_DONE -> V0_PACKAGENAME_DONE [label=" namespaceNameLength() "];
+ * V0_PACKAGENAME_DONE -> V0_NAMESPACENAME_DONE [label=" namespaceName(?) "];
+ * V0_NAMESPACENAME_DONE -> V0_NAMESPACENAME_DONE [label=" semanticVersionLength() "];
+ * V0_NAMESPACENAME_DONE -> V0_SEMANTICVERSION_DONE [label=" semanticVersion(?) "];
+ * }
+ * }
+ */
+ private static class CodecStates
+ {
+ private static final int NOT_WRAPPED = 0;
+ private static final int V0_BLOCK = 1;
+ private static final int V0_PACKAGENAME_DONE = 2;
+ private static final int V0_NAMESPACENAME_DONE = 3;
+ private static final int V0_SEMANTICVERSION_DONE = 4;
+
+ private static final String[] STATE_NAME_LOOKUP =
+ {
+ "NOT_WRAPPED",
+ "V0_BLOCK",
+ "V0_PACKAGENAME_DONE",
+ "V0_NAMESPACENAME_DONE",
+ "V0_SEMANTICVERSION_DONE",
+ };
+
+ private static final String[] STATE_TRANSITIONS_LOOKUP =
+ {
+ "\"wrap(version=0)\"",
+ "\"irId(?)\", \"irVersion(?)\", \"schemaVersion(?)\", \"packageNameLength()\", \"packageName(?)\"",
+ "\"namespaceNameLength()\", \"namespaceName(?)\"",
+ "\"semanticVersionLength()\", \"semanticVersion(?)\"",
+ "",
+ };
+
+ private static String name(final int state)
+ {
+ return STATE_NAME_LOOKUP[state];
+ }
+
+ private static String transitions(final int state)
+ {
+ return STATE_TRANSITIONS_LOOKUP[state];
+ }
+ }
+
+ private int codecState = CodecStates.NOT_WRAPPED;
+
+ private int codecState()
+ {
+ return codecState;
+ }
+
+ private void codecState(int newState)
+ {
+ codecState = newState;
+ }
+
public static final int BLOCK_LENGTH = 12;
public static final int TEMPLATE_ID = 1;
public static final int SCHEMA_ID = 1;
@@ -66,6 +143,19 @@ public int offset()
return offset;
}
+ private void onWrap(final int actingVersion)
+ {
+ switch(actingVersion)
+ {
+ case 0:
+ codecState(CodecStates.V0_BLOCK);
+ break;
+ default:
+ codecState(CodecStates.V0_BLOCK);
+ break;
+ }
+ }
+
public FrameCodecDecoder wrap(
final DirectBuffer buffer,
final int offset,
@@ -82,6 +172,11 @@ public FrameCodecDecoder wrap(
this.actingVersion = actingVersion;
limit(offset + actingBlockLength);
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onWrap(actingVersion);
+ }
+
return this;
}
@@ -113,10 +208,16 @@ public FrameCodecDecoder sbeRewind()
public int sbeDecodedLength()
{
final int currentLimit = limit();
+ final int currentCodecState = codecState();
sbeSkip();
final int decodedLength = encodedLength();
limit(currentLimit);
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ codecState(currentCodecState);
+ }
+
return decodedLength;
}
@@ -170,6 +271,17 @@ public static String irIdMetaAttribute(final MetaAttribute metaAttribute)
return "";
}
+ private void onIrIdAccessed()
+ {
+ if (codecState() == CodecStates.NOT_WRAPPED)
+ {
+ throw new IllegalStateException("Illegal field access order. " +
+ "Cannot access field \"irId\" in state: " + CodecStates.name(codecState()) +
+ ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) +
+ "]. Please see the diagram in the Javadoc of the class FrameCodecDecoder#CodecStates.");
+ }
+ }
+
public static int irIdNullValue()
{
return -2147483648;
@@ -187,6 +299,11 @@ public static int irIdMaxValue()
public int irId()
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onIrIdAccessed();
+ }
+
return buffer.getInt(offset + 0, java.nio.ByteOrder.LITTLE_ENDIAN);
}
@@ -221,6 +338,17 @@ public static String irVersionMetaAttribute(final MetaAttribute metaAttribute)
return "";
}
+ private void onIrVersionAccessed()
+ {
+ if (codecState() == CodecStates.NOT_WRAPPED)
+ {
+ throw new IllegalStateException("Illegal field access order. " +
+ "Cannot access field \"irVersion\" in state: " + CodecStates.name(codecState()) +
+ ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) +
+ "]. Please see the diagram in the Javadoc of the class FrameCodecDecoder#CodecStates.");
+ }
+ }
+
public static int irVersionNullValue()
{
return -2147483648;
@@ -238,6 +366,11 @@ public static int irVersionMaxValue()
public int irVersion()
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onIrVersionAccessed();
+ }
+
return buffer.getInt(offset + 4, java.nio.ByteOrder.LITTLE_ENDIAN);
}
@@ -272,6 +405,17 @@ public static String schemaVersionMetaAttribute(final MetaAttribute metaAttribut
return "";
}
+ private void onSchemaVersionAccessed()
+ {
+ if (codecState() == CodecStates.NOT_WRAPPED)
+ {
+ throw new IllegalStateException("Illegal field access order. " +
+ "Cannot access field \"schemaVersion\" in state: " + CodecStates.name(codecState()) +
+ ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) +
+ "]. Please see the diagram in the Javadoc of the class FrameCodecDecoder#CodecStates.");
+ }
+ }
+
public static int schemaVersionNullValue()
{
return -2147483648;
@@ -289,6 +433,11 @@ public static int schemaVersionMaxValue()
public int schemaVersion()
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onSchemaVersionAccessed();
+ }
+
return buffer.getInt(offset + 8, java.nio.ByteOrder.LITTLE_ENDIAN);
}
@@ -323,14 +472,54 @@ public static int packageNameHeaderLength()
return 2;
}
+ void onPackageNameLengthAccessed()
+ {
+ switch (codecState())
+ {
+ case CodecStates.V0_BLOCK:
+ codecState(CodecStates.V0_BLOCK);
+ break;
+ default:
+ throw new IllegalStateException("Illegal field access order. " +
+ "Cannot decode length of var data \"packageName\" in state: " + CodecStates.name(codecState()) +
+ ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) +
+ "]. Please see the diagram in the Javadoc of the class FrameCodecDecoder#CodecStates.");
+ }
+ }
+
+ private void onPackageNameAccessed()
+ {
+ switch (codecState())
+ {
+ case CodecStates.V0_BLOCK:
+ codecState(CodecStates.V0_PACKAGENAME_DONE);
+ break;
+ default:
+ throw new IllegalStateException("Illegal field access order. " +
+ "Cannot access field \"packageName\" in state: " + CodecStates.name(codecState()) +
+ ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) +
+ "]. Please see the diagram in the Javadoc of the class FrameCodecDecoder#CodecStates.");
+ }
+ }
+
public int packageNameLength()
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onPackageNameLengthAccessed();
+ }
+
final int limit = parentMessage.limit();
return (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
}
public int skipPackageName()
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onPackageNameAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
@@ -342,6 +531,11 @@ public int skipPackageName()
public int getPackageName(final MutableDirectBuffer dst, final int dstOffset, final int length)
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onPackageNameAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
@@ -354,6 +548,11 @@ public int getPackageName(final MutableDirectBuffer dst, final int dstOffset, fi
public int getPackageName(final byte[] dst, final int dstOffset, final int length)
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onPackageNameAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
@@ -366,6 +565,11 @@ public int getPackageName(final byte[] dst, final int dstOffset, final int lengt
public void wrapPackageName(final DirectBuffer wrapBuffer)
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onPackageNameAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
@@ -375,6 +579,11 @@ public void wrapPackageName(final DirectBuffer wrapBuffer)
public String packageName()
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onPackageNameAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
@@ -421,14 +630,54 @@ public static int namespaceNameHeaderLength()
return 2;
}
+ void onNamespaceNameLengthAccessed()
+ {
+ switch (codecState())
+ {
+ case CodecStates.V0_PACKAGENAME_DONE:
+ codecState(CodecStates.V0_PACKAGENAME_DONE);
+ break;
+ default:
+ throw new IllegalStateException("Illegal field access order. " +
+ "Cannot decode length of var data \"namespaceName\" in state: " + CodecStates.name(codecState()) +
+ ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) +
+ "]. Please see the diagram in the Javadoc of the class FrameCodecDecoder#CodecStates.");
+ }
+ }
+
+ private void onNamespaceNameAccessed()
+ {
+ switch (codecState())
+ {
+ case CodecStates.V0_PACKAGENAME_DONE:
+ codecState(CodecStates.V0_NAMESPACENAME_DONE);
+ break;
+ default:
+ throw new IllegalStateException("Illegal field access order. " +
+ "Cannot access field \"namespaceName\" in state: " + CodecStates.name(codecState()) +
+ ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) +
+ "]. Please see the diagram in the Javadoc of the class FrameCodecDecoder#CodecStates.");
+ }
+ }
+
public int namespaceNameLength()
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onNamespaceNameLengthAccessed();
+ }
+
final int limit = parentMessage.limit();
return (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
}
public int skipNamespaceName()
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onNamespaceNameAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
@@ -440,6 +689,11 @@ public int skipNamespaceName()
public int getNamespaceName(final MutableDirectBuffer dst, final int dstOffset, final int length)
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onNamespaceNameAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
@@ -452,6 +706,11 @@ public int getNamespaceName(final MutableDirectBuffer dst, final int dstOffset,
public int getNamespaceName(final byte[] dst, final int dstOffset, final int length)
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onNamespaceNameAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
@@ -464,6 +723,11 @@ public int getNamespaceName(final byte[] dst, final int dstOffset, final int len
public void wrapNamespaceName(final DirectBuffer wrapBuffer)
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onNamespaceNameAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
@@ -473,6 +737,11 @@ public void wrapNamespaceName(final DirectBuffer wrapBuffer)
public String namespaceName()
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onNamespaceNameAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
@@ -519,14 +788,54 @@ public static int semanticVersionHeaderLength()
return 2;
}
+ void onSemanticVersionLengthAccessed()
+ {
+ switch (codecState())
+ {
+ case CodecStates.V0_NAMESPACENAME_DONE:
+ codecState(CodecStates.V0_NAMESPACENAME_DONE);
+ break;
+ default:
+ throw new IllegalStateException("Illegal field access order. " +
+ "Cannot decode length of var data \"semanticVersion\" in state: " + CodecStates.name(codecState()) +
+ ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) +
+ "]. Please see the diagram in the Javadoc of the class FrameCodecDecoder#CodecStates.");
+ }
+ }
+
+ private void onSemanticVersionAccessed()
+ {
+ switch (codecState())
+ {
+ case CodecStates.V0_NAMESPACENAME_DONE:
+ codecState(CodecStates.V0_SEMANTICVERSION_DONE);
+ break;
+ default:
+ throw new IllegalStateException("Illegal field access order. " +
+ "Cannot access field \"semanticVersion\" in state: " + CodecStates.name(codecState()) +
+ ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) +
+ "]. Please see the diagram in the Javadoc of the class FrameCodecDecoder#CodecStates.");
+ }
+ }
+
public int semanticVersionLength()
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onSemanticVersionLengthAccessed();
+ }
+
final int limit = parentMessage.limit();
return (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
}
public int skipSemanticVersion()
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onSemanticVersionAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
@@ -538,6 +847,11 @@ public int skipSemanticVersion()
public int getSemanticVersion(final MutableDirectBuffer dst, final int dstOffset, final int length)
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onSemanticVersionAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
@@ -550,6 +864,11 @@ public int getSemanticVersion(final MutableDirectBuffer dst, final int dstOffset
public int getSemanticVersion(final byte[] dst, final int dstOffset, final int length)
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onSemanticVersionAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
@@ -562,6 +881,11 @@ public int getSemanticVersion(final byte[] dst, final int dstOffset, final int l
public void wrapSemanticVersion(final DirectBuffer wrapBuffer)
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onSemanticVersionAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
@@ -571,6 +895,11 @@ public void wrapSemanticVersion(final DirectBuffer wrapBuffer)
public String semanticVersion()
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onSemanticVersionAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecEncoder.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecEncoder.java
index 0fd115d556..a5524033fc 100644
--- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecEncoder.java
+++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/FrameCodecEncoder.java
@@ -11,6 +11,83 @@
@SuppressWarnings("all")
public final class FrameCodecEncoder
{
+ private static final boolean ENABLE_BOUNDS_CHECKS = !Boolean.getBoolean("agrona.disable.bounds.checks");
+
+ private static final boolean SBE_ENABLE_IR_PRECEDENCE_CHECKS = Boolean.parseBoolean(System.getProperty(
+ "sbe.enable.ir.precedence.checks",
+ Boolean.toString(ENABLE_BOUNDS_CHECKS)));
+
+ /**
+ * The states in which a encoder/decoder/codec can live.
+ *
+ * The state machine diagram below, encoded in the dot language, describes
+ * the valid state transitions according to the order in which fields may be
+ * accessed safely. Tools such as PlantUML and Graphviz can render it.
+ *
+ *
{@code
+ * digraph G {
+ * NOT_WRAPPED -> V0_BLOCK [label=" wrap(version=0) "];
+ * V0_BLOCK -> V0_BLOCK [label=" irId(?) "];
+ * V0_BLOCK -> V0_BLOCK [label=" irVersion(?) "];
+ * V0_BLOCK -> V0_BLOCK [label=" schemaVersion(?) "];
+ * V0_BLOCK -> V0_BLOCK [label=" packageNameLength() "];
+ * V0_BLOCK -> V0_PACKAGENAME_DONE [label=" packageName(?) "];
+ * V0_PACKAGENAME_DONE -> V0_PACKAGENAME_DONE [label=" namespaceNameLength() "];
+ * V0_PACKAGENAME_DONE -> V0_NAMESPACENAME_DONE [label=" namespaceName(?) "];
+ * V0_NAMESPACENAME_DONE -> V0_NAMESPACENAME_DONE [label=" semanticVersionLength() "];
+ * V0_NAMESPACENAME_DONE -> V0_SEMANTICVERSION_DONE [label=" semanticVersion(?) "];
+ * }
+ * }
+ */
+ private static class CodecStates
+ {
+ private static final int NOT_WRAPPED = 0;
+ private static final int V0_BLOCK = 1;
+ private static final int V0_PACKAGENAME_DONE = 2;
+ private static final int V0_NAMESPACENAME_DONE = 3;
+ private static final int V0_SEMANTICVERSION_DONE = 4;
+
+ private static final String[] STATE_NAME_LOOKUP =
+ {
+ "NOT_WRAPPED",
+ "V0_BLOCK",
+ "V0_PACKAGENAME_DONE",
+ "V0_NAMESPACENAME_DONE",
+ "V0_SEMANTICVERSION_DONE",
+ };
+
+ private static final String[] STATE_TRANSITIONS_LOOKUP =
+ {
+ "\"wrap(version=0)\"",
+ "\"irId(?)\", \"irVersion(?)\", \"schemaVersion(?)\", \"packageNameLength()\", \"packageName(?)\"",
+ "\"namespaceNameLength()\", \"namespaceName(?)\"",
+ "\"semanticVersionLength()\", \"semanticVersion(?)\"",
+ "",
+ };
+
+ private static String name(final int state)
+ {
+ return STATE_NAME_LOOKUP[state];
+ }
+
+ private static String transitions(final int state)
+ {
+ return STATE_TRANSITIONS_LOOKUP[state];
+ }
+ }
+
+ private int codecState = CodecStates.NOT_WRAPPED;
+
+ private int codecState()
+ {
+ return codecState;
+ }
+
+ private void codecState(int newState)
+ {
+ codecState = newState;
+ }
+
public static final int BLOCK_LENGTH = 12;
public static final int TEMPLATE_ID = 1;
public static final int SCHEMA_ID = 1;
@@ -74,6 +151,11 @@ public FrameCodecEncoder wrap(final MutableDirectBuffer buffer, final int offset
this.offset = offset;
limit(offset + BLOCK_LENGTH);
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ codecState(CodecStates.V0_BLOCK);
+ }
+
return this;
}
@@ -135,6 +217,17 @@ public static String irIdMetaAttribute(final MetaAttribute metaAttribute)
return "";
}
+ private void onIrIdAccessed()
+ {
+ if (codecState() == CodecStates.NOT_WRAPPED)
+ {
+ throw new IllegalStateException("Illegal field access order. " +
+ "Cannot access field \"irId\" in state: " + CodecStates.name(codecState()) +
+ ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) +
+ "]. Please see the diagram in the Javadoc of the class FrameCodecEncoder#CodecStates.");
+ }
+ }
+
public static int irIdNullValue()
{
return -2147483648;
@@ -152,6 +245,11 @@ public static int irIdMaxValue()
public FrameCodecEncoder irId(final int value)
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onIrIdAccessed();
+ }
+
buffer.putInt(offset + 0, value, java.nio.ByteOrder.LITTLE_ENDIAN);
return this;
}
@@ -187,6 +285,17 @@ public static String irVersionMetaAttribute(final MetaAttribute metaAttribute)
return "";
}
+ private void onIrVersionAccessed()
+ {
+ if (codecState() == CodecStates.NOT_WRAPPED)
+ {
+ throw new IllegalStateException("Illegal field access order. " +
+ "Cannot access field \"irVersion\" in state: " + CodecStates.name(codecState()) +
+ ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) +
+ "]. Please see the diagram in the Javadoc of the class FrameCodecEncoder#CodecStates.");
+ }
+ }
+
public static int irVersionNullValue()
{
return -2147483648;
@@ -204,6 +313,11 @@ public static int irVersionMaxValue()
public FrameCodecEncoder irVersion(final int value)
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onIrVersionAccessed();
+ }
+
buffer.putInt(offset + 4, value, java.nio.ByteOrder.LITTLE_ENDIAN);
return this;
}
@@ -239,6 +353,17 @@ public static String schemaVersionMetaAttribute(final MetaAttribute metaAttribut
return "";
}
+ private void onSchemaVersionAccessed()
+ {
+ if (codecState() == CodecStates.NOT_WRAPPED)
+ {
+ throw new IllegalStateException("Illegal field access order. " +
+ "Cannot access field \"schemaVersion\" in state: " + CodecStates.name(codecState()) +
+ ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) +
+ "]. Please see the diagram in the Javadoc of the class FrameCodecEncoder#CodecStates.");
+ }
+ }
+
public static int schemaVersionNullValue()
{
return -2147483648;
@@ -256,6 +381,11 @@ public static int schemaVersionMaxValue()
public FrameCodecEncoder schemaVersion(final int value)
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onSchemaVersionAccessed();
+ }
+
buffer.putInt(offset + 8, value, java.nio.ByteOrder.LITTLE_ENDIAN);
return this;
}
@@ -286,6 +416,21 @@ public static int packageNameHeaderLength()
return 2;
}
+ private void onPackageNameAccessed()
+ {
+ switch (codecState())
+ {
+ case CodecStates.V0_BLOCK:
+ codecState(CodecStates.V0_PACKAGENAME_DONE);
+ break;
+ default:
+ throw new IllegalStateException("Illegal field access order. " +
+ "Cannot access field \"packageName\" in state: " + CodecStates.name(codecState()) +
+ ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) +
+ "]. Please see the diagram in the Javadoc of the class FrameCodecEncoder#CodecStates.");
+ }
+ }
+
public FrameCodecEncoder putPackageName(final DirectBuffer src, final int srcOffset, final int length)
{
if (length > 65534)
@@ -293,6 +438,11 @@ public FrameCodecEncoder putPackageName(final DirectBuffer src, final int srcOff
throw new IllegalStateException("length > maxValue for type: " + length);
}
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onPackageNameAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
parentMessage.limit(limit + headerLength + length);
@@ -309,6 +459,11 @@ public FrameCodecEncoder putPackageName(final byte[] src, final int srcOffset, f
throw new IllegalStateException("length > maxValue for type: " + length);
}
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onPackageNameAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
parentMessage.limit(limit + headerLength + length);
@@ -328,6 +483,11 @@ public FrameCodecEncoder packageName(final String value)
throw new IllegalStateException("length > maxValue for type: " + length);
}
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onPackageNameAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
parentMessage.limit(limit + headerLength + length);
@@ -362,6 +522,21 @@ public static int namespaceNameHeaderLength()
return 2;
}
+ private void onNamespaceNameAccessed()
+ {
+ switch (codecState())
+ {
+ case CodecStates.V0_PACKAGENAME_DONE:
+ codecState(CodecStates.V0_NAMESPACENAME_DONE);
+ break;
+ default:
+ throw new IllegalStateException("Illegal field access order. " +
+ "Cannot access field \"namespaceName\" in state: " + CodecStates.name(codecState()) +
+ ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) +
+ "]. Please see the diagram in the Javadoc of the class FrameCodecEncoder#CodecStates.");
+ }
+ }
+
public FrameCodecEncoder putNamespaceName(final DirectBuffer src, final int srcOffset, final int length)
{
if (length > 65534)
@@ -369,6 +544,11 @@ public FrameCodecEncoder putNamespaceName(final DirectBuffer src, final int srcO
throw new IllegalStateException("length > maxValue for type: " + length);
}
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onNamespaceNameAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
parentMessage.limit(limit + headerLength + length);
@@ -385,6 +565,11 @@ public FrameCodecEncoder putNamespaceName(final byte[] src, final int srcOffset,
throw new IllegalStateException("length > maxValue for type: " + length);
}
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onNamespaceNameAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
parentMessage.limit(limit + headerLength + length);
@@ -404,6 +589,11 @@ public FrameCodecEncoder namespaceName(final String value)
throw new IllegalStateException("length > maxValue for type: " + length);
}
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onNamespaceNameAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
parentMessage.limit(limit + headerLength + length);
@@ -438,6 +628,21 @@ public static int semanticVersionHeaderLength()
return 2;
}
+ private void onSemanticVersionAccessed()
+ {
+ switch (codecState())
+ {
+ case CodecStates.V0_NAMESPACENAME_DONE:
+ codecState(CodecStates.V0_SEMANTICVERSION_DONE);
+ break;
+ default:
+ throw new IllegalStateException("Illegal field access order. " +
+ "Cannot access field \"semanticVersion\" in state: " + CodecStates.name(codecState()) +
+ ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) +
+ "]. Please see the diagram in the Javadoc of the class FrameCodecEncoder#CodecStates.");
+ }
+ }
+
public FrameCodecEncoder putSemanticVersion(final DirectBuffer src, final int srcOffset, final int length)
{
if (length > 65534)
@@ -445,6 +650,11 @@ public FrameCodecEncoder putSemanticVersion(final DirectBuffer src, final int sr
throw new IllegalStateException("length > maxValue for type: " + length);
}
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onSemanticVersionAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
parentMessage.limit(limit + headerLength + length);
@@ -461,6 +671,11 @@ public FrameCodecEncoder putSemanticVersion(final byte[] src, final int srcOffse
throw new IllegalStateException("length > maxValue for type: " + length);
}
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onSemanticVersionAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
parentMessage.limit(limit + headerLength + length);
@@ -480,6 +695,11 @@ public FrameCodecEncoder semanticVersion(final String value)
throw new IllegalStateException("length > maxValue for type: " + length);
}
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onSemanticVersionAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
parentMessage.limit(limit + headerLength + length);
@@ -511,4 +731,21 @@ public StringBuilder appendTo(final StringBuilder builder)
return decoder.appendTo(builder);
}
+
+ public void checkEncodingIsComplete()
+ {
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ switch (codecState)
+ {
+ case CodecStates.V0_SEMANTICVERSION_DONE:
+ return;
+ default:
+ throw new IllegalStateException("Not fully encoded, current state: " +
+ CodecStates.name(codecState) + ", allowed transitions: " +
+ CodecStates.transitions(codecState));
+ }
+ }
+ }
+
}
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 8c7a7a1ee6..6a48488936 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
@@ -11,6 +11,130 @@
@SuppressWarnings("all")
public final class TokenCodecDecoder
{
+ private static final boolean ENABLE_BOUNDS_CHECKS = !Boolean.getBoolean("agrona.disable.bounds.checks");
+
+ private static final boolean SBE_ENABLE_IR_PRECEDENCE_CHECKS = Boolean.parseBoolean(System.getProperty(
+ "sbe.enable.ir.precedence.checks",
+ Boolean.toString(ENABLE_BOUNDS_CHECKS)));
+
+ /**
+ * The states in which a encoder/decoder/codec can live.
+ *
+ * The state machine diagram below, encoded in the dot language, describes
+ * the valid state transitions according to the order in which fields may be
+ * accessed safely. Tools such as PlantUML and Graphviz can render it.
+ *
+ *
{@code
+ * digraph G {
+ * NOT_WRAPPED -> V0_BLOCK [label=" wrap(version=0) "];
+ * V0_BLOCK -> V0_BLOCK [label=" tokenOffset(?) "];
+ * V0_BLOCK -> V0_BLOCK [label=" tokenSize(?) "];
+ * V0_BLOCK -> V0_BLOCK [label=" fieldId(?) "];
+ * V0_BLOCK -> V0_BLOCK [label=" tokenVersion(?) "];
+ * V0_BLOCK -> V0_BLOCK [label=" componentTokenCount(?) "];
+ * V0_BLOCK -> V0_BLOCK [label=" signal(?) "];
+ * V0_BLOCK -> V0_BLOCK [label=" primitiveType(?) "];
+ * V0_BLOCK -> V0_BLOCK [label=" byteOrder(?) "];
+ * V0_BLOCK -> V0_BLOCK [label=" presence(?) "];
+ * V0_BLOCK -> V0_BLOCK [label=" deprecated(?) "];
+ * V0_BLOCK -> V0_BLOCK [label=" nameLength() "];
+ * V0_BLOCK -> V0_NAME_DONE [label=" name(?) "];
+ * V0_NAME_DONE -> V0_NAME_DONE [label=" constValueLength() "];
+ * V0_NAME_DONE -> V0_CONSTVALUE_DONE [label=" constValue(?) "];
+ * V0_CONSTVALUE_DONE -> V0_CONSTVALUE_DONE [label=" minValueLength() "];
+ * V0_CONSTVALUE_DONE -> V0_MINVALUE_DONE [label=" minValue(?) "];
+ * V0_MINVALUE_DONE -> V0_MINVALUE_DONE [label=" maxValueLength() "];
+ * V0_MINVALUE_DONE -> V0_MAXVALUE_DONE [label=" maxValue(?) "];
+ * V0_MAXVALUE_DONE -> V0_MAXVALUE_DONE [label=" nullValueLength() "];
+ * V0_MAXVALUE_DONE -> V0_NULLVALUE_DONE [label=" nullValue(?) "];
+ * V0_NULLVALUE_DONE -> V0_NULLVALUE_DONE [label=" characterEncodingLength() "];
+ * V0_NULLVALUE_DONE -> V0_CHARACTERENCODING_DONE [label=" characterEncoding(?) "];
+ * V0_CHARACTERENCODING_DONE -> V0_CHARACTERENCODING_DONE [label=" epochLength() "];
+ * V0_CHARACTERENCODING_DONE -> V0_EPOCH_DONE [label=" epoch(?) "];
+ * V0_EPOCH_DONE -> V0_EPOCH_DONE [label=" timeUnitLength() "];
+ * V0_EPOCH_DONE -> V0_TIMEUNIT_DONE [label=" timeUnit(?) "];
+ * V0_TIMEUNIT_DONE -> V0_TIMEUNIT_DONE [label=" semanticTypeLength() "];
+ * V0_TIMEUNIT_DONE -> V0_SEMANTICTYPE_DONE [label=" semanticType(?) "];
+ * V0_SEMANTICTYPE_DONE -> V0_SEMANTICTYPE_DONE [label=" descriptionLength() "];
+ * V0_SEMANTICTYPE_DONE -> V0_DESCRIPTION_DONE [label=" description(?) "];
+ * V0_DESCRIPTION_DONE -> V0_DESCRIPTION_DONE [label=" referencedNameLength() "];
+ * V0_DESCRIPTION_DONE -> V0_REFERENCEDNAME_DONE [label=" referencedName(?) "];
+ * }
+ * }
+ */
+ private static class CodecStates
+ {
+ private static final int NOT_WRAPPED = 0;
+ private static final int V0_BLOCK = 1;
+ private static final int V0_NAME_DONE = 2;
+ private static final int V0_CONSTVALUE_DONE = 3;
+ private static final int V0_MINVALUE_DONE = 4;
+ private static final int V0_MAXVALUE_DONE = 5;
+ private static final int V0_NULLVALUE_DONE = 6;
+ private static final int V0_CHARACTERENCODING_DONE = 7;
+ private static final int V0_EPOCH_DONE = 8;
+ private static final int V0_TIMEUNIT_DONE = 9;
+ private static final int V0_SEMANTICTYPE_DONE = 10;
+ private static final int V0_DESCRIPTION_DONE = 11;
+ private static final int V0_REFERENCEDNAME_DONE = 12;
+
+ private static final String[] STATE_NAME_LOOKUP =
+ {
+ "NOT_WRAPPED",
+ "V0_BLOCK",
+ "V0_NAME_DONE",
+ "V0_CONSTVALUE_DONE",
+ "V0_MINVALUE_DONE",
+ "V0_MAXVALUE_DONE",
+ "V0_NULLVALUE_DONE",
+ "V0_CHARACTERENCODING_DONE",
+ "V0_EPOCH_DONE",
+ "V0_TIMEUNIT_DONE",
+ "V0_SEMANTICTYPE_DONE",
+ "V0_DESCRIPTION_DONE",
+ "V0_REFERENCEDNAME_DONE",
+ };
+
+ private static final String[] STATE_TRANSITIONS_LOOKUP =
+ {
+ "\"wrap(version=0)\"",
+ "\"tokenOffset(?)\", \"tokenSize(?)\", \"fieldId(?)\", \"tokenVersion(?)\", \"componentTokenCount(?)\", \"signal(?)\", \"primitiveType(?)\", \"byteOrder(?)\", \"presence(?)\", \"deprecated(?)\", \"nameLength()\", \"name(?)\"",
+ "\"constValueLength()\", \"constValue(?)\"",
+ "\"minValueLength()\", \"minValue(?)\"",
+ "\"maxValueLength()\", \"maxValue(?)\"",
+ "\"nullValueLength()\", \"nullValue(?)\"",
+ "\"characterEncodingLength()\", \"characterEncoding(?)\"",
+ "\"epochLength()\", \"epoch(?)\"",
+ "\"timeUnitLength()\", \"timeUnit(?)\"",
+ "\"semanticTypeLength()\", \"semanticType(?)\"",
+ "\"descriptionLength()\", \"description(?)\"",
+ "\"referencedNameLength()\", \"referencedName(?)\"",
+ "",
+ };
+
+ private static String name(final int state)
+ {
+ return STATE_NAME_LOOKUP[state];
+ }
+
+ private static String transitions(final int state)
+ {
+ return STATE_TRANSITIONS_LOOKUP[state];
+ }
+ }
+
+ private int codecState = CodecStates.NOT_WRAPPED;
+
+ private int codecState()
+ {
+ return codecState;
+ }
+
+ private void codecState(int newState)
+ {
+ codecState = newState;
+ }
+
public static final int BLOCK_LENGTH = 28;
public static final int TEMPLATE_ID = 2;
public static final int SCHEMA_ID = 1;
@@ -66,6 +190,19 @@ public int offset()
return offset;
}
+ private void onWrap(final int actingVersion)
+ {
+ switch(actingVersion)
+ {
+ case 0:
+ codecState(CodecStates.V0_BLOCK);
+ break;
+ default:
+ codecState(CodecStates.V0_BLOCK);
+ break;
+ }
+ }
+
public TokenCodecDecoder wrap(
final DirectBuffer buffer,
final int offset,
@@ -82,6 +219,11 @@ public TokenCodecDecoder wrap(
this.actingVersion = actingVersion;
limit(offset + actingBlockLength);
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onWrap(actingVersion);
+ }
+
return this;
}
@@ -113,10 +255,16 @@ public TokenCodecDecoder sbeRewind()
public int sbeDecodedLength()
{
final int currentLimit = limit();
+ final int currentCodecState = codecState();
sbeSkip();
final int decodedLength = encodedLength();
limit(currentLimit);
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ codecState(currentCodecState);
+ }
+
return decodedLength;
}
@@ -170,6 +318,17 @@ public static String tokenOffsetMetaAttribute(final MetaAttribute metaAttribute)
return "";
}
+ private void onTokenOffsetAccessed()
+ {
+ if (codecState() == CodecStates.NOT_WRAPPED)
+ {
+ throw new IllegalStateException("Illegal field access order. " +
+ "Cannot access field \"tokenOffset\" in state: " + CodecStates.name(codecState()) +
+ ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) +
+ "]. Please see the diagram in the Javadoc of the class TokenCodecDecoder#CodecStates.");
+ }
+ }
+
public static int tokenOffsetNullValue()
{
return -2147483648;
@@ -187,6 +346,11 @@ public static int tokenOffsetMaxValue()
public int tokenOffset()
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onTokenOffsetAccessed();
+ }
+
return buffer.getInt(offset + 0, java.nio.ByteOrder.LITTLE_ENDIAN);
}
@@ -221,6 +385,17 @@ public static String tokenSizeMetaAttribute(final MetaAttribute metaAttribute)
return "";
}
+ private void onTokenSizeAccessed()
+ {
+ if (codecState() == CodecStates.NOT_WRAPPED)
+ {
+ throw new IllegalStateException("Illegal field access order. " +
+ "Cannot access field \"tokenSize\" in state: " + CodecStates.name(codecState()) +
+ ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) +
+ "]. Please see the diagram in the Javadoc of the class TokenCodecDecoder#CodecStates.");
+ }
+ }
+
public static int tokenSizeNullValue()
{
return -2147483648;
@@ -238,6 +413,11 @@ public static int tokenSizeMaxValue()
public int tokenSize()
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onTokenSizeAccessed();
+ }
+
return buffer.getInt(offset + 4, java.nio.ByteOrder.LITTLE_ENDIAN);
}
@@ -272,6 +452,17 @@ public static String fieldIdMetaAttribute(final MetaAttribute metaAttribute)
return "";
}
+ private void onFieldIdAccessed()
+ {
+ if (codecState() == CodecStates.NOT_WRAPPED)
+ {
+ throw new IllegalStateException("Illegal field access order. " +
+ "Cannot access field \"fieldId\" in state: " + CodecStates.name(codecState()) +
+ ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) +
+ "]. Please see the diagram in the Javadoc of the class TokenCodecDecoder#CodecStates.");
+ }
+ }
+
public static int fieldIdNullValue()
{
return -2147483648;
@@ -289,6 +480,11 @@ public static int fieldIdMaxValue()
public int fieldId()
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onFieldIdAccessed();
+ }
+
return buffer.getInt(offset + 8, java.nio.ByteOrder.LITTLE_ENDIAN);
}
@@ -323,6 +519,17 @@ public static String tokenVersionMetaAttribute(final MetaAttribute metaAttribute
return "";
}
+ private void onTokenVersionAccessed()
+ {
+ if (codecState() == CodecStates.NOT_WRAPPED)
+ {
+ throw new IllegalStateException("Illegal field access order. " +
+ "Cannot access field \"tokenVersion\" in state: " + CodecStates.name(codecState()) +
+ ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) +
+ "]. Please see the diagram in the Javadoc of the class TokenCodecDecoder#CodecStates.");
+ }
+ }
+
public static int tokenVersionNullValue()
{
return -2147483648;
@@ -340,6 +547,11 @@ public static int tokenVersionMaxValue()
public int tokenVersion()
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onTokenVersionAccessed();
+ }
+
return buffer.getInt(offset + 12, java.nio.ByteOrder.LITTLE_ENDIAN);
}
@@ -374,6 +586,17 @@ public static String componentTokenCountMetaAttribute(final MetaAttribute metaAt
return "";
}
+ private void onComponentTokenCountAccessed()
+ {
+ if (codecState() == CodecStates.NOT_WRAPPED)
+ {
+ throw new IllegalStateException("Illegal field access order. " +
+ "Cannot access field \"componentTokenCount\" in state: " + CodecStates.name(codecState()) +
+ ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) +
+ "]. Please see the diagram in the Javadoc of the class TokenCodecDecoder#CodecStates.");
+ }
+ }
+
public static int componentTokenCountNullValue()
{
return -2147483648;
@@ -391,6 +614,11 @@ public static int componentTokenCountMaxValue()
public int componentTokenCount()
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onComponentTokenCountAccessed();
+ }
+
return buffer.getInt(offset + 16, java.nio.ByteOrder.LITTLE_ENDIAN);
}
@@ -425,13 +653,34 @@ public static String signalMetaAttribute(final MetaAttribute metaAttribute)
return "";
}
+ private void onSignalAccessed()
+ {
+ if (codecState() == CodecStates.NOT_WRAPPED)
+ {
+ throw new IllegalStateException("Illegal field access order. " +
+ "Cannot access field \"signal\" in state: " + CodecStates.name(codecState()) +
+ ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) +
+ "]. Please see the diagram in the Javadoc of the class TokenCodecDecoder#CodecStates.");
+ }
+ }
+
public short signalRaw()
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onSignalAccessed();
+ }
+
return ((short)(buffer.getByte(offset + 20) & 0xFF));
}
public SignalCodec signal()
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onSignalAccessed();
+ }
+
return SignalCodec.get(((short)(buffer.getByte(offset + 20) & 0xFF)));
}
@@ -466,13 +715,34 @@ public static String primitiveTypeMetaAttribute(final MetaAttribute metaAttribut
return "";
}
+ private void onPrimitiveTypeAccessed()
+ {
+ if (codecState() == CodecStates.NOT_WRAPPED)
+ {
+ throw new IllegalStateException("Illegal field access order. " +
+ "Cannot access field \"primitiveType\" in state: " + CodecStates.name(codecState()) +
+ ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) +
+ "]. Please see the diagram in the Javadoc of the class TokenCodecDecoder#CodecStates.");
+ }
+ }
+
public short primitiveTypeRaw()
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onPrimitiveTypeAccessed();
+ }
+
return ((short)(buffer.getByte(offset + 21) & 0xFF));
}
public PrimitiveTypeCodec primitiveType()
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onPrimitiveTypeAccessed();
+ }
+
return PrimitiveTypeCodec.get(((short)(buffer.getByte(offset + 21) & 0xFF)));
}
@@ -507,13 +777,34 @@ public static String byteOrderMetaAttribute(final MetaAttribute metaAttribute)
return "";
}
+ private void onByteOrderAccessed()
+ {
+ if (codecState() == CodecStates.NOT_WRAPPED)
+ {
+ throw new IllegalStateException("Illegal field access order. " +
+ "Cannot access field \"byteOrder\" in state: " + CodecStates.name(codecState()) +
+ ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) +
+ "]. Please see the diagram in the Javadoc of the class TokenCodecDecoder#CodecStates.");
+ }
+ }
+
public short byteOrderRaw()
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onByteOrderAccessed();
+ }
+
return ((short)(buffer.getByte(offset + 22) & 0xFF));
}
public ByteOrderCodec byteOrder()
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onByteOrderAccessed();
+ }
+
return ByteOrderCodec.get(((short)(buffer.getByte(offset + 22) & 0xFF)));
}
@@ -548,13 +839,34 @@ public static String presenceMetaAttribute(final MetaAttribute metaAttribute)
return "";
}
+ private void onPresenceAccessed()
+ {
+ if (codecState() == CodecStates.NOT_WRAPPED)
+ {
+ throw new IllegalStateException("Illegal field access order. " +
+ "Cannot access field \"presence\" in state: " + CodecStates.name(codecState()) +
+ ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) +
+ "]. Please see the diagram in the Javadoc of the class TokenCodecDecoder#CodecStates.");
+ }
+ }
+
public short presenceRaw()
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onPresenceAccessed();
+ }
+
return ((short)(buffer.getByte(offset + 23) & 0xFF));
}
public PresenceCodec presence()
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onPresenceAccessed();
+ }
+
return PresenceCodec.get(((short)(buffer.getByte(offset + 23) & 0xFF)));
}
@@ -589,6 +901,17 @@ public static String deprecatedMetaAttribute(final MetaAttribute metaAttribute)
return "";
}
+ private void onDeprecatedAccessed()
+ {
+ if (codecState() == CodecStates.NOT_WRAPPED)
+ {
+ throw new IllegalStateException("Illegal field access order. " +
+ "Cannot access field \"deprecated\" in state: " + CodecStates.name(codecState()) +
+ ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) +
+ "]. Please see the diagram in the Javadoc of the class TokenCodecDecoder#CodecStates.");
+ }
+ }
+
public static int deprecatedNullValue()
{
return 0;
@@ -606,6 +929,11 @@ public static int deprecatedMaxValue()
public int deprecated()
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onDeprecatedAccessed();
+ }
+
return buffer.getInt(offset + 24, java.nio.ByteOrder.LITTLE_ENDIAN);
}
@@ -640,14 +968,54 @@ public static int nameHeaderLength()
return 2;
}
+ void onNameLengthAccessed()
+ {
+ switch (codecState())
+ {
+ case CodecStates.V0_BLOCK:
+ codecState(CodecStates.V0_BLOCK);
+ break;
+ default:
+ throw new IllegalStateException("Illegal field access order. " +
+ "Cannot decode length of var data \"name\" in state: " + CodecStates.name(codecState()) +
+ ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) +
+ "]. Please see the diagram in the Javadoc of the class TokenCodecDecoder#CodecStates.");
+ }
+ }
+
+ private void onNameAccessed()
+ {
+ switch (codecState())
+ {
+ case CodecStates.V0_BLOCK:
+ codecState(CodecStates.V0_NAME_DONE);
+ break;
+ default:
+ throw new IllegalStateException("Illegal field access order. " +
+ "Cannot access field \"name\" in state: " + CodecStates.name(codecState()) +
+ ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) +
+ "]. Please see the diagram in the Javadoc of the class TokenCodecDecoder#CodecStates.");
+ }
+ }
+
public int nameLength()
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onNameLengthAccessed();
+ }
+
final int limit = parentMessage.limit();
return (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
}
public int skipName()
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onNameAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
@@ -659,6 +1027,11 @@ public int skipName()
public int getName(final MutableDirectBuffer dst, final int dstOffset, final int length)
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onNameAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
@@ -671,6 +1044,11 @@ public int getName(final MutableDirectBuffer dst, final int dstOffset, final int
public int getName(final byte[] dst, final int dstOffset, final int length)
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onNameAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
@@ -683,6 +1061,11 @@ public int getName(final byte[] dst, final int dstOffset, final int length)
public void wrapName(final DirectBuffer wrapBuffer)
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onNameAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
@@ -692,6 +1075,11 @@ public void wrapName(final DirectBuffer wrapBuffer)
public String name()
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onNameAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
@@ -738,14 +1126,54 @@ public static int constValueHeaderLength()
return 2;
}
+ void onConstValueLengthAccessed()
+ {
+ switch (codecState())
+ {
+ case CodecStates.V0_NAME_DONE:
+ codecState(CodecStates.V0_NAME_DONE);
+ break;
+ default:
+ throw new IllegalStateException("Illegal field access order. " +
+ "Cannot decode length of var data \"constValue\" in state: " + CodecStates.name(codecState()) +
+ ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) +
+ "]. Please see the diagram in the Javadoc of the class TokenCodecDecoder#CodecStates.");
+ }
+ }
+
+ private void onConstValueAccessed()
+ {
+ switch (codecState())
+ {
+ case CodecStates.V0_NAME_DONE:
+ codecState(CodecStates.V0_CONSTVALUE_DONE);
+ break;
+ default:
+ throw new IllegalStateException("Illegal field access order. " +
+ "Cannot access field \"constValue\" in state: " + CodecStates.name(codecState()) +
+ ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) +
+ "]. Please see the diagram in the Javadoc of the class TokenCodecDecoder#CodecStates.");
+ }
+ }
+
public int constValueLength()
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onConstValueLengthAccessed();
+ }
+
final int limit = parentMessage.limit();
return (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
}
public int skipConstValue()
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onConstValueAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
@@ -757,6 +1185,11 @@ public int skipConstValue()
public int getConstValue(final MutableDirectBuffer dst, final int dstOffset, final int length)
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onConstValueAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
@@ -769,6 +1202,11 @@ public int getConstValue(final MutableDirectBuffer dst, final int dstOffset, fin
public int getConstValue(final byte[] dst, final int dstOffset, final int length)
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onConstValueAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
@@ -781,6 +1219,11 @@ public int getConstValue(final byte[] dst, final int dstOffset, final int length
public void wrapConstValue(final DirectBuffer wrapBuffer)
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onConstValueAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
@@ -790,6 +1233,11 @@ public void wrapConstValue(final DirectBuffer wrapBuffer)
public String constValue()
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onConstValueAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
@@ -836,14 +1284,54 @@ public static int minValueHeaderLength()
return 2;
}
+ void onMinValueLengthAccessed()
+ {
+ switch (codecState())
+ {
+ case CodecStates.V0_CONSTVALUE_DONE:
+ codecState(CodecStates.V0_CONSTVALUE_DONE);
+ break;
+ default:
+ throw new IllegalStateException("Illegal field access order. " +
+ "Cannot decode length of var data \"minValue\" in state: " + CodecStates.name(codecState()) +
+ ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) +
+ "]. Please see the diagram in the Javadoc of the class TokenCodecDecoder#CodecStates.");
+ }
+ }
+
+ private void onMinValueAccessed()
+ {
+ switch (codecState())
+ {
+ case CodecStates.V0_CONSTVALUE_DONE:
+ codecState(CodecStates.V0_MINVALUE_DONE);
+ break;
+ default:
+ throw new IllegalStateException("Illegal field access order. " +
+ "Cannot access field \"minValue\" in state: " + CodecStates.name(codecState()) +
+ ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) +
+ "]. Please see the diagram in the Javadoc of the class TokenCodecDecoder#CodecStates.");
+ }
+ }
+
public int minValueLength()
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onMinValueLengthAccessed();
+ }
+
final int limit = parentMessage.limit();
return (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
}
public int skipMinValue()
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onMinValueAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
@@ -855,6 +1343,11 @@ public int skipMinValue()
public int getMinValue(final MutableDirectBuffer dst, final int dstOffset, final int length)
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onMinValueAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
@@ -867,6 +1360,11 @@ public int getMinValue(final MutableDirectBuffer dst, final int dstOffset, final
public int getMinValue(final byte[] dst, final int dstOffset, final int length)
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onMinValueAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
@@ -879,6 +1377,11 @@ public int getMinValue(final byte[] dst, final int dstOffset, final int length)
public void wrapMinValue(final DirectBuffer wrapBuffer)
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onMinValueAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
@@ -888,6 +1391,11 @@ public void wrapMinValue(final DirectBuffer wrapBuffer)
public String minValue()
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onMinValueAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
@@ -934,14 +1442,54 @@ public static int maxValueHeaderLength()
return 2;
}
+ void onMaxValueLengthAccessed()
+ {
+ switch (codecState())
+ {
+ case CodecStates.V0_MINVALUE_DONE:
+ codecState(CodecStates.V0_MINVALUE_DONE);
+ break;
+ default:
+ throw new IllegalStateException("Illegal field access order. " +
+ "Cannot decode length of var data \"maxValue\" in state: " + CodecStates.name(codecState()) +
+ ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) +
+ "]. Please see the diagram in the Javadoc of the class TokenCodecDecoder#CodecStates.");
+ }
+ }
+
+ private void onMaxValueAccessed()
+ {
+ switch (codecState())
+ {
+ case CodecStates.V0_MINVALUE_DONE:
+ codecState(CodecStates.V0_MAXVALUE_DONE);
+ break;
+ default:
+ throw new IllegalStateException("Illegal field access order. " +
+ "Cannot access field \"maxValue\" in state: " + CodecStates.name(codecState()) +
+ ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) +
+ "]. Please see the diagram in the Javadoc of the class TokenCodecDecoder#CodecStates.");
+ }
+ }
+
public int maxValueLength()
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onMaxValueLengthAccessed();
+ }
+
final int limit = parentMessage.limit();
return (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
}
public int skipMaxValue()
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onMaxValueAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
@@ -953,6 +1501,11 @@ public int skipMaxValue()
public int getMaxValue(final MutableDirectBuffer dst, final int dstOffset, final int length)
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onMaxValueAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
@@ -965,6 +1518,11 @@ public int getMaxValue(final MutableDirectBuffer dst, final int dstOffset, final
public int getMaxValue(final byte[] dst, final int dstOffset, final int length)
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onMaxValueAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
@@ -977,6 +1535,11 @@ public int getMaxValue(final byte[] dst, final int dstOffset, final int length)
public void wrapMaxValue(final DirectBuffer wrapBuffer)
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onMaxValueAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
@@ -986,6 +1549,11 @@ public void wrapMaxValue(final DirectBuffer wrapBuffer)
public String maxValue()
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onMaxValueAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
@@ -1032,14 +1600,54 @@ public static int nullValueHeaderLength()
return 2;
}
+ void onNullValueLengthAccessed()
+ {
+ switch (codecState())
+ {
+ case CodecStates.V0_MAXVALUE_DONE:
+ codecState(CodecStates.V0_MAXVALUE_DONE);
+ break;
+ default:
+ throw new IllegalStateException("Illegal field access order. " +
+ "Cannot decode length of var data \"nullValue\" in state: " + CodecStates.name(codecState()) +
+ ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) +
+ "]. Please see the diagram in the Javadoc of the class TokenCodecDecoder#CodecStates.");
+ }
+ }
+
+ private void onNullValueAccessed()
+ {
+ switch (codecState())
+ {
+ case CodecStates.V0_MAXVALUE_DONE:
+ codecState(CodecStates.V0_NULLVALUE_DONE);
+ break;
+ default:
+ throw new IllegalStateException("Illegal field access order. " +
+ "Cannot access field \"nullValue\" in state: " + CodecStates.name(codecState()) +
+ ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) +
+ "]. Please see the diagram in the Javadoc of the class TokenCodecDecoder#CodecStates.");
+ }
+ }
+
public int nullValueLength()
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onNullValueLengthAccessed();
+ }
+
final int limit = parentMessage.limit();
return (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
}
public int skipNullValue()
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onNullValueAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
@@ -1051,6 +1659,11 @@ public int skipNullValue()
public int getNullValue(final MutableDirectBuffer dst, final int dstOffset, final int length)
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onNullValueAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
@@ -1063,6 +1676,11 @@ public int getNullValue(final MutableDirectBuffer dst, final int dstOffset, fina
public int getNullValue(final byte[] dst, final int dstOffset, final int length)
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onNullValueAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
@@ -1075,6 +1693,11 @@ public int getNullValue(final byte[] dst, final int dstOffset, final int length)
public void wrapNullValue(final DirectBuffer wrapBuffer)
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onNullValueAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
@@ -1084,6 +1707,11 @@ public void wrapNullValue(final DirectBuffer wrapBuffer)
public String nullValue()
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onNullValueAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
@@ -1130,14 +1758,54 @@ public static int characterEncodingHeaderLength()
return 2;
}
+ void onCharacterEncodingLengthAccessed()
+ {
+ switch (codecState())
+ {
+ case CodecStates.V0_NULLVALUE_DONE:
+ codecState(CodecStates.V0_NULLVALUE_DONE);
+ break;
+ default:
+ throw new IllegalStateException("Illegal field access order. " +
+ "Cannot decode length of var data \"characterEncoding\" in state: " + CodecStates.name(codecState()) +
+ ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) +
+ "]. Please see the diagram in the Javadoc of the class TokenCodecDecoder#CodecStates.");
+ }
+ }
+
+ private void onCharacterEncodingAccessed()
+ {
+ switch (codecState())
+ {
+ case CodecStates.V0_NULLVALUE_DONE:
+ codecState(CodecStates.V0_CHARACTERENCODING_DONE);
+ break;
+ default:
+ throw new IllegalStateException("Illegal field access order. " +
+ "Cannot access field \"characterEncoding\" in state: " + CodecStates.name(codecState()) +
+ ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) +
+ "]. Please see the diagram in the Javadoc of the class TokenCodecDecoder#CodecStates.");
+ }
+ }
+
public int characterEncodingLength()
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onCharacterEncodingLengthAccessed();
+ }
+
final int limit = parentMessage.limit();
return (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
}
public int skipCharacterEncoding()
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onCharacterEncodingAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
@@ -1149,6 +1817,11 @@ public int skipCharacterEncoding()
public int getCharacterEncoding(final MutableDirectBuffer dst, final int dstOffset, final int length)
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onCharacterEncodingAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
@@ -1161,6 +1834,11 @@ public int getCharacterEncoding(final MutableDirectBuffer dst, final int dstOffs
public int getCharacterEncoding(final byte[] dst, final int dstOffset, final int length)
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onCharacterEncodingAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
@@ -1173,6 +1851,11 @@ public int getCharacterEncoding(final byte[] dst, final int dstOffset, final int
public void wrapCharacterEncoding(final DirectBuffer wrapBuffer)
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onCharacterEncodingAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
@@ -1182,6 +1865,11 @@ public void wrapCharacterEncoding(final DirectBuffer wrapBuffer)
public String characterEncoding()
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onCharacterEncodingAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
@@ -1228,14 +1916,54 @@ public static int epochHeaderLength()
return 2;
}
+ void onEpochLengthAccessed()
+ {
+ switch (codecState())
+ {
+ case CodecStates.V0_CHARACTERENCODING_DONE:
+ codecState(CodecStates.V0_CHARACTERENCODING_DONE);
+ break;
+ default:
+ throw new IllegalStateException("Illegal field access order. " +
+ "Cannot decode length of var data \"epoch\" in state: " + CodecStates.name(codecState()) +
+ ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) +
+ "]. Please see the diagram in the Javadoc of the class TokenCodecDecoder#CodecStates.");
+ }
+ }
+
+ private void onEpochAccessed()
+ {
+ switch (codecState())
+ {
+ case CodecStates.V0_CHARACTERENCODING_DONE:
+ codecState(CodecStates.V0_EPOCH_DONE);
+ break;
+ default:
+ throw new IllegalStateException("Illegal field access order. " +
+ "Cannot access field \"epoch\" in state: " + CodecStates.name(codecState()) +
+ ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) +
+ "]. Please see the diagram in the Javadoc of the class TokenCodecDecoder#CodecStates.");
+ }
+ }
+
public int epochLength()
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onEpochLengthAccessed();
+ }
+
final int limit = parentMessage.limit();
return (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
}
public int skipEpoch()
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onEpochAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
@@ -1247,6 +1975,11 @@ public int skipEpoch()
public int getEpoch(final MutableDirectBuffer dst, final int dstOffset, final int length)
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onEpochAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
@@ -1259,6 +1992,11 @@ public int getEpoch(final MutableDirectBuffer dst, final int dstOffset, final in
public int getEpoch(final byte[] dst, final int dstOffset, final int length)
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onEpochAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
@@ -1271,6 +2009,11 @@ public int getEpoch(final byte[] dst, final int dstOffset, final int length)
public void wrapEpoch(final DirectBuffer wrapBuffer)
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onEpochAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
@@ -1280,6 +2023,11 @@ public void wrapEpoch(final DirectBuffer wrapBuffer)
public String epoch()
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onEpochAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
@@ -1326,14 +2074,54 @@ public static int timeUnitHeaderLength()
return 2;
}
+ void onTimeUnitLengthAccessed()
+ {
+ switch (codecState())
+ {
+ case CodecStates.V0_EPOCH_DONE:
+ codecState(CodecStates.V0_EPOCH_DONE);
+ break;
+ default:
+ throw new IllegalStateException("Illegal field access order. " +
+ "Cannot decode length of var data \"timeUnit\" in state: " + CodecStates.name(codecState()) +
+ ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) +
+ "]. Please see the diagram in the Javadoc of the class TokenCodecDecoder#CodecStates.");
+ }
+ }
+
+ private void onTimeUnitAccessed()
+ {
+ switch (codecState())
+ {
+ case CodecStates.V0_EPOCH_DONE:
+ codecState(CodecStates.V0_TIMEUNIT_DONE);
+ break;
+ default:
+ throw new IllegalStateException("Illegal field access order. " +
+ "Cannot access field \"timeUnit\" in state: " + CodecStates.name(codecState()) +
+ ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) +
+ "]. Please see the diagram in the Javadoc of the class TokenCodecDecoder#CodecStates.");
+ }
+ }
+
public int timeUnitLength()
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onTimeUnitLengthAccessed();
+ }
+
final int limit = parentMessage.limit();
return (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
}
public int skipTimeUnit()
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onTimeUnitAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
@@ -1345,6 +2133,11 @@ public int skipTimeUnit()
public int getTimeUnit(final MutableDirectBuffer dst, final int dstOffset, final int length)
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onTimeUnitAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
@@ -1357,6 +2150,11 @@ public int getTimeUnit(final MutableDirectBuffer dst, final int dstOffset, final
public int getTimeUnit(final byte[] dst, final int dstOffset, final int length)
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onTimeUnitAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
@@ -1369,6 +2167,11 @@ public int getTimeUnit(final byte[] dst, final int dstOffset, final int length)
public void wrapTimeUnit(final DirectBuffer wrapBuffer)
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onTimeUnitAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
@@ -1378,6 +2181,11 @@ public void wrapTimeUnit(final DirectBuffer wrapBuffer)
public String timeUnit()
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onTimeUnitAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
@@ -1424,14 +2232,54 @@ public static int semanticTypeHeaderLength()
return 2;
}
+ void onSemanticTypeLengthAccessed()
+ {
+ switch (codecState())
+ {
+ case CodecStates.V0_TIMEUNIT_DONE:
+ codecState(CodecStates.V0_TIMEUNIT_DONE);
+ break;
+ default:
+ throw new IllegalStateException("Illegal field access order. " +
+ "Cannot decode length of var data \"semanticType\" in state: " + CodecStates.name(codecState()) +
+ ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) +
+ "]. Please see the diagram in the Javadoc of the class TokenCodecDecoder#CodecStates.");
+ }
+ }
+
+ private void onSemanticTypeAccessed()
+ {
+ switch (codecState())
+ {
+ case CodecStates.V0_TIMEUNIT_DONE:
+ codecState(CodecStates.V0_SEMANTICTYPE_DONE);
+ break;
+ default:
+ throw new IllegalStateException("Illegal field access order. " +
+ "Cannot access field \"semanticType\" in state: " + CodecStates.name(codecState()) +
+ ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) +
+ "]. Please see the diagram in the Javadoc of the class TokenCodecDecoder#CodecStates.");
+ }
+ }
+
public int semanticTypeLength()
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onSemanticTypeLengthAccessed();
+ }
+
final int limit = parentMessage.limit();
return (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
}
public int skipSemanticType()
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onSemanticTypeAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
@@ -1443,6 +2291,11 @@ public int skipSemanticType()
public int getSemanticType(final MutableDirectBuffer dst, final int dstOffset, final int length)
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onSemanticTypeAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
@@ -1455,6 +2308,11 @@ public int getSemanticType(final MutableDirectBuffer dst, final int dstOffset, f
public int getSemanticType(final byte[] dst, final int dstOffset, final int length)
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onSemanticTypeAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
@@ -1467,6 +2325,11 @@ public int getSemanticType(final byte[] dst, final int dstOffset, final int leng
public void wrapSemanticType(final DirectBuffer wrapBuffer)
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onSemanticTypeAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
@@ -1476,6 +2339,11 @@ public void wrapSemanticType(final DirectBuffer wrapBuffer)
public String semanticType()
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onSemanticTypeAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
@@ -1522,14 +2390,54 @@ public static int descriptionHeaderLength()
return 2;
}
+ void onDescriptionLengthAccessed()
+ {
+ switch (codecState())
+ {
+ case CodecStates.V0_SEMANTICTYPE_DONE:
+ codecState(CodecStates.V0_SEMANTICTYPE_DONE);
+ break;
+ default:
+ throw new IllegalStateException("Illegal field access order. " +
+ "Cannot decode length of var data \"description\" in state: " + CodecStates.name(codecState()) +
+ ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) +
+ "]. Please see the diagram in the Javadoc of the class TokenCodecDecoder#CodecStates.");
+ }
+ }
+
+ private void onDescriptionAccessed()
+ {
+ switch (codecState())
+ {
+ case CodecStates.V0_SEMANTICTYPE_DONE:
+ codecState(CodecStates.V0_DESCRIPTION_DONE);
+ break;
+ default:
+ throw new IllegalStateException("Illegal field access order. " +
+ "Cannot access field \"description\" in state: " + CodecStates.name(codecState()) +
+ ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) +
+ "]. Please see the diagram in the Javadoc of the class TokenCodecDecoder#CodecStates.");
+ }
+ }
+
public int descriptionLength()
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onDescriptionLengthAccessed();
+ }
+
final int limit = parentMessage.limit();
return (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
}
public int skipDescription()
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onDescriptionAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
@@ -1541,6 +2449,11 @@ public int skipDescription()
public int getDescription(final MutableDirectBuffer dst, final int dstOffset, final int length)
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onDescriptionAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
@@ -1553,6 +2466,11 @@ public int getDescription(final MutableDirectBuffer dst, final int dstOffset, fi
public int getDescription(final byte[] dst, final int dstOffset, final int length)
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onDescriptionAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
@@ -1565,6 +2483,11 @@ public int getDescription(final byte[] dst, final int dstOffset, final int lengt
public void wrapDescription(final DirectBuffer wrapBuffer)
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onDescriptionAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
@@ -1574,6 +2497,11 @@ public void wrapDescription(final DirectBuffer wrapBuffer)
public String description()
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onDescriptionAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
@@ -1620,14 +2548,54 @@ public static int referencedNameHeaderLength()
return 2;
}
+ void onReferencedNameLengthAccessed()
+ {
+ switch (codecState())
+ {
+ case CodecStates.V0_DESCRIPTION_DONE:
+ codecState(CodecStates.V0_DESCRIPTION_DONE);
+ break;
+ default:
+ throw new IllegalStateException("Illegal field access order. " +
+ "Cannot decode length of var data \"referencedName\" in state: " + CodecStates.name(codecState()) +
+ ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) +
+ "]. Please see the diagram in the Javadoc of the class TokenCodecDecoder#CodecStates.");
+ }
+ }
+
+ private void onReferencedNameAccessed()
+ {
+ switch (codecState())
+ {
+ case CodecStates.V0_DESCRIPTION_DONE:
+ codecState(CodecStates.V0_REFERENCEDNAME_DONE);
+ break;
+ default:
+ throw new IllegalStateException("Illegal field access order. " +
+ "Cannot access field \"referencedName\" in state: " + CodecStates.name(codecState()) +
+ ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) +
+ "]. Please see the diagram in the Javadoc of the class TokenCodecDecoder#CodecStates.");
+ }
+ }
+
public int referencedNameLength()
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onReferencedNameLengthAccessed();
+ }
+
final int limit = parentMessage.limit();
return (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
}
public int skipReferencedName()
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onReferencedNameAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
@@ -1639,6 +2607,11 @@ public int skipReferencedName()
public int getReferencedName(final MutableDirectBuffer dst, final int dstOffset, final int length)
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onReferencedNameAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
@@ -1651,6 +2624,11 @@ public int getReferencedName(final MutableDirectBuffer dst, final int dstOffset,
public int getReferencedName(final byte[] dst, final int dstOffset, final int length)
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onReferencedNameAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
@@ -1663,6 +2641,11 @@ public int getReferencedName(final byte[] dst, final int dstOffset, final int le
public void wrapReferencedName(final DirectBuffer wrapBuffer)
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onReferencedNameAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
@@ -1672,6 +2655,11 @@ public void wrapReferencedName(final DirectBuffer wrapBuffer)
public String referencedName()
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onReferencedNameAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
final int dataLength = (buffer.getShort(limit, java.nio.ByteOrder.LITTLE_ENDIAN) & 0xFFFF);
diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecEncoder.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecEncoder.java
index 793f10756f..a0ece1099d 100644
--- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecEncoder.java
+++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/ir/generated/TokenCodecEncoder.java
@@ -11,6 +11,130 @@
@SuppressWarnings("all")
public final class TokenCodecEncoder
{
+ private static final boolean ENABLE_BOUNDS_CHECKS = !Boolean.getBoolean("agrona.disable.bounds.checks");
+
+ private static final boolean SBE_ENABLE_IR_PRECEDENCE_CHECKS = Boolean.parseBoolean(System.getProperty(
+ "sbe.enable.ir.precedence.checks",
+ Boolean.toString(ENABLE_BOUNDS_CHECKS)));
+
+ /**
+ * The states in which a encoder/decoder/codec can live.
+ *
+ * The state machine diagram below, encoded in the dot language, describes
+ * the valid state transitions according to the order in which fields may be
+ * accessed safely. Tools such as PlantUML and Graphviz can render it.
+ *
+ *
{@code
+ * digraph G {
+ * NOT_WRAPPED -> V0_BLOCK [label=" wrap(version=0) "];
+ * V0_BLOCK -> V0_BLOCK [label=" tokenOffset(?) "];
+ * V0_BLOCK -> V0_BLOCK [label=" tokenSize(?) "];
+ * V0_BLOCK -> V0_BLOCK [label=" fieldId(?) "];
+ * V0_BLOCK -> V0_BLOCK [label=" tokenVersion(?) "];
+ * V0_BLOCK -> V0_BLOCK [label=" componentTokenCount(?) "];
+ * V0_BLOCK -> V0_BLOCK [label=" signal(?) "];
+ * V0_BLOCK -> V0_BLOCK [label=" primitiveType(?) "];
+ * V0_BLOCK -> V0_BLOCK [label=" byteOrder(?) "];
+ * V0_BLOCK -> V0_BLOCK [label=" presence(?) "];
+ * V0_BLOCK -> V0_BLOCK [label=" deprecated(?) "];
+ * V0_BLOCK -> V0_BLOCK [label=" nameLength() "];
+ * V0_BLOCK -> V0_NAME_DONE [label=" name(?) "];
+ * V0_NAME_DONE -> V0_NAME_DONE [label=" constValueLength() "];
+ * V0_NAME_DONE -> V0_CONSTVALUE_DONE [label=" constValue(?) "];
+ * V0_CONSTVALUE_DONE -> V0_CONSTVALUE_DONE [label=" minValueLength() "];
+ * V0_CONSTVALUE_DONE -> V0_MINVALUE_DONE [label=" minValue(?) "];
+ * V0_MINVALUE_DONE -> V0_MINVALUE_DONE [label=" maxValueLength() "];
+ * V0_MINVALUE_DONE -> V0_MAXVALUE_DONE [label=" maxValue(?) "];
+ * V0_MAXVALUE_DONE -> V0_MAXVALUE_DONE [label=" nullValueLength() "];
+ * V0_MAXVALUE_DONE -> V0_NULLVALUE_DONE [label=" nullValue(?) "];
+ * V0_NULLVALUE_DONE -> V0_NULLVALUE_DONE [label=" characterEncodingLength() "];
+ * V0_NULLVALUE_DONE -> V0_CHARACTERENCODING_DONE [label=" characterEncoding(?) "];
+ * V0_CHARACTERENCODING_DONE -> V0_CHARACTERENCODING_DONE [label=" epochLength() "];
+ * V0_CHARACTERENCODING_DONE -> V0_EPOCH_DONE [label=" epoch(?) "];
+ * V0_EPOCH_DONE -> V0_EPOCH_DONE [label=" timeUnitLength() "];
+ * V0_EPOCH_DONE -> V0_TIMEUNIT_DONE [label=" timeUnit(?) "];
+ * V0_TIMEUNIT_DONE -> V0_TIMEUNIT_DONE [label=" semanticTypeLength() "];
+ * V0_TIMEUNIT_DONE -> V0_SEMANTICTYPE_DONE [label=" semanticType(?) "];
+ * V0_SEMANTICTYPE_DONE -> V0_SEMANTICTYPE_DONE [label=" descriptionLength() "];
+ * V0_SEMANTICTYPE_DONE -> V0_DESCRIPTION_DONE [label=" description(?) "];
+ * V0_DESCRIPTION_DONE -> V0_DESCRIPTION_DONE [label=" referencedNameLength() "];
+ * V0_DESCRIPTION_DONE -> V0_REFERENCEDNAME_DONE [label=" referencedName(?) "];
+ * }
+ * }
+ */
+ private static class CodecStates
+ {
+ private static final int NOT_WRAPPED = 0;
+ private static final int V0_BLOCK = 1;
+ private static final int V0_NAME_DONE = 2;
+ private static final int V0_CONSTVALUE_DONE = 3;
+ private static final int V0_MINVALUE_DONE = 4;
+ private static final int V0_MAXVALUE_DONE = 5;
+ private static final int V0_NULLVALUE_DONE = 6;
+ private static final int V0_CHARACTERENCODING_DONE = 7;
+ private static final int V0_EPOCH_DONE = 8;
+ private static final int V0_TIMEUNIT_DONE = 9;
+ private static final int V0_SEMANTICTYPE_DONE = 10;
+ private static final int V0_DESCRIPTION_DONE = 11;
+ private static final int V0_REFERENCEDNAME_DONE = 12;
+
+ private static final String[] STATE_NAME_LOOKUP =
+ {
+ "NOT_WRAPPED",
+ "V0_BLOCK",
+ "V0_NAME_DONE",
+ "V0_CONSTVALUE_DONE",
+ "V0_MINVALUE_DONE",
+ "V0_MAXVALUE_DONE",
+ "V0_NULLVALUE_DONE",
+ "V0_CHARACTERENCODING_DONE",
+ "V0_EPOCH_DONE",
+ "V0_TIMEUNIT_DONE",
+ "V0_SEMANTICTYPE_DONE",
+ "V0_DESCRIPTION_DONE",
+ "V0_REFERENCEDNAME_DONE",
+ };
+
+ private static final String[] STATE_TRANSITIONS_LOOKUP =
+ {
+ "\"wrap(version=0)\"",
+ "\"tokenOffset(?)\", \"tokenSize(?)\", \"fieldId(?)\", \"tokenVersion(?)\", \"componentTokenCount(?)\", \"signal(?)\", \"primitiveType(?)\", \"byteOrder(?)\", \"presence(?)\", \"deprecated(?)\", \"nameLength()\", \"name(?)\"",
+ "\"constValueLength()\", \"constValue(?)\"",
+ "\"minValueLength()\", \"minValue(?)\"",
+ "\"maxValueLength()\", \"maxValue(?)\"",
+ "\"nullValueLength()\", \"nullValue(?)\"",
+ "\"characterEncodingLength()\", \"characterEncoding(?)\"",
+ "\"epochLength()\", \"epoch(?)\"",
+ "\"timeUnitLength()\", \"timeUnit(?)\"",
+ "\"semanticTypeLength()\", \"semanticType(?)\"",
+ "\"descriptionLength()\", \"description(?)\"",
+ "\"referencedNameLength()\", \"referencedName(?)\"",
+ "",
+ };
+
+ private static String name(final int state)
+ {
+ return STATE_NAME_LOOKUP[state];
+ }
+
+ private static String transitions(final int state)
+ {
+ return STATE_TRANSITIONS_LOOKUP[state];
+ }
+ }
+
+ private int codecState = CodecStates.NOT_WRAPPED;
+
+ private int codecState()
+ {
+ return codecState;
+ }
+
+ private void codecState(int newState)
+ {
+ codecState = newState;
+ }
+
public static final int BLOCK_LENGTH = 28;
public static final int TEMPLATE_ID = 2;
public static final int SCHEMA_ID = 1;
@@ -74,6 +198,11 @@ public TokenCodecEncoder wrap(final MutableDirectBuffer buffer, final int offset
this.offset = offset;
limit(offset + BLOCK_LENGTH);
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ codecState(CodecStates.V0_BLOCK);
+ }
+
return this;
}
@@ -135,6 +264,17 @@ public static String tokenOffsetMetaAttribute(final MetaAttribute metaAttribute)
return "";
}
+ private void onTokenOffsetAccessed()
+ {
+ if (codecState() == CodecStates.NOT_WRAPPED)
+ {
+ throw new IllegalStateException("Illegal field access order. " +
+ "Cannot access field \"tokenOffset\" in state: " + CodecStates.name(codecState()) +
+ ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) +
+ "]. Please see the diagram in the Javadoc of the class TokenCodecEncoder#CodecStates.");
+ }
+ }
+
public static int tokenOffsetNullValue()
{
return -2147483648;
@@ -152,6 +292,11 @@ public static int tokenOffsetMaxValue()
public TokenCodecEncoder tokenOffset(final int value)
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onTokenOffsetAccessed();
+ }
+
buffer.putInt(offset + 0, value, java.nio.ByteOrder.LITTLE_ENDIAN);
return this;
}
@@ -187,6 +332,17 @@ public static String tokenSizeMetaAttribute(final MetaAttribute metaAttribute)
return "";
}
+ private void onTokenSizeAccessed()
+ {
+ if (codecState() == CodecStates.NOT_WRAPPED)
+ {
+ throw new IllegalStateException("Illegal field access order. " +
+ "Cannot access field \"tokenSize\" in state: " + CodecStates.name(codecState()) +
+ ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) +
+ "]. Please see the diagram in the Javadoc of the class TokenCodecEncoder#CodecStates.");
+ }
+ }
+
public static int tokenSizeNullValue()
{
return -2147483648;
@@ -204,6 +360,11 @@ public static int tokenSizeMaxValue()
public TokenCodecEncoder tokenSize(final int value)
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onTokenSizeAccessed();
+ }
+
buffer.putInt(offset + 4, value, java.nio.ByteOrder.LITTLE_ENDIAN);
return this;
}
@@ -239,6 +400,17 @@ public static String fieldIdMetaAttribute(final MetaAttribute metaAttribute)
return "";
}
+ private void onFieldIdAccessed()
+ {
+ if (codecState() == CodecStates.NOT_WRAPPED)
+ {
+ throw new IllegalStateException("Illegal field access order. " +
+ "Cannot access field \"fieldId\" in state: " + CodecStates.name(codecState()) +
+ ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) +
+ "]. Please see the diagram in the Javadoc of the class TokenCodecEncoder#CodecStates.");
+ }
+ }
+
public static int fieldIdNullValue()
{
return -2147483648;
@@ -256,6 +428,11 @@ public static int fieldIdMaxValue()
public TokenCodecEncoder fieldId(final int value)
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onFieldIdAccessed();
+ }
+
buffer.putInt(offset + 8, value, java.nio.ByteOrder.LITTLE_ENDIAN);
return this;
}
@@ -291,6 +468,17 @@ public static String tokenVersionMetaAttribute(final MetaAttribute metaAttribute
return "";
}
+ private void onTokenVersionAccessed()
+ {
+ if (codecState() == CodecStates.NOT_WRAPPED)
+ {
+ throw new IllegalStateException("Illegal field access order. " +
+ "Cannot access field \"tokenVersion\" in state: " + CodecStates.name(codecState()) +
+ ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) +
+ "]. Please see the diagram in the Javadoc of the class TokenCodecEncoder#CodecStates.");
+ }
+ }
+
public static int tokenVersionNullValue()
{
return -2147483648;
@@ -308,6 +496,11 @@ public static int tokenVersionMaxValue()
public TokenCodecEncoder tokenVersion(final int value)
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onTokenVersionAccessed();
+ }
+
buffer.putInt(offset + 12, value, java.nio.ByteOrder.LITTLE_ENDIAN);
return this;
}
@@ -343,6 +536,17 @@ public static String componentTokenCountMetaAttribute(final MetaAttribute metaAt
return "";
}
+ private void onComponentTokenCountAccessed()
+ {
+ if (codecState() == CodecStates.NOT_WRAPPED)
+ {
+ throw new IllegalStateException("Illegal field access order. " +
+ "Cannot access field \"componentTokenCount\" in state: " + CodecStates.name(codecState()) +
+ ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) +
+ "]. Please see the diagram in the Javadoc of the class TokenCodecEncoder#CodecStates.");
+ }
+ }
+
public static int componentTokenCountNullValue()
{
return -2147483648;
@@ -360,6 +564,11 @@ public static int componentTokenCountMaxValue()
public TokenCodecEncoder componentTokenCount(final int value)
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onComponentTokenCountAccessed();
+ }
+
buffer.putInt(offset + 16, value, java.nio.ByteOrder.LITTLE_ENDIAN);
return this;
}
@@ -395,8 +604,24 @@ public static String signalMetaAttribute(final MetaAttribute metaAttribute)
return "";
}
+ private void onSignalAccessed()
+ {
+ if (codecState() == CodecStates.NOT_WRAPPED)
+ {
+ throw new IllegalStateException("Illegal field access order. " +
+ "Cannot access field \"signal\" in state: " + CodecStates.name(codecState()) +
+ ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) +
+ "]. Please see the diagram in the Javadoc of the class TokenCodecEncoder#CodecStates.");
+ }
+ }
+
public TokenCodecEncoder signal(final SignalCodec value)
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onSignalAccessed();
+ }
+
buffer.putByte(offset + 20, (byte)value.value());
return this;
}
@@ -431,8 +656,24 @@ public static String primitiveTypeMetaAttribute(final MetaAttribute metaAttribut
return "";
}
+ private void onPrimitiveTypeAccessed()
+ {
+ if (codecState() == CodecStates.NOT_WRAPPED)
+ {
+ throw new IllegalStateException("Illegal field access order. " +
+ "Cannot access field \"primitiveType\" in state: " + CodecStates.name(codecState()) +
+ ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) +
+ "]. Please see the diagram in the Javadoc of the class TokenCodecEncoder#CodecStates.");
+ }
+ }
+
public TokenCodecEncoder primitiveType(final PrimitiveTypeCodec value)
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onPrimitiveTypeAccessed();
+ }
+
buffer.putByte(offset + 21, (byte)value.value());
return this;
}
@@ -467,8 +708,24 @@ public static String byteOrderMetaAttribute(final MetaAttribute metaAttribute)
return "";
}
+ private void onByteOrderAccessed()
+ {
+ if (codecState() == CodecStates.NOT_WRAPPED)
+ {
+ throw new IllegalStateException("Illegal field access order. " +
+ "Cannot access field \"byteOrder\" in state: " + CodecStates.name(codecState()) +
+ ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) +
+ "]. Please see the diagram in the Javadoc of the class TokenCodecEncoder#CodecStates.");
+ }
+ }
+
public TokenCodecEncoder byteOrder(final ByteOrderCodec value)
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onByteOrderAccessed();
+ }
+
buffer.putByte(offset + 22, (byte)value.value());
return this;
}
@@ -503,8 +760,24 @@ public static String presenceMetaAttribute(final MetaAttribute metaAttribute)
return "";
}
+ private void onPresenceAccessed()
+ {
+ if (codecState() == CodecStates.NOT_WRAPPED)
+ {
+ throw new IllegalStateException("Illegal field access order. " +
+ "Cannot access field \"presence\" in state: " + CodecStates.name(codecState()) +
+ ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) +
+ "]. Please see the diagram in the Javadoc of the class TokenCodecEncoder#CodecStates.");
+ }
+ }
+
public TokenCodecEncoder presence(final PresenceCodec value)
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onPresenceAccessed();
+ }
+
buffer.putByte(offset + 23, (byte)value.value());
return this;
}
@@ -539,6 +812,17 @@ public static String deprecatedMetaAttribute(final MetaAttribute metaAttribute)
return "";
}
+ private void onDeprecatedAccessed()
+ {
+ if (codecState() == CodecStates.NOT_WRAPPED)
+ {
+ throw new IllegalStateException("Illegal field access order. " +
+ "Cannot access field \"deprecated\" in state: " + CodecStates.name(codecState()) +
+ ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) +
+ "]. Please see the diagram in the Javadoc of the class TokenCodecEncoder#CodecStates.");
+ }
+ }
+
public static int deprecatedNullValue()
{
return 0;
@@ -556,6 +840,11 @@ public static int deprecatedMaxValue()
public TokenCodecEncoder deprecated(final int value)
{
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onDeprecatedAccessed();
+ }
+
buffer.putInt(offset + 24, value, java.nio.ByteOrder.LITTLE_ENDIAN);
return this;
}
@@ -586,6 +875,21 @@ public static int nameHeaderLength()
return 2;
}
+ private void onNameAccessed()
+ {
+ switch (codecState())
+ {
+ case CodecStates.V0_BLOCK:
+ codecState(CodecStates.V0_NAME_DONE);
+ break;
+ default:
+ throw new IllegalStateException("Illegal field access order. " +
+ "Cannot access field \"name\" in state: " + CodecStates.name(codecState()) +
+ ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) +
+ "]. Please see the diagram in the Javadoc of the class TokenCodecEncoder#CodecStates.");
+ }
+ }
+
public TokenCodecEncoder putName(final DirectBuffer src, final int srcOffset, final int length)
{
if (length > 65534)
@@ -593,6 +897,11 @@ public TokenCodecEncoder putName(final DirectBuffer src, final int srcOffset, fi
throw new IllegalStateException("length > maxValue for type: " + length);
}
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onNameAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
parentMessage.limit(limit + headerLength + length);
@@ -609,6 +918,11 @@ public TokenCodecEncoder putName(final byte[] src, final int srcOffset, final in
throw new IllegalStateException("length > maxValue for type: " + length);
}
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onNameAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
parentMessage.limit(limit + headerLength + length);
@@ -628,6 +942,11 @@ public TokenCodecEncoder name(final String value)
throw new IllegalStateException("length > maxValue for type: " + length);
}
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onNameAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
parentMessage.limit(limit + headerLength + length);
@@ -662,6 +981,21 @@ public static int constValueHeaderLength()
return 2;
}
+ private void onConstValueAccessed()
+ {
+ switch (codecState())
+ {
+ case CodecStates.V0_NAME_DONE:
+ codecState(CodecStates.V0_CONSTVALUE_DONE);
+ break;
+ default:
+ throw new IllegalStateException("Illegal field access order. " +
+ "Cannot access field \"constValue\" in state: " + CodecStates.name(codecState()) +
+ ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) +
+ "]. Please see the diagram in the Javadoc of the class TokenCodecEncoder#CodecStates.");
+ }
+ }
+
public TokenCodecEncoder putConstValue(final DirectBuffer src, final int srcOffset, final int length)
{
if (length > 65534)
@@ -669,6 +1003,11 @@ public TokenCodecEncoder putConstValue(final DirectBuffer src, final int srcOffs
throw new IllegalStateException("length > maxValue for type: " + length);
}
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onConstValueAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
parentMessage.limit(limit + headerLength + length);
@@ -685,6 +1024,11 @@ public TokenCodecEncoder putConstValue(final byte[] src, final int srcOffset, fi
throw new IllegalStateException("length > maxValue for type: " + length);
}
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onConstValueAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
parentMessage.limit(limit + headerLength + length);
@@ -704,6 +1048,11 @@ public TokenCodecEncoder constValue(final String value)
throw new IllegalStateException("length > maxValue for type: " + length);
}
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onConstValueAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
parentMessage.limit(limit + headerLength + length);
@@ -738,6 +1087,21 @@ public static int minValueHeaderLength()
return 2;
}
+ private void onMinValueAccessed()
+ {
+ switch (codecState())
+ {
+ case CodecStates.V0_CONSTVALUE_DONE:
+ codecState(CodecStates.V0_MINVALUE_DONE);
+ break;
+ default:
+ throw new IllegalStateException("Illegal field access order. " +
+ "Cannot access field \"minValue\" in state: " + CodecStates.name(codecState()) +
+ ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) +
+ "]. Please see the diagram in the Javadoc of the class TokenCodecEncoder#CodecStates.");
+ }
+ }
+
public TokenCodecEncoder putMinValue(final DirectBuffer src, final int srcOffset, final int length)
{
if (length > 65534)
@@ -745,6 +1109,11 @@ public TokenCodecEncoder putMinValue(final DirectBuffer src, final int srcOffset
throw new IllegalStateException("length > maxValue for type: " + length);
}
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onMinValueAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
parentMessage.limit(limit + headerLength + length);
@@ -761,6 +1130,11 @@ public TokenCodecEncoder putMinValue(final byte[] src, final int srcOffset, fina
throw new IllegalStateException("length > maxValue for type: " + length);
}
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onMinValueAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
parentMessage.limit(limit + headerLength + length);
@@ -780,6 +1154,11 @@ public TokenCodecEncoder minValue(final String value)
throw new IllegalStateException("length > maxValue for type: " + length);
}
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onMinValueAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
parentMessage.limit(limit + headerLength + length);
@@ -814,6 +1193,21 @@ public static int maxValueHeaderLength()
return 2;
}
+ private void onMaxValueAccessed()
+ {
+ switch (codecState())
+ {
+ case CodecStates.V0_MINVALUE_DONE:
+ codecState(CodecStates.V0_MAXVALUE_DONE);
+ break;
+ default:
+ throw new IllegalStateException("Illegal field access order. " +
+ "Cannot access field \"maxValue\" in state: " + CodecStates.name(codecState()) +
+ ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) +
+ "]. Please see the diagram in the Javadoc of the class TokenCodecEncoder#CodecStates.");
+ }
+ }
+
public TokenCodecEncoder putMaxValue(final DirectBuffer src, final int srcOffset, final int length)
{
if (length > 65534)
@@ -821,6 +1215,11 @@ public TokenCodecEncoder putMaxValue(final DirectBuffer src, final int srcOffset
throw new IllegalStateException("length > maxValue for type: " + length);
}
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onMaxValueAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
parentMessage.limit(limit + headerLength + length);
@@ -837,6 +1236,11 @@ public TokenCodecEncoder putMaxValue(final byte[] src, final int srcOffset, fina
throw new IllegalStateException("length > maxValue for type: " + length);
}
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onMaxValueAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
parentMessage.limit(limit + headerLength + length);
@@ -856,6 +1260,11 @@ public TokenCodecEncoder maxValue(final String value)
throw new IllegalStateException("length > maxValue for type: " + length);
}
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onMaxValueAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
parentMessage.limit(limit + headerLength + length);
@@ -890,6 +1299,21 @@ public static int nullValueHeaderLength()
return 2;
}
+ private void onNullValueAccessed()
+ {
+ switch (codecState())
+ {
+ case CodecStates.V0_MAXVALUE_DONE:
+ codecState(CodecStates.V0_NULLVALUE_DONE);
+ break;
+ default:
+ throw new IllegalStateException("Illegal field access order. " +
+ "Cannot access field \"nullValue\" in state: " + CodecStates.name(codecState()) +
+ ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) +
+ "]. Please see the diagram in the Javadoc of the class TokenCodecEncoder#CodecStates.");
+ }
+ }
+
public TokenCodecEncoder putNullValue(final DirectBuffer src, final int srcOffset, final int length)
{
if (length > 65534)
@@ -897,6 +1321,11 @@ public TokenCodecEncoder putNullValue(final DirectBuffer src, final int srcOffse
throw new IllegalStateException("length > maxValue for type: " + length);
}
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onNullValueAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
parentMessage.limit(limit + headerLength + length);
@@ -913,6 +1342,11 @@ public TokenCodecEncoder putNullValue(final byte[] src, final int srcOffset, fin
throw new IllegalStateException("length > maxValue for type: " + length);
}
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onNullValueAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
parentMessage.limit(limit + headerLength + length);
@@ -932,6 +1366,11 @@ public TokenCodecEncoder nullValue(final String value)
throw new IllegalStateException("length > maxValue for type: " + length);
}
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onNullValueAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
parentMessage.limit(limit + headerLength + length);
@@ -966,6 +1405,21 @@ public static int characterEncodingHeaderLength()
return 2;
}
+ private void onCharacterEncodingAccessed()
+ {
+ switch (codecState())
+ {
+ case CodecStates.V0_NULLVALUE_DONE:
+ codecState(CodecStates.V0_CHARACTERENCODING_DONE);
+ break;
+ default:
+ throw new IllegalStateException("Illegal field access order. " +
+ "Cannot access field \"characterEncoding\" in state: " + CodecStates.name(codecState()) +
+ ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) +
+ "]. Please see the diagram in the Javadoc of the class TokenCodecEncoder#CodecStates.");
+ }
+ }
+
public TokenCodecEncoder putCharacterEncoding(final DirectBuffer src, final int srcOffset, final int length)
{
if (length > 65534)
@@ -973,6 +1427,11 @@ public TokenCodecEncoder putCharacterEncoding(final DirectBuffer src, final int
throw new IllegalStateException("length > maxValue for type: " + length);
}
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onCharacterEncodingAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
parentMessage.limit(limit + headerLength + length);
@@ -989,6 +1448,11 @@ public TokenCodecEncoder putCharacterEncoding(final byte[] src, final int srcOff
throw new IllegalStateException("length > maxValue for type: " + length);
}
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onCharacterEncodingAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
parentMessage.limit(limit + headerLength + length);
@@ -1008,6 +1472,11 @@ public TokenCodecEncoder characterEncoding(final String value)
throw new IllegalStateException("length > maxValue for type: " + length);
}
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onCharacterEncodingAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
parentMessage.limit(limit + headerLength + length);
@@ -1042,6 +1511,21 @@ public static int epochHeaderLength()
return 2;
}
+ private void onEpochAccessed()
+ {
+ switch (codecState())
+ {
+ case CodecStates.V0_CHARACTERENCODING_DONE:
+ codecState(CodecStates.V0_EPOCH_DONE);
+ break;
+ default:
+ throw new IllegalStateException("Illegal field access order. " +
+ "Cannot access field \"epoch\" in state: " + CodecStates.name(codecState()) +
+ ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) +
+ "]. Please see the diagram in the Javadoc of the class TokenCodecEncoder#CodecStates.");
+ }
+ }
+
public TokenCodecEncoder putEpoch(final DirectBuffer src, final int srcOffset, final int length)
{
if (length > 65534)
@@ -1049,6 +1533,11 @@ public TokenCodecEncoder putEpoch(final DirectBuffer src, final int srcOffset, f
throw new IllegalStateException("length > maxValue for type: " + length);
}
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onEpochAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
parentMessage.limit(limit + headerLength + length);
@@ -1065,6 +1554,11 @@ public TokenCodecEncoder putEpoch(final byte[] src, final int srcOffset, final i
throw new IllegalStateException("length > maxValue for type: " + length);
}
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onEpochAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
parentMessage.limit(limit + headerLength + length);
@@ -1084,6 +1578,11 @@ public TokenCodecEncoder epoch(final String value)
throw new IllegalStateException("length > maxValue for type: " + length);
}
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onEpochAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
parentMessage.limit(limit + headerLength + length);
@@ -1118,6 +1617,21 @@ public static int timeUnitHeaderLength()
return 2;
}
+ private void onTimeUnitAccessed()
+ {
+ switch (codecState())
+ {
+ case CodecStates.V0_EPOCH_DONE:
+ codecState(CodecStates.V0_TIMEUNIT_DONE);
+ break;
+ default:
+ throw new IllegalStateException("Illegal field access order. " +
+ "Cannot access field \"timeUnit\" in state: " + CodecStates.name(codecState()) +
+ ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) +
+ "]. Please see the diagram in the Javadoc of the class TokenCodecEncoder#CodecStates.");
+ }
+ }
+
public TokenCodecEncoder putTimeUnit(final DirectBuffer src, final int srcOffset, final int length)
{
if (length > 65534)
@@ -1125,6 +1639,11 @@ public TokenCodecEncoder putTimeUnit(final DirectBuffer src, final int srcOffset
throw new IllegalStateException("length > maxValue for type: " + length);
}
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onTimeUnitAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
parentMessage.limit(limit + headerLength + length);
@@ -1141,6 +1660,11 @@ public TokenCodecEncoder putTimeUnit(final byte[] src, final int srcOffset, fina
throw new IllegalStateException("length > maxValue for type: " + length);
}
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onTimeUnitAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
parentMessage.limit(limit + headerLength + length);
@@ -1160,6 +1684,11 @@ public TokenCodecEncoder timeUnit(final String value)
throw new IllegalStateException("length > maxValue for type: " + length);
}
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onTimeUnitAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
parentMessage.limit(limit + headerLength + length);
@@ -1194,6 +1723,21 @@ public static int semanticTypeHeaderLength()
return 2;
}
+ private void onSemanticTypeAccessed()
+ {
+ switch (codecState())
+ {
+ case CodecStates.V0_TIMEUNIT_DONE:
+ codecState(CodecStates.V0_SEMANTICTYPE_DONE);
+ break;
+ default:
+ throw new IllegalStateException("Illegal field access order. " +
+ "Cannot access field \"semanticType\" in state: " + CodecStates.name(codecState()) +
+ ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) +
+ "]. Please see the diagram in the Javadoc of the class TokenCodecEncoder#CodecStates.");
+ }
+ }
+
public TokenCodecEncoder putSemanticType(final DirectBuffer src, final int srcOffset, final int length)
{
if (length > 65534)
@@ -1201,6 +1745,11 @@ public TokenCodecEncoder putSemanticType(final DirectBuffer src, final int srcOf
throw new IllegalStateException("length > maxValue for type: " + length);
}
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onSemanticTypeAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
parentMessage.limit(limit + headerLength + length);
@@ -1217,6 +1766,11 @@ public TokenCodecEncoder putSemanticType(final byte[] src, final int srcOffset,
throw new IllegalStateException("length > maxValue for type: " + length);
}
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onSemanticTypeAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
parentMessage.limit(limit + headerLength + length);
@@ -1236,6 +1790,11 @@ public TokenCodecEncoder semanticType(final String value)
throw new IllegalStateException("length > maxValue for type: " + length);
}
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onSemanticTypeAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
parentMessage.limit(limit + headerLength + length);
@@ -1270,6 +1829,21 @@ public static int descriptionHeaderLength()
return 2;
}
+ private void onDescriptionAccessed()
+ {
+ switch (codecState())
+ {
+ case CodecStates.V0_SEMANTICTYPE_DONE:
+ codecState(CodecStates.V0_DESCRIPTION_DONE);
+ break;
+ default:
+ throw new IllegalStateException("Illegal field access order. " +
+ "Cannot access field \"description\" in state: " + CodecStates.name(codecState()) +
+ ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) +
+ "]. Please see the diagram in the Javadoc of the class TokenCodecEncoder#CodecStates.");
+ }
+ }
+
public TokenCodecEncoder putDescription(final DirectBuffer src, final int srcOffset, final int length)
{
if (length > 65534)
@@ -1277,6 +1851,11 @@ public TokenCodecEncoder putDescription(final DirectBuffer src, final int srcOff
throw new IllegalStateException("length > maxValue for type: " + length);
}
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onDescriptionAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
parentMessage.limit(limit + headerLength + length);
@@ -1293,6 +1872,11 @@ public TokenCodecEncoder putDescription(final byte[] src, final int srcOffset, f
throw new IllegalStateException("length > maxValue for type: " + length);
}
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onDescriptionAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
parentMessage.limit(limit + headerLength + length);
@@ -1312,6 +1896,11 @@ public TokenCodecEncoder description(final String value)
throw new IllegalStateException("length > maxValue for type: " + length);
}
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onDescriptionAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
parentMessage.limit(limit + headerLength + length);
@@ -1346,6 +1935,21 @@ public static int referencedNameHeaderLength()
return 2;
}
+ private void onReferencedNameAccessed()
+ {
+ switch (codecState())
+ {
+ case CodecStates.V0_DESCRIPTION_DONE:
+ codecState(CodecStates.V0_REFERENCEDNAME_DONE);
+ break;
+ default:
+ throw new IllegalStateException("Illegal field access order. " +
+ "Cannot access field \"referencedName\" in state: " + CodecStates.name(codecState()) +
+ ". Expected one of these transitions: [" + CodecStates.transitions(codecState()) +
+ "]. Please see the diagram in the Javadoc of the class TokenCodecEncoder#CodecStates.");
+ }
+ }
+
public TokenCodecEncoder putReferencedName(final DirectBuffer src, final int srcOffset, final int length)
{
if (length > 65534)
@@ -1353,6 +1957,11 @@ public TokenCodecEncoder putReferencedName(final DirectBuffer src, final int src
throw new IllegalStateException("length > maxValue for type: " + length);
}
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onReferencedNameAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
parentMessage.limit(limit + headerLength + length);
@@ -1369,6 +1978,11 @@ public TokenCodecEncoder putReferencedName(final byte[] src, final int srcOffset
throw new IllegalStateException("length > maxValue for type: " + length);
}
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onReferencedNameAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
parentMessage.limit(limit + headerLength + length);
@@ -1388,6 +2002,11 @@ public TokenCodecEncoder referencedName(final String value)
throw new IllegalStateException("length > maxValue for type: " + length);
}
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ onReferencedNameAccessed();
+ }
+
final int headerLength = 2;
final int limit = parentMessage.limit();
parentMessage.limit(limit + headerLength + length);
@@ -1419,4 +2038,21 @@ public StringBuilder appendTo(final StringBuilder builder)
return decoder.appendTo(builder);
}
+
+ public void checkEncodingIsComplete()
+ {
+ if (SBE_ENABLE_IR_PRECEDENCE_CHECKS)
+ {
+ switch (codecState)
+ {
+ case CodecStates.V0_REFERENCEDNAME_DONE:
+ return;
+ default:
+ throw new IllegalStateException("Not fully encoded, current state: " +
+ CodecStates.name(codecState) + ", allowed transitions: " +
+ CodecStates.transitions(codecState));
+ }
+ }
+ }
+
}
diff --git a/sbe-tool/src/test/cpp/BoundsCheckTest.cpp b/sbe-tool/src/test/cpp/BoundsCheckTest.cpp
index e3b81677dc..2adf48f06c 100644
--- a/sbe-tool/src/test/cpp/BoundsCheckTest.cpp
+++ b/sbe-tool/src/test/cpp/BoundsCheckTest.cpp
@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#include
#include
#include
@@ -217,43 +216,43 @@ class BoundsCheckTest : public testing::Test
performanceFigures.next();
EXPECT_EQ(performanceFigures.octaneRating(), 95);
- Car::PerformanceFigures::Acceleration &acceleration = performanceFigures.acceleration();
- EXPECT_EQ(acceleration.count(), 3u);
- EXPECT_TRUE(acceleration.hasNext());
- acceleration.next();
- EXPECT_EQ(acceleration.mph(), 30);
- EXPECT_EQ(acceleration.seconds(), 4.0f);
+ Car::PerformanceFigures::Acceleration &acceleration1 = performanceFigures.acceleration();
+ EXPECT_EQ(acceleration1.count(), 3u);
+ EXPECT_TRUE(acceleration1.hasNext());
+ acceleration1.next();
+ EXPECT_EQ(acceleration1.mph(), 30);
+ EXPECT_EQ(acceleration1.seconds(), 4.0f);
- EXPECT_TRUE(acceleration.hasNext());
- acceleration.next();
- EXPECT_EQ(acceleration.mph(), 60);
- EXPECT_EQ(acceleration.seconds(), 7.5f);
+ EXPECT_TRUE(acceleration1.hasNext());
+ acceleration1.next();
+ EXPECT_EQ(acceleration1.mph(), 60);
+ EXPECT_EQ(acceleration1.seconds(), 7.5f);
- EXPECT_TRUE(acceleration.hasNext());
- acceleration.next();
- EXPECT_EQ(acceleration.mph(), 100);
- EXPECT_EQ(acceleration.seconds(), 12.2f);
+ EXPECT_TRUE(acceleration1.hasNext());
+ acceleration1.next();
+ EXPECT_EQ(acceleration1.mph(), 100);
+ EXPECT_EQ(acceleration1.seconds(), 12.2f);
EXPECT_TRUE(performanceFigures.hasNext());
performanceFigures.next();
EXPECT_EQ(performanceFigures.octaneRating(), 99);
- acceleration = performanceFigures.acceleration();
- EXPECT_EQ(acceleration.count(), 3u);
- EXPECT_TRUE(acceleration.hasNext());
- acceleration.next();
- EXPECT_EQ(acceleration.mph(), 30);
- EXPECT_EQ(acceleration.seconds(), 3.8f);
-
- EXPECT_TRUE(acceleration.hasNext());
- acceleration.next();
- EXPECT_EQ(acceleration.mph(), 60);
- EXPECT_EQ(acceleration.seconds(), 7.1f);
-
- EXPECT_TRUE(acceleration.hasNext());
- acceleration.next();
- EXPECT_EQ(acceleration.mph(), 100);
- EXPECT_EQ(acceleration.seconds(), 11.8f);
+ Car::PerformanceFigures::Acceleration &acceleration2 = performanceFigures.acceleration();
+ EXPECT_EQ(acceleration2.count(), 3u);
+ EXPECT_TRUE(acceleration2.hasNext());
+ acceleration2.next();
+ EXPECT_EQ(acceleration2.mph(), 30);
+ EXPECT_EQ(acceleration2.seconds(), 3.8f);
+
+ EXPECT_TRUE(acceleration2.hasNext());
+ acceleration2.next();
+ EXPECT_EQ(acceleration2.mph(), 60);
+ EXPECT_EQ(acceleration2.seconds(), 7.1f);
+
+ EXPECT_TRUE(acceleration2.hasNext());
+ acceleration2.next();
+ EXPECT_EQ(acceleration2.mph(), 100);
+ EXPECT_EQ(acceleration2.seconds(), 11.8f);
return m_carDecoder.encodedLength();
}
diff --git a/sbe-tool/src/test/cpp/CMakeLists.txt b/sbe-tool/src/test/cpp/CMakeLists.txt
index 03d1ab1cfb..8c698c83c3 100644
--- a/sbe-tool/src/test/cpp/CMakeLists.txt
+++ b/sbe-tool/src/test/cpp/CMakeLists.txt
@@ -18,6 +18,7 @@ function(sbe_test name)
add_executable("${name}" "${name}.cpp")
target_include_directories("${name}"
PRIVATE ${GTEST_SOURCE_DIR}/googletest/include
+ PRIVATE ${GTEST_SOURCE_DIR}/googlemock/include
PRIVATE ${CXX_CODEC_TARGET_DIR}
)
target_compile_options("${name}" PRIVATE $<$:-Werror>)
@@ -40,6 +41,7 @@ set(MESSAGE_BLOCK_LENGTH_TEST ${CODEC_SCHEMA_DIR}/message-block-length-test.xml)
set(GROUP_WITH_DATA_SCHEMA ${CODEC_SCHEMA_DIR}/group-with-data-schema.xml)
set(ISSUE835_SCHEMA ${CODEC_SCHEMA_DIR}/issue835.xml)
set(ISSUE889_SCHEMA ${CODEC_SCHEMA_DIR}/issue889.xml)
+set(ACCESS_ORDER_SCHEMA ${CODEC_SCHEMA_DIR}/field-order-check-schema.xml)
set(GENERATED_CODECS
${CXX_CODEC_TARGET_DIR}
@@ -54,12 +56,16 @@ add_custom_command(
${GROUP_WITH_DATA_SCHEMA}
${ISSUE835_SCHEMA}
${ISSUE889_SCHEMA}
+ ${ACCESS_ORDER_SCHEMA}
sbe-jar ${SBE_JAR}
COMMAND
${Java_JAVA_EXECUTABLE}
-Dsbe.output.dir=${CXX_CODEC_TARGET_DIR}
-Dsbe.generate.ir="true"
-Dsbe.target.language="cpp"
+ -Dsbe.generate.precedence.checks="true"
+ -Dsbe.precedence.checks.flag.name="SBE_ENABLE_PRECEDENCE_CHECKS_IN_TESTS"
+ -Dsbe.cpp.disable.implicit.copying="true"
-jar ${SBE_JAR}
${CODE_GENERATION_SCHEMA}
${COMPOSITE_OFFSETS_SCHEMA}
@@ -68,6 +74,7 @@ add_custom_command(
${COMPOSITE_ELEMENTS_SCHEMA}
${ISSUE835_SCHEMA}
${ISSUE889_SCHEMA}
+ ${ACCESS_ORDER_SCHEMA}
)
add_custom_target(codecs DEPENDS ${GENERATED_CODECS})
@@ -83,3 +90,5 @@ sbe_test(Rc3OtfFullIrTest codecs)
sbe_test(CompositeElementsTest codecs)
sbe_test(Issue835Test codecs)
sbe_test(Issue889Test codecs)
+sbe_test(FieldAccessOrderCheckTest codecs)
+target_compile_definitions(FieldAccessOrderCheckTest PRIVATE SBE_ENABLE_PRECEDENCE_CHECKS_IN_TESTS)
diff --git a/sbe-tool/src/test/cpp/CodeGenTest.cpp b/sbe-tool/src/test/cpp/CodeGenTest.cpp
index 698d7ec849..6675172c8d 100644
--- a/sbe-tool/src/test/cpp/CodeGenTest.cpp
+++ b/sbe-tool/src/test/cpp/CodeGenTest.cpp
@@ -680,43 +680,43 @@ TEST_F(CodeGenTest, shouldBeAbleToEncodeAndDecodeHeaderPlusCarCorrectly)
performanceFigures.next();
EXPECT_EQ(performanceFigures.octaneRating(), perf1Octane);
- Car::PerformanceFigures::Acceleration &acceleration = performanceFigures.acceleration();
- EXPECT_EQ(acceleration.count(), ACCELERATION_COUNT);
- ASSERT_TRUE(acceleration.hasNext());
- acceleration.next();
- EXPECT_EQ(acceleration.mph(), perf1aMph);
- EXPECT_EQ(acceleration.seconds(), perf1aSeconds);
-
- ASSERT_TRUE(acceleration.hasNext());
- acceleration.next();
- EXPECT_EQ(acceleration.mph(), perf1bMph);
- EXPECT_EQ(acceleration.seconds(), perf1bSeconds);
-
- ASSERT_TRUE(acceleration.hasNext());
- acceleration.next();
- EXPECT_EQ(acceleration.mph(), perf1cMph);
- EXPECT_EQ(acceleration.seconds(), perf1cSeconds);
+ Car::PerformanceFigures::Acceleration &acceleration1 = performanceFigures.acceleration();
+ EXPECT_EQ(acceleration1.count(), ACCELERATION_COUNT);
+ ASSERT_TRUE(acceleration1.hasNext());
+ acceleration1.next();
+ EXPECT_EQ(acceleration1.mph(), perf1aMph);
+ EXPECT_EQ(acceleration1.seconds(), perf1aSeconds);
+
+ ASSERT_TRUE(acceleration1.hasNext());
+ acceleration1.next();
+ EXPECT_EQ(acceleration1.mph(), perf1bMph);
+ EXPECT_EQ(acceleration1.seconds(), perf1bSeconds);
+
+ ASSERT_TRUE(acceleration1.hasNext());
+ acceleration1.next();
+ EXPECT_EQ(acceleration1.mph(), perf1cMph);
+ EXPECT_EQ(acceleration1.seconds(), perf1cSeconds);
ASSERT_TRUE(performanceFigures.hasNext());
performanceFigures.next();
EXPECT_EQ(performanceFigures.octaneRating(), perf2Octane);
- acceleration = performanceFigures.acceleration();
- EXPECT_EQ(acceleration.count(), ACCELERATION_COUNT);
- ASSERT_TRUE(acceleration.hasNext());
- acceleration.next();
- EXPECT_EQ(acceleration.mph(), perf2aMph);
- EXPECT_EQ(acceleration.seconds(), perf2aSeconds);
+ Car::PerformanceFigures::Acceleration &acceleration2 = performanceFigures.acceleration();
+ EXPECT_EQ(acceleration2.count(), ACCELERATION_COUNT);
+ ASSERT_TRUE(acceleration2.hasNext());
+ acceleration2.next();
+ EXPECT_EQ(acceleration2.mph(), perf2aMph);
+ EXPECT_EQ(acceleration2.seconds(), perf2aSeconds);
- ASSERT_TRUE(acceleration.hasNext());
- acceleration.next();
- EXPECT_EQ(acceleration.mph(), perf2bMph);
- EXPECT_EQ(acceleration.seconds(), perf2bSeconds);
+ ASSERT_TRUE(acceleration2.hasNext());
+ acceleration2.next();
+ EXPECT_EQ(acceleration2.mph(), perf2bMph);
+ EXPECT_EQ(acceleration2.seconds(), perf2bSeconds);
- ASSERT_TRUE(acceleration.hasNext());
- acceleration.next();
- EXPECT_EQ(acceleration.mph(), perf2cMph);
- EXPECT_EQ(acceleration.seconds(), perf2cSeconds);
+ ASSERT_TRUE(acceleration2.hasNext());
+ acceleration2.next();
+ EXPECT_EQ(acceleration2.mph(), perf2cMph);
+ EXPECT_EQ(acceleration2.seconds(), perf2cSeconds);
EXPECT_EQ(m_carDecoder.manufacturerLength(), MANUFACTURER_LENGTH);
EXPECT_EQ(std::string(m_carDecoder.manufacturer(), MANUFACTURER_LENGTH), MANUFACTURER);
@@ -748,7 +748,7 @@ struct CallbacksForEach
void operator()(Car::PerformanceFigures &performanceFigures)
{
- Car::PerformanceFigures::Acceleration acceleration = performanceFigures.acceleration();
+ Car::PerformanceFigures::Acceleration &acceleration = performanceFigures.acceleration();
countOfPerformanceFigures++;
acceleration.forEach(*this);
@@ -806,7 +806,7 @@ TEST_F(CodeGenTest, shouldBeAbleUseOnStackCodecsAndGroupForEach)
#if __cplusplus >= 201103L
performanceFigures.forEach([&](Car::PerformanceFigures &figures)
{
- Car::PerformanceFigures::Acceleration acceleration = figures.acceleration();
+ Car::PerformanceFigures::Acceleration &acceleration = figures.acceleration();
cbs.countOfPerformanceFigures++;
acceleration.forEach(
@@ -964,13 +964,13 @@ TEST_F(CodeGenTest, shouldBeAbleToUseStdStringMethodsForDecode)
Car::PerformanceFigures &perfFigures = carDecoder.performanceFigures();
perfFigures.next();
- Car::PerformanceFigures::Acceleration &acceleration = perfFigures.acceleration();
+ Car::PerformanceFigures::Acceleration &acceleration1 = perfFigures.acceleration();
- acceleration.next().next().next();
+ acceleration1.next().next().next();
perfFigures.next();
- acceleration = perfFigures.acceleration();
- acceleration.next().next().next();
+ Car::PerformanceFigures::Acceleration &acceleration2 = perfFigures.acceleration();
+ acceleration2.next().next().next();
EXPECT_EQ(carDecoder.getManufacturerAsString(), manufacturer);
EXPECT_EQ(carDecoder.getModelAsString(), model);
@@ -1037,15 +1037,15 @@ TEST_F(CodeGenTest, shouldPrintFullDecodedFlyweightRegardlessOfReadPosition)
Car::PerformanceFigures &perfFigures = carDecoder.performanceFigures();
perfFigures.next();
- Car::PerformanceFigures::Acceleration &acceleration = perfFigures.acceleration();
+ Car::PerformanceFigures::Acceleration &acceleration1 = perfFigures.acceleration();
- acceleration.next().next().next();
+ acceleration1.next().next().next();
expectDisplayString(expectedDisplayString, carDecoder);
perfFigures.next();
- acceleration = perfFigures.acceleration();
- acceleration.next().next().next();
+ Car::PerformanceFigures::Acceleration &acceleration2 = perfFigures.acceleration();
+ acceleration2.next().next().next();
EXPECT_EQ(carDecoder.getManufacturerAsString(), manufacturer);
EXPECT_EQ(carDecoder.getModelAsString(), model);
diff --git a/sbe-tool/src/test/cpp/CompositeOffsetsCodeGenTest.cpp b/sbe-tool/src/test/cpp/CompositeOffsetsCodeGenTest.cpp
index 28bcbb1880..39759c1d50 100644
--- a/sbe-tool/src/test/cpp/CompositeOffsetsCodeGenTest.cpp
+++ b/sbe-tool/src/test/cpp/CompositeOffsetsCodeGenTest.cpp
@@ -141,7 +141,7 @@ TEST_F(CompositeOffsetsCodeGenTest, shouldBeAbleToDecodeHeaderAndMsgCorrectly)
m_msgDecoder.wrapForDecode(
buffer, hdrSz, TestMessage1::sbeBlockLength(), TestMessage1::sbeSchemaVersion(), hdrSz + sz);
- TestMessage1::Entries entries = m_msgDecoder.entries();
+ TestMessage1::Entries &entries = m_msgDecoder.entries();
EXPECT_EQ(entries.count(), 2u);
ASSERT_TRUE(entries.hasNext());
diff --git a/sbe-tool/src/test/cpp/FieldAccessOrderCheckTest.cpp b/sbe-tool/src/test/cpp/FieldAccessOrderCheckTest.cpp
new file mode 100644
index 0000000000..89aeeca267
--- /dev/null
+++ b/sbe-tool/src/test/cpp/FieldAccessOrderCheckTest.cpp
@@ -0,0 +1,4937 @@
+/*
+ * Copyright 2013-2023 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.
+ */
+
+#include "gtest/gtest.h"
+#include "gmock/gmock.h"
+#include "order_check/MultipleVarLength.h"
+#include "order_check/GroupAndVarLength.h"
+#include "order_check/VarLengthInsideGroup.h"
+#include "order_check/NestedGroups.h"
+#include "order_check/CompositeInsideGroup.h"
+#include "order_check/AddPrimitiveV1.h"
+#include "order_check/AddPrimitiveV0.h"
+#include "order_check/AddPrimitiveBeforeGroupV1.h"
+#include "order_check/AddPrimitiveBeforeGroupV0.h"
+#include "order_check/AddPrimitiveBeforeVarDataV1.h"
+#include "order_check/AddPrimitiveBeforeVarDataV0.h"
+#include "order_check/AddPrimitiveInsideGroupV1.h"
+#include "order_check/AddPrimitiveInsideGroupV0.h"
+#include "order_check/AddGroupBeforeVarDataV1.h"
+#include "order_check/AddGroupBeforeVarDataV0.h"
+#include "order_check/AddEnumBeforeGroupV1.h"
+#include "order_check/AddEnumBeforeGroupV0.h"
+#include "order_check/AddCompositeBeforeGroupV1.h"
+#include "order_check/AddCompositeBeforeGroupV0.h"
+#include "order_check/AddArrayBeforeGroupV1.h"
+#include "order_check/AddArrayBeforeGroupV0.h"
+#include "order_check/AddBitSetBeforeGroupV1.h"
+#include "order_check/AddBitSetBeforeGroupV0.h"
+#include "order_check/EnumInsideGroup.h"
+#include "order_check/BitSetInsideGroup.h"
+#include "order_check/ArrayInsideGroup.h"
+#include "order_check/MultipleGroups.h"
+#include "order_check/AddVarDataV1.h"
+#include "order_check/AddVarDataV0.h"
+#include "order_check/AsciiInsideGroup.h"
+#include "order_check/AddAsciiBeforeGroupV1.h"
+#include "order_check/AddAsciiBeforeGroupV0.h"
+#include "order_check/NoBlock.h"
+#include "order_check/GroupWithNoBlock.h"
+#include "order_check/NestedGroupWithVarLength.h"
+
+using namespace order::check;
+using ::testing::HasSubstr;
+
+class FieldAccessOrderCheckTest : public testing::Test
+{
+public:
+ static const std::size_t BUFFER_LEN = 2048u;
+ static const std::size_t OFFSET = 0;
+
+ char m_buffer[BUFFER_LEN] = {};
+
+};
+
+TEST_F(FieldAccessOrderCheckTest, allowsEncodingAndDecodingVariableLengthFieldsInSchemaDefinedOrder1)
+{
+ MultipleVarLength encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(42);
+ encoder.putB("abc");
+ encoder.putC("def");
+ encoder.checkEncodingIsComplete();
+
+ MultipleVarLength decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ MultipleVarLength::sbeBlockLength(),
+ MultipleVarLength::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+
+ EXPECT_EQ(decoder.a(), 42);
+ EXPECT_EQ(decoder.getBAsString(), "abc");
+ EXPECT_EQ(decoder.getCAsString(), "def");
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsEncodingAndDecodingVariableLengthFieldsInSchemaDefinedOrder2)
+{
+ MultipleVarLength encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(42);
+ encoder.putB("abc");
+ encoder.putC("def");
+ encoder.checkEncodingIsComplete();
+
+ MultipleVarLength decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ MultipleVarLength::sbeBlockLength(),
+ MultipleVarLength::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+
+ EXPECT_EQ(decoder.a(), 42);
+ auto bLength = decoder.bLength();
+ auto *b = decoder.b();
+ const std::string bStr(b, bLength);
+ EXPECT_EQ(bStr, "abc");
+ auto cLength = decoder.cLength();
+ auto *c = decoder.c();
+ const std::string cStr(c, cLength);
+ EXPECT_EQ(cStr, "def");
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsEncodingAndDecodingVariableLengthFieldsInSchemaDefinedOrder3)
+{
+ MultipleVarLength encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(42);
+ auto *bIn = "abc";
+ encoder.putB(bIn, strlen(bIn));
+ auto *cIn = "def";
+ encoder.putC(cIn, strlen(cIn));
+ encoder.checkEncodingIsComplete();
+
+ MultipleVarLength decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ MultipleVarLength::sbeBlockLength(),
+ MultipleVarLength::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+
+ EXPECT_EQ(decoder.a(), 42);
+
+ const auto bLength = decoder.bLength();
+ auto *bOut = new char[bLength];
+ decoder.getB(bOut, bLength);
+ EXPECT_EQ(std::strncmp(bOut, "abc", bLength), 0);
+ delete[] bOut;
+
+ auto cLength = decoder.cLength();
+ auto *cOut = new char[cLength];
+ decoder.getC(cOut, cLength);
+ EXPECT_EQ(std::strncmp(cOut, "def", cLength), 0);
+ delete[] cOut;
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsDecodingVariableLengthFieldsAfterRewind)
+{
+ MultipleVarLength encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(42);
+ encoder.putB("abc");
+ encoder.putC("def");
+ encoder.checkEncodingIsComplete();
+
+ MultipleVarLength decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ MultipleVarLength::sbeBlockLength(),
+ MultipleVarLength::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+
+ EXPECT_EQ(decoder.a(), 42);
+ EXPECT_EQ(decoder.getBAsString(), "abc");
+ EXPECT_EQ(decoder.getCAsString(), "def");
+
+ decoder.sbeRewind();
+
+ EXPECT_EQ(decoder.a(), 42);
+ EXPECT_EQ(decoder.getBAsString(), "abc");
+ EXPECT_EQ(decoder.getCAsString(), "def");
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsDecodingToSkipVariableLengthFields)
+{
+ MultipleVarLength encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(42);
+ encoder.putB("abc");
+ encoder.putC("def");
+ encoder.checkEncodingIsComplete();
+
+ MultipleVarLength decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ MultipleVarLength::sbeBlockLength(),
+ MultipleVarLength::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+
+ EXPECT_EQ(decoder.a(), 42);
+ EXPECT_EQ(decoder.skipB(), 3ul);
+ EXPECT_EQ(decoder.getCAsString(), "def");
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsReEncodingTopLevelPrimitiveFields)
+{
+ MultipleVarLength encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(42);
+ encoder.putB("abc");
+ encoder.putC("def");
+ encoder.a(43);
+ encoder.checkEncodingIsComplete();
+
+ MultipleVarLength decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ MultipleVarLength::sbeBlockLength(),
+ MultipleVarLength::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+
+ EXPECT_EQ(decoder.a(), 43);
+ EXPECT_EQ(decoder.getBAsString(), "abc");
+ EXPECT_EQ(decoder.getCAsString(), "def");
+}
+
+TEST_F(FieldAccessOrderCheckTest, disallowsSkippingEncodingOfVariableLengthField1)
+{
+ MultipleVarLength encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(42);
+ EXPECT_THROW(
+ {
+ try
+ {
+ encoder.putC("def");
+ }
+ catch (const std::exception &e)
+ {
+ EXPECT_THAT(
+ e.what(),
+ HasSubstr("Cannot access field \"c\" in state: V0_BLOCK")
+ );
+ throw;
+ }
+ },
+ std::logic_error
+ );
+}
+
+TEST_F(FieldAccessOrderCheckTest, disallowsSkippingEncodingOfVariableLengthField2)
+{
+ MultipleVarLength encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(42);
+ EXPECT_THROW(
+ {
+ try
+ {
+ auto *cIn = "cIn";
+ encoder.putC(cIn, std::strlen(cIn));
+ }
+ catch (const std::exception &e)
+ {
+ EXPECT_THAT(
+ e.what(),
+ HasSubstr("Cannot access field \"c\" in state: V0_BLOCK")
+ );
+ throw;
+ }
+ },
+ std::logic_error
+ );
+}
+
+TEST_F(FieldAccessOrderCheckTest, disallowsReEncodingEarlierVariableLengthFields)
+{
+ MultipleVarLength encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(42);
+ encoder.putB("abc");
+ encoder.putC("def");
+ EXPECT_THROW(
+ {
+ try
+ {
+ encoder.putB("ghi");
+ }
+ catch (const std::exception &e)
+ {
+ EXPECT_THAT(
+ e.what(),
+ HasSubstr("Cannot access field \"b\" in state: V0_C_DONE")
+ );
+ throw;
+ }
+ },
+ std::logic_error
+ );
+}
+
+TEST_F(FieldAccessOrderCheckTest, disallowsReEncodingLatestVariableLengthField)
+{
+ MultipleVarLength encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(42);
+ encoder.putB("abc");
+ encoder.putC("def");
+ EXPECT_THROW(
+ {
+ try
+ {
+ encoder.putC("ghi");
+ }
+ catch (const std::exception &e)
+ {
+ EXPECT_THAT(
+ e.what(),
+ HasSubstr("Cannot access field \"c\" in state: V0_C_DONE")
+ );
+ throw;
+ }
+ },
+ std::logic_error
+ );
+}
+
+TEST_F(FieldAccessOrderCheckTest, disallowsSkippingDecodingOfVariableLengthField1)
+{
+ MultipleVarLength encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(42);
+ encoder.putB("abc");
+ encoder.putC("def");
+ encoder.checkEncodingIsComplete();
+
+ MultipleVarLength decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ MultipleVarLength::sbeBlockLength(),
+ MultipleVarLength::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+
+ EXPECT_EQ(decoder.a(), 42);
+
+ EXPECT_THROW(
+ {
+ try
+ {
+ decoder.getCAsString();
+ }
+ catch (const std::exception &e)
+ {
+ EXPECT_THAT(
+ e.what(),
+ HasSubstr("Cannot access field \"c\" in state: V0_BLOCK")
+ );
+ throw;
+ }
+ },
+ std::logic_error
+ );
+}
+
+TEST_F(FieldAccessOrderCheckTest, disallowsSkippingDecodingOfVariableLengthField2)
+{
+ MultipleVarLength encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(42);
+ encoder.putB("abc");
+ encoder.putC("def");
+ encoder.checkEncodingIsComplete();
+
+ MultipleVarLength decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ MultipleVarLength::sbeBlockLength(),
+ MultipleVarLength::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+
+ EXPECT_EQ(decoder.a(), 42);
+
+ EXPECT_THROW(
+ {
+ try
+ {
+ decoder.cLength();
+ }
+ catch (const std::exception &e)
+ {
+ EXPECT_THAT(
+ e.what(),
+ HasSubstr("Cannot decode length of var data \"c\" in state: V0_BLOCK")
+ );
+ throw;
+ }
+ },
+ std::logic_error
+ );
+}
+
+TEST_F(FieldAccessOrderCheckTest, disallowsSkippingDecodingOfVariableLengthField3)
+{
+ MultipleVarLength encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(42);
+ encoder.putB("abc");
+ encoder.putC("def");
+ encoder.checkEncodingIsComplete();
+
+ MultipleVarLength decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ MultipleVarLength::sbeBlockLength(),
+ MultipleVarLength::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+
+ EXPECT_EQ(decoder.a(), 42);
+
+ EXPECT_THROW(
+ {
+ try
+ {
+ char cOut[3];
+ decoder.getC(cOut, 3);
+ }
+ catch (const std::exception &e)
+ {
+ EXPECT_THAT(
+ e.what(),
+ HasSubstr("Cannot access field \"c\" in state: V0_BLOCK")
+ );
+ throw;
+ }
+ },
+ std::logic_error
+ );
+}
+
+TEST_F(FieldAccessOrderCheckTest, disallowsReDecodingEarlierVariableLengthField)
+{
+ MultipleVarLength encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(42);
+ encoder.putB("abc");
+ encoder.putC("def");
+ encoder.checkEncodingIsComplete();
+
+ MultipleVarLength decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ MultipleVarLength::sbeBlockLength(),
+ MultipleVarLength::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+
+ EXPECT_EQ(decoder.a(), 42);
+ EXPECT_EQ(decoder.getBAsString(), "abc");
+ EXPECT_EQ(decoder.getCAsString(), "def");
+
+ EXPECT_THROW(
+ {
+ try
+ {
+ decoder.getBAsString();
+ }
+ catch (const std::exception &e)
+ {
+ EXPECT_THAT(
+ e.what(),
+ HasSubstr("Cannot access field \"b\" in state: V0_C_DONE")
+ );
+ throw;
+ }
+ },
+ std::logic_error
+ );
+}
+
+TEST_F(FieldAccessOrderCheckTest, disallowsReDecodingLatestVariableLengthField)
+{
+ MultipleVarLength encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(42);
+ encoder.putB("abc");
+ encoder.putC("def");
+ encoder.checkEncodingIsComplete();
+
+ MultipleVarLength decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ MultipleVarLength::sbeBlockLength(),
+ MultipleVarLength::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+
+ EXPECT_EQ(decoder.a(), 42);
+ EXPECT_EQ(decoder.getBAsString(), "abc");
+ EXPECT_EQ(decoder.getCAsString(), "def");
+
+ EXPECT_THROW(
+ {
+ try
+ {
+ decoder.getCAsString();
+ }
+ catch (const std::exception &e)
+ {
+ EXPECT_THAT(
+ e.what(),
+ HasSubstr("Cannot access field \"c\" in state: V0_C_DONE")
+ );
+ throw;
+ }
+ },
+ std::logic_error
+ );
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsEncodingAndDecodingGroupAndVariableLengthFieldsInSchemaDefinedOrder)
+{
+ GroupAndVarLength encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(42);
+ GroupAndVarLength::B &b = encoder.bCount(2);
+ b.next().c(1);
+ b.next().c(2);
+ encoder.putD("abc");
+ encoder.checkEncodingIsComplete();
+
+ GroupAndVarLength decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ GroupAndVarLength::sbeBlockLength(),
+ GroupAndVarLength::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+
+ EXPECT_EQ(decoder.a(), 42);
+ GroupAndVarLength::B &bDecoder = decoder.b();
+ EXPECT_EQ(bDecoder.count(), 2u);
+ EXPECT_EQ(bDecoder.next().c(), 1);
+ EXPECT_EQ(bDecoder.next().c(), 2);
+ EXPECT_EQ(decoder.getDAsString(), "abc");
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsDecodingGroupAndVariableLengthFieldsAfterRewind)
+{
+ GroupAndVarLength encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(42);
+ GroupAndVarLength::B &b = encoder.bCount(2);
+ b.next().c(1);
+ b.next().c(2);
+ encoder.putD("abc");
+ encoder.checkEncodingIsComplete();
+
+ GroupAndVarLength decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ GroupAndVarLength::sbeBlockLength(),
+ GroupAndVarLength::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+
+ EXPECT_EQ(decoder.a(), 42);
+ GroupAndVarLength::B &bDecoder = decoder.b();
+ EXPECT_EQ(bDecoder.count(), 2u);
+ EXPECT_EQ(bDecoder.next().c(), 1);
+ EXPECT_EQ(bDecoder.next().c(), 2);
+ EXPECT_EQ(decoder.getDAsString(), "abc");
+
+ decoder.sbeRewind();
+
+ EXPECT_EQ(decoder.a(), 42);
+ GroupAndVarLength::B &bDecoder2 = decoder.b();
+ EXPECT_EQ(bDecoder2.count(), 2u);
+ EXPECT_EQ(bDecoder2.next().c(), 1);
+ EXPECT_EQ(bDecoder2.next().c(), 2);
+ EXPECT_EQ(decoder.getDAsString(), "abc");
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsDecodingToSkipMessage)
+{
+ GroupAndVarLength encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(42);
+ GroupAndVarLength::B &b = encoder.bCount(2);
+ b.next().c(1);
+ b.next().c(2);
+ encoder.putD("abc");
+ encoder.checkEncodingIsComplete();
+
+ const std::uint64_t nextEncodeOffset = encoder.sbePosition();
+ encoder.wrapForEncode(m_buffer, nextEncodeOffset, BUFFER_LEN);
+ encoder.a(43);
+ GroupAndVarLength::B &b2 = encoder.bCount(2);
+ b2.next().c(3);
+ b2.next().c(4);
+ encoder.putD("def");
+ encoder.checkEncodingIsComplete();
+
+ GroupAndVarLength decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ GroupAndVarLength::sbeBlockLength(),
+ GroupAndVarLength::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+
+ decoder.skip();
+ const std::uint64_t nextDecodeOffset = decoder.sbePosition();
+ EXPECT_EQ(nextDecodeOffset, nextEncodeOffset);
+
+ decoder.wrapForDecode(
+ m_buffer,
+ nextDecodeOffset,
+ GroupAndVarLength::sbeBlockLength(),
+ GroupAndVarLength::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 43);
+ GroupAndVarLength::B &bDecoder = decoder.b();
+ EXPECT_EQ(bDecoder.count(), 2u);
+ EXPECT_EQ(bDecoder.next().c(), 3);
+ EXPECT_EQ(bDecoder.next().c(), 4);
+ EXPECT_EQ(decoder.getDAsString(), "def");
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsDecodingToDetermineMessageLengthBeforeReadingFields)
+{
+ GroupAndVarLength encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(43);
+ GroupAndVarLength::B &b = encoder.bCount(2);
+ b.next().c(3);
+ b.next().c(4);
+ encoder.putD("def");
+ encoder.checkEncodingIsComplete();
+
+ GroupAndVarLength decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ GroupAndVarLength::sbeBlockLength(),
+ GroupAndVarLength::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+
+ EXPECT_EQ(decoder.decodeLength(), 18u);
+ EXPECT_EQ(decoder.a(), 43);
+ GroupAndVarLength::B &bDecoder = decoder.b();
+ EXPECT_EQ(bDecoder.count(), 2u);
+ EXPECT_EQ(bDecoder.next().c(), 3);
+ EXPECT_EQ(bDecoder.next().c(), 4);
+ EXPECT_EQ(decoder.getDAsString(), "def");
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsEncodingAndDecodingEmptyGroupAndVariableLengthFieldsInSchemaDefinedOrder)
+{
+ GroupAndVarLength encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(42);
+ encoder.bCount(0);
+ encoder.putD("abc");
+ encoder.checkEncodingIsComplete();
+
+ GroupAndVarLength decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ GroupAndVarLength::sbeBlockLength(),
+ GroupAndVarLength::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 42);
+ GroupAndVarLength::B &bDecoder = decoder.b();
+ EXPECT_EQ(bDecoder.count(), 0u);
+ EXPECT_EQ(decoder.getDAsString(), "abc");
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsEncoderToResetZeroGroupLengthToNonZero)
+{
+ GroupAndVarLength encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(42);
+ encoder.bCount(0).resetCountToIndex();
+ encoder.putD("abc");
+ encoder.checkEncodingIsComplete();
+
+ GroupAndVarLength decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ GroupAndVarLength::sbeBlockLength(),
+ GroupAndVarLength::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 42);
+ GroupAndVarLength::B &bDecoder = decoder.b();
+ EXPECT_EQ(bDecoder.count(), 0u);
+ EXPECT_EQ(decoder.getDAsString(), "abc");
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsEncoderToResetNonZeroGroupLengthToNonZeroBeforeCallingNext)
+{
+ GroupAndVarLength encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(42);
+ encoder.bCount(2).resetCountToIndex();
+ encoder.putD("abc");
+ encoder.checkEncodingIsComplete();
+
+ GroupAndVarLength decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ GroupAndVarLength::sbeBlockLength(),
+ GroupAndVarLength::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 42);
+ GroupAndVarLength::B &bDecoder = decoder.b();
+ EXPECT_EQ(bDecoder.count(), 0u);
+ EXPECT_EQ(decoder.getDAsString(), "abc");
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsEncoderToResetNonZeroGroupLengthToNonZero)
+{
+ GroupAndVarLength encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(42);
+ GroupAndVarLength::B &bEncoder = encoder.bCount(2).next();
+ bEncoder.c(43);
+ bEncoder.resetCountToIndex();
+ encoder.putD("abc");
+ encoder.checkEncodingIsComplete();
+
+ GroupAndVarLength decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ GroupAndVarLength::sbeBlockLength(),
+ GroupAndVarLength::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 42);
+ GroupAndVarLength::B &bDecoder = decoder.b();
+ EXPECT_EQ(bDecoder.count(), 1u);
+ EXPECT_EQ(bDecoder.next().c(), 43);
+ EXPECT_EQ(decoder.getDAsString(), "abc");
+}
+
+TEST_F(FieldAccessOrderCheckTest, disallowsEncoderToResetGroupLengthMidGroupElement)
+{
+ NestedGroups encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(42);
+ NestedGroups::B &bEncoder = encoder.bCount(2).next();
+ bEncoder.c(43);
+ EXPECT_THROW(
+ {
+ try
+ {
+ bEncoder.resetCountToIndex();
+ }
+ catch (const std::logic_error &e)
+ {
+ EXPECT_THAT(
+ e.what(),
+ HasSubstr("Cannot reset count of repeating group \"b\" in state: V0_B_N_BLOCK")
+ );
+ throw;
+ }
+ },
+ std::logic_error
+ );
+}
+
+
+TEST_F(FieldAccessOrderCheckTest, disallowsEncodingGroupElementBeforeCallingNext)
+{
+ GroupAndVarLength encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(42);
+ GroupAndVarLength::B &bEncoder = encoder.bCount(1);
+ EXPECT_THROW(
+ {
+ try
+ {
+ bEncoder.c(1);
+ }
+ catch (const std::logic_error &e)
+ {
+ EXPECT_THAT(
+ e.what(),
+ HasSubstr("Cannot access field \"b.c\" in state: V0_B_N")
+ );
+ throw;
+ }
+ },
+ std::logic_error
+ );
+}
+
+TEST_F(FieldAccessOrderCheckTest, disallowsDecodingGroupElementBeforeCallingNext)
+{
+ GroupAndVarLength encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(42);
+ GroupAndVarLength::B &bEncoder = encoder.bCount(2);
+ bEncoder.next().c(1);
+ bEncoder.next().c(2);
+ encoder.putD("abc");
+ encoder.checkEncodingIsComplete();
+
+ GroupAndVarLength decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ GroupAndVarLength::sbeBlockLength(),
+ GroupAndVarLength::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 42);
+ GroupAndVarLength::B &bDecoder = decoder.b();
+ EXPECT_EQ(bDecoder.count(), 2u);
+
+ EXPECT_THROW(
+ {
+ try
+ {
+ bDecoder.c();
+ }
+ catch (const std::logic_error &e)
+ {
+ EXPECT_THAT(
+ e.what(),
+ HasSubstr("Cannot access field \"b.c\" in state: V0_B_N")
+ );
+ throw;
+ }
+ },
+ std::logic_error
+ );
+}
+
+TEST_F(FieldAccessOrderCheckTest, disallowsSkippingEncodingOfGroup)
+{
+ GroupAndVarLength encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(42);
+ EXPECT_THROW(
+ {
+ try
+ {
+ encoder.putD("abc");
+ }
+ catch (const std::logic_error &e)
+ {
+ EXPECT_THAT(
+ e.what(),
+ HasSubstr("Cannot access field \"d\" in state: V0_BLOCK")
+ );
+ throw;
+ }
+ },
+ std::logic_error
+ );
+}
+
+TEST_F(FieldAccessOrderCheckTest, disallowsReEncodingVariableLengthFieldAfterGroup)
+{
+ GroupAndVarLength encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(42);
+ GroupAndVarLength::B &bEncoder = encoder.bCount(2);
+ bEncoder.next().c(1);
+ bEncoder.next().c(2);
+ encoder.putD("abc");
+ EXPECT_THROW(
+ {
+ try
+ {
+ encoder.putD("def");
+ }
+ catch (const std::logic_error &e)
+ {
+ EXPECT_THAT(
+ e.what(),
+ HasSubstr("Cannot access field \"d\" in state: V0_D_DONE")
+ );
+ throw;
+ }
+ },
+ std::logic_error
+ );
+}
+
+TEST_F(FieldAccessOrderCheckTest, disallowsReEncodingGroupCount)
+{
+ GroupAndVarLength encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(42);
+ GroupAndVarLength::B &bEncoder = encoder.bCount(2);
+ bEncoder.next().c(1);
+ bEncoder.next().c(2);
+ encoder.putD("abc");
+ EXPECT_THROW(
+ {
+ try
+ {
+ encoder.bCount(1);
+ }
+ catch (const std::logic_error &e)
+ {
+ EXPECT_THAT(
+ e.what(),
+ HasSubstr("Cannot encode count of repeating group \"b\" in state: V0_D_DONE")
+ );
+ throw;
+ }
+ },
+ std::logic_error
+ );
+}
+
+TEST_F(FieldAccessOrderCheckTest, disallowsMissedDecodingOfGroupBeforeVariableLengthField)
+{
+ GroupAndVarLength encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(42);
+ GroupAndVarLength::B &bEncoder = encoder.bCount(2);
+ bEncoder.next().c(1);
+ bEncoder.next().c(2);
+ encoder.putD("abc");
+ encoder.checkEncodingIsComplete();
+
+ GroupAndVarLength decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ GroupAndVarLength::sbeBlockLength(),
+ GroupAndVarLength::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 42);
+ EXPECT_THROW(
+ {
+ try
+ {
+ decoder.getDAsString();
+ }
+ catch (const std::logic_error &e)
+ {
+ EXPECT_THAT(
+ e.what(),
+ HasSubstr("Cannot access field \"d\" in state: V0_BLOCK")
+ );
+ throw;
+ }
+ },
+ std::logic_error
+ );
+}
+
+TEST_F(FieldAccessOrderCheckTest, disallowsReDecodingVariableLengthFieldAfterGroup)
+{
+ GroupAndVarLength encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(42);
+ GroupAndVarLength::B &bEncoder = encoder.bCount(2);
+ bEncoder.next().c(1);
+ bEncoder.next().c(2);
+ encoder.putD("abc");
+ encoder.checkEncodingIsComplete();
+
+ GroupAndVarLength decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ GroupAndVarLength::sbeBlockLength(),
+ GroupAndVarLength::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 42);
+ GroupAndVarLength::B &bDecoder = decoder.b();
+ EXPECT_EQ(bDecoder.count(), 2u);
+ EXPECT_EQ(bDecoder.next().c(), 1);
+ EXPECT_EQ(bDecoder.next().c(), 2);
+ EXPECT_EQ(decoder.getDAsString(), "abc");
+ EXPECT_THROW(
+ {
+ try
+ {
+ decoder.getDAsString();
+ }
+ catch (const std::logic_error &e)
+ {
+ EXPECT_THAT(
+ e.what(),
+ HasSubstr("Cannot access field \"d\" in state: V0_D_DONE")
+ );
+ throw;
+ }
+ },
+ std::logic_error
+ );
+}
+
+TEST_F(FieldAccessOrderCheckTest, disallowsReDecodingGroupAfterVariableLengthField)
+{
+ GroupAndVarLength encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(42);
+ GroupAndVarLength::B &bEncoder = encoder.bCount(2);
+ bEncoder.next().c(1);
+ bEncoder.next().c(2);
+ encoder.putD("abc");
+ encoder.checkEncodingIsComplete();
+
+ GroupAndVarLength decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ GroupAndVarLength::sbeBlockLength(),
+ GroupAndVarLength::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 42);
+ GroupAndVarLength::B &bDecoder = decoder.b();
+ EXPECT_EQ(bDecoder.count(), 2u);
+ EXPECT_EQ(bDecoder.next().c(), 1);
+ EXPECT_EQ(bDecoder.next().c(), 2);
+ EXPECT_EQ(decoder.getDAsString(), "abc");
+ EXPECT_THROW(
+ {
+ try
+ {
+ decoder.b();
+ }
+ catch (const std::logic_error &e)
+ {
+ EXPECT_THAT(
+ e.what(),
+ HasSubstr("Cannot decode count of repeating group \"b\" in state: V0_D_DONE")
+ );
+ throw;
+ }
+ },
+ std::logic_error
+ );
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsEncodingAndDecodingVariableLengthFieldInsideGroupInSchemaDefinedOrder)
+{
+ VarLengthInsideGroup encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(42);
+ VarLengthInsideGroup::B &bEncoder = encoder.bCount(2);
+ bEncoder.next().c(1).putD("abc");
+ bEncoder.next().c(2).putD("def");
+ encoder.putE("ghi");
+ encoder.checkEncodingIsComplete();
+
+ VarLengthInsideGroup decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ VarLengthInsideGroup::sbeBlockLength(),
+ VarLengthInsideGroup::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 42);
+ VarLengthInsideGroup::B &bDecoder = decoder.b();
+ EXPECT_EQ(bDecoder.count(), 2u);
+ EXPECT_EQ(bDecoder.next().c(), 1);
+ EXPECT_EQ(bDecoder.getDAsString(), "abc");
+ EXPECT_EQ(bDecoder.next().c(), 2);
+ EXPECT_EQ(bDecoder.getDAsString(), "def");
+ EXPECT_EQ(decoder.getEAsString(), "ghi");
+}
+
+TEST_F(FieldAccessOrderCheckTest, disallowsMissedGroupElementVariableLengthFieldToEncodeAtTopLevel)
+{
+ VarLengthInsideGroup encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(42);
+ VarLengthInsideGroup::B &bEncoder = encoder.bCount(1);
+ bEncoder.next().c(1);
+ EXPECT_THROW(
+ {
+ try
+ {
+ encoder.putE("abc");
+ }
+ catch (const std::logic_error &e)
+ {
+ EXPECT_THAT(
+ e.what(),
+ HasSubstr("Cannot access field \"e\" in state: V0_B_1_BLOCK")
+ );
+ throw;
+ }
+ },
+ std::logic_error
+ );
+}
+
+TEST_F(FieldAccessOrderCheckTest, disallowsMissedGroupElementVariableLengthFieldToEncodeNextElement)
+{
+ VarLengthInsideGroup encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(42);
+ VarLengthInsideGroup::B &bEncoder = encoder.bCount(2);
+ bEncoder.next();
+ EXPECT_THROW(
+ {
+ try
+ {
+ bEncoder.next();
+ }
+ catch (const std::logic_error &e)
+ {
+ EXPECT_THAT(
+ e.what(),
+ HasSubstr("Cannot access next element in repeating group \"b\" in state: V0_B_N_BLOCK")
+ );
+ throw;
+ }
+ },
+ std::logic_error
+ );
+}
+
+TEST_F(FieldAccessOrderCheckTest, disallowsMissedGroupElementEncoding)
+{
+ VarLengthInsideGroup encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(42);
+ VarLengthInsideGroup::B &bEncoder = encoder.bCount(2);
+ bEncoder.next().c(1).putD("abc");
+ EXPECT_THROW(
+ {
+ try
+ {
+ encoder.putE("abc");
+ }
+ catch (const std::logic_error &e)
+ {
+ EXPECT_THAT(
+ e.what(),
+ HasSubstr("Cannot access field \"e\" in state: V0_B_N_D_DONE")
+ );
+ throw;
+ }
+ },
+ std::logic_error
+ );
+}
+
+TEST_F(FieldAccessOrderCheckTest, disallowsReEncodingGroupElementVariableLengthField)
+{
+ VarLengthInsideGroup encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(42);
+ VarLengthInsideGroup::B &bEncoder = encoder.bCount(1);
+ bEncoder.next().c(1).putD("abc");
+ encoder.putE("def");
+ EXPECT_THROW(
+ {
+ try
+ {
+ bEncoder.putD("ghi");
+ }
+ catch (const std::logic_error &e)
+ {
+ EXPECT_THAT(
+ e.what(),
+ HasSubstr("Cannot access field \"b.d\" in state: V0_E_DONE")
+ );
+ throw;
+ }
+ },
+ std::logic_error
+ );
+}
+
+TEST_F(FieldAccessOrderCheckTest, disallowsReDecodingGroupElementVariableLengthField)
+{
+ VarLengthInsideGroup encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(42);
+ VarLengthInsideGroup::B &bEncoder = encoder.bCount(2);
+ bEncoder.next().c(1).putD("abc");
+ bEncoder.next().c(2).putD("def");
+ encoder.putE("ghi");
+ encoder.checkEncodingIsComplete();
+
+ VarLengthInsideGroup decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ VarLengthInsideGroup::sbeBlockLength(),
+ VarLengthInsideGroup::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 42);
+ VarLengthInsideGroup::B &bDecoder = decoder.b();
+ EXPECT_EQ(bDecoder.count(), 2u);
+ EXPECT_EQ(bDecoder.next().c(), 1);
+ EXPECT_EQ(bDecoder.getDAsString(), "abc");
+ EXPECT_THROW(
+ {
+ try
+ {
+ bDecoder.getDAsString();
+ }
+ catch (const std::logic_error &e)
+ {
+ EXPECT_THAT(
+ e.what(),
+ HasSubstr("Cannot access field \"b.d\" in state: V0_B_N_D_DONE")
+ );
+ throw;
+ }
+ },
+ std::logic_error
+ );
+}
+
+TEST_F(FieldAccessOrderCheckTest, disallowsMissedDecodingOfGroupElementVariableLengthFieldToNextElement)
+{
+ VarLengthInsideGroup encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(42);
+ VarLengthInsideGroup::B &bEncoder = encoder.bCount(2);
+ bEncoder.next().c(1).putD("abc");
+ bEncoder.next().c(2).putD("def");
+ encoder.putE("ghi");
+ encoder.checkEncodingIsComplete();
+
+ VarLengthInsideGroup decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ VarLengthInsideGroup::sbeBlockLength(),
+ VarLengthInsideGroup::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 42);
+ VarLengthInsideGroup::B &bDecoder = decoder.b();
+ EXPECT_EQ(bDecoder.count(), 2u);
+ EXPECT_EQ(bDecoder.next().c(), 1);
+ EXPECT_THROW(
+ {
+ try
+ {
+ bDecoder.next();
+ }
+ catch (const std::logic_error &e)
+ {
+ EXPECT_THAT(
+ e.what(),
+ HasSubstr("Cannot access next element in repeating group \"b\" in state: V0_B_N_BLOCK")
+ );
+ throw;
+ }
+ },
+ std::logic_error
+ );
+}
+
+TEST_F(FieldAccessOrderCheckTest, disallowsMissedDecodingOfGroupElementVariableLengthFieldToTopLevel)
+{
+ VarLengthInsideGroup encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(42);
+ VarLengthInsideGroup::B &bEncoder = encoder.bCount(1);
+ bEncoder.next().c(1).putD("abc");
+ encoder.putE("ghi");
+ encoder.checkEncodingIsComplete();
+
+ VarLengthInsideGroup decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ VarLengthInsideGroup::sbeBlockLength(),
+ VarLengthInsideGroup::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 42);
+ VarLengthInsideGroup::B &bDecoder = decoder.b();
+ EXPECT_EQ(bDecoder.count(), 1u);
+ EXPECT_EQ(bDecoder.next().c(), 1);
+ EXPECT_THROW(
+ {
+ try
+ {
+ decoder.getEAsString();
+ }
+ catch (const std::logic_error &e)
+ {
+ EXPECT_THAT(
+ e.what(),
+ HasSubstr("Cannot access field \"e\" in state: V0_B_1_BLOCK")
+ );
+ throw;
+ }
+ },
+ std::logic_error
+ );
+}
+
+TEST_F(FieldAccessOrderCheckTest, disallowsMissedDecodingOfGroupElement)
+{
+ VarLengthInsideGroup encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(42);
+ VarLengthInsideGroup::B &bEncoder = encoder.bCount(2);
+ bEncoder.next().c(1).putD("abc");
+ bEncoder.next().c(2).putD("def");
+ encoder.putE("ghi");
+ encoder.checkEncodingIsComplete();
+
+ VarLengthInsideGroup decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ VarLengthInsideGroup::sbeBlockLength(),
+ VarLengthInsideGroup::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 42);
+ VarLengthInsideGroup::B &bDecoder = decoder.b();
+ EXPECT_EQ(bDecoder.count(), 2u);
+ EXPECT_EQ(bDecoder.next().c(), 1);
+ EXPECT_THROW(
+ {
+ try
+ {
+ decoder.getEAsString();
+ }
+ catch (const std::logic_error &e)
+ {
+ EXPECT_THAT(
+ e.what(),
+ HasSubstr("Cannot access field \"e\" in state: V0_B_N_BLOCK")
+ );
+ throw;
+ }
+ },
+ std::logic_error
+ );
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsEncodingNestedGroupsInSchemaDefinedOrder)
+{
+ NestedGroups encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(42);
+ NestedGroups::B &bEncoder = encoder.bCount(2);
+ bEncoder.next();
+ bEncoder.c(1);
+ NestedGroups::B::D &b0dEncoder = bEncoder.dCount(2);
+ b0dEncoder.next().e(2);
+ b0dEncoder.next().e(3);
+ NestedGroups::B::F &b0fEncoder = bEncoder.fCount(1);
+ b0fEncoder.next().g(4);
+ bEncoder.next();
+ bEncoder.c(5);
+ NestedGroups::B::D &b1dEncoder = bEncoder.dCount(1);
+ b1dEncoder.next().e(6);
+ NestedGroups::B::F &b1fEncoder = bEncoder.fCount(1);
+ b1fEncoder.next().g(7);
+ NestedGroups::H &hEncoder = encoder.hCount(1);
+ hEncoder.next().i(8);
+ encoder.checkEncodingIsComplete();
+
+ NestedGroups decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ NestedGroups::sbeBlockLength(),
+ NestedGroups::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 42);
+ NestedGroups::B &bDecoder = decoder.b();
+ EXPECT_EQ(bDecoder.count(), 2u);
+ NestedGroups::B &b0Decoder = bDecoder.next();
+ EXPECT_EQ(b0Decoder.c(), 1);
+ NestedGroups::B::D &b0dDecoder = b0Decoder.d();
+ EXPECT_EQ(b0dDecoder.count(), 2u);
+ EXPECT_EQ(b0dDecoder.next().e(), 2);
+ EXPECT_EQ(b0dDecoder.next().e(), 3);
+ NestedGroups::B::F &b0fDecoder = b0Decoder.f();
+ EXPECT_EQ(b0fDecoder.count(), 1u);
+ EXPECT_EQ(b0fDecoder.next().g(), 4);
+ NestedGroups::B &b1Decoder = bDecoder.next();
+ EXPECT_EQ(b1Decoder.c(), 5);
+ NestedGroups::B::D &b1dDecoder = b1Decoder.d();
+ EXPECT_EQ(b1dDecoder.count(), 1u);
+ EXPECT_EQ(b1dDecoder.next().e(), 6);
+ NestedGroups::B::F &b1fDecoder = b1Decoder.f();
+ EXPECT_EQ(b1fDecoder.count(), 1u);
+ EXPECT_EQ(b1fDecoder.next().g(), 7);
+ NestedGroups::H &hDecoder = decoder.h();
+ EXPECT_EQ(hDecoder.count(), 1u);
+ EXPECT_EQ(hDecoder.next().i(), 8);
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsEncodingEmptyNestedGroupsInSchemaDefinedOrder)
+{
+ NestedGroups encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(42);
+ encoder.bCount(0);
+ encoder.hCount(0);
+ encoder.checkEncodingIsComplete();
+
+ NestedGroups decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ NestedGroups::sbeBlockLength(),
+ NestedGroups::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 42);
+ NestedGroups::B &bDecoder = decoder.b();
+ EXPECT_EQ(bDecoder.count(), 0u);
+ NestedGroups::H &hDecoder = decoder.h();
+ EXPECT_EQ(hDecoder.count(), 0u);
+}
+
+TEST_F(FieldAccessOrderCheckTest, disallowsMissedEncodingOfNestedGroup)
+{
+ NestedGroups encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(42);
+ NestedGroups::B &bEncoder = encoder.bCount(1).next();
+ bEncoder.c(1);
+ EXPECT_THROW(
+ {
+ try
+ {
+ bEncoder.fCount(1);
+ }
+ catch (const std::logic_error &e)
+ {
+ EXPECT_THAT(
+ e.what(),
+ HasSubstr("Cannot encode count of repeating group \"b.f\" in state: V0_B_1_BLOCK")
+ );
+ throw;
+ }
+ },
+ std::logic_error
+ );
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsEncodingAndDecodingCompositeInsideGroupInSchemaDefinedOrder)
+{
+ CompositeInsideGroup encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ Point &aEncoder = encoder.a();
+ aEncoder.x(1);
+ aEncoder.y(2);
+ CompositeInsideGroup::B &bEncoder = encoder.bCount(1).next();
+ Point &cEncoder = bEncoder.c();
+ cEncoder.x(3);
+ cEncoder.y(4);
+ encoder.checkEncodingIsComplete();
+
+ CompositeInsideGroup decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ CompositeInsideGroup::sbeBlockLength(),
+ CompositeInsideGroup::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ Point &aDecoder = decoder.a();
+ EXPECT_EQ(aDecoder.x(), 1);
+ EXPECT_EQ(aDecoder.y(), 2);
+ CompositeInsideGroup::B &bDecoder = decoder.b();
+ EXPECT_EQ(bDecoder.count(), 1u);
+ Point &cDecoder = bDecoder.next().c();
+ EXPECT_EQ(cDecoder.x(), 3);
+ EXPECT_EQ(cDecoder.y(), 4);
+}
+
+TEST_F(FieldAccessOrderCheckTest, disallowsEncodingCompositeInsideGroupBeforeCallingNext)
+{
+ CompositeInsideGroup encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ Point &aEncoder = encoder.a();
+ aEncoder.x(1);
+ aEncoder.y(2);
+ CompositeInsideGroup::B &bEncoder = encoder.bCount(1);
+ EXPECT_THROW(
+ {
+ try
+ {
+ bEncoder.c();
+ }
+ catch (const std::logic_error &e)
+ {
+ EXPECT_THAT(
+ e.what(),
+ HasSubstr("Cannot access field \"b.c\" in state: V0_B_N")
+ );
+ throw;
+ }
+ },
+ std::logic_error
+ );
+}
+
+TEST_F(FieldAccessOrderCheckTest, disallowsDecodingCompositeInsideGroupBeforeCallingNext)
+{
+ CompositeInsideGroup encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ Point &aEncoder = encoder.a();
+ aEncoder.x(1);
+ aEncoder.y(2);
+ CompositeInsideGroup::B &bEncoder = encoder.bCount(1).next();
+ Point &cEncoder = bEncoder.c();
+ cEncoder.x(3);
+ cEncoder.y(4);
+
+ CompositeInsideGroup decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ CompositeInsideGroup::sbeBlockLength(),
+ CompositeInsideGroup::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ Point &aDecoder = decoder.a();
+ EXPECT_EQ(aDecoder.x(), 1);
+ EXPECT_EQ(aDecoder.y(), 2);
+ CompositeInsideGroup::B &bDecoder = decoder.b();
+ EXPECT_EQ(bDecoder.count(), 1u);
+ EXPECT_THROW(
+ {
+ try
+ {
+ bDecoder.c();
+ }
+ catch (const std::logic_error &e)
+ {
+ EXPECT_THAT(
+ e.what(),
+ HasSubstr("Cannot access field \"b.c\" in state: V0_B_N")
+ );
+ throw;
+ }
+ },
+ std::logic_error
+ );
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsReEncodingTopLevelCompositeViaReWrap)
+{
+ CompositeInsideGroup encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ Point &aEncoder = encoder.a();
+ aEncoder.x(1);
+ aEncoder.y(2);
+ CompositeInsideGroup::B &bEncoder = encoder.bCount(1).next();
+ Point &cEncoder = bEncoder.c();
+ cEncoder.x(3);
+ cEncoder.y(4);
+ encoder.a().x(5);
+ encoder.a().y(6);
+ encoder.checkEncodingIsComplete();
+
+ CompositeInsideGroup decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ CompositeInsideGroup::sbeBlockLength(),
+ CompositeInsideGroup::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ Point &aDecoder = decoder.a();
+ EXPECT_EQ(aDecoder.x(), 5);
+ EXPECT_EQ(aDecoder.y(), 6);
+ CompositeInsideGroup::B &bDecoder = decoder.b();
+ EXPECT_EQ(bDecoder.count(), 1u);
+ Point &cDecoder = bDecoder.next().c();
+ EXPECT_EQ(cDecoder.x(), 3);
+ EXPECT_EQ(cDecoder.y(), 4);
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsReEncodingTopLevelCompositeViaEncoderReference)
+{
+ CompositeInsideGroup encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ Point &aEncoder = encoder.a();
+ aEncoder.x(1);
+ aEncoder.y(2);
+ CompositeInsideGroup::B &bEncoder = encoder.bCount(1).next();
+ Point &cEncoder = bEncoder.c();
+ cEncoder.x(3);
+ cEncoder.y(4);
+ aEncoder.x(5);
+ aEncoder.y(6);
+ encoder.checkEncodingIsComplete();
+
+ CompositeInsideGroup decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ CompositeInsideGroup::sbeBlockLength(),
+ CompositeInsideGroup::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ Point &aDecoder = decoder.a();
+ EXPECT_EQ(aDecoder.x(), 5);
+ EXPECT_EQ(aDecoder.y(), 6);
+ CompositeInsideGroup::B &bDecoder = decoder.b();
+ EXPECT_EQ(bDecoder.count(), 1u);
+ Point &cDecoder = bDecoder.next().c();
+ EXPECT_EQ(cDecoder.x(), 3);
+ EXPECT_EQ(cDecoder.y(), 4);
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsReEncodingGroupElementCompositeViaReWrap)
+{
+ CompositeInsideGroup encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ Point &aEncoder = encoder.a();
+ aEncoder.x(1);
+ aEncoder.y(2);
+ CompositeInsideGroup::B &bEncoder = encoder.bCount(1).next();
+ bEncoder.c().x(3).y(4);
+ bEncoder.c().x(5).y(6);
+ encoder.checkEncodingIsComplete();
+
+ CompositeInsideGroup decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ CompositeInsideGroup::sbeBlockLength(),
+ CompositeInsideGroup::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ Point &aDecoder = decoder.a();
+ EXPECT_EQ(aDecoder.x(), 1);
+ EXPECT_EQ(aDecoder.y(), 2);
+ CompositeInsideGroup::B &bDecoder = decoder.b();
+ EXPECT_EQ(bDecoder.count(), 1u);
+ Point &cDecoder = bDecoder.next().c();
+ EXPECT_EQ(cDecoder.x(), 5);
+ EXPECT_EQ(cDecoder.y(), 6);
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsReEncodingGroupElementCompositeViaEncoderReference)
+{
+ CompositeInsideGroup encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ Point &aEncoder = encoder.a();
+ aEncoder.x(1);
+ aEncoder.y(2);
+ CompositeInsideGroup::B &bEncoder = encoder.bCount(1).next();
+ Point &cEncoder = bEncoder.c();
+ cEncoder.x(3);
+ cEncoder.y(4);
+ cEncoder.x(5);
+ cEncoder.y(6);
+ encoder.checkEncodingIsComplete();
+
+ CompositeInsideGroup decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ CompositeInsideGroup::sbeBlockLength(),
+ CompositeInsideGroup::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ Point &aDecoder = decoder.a();
+ EXPECT_EQ(aDecoder.x(), 1);
+ EXPECT_EQ(aDecoder.y(), 2);
+ CompositeInsideGroup::B &bDecoder = decoder.b();
+ EXPECT_EQ(bDecoder.count(), 1u);
+ Point &cDecoder = bDecoder.next().c();
+ EXPECT_EQ(cDecoder.x(), 5);
+ EXPECT_EQ(cDecoder.y(), 6);
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsReDecodingTopLevelCompositeViaReWrap)
+{
+ CompositeInsideGroup encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ Point &aEncoder = encoder.a();
+ aEncoder.x(1);
+ aEncoder.y(2);
+ CompositeInsideGroup::B &bEncoder = encoder.bCount(1).next();
+ bEncoder.c().x(3).y(4);
+ encoder.checkEncodingIsComplete();
+
+ CompositeInsideGroup decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ CompositeInsideGroup::sbeBlockLength(),
+ CompositeInsideGroup::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ Point &a1Decoder = decoder.a();
+ EXPECT_EQ(a1Decoder.x(), 1);
+ EXPECT_EQ(a1Decoder.y(), 2);
+ CompositeInsideGroup::B &bDecoder = decoder.b();
+ EXPECT_EQ(bDecoder.count(), 1u);
+ Point &cDecoder = bDecoder.next().c();
+ EXPECT_EQ(cDecoder.x(), 3);
+ EXPECT_EQ(cDecoder.y(), 4);
+ Point &a2Decoder = decoder.a();
+ EXPECT_EQ(a2Decoder.x(), 1);
+ EXPECT_EQ(a2Decoder.y(), 2);
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsReDecodingTopLevelCompositeViaEncoderReference)
+{
+ CompositeInsideGroup encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ Point &aEncoder = encoder.a();
+ aEncoder.x(1);
+ aEncoder.y(2);
+ CompositeInsideGroup::B &bEncoder = encoder.bCount(1).next();
+ bEncoder.c().x(3).y(4);
+ encoder.checkEncodingIsComplete();
+
+ CompositeInsideGroup decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ CompositeInsideGroup::sbeBlockLength(),
+ CompositeInsideGroup::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ Point &aDecoder = decoder.a();
+ EXPECT_EQ(aDecoder.x(), 1);
+ EXPECT_EQ(aDecoder.y(), 2);
+ CompositeInsideGroup::B &bDecoder = decoder.b();
+ EXPECT_EQ(bDecoder.count(), 1u);
+ Point &cDecoder = bDecoder.next().c();
+ EXPECT_EQ(cDecoder.x(), 3);
+ EXPECT_EQ(cDecoder.y(), 4);
+ EXPECT_EQ(aDecoder.x(), 1);
+ EXPECT_EQ(aDecoder.y(), 2);
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsReDecodingGroupElementCompositeViaReWrap)
+{
+ CompositeInsideGroup encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ Point &aEncoder = encoder.a();
+ aEncoder.x(1);
+ aEncoder.y(2);
+ CompositeInsideGroup::B &bEncoder = encoder.bCount(1).next();
+ bEncoder.c().x(3).y(4);
+ encoder.checkEncodingIsComplete();
+
+ CompositeInsideGroup decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ CompositeInsideGroup::sbeBlockLength(),
+ CompositeInsideGroup::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ Point &aDecoder = decoder.a();
+ EXPECT_EQ(aDecoder.x(), 1);
+ EXPECT_EQ(aDecoder.y(), 2);
+ CompositeInsideGroup::B &bDecoder = decoder.b();
+ EXPECT_EQ(bDecoder.count(), 1u);
+ Point &c1Decoder = bDecoder.next().c();
+ EXPECT_EQ(c1Decoder.x(), 3);
+ EXPECT_EQ(c1Decoder.y(), 4);
+ Point &c2Decoder = bDecoder.c();
+ EXPECT_EQ(c2Decoder.x(), 3);
+ EXPECT_EQ(c2Decoder.y(), 4);
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsReDecodingGroupElementCompositeViaEncoderReference)
+{
+ CompositeInsideGroup encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ Point &aEncoder = encoder.a();
+ aEncoder.x(1);
+ aEncoder.y(2);
+ CompositeInsideGroup::B &bEncoder = encoder.bCount(1).next();
+ bEncoder.c().x(3).y(4);
+ encoder.checkEncodingIsComplete();
+
+ CompositeInsideGroup decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ CompositeInsideGroup::sbeBlockLength(),
+ CompositeInsideGroup::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ Point &aDecoder = decoder.a();
+ EXPECT_EQ(aDecoder.x(), 1);
+ EXPECT_EQ(aDecoder.y(), 2);
+ CompositeInsideGroup::B &bDecoder = decoder.b();
+ EXPECT_EQ(bDecoder.count(), 1u);
+ Point &cDecoder = bDecoder.next().c();
+ EXPECT_EQ(cDecoder.x(), 3);
+ EXPECT_EQ(cDecoder.y(), 4);
+ EXPECT_EQ(cDecoder.x(), 3);
+ EXPECT_EQ(cDecoder.y(), 4);
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToDecodeAddedPrimitiveField)
+{
+ AddPrimitiveV1 encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(1);
+ encoder.b(2);
+ encoder.checkEncodingIsComplete();
+
+ AddPrimitiveV1 decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ AddPrimitiveV1::sbeBlockLength(),
+ AddPrimitiveV1::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 1);
+ EXPECT_EQ(decoder.b(), 2);
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToDecodeMissingPrimitiveFieldAsNullValue)
+{
+ AddPrimitiveV0 encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(1);
+ encoder.checkEncodingIsComplete();
+
+ AddPrimitiveV1 decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ AddPrimitiveV0::sbeBlockLength(),
+ 0,
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 1);
+ EXPECT_EQ(decoder.b(), AddPrimitiveV1::bNullValue());
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToDecodeAddedPrimitiveFieldBeforeGroup)
+{
+ AddPrimitiveBeforeGroupV1 encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(1);
+ encoder.d(3);
+ AddPrimitiveBeforeGroupV1::B &bEncoder = encoder.bCount(1).next();
+ bEncoder.c(2);
+ encoder.checkEncodingIsComplete();
+
+ AddPrimitiveBeforeGroupV1 decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ AddPrimitiveBeforeGroupV1::sbeBlockLength(),
+ AddPrimitiveBeforeGroupV1::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 1);
+ EXPECT_EQ(decoder.d(), 3);
+ AddPrimitiveBeforeGroupV1::B &bDecoder = decoder.b();
+ EXPECT_EQ(bDecoder.count(), 1u);
+ EXPECT_EQ(bDecoder.next().c(), 2);
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToDecodeMissingPrimitiveFieldBeforeGroupAsNullValue)
+{
+ AddPrimitiveBeforeGroupV0 encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(1);
+ AddPrimitiveBeforeGroupV0::B &bEncoder = encoder.bCount(1).next();
+ bEncoder.c(2);
+ encoder.checkEncodingIsComplete();
+
+ AddPrimitiveBeforeGroupV1 decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ AddPrimitiveBeforeGroupV0::sbeBlockLength(),
+ 0,
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 1);
+ EXPECT_EQ(decoder.d(), AddPrimitiveBeforeGroupV1::dNullValue());
+ AddPrimitiveBeforeGroupV1::B &bDecoder = decoder.b();
+ EXPECT_EQ(bDecoder.count(), 1u);
+ EXPECT_EQ(bDecoder.next().c(), 2);
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToSkipPresentButAddedPrimitiveFieldBeforeGroup)
+{
+ AddPrimitiveBeforeGroupV1 encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(1);
+ encoder.d(3);
+ AddPrimitiveBeforeGroupV1::B &bEncoder = encoder.bCount(1).next();
+ bEncoder.c(2);
+ encoder.checkEncodingIsComplete();
+
+ AddPrimitiveBeforeGroupV1 decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ AddPrimitiveBeforeGroupV1::sbeBlockLength(),
+ AddPrimitiveBeforeGroupV1::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 1);
+ AddPrimitiveBeforeGroupV1::B &bDecoder = decoder.b();
+ EXPECT_EQ(bDecoder.count(), 1u);
+ EXPECT_EQ(bDecoder.next().c(), 2);
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsOldDecoderToSkipAddedPrimitiveFieldBeforeGroup)
+{
+ AddPrimitiveBeforeGroupV1 encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(1);
+ encoder.d(3);
+ AddPrimitiveBeforeGroupV1::B &bEncoder = encoder.bCount(1).next();
+ bEncoder.c(2);
+ encoder.checkEncodingIsComplete();
+
+ AddPrimitiveBeforeGroupV0 decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ AddPrimitiveBeforeGroupV1::sbeBlockLength(),
+ 1,
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 1);
+ AddPrimitiveBeforeGroupV0::B &bDecoder = decoder.b();
+ EXPECT_EQ(bDecoder.count(), 1u);
+ EXPECT_EQ(bDecoder.next().c(), 2);
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToDecodeAddedPrimitiveFieldBeforeVarData)
+{
+ AddPrimitiveBeforeVarDataV1 encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(1);
+ encoder.c(3);
+ encoder.putB("abc");
+ encoder.checkEncodingIsComplete();
+
+ AddPrimitiveBeforeVarDataV1 decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ AddPrimitiveBeforeVarDataV1::sbeBlockLength(),
+ AddPrimitiveBeforeVarDataV1::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 1);
+ EXPECT_EQ(decoder.c(), 3);
+ EXPECT_EQ(decoder.getBAsString(), "abc");
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToDecodeMissingPrimitiveFieldBeforeVarDataAsNullValue)
+{
+ AddPrimitiveBeforeVarDataV0 encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(1);
+ encoder.putB("abc");
+ encoder.checkEncodingIsComplete();
+
+ AddPrimitiveBeforeVarDataV1 decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ AddPrimitiveBeforeVarDataV0::sbeBlockLength(),
+ 0,
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 1);
+ EXPECT_EQ(decoder.c(), AddPrimitiveBeforeVarDataV1::cNullValue());
+ EXPECT_EQ(decoder.getBAsString(), "abc");
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToSkipPresentButAddedPrimitiveFieldBeforeVarData)
+{
+ AddPrimitiveBeforeVarDataV1 encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(1);
+ encoder.c(3);
+ encoder.putB("abc");
+ encoder.checkEncodingIsComplete();
+
+ AddPrimitiveBeforeVarDataV1 decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ AddPrimitiveBeforeVarDataV1::sbeBlockLength(),
+ AddPrimitiveBeforeVarDataV1::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 1);
+ EXPECT_EQ(decoder.getBAsString(), "abc");
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsOldDecoderToSkipAddedPrimitiveFieldBeforeVarData)
+{
+ AddPrimitiveBeforeVarDataV1 encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(1);
+ encoder.c(3);
+ encoder.putB("abc");
+ encoder.checkEncodingIsComplete();
+
+ AddPrimitiveBeforeVarDataV0 decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ AddPrimitiveBeforeVarDataV1::sbeBlockLength(),
+ 1,
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 1);
+ EXPECT_EQ(decoder.getBAsString(), "abc");
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToDecodeAddedPrimitiveFieldInsideGroup)
+{
+ AddPrimitiveInsideGroupV1 encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(1);
+ encoder.bCount(1).next().c(2).d(3);
+ encoder.checkEncodingIsComplete();
+
+ AddPrimitiveInsideGroupV1 decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ AddPrimitiveInsideGroupV1::sbeBlockLength(),
+ AddPrimitiveInsideGroupV1::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 1);
+ AddPrimitiveInsideGroupV1::B &bDecoder = decoder.b();
+ EXPECT_EQ(bDecoder.count(), 1u);
+ EXPECT_EQ(bDecoder.next().c(), 2);
+ EXPECT_EQ(bDecoder.d(), 3);
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToDecodeMissingPrimitiveFieldInsideGroupAsNullValue)
+{
+ AddPrimitiveInsideGroupV0 encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(1);
+ encoder.bCount(1).next().c(2);
+ encoder.checkEncodingIsComplete();
+
+ AddPrimitiveInsideGroupV1 decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ AddPrimitiveInsideGroupV0::sbeBlockLength(),
+ 0,
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 1);
+ AddPrimitiveInsideGroupV1::B &bDecoder = decoder.b();
+ EXPECT_EQ(bDecoder.count(), 1u);
+ EXPECT_EQ(bDecoder.next().c(), 2);
+ EXPECT_EQ(bDecoder.d(), AddPrimitiveInsideGroupV1::B::dNullValue());
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToSkipPresentButAddedPrimitiveFieldInsideGroup)
+{
+ AddPrimitiveInsideGroupV1 encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(1);
+ encoder.bCount(2).next().c(2).d(3).next().c(4).d(5);
+ encoder.checkEncodingIsComplete();
+
+ AddPrimitiveInsideGroupV1 decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ AddPrimitiveInsideGroupV1::sbeBlockLength(),
+ AddPrimitiveInsideGroupV1::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 1);
+ AddPrimitiveInsideGroupV1::B &bDecoder = decoder.b();
+ EXPECT_EQ(bDecoder.count(), 2u);
+ EXPECT_EQ(bDecoder.next().c(), 2);
+ EXPECT_EQ(bDecoder.next().c(), 4);
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsOldDecoderToSkipAddedPrimitiveFieldInsideGroup)
+{
+ AddPrimitiveInsideGroupV1 encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(1);
+ encoder.bCount(2).next().c(2).d(3).next().c(4).d(5);
+ encoder.checkEncodingIsComplete();
+
+ AddPrimitiveInsideGroupV0 decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ AddPrimitiveInsideGroupV1::sbeBlockLength(),
+ 1,
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 1);
+ AddPrimitiveInsideGroupV0::B &bDecoder = decoder.b();
+ EXPECT_EQ(bDecoder.count(), 2u);
+ EXPECT_EQ(bDecoder.next().c(), 2);
+ EXPECT_EQ(bDecoder.next().c(), 4);
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToDecodeAddedGroupBeforeVarData)
+{
+ AddGroupBeforeVarDataV1 encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(1);
+ encoder.cCount(1).next().d(2);
+ encoder.putB("abc");
+ encoder.checkEncodingIsComplete();
+
+ AddGroupBeforeVarDataV1 decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ AddGroupBeforeVarDataV1::sbeBlockLength(),
+ AddGroupBeforeVarDataV1::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 1);
+ AddGroupBeforeVarDataV1::C &cDecoder = decoder.c();
+ EXPECT_EQ(cDecoder.count(), 1u);
+ EXPECT_EQ(cDecoder.next().d(), 2);
+ EXPECT_EQ(decoder.getBAsString(), "abc");
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToDecodeMissingGroupBeforeVarDataAsNullValue)
+{
+ AddGroupBeforeVarDataV0 encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(1);
+ encoder.putB("abc");
+ encoder.checkEncodingIsComplete();
+
+ AddGroupBeforeVarDataV1 decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ AddGroupBeforeVarDataV0::sbeBlockLength(),
+ 0,
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 1);
+ AddGroupBeforeVarDataV1::C &cDecoder = decoder.c();
+ EXPECT_EQ(cDecoder.count(), 0u);
+ EXPECT_EQ(decoder.getBAsString(), "abc");
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToSkipMissingGroupBeforeVarData)
+{
+ AddGroupBeforeVarDataV0 encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(1);
+ encoder.putB("abc");
+ encoder.checkEncodingIsComplete();
+
+ AddGroupBeforeVarDataV1 decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ AddGroupBeforeVarDataV0::sbeBlockLength(),
+ 0,
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 1);
+ EXPECT_EQ(decoder.getBAsString(), "abc");
+}
+
+TEST_F(FieldAccessOrderCheckTest, disallowsNewDecoderToSkipPresentButAddedGroupBeforeVarData)
+{
+ AddGroupBeforeVarDataV1 encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(1);
+ encoder.cCount(1).next().d(2);
+ encoder.putB("abc");
+ encoder.checkEncodingIsComplete();
+
+ AddGroupBeforeVarDataV1 decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ AddGroupBeforeVarDataV1::sbeBlockLength(),
+ AddGroupBeforeVarDataV1::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 1);
+ EXPECT_THROW(
+ {
+ try
+ {
+ decoder.b();
+ }
+ catch (const std::exception &e)
+ {
+ EXPECT_THAT(
+ e.what(),
+ HasSubstr("Cannot access field \"b\" in state: V1_BLOCK")
+ );
+ throw;
+ }
+ },
+ std::logic_error
+ );
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsOldDecoderToSkipAddedGroupBeforeVarData)
+{
+ AddGroupBeforeVarDataV1 encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(1);
+ encoder.cCount(1).next().d(2);
+ encoder.putB("abc");
+ encoder.checkEncodingIsComplete();
+
+ AddGroupBeforeVarDataV0 decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ AddGroupBeforeVarDataV1::sbeBlockLength(),
+ AddGroupBeforeVarDataV1::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 1);
+
+ // In reality, we would use a numGroups field in the message header to skip over unknown groups
+ // rather than the hardcoded knowledge of one extra group below:
+
+ GroupSizeEncoding groupSizeEncodingDecoder;
+ groupSizeEncodingDecoder.wrap(
+ m_buffer,
+ decoder.sbePosition(),
+ GroupSizeEncoding::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ const std::uint64_t bytesToSkip = GroupSizeEncoding::encodedLength() +
+ groupSizeEncodingDecoder.blockLength() * groupSizeEncodingDecoder.numInGroup();
+ decoder.sbePosition(decoder.sbePosition() + bytesToSkip);
+
+ EXPECT_EQ(decoder.getBAsString(), "abc");
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToDecodeAddedEnumFieldBeforeGroup)
+{
+ AddEnumBeforeGroupV1 encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(1);
+ encoder.d(Direction::BUY);
+ encoder.bCount(1).next().c(2);
+ encoder.checkEncodingIsComplete();
+
+ AddEnumBeforeGroupV1 decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ AddEnumBeforeGroupV1::sbeBlockLength(),
+ 1,
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 1);
+ EXPECT_EQ(decoder.d(), Direction::BUY);
+ AddEnumBeforeGroupV1::B &b = decoder.b();
+ EXPECT_EQ(b.count(), 1u);
+ EXPECT_EQ(b.next().c(), 2);
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToDecodeMissingEnumFieldBeforeGroupAsNullValue)
+{
+ AddEnumBeforeGroupV0 encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(1);
+ encoder.bCount(1).next().c(2);
+ encoder.checkEncodingIsComplete();
+
+ AddEnumBeforeGroupV1 decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ AddEnumBeforeGroupV0::sbeBlockLength(),
+ 0,
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 1);
+ EXPECT_EQ(decoder.d(), Direction::NULL_VALUE);
+ AddEnumBeforeGroupV1::B &b = decoder.b();
+ EXPECT_EQ(b.count(), 1u);
+ EXPECT_EQ(b.next().c(), 2);
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToSkipPresentButAddedEnumFieldBeforeGroup)
+{
+ AddEnumBeforeGroupV1 encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(1);
+ encoder.d(Direction::SELL);
+ encoder.bCount(1).next().c(2);
+ encoder.checkEncodingIsComplete();
+
+ AddEnumBeforeGroupV1 decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ AddEnumBeforeGroupV1::sbeBlockLength(),
+ 1,
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 1);
+ AddEnumBeforeGroupV1::B &b = decoder.b();
+ EXPECT_EQ(b.count(), 1u);
+ EXPECT_EQ(b.next().c(), 2);
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsOldDecoderToSkipAddedEnumFieldBeforeGroup)
+{
+ AddEnumBeforeGroupV1 encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(1);
+ encoder.d(Direction::BUY);
+ encoder.bCount(1).next().c(2);
+ encoder.checkEncodingIsComplete();
+
+ AddEnumBeforeGroupV0 decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ AddEnumBeforeGroupV1::sbeBlockLength(),
+ 1,
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 1);
+ AddEnumBeforeGroupV0::B &b = decoder.b();
+ EXPECT_EQ(b.count(), 1u);
+ EXPECT_EQ(b.next().c(), 2);
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToDecodeAddedCompositeFieldBeforeGroup)
+{
+ AddCompositeBeforeGroupV1 encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(1);
+ Point &d = encoder.d();
+ d.x(-1);
+ d.y(-2);
+ encoder.bCount(1).next().c(2);
+ encoder.checkEncodingIsComplete();
+
+ AddCompositeBeforeGroupV1 decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ AddCompositeBeforeGroupV1::sbeBlockLength(),
+ 1,
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 1);
+ Point &d2 = decoder.d();
+ EXPECT_EQ(d2.x(), -1);
+ EXPECT_EQ(d2.y(), -2);
+ AddCompositeBeforeGroupV1::B &b = decoder.b();
+ EXPECT_EQ(b.count(), 1u);
+ EXPECT_EQ(b.next().c(), 2);
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToSkipPresentButAddedCompositeFieldBeforeGroup)
+{
+ AddCompositeBeforeGroupV1 encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(1);
+ Point &d = encoder.d();
+ d.x(-1);
+ d.y(-2);
+ encoder.bCount(1).next().c(2);
+ encoder.checkEncodingIsComplete();
+
+ AddCompositeBeforeGroupV1 decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ AddCompositeBeforeGroupV1::sbeBlockLength(),
+ 1,
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 1);
+ AddCompositeBeforeGroupV1::B &b = decoder.b();
+ EXPECT_EQ(b.count(), 1u);
+ EXPECT_EQ(b.next().c(), 2);
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsOldDecoderToSkipAddedCompositeFieldBeforeGroup)
+{
+ AddCompositeBeforeGroupV1 encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(1);
+ Point &d = encoder.d();
+ d.x(-1);
+ d.y(-2);
+ encoder.bCount(1).next().c(2);
+ encoder.checkEncodingIsComplete();
+
+ AddCompositeBeforeGroupV0 decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ AddCompositeBeforeGroupV1::sbeBlockLength(),
+ 1,
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 1);
+ AddCompositeBeforeGroupV0::B &b = decoder.b();
+ EXPECT_EQ(b.count(), 1u);
+ EXPECT_EQ(b.next().c(), 2);
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToDecodeAddedArrayFieldBeforeGroup)
+{
+ AddArrayBeforeGroupV1 encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(1);
+ encoder.putD(1, 2, 3, 4);
+ encoder.bCount(1).next().c(2);
+ encoder.checkEncodingIsComplete();
+
+ AddArrayBeforeGroupV1 decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ AddArrayBeforeGroupV1::sbeBlockLength(),
+ 1,
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 1);
+ EXPECT_EQ(decoder.d(0), 1);
+ EXPECT_EQ(decoder.d(1), 2);
+ EXPECT_EQ(decoder.d(2), 3);
+ EXPECT_EQ(decoder.d(3), 4);
+ AddArrayBeforeGroupV1::B &b = decoder.b();
+ EXPECT_EQ(b.count(), 1u);
+ EXPECT_EQ(b.next().c(), 2);
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToDecodeMissingArrayFieldBeforeGroupAsNullValue1)
+{
+ AddArrayBeforeGroupV0 encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(1);
+ encoder.bCount(1).next().c(2);
+ encoder.checkEncodingIsComplete();
+
+ AddArrayBeforeGroupV1 decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ AddArrayBeforeGroupV0::sbeBlockLength(),
+ 0,
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 1);
+ EXPECT_EQ(decoder.d(0), AddArrayBeforeGroupV1::dNullValue());
+ EXPECT_EQ(decoder.d(1), AddArrayBeforeGroupV1::dNullValue());
+ EXPECT_EQ(decoder.d(2), AddArrayBeforeGroupV1::dNullValue());
+ EXPECT_EQ(decoder.d(3), AddArrayBeforeGroupV1::dNullValue());
+ AddArrayBeforeGroupV1::B &b = decoder.b();
+ EXPECT_EQ(b.count(), 1u);
+ EXPECT_EQ(b.next().c(), 2);
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToDecodeMissingArrayFieldBeforeGroupAsNullValue2)
+{
+ AddArrayBeforeGroupV0 encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(1);
+ encoder.bCount(1).next().c(2);
+ encoder.checkEncodingIsComplete();
+
+ AddArrayBeforeGroupV1 decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ AddArrayBeforeGroupV0::sbeBlockLength(),
+ 0,
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 1);
+ char *d = decoder.d();
+ EXPECT_EQ(d, nullptr);
+ AddArrayBeforeGroupV1::B &b = decoder.b();
+ EXPECT_EQ(b.count(), 1u);
+ EXPECT_EQ(b.next().c(), 2);
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToDecodeMissingArrayFieldBeforeGroupAsNullValue3)
+{
+ AddArrayBeforeGroupV0 encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(1);
+ encoder.bCount(1).next().c(2);
+ encoder.checkEncodingIsComplete();
+
+ AddArrayBeforeGroupV1 decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ AddArrayBeforeGroupV0::sbeBlockLength(),
+ 0,
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 1);
+ const AddArrayBeforeGroupV1 &constDecoder = decoder;
+ const char *d = constDecoder.d();
+ EXPECT_EQ(d, nullptr);
+ AddArrayBeforeGroupV1::B &b = decoder.b();
+ EXPECT_EQ(b.count(), 1u);
+ EXPECT_EQ(b.next().c(), 2);
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToSkipPresentButAddedArrayFieldBeforeGroup)
+{
+ AddArrayBeforeGroupV1 encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(1);
+ encoder.putD(1, 2, 3, 4);
+ encoder.bCount(1).next().c(2);
+ encoder.checkEncodingIsComplete();
+
+ AddArrayBeforeGroupV1 decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ AddArrayBeforeGroupV1::sbeBlockLength(),
+ 1,
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 1);
+ AddArrayBeforeGroupV1::B &b = decoder.b();
+ EXPECT_EQ(b.count(), 1u);
+ EXPECT_EQ(b.next().c(), 2);
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsOldDecoderToSkipAddedArrayFieldBeforeGroup)
+{
+ AddArrayBeforeGroupV1 encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(1);
+ encoder.putD(1, 2, 3, 4);
+ encoder.bCount(1).next().c(2);
+ encoder.checkEncodingIsComplete();
+
+ AddArrayBeforeGroupV0 decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ AddArrayBeforeGroupV1::sbeBlockLength(),
+ 1,
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 1);
+ AddArrayBeforeGroupV0::B &b = decoder.b();
+ EXPECT_EQ(b.count(), 1u);
+ EXPECT_EQ(b.next().c(), 2);
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToDecodeAddedBitSetFieldBeforeGroup)
+{
+ AddBitSetBeforeGroupV1 encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(1).d().guacamole(true).cheese(true).sourCream(false);
+ encoder.bCount(1).next().c(2);
+ encoder.checkEncodingIsComplete();
+
+ AddBitSetBeforeGroupV1 decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ AddBitSetBeforeGroupV1::sbeBlockLength(),
+ 0,
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 1);
+ Flags &d = decoder.d();
+ EXPECT_EQ(d.guacamole(), true);
+ EXPECT_EQ(d.cheese(), true);
+ EXPECT_EQ(d.sourCream(), false);
+ AddBitSetBeforeGroupV1::B &b = decoder.b();
+ EXPECT_EQ(b.count(), 1u);
+ EXPECT_EQ(b.next().c(), 2);
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToDecodeMissingBitSetFieldBeforeGroupAsNullValue)
+{
+ AddBitSetBeforeGroupV0 encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(1).bCount(1).next().c(2);
+ encoder.checkEncodingIsComplete();
+
+ AddBitSetBeforeGroupV1 decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ AddBitSetBeforeGroupV0::sbeBlockLength(),
+ 0,
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 1);
+ EXPECT_EQ(decoder.dInActingVersion(), false);
+ AddBitSetBeforeGroupV1::B &b = decoder.b();
+ EXPECT_EQ(b.count(), 1u);
+ EXPECT_EQ(b.next().c(), 2);
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToSkipPresentButAddedBitSetFieldBeforeGroup)
+{
+ AddBitSetBeforeGroupV1 encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(1).d().guacamole(true).cheese(true).sourCream(false);
+ encoder.bCount(1).next().c(2);
+ encoder.checkEncodingIsComplete();
+
+ AddBitSetBeforeGroupV1 decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ AddBitSetBeforeGroupV1::sbeBlockLength(),
+ 0,
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 1);
+ AddBitSetBeforeGroupV1::B &b = decoder.b();
+ EXPECT_EQ(b.count(), 1u);
+ EXPECT_EQ(b.next().c(), 2);
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsOldDecoderToSkipAddedBitSetFieldBeforeGroup)
+{
+ AddBitSetBeforeGroupV1 encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(1).d().guacamole(true).cheese(true).sourCream(false);
+ encoder.bCount(1).next().c(2);
+ encoder.checkEncodingIsComplete();
+
+ AddBitSetBeforeGroupV0 decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ AddBitSetBeforeGroupV1::sbeBlockLength(),
+ 1,
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 1);
+ AddBitSetBeforeGroupV0::B &b = decoder.b();
+ EXPECT_EQ(b.count(), 1u);
+ EXPECT_EQ(b.next().c(), 2);
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsEncodingAndDecodingEnumInsideGroupInSchemaDefinedOrder)
+{
+ EnumInsideGroup encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(Direction::BUY)
+ .bCount(1)
+ .next()
+ .c(Direction::SELL);
+ encoder.checkEncodingIsComplete();
+
+ EnumInsideGroup decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ EnumInsideGroup::sbeBlockLength(),
+ EnumInsideGroup::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), Direction::BUY);
+ EnumInsideGroup::B &b = decoder.b();
+ EXPECT_EQ(b.count(), 1u);
+ EXPECT_EQ(b.next().c(), Direction::SELL);
+}
+
+TEST_F(FieldAccessOrderCheckTest, disallowsEncodingEnumInsideGroupBeforeCallingNext)
+{
+ EnumInsideGroup encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(Direction::BUY);
+ EnumInsideGroup::B &bEncoder = encoder.bCount(1);
+ EXPECT_THROW(
+ {
+ try
+ {
+ bEncoder.c(Direction::SELL);
+ }
+ catch (const std::logic_error &e)
+ {
+ EXPECT_THAT(
+ e.what(),
+ HasSubstr("Cannot access field \"b.c\" in state: V0_B_N")
+ );
+ throw;
+ }
+ },
+ std::logic_error
+ );
+}
+
+TEST_F(FieldAccessOrderCheckTest, disallowsDecodingEnumInsideGroupBeforeCallingNext)
+{
+ EnumInsideGroup encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(Direction::BUY)
+ .bCount(1)
+ .next()
+ .c(Direction::SELL);
+ encoder.checkEncodingIsComplete();
+
+ EnumInsideGroup decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ EnumInsideGroup::sbeBlockLength(),
+ EnumInsideGroup::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), Direction::BUY);
+ EnumInsideGroup::B &b = decoder.b();
+ EXPECT_EQ(b.count(), 1u);
+ EXPECT_THROW(
+ {
+ try
+ {
+ b.c();
+ }
+ catch (const std::logic_error &e)
+ {
+ EXPECT_THAT(
+ e.what(),
+ HasSubstr("Cannot access field \"b.c\" in state: V0_B_N")
+ );
+ throw;
+ }
+ },
+ std::logic_error
+ );
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsReEncodingTopLevelEnum)
+{
+ EnumInsideGroup encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(Direction::BUY)
+ .bCount(1)
+ .next()
+ .c(Direction::SELL);
+
+ encoder.a(Direction::SELL);
+ encoder.checkEncodingIsComplete();
+
+ EnumInsideGroup decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ EnumInsideGroup::sbeBlockLength(),
+ EnumInsideGroup::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), Direction::SELL);
+ EnumInsideGroup::B &b = decoder.b();
+ EXPECT_EQ(b.count(), 1u);
+ EXPECT_EQ(b.next().c(), Direction::SELL);
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsEncodingAndDecodingBitSetInsideGroupInSchemaDefinedOrder)
+{
+ BitSetInsideGroup encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ Flags &a = encoder.a();
+ a.cheese(true).guacamole(true).sourCream(false);
+ BitSetInsideGroup::B &b = encoder.bCount(1)
+ .next();
+ Flags &c = b.c();
+ c.cheese(false).guacamole(false).sourCream(true);
+ encoder.checkEncodingIsComplete();
+
+ BitSetInsideGroup decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ BitSetInsideGroup::sbeBlockLength(),
+ BitSetInsideGroup::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ Flags &aFlags = decoder.a();
+ EXPECT_EQ(aFlags.guacamole(), true);
+ EXPECT_EQ(aFlags.cheese(), true);
+ EXPECT_EQ(aFlags.sourCream(), false);
+ BitSetInsideGroup::B &bGroup = decoder.b();
+ EXPECT_EQ(bGroup.count(), 1u);
+ Flags &cFlags = bGroup.next().c();
+ EXPECT_EQ(cFlags.guacamole(), false);
+ EXPECT_EQ(cFlags.cheese(), false);
+ EXPECT_EQ(cFlags.sourCream(), true);
+}
+
+TEST_F(FieldAccessOrderCheckTest, disallowsEncodingBitSetInsideGroupBeforeCallingNext)
+{
+ BitSetInsideGroup encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ Flags &a = encoder.a();
+ a.cheese(true).guacamole(true).sourCream(false);
+ BitSetInsideGroup::B &b = encoder.bCount(1);
+ EXPECT_THROW(
+ {
+ try
+ {
+ b.c();
+ }
+ catch (const std::logic_error &e)
+ {
+ EXPECT_THAT(
+ e.what(),
+ HasSubstr("Cannot access field \"b.c\" in state: V0_B_N")
+ );
+ throw;
+ }
+ },
+ std::logic_error
+ );
+}
+
+TEST_F(FieldAccessOrderCheckTest, disallowsDecodingBitSetInsideGroupBeforeCallingNext)
+{
+ BitSetInsideGroup encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ Flags &a = encoder.a();
+ a.cheese(true).guacamole(true).sourCream(false);
+ BitSetInsideGroup::B &b = encoder.bCount(1)
+ .next();
+ Flags &c = b.c();
+ c.cheese(false).guacamole(false).sourCream(true);
+ encoder.checkEncodingIsComplete();
+
+ BitSetInsideGroup decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ BitSetInsideGroup::sbeBlockLength(),
+ BitSetInsideGroup::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ Flags &aFlags = decoder.a();
+ EXPECT_EQ(aFlags.guacamole(), true);
+ EXPECT_EQ(aFlags.cheese(), true);
+ EXPECT_EQ(aFlags.sourCream(), false);
+ BitSetInsideGroup::B &bGroup = decoder.b();
+ EXPECT_EQ(bGroup.count(), 1u);
+ EXPECT_THROW(
+ {
+ try
+ {
+ bGroup.c();
+ }
+ catch (const std::logic_error &e)
+ {
+ EXPECT_THAT(
+ e.what(),
+ HasSubstr("Cannot access field \"b.c\" in state: V0_B_N")
+ );
+ throw;
+ }
+ },
+ std::logic_error
+ );
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsReEncodingTopLevelBitSetViaReWrap)
+{
+ BitSetInsideGroup encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ Flags &a = encoder.a();
+ a.cheese(true).guacamole(true).sourCream(false);
+ BitSetInsideGroup::B &b = encoder.bCount(1)
+ .next();
+ Flags &c = b.c();
+ c.cheese(false).guacamole(false).sourCream(true);
+
+ Flags &aPrime = encoder.a();
+ aPrime.sourCream(true);
+ encoder.checkEncodingIsComplete();
+
+ BitSetInsideGroup decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ BitSetInsideGroup::sbeBlockLength(),
+ BitSetInsideGroup::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ Flags &aFlags = decoder.a();
+ EXPECT_EQ(aFlags.guacamole(), true);
+ EXPECT_EQ(aFlags.cheese(), true);
+ EXPECT_EQ(aFlags.sourCream(), true);
+ BitSetInsideGroup::B &bGroup = decoder.b();
+ EXPECT_EQ(bGroup.count(), 1u);
+ Flags &cFlags = bGroup.next().c();
+ EXPECT_EQ(cFlags.guacamole(), false);
+ EXPECT_EQ(cFlags.cheese(), false);
+ EXPECT_EQ(cFlags.sourCream(), true);
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsEncodingAndDecodingArrayInsideGroupInSchemaDefinedOrder)
+{
+ ArrayInsideGroup encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.putA(1, 2, 3, 4);
+ ArrayInsideGroup::B &b = encoder.bCount(1)
+ .next();
+ b.putC(5, 6, 7, 8);
+ encoder.checkEncodingIsComplete();
+
+ ArrayInsideGroup decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ ArrayInsideGroup::sbeBlockLength(),
+ ArrayInsideGroup::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(0), 1);
+ EXPECT_EQ(decoder.a(1), 2);
+ EXPECT_EQ(decoder.a(2), 3);
+ EXPECT_EQ(decoder.a(3), 4);
+ ArrayInsideGroup::B &bGroup = decoder.b();
+ EXPECT_EQ(bGroup.count(), 1u);
+ bGroup.next();
+ EXPECT_EQ(bGroup.c(0), 5);
+ EXPECT_EQ(bGroup.c(1), 6);
+ EXPECT_EQ(bGroup.c(2), 7);
+ EXPECT_EQ(bGroup.c(3), 8);
+}
+
+TEST_F(FieldAccessOrderCheckTest, disallowsEncodingArrayInsideGroupBeforeCallingNext1)
+{
+ ArrayInsideGroup encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.putA(1, 2, 3, 4);
+ ArrayInsideGroup::B &bEncoder = encoder.bCount(1);
+ EXPECT_THROW(
+ {
+ try
+ {
+ bEncoder.putC(5, 6, 7, 8);
+ }
+ catch (const std::logic_error &e)
+ {
+ EXPECT_THAT(e.what(), testing::HasSubstr("Cannot access field \"b.c\" in state: V0_B_N"));
+ throw;
+ }
+ },
+ std::logic_error
+ );
+}
+
+TEST_F(FieldAccessOrderCheckTest, disallowsEncodingArrayInsideGroupBeforeCallingNext2)
+{
+ ArrayInsideGroup encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.putA(1, 2, 3, 4);
+ ArrayInsideGroup::B &bEncoder = encoder.bCount(1);
+ const char c[4] = {5, 6, 7, 8};
+ const char *cPtr = c;
+ EXPECT_THROW(
+ {
+ try
+ {
+ bEncoder.putC(cPtr);
+ }
+ catch (const std::logic_error &e)
+ {
+ EXPECT_THAT(e.what(), testing::HasSubstr("Cannot access field \"b.c\" in state: V0_B_N"));
+ throw;
+ }
+ },
+ std::logic_error
+ );
+}
+
+TEST_F(FieldAccessOrderCheckTest, disallowsDecodingArrayInsideGroupBeforeCallingNext1)
+{
+ ArrayInsideGroup encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.putA(1, 2, 3, 4);
+ ArrayInsideGroup::B &bEncoder = encoder.bCount(1);
+ bEncoder.next();
+ bEncoder.putC(5, 6, 7, 8);
+ encoder.checkEncodingIsComplete();
+
+ ArrayInsideGroup decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ ArrayInsideGroup::sbeBlockLength(),
+ ArrayInsideGroup::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(0), 1);
+ EXPECT_EQ(decoder.a(1), 2);
+ EXPECT_EQ(decoder.a(2), 3);
+ EXPECT_EQ(decoder.a(3), 4);
+ ArrayInsideGroup::B &bGroup = decoder.b();
+ EXPECT_EQ(bGroup.count(), 1u);
+ EXPECT_THROW(
+ {
+ try
+ {
+ bGroup.c(0);
+ }
+ catch (const std::logic_error &e)
+ {
+ EXPECT_THAT(e.what(), testing::HasSubstr("Cannot access field \"b.c\" in state: V0_B_N"));
+ throw;
+ }
+ },
+ std::logic_error
+ );
+}
+
+TEST_F(FieldAccessOrderCheckTest, disallowsDecodingArrayInsideGroupBeforeCallingNext2)
+{
+ ArrayInsideGroup encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.putA(1, 2, 3, 4);
+ ArrayInsideGroup::B &bEncoder = encoder.bCount(1);
+ bEncoder.next();
+ bEncoder.putC(5, 6, 7, 8);
+ encoder.checkEncodingIsComplete();
+
+ ArrayInsideGroup decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ ArrayInsideGroup::sbeBlockLength(),
+ ArrayInsideGroup::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(0), 1);
+ EXPECT_EQ(decoder.a(1), 2);
+ EXPECT_EQ(decoder.a(2), 3);
+ EXPECT_EQ(decoder.a(3), 4);
+ ArrayInsideGroup::B &bGroup = decoder.b();
+ EXPECT_EQ(bGroup.count(), 1u);
+ EXPECT_THROW(
+ {
+ try
+ {
+ const std::uint64_t charCount = 4;
+ char c[charCount];
+ bGroup.getC(c, charCount);
+ }
+ catch (const std::logic_error &e)
+ {
+ EXPECT_THAT(e.what(), testing::HasSubstr("Cannot access field \"b.c\" in state: V0_B_N"));
+ throw;
+ }
+ },
+ std::logic_error
+ );
+}
+
+TEST_F(FieldAccessOrderCheckTest, disallowsDecodingArrayInsideGroupBeforeCallingNext3)
+{
+ ArrayInsideGroup encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.putA(1, 2, 3, 4);
+ ArrayInsideGroup::B &bEncoder = encoder.bCount(1);
+ bEncoder.next();
+ bEncoder.putC(5, 6, 7, 8);
+ encoder.checkEncodingIsComplete();
+
+ ArrayInsideGroup decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ ArrayInsideGroup::sbeBlockLength(),
+ ArrayInsideGroup::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(0), 1);
+ EXPECT_EQ(decoder.a(1), 2);
+ EXPECT_EQ(decoder.a(2), 3);
+ EXPECT_EQ(decoder.a(3), 4);
+ ArrayInsideGroup::B &bGroup = decoder.b();
+ EXPECT_EQ(bGroup.count(), 1u);
+ EXPECT_THROW(
+ {
+ try
+ {
+ bGroup.c();
+ }
+ catch (const std::logic_error &e)
+ {
+ EXPECT_THAT(e.what(), testing::HasSubstr("Cannot access field \"b.c\" in state: V0_B_N"));
+ throw;
+ }
+ },
+ std::logic_error
+ );
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsReEncodingTopLevelArray)
+{
+ ArrayInsideGroup encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.putA(1, 2, 3, 4);
+ ArrayInsideGroup::B &bEncoder = encoder.bCount(1);
+ bEncoder.next();
+ bEncoder.putC(5, 6, 7, 8);
+
+ encoder.putA(9, 10, 11, 12);
+ encoder.checkEncodingIsComplete();
+
+ ArrayInsideGroup decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ ArrayInsideGroup::sbeBlockLength(),
+ ArrayInsideGroup::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(0), 9);
+ EXPECT_EQ(decoder.a(1), 10);
+ EXPECT_EQ(decoder.a(2), 11);
+ EXPECT_EQ(decoder.a(3), 12);
+ ArrayInsideGroup::B &bGroup = decoder.b();
+ EXPECT_EQ(bGroup.count(), 1u);
+ bGroup.next();
+ EXPECT_EQ(bGroup.c(0), 5);
+ EXPECT_EQ(bGroup.c(1), 6);
+ EXPECT_EQ(bGroup.c(2), 7);
+ EXPECT_EQ(bGroup.c(3), 8);
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsEncodingAndDecodingGroupFieldsInSchemaDefinedOrder1)
+{
+ MultipleGroups encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(42);
+ encoder.bCount(0);
+ MultipleGroups::D &dEncoder = encoder.dCount(1);
+ dEncoder.next();
+ dEncoder.e(43);
+ encoder.checkEncodingIsComplete();
+
+ MultipleGroups decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ MultipleGroups::sbeBlockLength(),
+ MultipleGroups::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 42);
+ MultipleGroups::B &bGroup = decoder.b();
+ EXPECT_EQ(bGroup.count(), 0u);
+ MultipleGroups::D &dGroup = decoder.d();
+ EXPECT_EQ(dGroup.count(), 1u);
+ dGroup.next();
+ EXPECT_EQ(dGroup.e(), 43);
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsEncodingAndDecodingGroupFieldsInSchemaDefinedOrder2)
+{
+ MultipleGroups encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(41);
+ MultipleGroups::B &bEncoder = encoder.bCount(1);
+ bEncoder.next();
+ bEncoder.c(42);
+ MultipleGroups::D &dEncoder = encoder.dCount(1);
+ dEncoder.next();
+ dEncoder.e(43);
+ encoder.checkEncodingIsComplete();
+
+ MultipleGroups decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ MultipleGroups::sbeBlockLength(),
+ MultipleGroups::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 41);
+ MultipleGroups::B &bGroup = decoder.b();
+ EXPECT_EQ(bGroup.count(), 1u);
+ bGroup.next();
+ EXPECT_EQ(bGroup.c(), 42);
+ MultipleGroups::D &dGroup = decoder.d();
+ EXPECT_EQ(dGroup.count(), 1u);
+ dGroup.next();
+ EXPECT_EQ(dGroup.e(), 43);
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsReEncodingTopLevelPrimitiveFieldsAfterGroups)
+{
+ MultipleGroups encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(41);
+ MultipleGroups::B &bEncoder = encoder.bCount(1);
+ bEncoder.next();
+ bEncoder.c(42);
+ MultipleGroups::D &dEncoder = encoder.dCount(1);
+ dEncoder.next();
+ dEncoder.e(43);
+ encoder.a(44);
+ encoder.checkEncodingIsComplete();
+
+ MultipleGroups decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ MultipleGroups::sbeBlockLength(),
+ MultipleGroups::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 44);
+ MultipleGroups::B &bGroup = decoder.b();
+ EXPECT_EQ(bGroup.count(), 1u);
+ bGroup.next();
+ EXPECT_EQ(bGroup.c(), 42);
+ MultipleGroups::D &dGroup = decoder.d();
+ EXPECT_EQ(dGroup.count(), 1u);
+ dGroup.next();
+ EXPECT_EQ(dGroup.e(), 43);
+}
+
+TEST_F(FieldAccessOrderCheckTest, disallowsMissedEncodingOfGroupField)
+{
+ MultipleGroups encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(41);
+ EXPECT_THROW(
+ {
+ try
+ {
+ encoder.dCount(0);
+ }
+ catch (const std::logic_error &e)
+ {
+ EXPECT_THAT(
+ e.what(),
+ HasSubstr("Cannot encode count of repeating group \"d\" in state: V0_BLOCK")
+ );
+ throw;
+ }
+ }, std::logic_error);
+}
+
+TEST_F(FieldAccessOrderCheckTest, disallowsReEncodingEarlierGroupFields)
+{
+ MultipleGroups encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(41);
+ MultipleGroups::B &bEncoder = encoder.bCount(1);
+ bEncoder.next();
+ bEncoder.c(42);
+ MultipleGroups::D &dEncoder = encoder.dCount(1);
+ dEncoder.next();
+ dEncoder.e(43);
+ EXPECT_THROW(
+ {
+ try
+ {
+ encoder.bCount(1);
+ }
+ catch (const std::logic_error &e)
+ {
+ EXPECT_THAT(
+ e.what(),
+ HasSubstr("Cannot encode count of repeating group \"b\" in state: V0_D_1_BLOCK")
+ );
+ throw;
+ }
+ }, std::logic_error);
+}
+
+TEST_F(FieldAccessOrderCheckTest, disallowsReEncodingLatestGroupField)
+{
+ MultipleGroups encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(41);
+ MultipleGroups::B &bEncoder = encoder.bCount(1);
+ bEncoder.next();
+ bEncoder.c(42);
+ MultipleGroups::D &dEncoder = encoder.dCount(1);
+ dEncoder.next();
+ dEncoder.e(43);
+ EXPECT_THROW(
+ {
+ try
+ {
+ encoder.dCount(1);
+ }
+ catch (const std::logic_error &e)
+ {
+ EXPECT_THAT(
+ e.what(),
+ HasSubstr("Cannot encode count of repeating group \"d\" in state: V0_D_1_BLOCK")
+ );
+ throw;
+ }
+ }, std::logic_error);
+}
+
+TEST_F(FieldAccessOrderCheckTest, disallowsMissedDecodingOfGroupField)
+{
+ MultipleGroups encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(41);
+ MultipleGroups::B &bEncoder = encoder.bCount(1);
+ bEncoder.next();
+ bEncoder.c(42);
+ MultipleGroups::D &dEncoder = encoder.dCount(1);
+ dEncoder.next();
+ dEncoder.e(43);
+ encoder.checkEncodingIsComplete();
+
+ MultipleGroups decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ MultipleGroups::sbeBlockLength(),
+ MultipleGroups::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 41);
+ EXPECT_THROW(
+ {
+ try
+ {
+ decoder.d();
+ }
+ catch (const std::logic_error &e)
+ {
+ EXPECT_THAT(
+ e.what(),
+ HasSubstr("Cannot decode count of repeating group \"d\" in state: V0_BLOCK")
+ );
+ throw;
+ }
+ }, std::logic_error);
+}
+
+TEST_F(FieldAccessOrderCheckTest, disallowsReDecodingEarlierGroupField)
+{
+ MultipleGroups encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(41);
+ MultipleGroups::B &bEncoder = encoder.bCount(1);
+ bEncoder.next();
+ bEncoder.c(42);
+ MultipleGroups::D &dEncoder = encoder.dCount(1);
+ dEncoder.next();
+ dEncoder.e(43);
+ encoder.checkEncodingIsComplete();
+
+ MultipleGroups decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ MultipleGroups::sbeBlockLength(),
+ MultipleGroups::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 41);
+ MultipleGroups::B &bGroup = decoder.b();
+ EXPECT_EQ(bGroup.count(), 1u);
+ EXPECT_EQ(bGroup.next().c(), 42);
+ MultipleGroups::D &dGroup = decoder.d();
+ EXPECT_EQ(dGroup.count(), 1u);
+ EXPECT_EQ(dGroup.next().e(), 43);
+ EXPECT_THROW(
+ {
+ try
+ {
+ decoder.b();
+ }
+ catch (const std::logic_error &e)
+ {
+ EXPECT_THAT(
+ e.what(),
+ HasSubstr("Cannot decode count of repeating group \"b\" in state: V0_D_1_BLOCK")
+ );
+ throw;
+ }
+ }, std::logic_error);
+}
+
+TEST_F(FieldAccessOrderCheckTest, disallowsReDecodingLatestGroupField)
+{
+ MultipleGroups encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(41);
+ MultipleGroups::B &bEncoder = encoder.bCount(1);
+ bEncoder.next();
+ bEncoder.c(42);
+ MultipleGroups::D &dEncoder = encoder.dCount(1);
+ dEncoder.next();
+ dEncoder.e(43);
+ encoder.checkEncodingIsComplete();
+
+ MultipleGroups decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ MultipleGroups::sbeBlockLength(),
+ MultipleGroups::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 41);
+ MultipleGroups::B &bGroup = decoder.b();
+ EXPECT_EQ(bGroup.count(), 1u);
+ EXPECT_EQ(bGroup.next().c(), 42);
+ MultipleGroups::D &dGroup = decoder.d();
+ EXPECT_EQ(dGroup.count(), 1u);
+ EXPECT_EQ(dGroup.next().e(), 43);
+ EXPECT_THROW(
+ {
+ try
+ {
+ decoder.d();
+ }
+ catch (const std::logic_error &e)
+ {
+ EXPECT_THAT(
+ e.what(),
+ HasSubstr("Cannot decode count of repeating group \"d\" in state: V0_D_1_BLOCK")
+ );
+ throw;
+ }
+ }, std::logic_error);
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToDecodeAddedVarData)
+{
+ AddVarDataV1 encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(42);
+ encoder.putB("abc");
+ encoder.checkEncodingIsComplete();
+
+ AddVarDataV1 decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ AddVarDataV1::sbeBlockLength(),
+ AddVarDataV1::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 42);
+ EXPECT_EQ(decoder.getBAsString(), "abc");
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToDecodeMissingAddedVarDataAsNullValue1)
+{
+ AddVarDataV0 encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(42);
+ encoder.checkEncodingIsComplete();
+
+ AddVarDataV1 decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ AddVarDataV1::sbeBlockLength(),
+ 0,
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 42);
+ EXPECT_EQ(decoder.b(), nullptr);
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToDecodeMissingAddedVarDataAsNullValue2)
+{
+ AddVarDataV0 encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(42);
+ encoder.checkEncodingIsComplete();
+
+ AddVarDataV1 decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ AddVarDataV1::sbeBlockLength(),
+ 0,
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 42);
+ EXPECT_EQ(decoder.getBAsString(), "");
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToDecodeMissingAddedVarDataAsNullValue3)
+{
+ AddVarDataV0 encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(42);
+ encoder.checkEncodingIsComplete();
+
+ AddVarDataV1 decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ AddVarDataV1::sbeBlockLength(),
+ 0,
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 42);
+ EXPECT_EQ(decoder.getBAsJsonEscapedString(), "");
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToDecodeMissingAddedVarDataAsNullValue4)
+{
+ AddVarDataV0 encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(42);
+ encoder.checkEncodingIsComplete();
+
+ AddVarDataV1 decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ AddVarDataV1::sbeBlockLength(),
+ 0,
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 42);
+ char b[16];
+ EXPECT_EQ(decoder.getB(b, 16), 0u);
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsEncodingAndDecodingAsciiInsideGroupInSchemaDefinedOrder1)
+{
+ AsciiInsideGroup encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.putA("GBPUSD");
+ AsciiInsideGroup::B &bGroup = encoder.bCount(1);
+ bGroup.next().putC("EURUSD");
+ encoder.checkEncodingIsComplete();
+
+ AsciiInsideGroup decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ AsciiInsideGroup::sbeBlockLength(),
+ AsciiInsideGroup::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.getAAsString(), "GBPUSD");
+ AsciiInsideGroup::B &b = decoder.b();
+ EXPECT_EQ(b.count(), 1u);
+ EXPECT_EQ(b.next().getCAsString(), "EURUSD");
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsEncodingAndDecodingAsciiInsideGroupInSchemaDefinedOrder2)
+{
+ AsciiInsideGroup encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ std::string a("GBPUSD");
+ encoder.putA(a);
+ AsciiInsideGroup::B &bGroup = encoder.bCount(1);
+ bGroup.next()
+ .c(0, 'E')
+ .c(1, 'U')
+ .c(2, 'R')
+ .c(3, 'U')
+ .c(4, 'S')
+ .c(5, 'D');
+ encoder.checkEncodingIsComplete();
+
+ AsciiInsideGroup decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ AsciiInsideGroup::sbeBlockLength(),
+ AsciiInsideGroup::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ char aOut[6];
+ decoder.getA(aOut, 6);
+ EXPECT_STREQ(aOut, "GBPUSD");
+ AsciiInsideGroup::B &b = decoder.b();
+ EXPECT_EQ(b.count(), 1u);
+ EXPECT_EQ(b.next().c(0), 'E');
+ EXPECT_EQ(b.c(1), 'U');
+ EXPECT_EQ(b.c(2), 'R');
+ EXPECT_EQ(b.c(3), 'U');
+ EXPECT_EQ(b.c(4), 'S');
+ EXPECT_EQ(b.c(5), 'D');
+}
+
+TEST_F(FieldAccessOrderCheckTest, disallowsEncodingAsciiInsideGroupBeforeCallingNext1)
+{
+ AsciiInsideGroup encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.putA("GBPUSD");
+ AsciiInsideGroup::B &bGroup = encoder.bCount(1);
+ EXPECT_THROW(
+ {
+ try
+ {
+ bGroup.putC("EURUSD");
+ }
+ catch (const std::logic_error &e)
+ {
+ EXPECT_THAT(
+ e.what(),
+ HasSubstr("Cannot access field \"b.c\" in state: V0_B_N")
+ );
+ throw;
+ }
+ },
+ std::logic_error
+ );
+}
+
+TEST_F(FieldAccessOrderCheckTest, disallowsEncodingAsciiInsideGroupBeforeCallingNext2)
+{
+ AsciiInsideGroup encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.putA("GBPUSD");
+ AsciiInsideGroup::B &bGroup = encoder.bCount(1);
+ EXPECT_THROW(
+ {
+ try
+ {
+ const std::string c("EURUSD");
+ bGroup.putC(c);
+ }
+ catch (const std::logic_error &e)
+ {
+ EXPECT_THAT(
+ e.what(),
+ HasSubstr("Cannot access field \"b.c\" in state: V0_B_N")
+ );
+ throw;
+ }
+ },
+ std::logic_error
+ );
+}
+
+TEST_F(FieldAccessOrderCheckTest, disallowsEncodingAsciiInsideGroupBeforeCallingNext3)
+{
+ AsciiInsideGroup encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.putA("GBPUSD");
+ AsciiInsideGroup::B &bGroup = encoder.bCount(1);
+ EXPECT_THROW(
+ {
+ try
+ {
+ bGroup.c(0, 'E')
+ .c(1, 'U')
+ .c(2, 'R')
+ .c(3, 'U')
+ .c(4, 'S')
+ .c(5, 'D');
+ }
+ catch (const std::logic_error &e)
+ {
+ EXPECT_THAT(
+ e.what(),
+ HasSubstr("Cannot access field \"b.c\" in state: V0_B_N")
+ );
+ throw;
+ }
+ },
+ std::logic_error
+ );
+}
+
+TEST_F(FieldAccessOrderCheckTest, disallowsDecodingAsciiInsideGroupBeforeCallingNext1)
+{
+ AsciiInsideGroup encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.putA("GBPUSD");
+ AsciiInsideGroup::B &bGroup = encoder.bCount(1);
+ bGroup.next().putC("EURUSD");
+ encoder.checkEncodingIsComplete();
+
+ AsciiInsideGroup decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ AsciiInsideGroup::sbeBlockLength(),
+ AsciiInsideGroup::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.getAAsString(), "GBPUSD");
+ AsciiInsideGroup::B &b = decoder.b();
+ EXPECT_EQ(b.count(), 1u);
+ EXPECT_THROW(
+ {
+ try
+ {
+ b.getCAsString();
+ }
+ catch (const std::logic_error &e)
+ {
+ EXPECT_THAT(
+ e.what(),
+ HasSubstr("Cannot access field \"b.c\" in state: V0_B_N")
+ );
+ throw;
+ }
+ },
+ std::logic_error
+ );
+}
+
+TEST_F(FieldAccessOrderCheckTest, disallowsDecodingAsciiInsideGroupBeforeCallingNext2)
+{
+ AsciiInsideGroup encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.putA("GBPUSD");
+ AsciiInsideGroup::B &bGroup = encoder.bCount(1);
+ bGroup.next().putC("EURUSD");
+ encoder.checkEncodingIsComplete();
+
+ AsciiInsideGroup decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ AsciiInsideGroup::sbeBlockLength(),
+ AsciiInsideGroup::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.getAAsString(), "GBPUSD");
+ AsciiInsideGroup::B &b = decoder.b();
+ EXPECT_EQ(b.count(), 1u);
+ EXPECT_THROW(
+ {
+ try
+ {
+ b.c();
+ }
+ catch (const std::logic_error &e)
+ {
+ EXPECT_THAT(
+ e.what(),
+ HasSubstr("Cannot access field \"b.c\" in state: V0_B_N")
+ );
+ throw;
+ }
+ },
+ std::logic_error
+ );
+}
+
+TEST_F(FieldAccessOrderCheckTest, disallowsDecodingAsciiInsideGroupBeforeCallingNext3)
+{
+ AsciiInsideGroup encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.putA("GBPUSD");
+ AsciiInsideGroup::B &bGroup = encoder.bCount(1);
+ bGroup.next().putC("EURUSD");
+ encoder.checkEncodingIsComplete();
+
+ AsciiInsideGroup decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ AsciiInsideGroup::sbeBlockLength(),
+ AsciiInsideGroup::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.getAAsString(), "GBPUSD");
+ AsciiInsideGroup::B &b = decoder.b();
+ EXPECT_EQ(b.count(), 1u);
+ EXPECT_THROW(
+ {
+ try
+ {
+ char c[6];
+ b.getC(c, 6);
+ }
+ catch (const std::logic_error &e)
+ {
+ EXPECT_THAT(
+ e.what(),
+ HasSubstr("Cannot access field \"b.c\" in state: V0_B_N")
+ );
+ throw;
+ }
+ },
+ std::logic_error
+ );
+}
+
+TEST_F(FieldAccessOrderCheckTest, disallowsDecodingAsciiInsideGroupBeforeCallingNext4)
+{
+ AsciiInsideGroup encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.putA("GBPUSD");
+ AsciiInsideGroup::B &bGroup = encoder.bCount(1);
+ bGroup.next().putC("EURUSD");
+ encoder.checkEncodingIsComplete();
+
+ AsciiInsideGroup decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ AsciiInsideGroup::sbeBlockLength(),
+ AsciiInsideGroup::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.getAAsString(), "GBPUSD");
+ AsciiInsideGroup::B &b = decoder.b();
+ EXPECT_EQ(b.count(), 1u);
+ EXPECT_THROW(
+ {
+ try
+ {
+ b.c(0);
+ }
+ catch (const std::logic_error &e)
+ {
+ EXPECT_THAT(
+ e.what(),
+ HasSubstr("Cannot access field \"b.c\" in state: V0_B_N")
+ );
+ throw;
+ }
+ },
+ std::logic_error
+ );
+}
+
+TEST_F(FieldAccessOrderCheckTest, disallowsDecodingAsciiInsideGroupBeforeCallingNext5)
+{
+ AsciiInsideGroup encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.putA("GBPUSD");
+ AsciiInsideGroup::B &bGroup = encoder.bCount(1);
+ bGroup.next().putC("EURUSD");
+ encoder.checkEncodingIsComplete();
+
+ AsciiInsideGroup decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ AsciiInsideGroup::sbeBlockLength(),
+ AsciiInsideGroup::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.getAAsString(), "GBPUSD");
+ AsciiInsideGroup::B &b = decoder.b();
+ EXPECT_EQ(b.count(), 1u);
+ EXPECT_THROW(
+ {
+ try
+ {
+ b.getCAsJsonEscapedString();
+ }
+ catch (const std::logic_error &e)
+ {
+ EXPECT_THAT(
+ e.what(),
+ HasSubstr("Cannot access field \"b.c\" in state: V0_B_N")
+ );
+ throw;
+ }
+ },
+ std::logic_error
+ );
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsReEncodingTopLevelAscii)
+{
+ AsciiInsideGroup encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.putA("GBPUSD");
+ AsciiInsideGroup::B &bGroup = encoder.bCount(1);
+ bGroup.next().putC("EURUSD");
+
+ encoder.putA("CADUSD");
+ encoder.checkEncodingIsComplete();
+
+ AsciiInsideGroup decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ AsciiInsideGroup::sbeBlockLength(),
+ AsciiInsideGroup::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.getAAsString(), "CADUSD");
+ AsciiInsideGroup::B &b = decoder.b();
+ EXPECT_EQ(b.count(), 1u);
+ b.next();
+ EXPECT_EQ(b.getCAsString(), "EURUSD");
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToDecodeAddedAsciiFieldBeforeGroup1)
+{
+ AddAsciiBeforeGroupV1 encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(1);
+ encoder.putD("EURUSD");
+ AddAsciiBeforeGroupV1::B &bGroup = encoder.bCount(1);
+ bGroup.next().c(2);
+ encoder.checkEncodingIsComplete();
+
+ AddAsciiBeforeGroupV1 decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ AddAsciiBeforeGroupV1::sbeBlockLength(),
+ AddAsciiBeforeGroupV1::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 1);
+ EXPECT_EQ(decoder.getDAsString(), "EURUSD");
+ AddAsciiBeforeGroupV1::B &b = decoder.b();
+ EXPECT_EQ(b.count(), 1u);
+ EXPECT_EQ(b.next().c(), 2);
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToDecodeAddedAsciiFieldBeforeGroup2)
+{
+ AddAsciiBeforeGroupV1 encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(1);
+ encoder.putD("EURUSD");
+ AddAsciiBeforeGroupV1::B &bGroup = encoder.bCount(1);
+ bGroup.next().c(2);
+ encoder.checkEncodingIsComplete();
+
+ AddAsciiBeforeGroupV1 decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ AddAsciiBeforeGroupV1::sbeBlockLength(),
+ AddAsciiBeforeGroupV1::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 1);
+ const char *d = decoder.d();
+ const std::string dAsString(d, 6);
+ ASSERT_EQ(dAsString, "EURUSD");
+ AddAsciiBeforeGroupV1::B &b = decoder.b();
+ EXPECT_EQ(b.count(), 1u);
+ EXPECT_EQ(b.next().c(), 2);
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToDecodeAddedAsciiFieldBeforeGroup3)
+{
+ AddAsciiBeforeGroupV1 encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(1);
+ encoder.putD("EURUSD");
+ AddAsciiBeforeGroupV1::B &bGroup = encoder.bCount(1);
+ bGroup.next().c(2);
+ encoder.checkEncodingIsComplete();
+
+ AddAsciiBeforeGroupV1 decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ AddAsciiBeforeGroupV1::sbeBlockLength(),
+ AddAsciiBeforeGroupV1::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ const AddAsciiBeforeGroupV1 &constDecoder = decoder;
+ EXPECT_EQ(constDecoder.a(), 1);
+ const char *d = constDecoder.d();
+ const std::string dAsString(d, 6);
+ ASSERT_EQ(dAsString, "EURUSD");
+ AddAsciiBeforeGroupV1::B &b = decoder.b();
+ EXPECT_EQ(b.count(), 1u);
+ EXPECT_EQ(b.next().c(), 2);
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToDecodeAddedAsciiFieldBeforeGroup4)
+{
+ AddAsciiBeforeGroupV1 encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(1);
+ encoder.putD("EURUSD");
+ AddAsciiBeforeGroupV1::B &bGroup = encoder.bCount(1);
+ bGroup.next().c(2);
+ encoder.checkEncodingIsComplete();
+
+ AddAsciiBeforeGroupV1 decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ AddAsciiBeforeGroupV1::sbeBlockLength(),
+ AddAsciiBeforeGroupV1::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 1);
+ char d[6];
+ decoder.getD(d, 6);
+ const std::string dAsString(d, 6);
+ ASSERT_EQ(dAsString, "EURUSD");
+ AddAsciiBeforeGroupV1::B &b = decoder.b();
+ EXPECT_EQ(b.count(), 1u);
+ EXPECT_EQ(b.next().c(), 2);
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToDecodeAddedAsciiFieldBeforeGroup5)
+{
+ AddAsciiBeforeGroupV1 encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(1);
+ encoder.putD("EURUSD");
+ AddAsciiBeforeGroupV1::B &bGroup = encoder.bCount(1);
+ bGroup.next().c(2);
+ encoder.checkEncodingIsComplete();
+
+ AddAsciiBeforeGroupV1 decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ AddAsciiBeforeGroupV1::sbeBlockLength(),
+ AddAsciiBeforeGroupV1::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 1);
+ ASSERT_EQ(decoder.d(0), 'E');
+ ASSERT_EQ(decoder.d(1), 'U');
+ ASSERT_EQ(decoder.d(2), 'R');
+ ASSERT_EQ(decoder.d(3), 'U');
+ ASSERT_EQ(decoder.d(4), 'S');
+ ASSERT_EQ(decoder.d(5), 'D');
+ AddAsciiBeforeGroupV1::B &b = decoder.b();
+ EXPECT_EQ(b.count(), 1u);
+ EXPECT_EQ(b.next().c(), 2);
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToDecodeAddedAsciiFieldBeforeGroup6)
+{
+ AddAsciiBeforeGroupV1 encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(1);
+ encoder.putD("EURUSD");
+ AddAsciiBeforeGroupV1::B &bGroup = encoder.bCount(1);
+ bGroup.next().c(2);
+ encoder.checkEncodingIsComplete();
+
+ AddAsciiBeforeGroupV1 decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ AddAsciiBeforeGroupV1::sbeBlockLength(),
+ AddAsciiBeforeGroupV1::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 1);
+ ASSERT_EQ(decoder.dLength(), 6u);
+ AddAsciiBeforeGroupV1::B &b = decoder.b();
+ EXPECT_EQ(b.count(), 1u);
+ EXPECT_EQ(b.next().c(), 2);
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToDecodeMissingAsciiFieldBeforeGroupAsNullValue1)
+{
+ AddAsciiBeforeGroupV0 encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(1);
+ AddAsciiBeforeGroupV0::B &bGroup = encoder.bCount(1);
+ bGroup.next().c(2);
+ encoder.checkEncodingIsComplete();
+
+ AddAsciiBeforeGroupV1 decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ AddAsciiBeforeGroupV0::sbeBlockLength(),
+ 0,
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 1);
+ ASSERT_EQ(decoder.d(0), AddAsciiBeforeGroupV1::dNullValue());
+ ASSERT_EQ(decoder.d(1), AddAsciiBeforeGroupV1::dNullValue());
+ ASSERT_EQ(decoder.d(2), AddAsciiBeforeGroupV1::dNullValue());
+ ASSERT_EQ(decoder.d(3), AddAsciiBeforeGroupV1::dNullValue());
+ ASSERT_EQ(decoder.d(4), AddAsciiBeforeGroupV1::dNullValue());
+ ASSERT_EQ(decoder.d(5), AddAsciiBeforeGroupV1::dNullValue());
+ AddAsciiBeforeGroupV1::B &b = decoder.b();
+ EXPECT_EQ(b.count(), 1u);
+ EXPECT_EQ(b.next().c(), 2);
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToDecodeMissingAsciiFieldBeforeGroupAsNullValue2)
+{
+ AddAsciiBeforeGroupV0 encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(1);
+ AddAsciiBeforeGroupV0::B &bGroup = encoder.bCount(1);
+ bGroup.next().c(2);
+ encoder.checkEncodingIsComplete();
+
+ AddAsciiBeforeGroupV1 decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ AddAsciiBeforeGroupV0::sbeBlockLength(),
+ 0,
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 1);
+ char d[6];
+ ASSERT_EQ(decoder.getD(d, 6), 0u);
+ AddAsciiBeforeGroupV1::B &b = decoder.b();
+ EXPECT_EQ(b.count(), 1u);
+ EXPECT_EQ(b.next().c(), 2);
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToDecodeMissingAsciiFieldBeforeGroupAsNullValue3)
+{
+ AddAsciiBeforeGroupV0 encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(1);
+ AddAsciiBeforeGroupV0::B &bGroup = encoder.bCount(1);
+ bGroup.next().c(2);
+ encoder.checkEncodingIsComplete();
+
+ AddAsciiBeforeGroupV1 decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ AddAsciiBeforeGroupV0::sbeBlockLength(),
+ 0,
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 1);
+ ASSERT_EQ(decoder.getDAsString(), "");
+ AddAsciiBeforeGroupV1::B &b = decoder.b();
+ EXPECT_EQ(b.count(), 1u);
+ EXPECT_EQ(b.next().c(), 2);
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsNewDecoderToSkipPresentButAddedAsciiFieldBeforeGroup)
+{
+ AddAsciiBeforeGroupV1 encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(1);
+ encoder.putD("EURUSD");
+ AddAsciiBeforeGroupV1::B &bGroup = encoder.bCount(1);
+ bGroup.next().c(2);
+ encoder.checkEncodingIsComplete();
+
+ AddAsciiBeforeGroupV1 decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ AddAsciiBeforeGroupV1::sbeBlockLength(),
+ AddAsciiBeforeGroupV1::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 1);
+ AddAsciiBeforeGroupV1::B &b = decoder.b();
+ EXPECT_EQ(b.count(), 1u);
+ EXPECT_EQ(b.next().c(), 2);
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsOldDecoderToSkipAddedAsciiFieldBeforeGroup)
+{
+ AddAsciiBeforeGroupV1 encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(1);
+ encoder.putD("EURUSD");
+ AddAsciiBeforeGroupV1::B &bGroup = encoder.bCount(1);
+ bGroup.next().c(2);
+ encoder.checkEncodingIsComplete();
+
+ AddAsciiBeforeGroupV0 decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ AddAsciiBeforeGroupV1::sbeBlockLength(),
+ AddAsciiBeforeGroupV1::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 1);
+ AddAsciiBeforeGroupV0::B &b = decoder.b();
+ EXPECT_EQ(b.count(), 1u);
+ EXPECT_EQ(b.next().c(), 2);
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsEncodeAndDecodeOfMessagesWithNoBlock)
+{
+ NoBlock encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.putA("abc");
+ encoder.checkEncodingIsComplete();
+
+ NoBlock decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ NoBlock::sbeBlockLength(),
+ NoBlock::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.getAAsString(), "abc");
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsEncodeAndDecodeOfGroupsWithNoBlock)
+{
+ GroupWithNoBlock encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ GroupWithNoBlock::A &aGroup = encoder.aCount(1);
+ aGroup.next().putB("abc");
+ encoder.checkEncodingIsComplete();
+
+ GroupWithNoBlock decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ GroupWithNoBlock::sbeBlockLength(),
+ GroupWithNoBlock::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ GroupWithNoBlock::A &a = decoder.a();
+ EXPECT_EQ(a.count(), 1u);
+ EXPECT_EQ(a.next().getBAsString(), "abc");
+}
+
+TEST_F(FieldAccessOrderCheckTest, disallowsEncodingElementOfEmptyGroup1)
+{
+ MultipleGroups encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(42);
+ MultipleGroups::B &bGroup = encoder.bCount(0);
+ MultipleGroups::D &dGroup = encoder.dCount(1);
+ dGroup.next().e(43);
+
+ EXPECT_THROW(
+ {
+ try
+ {
+ bGroup.c(44);
+ }
+ catch (const std::logic_error &e)
+ {
+ EXPECT_THAT(e.what(), testing::HasSubstr("Cannot access field \"b.c\" in state: V0_D_1_BLOCK"));
+ throw e;
+ }
+ },
+ std::logic_error
+ );
+}
+
+TEST_F(FieldAccessOrderCheckTest, disallowsEncodingElementOfEmptyGroup2)
+{
+ NestedGroups encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(42);
+ NestedGroups::B &bGroup = encoder.bCount(1);
+ bGroup.next();
+ bGroup.c(43);
+ NestedGroups::B::D &dGroup = bGroup.dCount(0);
+ bGroup.fCount(0);
+ encoder.hCount(0);
+
+ EXPECT_THROW(
+ {
+ try
+ {
+ dGroup.e(44);
+ }
+ catch (const std::logic_error &e)
+ {
+ EXPECT_THAT(e.what(), testing::HasSubstr("Cannot access field \"b.d.e\" in state: V0_H_DONE"));
+ throw e;
+ }
+ },
+ std::logic_error
+ );
+}
+
+TEST_F(FieldAccessOrderCheckTest, disallowsEncodingElementOfEmptyGroup3)
+{
+ NestedGroups encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(42);
+ NestedGroups::B &bGroup = encoder.bCount(1);
+ bGroup.next();
+ bGroup.c(43);
+ NestedGroups::B::D &dGroup = bGroup.dCount(0);
+ bGroup.fCount(0);
+
+ EXPECT_THROW(
+ {
+ try
+ {
+ dGroup.e(44);
+ }
+ catch (const std::logic_error &e)
+ {
+ EXPECT_THAT(e.what(), testing::HasSubstr("Cannot access field \"b.d.e\" in state: V0_B_1_F_DONE"));
+ throw e;
+ }
+ },
+ std::logic_error
+ );
+}
+
+TEST_F(FieldAccessOrderCheckTest, disallowsEncodingElementOfEmptyGroup4)
+{
+ AddPrimitiveInsideGroupV1 encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(42);
+ AddPrimitiveInsideGroupV1::B &bGroup = encoder.bCount(0);
+
+ EXPECT_THROW(
+ {
+ try
+ {
+ bGroup.c(43);
+ }
+ catch (const std::logic_error &e)
+ {
+ EXPECT_THAT(e.what(), testing::HasSubstr("Cannot access field \"b.c\" in state: V1_B_DONE"));
+ throw e;
+ }
+ },
+ std::logic_error
+ );
+}
+
+TEST_F(FieldAccessOrderCheckTest, disallowsEncodingElementOfEmptyGroup5)
+{
+ GroupAndVarLength encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(42);
+ GroupAndVarLength::B &bGroup = encoder.bCount(0);
+ encoder.putD("abc");
+
+ EXPECT_THROW(
+ {
+ try
+ {
+ bGroup.c(43);
+ }
+ catch (const std::logic_error &e)
+ {
+ EXPECT_THAT(e.what(), testing::HasSubstr("Cannot access field \"b.c\" in state: V0_D_DONE"));
+ throw e;
+ }
+ },
+ std::logic_error
+ );
+}
+
+TEST_F(FieldAccessOrderCheckTest, allowsEncodingAndDecodingNestedGroupWithVarDataInSchemaDefinedOrder)
+{
+ NestedGroupWithVarLength encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(1);
+ NestedGroupWithVarLength::B &bGroup = encoder.bCount(3);
+ bGroup.next().c(2).dCount(0);
+ bGroup.next().c(3).dCount(1).next().e(4).putF("abc");
+ bGroup.next().c(5).dCount(2).next().e(6).putF("def").next().e(7).putF("ghi");
+ encoder.checkEncodingIsComplete();
+
+ NestedGroupWithVarLength decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ NestedGroupWithVarLength::sbeBlockLength(),
+ NestedGroupWithVarLength::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 1);
+ NestedGroupWithVarLength::B &bDecoder = decoder.b();
+ EXPECT_EQ(bDecoder.count(), 3u);
+ EXPECT_EQ(bDecoder.next().c(), 2);
+ NestedGroupWithVarLength::B::D &dDecoder1 = bDecoder.d();
+ EXPECT_EQ(dDecoder1.count(), 0u);
+ EXPECT_EQ(bDecoder.next().c(), 3);
+ NestedGroupWithVarLength::B::D &dDecoder2 = bDecoder.d();
+ EXPECT_EQ(dDecoder2.count(), 1u);
+ EXPECT_EQ(dDecoder2.next().e(), 4);
+ EXPECT_EQ(dDecoder2.getFAsString(), "abc");
+ EXPECT_EQ(bDecoder.next().c(), 5);
+ NestedGroupWithVarLength::B::D &dDecoder3 = bDecoder.d();
+ EXPECT_EQ(dDecoder3.count(), 2u);
+ EXPECT_EQ(dDecoder3.next().e(), 6);
+ EXPECT_EQ(dDecoder3.getFAsString(), "def");
+ EXPECT_EQ(dDecoder3.next().e(), 7);
+ EXPECT_EQ(dDecoder3.getFAsString(), "ghi");
+}
+
+TEST_F(FieldAccessOrderCheckTest, disallowsMissedEncodingOfVarLengthFieldInNestedGroupToNextInnerElement1)
+{
+ NestedGroupWithVarLength encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(1);
+ NestedGroupWithVarLength::B &bGroup = encoder.bCount(1);
+ NestedGroupWithVarLength::B::D &dGroup = bGroup.next().c(5).dCount(2);
+ dGroup.next().e(7);
+
+ EXPECT_THROW(
+ {
+ try
+ {
+ dGroup.next();
+ }
+ catch (const std::logic_error &e)
+ {
+ EXPECT_THAT(e.what(),
+ testing::HasSubstr(
+ "Cannot access next element in repeating group \"b.d\" in state: V0_B_1_D_N_BLOCK"));
+ EXPECT_THAT(e.what(),
+ testing::HasSubstr(
+ "Expected one of these transitions: [\"b.d.e(?)\", \"b.d.fLength()\", \"b.d.f(?)\"]."));
+ throw e;
+ }
+ },
+ std::logic_error
+ );
+}
+
+TEST_F(FieldAccessOrderCheckTest, disallowsMissedEncodingOfVarLengthFieldInNestedGroupToNextInnerElement2)
+{
+ NestedGroupWithVarLength encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(1);
+ NestedGroupWithVarLength::B &bGroup = encoder.bCount(2);
+ NestedGroupWithVarLength::B::D &dGroup = bGroup.next().c(5).dCount(2);
+ dGroup.next().e(7);
+
+ EXPECT_THROW(
+ {
+ try
+ {
+ dGroup.next();
+ }
+ catch (const std::logic_error &e)
+ {
+ EXPECT_THAT(e.what(),
+ testing::HasSubstr(
+ "Cannot access next element in repeating group \"b.d\" in state: V0_B_N_D_N_BLOCK"));
+ EXPECT_THAT(e.what(),
+ testing::HasSubstr(
+ "Expected one of these transitions: [\"b.d.e(?)\", \"b.d.fLength()\", \"b.d.f(?)\"]."));
+ throw e;
+ }
+ },
+ std::logic_error
+ );
+}
+
+TEST_F(FieldAccessOrderCheckTest, disallowsMissedDecodingOfVarLengthFieldInNestedGroupToNextInnerElement1)
+{
+ NestedGroupWithVarLength encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(1);
+ NestedGroupWithVarLength::B &bGroup = encoder.bCount(1);
+ NestedGroupWithVarLength::B::D &dGroup = bGroup.next().c(2).dCount(2);
+ dGroup.next().e(3).putF("abc");
+ dGroup.next().e(4).putF("def");
+ encoder.checkEncodingIsComplete();
+
+ NestedGroupWithVarLength decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ NestedGroupWithVarLength::sbeBlockLength(),
+ NestedGroupWithVarLength::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 1);
+ NestedGroupWithVarLength::B &bDecoder = decoder.b();
+ EXPECT_EQ(bDecoder.count(), 1u);
+ EXPECT_EQ(bDecoder.next().c(), 2);
+ NestedGroupWithVarLength::B::D &dDecoder = bDecoder.d();
+ EXPECT_EQ(dDecoder.count(), 2u);
+ EXPECT_EQ(dDecoder.next().e(), 3);
+
+ EXPECT_THROW(
+ {
+ try
+ {
+ dDecoder.next();
+ }
+ catch (const std::logic_error &e)
+ {
+ EXPECT_THAT(e.what(),
+ testing::HasSubstr(
+ "Cannot access next element in repeating group \"b.d\" in state: V0_B_1_D_N_BLOCK."));
+ EXPECT_THAT(e.what(),
+ testing::HasSubstr(
+ "Expected one of these transitions: [\"b.d.e(?)\", \"b.d.fLength()\", \"b.d.f(?)\"]."));
+ throw e;
+ }
+ },
+ std::logic_error
+ );
+}
+
+TEST_F(FieldAccessOrderCheckTest, disallowsMissedDecodingOfVarLengthFieldInNestedGroupToNextInnerElement2)
+{
+ NestedGroupWithVarLength encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(1);
+ NestedGroupWithVarLength::B &bGroup = encoder.bCount(2);
+ NestedGroupWithVarLength::B::D &dGroup = bGroup.next().c(2).dCount(2);
+ dGroup.next().e(3).putF("abc");
+ dGroup.next().e(4).putF("def");
+ bGroup.next().c(5).dCount(0);
+ encoder.checkEncodingIsComplete();
+
+ NestedGroupWithVarLength decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ NestedGroupWithVarLength::sbeBlockLength(),
+ NestedGroupWithVarLength::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 1);
+ NestedGroupWithVarLength::B &bDecoder = decoder.b();
+ EXPECT_EQ(bDecoder.count(), 2u);
+ EXPECT_EQ(bDecoder.next().c(), 2);
+ NestedGroupWithVarLength::B::D &dDecoder = bDecoder.d();
+ EXPECT_EQ(dDecoder.count(), 2u);
+ EXPECT_EQ(dDecoder.next().e(), 3);
+
+ EXPECT_THROW(
+ {
+ try
+ {
+ dDecoder.next();
+ }
+ catch (const std::logic_error &e)
+ {
+ EXPECT_THAT(e.what(),
+ testing::HasSubstr(
+ "Cannot access next element in repeating group \"b.d\" in state: V0_B_N_D_N_BLOCK."));
+ EXPECT_THAT(e.what(),
+ testing::HasSubstr(
+ "Expected one of these transitions: [\"b.d.e(?)\", \"b.d.fLength()\", \"b.d.f(?)\"]."));
+ throw e;
+ }
+ },
+ std::logic_error
+ );
+}
+
+TEST_F(FieldAccessOrderCheckTest, disallowsMissedDecodingOfVarLengthFieldInNestedGroupToNextOuterElement1)
+{
+ NestedGroupWithVarLength encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(1);
+ NestedGroupWithVarLength::B &bGroup = encoder.bCount(2);
+ NestedGroupWithVarLength::B::D &dGroup = bGroup.next().c(2).dCount(2);
+ dGroup.next().e(3).putF("abc");
+ dGroup.next().e(4).putF("def");
+ bGroup.next().c(5).dCount(0);
+ encoder.checkEncodingIsComplete();
+
+ NestedGroupWithVarLength decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ NestedGroupWithVarLength::sbeBlockLength(),
+ NestedGroupWithVarLength::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 1);
+ NestedGroupWithVarLength::B &bDecoder = decoder.b();
+ EXPECT_EQ(bDecoder.count(), 2u);
+ EXPECT_EQ(bDecoder.next().c(), 2);
+ NestedGroupWithVarLength::B::D &dDecoder = bDecoder.d();
+ EXPECT_EQ(dDecoder.count(), 2u);
+ EXPECT_EQ(dDecoder.next().e(), 3);
+
+ EXPECT_THROW(
+ {
+ try
+ {
+ bDecoder.next();
+ }
+ catch (const std::logic_error &e)
+ {
+ EXPECT_THAT(e.what(),
+ testing::HasSubstr(
+ "Cannot access next element in repeating group \"b\" in state: V0_B_N_D_N_BLOCK."));
+ EXPECT_THAT(e.what(),
+ testing::HasSubstr(
+ "Expected one of these transitions: [\"b.d.e(?)\", \"b.d.fLength()\", \"b.d.f(?)\"]."));
+ throw e;
+ }
+ },
+ std::logic_error
+ );
+}
+
+TEST_F(FieldAccessOrderCheckTest, disallowsMissedDecodingOfVarLengthFieldInNestedGroupToNextOuterElement2)
+{
+ NestedGroupWithVarLength encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(1);
+ NestedGroupWithVarLength::B &bGroup = encoder.bCount(2);
+ NestedGroupWithVarLength::B::D &dGroup = bGroup.next().c(2).dCount(1);
+ dGroup.next().e(3).putF("abc");
+ bGroup.next().c(5).dCount(0);
+ encoder.checkEncodingIsComplete();
+
+ NestedGroupWithVarLength decoder;
+ decoder.wrapForDecode(
+ m_buffer,
+ OFFSET,
+ NestedGroupWithVarLength::sbeBlockLength(),
+ NestedGroupWithVarLength::sbeSchemaVersion(),
+ BUFFER_LEN
+ );
+ EXPECT_EQ(decoder.a(), 1);
+ NestedGroupWithVarLength::B &bDecoder = decoder.b();
+ EXPECT_EQ(bDecoder.count(), 2u);
+ EXPECT_EQ(bDecoder.next().c(), 2);
+ NestedGroupWithVarLength::B::D &dDecoder = bDecoder.d();
+ EXPECT_EQ(dDecoder.count(), 1u);
+ EXPECT_EQ(dDecoder.next().e(), 3);
+
+ EXPECT_THROW(
+ {
+ try
+ {
+ bDecoder.next();
+ }
+ catch (const std::logic_error &e)
+ {
+ EXPECT_THAT(e.what(),
+ testing::HasSubstr(
+ "Cannot access next element in repeating group \"b\" in state: V0_B_N_D_1_BLOCK."));
+ EXPECT_THAT(e.what(),
+ testing::HasSubstr(
+ "Expected one of these transitions: [\"b.d.e(?)\", \"b.d.fLength()\", \"b.d.f(?)\"]."));
+ throw e;
+ }
+ },
+ std::logic_error
+ );
+}
+
+TEST_F(FieldAccessOrderCheckTest, disallowsIncompleteMessagesDueToMissingVarLengthField1)
+{
+ MultipleVarLength encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(1).putB("abc");
+
+ EXPECT_THROW(
+ {
+ try
+ {
+ encoder.checkEncodingIsComplete();
+ }
+ catch (const std::logic_error &e)
+ {
+ EXPECT_THAT(e.what(),
+ testing::HasSubstr(
+ std::string("Not fully encoded, current state: V0_B_DONE, ") +
+ "allowed transitions: \"cLength()\", \"c(?)\""));
+ throw e;
+ }
+ },
+ std::logic_error
+ );
+}
+
+TEST_F(FieldAccessOrderCheckTest, disallowsIncompleteMessagesDueToMissingVarLengthField2)
+{
+ NoBlock encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+
+ EXPECT_THROW(
+ {
+ try
+ {
+ encoder.checkEncodingIsComplete();
+ }
+ catch (const std::logic_error &e)
+ {
+ EXPECT_THAT(e.what(),
+ testing::HasSubstr(
+ std::string("Not fully encoded, current state: V0_BLOCK, ") +
+ "allowed transitions: \"aLength()\", \"a(?)\""));
+ throw e;
+ }
+ },
+ std::logic_error
+ );
+}
+
+TEST_F(FieldAccessOrderCheckTest, disallowsIncompleteMessagesDueToMissingTopLevelGroup1)
+{
+ MultipleGroups encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(1).bCount(0);
+
+ EXPECT_THROW(
+ {
+ try
+ {
+ encoder.checkEncodingIsComplete();
+ }
+ catch (const std::logic_error &e)
+ {
+ EXPECT_THAT(e.what(), testing::HasSubstr(
+ "Not fully encoded, current state: V0_B_DONE, allowed transitions:"
+ " \"b.resetCountToIndex()\", \"dCount(0)\", \"dCount(>0)\""));
+ throw e;
+ }
+ },
+ std::logic_error
+ );
+}
+
+TEST_F(FieldAccessOrderCheckTest, disallowsIncompleteMessagesDueToMissingTopLevelGroup2)
+{
+ MultipleGroups encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(1).bCount(1).next().c(2);
+
+ EXPECT_THROW(
+ {
+ try
+ {
+ encoder.checkEncodingIsComplete();
+ }
+ catch (const std::logic_error &e)
+ {
+ EXPECT_THAT(e.what(), testing::HasSubstr(
+ "Not fully encoded, current state: V0_B_1_BLOCK, allowed transitions: "
+ "\"b.c(?)\", \"b.resetCountToIndex()\", \"dCount(0)\", \"dCount(>0)\""));
+ throw e;
+ }
+ },
+ std::logic_error
+ );
+}
+
+TEST_F(FieldAccessOrderCheckTest, disallowsIncompleteMessagesDueToMissingTopLevelGroup3)
+{
+ MultipleGroups encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(1);
+
+ EXPECT_THROW(
+ {
+ try
+ {
+ encoder.checkEncodingIsComplete();
+ }
+ catch (const std::logic_error &e)
+ {
+ EXPECT_THAT(e.what(), testing::HasSubstr(
+ "Not fully encoded, current state: V0_BLOCK, allowed transitions: "
+ "\"a(?)\", \"bCount(0)\", \"bCount(>0)\""));
+ throw e;
+ }
+ },
+ std::logic_error
+ );
+}
+
+class DisallowsIncompleteMessagesPart1Test : public FieldAccessOrderCheckTest,
+ public testing::WithParamInterface>
+{
+};
+
+TEST_P(DisallowsIncompleteMessagesPart1Test, disallowsIncompleteMessagesDueToMissingNestedGroup1)
+{
+ const auto bCount = std::get<0>(GetParam());
+ const auto expectedState = std::get<1>(GetParam());
+
+ NestedGroupWithVarLength encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(1).bCount(bCount).next().c(2);
+
+ EXPECT_THROW(
+ {
+ try
+ {
+ encoder.checkEncodingIsComplete();
+ }
+ catch (const std::logic_error &e)
+ {
+ EXPECT_THAT(e.what(), testing::HasSubstr(
+ "Not fully encoded, current state: " + expectedState));
+ throw e;
+ }
+ },
+ std::logic_error
+ );
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ FieldAccessOrderCheckTest,
+ DisallowsIncompleteMessagesPart1Test,
+ testing::Values(
+ std::make_tuple(1, "V0_B_1_BLOCK"),
+ std::make_tuple(2, "V0_B_N_BLOCK")
+ )
+);
+
+class DisallowsIncompleteMessagesPart2Test : public FieldAccessOrderCheckTest,
+ public testing::WithParamInterface>
+{
+};
+
+TEST_P(DisallowsIncompleteMessagesPart2Test, disallowsIncompleteMessagesDueToMissingNestedGroup2)
+{
+ const auto bCount = std::get<0>(GetParam());
+ const auto dCount = std::get<1>(GetParam());
+ const auto expectedState = std::get<2>(GetParam());
+
+ NestedGroupWithVarLength encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(1).bCount(bCount).next().c(2).dCount(dCount);
+
+ EXPECT_THROW(
+ {
+ try
+ {
+ encoder.checkEncodingIsComplete();
+ }
+ catch (const std::logic_error &e)
+ {
+ EXPECT_THAT(e.what(), testing::HasSubstr(
+ "Not fully encoded, current state: " + expectedState));
+ throw e;
+ }
+ },
+ std::logic_error
+ );
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ FieldAccessOrderCheckTest,
+ DisallowsIncompleteMessagesPart2Test,
+ testing::Values(
+ std::make_tuple(1, 1, "V0_B_1_D_N"),
+ std::make_tuple(1, 2, "V0_B_1_D_N"),
+ std::make_tuple(2, 0, "V0_B_N_D_DONE"),
+ std::make_tuple(2, 1, "V0_B_N_D_N"),
+ std::make_tuple(2, 2, "V0_B_N_D_N")
+ )
+);
+
+class DisallowsIncompleteMessagesPart3Test : public FieldAccessOrderCheckTest,
+ public testing::WithParamInterface>
+{
+};
+
+TEST_P(DisallowsIncompleteMessagesPart3Test, disallowsIncompleteMessagesDueToMissingVarDataInNestedGroup)
+{
+ const auto bCount = std::get<0>(GetParam());
+ const auto dCount = std::get<1>(GetParam());
+ const auto expectedState = std::get<2>(GetParam());
+
+ NestedGroupWithVarLength encoder;
+ encoder.wrapForEncode(m_buffer, OFFSET, BUFFER_LEN);
+ encoder.a(1).bCount(bCount).next().c(2).dCount(dCount).next().e(10);
+
+ EXPECT_THROW(
+ {
+ try
+ {
+ encoder.checkEncodingIsComplete();
+ }
+ catch (const std::logic_error &e)
+ {
+ EXPECT_THAT(e.what(), testing::HasSubstr(
+ "Not fully encoded, current state: " + expectedState));
+ throw e;
+ }
+ },
+ std::logic_error
+ );
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ FieldAccessOrderCheckTest,
+ DisallowsIncompleteMessagesPart3Test,
+ testing::Values(
+ std::make_tuple(1, 1, "V0_B_1_D_1_BLOCK"),
+ std::make_tuple(1, 2, "V0_B_1_D_N_BLOCK"),
+ std::make_tuple(2, 1, "V0_B_N_D_1_BLOCK"),
+ std::make_tuple(2, 2, "V0_B_N_D_N_BLOCK")
+ )
+);
diff --git a/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldAccessOrderCheckTest.java b/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldAccessOrderCheckTest.java
new file mode 100644
index 0000000000..97d51b199c
--- /dev/null
+++ b/sbe-tool/src/test/java/uk/co/real_logic/sbe/FieldAccessOrderCheckTest.java
@@ -0,0 +1,3425 @@
+/*
+ * Copyright 2013-2023 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;
+
+import order.check.*;
+import org.agrona.ExpandableArrayBuffer;
+import org.agrona.MutableDirectBuffer;
+import org.agrona.concurrent.UnsafeBuffer;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.CsvSource;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Random;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.*;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assumptions.assumeFalse;
+
+public class FieldAccessOrderCheckTest
+{
+ private static final Class INCORRECT_ORDER_EXCEPTION_CLASS = IllegalStateException.class;
+ private static final int OFFSET = 0;
+ private final MutableDirectBuffer buffer = new ExpandableArrayBuffer();
+ private final MessageHeaderEncoder messageHeaderEncoder = new MessageHeaderEncoder();
+ private final MessageHeaderDecoder messageHeaderDecoder = new MessageHeaderDecoder();
+
+ @BeforeAll
+ static void assumeDebugMode()
+ {
+ final boolean productionMode = Boolean.getBoolean("agrona.disable.bounds.checks");
+ assumeFalse(productionMode);
+ }
+
+ @BeforeEach
+ void setUp()
+ {
+ new Random().nextBytes(buffer.byteArray());
+ }
+
+ @Test
+ void allowsEncodingAndDecodingVariableLengthFieldsInSchemaDefinedOrder()
+ {
+ final MultipleVarLengthEncoder encoder = new MultipleVarLengthEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(42);
+ encoder.b("abc");
+ encoder.c("def");
+ encoder.checkEncodingIsComplete();
+
+ final MultipleVarLengthDecoder decoder = new MultipleVarLengthDecoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(42));
+ assertThat(decoder.b(), equalTo("abc"));
+ assertThat(decoder.c(), equalTo("def"));
+ assertThat(decoder.toString(), containsString("a=42|b='abc'|c='def'"));
+ }
+
+ @Test
+ void allowsDecodingVariableLengthFieldsAfterRewind()
+ {
+ final MultipleVarLengthEncoder encoder = new MultipleVarLengthEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(42);
+ encoder.b("abc");
+ encoder.c("def");
+ encoder.checkEncodingIsComplete();
+
+ final MultipleVarLengthDecoder decoder = new MultipleVarLengthDecoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(42));
+ assertThat(decoder.b(), equalTo("abc"));
+ assertThat(decoder.c(), equalTo("def"));
+ assertThat(decoder.toString(), containsString("a=42|b='abc'|c='def'"));
+
+ decoder.sbeRewind();
+
+ assertThat(decoder.a(), equalTo(42));
+ assertThat(decoder.b(), equalTo("abc"));
+ assertThat(decoder.c(), equalTo("def"));
+ assertThat(decoder.toString(), containsString("a=42|b='abc'|c='def'"));
+ }
+
+ @Test
+ void allowsDecodingToSkipVariableLengthFields()
+ {
+ final MultipleVarLengthEncoder encoder = new MultipleVarLengthEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(42);
+ encoder.b("abc");
+ encoder.c("def");
+ encoder.checkEncodingIsComplete();
+
+ final MultipleVarLengthDecoder decoder = new MultipleVarLengthDecoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(42));
+ assertThat(decoder.skipB(), equalTo(3));
+ assertThat(decoder.c(), equalTo("def"));
+ assertThat(decoder.toString(), containsString("a=42|b='abc'|c='def'"));
+ }
+
+ @Test
+ void allowsReEncodingTopLevelPrimitiveFields()
+ {
+ final MultipleVarLengthEncoder encoder = new MultipleVarLengthEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(42);
+ encoder.b("abc");
+ encoder.c("def");
+ encoder.a(43);
+ encoder.checkEncodingIsComplete();
+
+ final MultipleVarLengthDecoder decoder = new MultipleVarLengthDecoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(43));
+ assertThat(decoder.b(), equalTo("abc"));
+ assertThat(decoder.c(), equalTo("def"));
+ }
+
+ @Test
+ void disallowsSkippingEncodingOfVariableLengthField1()
+ {
+ final MultipleVarLengthEncoder encoder = new MultipleVarLengthEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(42);
+ final Exception exception =
+ assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> encoder.c("def"));
+ assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_BLOCK"));
+ }
+
+ @Test
+ void disallowsSkippingEncodingOfVariableLengthField2()
+ {
+ final MultipleVarLengthEncoder encoder = new MultipleVarLengthEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(42);
+ final CharSequence def = new StringBuilder("def");
+ final Exception exception =
+ assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> encoder.c(def));
+ assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_BLOCK"));
+ }
+
+ @Test
+ void disallowsSkippingEncodingOfVariableLengthField3()
+ {
+ final MultipleVarLengthEncoder encoder = new MultipleVarLengthEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(42);
+ final byte[] value = "def".getBytes(StandardCharsets.US_ASCII);
+ final Exception exception =
+ assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> encoder.putC(value, 0, value.length));
+ assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_BLOCK"));
+ }
+
+ @Test
+ void disallowsSkippingEncodingOfVariableLengthField4()
+ {
+ final MultipleVarLengthEncoder encoder = new MultipleVarLengthEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(42);
+ final byte[] value = "def".getBytes(StandardCharsets.US_ASCII);
+ final UnsafeBuffer buffer = new UnsafeBuffer(value);
+ final Exception exception =
+ assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> encoder.putC(buffer, 0, buffer.capacity()));
+ assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_BLOCK"));
+ }
+
+ @Test
+ void disallowsReEncodingEarlierVariableLengthFields()
+ {
+ final MultipleVarLengthEncoder encoder = new MultipleVarLengthEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(42);
+ encoder.b("abc");
+ encoder.c("def");
+ final Exception exception =
+ assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> encoder.b("ghi"));
+ assertThat(exception.getMessage(), containsString("Cannot access field \"b\" in state: V0_C_DONE"));
+ }
+
+ @Test
+ void disallowsReEncodingLatestVariableLengthField()
+ {
+ final MultipleVarLengthEncoder encoder = new MultipleVarLengthEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(42);
+ encoder.b("abc");
+ encoder.c("def");
+ final Exception exception =
+ assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> encoder.c("ghi"));
+ assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_C_DONE"));
+ }
+
+ @Test
+ void disallowsSkippingDecodingOfVariableLengthField1()
+ {
+ final MultipleVarLengthDecoder decoder = decodeUntilVarLengthFields();
+
+ final Exception exception =
+ assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, decoder::c);
+ assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_BLOCK"));
+ }
+
+ @Test
+ void disallowsSkippingDecodingOfVariableLengthField2()
+ {
+ final MultipleVarLengthDecoder decoder = decodeUntilVarLengthFields();
+ final Exception exception =
+ assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> decoder.wrapC(new UnsafeBuffer()));
+ assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_BLOCK"));
+ }
+
+ @Test
+ void disallowsSkippingDecodingOfVariableLengthField3()
+ {
+ final MultipleVarLengthDecoder decoder = decodeUntilVarLengthFields();
+ final Exception exception =
+ assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> decoder.getC(new StringBuilder()));
+ assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_BLOCK"));
+ }
+
+ @Test
+ void disallowsSkippingDecodingOfVariableLengthField4()
+ {
+ final MultipleVarLengthDecoder decoder = decodeUntilVarLengthFields();
+ final Exception exception =
+ assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> decoder.getC(new byte[3], 0, 3));
+ assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_BLOCK"));
+ }
+
+ @Test
+ void disallowsSkippingDecodingOfVariableLengthField5()
+ {
+ final MultipleVarLengthDecoder decoder = decodeUntilVarLengthFields();
+ final Exception exception =
+ assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> decoder.getC(new ExpandableArrayBuffer(), 0, 3));
+ assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_BLOCK"));
+ }
+
+ @Test
+ void allowsRepeatedDecodingOfVariableLengthDataLength()
+ {
+ final MultipleVarLengthDecoder decoder = decodeUntilVarLengthFields();
+ assertThat(decoder.bLength(), equalTo(3));
+ assertThat(decoder.bLength(), equalTo(3));
+ assertThat(decoder.bLength(), equalTo(3));
+ assertThat(decoder.b(), equalTo("abc"));
+ assertThat(decoder.cLength(), equalTo(3));
+ assertThat(decoder.cLength(), equalTo(3));
+ assertThat(decoder.cLength(), equalTo(3));
+ }
+
+ @Test
+ void disallowsReDecodingEarlierVariableLengthField()
+ {
+ final MultipleVarLengthDecoder decoder = decodeUntilVarLengthFields();
+ assertThat(decoder.b(), equalTo("abc"));
+ assertThat(decoder.c(), equalTo("def"));
+ final Exception exception =
+ assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, decoder::b);
+ assertThat(exception.getMessage(), containsString("Cannot access field \"b\" in state: V0_C_DONE"));
+ }
+
+ @Test
+ void disallowsReDecodingLatestVariableLengthField()
+ {
+ final MultipleVarLengthDecoder decoder = decodeUntilVarLengthFields();
+ assertThat(decoder.b(), equalTo("abc"));
+ assertThat(decoder.c(), equalTo("def"));
+ final Exception exception =
+ assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, decoder::c);
+ assertThat(exception.getMessage(), containsString("Cannot access field \"c\" in state: V0_C_DONE"));
+ }
+
+ private MultipleVarLengthDecoder decodeUntilVarLengthFields()
+ {
+ final MultipleVarLengthEncoder encoder = new MultipleVarLengthEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(42);
+ encoder.b("abc");
+ encoder.c("def");
+ encoder.checkEncodingIsComplete();
+
+ final MultipleVarLengthDecoder decoder = new MultipleVarLengthDecoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(42));
+ return decoder;
+ }
+
+ @Test
+ void allowsEncodingAndDecodingGroupAndVariableLengthFieldsInSchemaDefinedOrder()
+ {
+ final GroupAndVarLengthEncoder encoder = new GroupAndVarLengthEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(42);
+ encoder.bCount(2)
+ .next()
+ .c(1)
+ .next()
+ .c(2);
+ encoder.d("abc");
+ encoder.checkEncodingIsComplete();
+
+ final GroupAndVarLengthDecoder decoder = new GroupAndVarLengthDecoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(42));
+ final GroupAndVarLengthDecoder.BDecoder bs = decoder.b();
+ assertThat(bs.count(), equalTo(2));
+ assertThat(bs.next().c(), equalTo(1));
+ assertThat(bs.next().c(), equalTo(2));
+ assertThat(decoder.d(), equalTo("abc"));
+ assertThat(decoder.toString(), containsString("a=42|b=[(c=1),(c=2)]|d='abc'"));
+ }
+
+ @Test
+ void allowsEncoderToResetZeroGroupLengthToZero()
+ {
+ final GroupAndVarLengthEncoder encoder = new GroupAndVarLengthEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(42);
+ encoder.bCount(0).resetCountToIndex();
+ encoder.d("abc");
+ encoder.checkEncodingIsComplete();
+
+ final GroupAndVarLengthDecoder decoder = new GroupAndVarLengthDecoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(42));
+ final GroupAndVarLengthDecoder.BDecoder bs = decoder.b();
+ assertThat(bs.count(), equalTo(0));
+ assertThat(decoder.d(), equalTo("abc"));
+ assertThat(decoder.toString(), containsString("a=42|b=[]|d='abc'"));
+ }
+
+ @Test
+ void allowsEncoderToResetNonZeroGroupLengthToZeroBeforeCallingNext()
+ {
+ final GroupAndVarLengthEncoder encoder = new GroupAndVarLengthEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(42);
+ encoder.bCount(2).resetCountToIndex();
+ encoder.d("abc");
+ encoder.checkEncodingIsComplete();
+
+ final GroupAndVarLengthDecoder decoder = new GroupAndVarLengthDecoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(42));
+ final GroupAndVarLengthDecoder.BDecoder bs = decoder.b();
+ assertThat(bs.count(), equalTo(0));
+ assertThat(decoder.d(), equalTo("abc"));
+ assertThat(decoder.toString(), containsString("a=42|b=[]|d='abc'"));
+ }
+
+ @Test
+ void allowsEncoderToResetNonZeroGroupLengthToNonZero()
+ {
+ final GroupAndVarLengthEncoder encoder = new GroupAndVarLengthEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(42);
+ encoder.bCount(2).next().c(43).resetCountToIndex();
+ encoder.d("abc");
+ encoder.checkEncodingIsComplete();
+
+ final GroupAndVarLengthDecoder decoder = new GroupAndVarLengthDecoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(42));
+ final GroupAndVarLengthDecoder.BDecoder bs = decoder.b();
+ assertThat(bs.count(), equalTo(1));
+ assertThat(bs.next().c(), equalTo(43));
+ assertThat(decoder.d(), equalTo("abc"));
+ assertThat(decoder.toString(), containsString("a=42|b=[(c=43)]|d='abc'"));
+ }
+
+ @Test
+ void disallowsEncoderToResetGroupLengthMidGroupElement()
+ {
+ final NestedGroupsEncoder encoder = new NestedGroupsEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(42);
+ final NestedGroupsEncoder.BEncoder bEncoder = encoder.bCount(2).next().c(43);
+ final IllegalStateException exception = assertThrows(IllegalStateException.class, bEncoder::resetCountToIndex);
+ assertThat(exception.getMessage(),
+ containsString("Cannot reset count of repeating group \"b\" in state: V0_B_N_BLOCK"));
+ }
+
+ @Test
+ void allowsDecodingGroupAndVariableLengthFieldsAfterRewind()
+ {
+ final GroupAndVarLengthEncoder encoder = new GroupAndVarLengthEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(42);
+ encoder.bCount(2)
+ .next()
+ .c(1)
+ .next()
+ .c(2);
+ encoder.d("abc");
+ encoder.checkEncodingIsComplete();
+
+ final GroupAndVarLengthDecoder decoder = new GroupAndVarLengthDecoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(42));
+ final GroupAndVarLengthDecoder.BDecoder bs = decoder.b();
+ assertThat(bs.count(), equalTo(2));
+ assertThat(bs.next().c(), equalTo(1));
+ assertThat(bs.next().c(), equalTo(2));
+ assertThat(decoder.d(), equalTo("abc"));
+ assertThat(decoder.toString(), containsString("a=42|b=[(c=1),(c=2)]|d='abc'"));
+
+ decoder.sbeRewind();
+
+ assertThat(decoder.a(), equalTo(42));
+ final GroupAndVarLengthDecoder.BDecoder bs2 = decoder.b();
+ assertThat(bs2.count(), equalTo(2));
+ assertThat(bs2.next().c(), equalTo(1));
+ assertThat(bs2.next().c(), equalTo(2));
+ assertThat(decoder.d(), equalTo("abc"));
+ assertThat(decoder.toString(), containsString("a=42|b=[(c=1),(c=2)]|d='abc'"));
+ }
+
+ @Test
+ void allowsDecodingToSkipMessage()
+ {
+ final GroupAndVarLengthEncoder encoder = new GroupAndVarLengthEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(42);
+ encoder.bCount(2)
+ .next()
+ .c(1)
+ .next()
+ .c(2);
+ encoder.d("abc");
+ encoder.checkEncodingIsComplete();
+
+ final int nextEncodeOffset = encoder.limit();
+ encoder.wrapAndApplyHeader(buffer, nextEncodeOffset, messageHeaderEncoder);
+ encoder.a(43);
+ encoder.bCount(2)
+ .next()
+ .c(3)
+ .next()
+ .c(4);
+ encoder.d("def");
+ encoder.checkEncodingIsComplete();
+
+ final GroupAndVarLengthDecoder decoder = new GroupAndVarLengthDecoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+
+ decoder.sbeSkip();
+ final int nextDecodeOffset = decoder.limit();
+ assertThat(nextDecodeOffset, equalTo(nextEncodeOffset));
+
+ decoder.wrapAndApplyHeader(buffer, nextDecodeOffset, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(43));
+ final GroupAndVarLengthDecoder.BDecoder bs = decoder.b();
+ assertThat(bs.count(), equalTo(2));
+ assertThat(bs.next().c(), equalTo(3));
+ assertThat(bs.next().c(), equalTo(4));
+ assertThat(decoder.d(), equalTo("def"));
+ }
+
+ @Test
+ void allowsDecodingToDetermineMessageLengthBeforeReadingFields()
+ {
+ final GroupAndVarLengthEncoder encoder = new GroupAndVarLengthEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(43);
+ encoder.bCount(2)
+ .next()
+ .c(3)
+ .next()
+ .c(4);
+ encoder.d("def");
+ encoder.checkEncodingIsComplete();
+
+ final GroupAndVarLengthDecoder decoder = new GroupAndVarLengthDecoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+
+ assertThat(decoder.sbeDecodedLength(), equalTo(18));
+ assertThat(decoder.a(), equalTo(43));
+ final GroupAndVarLengthDecoder.BDecoder bs = decoder.b();
+ assertThat(bs.count(), equalTo(2));
+ assertThat(bs.next().c(), equalTo(3));
+ assertThat(bs.next().c(), equalTo(4));
+ assertThat(decoder.d(), equalTo("def"));
+ }
+
+ @Test
+ void allowsEncodingAndDecodingEmptyGroupAndVariableLengthFieldsInSchemaDefinedOrder()
+ {
+ final GroupAndVarLengthEncoder encoder = new GroupAndVarLengthEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(42);
+ encoder.bCount(0);
+ encoder.d("abc");
+ encoder.checkEncodingIsComplete();
+
+ final GroupAndVarLengthDecoder decoder = new GroupAndVarLengthDecoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(42));
+ final GroupAndVarLengthDecoder.BDecoder bs = decoder.b();
+ assertThat(bs.count(), equalTo(0));
+ assertThat(decoder.d(), equalTo("abc"));
+ assertThat(decoder.toString(), containsString("a=42|b=[]|d='abc'"));
+ }
+
+ @Test
+ @Disabled("Our access checks are too strict to allow the behaviour in this test.")
+ void allowsReEncodingPrimitiveFieldInGroupElementAfterTopLevelVariableLengthField()
+ {
+ final GroupAndVarLengthEncoder encoder = new GroupAndVarLengthEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(42);
+ final GroupAndVarLengthEncoder.BEncoder bEncoder = encoder.bCount(2);
+ bEncoder
+ .next()
+ .c(1)
+ .next()
+ .c(2);
+ encoder.d("abc");
+ bEncoder.c(3);
+ encoder.checkEncodingIsComplete();
+
+ final GroupAndVarLengthDecoder decoder = new GroupAndVarLengthDecoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(42));
+ final GroupAndVarLengthDecoder.BDecoder bs = decoder.b();
+ assertThat(bs.count(), equalTo(2));
+ assertThat(bs.next().c(), equalTo(1));
+ assertThat(bs.next().c(), equalTo(3));
+ assertThat(decoder.d(), equalTo("abc"));
+ }
+
+ @Test
+ @Disabled("Our access checks are too strict to allow the behaviour in this test.")
+ void allowsReWrappingGroupDecoderAfterAccessingGroupCount()
+ {
+ final GroupAndVarLengthEncoder encoder = new GroupAndVarLengthEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(42);
+ final GroupAndVarLengthEncoder.BEncoder bEncoder = encoder.bCount(2);
+ bEncoder
+ .next()
+ .c(1)
+ .next()
+ .c(2);
+ encoder.d("abc");
+ encoder.checkEncodingIsComplete();
+
+ final GroupAndVarLengthDecoder decoder = new GroupAndVarLengthDecoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(42));
+ assertThat(decoder.b().count(), equalTo(2));
+ final GroupAndVarLengthDecoder.BDecoder b = decoder.b();
+ assertThat(b.next().c(), equalTo(1));
+ assertThat(b.next().c(), equalTo(2));
+ assertThat(decoder.d(), equalTo("abc"));
+ }
+
+ @Test
+ void disallowsEncodingGroupElementBeforeCallingNext()
+ {
+ final GroupAndVarLengthEncoder encoder = new GroupAndVarLengthEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(42);
+ final GroupAndVarLengthEncoder.BEncoder bEncoder = encoder.bCount(1);
+ final Exception exception =
+ assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> bEncoder.c(1));
+ assertThat(exception.getMessage(), containsString("Cannot access field \"b.c\" in state: V0_B_N"));
+ }
+
+ @Test
+ void disallowsDecodingGroupElementBeforeCallingNext()
+ {
+ final GroupAndVarLengthEncoder encoder = new GroupAndVarLengthEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(42);
+ encoder.bCount(2)
+ .next()
+ .c(1)
+ .next()
+ .c(2);
+ encoder.d("abc");
+ encoder.checkEncodingIsComplete();
+
+ final GroupAndVarLengthDecoder decoder = new GroupAndVarLengthDecoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(42));
+ final GroupAndVarLengthDecoder.BDecoder bs = decoder.b();
+ assertThat(bs.count(), equalTo(2));
+ final Exception exception =
+ assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, bs::c);
+ assertThat(exception.getMessage(), containsString("Cannot access field \"b.c\" in state: V0_B_N"));
+ }
+
+ @Test
+ void disallowsSkippingEncodingOfGroup()
+ {
+ final GroupAndVarLengthEncoder encoder = new GroupAndVarLengthEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(42);
+ final Exception exception =
+ assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> encoder.d("abc"));
+ assertThat(exception.getMessage(), containsString("Cannot access field \"d\" in state: V0_BLOCK"));
+ }
+
+ @Test
+ void disallowsReEncodingVariableLengthFieldAfterGroup()
+ {
+ final GroupAndVarLengthEncoder encoder = new GroupAndVarLengthEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(42);
+ encoder.bCount(2)
+ .next()
+ .c(1)
+ .next()
+ .c(2);
+ encoder.d("abc");
+ final Exception exception =
+ assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> encoder.d("def"));
+ assertThat(exception.getMessage(), containsString("Cannot access field \"d\" in state: V0_D_DONE"));
+ }
+
+ @Test
+ void disallowsReEncodingGroupCount()
+ {
+ final GroupAndVarLengthEncoder encoder = new GroupAndVarLengthEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(42);
+ encoder.bCount(2)
+ .next()
+ .c(1)
+ .next()
+ .c(2);
+ encoder.d("abc");
+ final Exception exception =
+ assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> encoder.bCount(1));
+ assertThat(exception.getMessage(),
+ containsString("Cannot encode count of repeating group \"b\" in state: V0_D_DONE"));
+ }
+
+ @Test
+ @Disabled("Our access checks are too strict to allow the behaviour in this test.")
+ void allowsReEncodingGroupElementBlockFieldAfterTopLevelVariableLengthField()
+ {
+ final GroupAndVarLengthEncoder encoder = new GroupAndVarLengthEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(42);
+ final GroupAndVarLengthEncoder.BEncoder b = encoder.bCount(2)
+ .next()
+ .c(1)
+ .next()
+ .c(2);
+ encoder.d("abc");
+ b.c(3);
+ encoder.checkEncodingIsComplete();
+
+ final GroupAndVarLengthDecoder decoder = new GroupAndVarLengthDecoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(42));
+ final GroupAndVarLengthDecoder.BDecoder bs = decoder.b();
+ assertThat(bs.count(), equalTo(2));
+ assertThat(bs.next().c(), equalTo(1));
+ assertThat(bs.next().c(), equalTo(3));
+ assertThat(decoder.d(), equalTo("abc"));
+ }
+
+ @Test
+ void disallowsMissedDecodingOfGroupBeforeVariableLengthField()
+ {
+ final GroupAndVarLengthEncoder encoder = new GroupAndVarLengthEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(42);
+ encoder.bCount(2)
+ .next()
+ .c(1)
+ .next()
+ .c(2);
+ encoder.d("abc");
+ encoder.checkEncodingIsComplete();
+
+ final GroupAndVarLengthDecoder decoder = new GroupAndVarLengthDecoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(42));
+ final Exception exception =
+ assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, decoder::d);
+ assertThat(exception.getMessage(), containsString("Cannot access field \"d\" in state: V0_BLOCK"));
+ }
+
+ @Test
+ void disallowsReDecodingVariableLengthFieldAfterGroup()
+ {
+ final GroupAndVarLengthEncoder encoder = new GroupAndVarLengthEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(42);
+ encoder.bCount(2)
+ .next()
+ .c(1)
+ .next()
+ .c(2);
+ encoder.d("abc");
+ encoder.checkEncodingIsComplete();
+
+ final GroupAndVarLengthDecoder decoder = new GroupAndVarLengthDecoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(42));
+ final GroupAndVarLengthDecoder.BDecoder bs = decoder.b();
+ assertThat(bs.count(), equalTo(2));
+ assertThat(bs.next().c(), equalTo(1));
+ assertThat(bs.next().c(), equalTo(2));
+ assertThat(decoder.d(), equalTo("abc"));
+ final Exception exception =
+ assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, decoder::d);
+ assertThat(exception.getMessage(), containsString("Cannot access field \"d\" in state: V0_D_DONE"));
+ }
+
+ @Test
+ void disallowsReDecodingGroupAfterVariableLengthField()
+ {
+ final GroupAndVarLengthEncoder encoder = new GroupAndVarLengthEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(42);
+ encoder.bCount(2)
+ .next()
+ .c(1)
+ .next()
+ .c(2);
+ encoder.d("abc");
+ encoder.checkEncodingIsComplete();
+
+ final GroupAndVarLengthDecoder decoder = new GroupAndVarLengthDecoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(42));
+ final GroupAndVarLengthDecoder.BDecoder bs = decoder.b();
+ assertThat(bs.count(), equalTo(2));
+ assertThat(bs.next().c(), equalTo(1));
+ assertThat(bs.next().c(), equalTo(2));
+ assertThat(decoder.d(), equalTo("abc"));
+ final Exception exception =
+ assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, decoder::b);
+ assertThat(exception.getMessage(),
+ containsString("Cannot decode count of repeating group \"b\" in state: V0_D_DONE"));
+ }
+
+ @Test
+ void allowsEncodingAndDecodingVariableLengthFieldInsideGroupInSchemaDefinedOrder()
+ {
+ final VarLengthInsideGroupEncoder encoder = new VarLengthInsideGroupEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(42);
+ encoder.bCount(2)
+ .next()
+ .c(1)
+ .d("abc")
+ .next()
+ .c(2)
+ .d("def");
+ encoder.e("ghi");
+ encoder.checkEncodingIsComplete();
+
+ final VarLengthInsideGroupDecoder decoder = new VarLengthInsideGroupDecoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(42));
+ final VarLengthInsideGroupDecoder.BDecoder bs = decoder.b();
+ assertThat(bs.count(), equalTo(2));
+ assertThat(bs.next().c(), equalTo(1));
+ assertThat(bs.d(), equalTo("abc"));
+ assertThat(bs.next().c(), equalTo(2));
+ assertThat(bs.d(), equalTo("def"));
+ assertThat(decoder.e(), equalTo("ghi"));
+ assertThat(decoder.toString(), containsString("a=42|b=[(c=1|d='abc'),(c=2|d='def')]|e='ghi'"));
+ }
+
+ @Test
+ @Disabled("Our access checks are too strict to allow the behaviour in this test.")
+ void allowsReEncodingGroupElementPrimitiveFieldAfterElementVariableLengthField()
+ {
+ final VarLengthInsideGroupEncoder encoder = new VarLengthInsideGroupEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(42);
+ final VarLengthInsideGroupEncoder.BEncoder bEncoder = encoder.bCount(1);
+ bEncoder
+ .next()
+ .c(1)
+ .d("abc");
+ bEncoder.c(2);
+ encoder.e("ghi");
+ encoder.checkEncodingIsComplete();
+
+ final VarLengthInsideGroupDecoder decoder = new VarLengthInsideGroupDecoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(42));
+ final VarLengthInsideGroupDecoder.BDecoder bs = decoder.b();
+ assertThat(bs.count(), equalTo(1));
+ assertThat(bs.next().c(), equalTo(2));
+ assertThat(bs.d(), equalTo("abc"));
+ assertThat(decoder.e(), equalTo("ghi"));
+ }
+
+ @Test
+ void disallowsMissedGroupElementVariableLengthFieldToEncodeAtTopLevel()
+ {
+ final VarLengthInsideGroupEncoder encoder = new VarLengthInsideGroupEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(42);
+ encoder.bCount(1).next().c(1);
+ final Exception exception =
+ assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> encoder.e("abc"));
+ assertThat(exception.getMessage(), containsString("Cannot access field \"e\" in state: V0_B_1_BLOCK"));
+ }
+
+ @Test
+ void disallowsMissedGroupElementVariableLengthFieldToEncodeNextElement()
+ {
+ final VarLengthInsideGroupEncoder encoder = new VarLengthInsideGroupEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(42);
+ final VarLengthInsideGroupEncoder.BEncoder b = encoder.bCount(2)
+ .next();
+ final Exception exception =
+ assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, b::next);
+ assertThat(exception.getMessage(),
+ containsString("Cannot access next element in repeating group \"b\" in state: V0_B_N_BLOCK"));
+ }
+
+ @Test
+ void disallowsMissedGroupElementEncoding()
+ {
+ final VarLengthInsideGroupEncoder encoder = new VarLengthInsideGroupEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(42);
+ encoder.bCount(2)
+ .next()
+ .c(1)
+ .d("abc");
+ final Exception exception =
+ assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> encoder.e("abc"));
+ assertThat(exception.getMessage(), containsString("Cannot access field \"e\" in state: V0_B_N_D_DONE"));
+ }
+
+ @Test
+ void disallowsReEncodingGroupElementVariableLengthField()
+ {
+ final VarLengthInsideGroupEncoder encoder = new VarLengthInsideGroupEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(42);
+ final VarLengthInsideGroupEncoder.BEncoder b = encoder.bCount(1)
+ .next()
+ .c(1)
+ .d("abc");
+ encoder.e("def");
+ final Exception exception =
+ assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> b.d("ghi"));
+ assertThat(exception.getMessage(), containsString("Cannot access field \"b.d\" in state: V0_E_DONE"));
+ }
+
+ @Test
+ void disallowsReDecodingGroupElementVariableLengthField()
+ {
+ final VarLengthInsideGroupEncoder encoder = new VarLengthInsideGroupEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(42);
+ encoder.bCount(2)
+ .next()
+ .c(1)
+ .d("abc")
+ .next()
+ .c(2)
+ .d("def");
+ encoder.e("ghi");
+ encoder.checkEncodingIsComplete();
+
+ final VarLengthInsideGroupDecoder decoder = new VarLengthInsideGroupDecoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(42));
+ final VarLengthInsideGroupDecoder.BDecoder bs = decoder.b();
+ assertThat(bs.count(), equalTo(2));
+ assertThat(bs.next().c(), equalTo(1));
+ assertThat(bs.d(), equalTo("abc"));
+ final Exception exception =
+ assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, bs::d);
+ assertThat(exception.getMessage(), containsString("Cannot access field \"b.d\" in state: V0_B_N_D_DONE"));
+ }
+
+ @Test
+ void disallowsMissedDecodingOfGroupElementVariableLengthFieldToNextElement()
+ {
+ final VarLengthInsideGroupEncoder encoder = new VarLengthInsideGroupEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(42);
+ encoder.bCount(2)
+ .next()
+ .c(1)
+ .d("abc")
+ .next()
+ .c(2)
+ .d("def");
+ encoder.e("ghi");
+ encoder.checkEncodingIsComplete();
+
+ final VarLengthInsideGroupDecoder decoder = new VarLengthInsideGroupDecoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(42));
+ final VarLengthInsideGroupDecoder.BDecoder bs = decoder.b();
+ assertThat(bs.count(), equalTo(2));
+ assertThat(bs.next().c(), equalTo(1));
+ final Exception exception =
+ assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, bs::next);
+ assertThat(exception.getMessage(),
+ containsString("Cannot access next element in repeating group \"b\" in state: V0_B_N_BLOCK"));
+ }
+
+ @Test
+ void disallowsMissedDecodingOfGroupElementVariableLengthFieldToTopLevel()
+ {
+ final VarLengthInsideGroupEncoder encoder = new VarLengthInsideGroupEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(42);
+ encoder.bCount(1)
+ .next()
+ .c(1)
+ .d("abc");
+ encoder.e("ghi");
+ encoder.checkEncodingIsComplete();
+
+ final VarLengthInsideGroupDecoder decoder = new VarLengthInsideGroupDecoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(42));
+ final VarLengthInsideGroupDecoder.BDecoder bs = decoder.b();
+ assertThat(bs.count(), equalTo(1));
+ assertThat(bs.next().c(), equalTo(1));
+ final Exception exception =
+ assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, decoder::e);
+ assertThat(exception.getMessage(), containsString("Cannot access field \"e\" in state: V0_B_1_BLOCK"));
+ }
+
+ @Test
+ void disallowsMissedDecodingOfGroupElement()
+ {
+ final VarLengthInsideGroupEncoder encoder = new VarLengthInsideGroupEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(42);
+ encoder.bCount(2)
+ .next()
+ .c(1)
+ .d("abc")
+ .next()
+ .c(2)
+ .d("def");
+ encoder.e("ghi");
+ encoder.checkEncodingIsComplete();
+
+ final VarLengthInsideGroupDecoder decoder = new VarLengthInsideGroupDecoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(42));
+ final VarLengthInsideGroupDecoder.BDecoder bs = decoder.b();
+ assertThat(bs.count(), equalTo(2));
+ assertThat(bs.next().c(), equalTo(1));
+ final Exception exception =
+ assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, decoder::e);
+ assertThat(exception.getMessage(), containsString("Cannot access field \"e\" in state: V0_B_N_BLOCK"));
+ }
+
+ @Test
+ void allowsEncodingNestedGroupsInSchemaDefinedOrder()
+ {
+ final NestedGroupsEncoder encoder = new NestedGroupsEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(42);
+ final NestedGroupsEncoder.BEncoder b = encoder.bCount(2)
+ .next();
+ b.c(1);
+ b.dCount(2)
+ .next()
+ .e(2)
+ .next()
+ .e(3);
+ b.fCount(1)
+ .next()
+ .g(4);
+ b.next();
+ b.c(5);
+ b.dCount(1)
+ .next()
+ .e(6);
+ b.fCount(1)
+ .next()
+ .g(7);
+ encoder.hCount(1)
+ .next()
+ .i(8);
+ encoder.checkEncodingIsComplete();
+
+ final NestedGroupsDecoder decoder = new NestedGroupsDecoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(42));
+ final NestedGroupsDecoder.BDecoder bs = decoder.b();
+ assertThat(bs.count(), equalTo(2));
+ final NestedGroupsDecoder.BDecoder b0 = bs.next();
+ assertThat(b0.c(), equalTo(1));
+ final NestedGroupsDecoder.BDecoder.DDecoder b0ds = b0.d();
+ assertThat(b0ds.count(), equalTo(2));
+ assertThat(b0ds.next().e(), equalTo(2));
+ assertThat(b0ds.next().e(), equalTo(3));
+ final NestedGroupsDecoder.BDecoder.FDecoder b0fs = b0.f();
+ assertThat(b0fs.count(), equalTo(1));
+ assertThat(b0fs.next().g(), equalTo(4));
+ final NestedGroupsDecoder.BDecoder b1 = bs.next();
+ assertThat(b1.c(), equalTo(5));
+ final NestedGroupsDecoder.BDecoder.DDecoder b1ds = b1.d();
+ assertThat(b1ds.count(), equalTo(1));
+ assertThat(b1ds.next().e(), equalTo(6));
+ final NestedGroupsDecoder.BDecoder.FDecoder b1fs = b1.f();
+ assertThat(b1fs.count(), equalTo(1));
+ assertThat(b1fs.next().g(), equalTo(7));
+ final NestedGroupsDecoder.HDecoder hs = decoder.h();
+ assertThat(hs.count(), equalTo(1));
+ assertThat(hs.next().i(), equalTo(8));
+ assertThat(decoder.toString(),
+ containsString("a=42|b=[(c=1|d=[(e=2),(e=3)]|f=[(g=4)]),(c=5|d=[(e=6)]|f=[(g=7)])]|h=[(i=8)]"));
+ }
+
+ @Test
+ void allowsEncodingEmptyNestedGroupsInSchemaDefinedOrder()
+ {
+ final NestedGroupsEncoder encoder = new NestedGroupsEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(42);
+ encoder.bCount(0);
+ encoder.hCount(0);
+ encoder.checkEncodingIsComplete();
+
+ final NestedGroupsDecoder decoder = new NestedGroupsDecoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(42));
+ final NestedGroupsDecoder.BDecoder bs = decoder.b();
+ assertThat(bs.count(), equalTo(0));
+ final NestedGroupsDecoder.HDecoder hs = decoder.h();
+ assertThat(hs.count(), equalTo(0));
+ assertThat(decoder.toString(), containsString("a=42|b=[]|h=[]"));
+ }
+
+ @Test
+ void disallowsMissedEncodingOfNestedGroup()
+ {
+ final NestedGroupsEncoder encoder = new NestedGroupsEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(42);
+ final NestedGroupsEncoder.BEncoder b = encoder.bCount(1)
+ .next()
+ .c(1);
+ final Exception exception =
+ assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> b.fCount(1));
+ assertThat(exception.getMessage(),
+ containsString("Cannot encode count of repeating group \"b.f\" in state: V0_B_1_BLOCK"));
+ }
+
+ @Test
+ void allowsEncodingAndDecodingCompositeInsideGroupInSchemaDefinedOrder()
+ {
+ final CompositeInsideGroupEncoder encoder = new CompositeInsideGroupEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a().x(1).y(2);
+ encoder.bCount(1)
+ .next()
+ .c().x(3).y(4);
+ encoder.checkEncodingIsComplete();
+
+ final CompositeInsideGroupDecoder decoder = new CompositeInsideGroupDecoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ final PointDecoder a = decoder.a();
+ assertThat(a.x(), equalTo(1));
+ assertThat(a.y(), equalTo(2));
+ final CompositeInsideGroupDecoder.BDecoder b = decoder.b();
+ assertThat(b.count(), equalTo(1));
+ final PointDecoder c = b.next().c();
+ assertThat(c.x(), equalTo(3));
+ assertThat(c.y(), equalTo(4));
+ assertThat(decoder.toString(), containsString("a=(x=1|y=2)|b=[(c=(x=3|y=4))]"));
+ }
+
+ @Test
+ void disallowsEncodingCompositeInsideGroupBeforeCallingNext()
+ {
+ final CompositeInsideGroupEncoder encoder = new CompositeInsideGroupEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a().x(1).y(2);
+ final CompositeInsideGroupEncoder.BEncoder bEncoder = encoder.bCount(1);
+ final Exception exception =
+ assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, bEncoder::c);
+ assertThat(exception.getMessage(), containsString("Cannot access field \"b.c\" in state: V0_B_N"));
+ }
+
+ @Test
+ void disallowsDecodingCompositeInsideGroupBeforeCallingNext()
+ {
+ final CompositeInsideGroupEncoder encoder = new CompositeInsideGroupEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a().x(1).y(2);
+ encoder.bCount(1)
+ .next()
+ .c().x(3).y(4);
+
+ final CompositeInsideGroupDecoder decoder = new CompositeInsideGroupDecoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ final PointDecoder a = decoder.a();
+ assertThat(a.x(), equalTo(1));
+ assertThat(a.y(), equalTo(2));
+ final CompositeInsideGroupDecoder.BDecoder b = decoder.b();
+ assertThat(b.count(), equalTo(1));
+ final Exception exception =
+ assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, b::c);
+ assertThat(exception.getMessage(), containsString("Cannot access field \"b.c\" in state: V0_B_N"));
+ }
+
+ @Test
+ void allowsReEncodingTopLevelCompositeViaReWrap()
+ {
+ final CompositeInsideGroupEncoder encoder = new CompositeInsideGroupEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a().x(1).y(2);
+ encoder.bCount(1)
+ .next()
+ .c().x(3).y(4);
+ encoder.a().x(5).y(6);
+ encoder.checkEncodingIsComplete();
+
+ final CompositeInsideGroupDecoder decoder = new CompositeInsideGroupDecoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ final PointDecoder a = decoder.a();
+ assertThat(a.x(), equalTo(5));
+ assertThat(a.y(), equalTo(6));
+ final CompositeInsideGroupDecoder.BDecoder b = decoder.b();
+ assertThat(b.count(), equalTo(1));
+ final PointDecoder c = b.next().c();
+ assertThat(c.x(), equalTo(3));
+ assertThat(c.y(), equalTo(4));
+ }
+
+ @Test
+ void allowsReEncodingTopLevelCompositeViaEncoderReference()
+ {
+ final CompositeInsideGroupEncoder encoder = new CompositeInsideGroupEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ final PointEncoder aEncoder = encoder.a();
+ aEncoder.x(1).y(2);
+ encoder.bCount(1)
+ .next()
+ .c().x(3).y(4);
+ aEncoder.x(5).y(6);
+ encoder.checkEncodingIsComplete();
+
+ final CompositeInsideGroupDecoder decoder = new CompositeInsideGroupDecoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ final PointDecoder a = decoder.a();
+ assertThat(a.x(), equalTo(5));
+ assertThat(a.y(), equalTo(6));
+ final CompositeInsideGroupDecoder.BDecoder b = decoder.b();
+ assertThat(b.count(), equalTo(1));
+ final PointDecoder c = b.next().c();
+ assertThat(c.x(), equalTo(3));
+ assertThat(c.y(), equalTo(4));
+ }
+
+ @Test
+ void allowsReEncodingGroupElementCompositeViaReWrap()
+ {
+ final CompositeInsideGroupEncoder encoder = new CompositeInsideGroupEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a().x(1).y(2);
+ final CompositeInsideGroupEncoder.BEncoder bEncoder = encoder.bCount(1).next();
+ bEncoder.c().x(3).y(4);
+ bEncoder.c().x(5).y(6);
+ encoder.checkEncodingIsComplete();
+
+ final CompositeInsideGroupDecoder decoder = new CompositeInsideGroupDecoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ final PointDecoder a = decoder.a();
+ assertThat(a.x(), equalTo(1));
+ assertThat(a.y(), equalTo(2));
+ final CompositeInsideGroupDecoder.BDecoder b = decoder.b();
+ assertThat(b.count(), equalTo(1));
+ final PointDecoder c = b.next().c();
+ assertThat(c.x(), equalTo(5));
+ assertThat(c.y(), equalTo(6));
+ }
+
+ @Test
+ void allowsReEncodingGroupElementCompositeViaEncoderReference()
+ {
+ final CompositeInsideGroupEncoder encoder = new CompositeInsideGroupEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a().x(1).y(2);
+ final CompositeInsideGroupEncoder.BEncoder bEncoder = encoder.bCount(1).next();
+ final PointEncoder cEncoder = bEncoder.c();
+ cEncoder.x(3).y(4);
+ cEncoder.x(5).y(6);
+ encoder.checkEncodingIsComplete();
+
+ final CompositeInsideGroupDecoder decoder = new CompositeInsideGroupDecoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ final PointDecoder a = decoder.a();
+ assertThat(a.x(), equalTo(1));
+ assertThat(a.y(), equalTo(2));
+ final CompositeInsideGroupDecoder.BDecoder b = decoder.b();
+ assertThat(b.count(), equalTo(1));
+ final PointDecoder c = b.next().c();
+ assertThat(c.x(), equalTo(5));
+ assertThat(c.y(), equalTo(6));
+ }
+
+ @Test
+ void allowsReDecodingTopLevelCompositeViaReWrap()
+ {
+ final CompositeInsideGroupEncoder encoder = new CompositeInsideGroupEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a().x(1).y(2);
+ encoder.bCount(1)
+ .next()
+ .c().x(3).y(4);
+ encoder.checkEncodingIsComplete();
+
+ final CompositeInsideGroupDecoder decoder = new CompositeInsideGroupDecoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ final PointDecoder a1 = decoder.a();
+ assertThat(a1.x(), equalTo(1));
+ assertThat(a1.y(), equalTo(2));
+ final CompositeInsideGroupDecoder.BDecoder b = decoder.b();
+ assertThat(b.count(), equalTo(1));
+ final PointDecoder c = b.next().c();
+ assertThat(c.x(), equalTo(3));
+ assertThat(c.y(), equalTo(4));
+ final PointDecoder a2 = decoder.a();
+ assertThat(a2.x(), equalTo(1));
+ assertThat(a2.y(), equalTo(2));
+ }
+
+ @Test
+ void allowsReDecodingTopLevelCompositeViaEncoderReference()
+ {
+ final CompositeInsideGroupEncoder encoder = new CompositeInsideGroupEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a().x(1).y(2);
+ encoder.bCount(1)
+ .next()
+ .c().x(3).y(4);
+ encoder.checkEncodingIsComplete();
+
+ final CompositeInsideGroupDecoder decoder = new CompositeInsideGroupDecoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ final PointDecoder a = decoder.a();
+ assertThat(a.x(), equalTo(1));
+ assertThat(a.y(), equalTo(2));
+ final CompositeInsideGroupDecoder.BDecoder b = decoder.b();
+ assertThat(b.count(), equalTo(1));
+ final PointDecoder c = b.next().c();
+ assertThat(c.x(), equalTo(3));
+ assertThat(c.y(), equalTo(4));
+ assertThat(a.x(), equalTo(1));
+ assertThat(a.y(), equalTo(2));
+ }
+
+ @Test
+ void allowsReDecodingGroupElementCompositeViaReWrap()
+ {
+ final CompositeInsideGroupEncoder encoder = new CompositeInsideGroupEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a().x(1).y(2);
+ encoder.bCount(1)
+ .next()
+ .c().x(3).y(4);
+ encoder.checkEncodingIsComplete();
+
+ final CompositeInsideGroupDecoder decoder = new CompositeInsideGroupDecoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ final PointDecoder a = decoder.a();
+ assertThat(a.x(), equalTo(1));
+ assertThat(a.y(), equalTo(2));
+ final CompositeInsideGroupDecoder.BDecoder b = decoder.b();
+ assertThat(b.count(), equalTo(1));
+ final PointDecoder c1 = b.next().c();
+ assertThat(c1.x(), equalTo(3));
+ assertThat(c1.y(), equalTo(4));
+ final PointDecoder c2 = b.c();
+ assertThat(c2.x(), equalTo(3));
+ assertThat(c2.y(), equalTo(4));
+ }
+
+ @Test
+ void allowsReDecodingGroupElementCompositeViaEncoderReference()
+ {
+ final CompositeInsideGroupEncoder encoder = new CompositeInsideGroupEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a().x(1).y(2);
+ encoder.bCount(1)
+ .next()
+ .c().x(3).y(4);
+ encoder.checkEncodingIsComplete();
+
+ final CompositeInsideGroupDecoder decoder = new CompositeInsideGroupDecoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ final PointDecoder a = decoder.a();
+ assertThat(a.x(), equalTo(1));
+ assertThat(a.y(), equalTo(2));
+ final CompositeInsideGroupDecoder.BDecoder b = decoder.b();
+ assertThat(b.count(), equalTo(1));
+ final PointDecoder c = b.next().c();
+ assertThat(c.x(), equalTo(3));
+ assertThat(c.y(), equalTo(4));
+ assertThat(c.x(), equalTo(3));
+ assertThat(c.y(), equalTo(4));
+ }
+
+ @Test
+ void allowsNewDecoderToDecodeAddedPrimitiveField()
+ {
+ final AddPrimitiveV1Encoder encoder = new AddPrimitiveV1Encoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(1).b(2);
+ encoder.checkEncodingIsComplete();
+
+ final AddPrimitiveV1Decoder decoder = new AddPrimitiveV1Decoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(1));
+ assertThat(decoder.b(), equalTo(2));
+ }
+
+ @Test
+ void allowsNewDecoderToDecodeMissingPrimitiveFieldAsNullValue()
+ {
+ final AddPrimitiveV0Encoder encoder = new AddPrimitiveV0Encoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(1);
+ encoder.checkEncodingIsComplete();
+
+ modifyHeaderToLookLikeVersion0();
+
+ final AddPrimitiveV1Decoder decoder = new AddPrimitiveV1Decoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(1));
+ assertThat(decoder.b(), equalTo(AddPrimitiveV1Decoder.bNullValue()));
+ }
+
+ @Test
+ void allowsNewDecoderToDecodeAddedPrimitiveFieldBeforeGroup()
+ {
+ final AddPrimitiveBeforeGroupV1Encoder encoder = new AddPrimitiveBeforeGroupV1Encoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(1).d(3).bCount(1).next().c(2);
+ encoder.checkEncodingIsComplete();
+
+ final AddPrimitiveBeforeGroupV1Decoder decoder = new AddPrimitiveBeforeGroupV1Decoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(1));
+ assertThat(decoder.d(), equalTo(3));
+ final AddPrimitiveBeforeGroupV1Decoder.BDecoder b = decoder.b();
+ assertThat(b.count(), equalTo(1));
+ assertThat(b.next().c(), equalTo(2));
+ }
+
+ @Test
+ void allowsNewDecoderToDecodeMissingPrimitiveFieldBeforeGroupAsNullValue()
+ {
+ final AddPrimitiveBeforeGroupV0Encoder encoder = new AddPrimitiveBeforeGroupV0Encoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(1).bCount(1).next().c(2);
+ encoder.checkEncodingIsComplete();
+
+ modifyHeaderToLookLikeVersion0();
+
+ final AddPrimitiveBeforeGroupV1Decoder decoder = new AddPrimitiveBeforeGroupV1Decoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(1));
+ assertThat(decoder.d(), equalTo(AddPrimitiveBeforeGroupV1Decoder.dNullValue()));
+ final AddPrimitiveBeforeGroupV1Decoder.BDecoder b = decoder.b();
+ assertThat(b.count(), equalTo(1));
+ assertThat(b.next().c(), equalTo(2));
+ }
+
+ @Test
+ void allowsNewDecoderToSkipPresentButAddedPrimitiveFieldBeforeGroup()
+ {
+ final AddPrimitiveBeforeGroupV1Encoder encoder = new AddPrimitiveBeforeGroupV1Encoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(1).d(3).bCount(1).next().c(2);
+ encoder.checkEncodingIsComplete();
+
+ final AddPrimitiveBeforeGroupV1Decoder decoder = new AddPrimitiveBeforeGroupV1Decoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(1));
+ final AddPrimitiveBeforeGroupV1Decoder.BDecoder b = decoder.b();
+ assertThat(b.count(), equalTo(1));
+ assertThat(b.next().c(), equalTo(2));
+ }
+
+ @Test
+ void allowsOldDecoderToSkipAddedPrimitiveFieldBeforeGroup()
+ {
+ final AddPrimitiveBeforeGroupV1Encoder encoder = new AddPrimitiveBeforeGroupV1Encoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(1).d(3).bCount(1).next().c(2);
+ encoder.checkEncodingIsComplete();
+
+ modifyHeaderToLookLikeVersion1();
+
+ final AddPrimitiveBeforeGroupV0Decoder decoder = new AddPrimitiveBeforeGroupV0Decoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(1));
+ final AddPrimitiveBeforeGroupV0Decoder.BDecoder b = decoder.b();
+ assertThat(b.count(), equalTo(1));
+ assertThat(b.next().c(), equalTo(2));
+ }
+
+ @Test
+ void allowsNewDecoderToDecodeAddedPrimitiveFieldBeforeVarData()
+ {
+ final AddPrimitiveBeforeVarDataV1Encoder encoder = new AddPrimitiveBeforeVarDataV1Encoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(1).c(3).b("abc");
+ encoder.checkEncodingIsComplete();
+
+ final AddPrimitiveBeforeVarDataV1Decoder decoder = new AddPrimitiveBeforeVarDataV1Decoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(1));
+ assertThat(decoder.c(), equalTo(3));
+ assertThat(decoder.b(), equalTo("abc"));
+ }
+
+ @Test
+ void allowsNewDecoderToDecodeMissingPrimitiveFieldBeforeVarDataAsNullValue()
+ {
+ final AddPrimitiveBeforeVarDataV0Encoder encoder = new AddPrimitiveBeforeVarDataV0Encoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(1).b("abc");
+ encoder.checkEncodingIsComplete();
+
+ modifyHeaderToLookLikeVersion0();
+
+ final AddPrimitiveBeforeVarDataV1Decoder decoder = new AddPrimitiveBeforeVarDataV1Decoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(1));
+ assertThat(decoder.c(), equalTo(AddPrimitiveBeforeVarDataV1Decoder.cNullValue()));
+ assertThat(decoder.b(), equalTo("abc"));
+ }
+
+ @Test
+ void allowsNewDecoderToSkipPresentButAddedPrimitiveFieldBeforeVarData()
+ {
+ final AddPrimitiveBeforeVarDataV1Encoder encoder = new AddPrimitiveBeforeVarDataV1Encoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(1).c(3).b("abc");
+ encoder.checkEncodingIsComplete();
+
+ final AddPrimitiveBeforeVarDataV1Decoder decoder = new AddPrimitiveBeforeVarDataV1Decoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(1));
+ assertThat(decoder.b(), equalTo("abc"));
+ }
+
+ @Test
+ void allowsOldDecoderToSkipAddedPrimitiveFieldBeforeVarData()
+ {
+ final AddPrimitiveBeforeVarDataV1Encoder encoder = new AddPrimitiveBeforeVarDataV1Encoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(1).c(3).b("abc");
+ encoder.checkEncodingIsComplete();
+
+ modifyHeaderToLookLikeVersion1();
+
+ final AddPrimitiveBeforeVarDataV0Decoder decoder = new AddPrimitiveBeforeVarDataV0Decoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(1));
+ assertThat(decoder.b(), equalTo("abc"));
+ }
+
+ @Test
+ void allowsNewDecoderToDecodeAddedPrimitiveFieldInsideGroup()
+ {
+ final AddPrimitiveInsideGroupV1Encoder encoder = new AddPrimitiveInsideGroupV1Encoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(1).bCount(1).next().c(2).d(3);
+ encoder.checkEncodingIsComplete();
+
+ final AddPrimitiveInsideGroupV1Decoder decoder = new AddPrimitiveInsideGroupV1Decoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(1));
+ final AddPrimitiveInsideGroupV1Decoder.BDecoder b = decoder.b();
+ assertThat(b.count(), equalTo(1));
+ assertThat(b.next().c(), equalTo(2));
+ assertThat(b.d(), equalTo(3));
+ }
+
+ @Test
+ void allowsNewDecoderToDecodeMissingPrimitiveFieldInsideGroupAsNullValue()
+ {
+ final AddPrimitiveInsideGroupV0Encoder encoder = new AddPrimitiveInsideGroupV0Encoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(1).bCount(1).next().c(2);
+ encoder.checkEncodingIsComplete();
+
+ modifyHeaderToLookLikeVersion0();
+
+ final AddPrimitiveInsideGroupV1Decoder decoder = new AddPrimitiveInsideGroupV1Decoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(1));
+ final AddPrimitiveInsideGroupV1Decoder.BDecoder b = decoder.b();
+ assertThat(b.count(), equalTo(1));
+ assertThat(b.next().c(), equalTo(2));
+ assertThat(b.d(), equalTo(AddPrimitiveInsideGroupV1Decoder.BDecoder.dNullValue()));
+ }
+
+ @Test
+ void allowsNewDecoderToSkipPresentButAddedPrimitiveFieldInsideGroup()
+ {
+ final AddPrimitiveInsideGroupV1Encoder encoder = new AddPrimitiveInsideGroupV1Encoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(1).bCount(2).next().c(2).d(3).next().c(4).d(5);
+ encoder.checkEncodingIsComplete();
+
+ final AddPrimitiveInsideGroupV1Decoder decoder = new AddPrimitiveInsideGroupV1Decoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(1));
+ final AddPrimitiveInsideGroupV1Decoder.BDecoder b = decoder.b();
+ assertThat(b.count(), equalTo(2));
+ assertThat(b.next().c(), equalTo(2));
+ assertThat(b.next().c(), equalTo(4));
+ }
+
+ @Test
+ void allowsOldDecoderToSkipAddedPrimitiveFieldInsideGroup()
+ {
+ final AddPrimitiveInsideGroupV1Encoder encoder = new AddPrimitiveInsideGroupV1Encoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(1).bCount(2).next().c(2).d(3).next().c(4).d(5);
+ encoder.checkEncodingIsComplete();
+
+ modifyHeaderToLookLikeVersion1();
+
+ final AddPrimitiveInsideGroupV0Decoder decoder = new AddPrimitiveInsideGroupV0Decoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(1));
+ final AddPrimitiveInsideGroupV0Decoder.BDecoder b = decoder.b();
+ assertThat(b.count(), equalTo(2));
+ assertThat(b.next().c(), equalTo(2));
+ assertThat(b.next().c(), equalTo(4));
+ }
+
+ @Test
+ void allowsNewDecoderToDecodeAddedGroupBeforeVarData()
+ {
+ final AddGroupBeforeVarDataV1Encoder encoder = new AddGroupBeforeVarDataV1Encoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(1).cCount(1).next().d(2);
+ encoder.b("abc");
+ encoder.checkEncodingIsComplete();
+
+ final AddGroupBeforeVarDataV1Decoder decoder = new AddGroupBeforeVarDataV1Decoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(1));
+ final AddGroupBeforeVarDataV1Decoder.CDecoder c = decoder.c();
+ assertThat(c.count(), equalTo(1));
+ assertThat(c.next().d(), equalTo(2));
+ assertThat(decoder.b(), equalTo("abc"));
+ }
+
+ @Test
+ void allowsNewDecoderToDecodeMissingGroupBeforeVarDataAsNullValue()
+ {
+ final AddGroupBeforeVarDataV0Encoder encoder = new AddGroupBeforeVarDataV0Encoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(1).b("abc");
+ encoder.checkEncodingIsComplete();
+
+ modifyHeaderToLookLikeVersion0();
+
+ final AddGroupBeforeVarDataV1Decoder decoder = new AddGroupBeforeVarDataV1Decoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(1));
+ final AddGroupBeforeVarDataV1Decoder.CDecoder c = decoder.c();
+ assertThat(c.count(), equalTo(0));
+ assertThat(decoder.b(), equalTo("abc"));
+ assertThat(decoder.toString(), containsString("a=1|c=[]|b='abc'"));
+ }
+
+ @Test
+ void allowsNewDecoderToSkipMissingGroupBeforeVarData()
+ {
+ final AddGroupBeforeVarDataV0Encoder encoder = new AddGroupBeforeVarDataV0Encoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(1).b("abc");
+ encoder.checkEncodingIsComplete();
+
+ modifyHeaderToLookLikeVersion0();
+
+ final AddGroupBeforeVarDataV1Decoder decoder = new AddGroupBeforeVarDataV1Decoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(1));
+ assertThat(decoder.b(), equalTo("abc"));
+ }
+
+ @Test
+ void disallowsNewDecoderToSkipPresentButAddedGroupBeforeVarData()
+ {
+ final AddGroupBeforeVarDataV1Encoder encoder = new AddGroupBeforeVarDataV1Encoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(1).cCount(1).next().d(2);
+ encoder.b("abc");
+ encoder.checkEncodingIsComplete();
+
+ final AddGroupBeforeVarDataV1Decoder decoder = new AddGroupBeforeVarDataV1Decoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(1));
+ final Exception exception =
+ assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, decoder::b);
+ assertThat(exception.getMessage(), containsString("Cannot access field \"b\" in state: V1_BLOCK"));
+ }
+
+ @Test
+ void allowsOldDecoderToSkipAddedGroupBeforeVarData()
+ {
+ final AddGroupBeforeVarDataV1Encoder encoder = new AddGroupBeforeVarDataV1Encoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ messageHeaderEncoder.numGroups(1);
+ encoder.a(1).cCount(1).next().d(2);
+ encoder.b("abc");
+ encoder.checkEncodingIsComplete();
+
+ modifyHeaderToLookLikeVersion1();
+
+ final AddGroupBeforeVarDataV0Decoder decoder = new AddGroupBeforeVarDataV0Decoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(1));
+
+ for (int i = 0; i < messageHeaderDecoder.numGroups(); i++)
+ {
+ skipGroup(decoder);
+ }
+
+ assertThat(decoder.b(), equalTo("abc"));
+ }
+
+ private void skipGroup(final AddGroupBeforeVarDataV0Decoder decoder)
+ {
+ final GroupSizeEncodingDecoder groupSizeEncodingDecoder = new GroupSizeEncodingDecoder()
+ .wrap(buffer, decoder.limit());
+ final int bytesToSkip = groupSizeEncodingDecoder.encodedLength() +
+ groupSizeEncodingDecoder.blockLength() * groupSizeEncodingDecoder.numInGroup();
+ decoder.limit(decoder.limit() + bytesToSkip);
+ }
+
+ @Test
+ void allowsNewDecoderToDecodeAddedEnumFieldBeforeGroup()
+ {
+ final AddEnumBeforeGroupV1Encoder encoder = new AddEnumBeforeGroupV1Encoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(1).d(Direction.BUY).bCount(1).next().c(2);
+ encoder.checkEncodingIsComplete();
+
+ final AddEnumBeforeGroupV1Decoder decoder = new AddEnumBeforeGroupV1Decoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(1));
+ assertThat(decoder.d(), equalTo(Direction.BUY));
+ final AddEnumBeforeGroupV1Decoder.BDecoder b = decoder.b();
+ assertThat(b.count(), equalTo(1));
+ assertThat(b.next().c(), equalTo(2));
+ }
+
+ @Test
+ void allowsNewDecoderToDecodeMissingEnumFieldBeforeGroupAsNullValue()
+ {
+ final AddEnumBeforeGroupV0Encoder encoder = new AddEnumBeforeGroupV0Encoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(1).bCount(1).next().c(2);
+ encoder.checkEncodingIsComplete();
+
+ modifyHeaderToLookLikeVersion0();
+
+ final AddEnumBeforeGroupV1Decoder decoder = new AddEnumBeforeGroupV1Decoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(1));
+ assertThat(decoder.d(), equalTo(Direction.NULL_VAL));
+ final AddEnumBeforeGroupV1Decoder.BDecoder b = decoder.b();
+ assertThat(b.count(), equalTo(1));
+ assertThat(b.next().c(), equalTo(2));
+ }
+
+ @Test
+ void allowsNewDecoderToSkipPresentButAddedEnumFieldBeforeGroup()
+ {
+ final AddEnumBeforeGroupV1Encoder encoder = new AddEnumBeforeGroupV1Encoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(1).d(Direction.SELL).bCount(1).next().c(2);
+ encoder.checkEncodingIsComplete();
+
+ final AddEnumBeforeGroupV1Decoder decoder = new AddEnumBeforeGroupV1Decoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(1));
+ final AddEnumBeforeGroupV1Decoder.BDecoder b = decoder.b();
+ assertThat(b.count(), equalTo(1));
+ assertThat(b.next().c(), equalTo(2));
+ }
+
+ @Test
+ void allowsOldDecoderToSkipAddedEnumFieldBeforeGroup()
+ {
+ final AddEnumBeforeGroupV1Encoder encoder = new AddEnumBeforeGroupV1Encoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(1).d(Direction.BUY).bCount(1).next().c(2);
+ encoder.checkEncodingIsComplete();
+
+ modifyHeaderToLookLikeVersion1();
+
+ final AddEnumBeforeGroupV0Decoder decoder = new AddEnumBeforeGroupV0Decoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(1));
+ final AddEnumBeforeGroupV0Decoder.BDecoder b = decoder.b();
+ assertThat(b.count(), equalTo(1));
+ assertThat(b.next().c(), equalTo(2));
+ }
+
+ @Test
+ void allowsNewDecoderToDecodeAddedCompositeFieldBeforeGroup()
+ {
+ final AddCompositeBeforeGroupV1Encoder encoder = new AddCompositeBeforeGroupV1Encoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(1).d().x(-1).y(-2);
+ encoder.bCount(1).next().c(2);
+ encoder.checkEncodingIsComplete();
+
+ final AddCompositeBeforeGroupV1Decoder decoder = new AddCompositeBeforeGroupV1Decoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(1));
+ final PointDecoder d = decoder.d();
+ assertThat(d, notNullValue());
+ assertThat(d.x(), equalTo(-1));
+ assertThat(d.y(), equalTo(-2));
+ final AddCompositeBeforeGroupV1Decoder.BDecoder b = decoder.b();
+ assertThat(b.count(), equalTo(1));
+ assertThat(b.next().c(), equalTo(2));
+ }
+
+ @Test
+ void allowsNewDecoderToDecodeMissingCompositeFieldBeforeGroupAsNullValue()
+ {
+ final AddCompositeBeforeGroupV0Encoder encoder = new AddCompositeBeforeGroupV0Encoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(1).bCount(1).next().c(2);
+ encoder.checkEncodingIsComplete();
+
+ modifyHeaderToLookLikeVersion0();
+
+ final AddCompositeBeforeGroupV1Decoder decoder = new AddCompositeBeforeGroupV1Decoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(1));
+ assertThat(decoder.d(), nullValue());
+ final AddCompositeBeforeGroupV1Decoder.BDecoder b = decoder.b();
+ assertThat(b.count(), equalTo(1));
+ assertThat(b.next().c(), equalTo(2));
+ }
+
+ @Test
+ void allowsNewDecoderToSkipPresentButAddedCompositeFieldBeforeGroup()
+ {
+ final AddCompositeBeforeGroupV1Encoder encoder = new AddCompositeBeforeGroupV1Encoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(1).d().x(-1).y(-2);
+ encoder.bCount(1).next().c(2);
+ encoder.checkEncodingIsComplete();
+
+ final AddCompositeBeforeGroupV1Decoder decoder = new AddCompositeBeforeGroupV1Decoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(1));
+ final AddCompositeBeforeGroupV1Decoder.BDecoder b = decoder.b();
+ assertThat(b.count(), equalTo(1));
+ assertThat(b.next().c(), equalTo(2));
+ }
+
+ @Test
+ void allowsOldDecoderToSkipAddedCompositeFieldBeforeGroup()
+ {
+ final AddCompositeBeforeGroupV1Encoder encoder = new AddCompositeBeforeGroupV1Encoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(1).d().x(-1).y(-2);
+ encoder.bCount(1).next().c(2);
+ encoder.checkEncodingIsComplete();
+
+ modifyHeaderToLookLikeVersion1();
+
+ final AddCompositeBeforeGroupV0Decoder decoder = new AddCompositeBeforeGroupV0Decoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(1));
+ final AddCompositeBeforeGroupV0Decoder.BDecoder b = decoder.b();
+ assertThat(b.count(), equalTo(1));
+ assertThat(b.next().c(), equalTo(2));
+ }
+
+ @Test
+ void allowsNewDecoderToDecodeAddedArrayFieldBeforeGroup()
+ {
+ final AddArrayBeforeGroupV1Encoder encoder = new AddArrayBeforeGroupV1Encoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(1)
+ .putD((short)1, (short)2, (short)3, (short)4)
+ .bCount(1)
+ .next().c(2);
+ encoder.checkEncodingIsComplete();
+
+ final AddArrayBeforeGroupV1Decoder decoder = new AddArrayBeforeGroupV1Decoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(1));
+ assertThat(decoder.d(0), equalTo((short)1));
+ assertThat(decoder.d(1), equalTo((short)2));
+ assertThat(decoder.d(2), equalTo((short)3));
+ assertThat(decoder.d(3), equalTo((short)4));
+ final AddArrayBeforeGroupV1Decoder.BDecoder b = decoder.b();
+ assertThat(b.count(), equalTo(1));
+ assertThat(b.next().c(), equalTo(2));
+ }
+
+ @Test
+ void allowsNewDecoderToDecodeMissingArrayFieldBeforeGroupAsNullValue1()
+ {
+ final AddArrayBeforeGroupV0Encoder encoder = new AddArrayBeforeGroupV0Encoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(1).bCount(1).next().c(2);
+ encoder.checkEncodingIsComplete();
+
+ modifyHeaderToLookLikeVersion0();
+
+ final AddArrayBeforeGroupV1Decoder decoder = new AddArrayBeforeGroupV1Decoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(1));
+ assertThat(decoder.d(0), equalTo(AddArrayBeforeGroupV1Decoder.dNullValue()));
+ assertThat(decoder.d(1), equalTo(AddArrayBeforeGroupV1Decoder.dNullValue()));
+ assertThat(decoder.d(2), equalTo(AddArrayBeforeGroupV1Decoder.dNullValue()));
+ assertThat(decoder.d(3), equalTo(AddArrayBeforeGroupV1Decoder.dNullValue()));
+ final AddArrayBeforeGroupV1Decoder.BDecoder b = decoder.b();
+ assertThat(b.count(), equalTo(1));
+ assertThat(b.next().c(), equalTo(2));
+ }
+
+ @Test
+ void allowsNewDecoderToDecodeMissingArrayFieldBeforeGroupAsNullValue2()
+ {
+ final AddArrayBeforeGroupV0Encoder encoder = new AddArrayBeforeGroupV0Encoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(1).bCount(1).next().c(2);
+ encoder.checkEncodingIsComplete();
+
+ modifyHeaderToLookLikeVersion0();
+
+ final AddArrayBeforeGroupV1Decoder decoder = new AddArrayBeforeGroupV1Decoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(1));
+ assertThat(decoder.getD(new byte[8], 0, 8), equalTo(0));
+ final AddArrayBeforeGroupV1Decoder.BDecoder b = decoder.b();
+ assertThat(b.count(), equalTo(1));
+ assertThat(b.next().c(), equalTo(2));
+ }
+
+ @Test
+ void allowsNewDecoderToDecodeMissingArrayFieldBeforeGroupAsNullValue3()
+ {
+ final AddArrayBeforeGroupV0Encoder encoder = new AddArrayBeforeGroupV0Encoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(1).bCount(1).next().c(2);
+ encoder.checkEncodingIsComplete();
+
+ modifyHeaderToLookLikeVersion0();
+
+ final AddArrayBeforeGroupV1Decoder decoder = new AddArrayBeforeGroupV1Decoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(1));
+ assertThat(decoder.getD(new ExpandableArrayBuffer(), 0, 8), equalTo(0));
+ final AddArrayBeforeGroupV1Decoder.BDecoder b = decoder.b();
+ assertThat(b.count(), equalTo(1));
+ assertThat(b.next().c(), equalTo(2));
+ }
+
+ @Test
+ void allowsNewDecoderToDecodeMissingArrayFieldBeforeGroupAsNullValue4()
+ {
+ final AddArrayBeforeGroupV0Encoder encoder = new AddArrayBeforeGroupV0Encoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(1).bCount(1).next().c(2);
+ encoder.checkEncodingIsComplete();
+
+ modifyHeaderToLookLikeVersion0();
+
+ final AddArrayBeforeGroupV1Decoder decoder = new AddArrayBeforeGroupV1Decoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(1));
+ final UnsafeBuffer buffer = new UnsafeBuffer();
+ decoder.wrapD(buffer);
+ assertThat(buffer.capacity(), equalTo(0));
+ final AddArrayBeforeGroupV1Decoder.BDecoder b = decoder.b();
+ assertThat(b.count(), equalTo(1));
+ assertThat(b.next().c(), equalTo(2));
+ }
+
+ @Test
+ void allowsNewDecoderToSkipPresentButAddedArrayFieldBeforeGroup()
+ {
+ final AddArrayBeforeGroupV1Encoder encoder = new AddArrayBeforeGroupV1Encoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(1)
+ .putD((short)1, (short)2, (short)3, (short)4)
+ .bCount(1)
+ .next().c(2);
+ encoder.checkEncodingIsComplete();
+
+ final AddArrayBeforeGroupV1Decoder decoder = new AddArrayBeforeGroupV1Decoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(1));
+ final AddArrayBeforeGroupV1Decoder.BDecoder b = decoder.b();
+ assertThat(b.count(), equalTo(1));
+ assertThat(b.next().c(), equalTo(2));
+ }
+
+ @Test
+ void allowsOldDecoderToSkipAddedArrayFieldBeforeGroup()
+ {
+ final AddArrayBeforeGroupV1Encoder encoder = new AddArrayBeforeGroupV1Encoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(1)
+ .putD((short)1, (short)2, (short)3, (short)4)
+ .bCount(1)
+ .next().c(2);
+ encoder.checkEncodingIsComplete();
+
+ modifyHeaderToLookLikeVersion1();
+
+ final AddArrayBeforeGroupV0Decoder decoder = new AddArrayBeforeGroupV0Decoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(1));
+ final AddArrayBeforeGroupV0Decoder.BDecoder b = decoder.b();
+ assertThat(b.count(), equalTo(1));
+ assertThat(b.next().c(), equalTo(2));
+ }
+
+ @Test
+ void allowsNewDecoderToDecodeAddedBitSetFieldBeforeGroup()
+ {
+ final AddBitSetBeforeGroupV1Encoder encoder = new AddBitSetBeforeGroupV1Encoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(1).d().guacamole(true).cheese(true).sourCream(false);
+ encoder.bCount(1).next().c(2);
+ encoder.checkEncodingIsComplete();
+
+ final AddBitSetBeforeGroupV1Decoder decoder = new AddBitSetBeforeGroupV1Decoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(1));
+ final FlagsDecoder d = decoder.d();
+ assertThat(d, notNullValue());
+ assertThat(d.guacamole(), equalTo(true));
+ assertThat(d.cheese(), equalTo(true));
+ assertThat(d.sourCream(), equalTo(false));
+ final AddBitSetBeforeGroupV1Decoder.BDecoder b = decoder.b();
+ assertThat(b.count(), equalTo(1));
+ assertThat(b.next().c(), equalTo(2));
+ }
+
+ @Test
+ void allowsNewDecoderToDecodeMissingBitSetFieldBeforeGroupAsNullValue()
+ {
+ final AddBitSetBeforeGroupV0Encoder encoder = new AddBitSetBeforeGroupV0Encoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(1).bCount(1).next().c(2);
+ encoder.checkEncodingIsComplete();
+
+ modifyHeaderToLookLikeVersion0();
+
+ final AddBitSetBeforeGroupV1Decoder decoder = new AddBitSetBeforeGroupV1Decoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(1));
+ assertThat(decoder.d(), nullValue());
+ final AddBitSetBeforeGroupV1Decoder.BDecoder b = decoder.b();
+ assertThat(b.count(), equalTo(1));
+ assertThat(b.next().c(), equalTo(2));
+ }
+
+ @Test
+ void allowsNewDecoderToSkipPresentButAddedBitSetFieldBeforeGroup()
+ {
+ final AddBitSetBeforeGroupV1Encoder encoder = new AddBitSetBeforeGroupV1Encoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(1).d().guacamole(true).cheese(true).sourCream(false);
+ encoder.bCount(1).next().c(2);
+ encoder.checkEncodingIsComplete();
+
+ final AddBitSetBeforeGroupV1Decoder decoder = new AddBitSetBeforeGroupV1Decoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(1));
+ final AddBitSetBeforeGroupV1Decoder.BDecoder b = decoder.b();
+ assertThat(b.count(), equalTo(1));
+ assertThat(b.next().c(), equalTo(2));
+ }
+
+ @Test
+ void allowsOldDecoderToSkipAddedBitSetFieldBeforeGroup()
+ {
+ final AddBitSetBeforeGroupV1Encoder encoder = new AddBitSetBeforeGroupV1Encoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(1).d().guacamole(true).cheese(true).sourCream(false);
+ encoder.bCount(1).next().c(2);
+ encoder.checkEncodingIsComplete();
+
+ modifyHeaderToLookLikeVersion1();
+
+ final AddBitSetBeforeGroupV0Decoder decoder = new AddBitSetBeforeGroupV0Decoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(1));
+ final AddBitSetBeforeGroupV0Decoder.BDecoder b = decoder.b();
+ assertThat(b.count(), equalTo(1));
+ assertThat(b.next().c(), equalTo(2));
+ }
+
+ @Test
+ void allowsEncodingAndDecodingEnumInsideGroupInSchemaDefinedOrder()
+ {
+ final EnumInsideGroupEncoder encoder = new EnumInsideGroupEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(Direction.BUY)
+ .bCount(1)
+ .next()
+ .c(Direction.SELL);
+ encoder.checkEncodingIsComplete();
+
+ final EnumInsideGroupDecoder decoder = new EnumInsideGroupDecoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(Direction.BUY));
+ final EnumInsideGroupDecoder.BDecoder b = decoder.b();
+ assertThat(b.count(), equalTo(1));
+ assertThat(b.next().c(), equalTo(Direction.SELL));
+ assertThat(decoder.toString(), containsString("a=BUY|b=[(c=SELL)]"));
+ }
+
+ @Test
+ void disallowsEncodingEnumInsideGroupBeforeCallingNext()
+ {
+ final EnumInsideGroupEncoder encoder = new EnumInsideGroupEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(Direction.BUY);
+ final EnumInsideGroupEncoder.BEncoder bEncoder = encoder.bCount(1);
+ final Exception exception =
+ assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> bEncoder.c(Direction.SELL));
+ assertThat(exception.getMessage(), containsString("Cannot access field \"b.c\" in state: V0_B_N"));
+ }
+
+ @Test
+ void disallowsDecodingEnumInsideGroupBeforeCallingNext()
+ {
+ final EnumInsideGroupEncoder encoder = new EnumInsideGroupEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(Direction.BUY)
+ .bCount(1)
+ .next()
+ .c(Direction.SELL);
+ encoder.checkEncodingIsComplete();
+
+ final EnumInsideGroupDecoder decoder = new EnumInsideGroupDecoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(Direction.BUY));
+ final EnumInsideGroupDecoder.BDecoder b = decoder.b();
+ assertThat(b.count(), equalTo(1));
+ final Exception exception =
+ assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, b::c);
+ assertThat(exception.getMessage(), containsString("Cannot access field \"b.c\" in state: V0_B_N"));
+ }
+
+ @Test
+ void allowsReEncodingTopLevelEnum()
+ {
+ final EnumInsideGroupEncoder encoder = new EnumInsideGroupEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(Direction.BUY)
+ .bCount(1)
+ .next()
+ .c(Direction.SELL);
+
+ encoder.a(Direction.SELL);
+ encoder.checkEncodingIsComplete();
+
+
+ final EnumInsideGroupDecoder decoder = new EnumInsideGroupDecoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(Direction.SELL));
+ final EnumInsideGroupDecoder.BDecoder b = decoder.b();
+ assertThat(b.count(), equalTo(1));
+ assertThat(b.next().c(), equalTo(Direction.SELL));
+ }
+
+ @Test
+ void allowsEncodingAndDecodingBitSetInsideGroupInSchemaDefinedOrder()
+ {
+ final BitSetInsideGroupEncoder encoder = new BitSetInsideGroupEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a().cheese(true).guacamole(true).sourCream(false);
+ encoder.bCount(1)
+ .next()
+ .c().cheese(false).guacamole(false).sourCream(true);
+ encoder.checkEncodingIsComplete();
+
+ final BitSetInsideGroupDecoder decoder = new BitSetInsideGroupDecoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ final FlagsDecoder a = decoder.a();
+ assertThat(a.guacamole(), equalTo(true));
+ assertThat(a.cheese(), equalTo(true));
+ assertThat(a.sourCream(), equalTo(false));
+ final BitSetInsideGroupDecoder.BDecoder b = decoder.b();
+ assertThat(b.count(), equalTo(1));
+ final FlagsDecoder c = b.next().c();
+ assertThat(c.guacamole(), equalTo(false));
+ assertThat(c.cheese(), equalTo(false));
+ assertThat(c.sourCream(), equalTo(true));
+ assertThat(decoder.toString(), containsString("a={guacamole,cheese}|b=[(c={sourCream})]"));
+ }
+
+ @Test
+ void disallowsEncodingBitSetInsideGroupBeforeCallingNext()
+ {
+ final BitSetInsideGroupEncoder encoder = new BitSetInsideGroupEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a().cheese(true).guacamole(true).sourCream(false);
+ final BitSetInsideGroupEncoder.BEncoder bEncoder = encoder.bCount(1);
+ final Exception exception =
+ assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, bEncoder::c);
+ assertThat(exception.getMessage(), containsString("Cannot access field \"b.c\" in state: V0_B_N"));
+ }
+
+ @Test
+ void disallowsDecodingBitSetInsideGroupBeforeCallingNext()
+ {
+ final BitSetInsideGroupEncoder encoder = new BitSetInsideGroupEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a().cheese(true).guacamole(true).sourCream(false);
+ encoder.bCount(1)
+ .next()
+ .c().guacamole(false).cheese(false).sourCream(true);
+ encoder.checkEncodingIsComplete();
+
+ final BitSetInsideGroupDecoder decoder = new BitSetInsideGroupDecoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ final FlagsDecoder a = decoder.a();
+ assertThat(a.guacamole(), equalTo(true));
+ assertThat(a.cheese(), equalTo(true));
+ assertThat(a.sourCream(), equalTo(false));
+ final BitSetInsideGroupDecoder.BDecoder b = decoder.b();
+ assertThat(b.count(), equalTo(1));
+ final Exception exception =
+ assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, b::c);
+ assertThat(exception.getMessage(), containsString("Cannot access field \"b.c\" in state: V0_B_N"));
+ }
+
+ @Test
+ void allowsReEncodingTopLevelBitSetViaReWrap()
+ {
+ final BitSetInsideGroupEncoder encoder = new BitSetInsideGroupEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a().cheese(true).guacamole(true).sourCream(false);
+ encoder.bCount(1)
+ .next()
+ .c().cheese(false).guacamole(false).sourCream(true);
+
+ encoder.a().sourCream(true);
+ encoder.checkEncodingIsComplete();
+
+ final BitSetInsideGroupDecoder decoder = new BitSetInsideGroupDecoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ final FlagsDecoder a = decoder.a();
+ assertThat(a.guacamole(), equalTo(true));
+ assertThat(a.cheese(), equalTo(true));
+ assertThat(a.sourCream(), equalTo(true));
+ final BitSetInsideGroupDecoder.BDecoder b = decoder.b();
+ assertThat(b.count(), equalTo(1));
+ final FlagsDecoder c = b.next().c();
+ assertThat(c.guacamole(), equalTo(false));
+ assertThat(c.cheese(), equalTo(false));
+ assertThat(c.sourCream(), equalTo(true));
+ }
+
+ @Test
+ void allowsEncodingAndDecodingArrayInsideGroupInSchemaDefinedOrder()
+ {
+ final ArrayInsideGroupEncoder encoder = new ArrayInsideGroupEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.putA((short)1, (short)2, (short)3, (short)4);
+ encoder.bCount(1)
+ .next()
+ .putC((short)5, (short)6, (short)7, (short)8);
+ encoder.checkEncodingIsComplete();
+
+ final ArrayInsideGroupDecoder decoder = new ArrayInsideGroupDecoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(0), equalTo((short)1));
+ assertThat(decoder.a(1), equalTo((short)2));
+ assertThat(decoder.a(2), equalTo((short)3));
+ assertThat(decoder.a(3), equalTo((short)4));
+ final ArrayInsideGroupDecoder.BDecoder b = decoder.b();
+ assertThat(b.count(), equalTo(1));
+ b.next();
+ assertThat(b.c(0), equalTo((short)5));
+ assertThat(b.c(1), equalTo((short)6));
+ assertThat(b.c(2), equalTo((short)7));
+ assertThat(b.c(3), equalTo((short)8));
+ assertThat(decoder.toString(), containsString("a=[1,2,3,4]|b=[(c=[5,6,7,8])]"));
+ }
+
+ @Test
+ void disallowsEncodingArrayInsideGroupBeforeCallingNext1()
+ {
+ final ArrayInsideGroupEncoder.BEncoder bEncoder = encodeUntilGroupWithArrayInside();
+ final Exception exception =
+ assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> bEncoder.putC((short)5, (short)6, (short)7, (short)8));
+ assertThat(exception.getMessage(), containsString("Cannot access field \"b.c\" in state: V0_B_N"));
+ }
+
+ @Test
+ void disallowsEncodingArrayInsideGroupBeforeCallingNext2()
+ {
+ final ArrayInsideGroupEncoder.BEncoder bEncoder = encodeUntilGroupWithArrayInside();
+ final Exception exception =
+ assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> bEncoder.c(0, (short)5));
+ assertThat(exception.getMessage(), containsString("Cannot access field \"b.c\" in state: V0_B_N"));
+ }
+
+ @Test
+ void disallowsEncodingArrayInsideGroupBeforeCallingNext3()
+ {
+ final ArrayInsideGroupEncoder.BEncoder bEncoder = encodeUntilGroupWithArrayInside();
+ final byte[] bytes = {5, 6, 7, 8};
+ final Exception exception =
+ assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> bEncoder.putC(bytes, 0, 4));
+ assertThat(exception.getMessage(), containsString("Cannot access field \"b.c\" in state: V0_B_N"));
+ }
+
+ @Test
+ void disallowsEncodingArrayInsideGroupBeforeCallingNext4()
+ {
+ final ArrayInsideGroupEncoder.BEncoder bEncoder = encodeUntilGroupWithArrayInside();
+ final UnsafeBuffer buffer = new UnsafeBuffer(new byte[8]);
+ buffer.putByte(0, (byte)5);
+ buffer.putByte(2, (byte)6);
+ buffer.putByte(4, (byte)7);
+ buffer.putByte(6, (byte)8);
+ final Exception exception =
+ assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> bEncoder.putC(buffer, 0, 4));
+ assertThat(exception.getMessage(), containsString("Cannot access field \"b.c\" in state: V0_B_N"));
+ }
+
+ private ArrayInsideGroupEncoder.BEncoder encodeUntilGroupWithArrayInside()
+ {
+ final ArrayInsideGroupEncoder encoder = new ArrayInsideGroupEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.putA((short)1, (short)2, (short)3, (short)4);
+ return encoder.bCount(1);
+ }
+
+ @Test
+ void disallowsDecodingArrayInsideGroupBeforeCallingNext1()
+ {
+ final ArrayInsideGroupDecoder.BDecoder b = decodeUntilGroupWithArrayInside();
+ final Exception exception =
+ assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> b.c(0));
+ assertThat(exception.getMessage(), containsString("Cannot access field \"b.c\" in state: V0_B_N"));
+ }
+
+ @Test
+ void disallowsDecodingArrayInsideGroupBeforeCallingNext2()
+ {
+ final ArrayInsideGroupDecoder.BDecoder b = decodeUntilGroupWithArrayInside();
+ final Exception exception =
+ assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> b.getC(new byte[8], 0, 8));
+ assertThat(exception.getMessage(), containsString("Cannot access field \"b.c\" in state: V0_B_N"));
+ }
+
+ @Test
+ void disallowsDecodingArrayInsideGroupBeforeCallingNext3()
+ {
+ final ArrayInsideGroupDecoder.BDecoder b = decodeUntilGroupWithArrayInside();
+ final Exception exception =
+ assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> b.getC(new ExpandableArrayBuffer(), 0, 8));
+ assertThat(exception.getMessage(), containsString("Cannot access field \"b.c\" in state: V0_B_N"));
+ }
+
+ @Test
+ void disallowsDecodingArrayInsideGroupBeforeCallingNext4()
+ {
+ final ArrayInsideGroupDecoder.BDecoder b = decodeUntilGroupWithArrayInside();
+ final Exception exception =
+ assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> b.wrapC(new UnsafeBuffer()));
+ assertThat(exception.getMessage(), containsString("Cannot access field \"b.c\" in state: V0_B_N"));
+ }
+
+ private ArrayInsideGroupDecoder.BDecoder decodeUntilGroupWithArrayInside()
+ {
+ final ArrayInsideGroupEncoder encoder = new ArrayInsideGroupEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.putA((short)1, (short)2, (short)3, (short)4);
+ encoder.bCount(1)
+ .next()
+ .putC((short)5, (short)6, (short)7, (short)8);
+ encoder.checkEncodingIsComplete();
+
+ final ArrayInsideGroupDecoder decoder = new ArrayInsideGroupDecoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(0), equalTo((short)1));
+ assertThat(decoder.a(1), equalTo((short)2));
+ assertThat(decoder.a(2), equalTo((short)3));
+ assertThat(decoder.a(3), equalTo((short)4));
+ final ArrayInsideGroupDecoder.BDecoder b = decoder.b();
+ assertThat(b.count(), equalTo(1));
+ return b;
+ }
+
+ @Test
+ void allowsReEncodingTopLevelArrayViaReWrap()
+ {
+ final ArrayInsideGroupEncoder encoder = new ArrayInsideGroupEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.putA((short)1, (short)2, (short)3, (short)4);
+ encoder.bCount(1)
+ .next()
+ .putC((short)5, (short)6, (short)7, (short)8);
+
+ encoder.putA((short)9, (short)10, (short)11, (short)12);
+ encoder.checkEncodingIsComplete();
+
+ final ArrayInsideGroupDecoder decoder = new ArrayInsideGroupDecoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(0), equalTo((short)9));
+ assertThat(decoder.a(1), equalTo((short)10));
+ assertThat(decoder.a(2), equalTo((short)11));
+ assertThat(decoder.a(3), equalTo((short)12));
+ final ArrayInsideGroupDecoder.BDecoder b = decoder.b();
+ assertThat(b.count(), equalTo(1));
+ b.next();
+ assertThat(b.c(0), equalTo((short)5));
+ assertThat(b.c(1), equalTo((short)6));
+ assertThat(b.c(2), equalTo((short)7));
+ assertThat(b.c(3), equalTo((short)8));
+ }
+
+ @Test
+ void allowsEncodingAndDecodingGroupFieldsInSchemaDefinedOrder1()
+ {
+ final MultipleGroupsEncoder encoder = new MultipleGroupsEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(42);
+ encoder.bCount(0);
+ encoder.dCount(1).next().e(43);
+ encoder.checkEncodingIsComplete();
+
+ final MultipleGroupsDecoder decoder = new MultipleGroupsDecoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(42));
+ assertThat(decoder.b().count(), equalTo(0));
+ final MultipleGroupsDecoder.DDecoder d = decoder.d();
+ assertThat(d.count(), equalTo(1));
+ assertThat(d.next().e(), equalTo(43));
+ assertThat(decoder.toString(), containsString("a=42|b=[]|d=[(e=43)]"));
+ }
+
+ @Test
+ void allowsEncodingAndDecodingGroupFieldsInSchemaDefinedOrder2()
+ {
+ final MultipleGroupsEncoder encoder = new MultipleGroupsEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(41);
+ encoder.bCount(1).next().c(42);
+ encoder.dCount(1).next().e(43);
+ encoder.checkEncodingIsComplete();
+
+ final MultipleGroupsDecoder decoder = new MultipleGroupsDecoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(41));
+ final MultipleGroupsDecoder.BDecoder b = decoder.b();
+ assertThat(b.count(), equalTo(1));
+ assertThat(b.next().c(), equalTo(42));
+ final MultipleGroupsDecoder.DDecoder d = decoder.d();
+ assertThat(d.count(), equalTo(1));
+ assertThat(d.next().e(), equalTo(43));
+ }
+
+ @Test
+ void allowsReEncodingTopLevelPrimitiveFieldsAfterGroups()
+ {
+ final MultipleGroupsEncoder encoder = new MultipleGroupsEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(41);
+ encoder.bCount(1).next().c(42);
+ encoder.dCount(1).next().e(43);
+ encoder.a(44);
+ encoder.checkEncodingIsComplete();
+
+ final MultipleGroupsDecoder decoder = new MultipleGroupsDecoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(44));
+ final MultipleGroupsDecoder.BDecoder b = decoder.b();
+ assertThat(b.count(), equalTo(1));
+ assertThat(b.next().c(), equalTo(42));
+ final MultipleGroupsDecoder.DDecoder d = decoder.d();
+ assertThat(d.count(), equalTo(1));
+ assertThat(d.next().e(), equalTo(43));
+ }
+
+ @Test
+ void disallowsMissedEncodingOfGroupField()
+ {
+ final MultipleGroupsEncoder encoder = new MultipleGroupsEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(41);
+ final Exception exception =
+ assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> encoder.dCount(0));
+ assertThat(exception.getMessage(),
+ containsString("Cannot encode count of repeating group \"d\" in state: V0_BLOCK"));
+ }
+
+ @Test
+ void disallowsReEncodingEarlierGroupFields()
+ {
+ final MultipleGroupsEncoder encoder = new MultipleGroupsEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(41);
+ encoder.bCount(1).next().c(42);
+ encoder.dCount(1).next().e(43);
+ final Exception exception =
+ assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> encoder.bCount(1));
+ assertThat(exception.getMessage(),
+ containsString("Cannot encode count of repeating group \"b\" in state: V0_D_1_BLOCK"));
+ }
+
+ @Test
+ void disallowsReEncodingLatestGroupField()
+ {
+ final MultipleGroupsEncoder encoder = new MultipleGroupsEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(41);
+ encoder.bCount(1).next().c(42);
+ encoder.dCount(1).next().e(43);
+ final Exception exception =
+ assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> encoder.dCount(1));
+ assertThat(exception.getMessage(),
+ containsString("Cannot encode count of repeating group \"d\" in state: V0_D_1_BLOCK"));
+ }
+
+ @Test
+ void disallowsMissedDecodingOfGroupField()
+ {
+ final MultipleGroupsEncoder encoder = new MultipleGroupsEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(41);
+ encoder.bCount(1).next().c(42);
+ encoder.dCount(1).next().e(43);
+ encoder.checkEncodingIsComplete();
+
+ final MultipleGroupsDecoder decoder = new MultipleGroupsDecoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(41));
+ final Exception exception =
+ assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, decoder::d);
+ assertThat(exception.getMessage(),
+ containsString("Cannot decode count of repeating group \"d\" in state: V0_BLOCK"));
+ }
+
+ @Test
+ void disallowsReDecodingEarlierGroupField()
+ {
+ final MultipleGroupsEncoder encoder = new MultipleGroupsEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(41);
+ encoder.bCount(1).next().c(42);
+ encoder.dCount(1).next().e(43);
+ encoder.checkEncodingIsComplete();
+
+ final MultipleGroupsDecoder decoder = new MultipleGroupsDecoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(41));
+ final MultipleGroupsDecoder.BDecoder b = decoder.b();
+ assertThat(b.count(), equalTo(1));
+ assertThat(b.next().c(), equalTo(42));
+ final MultipleGroupsDecoder.DDecoder d = decoder.d();
+ assertThat(d.count(), equalTo(1));
+ assertThat(d.next().e(), equalTo(43));
+ final Exception exception =
+ assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, decoder::b);
+ assertThat(exception.getMessage(),
+ containsString("Cannot decode count of repeating group \"b\" in state: V0_D_1_BLOCK"));
+ }
+
+ @Test
+ void disallowsReDecodingLatestGroupField()
+ {
+ final MultipleGroupsEncoder encoder = new MultipleGroupsEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(41);
+ encoder.bCount(1).next().c(42);
+ encoder.dCount(1).next().e(43);
+ encoder.checkEncodingIsComplete();
+
+ final MultipleGroupsDecoder decoder = new MultipleGroupsDecoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(41));
+ final MultipleGroupsDecoder.BDecoder b = decoder.b();
+ assertThat(b.count(), equalTo(1));
+ assertThat(b.next().c(), equalTo(42));
+ final MultipleGroupsDecoder.DDecoder d = decoder.d();
+ assertThat(d.count(), equalTo(1));
+ assertThat(d.next().e(), equalTo(43));
+ final Exception exception =
+ assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, decoder::d);
+ assertThat(exception.getMessage(),
+ containsString("Cannot decode count of repeating group \"d\" in state: V0_D_1_BLOCK"));
+ }
+
+ @Test
+ void allowsNewDecoderToDecodeAddedVarData()
+ {
+ final AddVarDataV1Encoder encoder = new AddVarDataV1Encoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(42);
+ encoder.b("abc");
+ encoder.checkEncodingIsComplete();
+
+ final AddVarDataV1Decoder decoder = new AddVarDataV1Decoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(42));
+ assertThat(decoder.b(), equalTo("abc"));
+ }
+
+ @Test
+ void allowsNewDecoderToDecodeMissingAddedVarDataAsNullValue1()
+ {
+ final AddVarDataV0Encoder encoder = new AddVarDataV0Encoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(42);
+ encoder.checkEncodingIsComplete();
+
+ modifyHeaderToLookLikeVersion0();
+
+ final AddVarDataV1Decoder decoder = new AddVarDataV1Decoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(42));
+ assertThat(decoder.b(), equalTo(""));
+ assertThat(decoder.toString(), containsString("a=42|b=''"));
+ }
+
+ @Test
+ void allowsNewDecoderToDecodeMissingAddedVarDataAsNullValue2()
+ {
+ final AddVarDataV0Encoder encoder = new AddVarDataV0Encoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(42);
+ encoder.checkEncodingIsComplete();
+
+ modifyHeaderToLookLikeVersion0();
+
+ final AddVarDataV1Decoder decoder = new AddVarDataV1Decoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(42));
+ assertThat(decoder.getB(new StringBuilder()), equalTo(0));
+ }
+
+ @Test
+ void allowsNewDecoderToDecodeMissingAddedVarDataAsNullValue3()
+ {
+ final AddVarDataV0Encoder encoder = new AddVarDataV0Encoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(42);
+ encoder.checkEncodingIsComplete();
+
+ modifyHeaderToLookLikeVersion0();
+
+ final AddVarDataV1Decoder decoder = new AddVarDataV1Decoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(42));
+ assertThat(decoder.getB(new byte[3], 0, 3), equalTo(0));
+ }
+
+ @Test
+ void allowsNewDecoderToDecodeMissingAddedVarDataAsNullValue4()
+ {
+ final AddVarDataV0Encoder encoder = new AddVarDataV0Encoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(42);
+ encoder.checkEncodingIsComplete();
+
+ modifyHeaderToLookLikeVersion0();
+
+ final AddVarDataV1Decoder decoder = new AddVarDataV1Decoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(42));
+ assertThat(decoder.getB(new ExpandableArrayBuffer(), 0, 3), equalTo(0));
+ }
+
+ @Test
+ void allowsNewDecoderToDecodeMissingAddedVarDataAsNullValue5()
+ {
+ final AddVarDataV0Encoder encoder = new AddVarDataV0Encoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(42);
+ encoder.checkEncodingIsComplete();
+
+ modifyHeaderToLookLikeVersion0();
+
+ final AddVarDataV1Decoder decoder = new AddVarDataV1Decoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(42));
+ assertThat(decoder.bLength(), equalTo(0));
+ }
+
+ @Test
+ void allowsEncodingAndDecodingAsciiInsideGroupInSchemaDefinedOrder1()
+ {
+ final AsciiInsideGroupEncoder encoder = new AsciiInsideGroupEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a("GBPUSD");
+ encoder.bCount(1)
+ .next()
+ .c("EURUSD");
+ encoder.checkEncodingIsComplete();
+
+ final AsciiInsideGroupDecoder decoder = new AsciiInsideGroupDecoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo("GBPUSD"));
+ final AsciiInsideGroupDecoder.BDecoder b = decoder.b();
+ assertThat(b.count(), equalTo(1));
+ b.next();
+ assertThat(b.c(), equalTo("EURUSD"));
+ assertThat(decoder.toString(), containsString("a=GBPUSD|b=[(c=EURUSD)]"));
+ }
+
+ @Test
+ void allowsEncodingAndDecodingAsciiInsideGroupInSchemaDefinedOrder2()
+ {
+ final AsciiInsideGroupEncoder encoder = new AsciiInsideGroupEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ final byte[] gbpUsdBytes = "GBPUSD".getBytes(StandardCharsets.US_ASCII);
+ encoder.putA(gbpUsdBytes, 0);
+ encoder.bCount(1)
+ .next()
+ .c(0, (byte)'E')
+ .c(1, (byte)'U')
+ .c(2, (byte)'R')
+ .c(3, (byte)'U')
+ .c(4, (byte)'S')
+ .c(5, (byte)'D');
+ encoder.checkEncodingIsComplete();
+
+ final AsciiInsideGroupDecoder decoder = new AsciiInsideGroupDecoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ final byte[] aBytes = new byte[6];
+ decoder.getA(aBytes, 0);
+ assertThat(aBytes, equalTo(gbpUsdBytes));
+ final AsciiInsideGroupDecoder.BDecoder b = decoder.b();
+ assertThat(b.count(), equalTo(1));
+ b.next();
+ assertThat(b.c(0), equalTo((byte)'E'));
+ assertThat(b.c(1), equalTo((byte)'U'));
+ assertThat(b.c(2), equalTo((byte)'R'));
+ assertThat(b.c(3), equalTo((byte)'U'));
+ assertThat(b.c(4), equalTo((byte)'S'));
+ assertThat(b.c(5), equalTo((byte)'D'));
+ }
+
+ @Test
+ void disallowsEncodingAsciiInsideGroupBeforeCallingNext1()
+ {
+ final AsciiInsideGroupEncoder.BEncoder bEncoder = encodeUntilGroupWithAsciiInside();
+ final Exception exception =
+ assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> bEncoder.c("EURUSD"));
+ assertThat(exception.getMessage(), containsString("Cannot access field \"b.c\" in state: V0_B_N"));
+ }
+
+ @Test
+ void disallowsEncodingAsciiInsideGroupBeforeCallingNext2()
+ {
+ final AsciiInsideGroupEncoder.BEncoder bEncoder = encodeUntilGroupWithAsciiInside();
+ final Exception exception =
+ assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> bEncoder.c(0, (byte)'E'));
+ assertThat(exception.getMessage(), containsString("Cannot access field \"b.c\" in state: V0_B_N"));
+ }
+
+ @Test
+ void disallowsEncodingAsciiInsideGroupBeforeCallingNext3()
+ {
+ final AsciiInsideGroupEncoder.BEncoder bEncoder = encodeUntilGroupWithAsciiInside();
+ final byte[] eurUsdBytes = "EURUSD".getBytes(StandardCharsets.US_ASCII);
+ final Exception exception =
+ assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> bEncoder.putC(eurUsdBytes, 0));
+ assertThat(exception.getMessage(), containsString("Cannot access field \"b.c\" in state: V0_B_N"));
+ }
+
+ private AsciiInsideGroupEncoder.BEncoder encodeUntilGroupWithAsciiInside()
+ {
+ final AsciiInsideGroupEncoder encoder = new AsciiInsideGroupEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a("GBPUSD");
+ return encoder.bCount(1);
+ }
+
+ @Test
+ void disallowsDecodingAsciiInsideGroupBeforeCallingNext1()
+ {
+ final AsciiInsideGroupDecoder.BDecoder b = decodeUntilGroupWithAsciiInside();
+ final Exception exception =
+ assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> b.c(0));
+ assertThat(exception.getMessage(), containsString("Cannot access field \"b.c\" in state: V0_B_N"));
+ }
+
+ @Test
+ void disallowsDecodingAsciiInsideGroupBeforeCallingNext2()
+ {
+ final AsciiInsideGroupDecoder.BDecoder b = decodeUntilGroupWithAsciiInside();
+ final Exception exception =
+ assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> b.getC(new byte[6], 0));
+ assertThat(exception.getMessage(), containsString("Cannot access field \"b.c\" in state: V0_B_N"));
+ }
+
+ @Test
+ void disallowsDecodingAsciiInsideGroupBeforeCallingNext3()
+ {
+ final AsciiInsideGroupDecoder.BDecoder b = decodeUntilGroupWithAsciiInside();
+ final Exception exception =
+ assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, b::c);
+ assertThat(exception.getMessage(), containsString("Cannot access field \"b.c\" in state: V0_B_N"));
+ }
+
+ @Test
+ void disallowsDecodingAsciiInsideGroupBeforeCallingNext4()
+ {
+ final AsciiInsideGroupDecoder.BDecoder b = decodeUntilGroupWithAsciiInside();
+ final Exception exception =
+ assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> b.getC(new StringBuilder()));
+ assertThat(exception.getMessage(), containsString("Cannot access field \"b.c\" in state: V0_B_N"));
+ }
+
+ private AsciiInsideGroupDecoder.BDecoder decodeUntilGroupWithAsciiInside()
+ {
+ final AsciiInsideGroupEncoder encoder = new AsciiInsideGroupEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a("GBPUSD");
+ encoder.bCount(1)
+ .next()
+ .c("EURUSD");
+ encoder.checkEncodingIsComplete();
+
+ final AsciiInsideGroupDecoder decoder = new AsciiInsideGroupDecoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo("GBPUSD"));
+ final AsciiInsideGroupDecoder.BDecoder b = decoder.b();
+ assertThat(b.count(), equalTo(1));
+ return b;
+ }
+
+ @Test
+ void allowsReEncodingTopLevelAsciiViaReWrap()
+ {
+ final AsciiInsideGroupEncoder encoder = new AsciiInsideGroupEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a("GBPUSD");
+ encoder.bCount(1)
+ .next()
+ .c("EURUSD");
+
+ encoder.a("CADUSD");
+ encoder.checkEncodingIsComplete();
+
+ final AsciiInsideGroupDecoder decoder = new AsciiInsideGroupDecoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo("CADUSD"));
+ final AsciiInsideGroupDecoder.BDecoder b = decoder.b();
+ assertThat(b.count(), equalTo(1));
+ b.next();
+ assertThat(b.c(), equalTo("EURUSD"));
+ }
+
+ @Test
+ void allowsNewDecoderToDecodeAddedAsciiFieldBeforeGroup1()
+ {
+ final AddAsciiBeforeGroupV1Encoder encoder = new AddAsciiBeforeGroupV1Encoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(1)
+ .d("EURUSD")
+ .bCount(1)
+ .next().c(2);
+ encoder.checkEncodingIsComplete();
+
+ final AddAsciiBeforeGroupV1Decoder decoder = new AddAsciiBeforeGroupV1Decoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(1));
+ assertThat(decoder.d(), equalTo("EURUSD"));
+ final AddAsciiBeforeGroupV1Decoder.BDecoder b = decoder.b();
+ assertThat(b.count(), equalTo(1));
+ assertThat(b.next().c(), equalTo(2));
+ }
+
+ @Test
+ void allowsNewDecoderToDecodeAddedAsciiFieldBeforeGroup2()
+ {
+ final AddAsciiBeforeGroupV1Encoder encoder = new AddAsciiBeforeGroupV1Encoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(1)
+ .d("EURUSD")
+ .bCount(1)
+ .next().c(2);
+ encoder.checkEncodingIsComplete();
+
+ final AddAsciiBeforeGroupV1Decoder decoder = new AddAsciiBeforeGroupV1Decoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(1));
+ final StringBuilder aValue = new StringBuilder();
+ decoder.getD(aValue);
+ assertThat(aValue.toString(), equalTo("EURUSD"));
+ final AddAsciiBeforeGroupV1Decoder.BDecoder b = decoder.b();
+ assertThat(b.count(), equalTo(1));
+ assertThat(b.next().c(), equalTo(2));
+ }
+
+
+ @Test
+ void allowsNewDecoderToDecodeAddedAsciiFieldBeforeGroup3()
+ {
+ final AddAsciiBeforeGroupV1Encoder encoder = new AddAsciiBeforeGroupV1Encoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ final byte[] eurUsdBytes = "EURUSD".getBytes(StandardCharsets.US_ASCII);
+ encoder.a(1)
+ .putD(eurUsdBytes, 0)
+ .bCount(1)
+ .next().c(2);
+ encoder.checkEncodingIsComplete();
+
+ final AddAsciiBeforeGroupV1Decoder decoder = new AddAsciiBeforeGroupV1Decoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(1));
+ final byte[] aBytes = new byte[6];
+ decoder.getD(aBytes, 0);
+ assertThat(aBytes, equalTo(eurUsdBytes));
+ final AddAsciiBeforeGroupV1Decoder.BDecoder b = decoder.b();
+ assertThat(b.count(), equalTo(1));
+ assertThat(b.next().c(), equalTo(2));
+ }
+
+
+ @Test
+ void allowsNewDecoderToDecodeAddedAsciiFieldBeforeGroup4()
+ {
+ final AddAsciiBeforeGroupV1Encoder encoder = new AddAsciiBeforeGroupV1Encoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(1)
+ .d(0, (byte)'E')
+ .d(1, (byte)'U')
+ .d(2, (byte)'R')
+ .d(3, (byte)'U')
+ .d(4, (byte)'S')
+ .d(5, (byte)'D')
+ .bCount(1)
+ .next().c(2);
+ encoder.checkEncodingIsComplete();
+
+ final AddAsciiBeforeGroupV1Decoder decoder = new AddAsciiBeforeGroupV1Decoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(1));
+ assertThat(decoder.d(0), equalTo((byte)'E'));
+ assertThat(decoder.d(1), equalTo((byte)'U'));
+ assertThat(decoder.d(2), equalTo((byte)'R'));
+ assertThat(decoder.d(3), equalTo((byte)'U'));
+ assertThat(decoder.d(4), equalTo((byte)'S'));
+ assertThat(decoder.d(5), equalTo((byte)'D'));
+ final AddAsciiBeforeGroupV1Decoder.BDecoder b = decoder.b();
+ assertThat(b.count(), equalTo(1));
+ assertThat(b.next().c(), equalTo(2));
+ }
+
+ @Test
+ void allowsNewDecoderToDecodeMissingAsciiFieldBeforeGroupAsNullValue1()
+ {
+ final AddAsciiBeforeGroupV0Encoder encoder = new AddAsciiBeforeGroupV0Encoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(1).bCount(1).next().c(2);
+ encoder.checkEncodingIsComplete();
+
+ modifyHeaderToLookLikeVersion0();
+
+ final AddAsciiBeforeGroupV1Decoder decoder = new AddAsciiBeforeGroupV1Decoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(1));
+ assertThat(decoder.d(0), equalTo(AddAsciiBeforeGroupV1Decoder.dNullValue()));
+ assertThat(decoder.d(1), equalTo(AddAsciiBeforeGroupV1Decoder.dNullValue()));
+ assertThat(decoder.d(2), equalTo(AddAsciiBeforeGroupV1Decoder.dNullValue()));
+ assertThat(decoder.d(3), equalTo(AddAsciiBeforeGroupV1Decoder.dNullValue()));
+ assertThat(decoder.d(4), equalTo(AddAsciiBeforeGroupV1Decoder.dNullValue()));
+ assertThat(decoder.d(5), equalTo(AddAsciiBeforeGroupV1Decoder.dNullValue()));
+ final AddAsciiBeforeGroupV1Decoder.BDecoder b = decoder.b();
+ assertThat(b.count(), equalTo(1));
+ assertThat(b.next().c(), equalTo(2));
+ }
+
+ @Test
+ void allowsNewDecoderToDecodeMissingAsciiFieldBeforeGroupAsNullValue2()
+ {
+ final AddAsciiBeforeGroupV0Encoder encoder = new AddAsciiBeforeGroupV0Encoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(1).bCount(1).next().c(2);
+ encoder.checkEncodingIsComplete();
+
+ modifyHeaderToLookLikeVersion0();
+
+ final AddAsciiBeforeGroupV1Decoder decoder = new AddAsciiBeforeGroupV1Decoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(1));
+ assertThat(decoder.d(), equalTo(""));
+ final AddAsciiBeforeGroupV1Decoder.BDecoder b = decoder.b();
+ assertThat(b.count(), equalTo(1));
+ assertThat(b.next().c(), equalTo(2));
+ }
+
+ @Test
+ void allowsNewDecoderToDecodeMissingAsciiFieldBeforeGroupAsNullValue3()
+ {
+ final AddAsciiBeforeGroupV0Encoder encoder = new AddAsciiBeforeGroupV0Encoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(1).bCount(1).next().c(2);
+ encoder.checkEncodingIsComplete();
+
+ modifyHeaderToLookLikeVersion0();
+
+ final AddAsciiBeforeGroupV1Decoder decoder = new AddAsciiBeforeGroupV1Decoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(1));
+ final StringBuilder aValue = new StringBuilder();
+ assertThat(decoder.getD(aValue), equalTo(0));
+ assertThat(aValue.length(), equalTo(0));
+ final AddAsciiBeforeGroupV1Decoder.BDecoder b = decoder.b();
+ assertThat(b.count(), equalTo(1));
+ assertThat(b.next().c(), equalTo(2));
+ }
+
+ @Test
+ void allowsNewDecoderToDecodeMissingAsciiFieldBeforeGroupAsNullValue4()
+ {
+ final AddAsciiBeforeGroupV0Encoder encoder = new AddAsciiBeforeGroupV0Encoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(1).bCount(1).next().c(2);
+ encoder.checkEncodingIsComplete();
+
+ modifyHeaderToLookLikeVersion0();
+
+ final AddAsciiBeforeGroupV1Decoder decoder = new AddAsciiBeforeGroupV1Decoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(1));
+ assertThat(decoder.getD(new byte[6], 0), equalTo(0));
+ final AddAsciiBeforeGroupV1Decoder.BDecoder b = decoder.b();
+ assertThat(b.count(), equalTo(1));
+ assertThat(b.next().c(), equalTo(2));
+ }
+
+ @Test
+ void allowsNewDecoderToSkipPresentButAddedAsciiFieldBeforeGroup()
+ {
+ final AddAsciiBeforeGroupV1Encoder encoder = new AddAsciiBeforeGroupV1Encoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(1)
+ .d("EURUSD")
+ .bCount(1)
+ .next().c(2);
+ encoder.checkEncodingIsComplete();
+
+ final AddAsciiBeforeGroupV1Decoder decoder = new AddAsciiBeforeGroupV1Decoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(1));
+ final AddAsciiBeforeGroupV1Decoder.BDecoder b = decoder.b();
+ assertThat(b.count(), equalTo(1));
+ assertThat(b.next().c(), equalTo(2));
+ }
+
+ @Test
+ void allowsOldDecoderToSkipAddedAsciiFieldBeforeGroup()
+ {
+ final AddAsciiBeforeGroupV1Encoder encoder = new AddAsciiBeforeGroupV1Encoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(1)
+ .d("EURUSD")
+ .bCount(1)
+ .next().c(2);
+ encoder.checkEncodingIsComplete();
+
+ modifyHeaderToLookLikeVersion1();
+
+ final AddAsciiBeforeGroupV0Decoder decoder = new AddAsciiBeforeGroupV0Decoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(1));
+ final AddAsciiBeforeGroupV0Decoder.BDecoder b = decoder.b();
+ assertThat(b.count(), equalTo(1));
+ assertThat(b.next().c(), equalTo(2));
+ }
+
+ @Test
+ void allowsEncodeAndDecodeOfMessagesWithNoBlock()
+ {
+ final NoBlockEncoder encoder = new NoBlockEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a("abc");
+ encoder.checkEncodingIsComplete();
+
+ final NoBlockDecoder decoder = new NoBlockDecoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo("abc"));
+ }
+
+ @Test
+ void allowsEncodeAndDecodeOfGroupsWithNoBlock()
+ {
+ final GroupWithNoBlockEncoder encoder = new GroupWithNoBlockEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.aCount(1).next().b("abc");
+ encoder.checkEncodingIsComplete();
+
+ final GroupWithNoBlockDecoder decoder = new GroupWithNoBlockDecoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ final GroupWithNoBlockDecoder.ADecoder a = decoder.a();
+ assertThat(a.count(), equalTo(1));
+ assertThat(a.next().b(), equalTo("abc"));
+ }
+
+ @Test
+ void disallowsEncodingElementOfEmptyGroup1()
+ {
+ final MultipleGroupsEncoder encoder = new MultipleGroupsEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(42);
+ final MultipleGroupsEncoder.BEncoder bEncoder = encoder.bCount(0);
+ encoder.dCount(1).next().e(43);
+ final IllegalStateException exception =
+ assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> bEncoder.c(44));
+ assertThat(exception.getMessage(), containsString("Cannot access field \"b.c\" in state: V0_D_1_BLOCK"));
+ }
+
+ @Test
+ void disallowsEncodingElementOfEmptyGroup2()
+ {
+ final NestedGroupsEncoder encoder = new NestedGroupsEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(42);
+ final NestedGroupsEncoder.BEncoder bEncoder = encoder.bCount(1);
+ bEncoder.next();
+ bEncoder.c(43);
+ final NestedGroupsEncoder.BEncoder.DEncoder dEncoder = bEncoder.dCount(0);
+ bEncoder.fCount(0);
+ encoder.hCount(0);
+ final IllegalStateException exception =
+ assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> dEncoder.e(44));
+ assertThat(exception.getMessage(), containsString("Cannot access field \"b.d.e\" in state: V0_H_DONE"));
+ }
+
+ @Test
+ void disallowsEncodingElementOfEmptyGroup3()
+ {
+ final NestedGroupsEncoder encoder = new NestedGroupsEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(42);
+ final NestedGroupsEncoder.BEncoder bEncoder = encoder.bCount(1);
+ bEncoder.next();
+ bEncoder.c(43);
+ final NestedGroupsEncoder.BEncoder.DEncoder dEncoder = bEncoder.dCount(0);
+ bEncoder.fCount(0);
+ final IllegalStateException exception =
+ assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> dEncoder.e(44));
+ assertThat(exception.getMessage(), containsString("Cannot access field \"b.d.e\" in state: V0_B_1_F_DONE"));
+ }
+
+ @Test
+ void disallowsEncodingElementOfEmptyGroup4()
+ {
+ final AddPrimitiveInsideGroupV1Encoder encoder = new AddPrimitiveInsideGroupV1Encoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(42);
+ final AddPrimitiveInsideGroupV1Encoder.BEncoder bEncoder = encoder.bCount(0);
+ final IllegalStateException exception =
+ assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> bEncoder.c(43));
+ assertThat(exception.getMessage(), containsString("Cannot access field \"b.c\" in state: V1_B_DONE"));
+ }
+
+ @Test
+ void disallowsEncodingElementOfEmptyGroup5()
+ {
+ final GroupAndVarLengthEncoder encoder = new GroupAndVarLengthEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(42);
+ final GroupAndVarLengthEncoder.BEncoder bEncoder = encoder.bCount(0);
+ encoder.d("abc");
+ final IllegalStateException exception =
+ assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, () -> bEncoder.c(43));
+ assertThat(exception.getMessage(), containsString("Cannot access field \"b.c\" in state: V0_D_DONE"));
+ }
+
+ @Test
+ void allowsEncodingAndDecodingNestedGroupWithVarDataInSchemaDefinedOrder()
+ {
+ final NestedGroupWithVarLengthEncoder encoder = new NestedGroupWithVarLengthEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(1);
+ final NestedGroupWithVarLengthEncoder.BEncoder bEncoder = encoder.bCount(3);
+ bEncoder.next().c(2).dCount(0);
+ bEncoder.next().c(3).dCount(1).next().e(4).f("abc");
+ bEncoder.next().c(5).dCount(2).next().e(6).f("def").next().e(7).f("ghi");
+ encoder.checkEncodingIsComplete();
+
+ final NestedGroupWithVarLengthDecoder decoder = new NestedGroupWithVarLengthDecoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(1));
+ final NestedGroupWithVarLengthDecoder.BDecoder bDecoder = decoder.b();
+ assertThat(bDecoder.count(), equalTo(3));
+ assertThat(bDecoder.next().c(), equalTo(2));
+ assertThat(bDecoder.d().count(), equalTo(0));
+ assertThat(bDecoder.next().c(), equalTo(3));
+ final NestedGroupWithVarLengthDecoder.BDecoder.DDecoder dDecoder = bDecoder.d();
+ assertThat(dDecoder.count(), equalTo(1));
+ assertThat(dDecoder.next().e(), equalTo(4));
+ assertThat(dDecoder.f(), equalTo("abc"));
+ assertThat(bDecoder.next().c(), equalTo(5));
+ assertThat(bDecoder.d(), sameInstance(dDecoder));
+ assertThat(dDecoder.count(), equalTo(2));
+ assertThat(dDecoder.next().e(), equalTo(6));
+ assertThat(dDecoder.f(), equalTo("def"));
+ assertThat(dDecoder.next().e(), equalTo(7));
+ assertThat(dDecoder.f(), equalTo("ghi"));
+ }
+
+ @CsvSource(value = {
+ "1,V0_B_1_D_N_BLOCK",
+ "2,V0_B_N_D_N_BLOCK"
+ })
+ @ParameterizedTest
+ void disallowsMissedEncodingOfVarLengthFieldInNestedGroupToNextInnerElement(
+ final int bCount,
+ final String expectedState)
+ {
+ final NestedGroupWithVarLengthEncoder encoder = new NestedGroupWithVarLengthEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(1);
+ final NestedGroupWithVarLengthEncoder.BEncoder bEncoder = encoder.bCount(bCount);
+ final NestedGroupWithVarLengthEncoder.BEncoder.DEncoder dEncoder = bEncoder.next().c(5).dCount(2);
+ dEncoder.next().e(7);
+ final IllegalStateException exception =
+ assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, dEncoder::next);
+ assertThat(exception.getMessage(),
+ containsString("Cannot access next element in repeating group \"b.d\" in state: " + expectedState));
+ assertThat(exception.getMessage(),
+ containsString("Expected one of these transitions: [\"b.d.e(?)\", \"b.d.fLength()\", \"b.d.f(?)\"]."));
+ }
+
+ @CsvSource(value = {
+ "1,V0_B_N_D_1_BLOCK",
+ "2,V0_B_N_D_N_BLOCK",
+ })
+ @ParameterizedTest
+ void disallowsMissedEncodingOfVarLengthFieldInNestedGroupToNextOuterElement(
+ final int dCount,
+ final String expectedState)
+ {
+ final NestedGroupWithVarLengthEncoder encoder = new NestedGroupWithVarLengthEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(1);
+ final NestedGroupWithVarLengthEncoder.BEncoder bEncoder = encoder.bCount(2);
+ bEncoder.next().c(3).dCount(dCount).next().e(4);
+ final IllegalStateException exception =
+ assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, bEncoder::next);
+ assertThat(exception.getMessage(),
+ containsString("Cannot access next element in repeating group \"b\" in state: " + expectedState));
+ assertThat(exception.getMessage(),
+ containsString("Expected one of these transitions: [\"b.d.e(?)\", \"b.d.fLength()\", \"b.d.f(?)\"]."));
+ }
+
+ @Test
+ void disallowsMissedDecodingOfVarLengthFieldInNestedGroupToNextInnerElement1()
+ {
+ final NestedGroupWithVarLengthEncoder encoder = new NestedGroupWithVarLengthEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(1);
+ final NestedGroupWithVarLengthEncoder.BEncoder bEncoder = encoder.bCount(1);
+ bEncoder.next().c(2).dCount(2).next().e(3).f("abc").next().e(4).f("def");
+ encoder.checkEncodingIsComplete();
+
+ final NestedGroupWithVarLengthDecoder decoder = new NestedGroupWithVarLengthDecoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(1));
+ final NestedGroupWithVarLengthDecoder.BDecoder bDecoder = decoder.b();
+ assertThat(bDecoder.count(), equalTo(1));
+ assertThat(bDecoder.next().c(), equalTo(2));
+ final NestedGroupWithVarLengthDecoder.BDecoder.DDecoder dDecoder = bDecoder.d();
+ assertThat(dDecoder.count(), equalTo(2));
+ assertThat(dDecoder.next().e(), equalTo(3));
+ final Exception exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, dDecoder::next);
+ assertThat(exception.getMessage(),
+ containsString("Cannot access next element in repeating group \"b.d\" in state: V0_B_1_D_N_BLOCK."));
+ assertThat(exception.getMessage(),
+ containsString("Expected one of these transitions: [\"b.d.e(?)\", \"b.d.fLength()\", \"b.d.f(?)\"]."));
+ }
+
+ @Test
+ void disallowsMissedDecodingOfVarLengthFieldInNestedGroupToNextInnerElement2()
+ {
+ final NestedGroupWithVarLengthEncoder encoder = new NestedGroupWithVarLengthEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(1);
+ final NestedGroupWithVarLengthEncoder.BEncoder bEncoder = encoder.bCount(2);
+ bEncoder.next().c(2).dCount(2).next().e(3).f("abc").next().e(4).f("def");
+ bEncoder.next().c(5).dCount(0);
+ encoder.checkEncodingIsComplete();
+
+ final NestedGroupWithVarLengthDecoder decoder = new NestedGroupWithVarLengthDecoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(1));
+ final NestedGroupWithVarLengthDecoder.BDecoder bDecoder = decoder.b();
+ assertThat(bDecoder.count(), equalTo(2));
+ assertThat(bDecoder.next().c(), equalTo(2));
+ final NestedGroupWithVarLengthDecoder.BDecoder.DDecoder dDecoder = bDecoder.d();
+ assertThat(dDecoder.count(), equalTo(2));
+ assertThat(dDecoder.next().e(), equalTo(3));
+ final Exception exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, dDecoder::next);
+ assertThat(exception.getMessage(),
+ containsString("Cannot access next element in repeating group \"b.d\" in state: V0_B_N_D_N_BLOCK."));
+ assertThat(exception.getMessage(),
+ containsString("Expected one of these transitions: [\"b.d.e(?)\", \"b.d.fLength()\", \"b.d.f(?)\"]."));
+ }
+
+ @Test
+ void disallowsMissedDecodingOfVarLengthFieldInNestedGroupToNextOuterElement1()
+ {
+ final NestedGroupWithVarLengthEncoder encoder = new NestedGroupWithVarLengthEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(1);
+ final NestedGroupWithVarLengthEncoder.BEncoder bEncoder = encoder.bCount(2);
+ bEncoder.next().c(2).dCount(2).next().e(3).f("abc").next().e(4).f("def");
+ bEncoder.next().c(5).dCount(0);
+ encoder.checkEncodingIsComplete();
+
+ final NestedGroupWithVarLengthDecoder decoder = new NestedGroupWithVarLengthDecoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(1));
+ final NestedGroupWithVarLengthDecoder.BDecoder bDecoder = decoder.b();
+ assertThat(bDecoder.count(), equalTo(2));
+ assertThat(bDecoder.next().c(), equalTo(2));
+ final NestedGroupWithVarLengthDecoder.BDecoder.DDecoder dDecoder = bDecoder.d();
+ assertThat(dDecoder.count(), equalTo(2));
+ assertThat(dDecoder.next().e(), equalTo(3));
+ final Exception exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, bDecoder::next);
+ assertThat(exception.getMessage(),
+ containsString("Cannot access next element in repeating group \"b\" in state: V0_B_N_D_N_BLOCK."));
+ assertThat(exception.getMessage(),
+ containsString("Expected one of these transitions: [\"b.d.e(?)\", \"b.d.fLength()\", \"b.d.f(?)\"]."));
+ }
+
+ @Test
+ void disallowsMissedDecodingOfVarLengthFieldInNestedGroupToNextOuterElement2()
+ {
+ final NestedGroupWithVarLengthEncoder encoder = new NestedGroupWithVarLengthEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(1);
+ final NestedGroupWithVarLengthEncoder.BEncoder bEncoder = encoder.bCount(2);
+ bEncoder.next().c(2).dCount(1).next().e(3).f("abc");
+ bEncoder.next().c(5).dCount(0);
+ encoder.checkEncodingIsComplete();
+
+ final NestedGroupWithVarLengthDecoder decoder = new NestedGroupWithVarLengthDecoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderDecoder);
+ assertThat(decoder.a(), equalTo(1));
+ final NestedGroupWithVarLengthDecoder.BDecoder bDecoder = decoder.b();
+ assertThat(bDecoder.count(), equalTo(2));
+ assertThat(bDecoder.next().c(), equalTo(2));
+ final NestedGroupWithVarLengthDecoder.BDecoder.DDecoder dDecoder = bDecoder.d();
+ assertThat(dDecoder.count(), equalTo(1));
+ assertThat(dDecoder.next().e(), equalTo(3));
+ final Exception exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, bDecoder::next);
+ assertThat(exception.getMessage(),
+ containsString("Cannot access next element in repeating group \"b\" in state: V0_B_N_D_1_BLOCK."));
+ assertThat(exception.getMessage(),
+ containsString("Expected one of these transitions: [\"b.d.e(?)\", \"b.d.fLength()\", \"b.d.f(?)\"]."));
+ }
+
+ @Test
+ void disallowsIncompleteMessagesDueToMissingVarLengthField1()
+ {
+ final MultipleVarLengthEncoder encoder = new MultipleVarLengthEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(1).b("abc");
+ final Exception exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, encoder::checkEncodingIsComplete);
+ assertThat(exception.getMessage(), containsString(
+ "Not fully encoded, current state: V0_B_DONE, allowed transitions: \"cLength()\", \"c(?)\""));
+ }
+
+ @Test
+ void disallowsIncompleteMessagesDueToMissingVarLengthField2()
+ {
+ final NoBlockEncoder encoder = new NoBlockEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ final Exception exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, encoder::checkEncodingIsComplete);
+ assertThat(exception.getMessage(), containsString(
+ "Not fully encoded, current state: V0_BLOCK, allowed transitions: \"aLength()\", \"a(?)\""));
+ }
+
+ @Test
+ void disallowsIncompleteMessagesDueToMissingTopLevelGroup1()
+ {
+ final MultipleGroupsEncoder encoder = new MultipleGroupsEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(1).bCount(0);
+ final Exception exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, encoder::checkEncodingIsComplete);
+ assertThat(exception.getMessage(), containsString(
+ "Not fully encoded, current state: V0_B_DONE, allowed transitions: " +
+ "\"b.resetCountToIndex()\", \"dCount(0)\", \"dCount(>0)\""));
+ }
+
+ @Test
+ void disallowsIncompleteMessagesDueToMissingTopLevelGroup2()
+ {
+ final MultipleGroupsEncoder encoder = new MultipleGroupsEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(1).bCount(1).next().c(2);
+ final Exception exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, encoder::checkEncodingIsComplete);
+ assertThat(exception.getMessage(), containsString(
+ "Not fully encoded, current state: V0_B_1_BLOCK, allowed transitions: " +
+ "\"b.c(?)\", \"b.resetCountToIndex()\", \"dCount(0)\", \"dCount(>0)\""));
+ }
+
+ @Test
+ void disallowsIncompleteMessagesDueToMissingTopLevelGroup3()
+ {
+ final MultipleGroupsEncoder encoder = new MultipleGroupsEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(1);
+ final Exception exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, encoder::checkEncodingIsComplete);
+ assertThat(exception.getMessage(), containsString(
+ "Not fully encoded, current state: V0_BLOCK, allowed transitions: " +
+ "\"a(?)\", \"bCount(0)\", \"bCount(>0)\""));
+ }
+
+ @CsvSource(value = {
+ "1,V0_B_1_BLOCK",
+ "2,V0_B_N_BLOCK",
+ })
+ @ParameterizedTest
+ void disallowsIncompleteMessagesDueToMissingNestedGroup1(final int bCount, final String expectedState)
+ {
+ final NestedGroupWithVarLengthEncoder encoder = new NestedGroupWithVarLengthEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(1).bCount(bCount).next().c(2);
+ final Exception exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, encoder::checkEncodingIsComplete);
+ assertThat(exception.getMessage(), containsString("Not fully encoded, current state: " + expectedState));
+ }
+
+ @CsvSource(value = {
+ "1,1,V0_B_1_D_N",
+ "1,2,V0_B_1_D_N",
+ "2,0,V0_B_N_D_DONE",
+ "2,1,V0_B_N_D_N",
+ "2,2,V0_B_N_D_N",
+ })
+ @ParameterizedTest
+ void disallowsIncompleteMessagesDueToMissingNestedGroup2(
+ final int bCount,
+ final int dCount,
+ final String expectedState)
+ {
+ final NestedGroupWithVarLengthEncoder encoder = new NestedGroupWithVarLengthEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(1).bCount(bCount).next().c(2).dCount(dCount);
+ final Exception exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, encoder::checkEncodingIsComplete);
+ assertThat(exception.getMessage(), containsString("Not fully encoded, current state: " + expectedState));
+ }
+
+ @CsvSource(value = {
+ "1,1,V0_B_1_D_1_BLOCK",
+ "1,2,V0_B_1_D_N_BLOCK",
+ "2,1,V0_B_N_D_1_BLOCK",
+ "2,2,V0_B_N_D_N_BLOCK",
+ })
+ @ParameterizedTest
+ void disallowsIncompleteMessagesDueToMissingVarDataInNestedGroup(
+ final int bCount,
+ final int dCount,
+ final String expectedState)
+ {
+ final NestedGroupWithVarLengthEncoder encoder = new NestedGroupWithVarLengthEncoder()
+ .wrapAndApplyHeader(buffer, OFFSET, messageHeaderEncoder);
+ encoder.a(1).bCount(bCount).next().c(2).dCount(dCount).next().e(10);
+ final Exception exception = assertThrows(INCORRECT_ORDER_EXCEPTION_CLASS, encoder::checkEncodingIsComplete);
+ assertThat(exception.getMessage(), containsString("Not fully encoded, current state: " + expectedState));
+ }
+
+ private void modifyHeaderToLookLikeVersion0()
+ {
+ messageHeaderDecoder.wrap(buffer, OFFSET);
+ final int v1TemplateId = messageHeaderDecoder.templateId() + 1_000;
+ messageHeaderEncoder.wrap(buffer, OFFSET);
+ messageHeaderEncoder.templateId(v1TemplateId).version(0);
+ }
+
+ private void modifyHeaderToLookLikeVersion1()
+ {
+ messageHeaderDecoder.wrap(buffer, OFFSET);
+ assert messageHeaderDecoder.version() == 1;
+ final int v0TemplateId = messageHeaderDecoder.templateId() - 1_000;
+ messageHeaderEncoder.wrap(buffer, OFFSET);
+ messageHeaderEncoder.templateId(v0TemplateId);
+ }
+}
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 f12fef3751..3f253ed778 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
@@ -15,6 +15,12 @@
*/
package uk.co.real_logic.sbe.generation.java;
+import uk.co.real_logic.sbe.Tests;
+import uk.co.real_logic.sbe.generation.common.PrecedenceChecks;
+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 org.agrona.DirectBuffer;
import org.agrona.MutableDirectBuffer;
import org.agrona.concurrent.UnsafeBuffer;
@@ -22,23 +28,23 @@
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 org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
import java.io.IOException;
import java.io.InputStream;
+import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.nio.ByteOrder;
+import java.util.Arrays;
import java.util.Map;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.startsWith;
import static org.hamcrest.Matchers.*;
import static org.junit.jupiter.api.Assertions.*;
-import static org.mockito.Mockito.*;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import static uk.co.real_logic.sbe.generation.java.JavaGenerator.MESSAGE_HEADER_DECODER_TYPE;
import static uk.co.real_logic.sbe.generation.java.ReflectionUtil.*;
import static uk.co.real_logic.sbe.xml.XmlSchemaParser.parse;
@@ -251,6 +257,37 @@ void shouldGenerateBasicMessage() throws Exception
assertThat(msgFlyweight.toString(), startsWith("[Car]"));
}
+ @Test
+ void shouldGenerateWithoutPrecedenceChecksByDefault() throws Exception
+ {
+ final PrecedenceChecks.Context context = new PrecedenceChecks.Context();
+ final PrecedenceChecks precedenceChecks = PrecedenceChecks.newInstance(context);
+ generator(precedenceChecks).generate();
+
+ final Field field = Arrays.stream(compileCarEncoder().getDeclaredFields())
+ .filter(f -> f.getName().equals(context.precedenceChecksFlagName()))
+ .findFirst()
+ .orElse(null);
+
+ assertNull(field);
+ }
+
+ @Test
+ void shouldGeneratePrecedenceChecksWhenEnabled() throws Exception
+ {
+ final PrecedenceChecks.Context context = new PrecedenceChecks.Context()
+ .shouldGeneratePrecedenceChecks(true);
+ final PrecedenceChecks precedenceChecks = PrecedenceChecks.newInstance(context);
+ generator(precedenceChecks).generate();
+
+ final Field field = Arrays.stream(compileCarEncoder().getDeclaredFields())
+ .filter(f -> f.getName().equals(context.precedenceChecksFlagName()))
+ .findFirst()
+ .orElse(null);
+
+ assertNotNull(field);
+ }
+
@Test
void shouldGenerateRepeatingGroupDecoder() throws Exception
{
@@ -298,8 +335,12 @@ void shouldGenerateVarDataCodecs() throws Exception
final Object encoder = wrap(buffer, compileCarEncoder().getConstructor().newInstance());
final Object decoder = getCarDecoder(buffer, encoder);
+ setEmptyFuelFiguresGroup(encoder);
+ setEmptyPerformanceFiguresGroup(encoder);
setManufacturer(encoder, expectedManufacturer);
+ skipFuelFiguresGroup(decoder);
+ skipPerformanceFiguresGroup(decoder);
final String manufacturer = getManufacturer(decoder);
assertEquals(expectedManufacturer, manufacturer);
@@ -402,28 +443,30 @@ void shouldGenerateGetFixedLengthStringUsingAppendable() throws Exception
assertThat(result.toString(), is("R11R12"));
}
- @Test
- void shouldGenerateGetVariableStringUsingAppendable() throws Exception
+ @ParameterizedTest
+ @ValueSource(strings = {"Red", "", "Red and Blue"})
+ void shouldGenerateGetVariableStringUsingAppendable(final String color) throws Exception
{
final UnsafeBuffer buffer = new UnsafeBuffer(new byte[4096]);
final StringBuilder result = new StringBuilder();
generator().generate();
final Object encoder = wrap(buffer, compileCarEncoder().getDeclaredConstructor().newInstance());
- final Object decoder = getCarDecoder(buffer, encoder);
- set(encoder, "color", String.class, "Red");
- get(decoder, "color", result);
- assertThat(result.toString(), is("Red"));
+ setEmptyFuelFiguresGroup(encoder);
+ setEmptyPerformanceFiguresGroup(encoder);
+ set(encoder, "manufacturer", String.class, "Bristol");
+ set(encoder, "model", String.class, "Britannia");
+ set(encoder, "activationCode", String.class, "12345");
+ set(encoder, "color", String.class, color);
- result.setLength(0);
- set(encoder, "color", String.class, "");
- get(decoder, "color", result);
- assertThat(result.toString(), is(""));
-
- result.setLength(0);
- set(encoder, "color", String.class, "Red and Blue");
- get(decoder, "color", result);
- assertThat(result.toString(), is("Red and Blue"));
+ final Object decoder = getCarDecoder(buffer, encoder);
+ skipFuelFiguresGroup(decoder);
+ skipPerformanceFiguresGroup(decoder);
+ assertThat(get(decoder, "manufacturer"), equalTo("Bristol"));
+ assertThat(get(decoder, "model"), equalTo("Britannia"));
+ assertThat(get(decoder, "activationCode"), equalTo("12345"));
+ assertThat(get(decoder, "color", result), equalTo(color.length()));
+ assertThat(result.toString(), equalTo(color));
}
@Test
@@ -664,6 +707,12 @@ private JavaGenerator generator()
return new JavaGenerator(ir, BUFFER_NAME, READ_ONLY_BUFFER_NAME, false, false, false, outputManager);
}
+ private JavaGenerator generator(final PrecedenceChecks precedenceChecks)
+ {
+ return new JavaGenerator(ir, BUFFER_NAME, READ_ONLY_BUFFER_NAME, false, false, false, false,
+ precedenceChecks, outputManager);
+ }
+
private void generateTypeStubs() throws IOException
{
final JavaGenerator javaGenerator = generator();
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 a48074eb89..beead1799e 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
@@ -41,10 +41,30 @@ static Object get(final Object object, final String fieldName) throws Exception
return object.getClass().getMethod(fieldName).invoke(object);
}
- static void get(final Object object, final String fieldName, final Appendable arg) throws Exception
+ static Object get(final Object object, final String fieldName, final Appendable arg) throws Exception
{
final String methodName = "get" + Generators.toUpperFirstChar(fieldName);
- object.getClass().getMethod(methodName, Appendable.class).invoke(object, arg);
+ return object.getClass().getMethod(methodName, Appendable.class).invoke(object, arg);
+ }
+
+ static void skipFuelFiguresGroup(final Object decoder) throws Exception
+ {
+ skipGroup(decoder, "fuelFigures");
+ }
+
+ static void skipPerformanceFiguresGroup(final Object decoder) throws Exception
+ {
+ skipGroup(decoder, "performanceFigures");
+ }
+
+ private static void skipGroup(final Object decoder, final String groupName) throws Exception
+ {
+ final Object group = get(decoder, groupName);
+ while ((boolean)get(group, "hasNext"))
+ {
+ get(group, "next");
+ get(group, "sbeSkip");
+ }
}
static String getManufacturer(final Object decoder) throws Exception
@@ -52,6 +72,16 @@ static String getManufacturer(final Object decoder) throws Exception
return (String)get(decoder, "manufacturer");
}
+ static void setEmptyFuelFiguresGroup(final Object encoder) throws Exception
+ {
+ encoder.getClass().getMethod("fuelFiguresCount", int.class).invoke(encoder, 0);
+ }
+
+ static void setEmptyPerformanceFiguresGroup(final Object encoder) throws Exception
+ {
+ encoder.getClass().getMethod("performanceFiguresCount", int.class).invoke(encoder, 0);
+ }
+
static void setManufacturer(final Object encoder, final String value) throws Exception
{
encoder.getClass().getMethod("manufacturer", String.class).invoke(encoder, value);
diff --git a/sbe-tool/src/test/resources/field-order-check-schema.xml b/sbe-tool/src/test/resources/field-order-check-schema.xml
new file mode 100644
index 0000000000..e4d62ebfd6
--- /dev/null
+++ b/sbe-tool/src/test/resources/field-order-check-schema.xml
@@ -0,0 +1,292 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+ 2
+
+
+
+ 1
+ 2
+ 4
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+