uk.co.real_logic.sbe.generation.cpp.CppGenerator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of sbe-all Show documentation
Show all versions of sbe-all Show documentation
FIX/SBE - OSI layer 6 presentation for encoding and decoding application messages in binary format for low-latency applications.
/*
* Copyright 2013-2024 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.cpp;
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.*;
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.cpp.CppUtil.*;
import static uk.co.real_logic.sbe.ir.GenerationUtil.*;
/**
* Codec generator for the C++11 programming language with conditional compilation for additional C++14 and C++17
* features.
*/
@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 Cpp language {@link CodeGenerator}.
*
* @param ir for the messages and types.
* @param shouldDecodeUnknownEnumValues generate support for unknown enum values when decoding.
* @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;
}
/**
* Generate the composites for dealing with the message header.
*
* @throws IOException if an error is encountered when writing the output.
*/
public void generateMessageHeaderStub() throws IOException
{
generateComposite(ir.headerStructure().tokens());
}
private List generateTypeStubs() throws IOException
{
final List typesToInclude = new ArrayList<>();
for (final List tokens : ir.types())
{
switch (tokens.get(0).signal())
{
case BEGIN_ENUM:
generateEnum(tokens);
break;
case BEGIN_SET:
generateChoiceSet(tokens);
break;
case BEGIN_COMPOSITE:
generateComposite(tokens);
break;
default:
break;
}
typesToInclude.add(tokens.get(0).applicableTypeName());
}
return typesToInclude;
}
private List generateTypesToIncludes(final List tokens)
{
final List typesToInclude = new ArrayList<>();
for (final Token token : tokens)
{
switch (token.signal())
{
case BEGIN_ENUM:
case BEGIN_SET:
case BEGIN_COMPOSITE:
typesToInclude.add(token.applicableTypeName());
break;
default:
break;
}
}
return typesToInclude;
}
/**
* {@inheritDoc}
*/
public void generate() throws IOException
{
generateMessageHeaderStub();
final List typesToInclude = generateTypeStubs();
for (final List tokens : ir.messages())
{
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))
{
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);
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, 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 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++)
{
final Token groupToken = tokens.get(i);
if (groupToken.signal() != Signal.BEGIN_GROUP)
{
throw new IllegalStateException("tokens must begin with BEGIN_GROUP: token=" + groupToken);
}
final String groupName = groupToken.name();
final Token numInGroupToken = Generators.findFirst("numInGroup", tokens, i);
final String cppTypeForNumInGroup = cppTypeName(numInGroupToken.encoding().primitiveType());
generateGroupClassHeader(sb, groupName, groupToken, tokens, fieldPrecedenceModel, i, indent + INDENT);
++i;
final int groupHeaderTokenCount = tokens.get(i).componentTokenCount();
i += groupHeaderTokenCount;
final List fields = new ArrayList<>();
i = collectFields(tokens, i, fields);
generateFields(sb, formatClassName(groupName), fields, fieldPrecedenceModel, indent + INDENT);
final List groups = new ArrayList<>();
i = collectGroups(tokens, i, groups);
generateGroups(sb, groups, fieldPrecedenceModel, indent + INDENT);
final List varData = new ArrayList<>();
i = collectVarData(tokens, i, varData);
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, fieldPrecedenceModel, 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();
final int blockLength = tokens.get(index).encodedLength();
final Token blockLengthToken = Generators.findFirst("blockLength", tokens, index);
final Token numInGroupToken = Generators.findFirst("numInGroup", tokens, index);
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" +
indent + "private:\n" +
indent + " char *m_buffer = nullptr;\n" +
indent + " std::uint64_t m_bufferLength = 0;\n" +
indent + " std::uint64_t m_initialPosition = 0;\n" +
indent + " std::uint64_t *m_positionPtr = nullptr;\n" +
indent + " std::uint64_t m_blockLength = 0;\n" +
indent + " std::uint64_t m_count = 0;\n" +
indent + " std::uint64_t m_index = 0;\n" +
indent + " std::uint64_t m_offset = 0;\n" +
indent + " std::uint64_t m_actingVersion = 0;\n\n" +
indent + " SBE_NODISCARD std::uint64_t *sbePositionPtr() SBE_NOEXCEPT\n" +
indent + " {\n" +
indent + " return m_positionPtr;\n" +
indent + " }\n\n",
groupClassName);
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%3$s" +
indent + " {\n" +
indent + " %2$s dimensions(buffer, *pos, bufferLength, actingVersion);\n" +
indent + " m_buffer = buffer;\n" +
indent + " m_bufferLength = bufferLength;\n" +
indent + " m_blockLength = dimensions.blockLength();\n" +
indent + " m_count = dimensions.numInGroup();\n" +
indent + " m_index = 0;\n" +
indent + " m_actingVersion = actingVersion;\n" +
indent + " m_initialPosition = *pos;\n" +
indent + " m_positionPtr = pos;\n" +
indent + " *m_positionPtr = *m_positionPtr + %1$d;\n" +
"%4$s" +
indent + " }\n",
dimensionHeaderLength,
dimensionsClassName,
codecStateParameter,
codecStateAssignment,
groupClassName);
final long minCount = numInGroupToken.encoding().applicableMinValue().longValue();
final String minCheck = minCount > 0 ? "count < " + minCount + " || " : "";
new Formatter(sb).format("\n" +
indent + " inline void wrapForEncode(\n" +
indent + " char *buffer,\n" +
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%8$s" +
indent + " {\n" +
indent + "#if defined(__GNUG__) && !defined(__clang__)\n" +
indent + "#pragma GCC diagnostic push\n" +
indent + "#pragma GCC diagnostic ignored \"-Wtype-limits\"\n" +
indent + "#endif\n" +
indent + " if (%5$scount > %6$d)\n" +
indent + " {\n" +
indent + " throw std::runtime_error(\"count outside of allowed range [E110]\");\n" +
indent + " }\n" +
indent + "#if defined(__GNUG__) && !defined(__clang__)\n" +
indent + "#pragma GCC diagnostic pop\n" +
indent + "#endif\n" +
indent + " m_buffer = buffer;\n" +
indent + " m_bufferLength = bufferLength;\n" +
indent + " %7$s dimensions(buffer, *pos, bufferLength, actingVersion);\n" +
indent + " dimensions.blockLength(static_cast<%1$s>(%2$d));\n" +
indent + " dimensions.numInGroup(static_cast<%3$s>(count));\n" +
indent + " m_index = 0;\n" +
indent + " m_count = count;\n" +
indent + " m_blockLength = %2$d;\n" +
indent + " m_actingVersion = actingVersion;\n" +
indent + " m_initialPosition = *pos;\n" +
indent + " m_positionPtr = pos;\n" +
indent + " *m_positionPtr = *m_positionPtr + %4$d;\n" +
"%9$s" +
indent + " }\n",
cppTypeBlockLength,
blockLength,
cppTypeNumInGroup,
dimensionHeaderLength,
minCheck,
numInGroupToken.encoding().applicableMaxValue().longValue(),
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" +
indent + " {\n" +
indent + " return %1$d;\n" +
indent + " }\n\n" +
indent + " static SBE_CONSTEXPR std::uint64_t sbeBlockLength() SBE_NOEXCEPT\n" +
indent + " {\n" +
indent + " return %2$d;\n" +
indent + " }\n\n" +
indent + " SBE_NODISCARD std::uint64_t sbeActingBlockLength() SBE_NOEXCEPT\n" +
indent + " {\n" +
indent + " return m_blockLength;\n" +
indent + " }\n\n" +
indent + " SBE_NODISCARD std::uint64_t sbePosition() const SBE_NOEXCEPT\n" +
indent + " {\n" +
indent + " return *m_positionPtr;\n" +
indent + " }\n\n" +
indent + " // NOLINTNEXTLINE(readability-convert-member-functions-to-static)\n" +
indent + " std::uint64_t sbeCheckPosition(const std::uint64_t position)\n" +
indent + " {\n" +
indent + " if (SBE_BOUNDS_CHECK_EXPECT((position > m_bufferLength), false))\n" +
indent + " {\n" +
indent + " throw std::runtime_error(\"buffer too short [E100]\");\n" +
indent + " }\n" +
indent + " return position;\n" +
indent + " }\n\n" +
indent + " void sbePosition(const std::uint64_t position)\n" +
indent + " {\n" +
indent + " *m_positionPtr = sbeCheckPosition(position);\n" +
indent + " }\n\n" +
indent + " SBE_NODISCARD inline std::uint64_t count() const SBE_NOEXCEPT\n" +
indent + " {\n" +
indent + " return m_count;\n" +
indent + " }\n\n" +
indent + " SBE_NODISCARD inline bool hasNext() const SBE_NOEXCEPT\n" +
indent + " {\n" +
indent + " return m_index < m_count;\n" +
indent + " }\n\n" +
indent + " inline %3$s &next()\n" +
indent + " {\n" +
indent + " if (m_index >= m_count)\n" +
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" +
indent + " throw std::runtime_error(\"buffer too short for next group index [E108]\");\n" +
indent + " }\n" +
indent + " *m_positionPtr = m_offset + m_blockLength;\n" +
indent + " ++m_index;\n\n" +
indent + " return *this;\n" +
indent + " }\n",
dimensionHeaderLength,
blockLength,
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")
.append(indent)
.append(" dimensions.numInGroup(static_cast<").append(cppTypeNumInGroup).append(">(m_count));\n")
.append(indent).append(" return m_count;\n")
.append(indent).append(" }\n");
sb.append("\n")
.append(indent).append(" template inline void forEach(Func &&func)\n")
.append(indent).append(" {\n")
.append(indent).append(" while (hasNext())\n")
.append(indent).append(" {\n")
.append(indent).append(" next();\n")
.append(indent).append(" func(*this);\n")
.append(indent).append(" }\n")
.append(indent).append(" }\n\n");
}
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);
final String propertyName = formatPropertyName(groupName);
new Formatter(sb).format("\n" +
"private:\n" +
indent + " %1$s m_%2$s;\n\n" +
"public:\n",
className,
propertyName);
new Formatter(sb).format(
indent + " SBE_NODISCARD static SBE_CONSTEXPR std::uint16_t %1$sId() SBE_NOEXCEPT\n" +
indent + " {\n" +
indent + " return %2$d;\n" +
indent + " }\n",
groupName,
token.id());
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%4$s);\n" +
"%5$s" +
indent + " return m_%2$s;\n" +
indent + " }\n",
className,
propertyName,
cppTypeForNumInGroup,
codecStateArgument,
onEncodeAccessOrderCall);
final int version = token.version();
final String versionCheck = 0 == version ?
" return true;\n" : " return m_actingVersion >= %1$sSinceVersion();\n";
new Formatter(sb).format("\n" +
indent + " SBE_NODISCARD static SBE_CONSTEXPR std::uint64_t %1$sSinceVersion() SBE_NOEXCEPT\n" +
indent + " {\n" +
indent + " return %2$d;\n" +
indent + " }\n\n" +
indent + " SBE_NODISCARD bool %1$sInActingVersion() const SBE_NOEXCEPT\n" +
indent + " {\n" +
indent + versionCheck +
indent + " }\n",
propertyName,
version);
}
private void generateVarData(
final StringBuilder sb,
final String className,
final List tokens,
final FieldPrecedenceModel fieldPrecedenceModel,
final String indent)
{
for (int i = 0, size = tokens.size(); i < size;)
{
final Token token = tokens.get(i);
if (token.signal() != Signal.BEGIN_VAR_DATA)
{
throw new IllegalStateException("tokens must begin with BEGIN_VAR_DATA: token=" + token);
}
final String propertyName = toUpperFirstChar(token.name());
final Token lengthToken = Generators.findFirst("length", tokens, i);
final Token varDataToken = Generators.findFirst("varData", tokens, i);
final String characterEncoding = varDataToken.encoding().characterEncoding();
final int lengthOfLengthField = lengthToken.encodedLength();
final String lengthCppType = cppTypeName(lengthToken.encoding().primitiveType());
final String lengthByteOrderStr = formatByteOrderEncoding(
lengthToken.encoding().byteOrder(), lengthToken.encoding().primitiveType());
generateFieldMetaAttributeMethod(sb, token, indent);
generateVarDataDescriptors(
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" +
indent + " std::memcpy(&lengthFieldValue, m_buffer + lengthPosition, sizeof(%5$s));\n" +
indent + " std::uint64_t dataLength = %4$s(lengthFieldValue);\n" +
indent + " sbePosition(lengthPosition + lengthOfLengthField + dataLength);\n" +
indent + " return dataLength;\n" +
indent + " }\n",
propertyName,
generateArrayFieldNotPresentCondition(token.version(), indent),
lengthOfLengthField,
lengthByteOrderStr,
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" +
indent + " sbePosition(sbePosition() + %3$d + %5$s(lengthFieldValue));\n" +
indent + " return fieldPtr;\n" +
indent + " }\n",
formatPropertyName(propertyName),
generateTypeFieldNotPresentCondition(token.version(), indent),
lengthOfLengthField,
lengthCppType,
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" +
indent + " %5$s lengthFieldValue;\n" +
indent + " std::memcpy(&lengthFieldValue, m_buffer + lengthPosition, sizeof(%5$s));\n" +
indent + " std::uint64_t dataLength = %4$s(lengthFieldValue);\n" +
indent + " std::uint64_t bytesToCopy = length < dataLength ? length : dataLength;\n" +
indent + " std::uint64_t pos = sbePosition();\n" +
indent + " sbePosition(pos + dataLength);\n" +
indent + " std::memcpy(dst, m_buffer + pos, static_cast(bytesToCopy));\n" +
indent + " return bytesToCopy;\n" +
indent + " }\n",
propertyName,
generateArrayFieldNotPresentCondition(token.version(), indent),
lengthOfLengthField,
lengthByteOrderStr,
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" +
indent + " sbePosition(lengthPosition + lengthOfLengthField);\n" +
indent + " std::memcpy(m_buffer + lengthPosition, &lengthFieldValue, sizeof(%3$s));\n" +
indent + " if (length != %3$s(0))\n" +
indent + " {\n" +
indent + " std::uint64_t pos = sbePosition();\n" +
indent + " sbePosition(pos + length);\n" +
indent + " std::memcpy(m_buffer + pos, src, length);\n" +
indent + " }\n" +
indent + " return *this;\n" +
indent + " }\n",
propertyName,
lengthOfLengthField,
lengthCppType,
lengthByteOrderStr,
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" +
indent + " %5$s lengthFieldValue;\n" +
indent + " std::memcpy(&lengthFieldValue, m_buffer + lengthPosition, sizeof(%5$s));\n" +
indent + " std::uint64_t dataLength = %4$s(lengthFieldValue);\n" +
indent + " std::uint64_t pos = sbePosition();\n" +
indent + " const std::string result(m_buffer + pos, dataLength);\n" +
indent + " sbePosition(pos + dataLength);\n" +
indent + " return result;\n" +
indent + " }\n",
propertyName,
generateStringNotPresentCondition(token.version(), indent),
lengthOfLengthField,
lengthByteOrderStr,
lengthCppType,
accessOrderListenerCall);
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" +
indent + " %5$s lengthFieldValue;\n" +
indent + " std::memcpy(&lengthFieldValue, m_buffer + lengthPosition, sizeof(%5$s));\n" +
indent + " std::uint64_t dataLength = %4$s(lengthFieldValue);\n" +
indent + " std::uint64_t pos = sbePosition();\n" +
indent + " const std::string_view result(m_buffer + pos, dataLength);\n" +
indent + " sbePosition(pos + dataLength);\n" +
indent + " return result;\n" +
indent + " }\n" +
indent + " #endif\n",
propertyName,
generateStringViewNotPresentCondition(token.version(), indent),
lengthOfLengthField,
lengthByteOrderStr,
lengthCppType,
accessOrderListenerCall);
new Formatter(sb).format("\n" +
indent + " %1$s &put%2$s(const std::string &str)\n" +
indent + " {\n" +
indent + " if (str.length() > %4$d)\n" +
indent + " {\n" +
indent + " throw std::runtime_error(\"std::string too long for length type [E109]\");\n" +
indent + " }\n" +
indent + " return put%2$s(str.data(), static_cast<%3$s>(str.length()));\n" +
indent + " }\n",
className,
propertyName,
lengthCppType,
lengthToken.encoding().applicableMaxValue().longValue());
new Formatter(sb).format("\n" +
indent + " #if __cplusplus >= 201703L\n" +
indent + " %1$s &put%2$s(const std::string_view str)\n" +
indent + " {\n" +
indent + " if (str.length() > %4$d)\n" +
indent + " {\n" +
indent + " throw std::runtime_error(\"std::string too long for length type [E109]\");\n" +
indent + " }\n" +
indent + " return put%2$s(str.data(), static_cast<%3$s>(str.length()));\n" +
indent + " }\n" +
indent + " #endif\n",
className,
propertyName,
lengthCppType,
lengthToken.encoding().applicableMaxValue().longValue());
i += token.componentTokenCount();
}
}
private void generateVarDataDescriptors(
final StringBuilder sb,
final Token token,
final String propertyName,
final String characterEncoding,
final Integer sizeOfLengthField,
final String indent)
{
new Formatter(sb).format("\n" +
indent + " static const char *%1$sCharacterEncoding() SBE_NOEXCEPT\n" +
indent + " {\n" +
indent + " return \"%2$s\";\n" +
indent + " }\n",
toLowerFirstChar(propertyName),
characterEncoding);
final int version = token.version();
final String versionCheck = 0 == version ?
" return true;\n" : " return m_actingVersion >= %1$sSinceVersion();\n";
new Formatter(sb).format("\n" +
indent + " static SBE_CONSTEXPR std::uint64_t %1$sSinceVersion() SBE_NOEXCEPT\n" +
indent + " {\n" +
indent + " return %2$d;\n" +
indent + " }\n\n" +
indent + " bool %1$sInActingVersion() SBE_NOEXCEPT\n" +
indent + " {\n" +
indent + versionCheck +
indent + " }\n\n" +
indent + " static SBE_CONSTEXPR std::uint16_t %1$sId() SBE_NOEXCEPT\n" +
indent + " {\n" +
indent + " return %3$d;\n" +
indent + " }\n",
toLowerFirstChar(propertyName),
version,
token.id());
new Formatter(sb).format("\n" +
indent + " static SBE_CONSTEXPR std::uint64_t %sHeaderLength() SBE_NOEXCEPT\n" +
indent + " {\n" +
indent + " return %d;\n" +
indent + " }\n",
toLowerFirstChar(propertyName),
sizeOfLengthField);
}
private void generateChoiceSet(final List tokens) throws IOException
{
final Token token = tokens.get(0);
final String bitSetName = formatClassName(token.applicableTypeName());
try (Writer out = outputManager.createOutput(bitSetName))
{
out.append(generateFileHeader(ir.namespaces(), bitSetName, null));
out.append(generateClassDeclaration(bitSetName));
out.append(generateFixedFlyweightCode(bitSetName, token.encodedLength()));
final Encoding encoding = token.encoding();
new Formatter(out).format("\n" +
" %1$s &clear()\n" +
" {\n" +
" %2$s zero = 0;\n" +
" std::memcpy(m_buffer + m_offset, &zero, sizeof(%2$s));\n" +
" return *this;\n" +
" }\n",
bitSetName,
cppTypeName(encoding.primitiveType()));
new Formatter(out).format("\n" +
" SBE_NODISCARD bool isEmpty() const\n" +
" {\n" +
" %1$s val;\n" +
" std::memcpy(&val, m_buffer + m_offset, sizeof(%1$s));\n" +
" return 0 == val;\n" +
" }\n",
cppTypeName(encoding.primitiveType()));
new Formatter(out).format("\n" +
" SBE_NODISCARD %1$s rawValue() const\n" +
" {\n" +
" %1$s val;\n" +
" std::memcpy(&val, m_buffer + m_offset, sizeof(%1$s));\n" +
" return val;\n" +
" }\n",
cppTypeName(encoding.primitiveType()));
new Formatter(out).format("\n" +
" %1$s &rawValue(%2$s value)\n" +
" {\n" +
" std::memcpy(m_buffer + m_offset, &value, sizeof(%2$s));\n" +
" return *this;\n" +
" }\n",
bitSetName,
cppTypeName(encoding.primitiveType()));
out.append(generateChoices(bitSetName, tokens.subList(1, tokens.size() - 1)));
out.append(generateChoicesDisplay(bitSetName, tokens.subList(1, tokens.size() - 1)));
out.append("};\n");
out.append(CppUtil.closingBraces(ir.namespaces().length)).append("#endif\n");
}
}
private void generateEnum(final List tokens) throws IOException
{
final Token enumToken = tokens.get(0);
final String enumName = formatClassName(tokens.get(0).applicableTypeName());
try (Writer out = outputManager.createOutput(enumName))
{
out.append(generateEnumFileHeader(ir.namespaces(), enumName));
out.append(generateEnumDeclaration(enumName));
out.append(generateEnumValues(tokens.subList(1, tokens.size() - 1), enumToken));
out.append(generateEnumLookupMethod(tokens.subList(1, tokens.size() - 1), enumToken));
out.append(generateEnumDisplay(tokens.subList(1, tokens.size() - 1), enumToken));
out.append("};\n\n");
out.append(CppUtil.closingBraces(ir.namespaces().length)).append("\n#endif\n");
}
}
private void generateComposite(final List tokens) throws IOException
{
final String compositeName = formatClassName(tokens.get(0).applicableTypeName());
try (Writer out = outputManager.createOutput(compositeName))
{
out.append(generateFileHeader(ir.namespaces(), compositeName,
generateTypesToIncludes(tokens.subList(1, tokens.size() - 1))));
out.append(generateClassDeclaration(compositeName));
out.append(generateFixedFlyweightCode(compositeName, tokens.get(0).encodedLength()));
out.append(generateCompositePropertyElements(
compositeName, tokens.subList(1, tokens.size() - 1), BASE_INDENT));
out.append(generateCompositeDisplay(
tokens.get(0).applicableTypeName(), tokens.subList(1, tokens.size() - 1)));
out.append("};\n\n");
out.append(CppUtil.closingBraces(ir.namespaces().length)).append("\n#endif\n");
}
}
private static CharSequence generateChoiceNotPresentCondition(final int sinceVersion)
{
if (0 == sinceVersion)
{
return "";
}
return String.format(
" if (m_actingVersion < %1$d)\n" +
" {\n" +
" return false;\n" +
" }\n\n",
sinceVersion);
}
private CharSequence generateChoices(final String bitsetClassName, final List tokens)
{
final StringBuilder sb = new StringBuilder();
tokens
.stream()
.filter((token) -> token.signal() == Signal.CHOICE)
.forEach((token) ->
{
final String choiceName = formatPropertyName(token.name());
final PrimitiveType type = token.encoding().primitiveType();
final String typeName = cppTypeName(type);
final String choiceBitPosition = token.encoding().constValue().toString();
final String byteOrderStr = formatByteOrderEncoding(token.encoding().byteOrder(), type);
final CharSequence constantOne = generateLiteral(type, "1");
new Formatter(sb).format("\n" +
" static bool %1$s(const %2$s bits)\n" +
" {\n" +
" return (bits & (%4$s << %3$su)) != 0;\n" +
" }\n",
choiceName,
typeName,
choiceBitPosition,
constantOne);
new Formatter(sb).format("\n" +
" static %2$s %1$s(const %2$s bits, const bool value)\n" +
" {\n" +
" return value ?" +
" static_cast<%2$s>(bits | (%4$s << %3$su)) : static_cast<%2$s>(bits & ~(%4$s << %3$su));\n" +
" }\n",
choiceName,
typeName,
choiceBitPosition,
constantOne);
new Formatter(sb).format("\n" +
" SBE_NODISCARD bool %1$s() const\n" +
" {\n" +
"%2$s" +
" %4$s val;\n" +
" std::memcpy(&val, m_buffer + m_offset, sizeof(%4$s));\n" +
" return (%3$s(val) & (%6$s << %5$su)) != 0;\n" +
" }\n",
choiceName,
generateChoiceNotPresentCondition(token.version()),
byteOrderStr,
typeName,
choiceBitPosition,
constantOne);
new Formatter(sb).format("\n" +
" %1$s &%2$s(const bool value)\n" +
" {\n" +
" %3$s bits;\n" +
" std::memcpy(&bits, m_buffer + m_offset, sizeof(%3$s));\n" +
" bits = %4$s(value ?" +
" static_cast<%3$s>(%4$s(bits) | (%6$s << %5$su)) " +
": static_cast<%3$s>(%4$s(bits) & ~(%6$s << %5$su)));\n" +
" std::memcpy(m_buffer + m_offset, &bits, sizeof(%3$s));\n" +
" return *this;\n" +
" }\n",
bitsetClassName,
choiceName,
typeName,
byteOrderStr,
choiceBitPosition,
constantOne);
});
return sb;
}
private CharSequence generateEnumValues(final List tokens, final Token encodingToken)
{
final StringBuilder sb = new StringBuilder();
final Encoding encoding = encodingToken.encoding();
sb.append(
" enum Value\n" +
" {\n");
for (final Token token : tokens)
{
final CharSequence constVal = generateLiteral(
token.encoding().primitiveType(), token.encoding().constValue().toString());
sb.append(" ")
.append(formatForCppKeyword(token.name()))
.append(" = ")
.append(constVal)
.append(",\n");
}
final CharSequence nullLiteral = generateLiteral(
encoding.primitiveType(), encoding.applicableNullValue().toString());
if (shouldDecodeUnknownEnumValues)
{
sb.append(" SBE_UNKNOWN = ").append(nullLiteral).append(",\n");
}
sb.append(" NULL_VALUE = ").append(nullLiteral);
sb.append("\n };\n\n");
return sb;
}
private CharSequence generateEnumLookupMethod(final List tokens, final Token encodingToken)
{
final String enumName = formatClassName(encodingToken.applicableTypeName());
final StringBuilder sb = new StringBuilder();
new Formatter(sb).format(
" static %1$s::Value get(const %2$s value)\n" +
" {\n" +
" switch (value)\n" +
" {\n",
enumName,
cppTypeName(tokens.get(0).encoding().primitiveType()));
for (final Token token : tokens)
{
final CharSequence constVal = generateLiteral(
token.encoding().primitiveType(), token.encoding().constValue().toString());
sb.append(" case ")
.append(constVal)
.append(": return ")
.append(formatForCppKeyword(token.name()))
.append(";\n");
}
final CharSequence nullVal = generateLiteral(
encodingToken.encoding().primitiveType(), encodingToken.encoding().applicableNullValue().toString());
sb.append(" case ").append(nullVal).append(": return NULL_VALUE;\n")
.append(" }\n\n");
if (shouldDecodeUnknownEnumValues)
{
sb.append(" return SBE_UNKNOWN;\n").append(" }\n");
}
else
{
new Formatter(sb).format(
" throw std::runtime_error(\"unknown value for enum %s [E103]\");\n" +
" }\n",
enumName);
}
return sb;
}
private CharSequence generateFieldNotPresentCondition(
final int sinceVersion, final Encoding encoding, final String indent)
{
if (0 == sinceVersion)
{
return "";
}
return String.format(
indent + " if (m_actingVersion < %1$d)\n" +
indent + " {\n" +
indent + " return %2$s;\n" +
indent + " }\n\n",
sinceVersion,
generateLiteral(encoding.primitiveType(), encoding.applicableNullValue().toString()));
}
private static CharSequence generateArrayFieldNotPresentCondition(final int sinceVersion, final String indent)
{
if (0 == sinceVersion)
{
return "";
}
return String.format(
indent + " if (m_actingVersion < %1$d)\n" +
indent + " {\n" +
indent + " return 0;\n" +
indent + " }\n\n",
sinceVersion);
}
private static CharSequence generateStringNotPresentCondition(final int sinceVersion, final String indent)
{
if (0 == sinceVersion)
{
return "";
}
return String.format(
indent + " if (m_actingVersion < %1$d)\n" +
indent + " {\n" +
indent + " return std::string(\"\");\n" +
indent + " }\n\n",
sinceVersion);
}
private static CharSequence generateStringViewNotPresentCondition(final int sinceVersion, final String indent)
{
if (0 == sinceVersion)
{
return "";
}
return String.format(
indent + " if (m_actingVersion < %1$d)\n" +
indent + " {\n" +
indent + " return std::string_view(\"\");\n" +
indent + " }\n\n",
sinceVersion);
}
private static CharSequence generateTypeFieldNotPresentCondition(final int sinceVersion, final String indent)
{
if (0 == sinceVersion)
{
return "";
}
return String.format(
indent + " if (m_actingVersion < %1$d)\n" +
indent + " {\n" +
indent + " return nullptr;\n" +
indent + " }\n\n",
sinceVersion);
}
private static CharSequence generateFileHeader(
final CharSequence[] namespaces,
final String className,
final List typesToInclude)
{
final StringBuilder sb = new StringBuilder();
sb.append("/* Generated SBE (Simple Binary Encoding) message codec */\n");
sb.append(String.format(
"#ifndef _%1$s_%2$s_CXX_H_\n" +
"#define _%1$s_%2$s_CXX_H_\n\n" +
"#if __cplusplus >= 201103L\n" +
"# define SBE_CONSTEXPR constexpr\n" +
"# define SBE_NOEXCEPT noexcept\n" +
"#else\n" +
"# define SBE_CONSTEXPR\n" +
"# define SBE_NOEXCEPT\n" +
"#endif\n\n" +
"#if __cplusplus >= 201703L\n" +
"# include \n" +
"# define SBE_NODISCARD [[nodiscard]]\n" +
"#else\n" +
"# define SBE_NODISCARD\n" +
"#endif\n\n" +
"#if !defined(__STDC_LIMIT_MACROS)\n" +
"# define __STDC_LIMIT_MACROS 1\n" +
"#endif\n\n" +
"#include \n" +
"#include \n" +
"#include \n" +
"#include \n" +
"#include \n" +
"#include \n" +
"#include \n" +
"#include \n" +
"#include \n" +
"#include \n" +
"\n" +
"#if defined(WIN32) || defined(_WIN32)\n" +
"# define SBE_BIG_ENDIAN_ENCODE_16(v) _byteswap_ushort(v)\n" +
"# define SBE_BIG_ENDIAN_ENCODE_32(v) _byteswap_ulong(v)\n" +
"# define SBE_BIG_ENDIAN_ENCODE_64(v) _byteswap_uint64(v)\n" +
"# define SBE_LITTLE_ENDIAN_ENCODE_16(v) (v)\n" +
"# define SBE_LITTLE_ENDIAN_ENCODE_32(v) (v)\n" +
"# define SBE_LITTLE_ENDIAN_ENCODE_64(v) (v)\n" +
"#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__\n" +
"# define SBE_BIG_ENDIAN_ENCODE_16(v) __builtin_bswap16(v)\n" +
"# define SBE_BIG_ENDIAN_ENCODE_32(v) __builtin_bswap32(v)\n" +
"# define SBE_BIG_ENDIAN_ENCODE_64(v) __builtin_bswap64(v)\n" +
"# define SBE_LITTLE_ENDIAN_ENCODE_16(v) (v)\n" +
"# define SBE_LITTLE_ENDIAN_ENCODE_32(v) (v)\n" +
"# define SBE_LITTLE_ENDIAN_ENCODE_64(v) (v)\n" +
"#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__\n" +
"# define SBE_LITTLE_ENDIAN_ENCODE_16(v) __builtin_bswap16(v)\n" +
"# define SBE_LITTLE_ENDIAN_ENCODE_32(v) __builtin_bswap32(v)\n" +
"# define SBE_LITTLE_ENDIAN_ENCODE_64(v) __builtin_bswap64(v)\n" +
"# define SBE_BIG_ENDIAN_ENCODE_16(v) (v)\n" +
"# define SBE_BIG_ENDIAN_ENCODE_32(v) (v)\n" +
"# define SBE_BIG_ENDIAN_ENCODE_64(v) (v)\n" +
"#else\n" +
"# error \"Byte Ordering of platform not determined. " +
"Set __BYTE_ORDER__ manually before including this file.\"\n" +
"#endif\n\n" +
"#if !defined(SBE_BOUNDS_CHECK_EXPECT)\n" +
"# if defined(SBE_NO_BOUNDS_CHECK)\n" +
"# define SBE_BOUNDS_CHECK_EXPECT(exp, c) (false)\n" +
"# elif defined(_MSC_VER)\n" +
"# define SBE_BOUNDS_CHECK_EXPECT(exp, c) (exp)\n" +
"# else \n" +
"# define SBE_BOUNDS_CHECK_EXPECT(exp, c) (__builtin_expect(exp, c))\n" +
"# endif\n\n" +
"#endif\n\n" +
"#define SBE_FLOAT_NAN std::numeric_limits::quiet_NaN()\n" +
"#define SBE_DOUBLE_NAN std::numeric_limits::quiet_NaN()\n" +
"#define SBE_NULLVALUE_INT8 (std::numeric_limits::min)()\n" +
"#define SBE_NULLVALUE_INT16 (std::numeric_limits::min)()\n" +
"#define SBE_NULLVALUE_INT32 (std::numeric_limits::min)()\n" +
"#define SBE_NULLVALUE_INT64 (std::numeric_limits::min)()\n" +
"#define SBE_NULLVALUE_UINT8 (std::numeric_limits::max)()\n" +
"#define SBE_NULLVALUE_UINT16 (std::numeric_limits::max)()\n" +
"#define SBE_NULLVALUE_UINT32 (std::numeric_limits::max)()\n" +
"#define SBE_NULLVALUE_UINT64 (std::numeric_limits::max)()\n\n",
String.join("_", namespaces).toUpperCase(),
className.toUpperCase()));
if (typesToInclude != null && !typesToInclude.isEmpty())
{
sb.append("\n");
for (final String incName : typesToInclude)
{
sb.append("#include \"").append(toUpperFirstChar(incName)).append(".h\"\n");
}
}
sb.append("\nnamespace ");
sb.append(String.join(" {\nnamespace ", namespaces));
sb.append(" {\n\n");
return sb;
}
private static CharSequence generateEnumFileHeader(final CharSequence[] namespaces, final String className)
{
final StringBuilder sb = new StringBuilder();
sb.append("/* Generated SBE (Simple Binary Encoding) message codec */\n");
sb.append(String.format(
"#ifndef _%1$s_%2$s_CXX_H_\n" +
"#define _%1$s_%2$s_CXX_H_\n\n" +
"#if !defined(__STDC_LIMIT_MACROS)\n" +
"# define __STDC_LIMIT_MACROS 1\n" +
"#endif\n\n" +
"#include \n" +
"#include \n" +
"#include \n" +
"#include \n" +
"#include \n" +
"#include \n" +
"#include \n" +
"\n" +
"#define SBE_NULLVALUE_INT8 (std::numeric_limits::min)()\n" +
"#define SBE_NULLVALUE_INT16 (std::numeric_limits::min)()\n" +
"#define SBE_NULLVALUE_INT32 (std::numeric_limits::min)()\n" +
"#define SBE_NULLVALUE_INT64 (std::numeric_limits::min)()\n" +
"#define SBE_NULLVALUE_UINT8 (std::numeric_limits::max)()\n" +
"#define SBE_NULLVALUE_UINT16 (std::numeric_limits::max)()\n" +
"#define SBE_NULLVALUE_UINT32 (std::numeric_limits::max)()\n" +
"#define SBE_NULLVALUE_UINT64 (std::numeric_limits::max)()\n",
String.join("_", namespaces).toUpperCase(),
className.toUpperCase()));
sb.append("\nnamespace ");
sb.append(String.join(" {\nnamespace ", namespaces));
sb.append(" {\n\n");
return sb;
}
private static CharSequence generateClassDeclaration(final String className)
{
return
"class " + className + "\n" +
"{\n";
}
private static CharSequence generateEnumDeclaration(final String name)
{
return "class " + name + "\n{\npublic:\n";
}
private CharSequence generateCompositePropertyElements(
final String containingClassName, final List tokens, final String indent)
{
final StringBuilder sb = new StringBuilder();
for (int i = 0; i < tokens.size();)
{
final Token fieldToken = tokens.get(i);
final String propertyName = formatPropertyName(fieldToken.name());
generateFieldMetaAttributeMethod(sb, fieldToken, indent);
generateFieldCommonMethods(indent, sb, fieldToken, fieldToken, propertyName);
switch (fieldToken.signal())
{
case ENCODING:
generatePrimitiveProperty(sb, containingClassName, propertyName, fieldToken, fieldToken,
null, indent);
break;
case BEGIN_ENUM:
generateEnumProperty(sb, containingClassName, fieldToken, propertyName, fieldToken,
null, indent);
break;
case BEGIN_SET:
generateBitsetProperty(sb, propertyName, fieldToken, fieldToken, null, indent);
break;
case BEGIN_COMPOSITE:
generateCompositeProperty(sb, propertyName, fieldToken, fieldToken, null, indent);
break;
default:
break;
}
i += tokens.get(i).componentTokenCount();
}
return sb;
}
private void generatePrimitiveProperty(
final StringBuilder sb,
final String containingClassName,
final String propertyName,
final Token propertyToken,
final Token encodingToken,
final FieldPrecedenceModel fieldPrecedenceModel,
final String indent)
{
generatePrimitiveFieldMetaData(sb, propertyName, encodingToken, indent);
if (encodingToken.isConstantEncoding())
{
generateConstPropertyMethods(sb, propertyName, encodingToken, indent);
}
else
{
generatePrimitivePropertyMethods(
sb, containingClassName, propertyName, propertyToken, encodingToken, fieldPrecedenceModel, indent);
}
}
private void generatePrimitivePropertyMethods(
final StringBuilder sb,
final String containingClassName,
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,
fieldPrecedenceModel, indent);
}
else if (arrayLength > 1)
{
generateArrayProperty(sb, containingClassName, propertyName, propertyToken, encodingToken,
fieldPrecedenceModel, indent);
}
}
private void generatePrimitiveFieldMetaData(
final StringBuilder sb, final String propertyName, final Token token, final String indent)
{
final Encoding encoding = token.encoding();
final PrimitiveType primitiveType = encoding.primitiveType();
final String cppTypeName = cppTypeName(primitiveType);
final CharSequence nullValueString = generateNullValueLiteral(primitiveType, encoding);
new Formatter(sb).format("\n" +
indent + " static SBE_CONSTEXPR %1$s %2$sNullValue() SBE_NOEXCEPT\n" +
indent + " {\n" +
indent + " return %3$s;\n" +
indent + " }\n",
cppTypeName,
propertyName,
nullValueString);
new Formatter(sb).format("\n" +
indent + " static SBE_CONSTEXPR %1$s %2$sMinValue() SBE_NOEXCEPT\n" +
indent + " {\n" +
indent + " return %3$s;\n" +
indent + " }\n",
cppTypeName,
propertyName,
generateLiteral(primitiveType, token.encoding().applicableMinValue().toString()));
new Formatter(sb).format("\n" +
indent + " static SBE_CONSTEXPR %1$s %2$sMaxValue() SBE_NOEXCEPT\n" +
indent + " {\n" +
indent + " return %3$s;\n" +
indent + " }\n",
cppTypeName,
propertyName,
generateLiteral(primitiveType, token.encoding().applicableMaxValue().toString()));
new Formatter(sb).format("\n" +
indent + " static SBE_CONSTEXPR std::size_t %1$sEncodingLength() SBE_NOEXCEPT\n" +
indent + " {\n" +
indent + " return %2$d;\n" +
indent + " }\n",
propertyName,
token.encoding().primitiveType().size() * token.arrayLength());
}
private CharSequence generateLoadValue(
final PrimitiveType primitiveType,
final String offsetStr,
final ByteOrder byteOrder,
final String indent)
{
final String cppTypeName = cppTypeName(primitiveType);
final String byteOrderStr = formatByteOrderEncoding(byteOrder, primitiveType);
final StringBuilder sb = new StringBuilder();
if (primitiveType == PrimitiveType.FLOAT || primitiveType == PrimitiveType.DOUBLE)
{
final String stackUnion =
primitiveType == PrimitiveType.FLOAT ? "union sbe_float_as_uint_u" : "union sbe_double_as_uint_u";
new Formatter(sb).format(
indent + " %1$s val;\n" +
indent + " std::memcpy(&val, m_buffer + m_offset + %2$s, sizeof(%3$s));\n" +
indent + " val.uint_value = %4$s(val.uint_value);\n" +
indent + " return val.fp_value;\n",
stackUnion,
offsetStr,
cppTypeName,
byteOrderStr);
}
else
{
new Formatter(sb).format(
indent + " %1$s val;\n" +
indent + " std::memcpy(&val, m_buffer + m_offset + %2$s, sizeof(%1$s));\n" +
indent + " return %3$s(val);\n",
cppTypeName,
offsetStr,
byteOrderStr);
}
return sb;
}
private CharSequence generateStoreValue(
final PrimitiveType primitiveType,
final String valueSuffix,
final String offsetStr,
final ByteOrder byteOrder,
final String indent)
{
final String cppTypeName = cppTypeName(primitiveType);
final String byteOrderStr = formatByteOrderEncoding(byteOrder, primitiveType);
final StringBuilder sb = new StringBuilder();
if (primitiveType == PrimitiveType.FLOAT || primitiveType == PrimitiveType.DOUBLE)
{
final String stackUnion = primitiveType == PrimitiveType.FLOAT ?
"union sbe_float_as_uint_u" : "union sbe_double_as_uint_u";
new Formatter(sb).format(
indent + " %1$s val%2$s;\n" +
indent + " val%2$s.fp_value = value%2$s;\n" +
indent + " val%2$s.uint_value = %3$s(val%2$s.uint_value);\n" +
indent + " std::memcpy(m_buffer + m_offset + %4$s, &val%2$s, sizeof(%5$s));\n",
stackUnion,
valueSuffix,
byteOrderStr,
offsetStr,
cppTypeName);
}
else
{
new Formatter(sb).format(
indent + " %1$s val%2$s = %3$s(value%2$s);\n" +
indent + " std::memcpy(m_buffer + m_offset + %4$s, &val%2$s, sizeof(%1$s));\n",
cppTypeName,
valueSuffix,
byteOrderStr,
offsetStr);
}
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%6$s\n" +
indent + " {\n" +
"%3$s" +
"%4$s" +
"%5$s" +
indent + " }\n",
cppTypeName,
propertyName,
generateFieldNotPresentCondition(propertyToken.version(), encodingToken.encoding(), 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)%6$s\n" +
indent + " {\n" +
"%4$s" +
"%5$s" +
indent + " return *this;\n" +
indent + " }\n",
formatClassName(containingClassName),
propertyName,
cppTypeName,
storeValue,
accessOrderListenerCall,
noexceptDeclaration);
}
private void generateArrayProperty(
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);
final int arrayLength = encodingToken.arrayLength();
new Formatter(sb).format("\n" +
indent + " static SBE_CONSTEXPR std::uint64_t %1$sLength() SBE_NOEXCEPT\n" +
indent + " {\n" +
indent + " return %2$d;\n" +
indent + " }\n",
propertyName,
arrayLength);
new Formatter(sb).format("\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,
accessOrderListenerCall,
noexceptDeclaration);
new Formatter(sb).format("\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,
accessOrderListenerCall,
noexceptDeclaration);
final CharSequence loadValue = generateLoadValue(
primitiveType,
String.format("%d + (index * %d)", offset, primitiveType.size()),
encodingToken.encoding().byteOrder(),
indent);
new Formatter(sb).format("\n" +
indent + " SBE_NODISCARD %1$s %2$s(const std::uint64_t index) const\n" +
indent + " {\n" +
indent + " if (index >= %3$d)\n" +
indent + " {\n" +
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,
accessOrderListenerCall);
final CharSequence storeValue = generateStoreValue(
primitiveType,
"",
String.format("%d + (index * %d)", offset, primitiveType.size()),
encodingToken.encoding().byteOrder(),
indent);
new Formatter(sb).format("\n" +
indent + " %1$s &%2$s(const std::uint64_t index, const %3$s value)\n" +
indent + " {\n" +
indent + " if (index >= %4$d)\n" +
indent + " {\n" +
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",
containingClassName,
propertyName,
cppTypeName,
arrayLength,
storeValue,
accessOrderListenerCall);
new Formatter(sb).format("\n" +
indent + " std::uint64_t get%1$s(char *const dst, const std::uint64_t length) const\n" +
indent + " {\n" +
indent + " if (length > %2$d)\n" +
indent + " {\n" +
indent + " throw std::runtime_error(\"length too large for get%1$s [E106]\");\n" +
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" +
indent + " }\n",
toUpperFirstChar(propertyName),
arrayLength,
generateArrayFieldNotPresentCondition(propertyToken.version(), indent),
offset,
cppTypeName,
accessOrderListenerCall);
new Formatter(sb).format("\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",
containingClassName,
toUpperFirstChar(propertyName),
offset,
cppTypeName,
arrayLength,
accessOrderListenerCall,
noexceptDeclaration);
if (arrayLength > 1 && arrayLength <= 4)
{
sb.append("\n").append(indent).append(" ")
.append(containingClassName).append(" &put").append(toUpperFirstChar(propertyName))
.append("(\n");
for (int i = 0; i < arrayLength; i++)
{
sb.append(indent).append(" ")
.append("const ").append(cppTypeName).append(" value").append(i);
if (i < (arrayLength - 1))
{
sb.append(",\n");
}
}
sb.append(")").append(noexceptDeclaration).append("\n");
sb.append(indent).append(" {\n");
sb.append(accessOrderListenerCall);
for (int i = 0; i < arrayLength; i++)
{
sb.append(generateStoreValue(
primitiveType,
Integer.toString(i),
Integer.toString(offset + (i * primitiveType.size())),
encodingToken.encoding().byteOrder(),
indent));
}
sb.append("\n");
sb.append(indent).append(" return *this;\n");
sb.append(indent).append(" }\n");
}
if (encodingToken.encoding().primitiveType() == PrimitiveType.CHAR)
{
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" +
indent + " for (; length < %3$d && *(buffer + length) != '\\0'; ++length);\n" +
indent + " std::string result(buffer, length);\n\n" +
indent + " return result;\n" +
indent + " }\n",
toUpperFirstChar(propertyName),
offset,
arrayLength,
generateStringNotPresentCondition(propertyToken.version(), indent),
accessOrderListenerCall);
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%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" +
indent + " for (; length < %3$d && *(buffer + length) != '\\0'; ++length);\n" +
indent + " std::string_view result(buffer, length);\n\n" +
indent + " return result;\n" +
indent + " }\n" +
indent + " #endif\n",
toUpperFirstChar(propertyName),
offset,
arrayLength,
generateStringViewNotPresentCondition(propertyToken.version(), indent),
accessOrderListenerCall,
noexceptDeclaration);
new Formatter(sb).format("\n" +
indent + " #if __cplusplus >= 201703L\n" +
indent + " %1$s &put%2$s(const std::string_view str)\n" +
indent + " {\n" +
indent + " const std::size_t srcLength = str.length();\n" +
indent + " if (srcLength > %4$d)\n" +
indent + " {\n" +
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" +
indent + " m_buffer[m_offset + %3$d + start] = 0;\n" +
indent + " }\n\n" +
indent + " return *this;\n" +
indent + " }\n" +
indent + " #else\n" +
indent + " %1$s &put%2$s(const std::string &str)\n" +
indent + " {\n" +
indent + " const std::size_t srcLength = str.length();\n" +
indent + " if (srcLength > %4$d)\n" +
indent + " {\n" +
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" +
indent + " m_buffer[m_offset + %3$d + start] = 0;\n" +
indent + " }\n\n" +
indent + " return *this;\n" +
indent + " }\n" +
indent + " #endif\n",
containingClassName,
toUpperFirstChar(propertyName),
offset,
arrayLength,
accessOrderListenerCall);
}
}
private void generateJsonEscapedStringGetter(
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" +
indent + " {\n" +
indent + " switch (c)\n" +
indent + " {\n" +
indent + " case '\"': oss << \"\\\\\\\"\"; break;\n" +
indent + " case '\\\\': oss << \"\\\\\\\\\"; break;\n" +
indent + " case '\\b': oss << \"\\\\b\"; break;\n" +
indent + " case '\\f': oss << \"\\\\f\"; break;\n" +
indent + " case '\\n': oss << \"\\\\n\"; break;\n" +
indent + " case '\\r': oss << \"\\\\r\"; break;\n" +
indent + " case '\\t': oss << \"\\\\t\"; break;\n\n" +
indent + " default:\n" +
indent + " if ('\\x00' <= c && c <= '\\x1f')\n" +
indent + " {\n" +
indent + " oss << \"\\\\u\"" + " << std::hex << std::setw(4)\n" +
indent + " << std::setfill('0') << (int)(c);\n" +
indent + " }\n" +
indent + " else\n" +
indent + " {\n" +
indent + " oss << c;\n" +
indent + " }\n" +
indent + " }\n" +
indent + " }\n\n" +
indent + " return oss.str();\n" +
indent + " }\n",
toUpperFirstChar(propertyName),
generateStringNotPresentCondition(token.version(), indent),
accessOrderListenerCall);
}
private void generateConstPropertyMethods(
final StringBuilder sb, final String propertyName, final Token token, final String indent)
{
final String cppTypeName = cppTypeName(token.encoding().primitiveType());
if (token.encoding().primitiveType() != PrimitiveType.CHAR)
{
new Formatter(sb).format("\n" +
indent + " SBE_NODISCARD static SBE_CONSTEXPR %1$s %2$s() SBE_NOEXCEPT\n" +
indent + " {\n" +
indent + " return %3$s;\n" +
indent + " }\n",
cppTypeName,
propertyName,
generateLiteral(token.encoding().primitiveType(), token.encoding().constValue().toString()));
return;
}
final byte[] constantValue = token.encoding().constValue().byteArrayValue(token.encoding().primitiveType());
final StringBuilder values = new StringBuilder();
for (final byte b : constantValue)
{
values.append(b).append(", ");
}
if (values.length() > 0)
{
values.setLength(values.length() - 2);
}
new Formatter(sb).format("\n" +
indent + " static SBE_CONSTEXPR std::uint64_t %1$sLength() SBE_NOEXCEPT\n" +
indent + " {\n" +
indent + " return %2$d;\n" +
indent + " }\n",
propertyName,
constantValue.length);
new Formatter(sb).format("\n" +
indent + " SBE_NODISCARD const char *%1$s() const\n" +
indent + " {\n" +
indent + " static const std::uint8_t %1$sValues[] = { %2$s, 0 };\n\n" +
indent + " return (const char *)%1$sValues;\n" +
indent + " }\n",
propertyName,
values);
sb.append(String.format("\n" +
indent + " SBE_NODISCARD %1$s %2$s(const std::uint64_t index) const\n" +
indent + " {\n" +
indent + " static const std::uint8_t %2$sValues[] = { %3$s, 0 };\n\n" +
indent + " return (char)%2$sValues[index];\n" +
indent + " }\n",
cppTypeName,
propertyName,
values));
new Formatter(sb).format("\n" +
indent + " std::uint64_t get%1$s(char *dst, const std::uint64_t length) const\n" +
indent + " {\n" +
indent + " static std::uint8_t %2$sValues[] = { %3$s };\n" +
indent + " std::uint64_t bytesToCopy = " +
"length < sizeof(%2$sValues) ? length : sizeof(%2$sValues);\n\n" +
indent + " std::memcpy(dst, %2$sValues, static_cast(bytesToCopy));\n" +
indent + " return bytesToCopy;\n" +
indent + " }\n",
toUpperFirstChar(propertyName),
propertyName,
values);
new Formatter(sb).format("\n" +
indent + " std::string get%1$sAsString() const\n" +
indent + " {\n" +
indent + " static const std::uint8_t %1$sValues[] = { %2$s };\n\n" +
indent + " return std::string((const char *)%1$sValues, %3$s);\n" +
indent + " }\n",
toUpperFirstChar(propertyName),
values,
constantValue.length);
generateJsonEscapedStringGetter(sb, token, indent, propertyName, "");
}
private CharSequence generateFixedFlyweightCode(final String className, final int size)
{
final String schemaIdType = cppTypeName(ir.headerStructure().schemaIdType());
final String schemaVersionType = cppTypeName(ir.headerStructure().schemaVersionType());
return String.format(
"private:\n" +
" char *m_buffer = nullptr;\n" +
" 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" +
" {\n" +
" EPOCH, TIME_UNIT, SEMANTIC_TYPE, PRESENCE\n" +
" };\n\n" +
" union sbe_float_as_uint_u\n" +
" {\n" +
" float fp_value;\n" +
" std::uint32_t uint_value;\n" +
" };\n\n" +
" union sbe_double_as_uint_u\n" +
" {\n" +
" double fp_value;\n" +
" std::uint64_t uint_value;\n" +
" };\n\n" +
" %1$s() = default;\n\n" +
" %1$s(\n" +
" char *buffer,\n" +
" const std::uint64_t offset,\n" +
" const std::uint64_t bufferLength,\n" +
" const std::uint64_t 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\n" +
" %1$s(\n" +
" char *buffer,\n" +
" const std::uint64_t bufferLength,\n" +
" const std::uint64_t actingVersion) :\n" +
" %1$s(buffer, 0, bufferLength, actingVersion)\n" +
" {\n" +
" }\n\n" +
" %1$s(\n" +
" char *buffer,\n" +
" const std::uint64_t bufferLength) :\n" +
" %1$s(buffer, 0, bufferLength, sbeSchemaVersion())\n" +
" {\n" +
" }\n\n" +
" %1$s &wrap(\n" +
" char *buffer,\n" +
" const std::uint64_t offset,\n" +
" const std::uint64_t actingVersion,\n" +
" const std::uint64_t bufferLength)\n" +
" {\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" +
" {\n" +
" return %2$s;\n" +
" }\n\n" +
" SBE_NODISCARD std::uint64_t offset() const SBE_NOEXCEPT\n" +
" {\n" +
" return m_offset;\n" +
" }\n\n" +
" SBE_NODISCARD const char *buffer() const SBE_NOEXCEPT\n" +
" {\n" +
" return m_buffer;\n" +
" }\n\n" +
" SBE_NODISCARD char *buffer() SBE_NOEXCEPT\n" +
" {\n" +
" return m_buffer;\n" +
" }\n\n" +
" SBE_NODISCARD std::uint64_t bufferLength() const SBE_NOEXCEPT\n" +
" {\n" +
" return m_bufferLength;\n" +
" }\n\n" +
" SBE_NODISCARD std::uint64_t actingVersion() const SBE_NOEXCEPT\n" +
" {\n" +
" return m_actingVersion;\n" +
" }\n\n" +
" SBE_NODISCARD static SBE_CONSTEXPR %3$s sbeSchemaId() SBE_NOEXCEPT\n" +
" {\n" +
" return %4$s;\n" +
" }\n\n" +
" SBE_NODISCARD static SBE_CONSTEXPR %5$s sbeSchemaVersion() SBE_NOEXCEPT\n" +
" {\n" +
" return %6$s;\n" +
" }\n",
className,
size,
schemaIdType,
generateLiteral(ir.headerStructure().schemaIdType(), Integer.toString(ir.id())),
schemaVersionType,
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,
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" +
" const std::uint64_t bufferLength,\n" +
" const std::uint64_t actingBlockLength,\n" +
" const std::uint64_t actingVersion) :\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" +
" {\n" +
" }\n\n" +
" %1$s(char *buffer, const std::uint64_t bufferLength) :\n" +
" %1$s(buffer, 0, bufferLength, sbeBlockLength(), sbeSchemaVersion())\n" +
" {\n" +
" }\n\n" +
" %1$s(\n" +
" char *buffer,\n" +
" const std::uint64_t bufferLength,\n" +
" const std::uint64_t actingBlockLength,\n" +
" const std::uint64_t actingVersion) :\n" +
" %1$s(buffer, 0, bufferLength, actingBlockLength, actingVersion)\n" +
" {\n" +
" }\n\n",
className,
constructorWithCodecState);
}
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());
final String schemaIdType = cppTypeName(ir.headerStructure().schemaIdType());
final String schemaVersionType = cppTypeName(ir.headerStructure().schemaVersionType());
final String semanticType = token.encoding().semanticType() == null ? "" : token.encoding().semanticType();
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" +
"%17$s" +
" inline std::uint64_t *sbePositionPtr() SBE_NOEXCEPT\n" +
" {\n" +
" return &m_position;\n" +
" }\n\n" +
"%22$s" +
"public:\n" +
" static constexpr %1$s SBE_BLOCK_LENGTH = %2$s;\n" +
" static constexpr %3$s SBE_TEMPLATE_ID = %4$s;\n" +
" static constexpr %5$s SBE_SCHEMA_ID = %6$s;\n" +
" static constexpr %7$s SBE_SCHEMA_VERSION = %8$s;\n" +
" static constexpr const char* SBE_SEMANTIC_VERSION = \"%13$s\";\n\n" +
" enum MetaAttribute\n" +
" {\n" +
" EPOCH, TIME_UNIT, SEMANTIC_TYPE, PRESENCE\n" +
" };\n\n" +
" union sbe_float_as_uint_u\n" +
" {\n" +
" float fp_value;\n" +
" std::uint32_t uint_value;\n" +
" };\n\n" +
" union sbe_double_as_uint_u\n" +
" {\n" +
" double fp_value;\n" +
" std::uint64_t uint_value;\n" +
" };\n\n" +
" 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" +
" }\n\n" +
" SBE_NODISCARD static SBE_CONSTEXPR std::uint64_t sbeBlockAndHeaderLength() SBE_NOEXCEPT\n" +
" {\n" +
" return messageHeader::encodedLength() + sbeBlockLength();\n" +
" }\n\n" +
" SBE_NODISCARD static SBE_CONSTEXPR %3$s sbeTemplateId() SBE_NOEXCEPT\n" +
" {\n" +
" return %4$s;\n" +
" }\n\n" +
" SBE_NODISCARD static SBE_CONSTEXPR %5$s sbeSchemaId() SBE_NOEXCEPT\n" +
" {\n" +
" return %6$s;\n" +
" }\n\n" +
" SBE_NODISCARD static SBE_CONSTEXPR %7$s sbeSchemaVersion() SBE_NOEXCEPT\n" +
" {\n" +
" return %8$s;\n" +
" }\n\n" +
" SBE_NODISCARD static const char *sbeSemanticVersion() SBE_NOEXCEPT\n" +
" {\n" +
" return \"%13$s\";\n" +
" }\n\n" +
" SBE_NODISCARD static SBE_CONSTEXPR const char *sbeSemanticType() SBE_NOEXCEPT\n" +
" {\n" +
" return \"%9$s\";\n" +
" }\n\n" +
" SBE_NODISCARD std::uint64_t offset() const SBE_NOEXCEPT\n" +
" {\n" +
" return m_offset;\n" +
" }\n\n" +
" %10$s &wrapForEncode(char *buffer, const std::uint64_t offset, const std::uint64_t bufferLength)\n" +
" {\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(" +
"char *buffer, const std::uint64_t offset, const std::uint64_t bufferLength)\n" +
" {\n" +
" messageHeader hdr(buffer, offset, bufferLength, sbeSchemaVersion());\n\n" +
" hdr\n" +
" .blockLength(sbeBlockLength())\n" +
" .templateId(sbeTemplateId())\n" +
" .schemaId(sbeSchemaId())\n" +
" .version(sbeSchemaVersion());\n\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" +
" const std::uint64_t actingBlockLength,\n" +
" const std::uint64_t actingVersion,\n" +
" const std::uint64_t bufferLength)\n" +
" {\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" +
" {\n" +
" return wrapForDecode(" +
"m_buffer, m_offset, m_actingBlockLength, m_actingVersion, m_bufferLength);\n" +
" }\n\n" +
" SBE_NODISCARD std::uint64_t sbePosition() const SBE_NOEXCEPT\n" +
" {\n" +
" return m_position;\n" +
" }\n\n" +
" // NOLINTNEXTLINE(readability-convert-member-functions-to-static)\n" +
" std::uint64_t sbeCheckPosition(const std::uint64_t position)\n" +
" {\n" +
" if (SBE_BOUNDS_CHECK_EXPECT((position > m_bufferLength), false))\n" +
" {\n" +
" throw std::runtime_error(\"buffer too short [E100]\");\n" +
" }\n" +
" return position;\n" +
" }\n\n" +
" void sbePosition(const std::uint64_t position)\n" +
" {\n" +
" m_position = sbeCheckPosition(position);\n" +
" }\n\n" +
" SBE_NODISCARD std::uint64_t encodedLength() const SBE_NOEXCEPT\n" +
" {\n" +
" return sbePosition() - m_offset;\n" +
" }\n\n" +
" SBE_NODISCARD std::uint64_t decodeLength() const\n" +
" {\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" +
" SBE_NODISCARD const char *buffer() const SBE_NOEXCEPT\n" +
" {\n" +
" return m_buffer;\n" +
" }\n\n" +
" SBE_NODISCARD char *buffer() SBE_NOEXCEPT\n" +
" {\n" +
" return m_buffer;\n" +
" }\n\n" +
" SBE_NODISCARD std::uint64_t bufferLength() const SBE_NOEXCEPT\n" +
" {\n" +
" return m_bufferLength;\n" +
" }\n\n" +
" SBE_NODISCARD std::uint64_t actingVersion() const SBE_NOEXCEPT\n" +
" {\n" +
" return m_actingVersion;\n" +
" }\n",
blockLengthType,
generateLiteral(ir.headerStructure().blockLengthType(), Integer.toString(token.encodedLength())),
templateIdType,
generateLiteral(ir.headerStructure().templateIdType(), Integer.toString(token.id())),
schemaIdType,
generateLiteral(ir.headerStructure().schemaIdType(), Integer.toString(ir.id())),
schemaVersionType,
generateLiteral(ir.headerStructure().schemaVersionType(), Integer.toString(ir.version())),
semanticType,
className,
generateConstructorsAndOperators(className, fieldPrecedenceModel),
formatClassName(headerType),
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");
final MutableBoolean actingVersionCanBeTooLowToBeValid = new MutableBoolean(true);
fieldPrecedenceModel.forEachWrappedStateByVersionDesc((version, state) ->
{
if (version == 0)
{
actingVersionCanBeTooLowToBeValid.set(false);
sb.append(INDENT).append(" codecState(")
.append(qualifiedStateCase(state)).append(");\n");
}
else
{
sb.append(INDENT).append(" if (actingVersion >= ").append(version).append(")\n")
.append(INDENT).append(" {\n")
.append(INDENT).append(" codecState(")
.append(qualifiedStateCase(state)).append(");\n")
.append(INDENT).append(" return;\n")
.append(INDENT).append(" }\n\n");
}
});
if (actingVersionCanBeTooLowToBeValid.get())
{
sb.append(INDENT)
.append(" throw std::runtime_error(\"Unsupported acting version: \" + actingVersion);\n");
}
sb.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++)
{
final Token signalToken = tokens.get(i);
if (signalToken.signal() == Signal.BEGIN_FIELD)
{
final Token encodingToken = tokens.get(i + 1);
final String propertyName = formatPropertyName(signalToken.name());
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,
fieldPrecedenceModel, indent);
break;
case BEGIN_ENUM:
generateEnumProperty(sb, containingClassName, signalToken, propertyName, encodingToken,
fieldPrecedenceModel, indent);
break;
case BEGIN_SET:
generateBitsetProperty(sb, propertyName, signalToken, encodingToken,
fieldPrecedenceModel, indent);
break;
case BEGIN_COMPOSITE:
generateCompositeProperty(sb, propertyName, signalToken, encodingToken,
fieldPrecedenceModel, indent);
break;
default:
break;
}
}
}
}
private void generateFieldCommonMethods(
final String indent,
final StringBuilder sb,
final Token fieldToken,
final Token encodingToken,
final String propertyName)
{
new Formatter(sb).format("\n" +
indent + " static SBE_CONSTEXPR std::uint16_t %1$sId() SBE_NOEXCEPT\n" +
indent + " {\n" +
indent + " return %2$d;\n" +
indent + " }\n",
propertyName,
fieldToken.id());
final int version = fieldToken.version();
final String versionCheck = 0 == version ?
" return true;\n" : " return m_actingVersion >= %1$sSinceVersion();\n";
new Formatter(sb).format("\n" +
indent + " SBE_NODISCARD static SBE_CONSTEXPR std::uint64_t %1$sSinceVersion() SBE_NOEXCEPT\n" +
indent + " {\n" +
indent + " return %2$d;\n" +
indent + " }\n\n" +
indent + " SBE_NODISCARD bool %1$sInActingVersion() SBE_NOEXCEPT\n" +
indent + " {\n" +
indent + versionCheck +
indent + " }\n",
propertyName,
version);
new Formatter(sb).format("\n" +
indent + " SBE_NODISCARD static SBE_CONSTEXPR std::size_t %1$sEncodingOffset() SBE_NOEXCEPT\n" +
indent + " {\n" +
indent + " return %2$d;\n" +
indent + " }\n",
propertyName,
encodingToken.offset());
}
private static void generateFieldMetaAttributeMethod(
final StringBuilder sb, final Token token, final String indent)
{
final Encoding encoding = token.encoding();
final String epoch = encoding.epoch() == null ? "" : encoding.epoch();
final String timeUnit = encoding.timeUnit() == null ? "" : encoding.timeUnit();
final String semanticType = encoding.semanticType() == null ? "" : encoding.semanticType();
sb.append("\n")
.append(indent).append(" SBE_NODISCARD static const char *")
.append(token.name()).append("MetaAttribute(const MetaAttribute metaAttribute) SBE_NOEXCEPT\n")
.append(indent).append(" {\n")
.append(indent).append(" switch (metaAttribute)\n")
.append(indent).append(" {\n");
if (!Strings.isEmpty(epoch))
{
sb.append(indent)
.append(" case MetaAttribute::EPOCH: return \"").append(epoch).append("\";\n");
}
if (!Strings.isEmpty(timeUnit))
{
sb.append(indent)
.append(" case MetaAttribute::TIME_UNIT: return \"").append(timeUnit).append("\";\n");
}
if (!Strings.isEmpty(semanticType))
{
sb.append(indent)
.append(" case MetaAttribute::SEMANTIC_TYPE: return \"").append(semanticType)
.append("\";\n");
}
sb
.append(indent).append(" case MetaAttribute::PRESENCE: return \"")
.append(encoding.presence().toString().toLowerCase()).append("\";\n")
.append(indent).append(" default: return \"\";\n")
.append(indent).append(" }\n")
.append(indent).append(" }\n");
}
private static CharSequence generateEnumFieldNotPresentCondition(
final int sinceVersion, final String enumName, final String indent)
{
if (0 == sinceVersion)
{
return "";
}
return String.format(
indent + " if (m_actingVersion < %1$d)\n" +
indent + " {\n" +
indent + " return %2$s::NULL_VALUE;\n" +
indent + " }\n\n",
sinceVersion,
enumName);
}
private void generateEnumProperty(
final StringBuilder sb,
final String containingClassName,
final Token fieldToken,
final String propertyName,
final Token encodingToken,
final FieldPrecedenceModel fieldPrecedenceModel,
final String indent)
{
final String enumName = formatClassName(encodingToken.applicableTypeName());
final PrimitiveType primitiveType = encodingToken.encoding().primitiveType();
final String typeName = cppTypeName(primitiveType);
final int offset = encodingToken.offset();
new Formatter(sb).format("\n" +
indent + " SBE_NODISCARD static SBE_CONSTEXPR std::size_t %1$sEncodingLength() SBE_NOEXCEPT\n" +
indent + " {\n" +
indent + " return %2$d;\n" +
indent + " }\n",
propertyName,
fieldToken.encodedLength());
if (fieldToken.isConstantEncoding())
{
final String constValue = fieldToken.encoding().constValue().toString();
new Formatter(sb).format("\n" +
indent + " SBE_NODISCARD static SBE_CONSTEXPR %1$s::Value %2$sConstValue() SBE_NOEXCEPT\n" +
indent + " {\n" +
indent + " return %1$s::Value::%3$s;\n" +
indent + " }\n",
enumName,
propertyName,
constValue.substring(constValue.indexOf(".") + 1));
new Formatter(sb).format("\n" +
indent + " SBE_NODISCARD %1$s::Value %2$s() const\n" +
indent + " {\n" +
"%3$s" +
indent + " return %1$s::Value::%4$s;\n" +
indent + " }\n",
enumName,
propertyName,
generateEnumFieldNotPresentCondition(fieldToken.version(), enumName, indent),
constValue.substring(constValue.indexOf(".") + 1));
new Formatter(sb).format("\n" +
indent + " SBE_NODISCARD %1$s %2$sRaw() const SBE_NOEXCEPT\n" +
indent + " {\n" +
indent + " return static_cast<%1$s>(%3$s::Value::%4$s);\n" +
indent + " }\n",
typeName,
propertyName,
enumName,
constValue.substring(constValue.indexOf(".") + 1));
}
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%6$s\n" +
indent + " {\n" +
"%3$s" +
"%4$s" +
"%5$s" +
indent + " }\n",
typeName,
propertyName,
generateFieldNotPresentCondition(fieldToken.version(), encodingToken.encoding(), 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" +
indent + " }\n",
enumName,
propertyName,
generateEnumFieldNotPresentCondition(fieldToken.version(), enumName, indent),
formatByteOrderEncoding(encodingToken.encoding().byteOrder(), primitiveType),
typeName,
offset,
accessOrderListenerCall);
new Formatter(sb).format("\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" +
indent + " }\n",
formatClassName(containingClassName),
propertyName,
enumName,
typeName,
offset,
formatByteOrderEncoding(encodingToken.encoding().byteOrder(), primitiveType),
accessOrderListenerCall,
noexceptDeclaration);
}
}
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(encodingToken.applicableTypeName());
final int offset = encodingToken.offset();
new Formatter(sb).format("\n" +
indent + "private:\n" +
indent + " %1$s m_%2$s;\n\n" +
indent + "public:\n",
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,
accessOrderListenerCall);
new Formatter(sb).format("\n" +
indent + " static SBE_CONSTEXPR std::size_t %1$sEncodingLength() SBE_NOEXCEPT\n" +
indent + " {\n" +
indent + " return %2$d;\n" +
indent + " }\n",
propertyName,
encodingToken.encoding().primitiveType().size());
}
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(encodingToken.applicableTypeName());
new Formatter(sb).format("\n" +
"private:\n" +
indent + " %1$s m_%2$s;\n\n" +
"public:\n",
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,
encodingToken.offset(),
accessOrderListenerCall);
}
private CharSequence generateNullValueLiteral(final PrimitiveType primitiveType, final Encoding encoding)
{
// Visual C++ does not handle minimum integer values properly
// See: http://msdn.microsoft.com/en-us/library/4kh09110.aspx
// So null values get special handling
if (null == encoding.nullValue())
{
switch (primitiveType)
{
case CHAR:
case FLOAT:
case DOUBLE:
break; // no special handling
case INT8:
return "SBE_NULLVALUE_INT8";
case INT16:
return "SBE_NULLVALUE_INT16";
case INT32:
return "SBE_NULLVALUE_INT32";
case INT64:
return "SBE_NULLVALUE_INT64";
case UINT8:
return "SBE_NULLVALUE_UINT8";
case UINT16:
return "SBE_NULLVALUE_UINT16";
case UINT32:
return "SBE_NULLVALUE_UINT32";
case UINT64:
return "SBE_NULLVALUE_UINT64";
}
}
return generateLiteral(primitiveType, encoding.applicableNullValue().toString());
}
private void generateDisplay(
final StringBuilder sb,
final String name,
final List fields,
final List groups,
final List varData)
{
new Formatter(sb).format("\n" +
"template\n" +
"friend std::basic_ostream & operator << (\n" +
" std::basic_ostream &builder, const %1$s &_writer)\n" +
"{\n" +
" %1$s writer(\n" +
" _writer.m_buffer,\n" +
" _writer.m_offset,\n" +
" _writer.m_bufferLength,\n" +
" _writer.m_actingBlockLength,\n" +
" _writer.m_actingVersion);\n\n" +
" builder << '{';\n" +
" builder << R\"(\"Name\": \"%1$s\", )\";\n" +
" builder << R\"(\"sbeTemplateId\": )\";\n" +
" builder << writer.sbeTemplateId();\n" +
" builder << \", \";\n\n" +
"%2$s" +
" builder << '}';\n\n" +
" return builder;\n" +
"}\n",
formatClassName(name),
appendDisplay(fields, groups, varData, INDENT));
}
private CharSequence generateGroupDisplay(
final String name,
final List fields,
final List groups,
final List varData,
final String indent)
{
return String.format("\n" +
indent + "template\n" +
indent + "friend std::basic_ostream & operator << (\n" +
indent + " std::basic_ostream &builder, %1$s &writer)\n" +
indent + "{\n" +
indent + " builder << '{';\n" +
"%2$s" +
indent + " builder << '}';\n\n" +
indent + " return builder;\n" +
indent + "}\n",
formatClassName(name),
appendDisplay(fields, groups, varData, indent + INDENT));
}
private CharSequence generateCompositeDisplay(final String name, final List tokens)
{
return String.format("\n" +
"template\n" +
"friend std::basic_ostream & operator << (\n" +
" std::basic_ostream &builder, %1$s &writer)\n" +
"{\n" +
" builder << '{';\n" +
"%2$s" +
" builder << '}';\n\n" +
" return builder;\n" +
"}\n\n",
formatClassName(name),
appendDisplay(tokens, new ArrayList<>(), new ArrayList<>(), INDENT));
}
private CharSequence appendDisplay(
final List fields, final List groups, final List varData, final String indent)
{
final StringBuilder sb = new StringBuilder();
final boolean[] atLeastOne = { false };
for (int i = 0, size = fields.size(); i < size;)
{
final Token fieldToken = fields.get(i);
final Token encodingToken = fields.get(fieldToken.signal() == Signal.BEGIN_FIELD ? i + 1 : i);
writeTokenDisplay(sb, fieldToken.name(), encodingToken, atLeastOne, indent);
i += fieldToken.componentTokenCount();
}
for (int i = 0, size = groups.size(); i < size; i++)
{
final Token groupToken = groups.get(i);
if (groupToken.signal() != Signal.BEGIN_GROUP)
{
throw new IllegalStateException("tokens must begin with BEGIN_GROUP: token=" + groupToken);
}
if (atLeastOne[0])
{
sb.append(indent).append("builder << \", \";\n");
}
atLeastOne[0] = true;
new Formatter(sb).format(
indent + "{\n" +
indent + " bool atLeastOne = false;\n" +
indent + " builder << R\"(\"%3$s\": [)\";\n" +
indent + " writer.%2$s().forEach(\n" +
indent + " [&](%1$s &%2$s)\n" +
indent + " {\n" +
indent + " if (atLeastOne)\n" +
indent + " {\n" +
indent + " builder << \", \";\n" +
indent + " }\n" +
indent + " atLeastOne = true;\n" +
indent + " builder << %2$s;\n" +
indent + " });\n" +
indent + " builder << ']';\n" +
indent + "}\n\n",
formatClassName(groupToken.name()),
formatPropertyName(groupToken.name()),
groupToken.name());
i = findEndSignal(groups, i, Signal.END_GROUP, groupToken.name());
}
for (int i = 0, size = varData.size(); i < size;)
{
final Token varDataToken = varData.get(i);
if (varDataToken.signal() != Signal.BEGIN_VAR_DATA)
{
throw new IllegalStateException("tokens must begin with BEGIN_VAR_DATA: token=" + varDataToken);
}
if (atLeastOne[0])
{
sb.append(indent).append("builder << \", \";\n");
}
atLeastOne[0] = true;
final String characterEncoding = varData.get(i + 3).encoding().characterEncoding();
sb.append(indent).append("builder << R\"(\"").append(varDataToken.name()).append("\": )\";\n");
if (null == characterEncoding)
{
final String skipFunction = "writer.skip" + toUpperFirstChar(varDataToken.name()) + "()";
sb.append(indent).append("builder << '\"' <<\n").append(indent).append(INDENT).append(skipFunction)
.append(" << \" bytes of raw data\\\"\";\n");
}
else
{
final String getAsStringFunction =
"writer.get" + toUpperFirstChar(varDataToken.name()) + "AsJsonEscapedString().c_str()";
sb.append(indent).append("builder << '\"' <<\n").append(indent).append(INDENT)
.append(getAsStringFunction).append(" << '\"';\n\n");
}
i += varDataToken.componentTokenCount();
}
return sb;
}
private void writeTokenDisplay(
final StringBuilder sb,
final String fieldTokenName,
final Token typeToken,
final boolean[] atLeastOne,
final String indent)
{
if (typeToken.encodedLength() <= 0 || typeToken.isConstantEncoding())
{
return;
}
if (atLeastOne[0])
{
sb.append(indent).append("builder << \", \";\n");
}
else
{
atLeastOne[0] = true;
}
sb.append(indent).append("builder << R\"(\"").append(fieldTokenName).append("\": )\";\n");
final String fieldName = "writer." + formatPropertyName(fieldTokenName);
switch (typeToken.signal())
{
case ENCODING:
if (typeToken.arrayLength() > 1)
{
if (typeToken.encoding().primitiveType() == PrimitiveType.CHAR)
{
final String getAsStringFunction =
"writer.get" + toUpperFirstChar(fieldTokenName) + "AsJsonEscapedString().c_str()";
sb.append(indent).append("builder << '\"' <<\n").append(indent).append(INDENT)
.append(getAsStringFunction).append(" << '\"';\n");
}
else
{
sb.append(
indent + "builder << '[';\n" +
indent + "for (std::size_t i = 0, length = " + fieldName + "Length(); i < length; i++)\n" +
indent + "{\n" +
indent + " if (i)\n" +
indent + " {\n" +
indent + " builder << ',';\n" +
indent + " }\n" +
indent + " builder << +" + fieldName + "(i);\n" +
indent + "}\n" +
indent + "builder << ']';\n");
}
}
else
{
if (typeToken.encoding().primitiveType() == PrimitiveType.CHAR)
{
sb.append(
indent + "if (std::isprint(" + fieldName + "()))\n" +
indent + "{\n" +
indent + " builder << '\"' << (char)" + fieldName + "() << '\"';\n" +
indent + "}\n" +
indent + "else\n" +
indent + "{\n" +
indent + " builder << (int)" + fieldName + "();\n" +
indent + "}\n");
}
else
{
sb.append(indent).append("builder << +").append(fieldName).append("();\n");
}
}
break;
case BEGIN_ENUM:
sb.append(indent).append("builder << '\"' << ").append(fieldName).append("() << '\"';\n");
break;
case BEGIN_SET:
case BEGIN_COMPOSITE:
if (0 == typeToken.version())
{
sb.append(indent).append("builder << ").append(fieldName).append("();\n");
}
else
{
new Formatter(sb).format(
indent + "if (%1$sInActingVersion())\n" +
indent + "{\n" +
indent + " builder << %1$s();\n" +
indent + "}\n" +
indent + "else\n" +
indent + "{\n" +
indent + " builder << %2$s;\n" +
indent + "}\n",
fieldName,
typeToken.signal() == Signal.BEGIN_SET ? "\"[]\"" : "\"{}\"");
}
break;
default:
break;
}
sb.append('\n');
}
private CharSequence generateChoicesDisplay(final String name, final List tokens)
{
final String indent = INDENT;
final StringBuilder sb = new StringBuilder();
final List choiceTokens = new ArrayList<>();
collect(Signal.CHOICE, tokens, 0, choiceTokens);
new Formatter(sb).format("\n" +
indent + "template\n" +
indent + "friend std::basic_ostream & operator << (\n" +
indent + " std::basic_ostream &builder, %1$s &writer)\n" +
indent + "{\n" +
indent + " builder << '[';\n",
name);
if (choiceTokens.size() > 1)
{
sb.append(indent + " bool atLeastOne = false;\n");
}
for (int i = 0, size = choiceTokens.size(); i < size; i++)
{
final Token token = choiceTokens.get(i);
final String choiceName = "writer." + formatPropertyName(token.name());
sb.append(indent + " if (").append(choiceName).append("())\n")
.append(indent).append(" {\n");
if (i > 0)
{
sb.append(
indent + " if (atLeastOne)\n" +
indent + " {\n" +
indent + " builder << \",\";\n" +
indent + " }\n");
}
sb.append(indent + " builder << R\"(\"").append(formatPropertyName(token.name())).append("\")\";\n");
if (i < (size - 1))
{
sb.append(indent + " atLeastOne = true;\n");
}
sb.append(indent + " }\n");
}
sb.append(
indent + " builder << ']';\n" +
indent + " return builder;\n" +
indent + "}\n");
return sb;
}
private CharSequence generateEnumDisplay(final List tokens, final Token encodingToken)
{
final String enumName = formatClassName(encodingToken.applicableTypeName());
final StringBuilder sb = new StringBuilder();
new Formatter(sb).format("\n" +
" static const char *c_str(const %1$s::Value value)\n" +
" {\n" +
" switch (value)\n" +
" {\n",
enumName);
for (final Token token : tokens)
{
new Formatter(sb).format(
" case %1$s: return \"%1$s\";\n",
formatForCppKeyword(token.name()));
}
sb.append(" case NULL_VALUE: return \"NULL_VALUE\";\n").append(" }\n\n");
if (shouldDecodeUnknownEnumValues)
{
sb.append(" return \"SBE_UNKNOWN\";\n").append(" }\n\n");
}
else
{
new Formatter(sb).format(
" throw std::runtime_error(\"unknown value for enum %1$s [E103]:\");\n" +
" }\n\n",
enumName);
}
new Formatter(sb).format(
" template\n" +
" friend std::basic_ostream & operator << (\n" +
" std::basic_ostream &os, %1$s::Value m)\n" +
" {\n" +
" return os << %1$s::c_str(m);\n" +
" }\n",
enumName);
return sb;
}
private Object[] generateMessageLengthArgs(
final List groups,
final List varData,
final String indent,
final boolean withName)
{
final StringBuilder sb = new StringBuilder();
int count = 0;
for (int i = 0, size = groups.size(); i < size; i++)
{
final Token groupToken = groups.get(i);
if (groupToken.signal() != Signal.BEGIN_GROUP)
{
throw new IllegalStateException("tokens must begin with BEGIN_GROUP: token=" + groupToken);
}
final int endSignal = findEndSignal(groups, i, Signal.END_GROUP, groupToken.name());
final String groupName = formatPropertyName(groupToken.name());
if (count > 0)
{
sb.append(",\n").append(indent);
}
final List thisGroup = groups.subList(i, endSignal + 1);
if (isMessageConstLength(thisGroup))
{
sb.append("std::size_t");
if (withName)
{
sb.append(" ").append(groupName).append("Length = 0");
}
}
else
{
sb.append("const std::vector> &");
if (withName)
{
sb.append(groupName).append("ItemLengths = {}");
}
}
count += 1;
i = endSignal;
}
for (int i = 0, size = varData.size(); i < size;)
{
final Token varDataToken = varData.get(i);
if (varDataToken.signal() != Signal.BEGIN_VAR_DATA)
{
throw new IllegalStateException("tokens must begin with BEGIN_VAR_DATA: token=" + varDataToken);
}
if (count > 0)
{
sb.append(",\n").append(indent);
}
sb.append("std::size_t");
if (withName)
{
sb.append(" ").append(formatPropertyName(varDataToken.name())).append("Length = 0");
}
count += 1;
i += varDataToken.componentTokenCount();
}
CharSequence result = sb;
if (count > 1)
{
result = "\n" + indent + result;
}
return new Object[]{ result, count };
}
private Object[] generateMessageLengthArgs(final List tokens, final String indent, final boolean withName)
{
int i = 0;
final Token groupToken = tokens.get(i);
if (groupToken.signal() != Signal.BEGIN_GROUP)
{
throw new IllegalStateException("tokens must begin with BEGIN_GROUP: token=" + groupToken);
}
++i;
final int groupHeaderTokenCount = tokens.get(i).componentTokenCount();
i += groupHeaderTokenCount;
final List fields = new ArrayList<>();
i = collectFields(tokens, i, fields);
final List groups = new ArrayList<>();
i = collectGroups(tokens, i, groups);
final List varData = new ArrayList<>();
collectVarData(tokens, i, varData);
return generateMessageLengthArgs(groups, varData, indent, withName);
}
private boolean isMessageConstLength(final List tokens)
{
final Integer count = (Integer)generateMessageLengthArgs(tokens, BASE_INDENT, false)[1];
return count == 0;
}
private CharSequence generateMessageLengthCallPre17Helper(final List tokens)
{
final StringBuilder sb = new StringBuilder();
final Integer count = (Integer)generateMessageLengthArgs(tokens, BASE_INDENT, false)[1];
for (int i = 0; i < count; i++)
{
if (i > 0)
{
sb.append(", ");
}
sb.append("std::get<").append(i).append(">(e)");
}
return sb;
}
private CharSequence generateMessageLength(final List groups, final List varData, final String indent)
{
final StringBuilder sbEncode = new StringBuilder();
final StringBuilder sbSkip = new StringBuilder();
for (int i = 0, size = groups.size(); i < size; i++)
{
final Token groupToken = groups.get(i);
if (groupToken.signal() != Signal.BEGIN_GROUP)
{
throw new IllegalStateException("tokens must begin with BEGIN_GROUP: token=" + groupToken);
}
final int endSignal = findEndSignal(groups, i, Signal.END_GROUP, groupToken.name());
final List thisGroup = groups.subList(i, endSignal + 1);
final Token numInGroupToken = Generators.findFirst("numInGroup", groups, i);
final long minCount = numInGroupToken.encoding().applicableMinValue().longValue();
final long maxCount = numInGroupToken.encoding().applicableMaxValue().longValue();
final String countName = formatPropertyName(groupToken.name()) +
(isMessageConstLength(thisGroup) ? "Length" : "ItemLengths.size()");
final String minCheck = minCount > 0 ? countName + " < " + minCount + "LL || " : "";
final String maxCheck = countName + " > " + maxCount + "LL";
new Formatter(sbEncode).format("\n" +
indent + " length += %1$s::sbeHeaderSize();\n",
formatClassName(groupToken.name()));
if (isMessageConstLength(thisGroup))
{
new Formatter(sbEncode).format(
indent + " if (%3$s%4$s)\n" +
indent + " {\n" +
indent + " throw std::runtime_error(\"%5$s outside of allowed range [E110]\");\n" +
indent + " }\n" +
indent + " length += %1$sLength *%2$s::sbeBlockLength();\n",
formatPropertyName(groupToken.name()),
formatClassName(groupToken.name()),
minCheck,
maxCheck,
countName);
}
else
{
new Formatter(sbEncode).format(
indent + " if (%3$s%4$s)\n" +
indent + " {\n" +
indent + " throw std::runtime_error(\"%5$s outside of allowed range [E110]\");\n" +
indent + " }\n\n" +
indent + " for (const auto &e: %1$sItemLengths)\n" +
indent + " {\n" +
indent + " #if __cplusplus >= 201703L\n" +
indent + " length += std::apply(%2$s::computeLength, e);\n" +
indent + " #else\n" +
indent + " length += %2$s::computeLength(%6$s);\n" +
indent + " #endif\n" +
indent + " }\n",
formatPropertyName(groupToken.name()),
formatClassName(groupToken.name()),
minCheck,
maxCheck,
countName,
generateMessageLengthCallPre17Helper(thisGroup));
}
new Formatter(sbSkip).format(
indent + (" auto &%1$sGroup { %1$s() };\n") +
indent + (" while (%1$sGroup.hasNext())\n") +
indent + (" {\n") +
indent + (" %1$sGroup.next().skip();\n") +
indent + (" }\n"),
formatPropertyName(groupToken.name()));
i = endSignal;
}
for (int i = 0, size = varData.size(); i < size;)
{
final Token varDataToken = varData.get(i);
if (varDataToken.signal() != Signal.BEGIN_VAR_DATA)
{
throw new IllegalStateException("tokens must begin with BEGIN_VAR_DATA: token=" + varDataToken);
}
final Token lengthToken = Generators.findFirst("length", varData, i);
new Formatter(sbEncode).format("\n" +
indent + " length += %1$sHeaderLength();\n" +
indent + " if (%1$sLength > %2$dLL)\n" +
indent + " {\n" +
indent + " throw std::runtime_error(\"%1$sLength too long for length type [E109]\");\n" +
indent + " }\n" +
indent + " length += %1$sLength;\n",
formatPropertyName(varDataToken.name()),
lengthToken.encoding().applicableMaxValue().longValue());
new Formatter(sbSkip).format(
indent + " skip%1$s();\n",
toUpperFirstChar(varDataToken.name()));
i += varDataToken.componentTokenCount();
}
final StringBuilder sb = new StringBuilder();
new Formatter(sb).format("\n" +
indent + "void skip()\n" +
indent + "{\n" +
sbSkip +
indent + "}\n\n" +
indent + "SBE_NODISCARD static SBE_CONSTEXPR bool isConstLength() SBE_NOEXCEPT\n" +
indent + "{\n" +
indent + " return " + (groups.isEmpty() && varData.isEmpty()) + ";\n" +
indent + "}\n\n" +
indent + "SBE_NODISCARD static std::size_t computeLength(%1$s)\n" +
indent + "{\n" +
"#if defined(__GNUG__) && !defined(__clang__)\n" +
"#pragma GCC diagnostic push\n" +
"#pragma GCC diagnostic ignored \"-Wtype-limits\"\n" +
"#endif\n" +
indent + " std::size_t length = sbeBlockLength();\n" +
sbEncode + "\n" +
indent + " return length;\n" +
"#if defined(__GNUG__) && !defined(__clang__)\n" +
"#pragma GCC diagnostic pop\n" +
"#endif\n" +
indent + "}\n",
generateMessageLengthArgs(groups, varData, indent + INDENT, true)[0]);
return sb;
}
}