diff --git a/CMakeLists.txt b/CMakeLists.txt index 52e19d3aba..fd71dfac51 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -52,6 +52,14 @@ if(NOT DEFINED CMAKE_CXX_EXTENSIONS) set(CMAKE_CXX_EXTENSIONS OFF) endif() +if(NOT DEFINED CMAKE_C_STANDARD) + set(CMAKE_C_STANDARD 11) +endif() + +if(NOT DEFINED CMAKE_C_EXTENSIONS) + set(CMAKE_C_EXTENSIONS OFF) +endif() + if(SBE_TESTS) set(SBE_THIRDPARTY_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/thirdparty") @@ -121,6 +129,8 @@ add_custom_target(sbe-jar ) set(CODEC_TARGET_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated") +set(C_CODEC_TARGET_DIR "${CODEC_TARGET_DIR}/c") +set(CXX_CODEC_TARGET_DIR "${CODEC_TARGET_DIR}/cpp") set(CODEC_SCHEMA_DIR "${CMAKE_CURRENT_SOURCE_DIR}/sbe-tool/src/test/resources") set(CODEC_PERF_SCHEMA_DIR "${CMAKE_CURRENT_SOURCE_DIR}/sbe-benchmarks/src/main/resources") set(CODEC_EXAMPLES_SCHEMA_DIR "${CMAKE_CURRENT_SOURCE_DIR}/sbe-samples/src/main/resources") @@ -128,6 +138,7 @@ set(CODEC_EXAMPLES_SCHEMA_DIR "${CMAKE_CURRENT_SOURCE_DIR}/sbe-samples/src/main/ add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/sbe-tool/src/main/cpp) if(SBE_TESTS) + add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/sbe-tool/src/test/c) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/sbe-tool/src/test/cpp) endif() diff --git a/sbe-benchmarks/src/main/cpp/CMakeLists.txt b/sbe-benchmarks/src/main/cpp/CMakeLists.txt index 03e300c4c1..0dfecbd0a5 100644 --- a/sbe-benchmarks/src/main/cpp/CMakeLists.txt +++ b/sbe-benchmarks/src/main/cpp/CMakeLists.txt @@ -29,8 +29,8 @@ endif () set(SRCS_BENCHLET_MAIN benchlet-main.cpp) set(GENERATED_CODECS - ${CODEC_TARGET_DIR}/uk_co_real_logic_sbe_examples_car - ${CODEC_TARGET_DIR}/uk_co_real_logic_sbe_samples_fix + ${CXX_CODEC_TARGET_DIR}/uk_co_real_logic_sbe_examples_car + ${CXX_CODEC_TARGET_DIR}/uk_co_real_logic_sbe_samples_fix ) set(SBE_CAR_SCHEMA ${CODEC_PERF_SCHEMA_DIR}/car.xml) @@ -39,15 +39,15 @@ set(SBE_MD_SCHEMA ${CODEC_PERF_SCHEMA_DIR}/fix-message-samples.xml) add_custom_command( OUTPUT ${GENERATED_CODECS} DEPENDS ${SBE_CAR_SCHEMA} ${SBE_MD_SCHEMA} sbe-jar ${SBE_JAR} - COMMAND ${Java_JAVA_EXECUTABLE} -Dsbe.output.dir=${CODEC_TARGET_DIR} -Dsbe.target.language="cpp" -jar ${SBE_JAR} ${SBE_CAR_SCHEMA} ${SBE_MD_SCHEMA} + COMMAND ${Java_JAVA_EXECUTABLE} -Dsbe.output.dir=${CXX_CODEC_TARGET_DIR} -Dsbe.target.language="cpp" -jar ${SBE_JAR} ${SBE_CAR_SCHEMA} ${SBE_MD_SCHEMA} ) add_custom_target(perf_codecs DEPENDS ${GENERATED_CODECS}) add_executable(benchlet-sbe-car-runner ${SRCS_BENCHLET_MAIN} CarBench.cpp) -target_include_directories(benchlet-sbe-car-runner PRIVATE ${CODEC_TARGET_DIR}) +target_include_directories(benchlet-sbe-car-runner PRIVATE ${CXX_CODEC_TARGET_DIR}) target_link_libraries(benchlet-sbe-car-runner sbe) add_executable(benchlet-sbe-md-runner ${SRCS_BENCHLET_MAIN} MarketDataBench.cpp) -target_include_directories(benchlet-sbe-md-runner PRIVATE ${CODEC_TARGET_DIR}) +target_include_directories(benchlet-sbe-md-runner PRIVATE ${CXX_CODEC_TARGET_DIR}) target_link_libraries(benchlet-sbe-md-runner sbe) add_dependencies(benchlet-sbe-md-runner perf_codecs) add_dependencies(benchlet-sbe-car-runner perf_codecs) diff --git a/sbe-samples/src/main/cpp/CMakeLists.txt b/sbe-samples/src/main/cpp/CMakeLists.txt index d639c9546b..f1a20e2e36 100644 --- a/sbe-samples/src/main/cpp/CMakeLists.txt +++ b/sbe-samples/src/main/cpp/CMakeLists.txt @@ -17,7 +17,7 @@ find_package(Java REQUIRED) set(GENERATED_CODECS - ${CODEC_TARGET_DIR}/baseline + ${CXX_CODEC_TARGET_DIR}/baseline ) set(EXAMPLES_SCHEMA ${CODEC_EXAMPLES_SCHEMA_DIR}/example-schema.xml) @@ -26,15 +26,15 @@ add_custom_command( OUTPUT ${GENERATED_CODECS} WORKING_DIRECTORY ${CODEC_EXAMPLES_SCHEMA_DIR} DEPENDS ${EXAMPLES_SCHEMA} sbe-jar ${SBE_JAR} - COMMAND ${Java_JAVA_EXECUTABLE} -Dsbe.output.dir=${CODEC_TARGET_DIR} -Dsbe.generate.ir=true -Dsbe.target.language=cpp -Dsbe.xinclude.aware=true -jar ${SBE_JAR} ${EXAMPLES_SCHEMA} + COMMAND ${Java_JAVA_EXECUTABLE} -Dsbe.output.dir=${CXX_CODEC_TARGET_DIR} -Dsbe.generate.ir=true -Dsbe.target.language=cpp -Dsbe.xinclude.aware=true -jar ${SBE_JAR} ${EXAMPLES_SCHEMA} ) add_custom_target(examples_codecs DEPENDS ${GENERATED_CODECS}) add_executable(GeneratedStubExample GeneratedStubExample.cpp) -target_include_directories(GeneratedStubExample PRIVATE ${CODEC_TARGET_DIR}) +target_include_directories(GeneratedStubExample PRIVATE ${CXX_CODEC_TARGET_DIR}) target_link_libraries(GeneratedStubExample sbe) add_executable(OtfExample OtfExample.cpp) -target_include_directories(OtfExample PRIVATE ${CODEC_TARGET_DIR}) +target_include_directories(OtfExample PRIVATE ${CXX_CODEC_TARGET_DIR}) target_link_libraries(OtfExample sbe) add_dependencies(GeneratedStubExample examples_codecs) diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/TargetCodeGeneratorLoader.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/TargetCodeGeneratorLoader.java index fc800c9aad..b90813af2a 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/TargetCodeGeneratorLoader.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/TargetCodeGeneratorLoader.java @@ -16,6 +16,8 @@ package uk.co.real_logic.sbe.generation; import org.agrona.generation.PackageOutputManager; +import uk.co.real_logic.sbe.generation.c.CGenerator; +import uk.co.real_logic.sbe.generation.c.COutputManager; import uk.co.real_logic.sbe.generation.cpp.CppGenerator; import uk.co.real_logic.sbe.generation.cpp.NamespaceOutputManager; import uk.co.real_logic.sbe.generation.golang.GolangGenerator; @@ -42,6 +44,14 @@ public CodeGenerator newInstance(final Ir ir, final String outputDir) } }, + C() + { + public CodeGenerator newInstance(final Ir ir, final String outputDir) + { + return new CGenerator(ir, new COutputManager(outputDir, ir.applicableNamespace())); + } + }, + CPP() { public CodeGenerator newInstance(final Ir ir, final String outputDir) diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/c/CGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/c/CGenerator.java new file mode 100755 index 0000000000..31f67db08a --- /dev/null +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/c/CGenerator.java @@ -0,0 +1,2424 @@ +package uk.co.real_logic.sbe.generation.c; + +import org.agrona.Verify; +import org.agrona.generation.OutputManager; +import uk.co.real_logic.sbe.PrimitiveType; +import uk.co.real_logic.sbe.generation.CodeGenerator; +import uk.co.real_logic.sbe.generation.Generators; +import uk.co.real_logic.sbe.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 java.io.IOException; +import java.io.Writer; +import java.nio.ByteOrder; +import java.util.ArrayList; +import java.util.List; + +import static uk.co.real_logic.sbe.generation.c.CUtil.*; +import static uk.co.real_logic.sbe.ir.GenerationUtil.*; + +@SuppressWarnings("MethodLength") +public class CGenerator implements CodeGenerator +{ + private final Ir ir; + private final OutputManager outputManager; + + public CGenerator(final Ir ir, final OutputManager outputManager) + { + Verify.notNull(ir, "ir"); + Verify.notNull(outputManager, "outputManager"); + + this.ir = ir; + this.outputManager = outputManager; + } + + public void generateMessageHeaderStub() throws IOException + { + generateComposite(ir.namespaces(), ir.headerStructure().tokens()); + } + + public List generateTypeStubs(final CharSequence[] scope) throws IOException + { + final List typesToInclude = new ArrayList<>(); + + for (final List tokens : ir.types()) + { + switch (tokens.get(0).signal()) + { + case BEGIN_ENUM: + generateEnum(scope, tokens); + break; + + case BEGIN_SET: + generateChoiceSet(scope, tokens); + break; + + case BEGIN_COMPOSITE: + generateComposite(scope, tokens); + break; + } + + typesToInclude.add(tokens.get(0).applicableTypeName()); + } + + return typesToInclude; + } + + public 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; + } + } + + return typesToInclude; + } + + public void generate() throws IOException + { + generateMessageHeaderStub(); + final List typesToInclude = generateTypeStubs(ir.namespaces()); + + for (final List tokens : ir.messages()) + { + final Token msgToken = tokens.get(0); + + try (Writer out = outputManager.createOutput(formatName(msgToken.name()))) + { + final String structName = formatScopedName(ir.namespaces(), msgToken.name()); + out.append(generateFileHeader(structName, typesToInclude)); + + 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); + + final StringBuilder sb = new StringBuilder(); + generateGroupStructs(sb, ir.namespaces(), groups, structName); + + out.append(sb); + out.append(generateStructDeclaration(structName)); + out.append(generateMessageFlyweightMembers()); + out.append(generateFieldMembers(ir.namespaces(), fields)); + sb.setLength(0); + generateGroupPropertyMembers(sb, groups, structName); + out.append(sb); + out.append("};\n"); + out.append(String.format("\n" + + "enum %1$s_meta_attribute\n" + + "{\n" + + " %1$s_meta_attribute_EPOCH,\n" + + " %1$s_meta_attribute_TIME_UNIT,\n" + + " %1$s_meta_attribute_SEMANTIC_TYPE,\n" + + " %1$s_meta_attribute_PRESENCE\n" + + "};\n\n" + + "union %1$s_float_as_uint\n" + + "{\n" + + " float fp_value;\n" + + " uint32_t uint_value;\n" + + "};\n\n" + + "union %1$s_double_as_uint\n" + + "{\n" + + " double fp_value;\n" + + " uint64_t uint_value;\n" + + "};\n", + structName)); + + out.append(generateMessageFlyweightFunctions(structName, msgToken, ir.namespaces())); + + out.append(generateFieldFunctions(ir.namespaces(), structName, structName, fields)); + sb.setLength(0); + generateGroupFunctions(sb, ir.namespaces(), groups, structName, structName); + out.append(sb); + out.append(generateVarData(structName, structName, varData)); + out.append("\n#endif"); + } + } + } + + private void generateGroupStructs( + final StringBuilder sb, final CharSequence[] scope, final List tokens, final String outerStruct) + { + 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 int currentTokenIdx = i; + + ++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 String groupName = outerStruct + '_' + formatName(groupToken.name()); + generateGroupStructs(sb, scope, groups, groupName); + + final List varData = new ArrayList<>(); + i = collectVarData(tokens, i, varData); + + generateGroupStructMembers(sb, scope, groupName, tokens, currentTokenIdx); + generateGroupPropertyMembers(sb, groups, groupName); + sb.append("};\n"); + } + } + + private static void generateGroupPropertyMembers( + final StringBuilder sb, final List tokens, final String outerStruct) + { + 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); + } + + ++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<>(); + i = collectVarData(tokens, i, varData); + + final String groupName = outerStruct + '_' + formatName(groupToken.name()); + final String propertyName = formatPropertyName(groupToken.name()); + + sb.append(String.format( + " struct %1$s %2$s;\n", + groupName, + propertyName)); + } + } + + private void generateGroupFunctions( + final StringBuilder sb, + final CharSequence[] scope, + final List tokens, + final String outerStruct, + final String outermostStruct) + { + 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 = outerStruct + '_' + formatName(groupToken.name()); + final Token numInGroupToken = Generators.findFirst("numInGroup", tokens, i); + final String cTypeForNumInGroup = cTypeName(numInGroupToken.encoding().primitiveType()); + + generateGroupHeaderFunctions(sb, scope, groupName, tokens, i); + + ++i; + final int groupHeaderTokenCount = tokens.get(i).componentTokenCount(); + i += groupHeaderTokenCount; + + final List fields = new ArrayList<>(); + i = collectFields(tokens, i, fields); + sb.append(generateFieldFunctions(scope, groupName, outermostStruct, fields)); + + final List groups = new ArrayList<>(); + i = collectGroups(tokens, i, groups); + generateGroupFunctions(sb, scope, groups, groupName, outermostStruct); + + final List varData = new ArrayList<>(); + i = collectVarData(tokens, i, varData); + sb.append(generateVarData(groupName, outermostStruct, varData)); + + sb.append(generateGroupPropertyFunctions(outerStruct, groupName, groupToken, cTypeForNumInGroup)); + } + } + + private static void generateGroupStructMembers( + final StringBuilder sb, + final CharSequence[] scope, + final String groupName, + final List tokens, + final int index) + { + final String dimensionsStructName = formatScopedName(scope, tokens.get(index + 1).name()); + + sb.append(String.format("\n" + + "struct %1$s\n" + + "{\n" + + " char *buffer;\n" + + " uint64_t buffer_length;\n" + + " uint64_t *position_ptr;\n" + + " uint64_t block_length;\n" + + " uint64_t count;\n" + + " uint64_t index;\n" + + " uint64_t offset;\n" + + " uint64_t acting_version;\n" + + " struct %2$s dimensions;\n", + groupName, dimensionsStructName)); + } + + private static void generateGroupHeaderFunctions( + final StringBuilder sb, + final CharSequence[] scope, + final String groupName, + final List tokens, + final int index) + { + final String dimensionsStructName = formatScopedName(scope, 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 cTypeForBlockLength = cTypeName(blockLengthToken.encoding().primitiveType()); + final String cTypeForNumInGroup = cTypeName(numInGroupToken.encoding().primitiveType()); + + sb.append(String.format("\n" + + "SBE_ONE_DEF struct %1$s *%1$s_wrap_for_decode(\n" + + " struct %1$s *const codec,\n" + + " char *const buffer,\n" + + " uint64_t *const pos,\n" + + " const uint64_t acting_version,\n" + + " const uint64_t buffer_length)\n" + + "{\n" + + " codec->buffer = buffer;\n" + + " codec->buffer_length = buffer_length;\n" + + " if (!%2$s_wrap(&codec->dimensions, codec->buffer, *pos, acting_version, buffer_length))\n" + + " {\n" + + " return NULL;\n" + + " }\n" + + " codec->block_length = %2$s_blockLength(&codec->dimensions);\n" + + " codec->count = %2$s_numInGroup(&codec->dimensions);\n" + + " codec->index = -1;\n" + + " codec->acting_version = acting_version;\n" + + " codec->position_ptr = pos;\n" + + " *codec->position_ptr = *codec->position_ptr + %3$d;\n" + + " return codec;\n" + + "}\n", + groupName, dimensionsStructName, dimensionHeaderLength)); + + final long minCount = numInGroupToken.encoding().applicableMinValue().longValue(); + final String minCheck = minCount > 0 ? "count < " + minCount + " || " : ""; + + sb.append(String.format("\n" + + "SBE_ONE_DEF struct %1$s *%1$s_wrap_for_encode(\n" + + " struct %1$s *const codec,\n" + + " char *const buffer,\n" + + " const %4$s count,\n" + + " uint64_t *const pos,\n" + + " const uint64_t acting_version,\n" + + " const uint64_t buffer_length)\n" + + "{\n" + + "#if defined(__GNUG__) && !defined(__clang__)\n" + + "#pragma GCC diagnostic push\n" + + "#pragma GCC diagnostic ignored \"-Wtype-limits\"\n" + + "#endif\n" + + " if (%7$scount > %8$d)\n" + + " {\n" + + " errno = E110;\n" + + " return NULL;\n" + + " }\n" + + "#if defined(__GNUG__) && !defined(__clang__)\n" + + "#pragma GCC diagnostic pop\n" + + "#endif\n" + + " codec->buffer = buffer;\n" + + " codec->buffer_length = buffer_length;\n" + + " if (!%5$s_wrap(&codec->dimensions, codec->buffer, *pos, acting_version, buffer_length))\n" + + " {\n" + + " return NULL;\n" + + " }\n" + + " %5$s_set_blockLength(&codec->dimensions, (%2$s)%3$d);\n" + + " %5$s_set_numInGroup(&codec->dimensions, (%4$s)count);\n" + + " codec->index = -1;\n" + + " codec->count = count;\n" + + " codec->block_length = %3$d;\n" + + " codec->acting_version = acting_version;\n" + + " codec->position_ptr = pos;\n" + + " *codec->position_ptr = *codec->position_ptr + %6$d;\n" + + " return codec;\n" + + "}\n", + groupName, cTypeForBlockLength, blockLength, cTypeForNumInGroup, + dimensionsStructName, dimensionHeaderLength, + minCheck, + numInGroupToken.encoding().applicableMaxValue().longValue())); + + sb.append(String.format("\n" + + "SBE_ONE_DEF uint64_t %3$s_sbe_header_size(void)\n" + + "{\n" + + " return %1$d;\n" + + "}\n\n" + + "SBE_ONE_DEF uint64_t %3$s_sbe_block_length(void)\n" + + "{\n" + + " return %2$d;\n" + + "}\n\n" + + "SBE_ONE_DEF uint64_t %3$s_sbe_position(const struct %3$s *const codec)\n" + + "{\n" + + " return *codec->position_ptr;\n" + + "}\n\n" + + "SBE_ONE_DEF bool %3$s_set_sbe_position(struct %3$s *const codec, const uint64_t position)\n" + + "{\n" + + " if (SBE_BOUNDS_CHECK_EXPECT((position > codec->buffer_length), false))\n" + + " {\n" + + " errno = E100;\n" + + " return false;\n" + + " }\n" + + " *codec->position_ptr = position;\n" + + " return true;\n" + + "}\n\n" + + "SBE_ONE_DEF uint64_t %3$s_count(const struct %3$s *const codec)\n" + + "{\n" + + " return codec->count;\n" + + "}\n\n" + + "SBE_ONE_DEF bool %3$s_has_next(const struct %3$s *const codec)\n" + + "{\n" + + " return codec->index + 1 < codec->count;\n" + + "}\n\n" + + "SBE_ONE_DEF struct %3$s *%3$s_next(struct %3$s *const codec)\n" + + "{\n" + + " codec->offset = *codec->position_ptr;\n" + + " if (SBE_BOUNDS_CHECK_EXPECT(((codec->offset + codec->block_length) " + + "> codec->buffer_length), false))\n" + + " {\n" + + " errno = E108;\n" + + " return NULL;\n" + + " }\n" + + " *codec->position_ptr = codec->offset + codec->block_length;\n" + + " ++codec->index;\n\n" + + " return codec;\n" + + "}\n\n" + + "SBE_ONE_DEF struct %3$s *%3$s_for_each(\n" + + " struct %3$s *const codec,\n" + + " void (*func)(struct %3$s *, void *),\n" + + " void *const context)\n" + + "{\n" + + " while (%3$s_has_next(codec))\n" + + " {\n" + + " if (!%3$s_next(codec))\n" + + " {\n" + + " return NULL;\n" + + " }\n" + + " func(codec, context);\n" + + " }\n" + + " return codec;\n" + + "}\n", + dimensionHeaderLength, blockLength, groupName)); + } + + private static CharSequence generateGroupPropertyFunctions( + final String outerStruct, final String groupName, final Token token, final String cTypeForNumInGroup) + { + final StringBuilder sb = new StringBuilder(); + + final String structName = groupName; + final String propertyName = formatPropertyName(token.name()); + + sb.append(String.format("\n" + + "SBE_ONE_DEF uint16_t %1$s_id()\n" + + "{\n" + + " return %2$d;\n" + + "}\n", + groupName, + token.id())); + + sb.append(String.format("\n" + + "SBE_ONE_DEF struct %2$s *%1$s_get_%3$s(struct %1$s *const codec)\n" + + "{\n" + + " return %2$s_wrap_for_decode(\n" + + " &codec->%3$s,\n" + + " codec->buffer,\n" + + " codec->position_ptr,\n" + + " codec->acting_version,\n" + + " codec->buffer_length);\n" + + "}\n", + outerStruct, + structName, + propertyName)); + + sb.append(String.format("\n" + + "SBE_ONE_DEF struct %2$s *%2$s_set_count(struct %1$s *const codec, const %4$s count)\n" + + "{\n" + + " return %2$s_wrap_for_encode(\n" + + " &codec->%3$s,\n" + + " codec->buffer,\n" + + " count,\n" + + " codec->position_ptr,\n" + + " codec->acting_version,\n" + + " codec->buffer_length);\n" + + "}\n", + outerStruct, + structName, + propertyName, + cTypeForNumInGroup)); + + sb.append(String.format("\n" + + "SBE_ONE_DEF uint64_t %2$s_since_version(void)\n" + + "{\n" + + " return %3$d;\n" + + "}\n\n" + + "SBE_ONE_DEF bool %2$s_in_acting_version(struct %1$s *const codec)\n" + + "{\n" + + "#if defined(__clang__)\n" + + "#pragma clang diagnostic push\n" + + "#pragma clang diagnostic ignored \"-Wtautological-compare\"\n" + + "#endif\n" + + " return codec->acting_version >= %2$s_since_version();\n" + + "#if defined(__clang__)\n" + + "#pragma clang diagnostic pop\n" + + "#endif\n" + + "}\n", + outerStruct, + structName, + token.version())); + + return sb; + } + + private CharSequence generateVarData( + final String structName, + final String outermostStruct, + final List tokens) + { + final StringBuilder sb = new StringBuilder(); + + 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 = formatPropertyName(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 lengthCType = cTypeName(lengthToken.encoding().primitiveType()); + final String lengthByteOrderStr = formatByteOrderEncoding( + lengthToken.encoding().byteOrder(), lengthToken.encoding().primitiveType()); + + generateFieldMetaAttributeFunction(sb, token, structName, outermostStruct); + + generateVarDataDescriptors( + sb, token, propertyName, characterEncoding, lengthToken, + lengthOfLengthField, lengthCType, structName); + + sb.append(String.format("\n" + + "SBE_ONE_DEF const char *%6$s_%1$s(struct %6$s *const codec)\n" + + "{\n" + + "%2$s" + + " %4$s length_field_value;\n" + + " memcpy(&length_field_value, codec->buffer + %6$s_sbe_position(codec), sizeof(%4$s));\n" + + " const char *field_ptr = (codec->buffer + %6$s_sbe_position(codec) + %3$d);\n" + + " if (!%6$s_set_sbe_position(\n" + + " codec, %6$s_sbe_position(codec) + %3$d + %5$s(length_field_value)))\n" + + " {\n" + + " return NULL;\n" + + " }\n" + + " return field_ptr;\n" + + "}\n", + propertyName, + generateTypeFieldNotPresentCondition(token.version()), + lengthOfLengthField, + lengthCType, + lengthByteOrderStr, + structName)); + + sb.append(String.format("\n" + + "SBE_ONE_DEF uint64_t %6$s_get_%1$s(\n" + + " struct %6$s *const codec,\n" + + " char *dst,\n" + + " const uint64_t length)\n" + + "{\n" + + "%2$s" + + " uint64_t length_of_length_field = %3$d;\n" + + " uint64_t length_position = %6$s_sbe_position(codec);\n" + + " if (!%6$s_set_sbe_position(codec, length_position + length_of_length_field))\n" + + " {\n" + + " return 0;\n" + + " }\n" + + " %5$s length_field_value;\n" + + " memcpy(&length_field_value, codec->buffer + length_position, sizeof(%5$s));\n" + + " uint64_t data_length = %4$s(length_field_value);\n" + + " uint64_t bytes_to_copy = length < data_length ? length : data_length;\n" + + " uint64_t pos = %6$s_sbe_position(codec);\n" + + " if (!%6$s_set_sbe_position(codec, pos + data_length))\n" + + " {\n" + + " return 0;\n" + + " }\n" + + " memcpy(dst, codec->buffer + pos, bytes_to_copy);\n" + + " return bytes_to_copy;\n" + + "}\n", + propertyName, + generateArrayFieldNotPresentCondition(token.version()), + lengthOfLengthField, + lengthByteOrderStr, + lengthCType, + structName)); + + sb.append(String.format("\n" + + "SBE_ONE_DEF struct %5$s *%5$s_put_%1$s(\n" + + " struct %5$s *const codec,\n" + + " const char *src,\n" + + " const %3$s length)\n" + + "{\n" + + " uint64_t length_of_length_field = %2$d;\n" + + " uint64_t length_position = %5$s_sbe_position(codec);\n" + + " %3$s length_field_value = %4$s(length);\n" + + " if (!%5$s_set_sbe_position(codec, length_position + length_of_length_field))\n" + + " {\n" + + " return NULL;\n" + + " }\n" + + " memcpy(codec->buffer + length_position, &length_field_value, sizeof(%3$s));\n" + + " uint64_t pos = %5$s_sbe_position(codec);\n" + + " if (!%5$s_set_sbe_position(codec, pos + length))\n" + + " {\n" + + " return NULL;\n" + + " }\n" + + " memcpy(codec->buffer + pos, src, length);\n" + + " return codec;\n" + + "}\n", + propertyName, + lengthOfLengthField, + lengthCType, + lengthByteOrderStr, + structName)); + + i += token.componentTokenCount(); + } + + return sb; + } + + private void generateVarDataDescriptors( + final StringBuilder sb, + final Token token, + final String propertyName, + final String characterEncoding, + final Token lengthToken, + final Integer sizeOfLengthField, + final String lengthCType, + final String structName) + { + final String fullyQualifiedPropertyName = structName + "_" + propertyName; + + sb.append(String.format("\n" + + "SBE_ONE_DEF const char *%s_character_encoding(void)\n" + + "{\n" + + " return \"%s\";\n" + + "}\n", + fullyQualifiedPropertyName, + characterEncoding)); + + sb.append(String.format("\n" + + "SBE_ONE_DEF uint64_t %1$s_since_version(void)\n" + + "{\n" + + " return %2$d;\n" + + "}\n\n" + + "SBE_ONE_DEF bool %1$s_in_acting_version(const struct %4$s *const codec)\n" + + "{\n" + + "#if defined(__clang__)\n" + + "#pragma clang diagnostic push\n" + + "#pragma clang diagnostic ignored \"-Wtautological-compare\"\n" + + "#endif\n" + + " return codec->acting_version >= %1$s_since_version();\n" + + "#if defined(__clang__)\n" + + "#pragma clang diagnostic pop\n" + + "#endif\n" + + "}\n\n" + + "SBE_ONE_DEF uint16_t %1$s_id(void)\n" + + "{\n" + + " return %3$d;\n" + + "}\n", + fullyQualifiedPropertyName, + token.version(), + token.id(), + structName)); + + sb.append(String.format("\n" + + "SBE_ONE_DEF uint64_t %s_header_length(void)\n" + + "{\n" + + " return %d;\n" + + "}\n", + fullyQualifiedPropertyName, + sizeOfLengthField)); + + sb.append(String.format("\n" + + "SBE_ONE_DEF %4$s %1$s_length(const struct %5$s *const codec)\n" + + "{\n" + + "%2$s" + + " %4$s length;\n" + + " memcpy(&length, codec->buffer + %5$s_sbe_position(codec), sizeof(%4$s));\n" + + " return %3$s(length);\n" + + "}\n", + fullyQualifiedPropertyName, + generateArrayFieldNotPresentCondition(token.version()), + formatByteOrderEncoding(lengthToken.encoding().byteOrder(), lengthToken.encoding().primitiveType()), + lengthCType, + structName)); + } + + private void generateChoiceSet(final CharSequence[] scope, final List tokens) throws IOException + { + final Token bitsetToken = tokens.get(0); + + try (Writer out = outputManager.createOutput(formatName(bitsetToken.applicableTypeName()))) + { + final String bitSetName = formatScopedName(scope, bitsetToken.applicableTypeName()); + out.append(generateFileHeader(bitSetName, null)); + out.append(generateStructDeclaration(bitSetName)); + out.append(generateFixedFlyweightCodeMembers()); + out.append("};\n"); + out.append(String.format("\n" + + "enum %1$s_meta_attribute\n" + + "{\n" + + " %1$s_meta_attribute_EPOCH,\n" + + " %1$s_meta_attribute_TIME_UNIT,\n" + + " %1$s_meta_attribute_SEMANTIC_TYPE,\n" + + " %1$s_meta_attribute_PRESENCE\n" + + "};\n\n" + + "union %1$s_float_as_uint\n" + + "{\n" + + " float fp_value;\n" + + " uint32_t uint_value;\n" + + "};\n\n" + + "union %1$s_double_as_uint\n" + + "{\n" + + " double fp_value;\n" + + " uint64_t uint_value;\n" + + "};\n", + bitSetName)); + out.append(generateFixedFlyweightCodeFunctions(bitSetName, bitsetToken.encodedLength())); + + out.append(String.format("\n" + + "SBE_ONE_DEF struct %1$s *%1$s_clear(struct %1$s *const codec)\n" + + "{\n" + + " %2$s zero = 0;\n" + + " memcpy(codec->buffer + codec->offset, &zero, sizeof(%2$s));\n" + + " return codec;\n" + + "}\n", + bitSetName, + cTypeName(bitsetToken.encoding().primitiveType()))); + + out.append(String.format("\n" + + "SBE_ONE_DEF bool %1$s_is_empty(const struct %1$s *const codec)\n" + + "{\n" + + " %2$s val;\n" + + " memcpy(&val, codec->buffer + codec->offset, sizeof(%2$s));\n" + + " return 0 == val;\n" + + "}\n", + bitSetName, + cTypeName(bitsetToken.encoding().primitiveType()))); + + out.append(generateChoices(bitSetName, tokens.subList(1, tokens.size() - 1))); + out.append("\n#endif"); + } + } + + private void generateEnum(final CharSequence[] scope, final List tokens) throws IOException + { + final Token enumToken = tokens.get(0); + + try (Writer out = outputManager.createOutput(formatName(enumToken.applicableTypeName()))) + { + final String enumName = formatScopedName(scope, enumToken.applicableTypeName()); + + out.append(generateFileHeader(enumName, null)); + + out.append(generateEnumValues(scope, tokens.subList(1, tokens.size() - 1), enumToken)); + + out.append(generateEnumLookupFunction(scope, tokens.subList(1, tokens.size() - 1), enumToken)); + + out.append("\n#endif"); + } + } + + private void generateComposite(final CharSequence[] scope, final List tokens) throws IOException + { + final Token compositeToken = tokens.get(0); + + try (Writer out = outputManager.createOutput(formatName(compositeToken.applicableTypeName()))) + { + final String compositeName = formatScopedName(scope, compositeToken.applicableTypeName()); + + out.append(generateFileHeader( + compositeName, + generateTypesToIncludes(tokens.subList(1, tokens.size() - 1)))); + out.append(generateStructDeclaration(compositeName)); + out.append(generateFixedFlyweightCodeMembers()); + out.append(generateCompositePropertyMembers( + scope, compositeName, tokens.subList(1, tokens.size() - 1))); + out.append("};\n"); + out.append(String.format("\n" + + "enum %1$s_meta_attribute\n" + + "{\n" + + " %1$s_meta_attribute_EPOCH,\n" + + " %1$s_meta_attribute_TIME_UNIT,\n" + + " %1$s_meta_attribute_SEMANTIC_TYPE,\n" + + " %1$s_meta_attribute_PRESENCE\n" + + "};\n\n" + + "union %1$s_float_as_uint\n" + + "{\n" + + " float fp_value;\n" + + " uint32_t uint_value;\n" + + "};\n\n" + + "union %1$s_double_as_uint\n" + + "{\n" + + " double fp_value;\n" + + " uint64_t uint_value;\n" + + "};\n", + compositeName)); + + out.append(generateFixedFlyweightCodeFunctions(compositeName, compositeToken.encodedLength())); + out.append(generateCompositePropertyFunctions( + scope, compositeName, tokens.subList(1, tokens.size() - 1))); + + out.append("\n#endif"); + } + } + + private static CharSequence generateChoiceNotPresentCondition(final int sinceVersion) + { + if (0 == sinceVersion) + { + return ""; + } + + return String.format( + "if (codec->acting_version < %1$d)\n" + + " {\n" + + " return false;\n" + + " }\n\n", + sinceVersion); + } + + private CharSequence generateChoices(final String bitsetStructName, 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 String typeName = cTypeName(token.encoding().primitiveType()); + final String choiceBitPosition = token.encoding().constValue().toString(); + final String byteOrderStr = formatByteOrderEncoding( + token.encoding().byteOrder(), token.encoding().primitiveType()); + + sb.append(String.format("\n" + + "SBE_ONE_DEF bool %1$s_check_%2$s_bit(const %3$s bits)\n" + + "{\n" + + " return bits & ((%3$s)1 << %4$s);\n" + + "}\n", + bitsetStructName, + choiceName, + typeName, + choiceBitPosition)); + + sb.append(String.format("\n" + + "SBE_ONE_DEF %3$s %1$s_apply_%2$s_bit(const %3$s bits, const bool value)\n" + + "{\n" + + " return value ?" + + " (bits | ((%3$s)1 << %4$s)) : (bits & ~((%3$s)1 << %4$s));\n" + + "}\n", + bitsetStructName, + choiceName, + typeName, + choiceBitPosition)); + + sb.append(String.format("\n" + + "SBE_ONE_DEF bool %1$s_%2$s(const struct %1$s *const codec)\n" + + "{\n" + + "%3$s" + + " %5$s val;\n" + + " memcpy(&val, codec->buffer + codec->offset, sizeof(%5$s));\n" + + " return %4$s(val) & ((%5$s)1 << %6$s);\n" + + "}\n", + bitsetStructName, + choiceName, + generateChoiceNotPresentCondition(token.version()), + byteOrderStr, + typeName, + choiceBitPosition)); + + sb.append(String.format("\n" + + "SBE_ONE_DEF struct %1$s *%1$s_set_%2$s(struct %1$s *const codec, const bool value)\n" + + "{\n" + + " %3$s bits;\n" + + " memcpy(&bits, codec->buffer + codec->offset, sizeof(%3$s));\n" + + " bits = %4$s(value ?" + + " (%4$s(bits) | ((%3$s)1 << %5$s)) " + + ": (%4$s(bits) & ~((%3$s)1 << %5$s)));\n" + + " memcpy(codec->buffer + codec->offset, &bits, sizeof(%3$s));\n" + + " return codec;\n" + + "}\n", + bitsetStructName, + choiceName, + typeName, + byteOrderStr, + choiceBitPosition)); + }); + + return sb; + } + + private CharSequence generateEnumValues( + final CharSequence[] scope, + final List tokens, + final Token encodingToken) + { + final StringBuilder sb = new StringBuilder(); + final Encoding encoding = encodingToken.encoding(); + final String enumName = formatScopedName(scope, encodingToken.applicableTypeName()); + + sb.append(String.format("\n" + + "enum %1$s\n" + + "{\n", + enumName)); + + for (final Token token : tokens) + { + final CharSequence constVal = generateLiteral( + token.encoding().primitiveType(), token.encoding().constValue().toString()); + sb.append(String.format( + " %s_%s = %s,\n", + enumName, + token.name(), + constVal)); + } + + sb.append(String.format( + " %s_NULL_VALUE = %s\n", + enumName, + generateLiteral(encoding.primitiveType(), encoding.applicableNullValue().toString()))); + + sb.append("};\n\n"); + + return sb; + } + + private static CharSequence generateEnumLookupFunction( + final CharSequence[] scope, + final List tokens, + final Token encodingToken) + { + final String enumName = formatScopedName(scope, encodingToken.applicableTypeName()); + final StringBuilder sb = new StringBuilder(); + + sb.append(String.format( + "SBE_ONE_DEF bool %1$s_get(const %2$s value, enum %1$s *const out)\n" + + "{\n" + + " switch (value)\n" + + " {\n", + enumName, + cTypeName(tokens.get(0).encoding().primitiveType()))); + + for (final Token token : tokens) + { + sb.append(String.format( + " case %s:\n" + + " *out = %s_%s;\n" + + " return true;\n", + token.encoding().constValue().toString(), + enumName, + token.name())); + } + + sb.append(String.format( + " case %s:\n" + + " *out = %s_NULL_VALUE;\n" + + " return true;\n" + + " }\n\n" + + " errno = E103;\n" + + " return false;\n" + + "}\n", + encodingToken.encoding().applicableNullValue().toString(), + enumName)); + + return sb; + } + + private CharSequence generateFieldNotPresentCondition( + final int sinceVersion, final Encoding encoding) + { + if (0 == sinceVersion) + { + return ""; + } + + return String.format( + "if (codec->acting_version < %1$d)\n" + + " {\n" + + " return %2$s;\n" + + " }\n\n", + sinceVersion, + generateLiteral(encoding.primitiveType(), encoding.applicableNullValue().toString())); + } + + private static CharSequence generateArrayFieldNotPresentCondition(final int sinceVersion) + { + if (0 == sinceVersion) + { + return ""; + } + + return String.format( + "if (codec->acting_version < %1$d)\n" + + "{\n" + + " return 0;\n" + + "}\n\n", + sinceVersion); + } + + private static CharSequence generateTypeFieldNotPresentCondition(final int sinceVersion) + { + if (0 == sinceVersion) + { + return ""; + } + + return String.format( + "if (codec->acting_version < %1$d)\n" + + "{\n" + + " return NULL;\n" + + "}\n\n", + sinceVersion); + } + + private static CharSequence generateFileHeader( + final String structName, + 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_H_\n" + + "#define _%1$s_H_\n\n" + + "#include \n" + + "#if !defined(__STDC_LIMIT_MACROS)\n" + + "#define __STDC_LIMIT_MACROS 1\n" + + "#endif\n" + + "#include \n" + + "#define SBE_FLOAT_NAN NAN\n" + + "#define SBE_DOUBLE_NAN NAN\n" + + "#include \n" + + "#include \n" + + "#include \n" + + "#include \n", + structName.toUpperCase())); + + if (typesToInclude != null && typesToInclude.size() != 0) + { + sb.append("\n"); + for (final String incName : typesToInclude) + { + sb.append(String.format("#include \"%1$s.h\"\n", toLowerFirstChar(incName))); + } + } + + sb.append("\n" + + "#ifdef __cplusplus\n" + + "#define SBE_ONE_DEF inline\n" + + "#else\n" + + "#define SBE_ONE_DEF static inline\n" + + "#endif\n\n" + + "/*\n" + + " * Define some byte ordering macros\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_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_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" + + "#define SBE_NULLVALUE_INT8 INT8_MIN\n" + + "#define SBE_NULLVALUE_INT16 INT16_MIN\n" + + "#define SBE_NULLVALUE_INT32 INT32_MIN\n" + + "#define SBE_NULLVALUE_INT64 INT64_MIN\n" + + "#define SBE_NULLVALUE_UINT8 UINT8_MAX\n" + + "#define SBE_NULLVALUE_UINT16 UINT16_MAX\n" + + "#define SBE_NULLVALUE_UINT32 UINT32_MAX\n" + + "#define SBE_NULLVALUE_UINT64 UINT64_MAX\n\n" + + "#define E100 -50100 // E_BUF_SHORT\n" + + "#define E103 -50103 // VAL_UNKNWN_ENUM\n" + + "#define E104 -50104 // I_OUT_RANGE_NUM\n" + + "#define E105 -50105 // I_OUT_RANGE_NUM\n" + + "#define E106 -50106 // I_OUT_RANGE_NUM\n" + + "#define E107 -50107 // BUF_SHORT_FLYWEIGHT\n" + + "#define E108 -50108 // BUF_SHORT_NXT_GRP_IND\n" + + "#define E109 -50109 // STR_TOO_LONG_FOR_LEN_TYP\n" + + "#define E110 -50110 // CNT_OUT_RANGE\n\n" + + "#ifndef SBE_STRERROR_DEFINED\n" + + "#define SBE_STRERROR_DEFINED\n" + + "SBE_ONE_DEF const char *sbe_strerror(const int errnum)\n" + + "{\n" + + " switch (errnum)\n" + + " {\n" + + " case E100:\n" + + " return \"buffer too short\";\n" + + " case E103:\n" + + " return \"unknown value for enum\";\n" + + " case E104:\n" + + " return \"index out of range\";\n" + + " case E105:\n" + + " return \"index out of range\";\n" + + " case E106:\n" + + " return \"length too large\";\n" + + " case E107:\n" + + " return \"buffer too short for flyweight\";\n" + + " case E108:\n" + + " return \"buffer too short to support next group index\";\n" + + " case E109:\n" + + " return \"std::string too long for length type\";\n" + + " case E110:\n" + + " return \"count outside of allowed range\";\n" + + " default:\n" + + " return \"unknown error\";\n" + + " }\n" + + "}\n" + + "#endif\n"); + + return sb; + } + + private static CharSequence generateStructDeclaration(final String structName) + { + return String.format("\n" + + "struct %s\n" + + "{\n", + structName); + } + + private void generatePropertyMembers( + final StringBuilder sb, + final CharSequence[] scope, + final Token signalToken, + final String propertyName, + final Token encodingToken) + { + switch (encodingToken.signal()) + { + case BEGIN_SET: + sb.append(generateBitsetPropertyMember(scope, propertyName, encodingToken)); + break; + + case BEGIN_COMPOSITE: + sb.append(generateCompositePropertyMember(scope, propertyName, encodingToken)); + break; + } + } + + private void generatePropertyFunctions( + final StringBuilder sb, + final CharSequence[] scope, + final String containingStructName, + final String outermostStruct, + final Token signalToken, + final String propertyName, + final Token encodingToken) + { + switch (encodingToken.signal()) + { + case ENCODING: + sb.append(generatePrimitiveProperty( + containingStructName, + outermostStruct, + propertyName, + encodingToken)); + break; + + case BEGIN_ENUM: + sb.append(generateEnumProperty( + scope, + containingStructName, + signalToken, + propertyName, + encodingToken)); + break; + + case BEGIN_SET: + sb.append(generateBitsetPropertyFunctions( + scope, + propertyName, + encodingToken, + containingStructName)); + break; + + case BEGIN_COMPOSITE: + sb.append(generateCompositePropertyFunction( + scope, + propertyName, + encodingToken, + containingStructName)); + break; + } + } + + private CharSequence generateCompositePropertyMembers( + final CharSequence[] scope, + final String containingStructName, + final List tokens) + { + final StringBuilder sb = new StringBuilder(); + + for (int i = 0; i < tokens.size();) + { + final Token fieldToken = tokens.get(i); + final String propertyName = formatPropertyName(fieldToken.name()); + + generatePropertyMembers(sb, scope, fieldToken, propertyName, fieldToken); + + i += tokens.get(i).componentTokenCount(); + } + + return sb; + } + + private CharSequence generateCompositePropertyFunctions( + final CharSequence[] scope, + final String containingStructName, + final List tokens) + { + final StringBuilder sb = new StringBuilder(); + + for (int i = 0; i < tokens.size();) + { + final Token fieldToken = tokens.get(i); + final String propertyName = formatPropertyName(fieldToken.name()); + + generateFieldMetaAttributeFunction( + sb, + fieldToken, + containingStructName, + containingStructName); + generateFieldCommonFunctions( + sb, + fieldToken, + fieldToken, + propertyName, + containingStructName); + generatePropertyFunctions( + sb, + scope, + containingStructName, + containingStructName, + fieldToken, + propertyName, + fieldToken); + + i += tokens.get(i).componentTokenCount(); + } + + return sb; + } + + private CharSequence generatePrimitiveProperty( + final String containingStructName, final String outermostStruct, final String propertyName, final Token token) + { + final StringBuilder sb = new StringBuilder(); + + sb.append(generatePrimitiveFieldMetaData(propertyName, token, containingStructName)); + + if (token.isConstantEncoding()) + { + sb.append(generateConstPropertyFunctions(propertyName, token, containingStructName)); + } + else + { + sb.append(generatePrimitivePropertyFunctions(containingStructName, outermostStruct, propertyName, token)); + } + + return sb; + } + + private CharSequence generatePrimitivePropertyFunctions( + final String containingStructName, final String outermostStruct, final String propertyName, final Token token) + { + final int arrayLength = token.arrayLength(); + + if (arrayLength == 1) + { + return generateSingleValueProperty(containingStructName, outermostStruct, propertyName, token); + } + else if (arrayLength > 1) + { + return generateArrayProperty(containingStructName, outermostStruct, propertyName, token); + } + + return ""; + } + + private CharSequence generatePrimitiveFieldMetaData( + final String propertyName, final Token token, final String containingStructName) + { + final StringBuilder sb = new StringBuilder(); + + final Encoding encoding = token.encoding(); + final PrimitiveType primitiveType = encoding.primitiveType(); + final String cTypeName = cTypeName(primitiveType); + final CharSequence nullValueString = generateNullValueLiteral(primitiveType, encoding); + + sb.append(String.format("\n" + + "SBE_ONE_DEF %1$s %4$s_%2$s_null_value(void)\n" + + "{\n" + + " return %3$s;\n" + + "}\n", + cTypeName, + propertyName, + nullValueString, + containingStructName)); + + sb.append(String.format("\n" + + "SBE_ONE_DEF %1$s %4$s_%2$s_min_value(void)\n" + + "{\n" + + " return %3$s;\n" + + "}\n", + cTypeName, + propertyName, + generateLiteral(primitiveType, token.encoding().applicableMinValue().toString()), + containingStructName)); + + sb.append(String.format("\n" + + "SBE_ONE_DEF %1$s %4$s_%2$s_max_value(void)\n" + + "{\n" + + " return %3$s;\n" + + "}\n", + cTypeName, + propertyName, + generateLiteral(primitiveType, token.encoding().applicableMaxValue().toString()), + containingStructName)); + + sb.append(String.format("\n" + + "SBE_ONE_DEF size_t %3$s_%1$s_encoding_length(void)\n" + + "{\n" + + " return %2$d;\n" + + "}\n", + propertyName, + token.encoding().primitiveType().size() * token.arrayLength(), + containingStructName)); + + return sb; + } + + private CharSequence generateLoadValue( + final String outermostStruct, + final PrimitiveType primitiveType, + final String offsetStr, + final ByteOrder byteOrder, + final String outputHandler) + { + final String cTypeName = cTypeName(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) ? "float" : "double"; + + sb.append(String.format( + " union %1$s_%2$s_as_uint val;\n" + + " memcpy(&val, codec->buffer + codec->offset + %3$s, sizeof(%4$s));\n" + + " val.uint_value = %5$s(val.uint_value);\n" + + " %6$s val.fp_value;", + outermostStruct, + stackUnion, + offsetStr, + cTypeName, + byteOrderStr, + outputHandler)); + } + else + { + sb.append(String.format( + " %1$s val;\n" + + " memcpy(&val, codec->buffer + codec->offset + %2$s, sizeof(%1$s));\n" + + " %4$s %3$s(val);", + cTypeName, + offsetStr, + byteOrderStr, + outputHandler)); + } + + return sb; + } + + private CharSequence generateLoadValue( + final String outermostStruct, + final PrimitiveType primitiveType, + final String offsetStr, + final ByteOrder byteOrder) + { + return generateLoadValue(outermostStruct, primitiveType, offsetStr, byteOrder, "return"); + } + + + private CharSequence generateLoadValueUnsafe( + final String outermostStruct, + final PrimitiveType primitiveType, + final String offsetStr, + final ByteOrder byteOrder) + { + return generateLoadValue(outermostStruct, primitiveType, offsetStr, byteOrder, "*out ="); + } + + private CharSequence generateStoreValue( + final String outermostStruct, + final PrimitiveType primitiveType, + final String offsetStr, + final ByteOrder byteOrder) + { + final String cTypeName = cTypeName(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) ? "float" : "double"; + + sb.append(String.format( + " union %1$s_%2$s_as_uint val;\n" + + " val.fp_value = value;\n" + + " val.uint_value = %3$s(val.uint_value);\n" + + " memcpy(codec->buffer + codec->offset + %4$s, &val, sizeof(%5$s));", + outermostStruct, + stackUnion, + byteOrderStr, + offsetStr, + cTypeName)); + } + else + { + sb.append(String.format( + " %1$s val = %2$s(value);\n" + + " memcpy(codec->buffer + codec->offset + %3$s, &val, sizeof(%1$s));", + cTypeName, + byteOrderStr, + offsetStr)); + } + + return sb; + } + + private CharSequence generateSingleValueProperty( + final String containingStructName, final String outermostStruct, final String propertyName, final Token token) + { + final PrimitiveType primitiveType = token.encoding().primitiveType(); + final String cTypeName = cTypeName(primitiveType); + final int offset = token.offset(); + final StringBuilder sb = new StringBuilder(); + + final CharSequence loadValue = generateLoadValue( + outermostStruct, + primitiveType, + Integer.toString(offset), + token.encoding().byteOrder()); + + sb.append(String.format("\n" + + "SBE_ONE_DEF %1$s %5$s_%2$s(const struct %5$s *const codec)\n" + + "{\n" + + "%3$s" + + "%4$s\n" + + "}\n", + cTypeName, + propertyName, + generateFieldNotPresentCondition(token.version(), token.encoding()), + loadValue, + containingStructName)); + + final CharSequence storeValue = generateStoreValue( + outermostStruct, primitiveType, Integer.toString(offset), token.encoding().byteOrder()); + + sb.append(String.format("\n" + + "SBE_ONE_DEF struct %1$s *%1$s_set_%2$s(struct %1$s *const codec, const %3$s value)\n" + + "{\n" + + "%4$s\n" + + " return codec;\n" + + "}\n", + containingStructName, + propertyName, + cTypeName, + storeValue)); + + return sb; + } + + private CharSequence generateArrayProperty( + final String containingStructName, final String outermostStruct, final String propertyName, final Token token) + { + final PrimitiveType primitiveType = token.encoding().primitiveType(); + final String cTypeName = cTypeName(primitiveType); + final int offset = token.offset(); + + final StringBuilder sb = new StringBuilder(); + + sb.append(String.format("\n" + + "SBE_ONE_DEF uint64_t %1$s_%2$s_length(void)\n" + + "{\n" + + " return %3$d;\n" + + "}\n", + containingStructName, propertyName, + token.arrayLength())); + + sb.append(String.format("\n" + + "SBE_ONE_DEF const char *%1$s_%2$s_buffer(const struct %1$s *const codec)\n" + + "{\n" + + "%3$s" + + " return (codec->buffer + codec->offset + %4$d);\n" + + "}\n", + containingStructName, + propertyName, + generateTypeFieldNotPresentCondition(token.version()), + offset)); + + final CharSequence loadValue = generateLoadValue( + outermostStruct, + primitiveType, + String.format("%d + (index * %d)", offset, primitiveType.size()), + token.encoding().byteOrder()); + + sb.append(String.format("\n" + + "SBE_ONE_DEF %2$s %1$s_%3$s_unsafe(\n" + + " const struct %1$s *const codec,\n" + + " const uint64_t index)\n" + + "{\n" + + "%5$s" + + "%6$s\n" + + "}\n", + containingStructName, + cTypeName, + propertyName, + token.arrayLength(), + generateFieldNotPresentCondition(token.version(), token.encoding()), + loadValue)); + + final CharSequence loadValueUnsafe = generateLoadValueUnsafe( + outermostStruct, + primitiveType, + String.format("%d + (index * %d)", offset, primitiveType.size()), + token.encoding().byteOrder()); + + sb.append(String.format("\n" + + "SBE_ONE_DEF bool %1$s_%3$s(\n" + + " const struct %1$s *const codec,\n" + + " const uint64_t index,\n" + + " %2$s *const out)\n" + + "{\n" + + " if (index >= %4$d)\n" + + " {\n" + + " errno = E104;\n" + + " return false;\n" + + " }\n\n" + + "%5$s" + + "%6$s\n" + + " return true;\n" + + "}\n", + containingStructName, + cTypeName, + propertyName, + token.arrayLength(), + generateFieldNotPresentCondition(token.version(), token.encoding()), + loadValueUnsafe)); + + final CharSequence storeValue = generateStoreValue( + outermostStruct, + primitiveType, + String.format("%d + (index * %d)", offset, primitiveType.size()), + token.encoding().byteOrder()); + + sb.append(String.format("\n" + + "SBE_ONE_DEF struct %1$s *%1$s_set_%2$s_unsafe(\n" + + " struct %1$s *const codec,\n" + + " const uint64_t index,\n" + + " const %3$s value)\n" + + "{\n" + + "%5$s\n" + + " return codec;\n" + + "}\n", + containingStructName, + propertyName, + cTypeName, + token.arrayLength(), + storeValue)); + + sb.append(String.format("\n" + + "SBE_ONE_DEF struct %1$s *%1$s_set_%2$s(\n" + + " struct %1$s *const codec,\n" + + " const uint64_t index,\n" + + " const %3$s value)\n" + + "{\n" + + " if (index >= %4$d)\n" + + " {\n" + + " errno = E105;\n" + + " return NULL;\n" + + " }\n" + + "%5$s\n" + + " return codec;\n" + + "}\n", + containingStructName, + propertyName, + cTypeName, + token.arrayLength(), + storeValue)); + + sb.append(String.format("\n" + + "SBE_ONE_DEF char *%1$s_get_%2$s(\n" + + " const struct %1$s *const codec,\n" + + " char *dst,\n" + + " const uint64_t length)\n" + + "{\n" + + " if (length > %3$d)\n" + + " {\n" + + " errno = E106;\n" + + " return NULL;\n" + + " }\n\n" + + "%4$s" + + " memcpy(dst, codec->buffer + codec->offset + %5$d, sizeof(%6$s) * length);\n" + + " return dst;\n" + + "}\n", + containingStructName, + propertyName, + token.arrayLength(), + generateArrayFieldNotPresentCondition(token.version()), + offset, + cTypeName)); + + sb.append(String.format("\n" + + "SBE_ONE_DEF struct %1$s *%1$s_put_%2$s(\n" + + " struct %1$s *const codec,\n" + + " const char *src)\n" + + "{\n" + + " memcpy(codec->buffer + codec->offset + %3$d, src, sizeof(%4$s) * %5$d);\n" + + " return codec;\n" + + "}\n", + containingStructName, + propertyName, + offset, + cTypeName, + token.arrayLength())); + + return sb; + } + + private CharSequence generateConstPropertyFunctions( + final String propertyName, final Token token, final String containingStructName) + { + final String cTypeName = cTypeName(token.encoding().primitiveType()); + + if (token.encoding().primitiveType() != PrimitiveType.CHAR) + { + return String.format("\n" + + "SBE_ONE_DEF %1$s %4$s_%2$s(void)\n" + + "{\n" + + " return %3$s;\n" + + "}\n", + cTypeName, + propertyName, + generateLiteral(token.encoding().primitiveType(), token.encoding().constValue().toString()), + containingStructName); + } + + final StringBuilder sb = new StringBuilder(); + + 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); + } + + sb.append(String.format("\n" + + "SBE_ONE_DEF uint64_t %3$s_%1$s_length(void)\n" + + "{\n" + + " return %2$d;\n" + + "}\n", + propertyName, + constantValue.length, + containingStructName)); + + sb.append(String.format("\n" + + "SBE_ONE_DEF const char *%3$s_%1$s()\n" + + "{\n" + + " static uint8_t %1$s_values[] = {%2$s};\n\n" + + " return (const char *)%1$s_values;\n" + + "}\n", + propertyName, + values, + containingStructName)); + + sb.append(String.format("\n" + + "SBE_ONE_DEF %1$s %4$s_%2$s_index(const uint64_t index)\n" + + "{\n" + + " static uint8_t %2$s_values[] = {%3$s};\n\n" + + " return %2$s_values[index];\n" + + "}\n", + cTypeName, + propertyName, + values, + containingStructName)); + + sb.append(String.format("\n" + + "SBE_ONE_DEF uint64_t %4$s_get_%1$s(\n" + + " const struct %4$s *const codec,\n" + + " char *dst,\n" + + " const uint64_t length)\n" + + "{\n" + + " static uint8_t %2$s_values[] = {%3$s};\n" + + " uint64_t bytes_to_copy = length < sizeof(%2$s_values) ? length : sizeof(%2$s_values);\n\n" + + " memcpy(dst, %2$s_values, bytes_to_copy);\n" + + " return bytes_to_copy;\n" + + "}\n", + toUpperFirstChar(propertyName), + propertyName, + values, + containingStructName)); + + return sb; + } + + private CharSequence generateFixedFlyweightCodeMembers() + { + return String.format( + " char *buffer;\n" + + " uint64_t buffer_length;\n" + + " uint64_t offset;\n" + + " uint64_t acting_version;\n"); + } + + private CharSequence generateFixedFlyweightCodeFunctions(final String structName, final int size) + { + final String schemaIdType = cTypeName(ir.headerStructure().schemaIdType()); + final String schemaVersionType = cTypeName(ir.headerStructure().schemaVersionType()); + + return String.format("\n" + + "SBE_ONE_DEF struct %1$s *%1$s_reset(\n" + + " struct %1$s *const codec,\n" + + " char *buffer,\n" + + " const uint64_t offset,\n" + + " const uint64_t buffer_length,\n" + + " const uint64_t acting_version)\n" + + "{\n" + + " if (SBE_BOUNDS_CHECK_EXPECT(((offset + %2$s) > buffer_length), false))\n" + + " {\n" + + " errno = E107;\n" + + " return NULL;\n" + + " }\n" + + " codec->buffer = buffer;\n" + + " codec->buffer_length = buffer_length;\n" + + " codec->offset = offset;\n" + + " codec->acting_version = acting_version;\n" + + " return codec;\n" + + "}\n\n" + + "SBE_ONE_DEF struct %1$s *%1$s_wrap(\n" + + " struct %1$s *const codec,\n" + + " char *buffer,\n" + + " const uint64_t offset,\n" + + " const uint64_t acting_version,\n" + + " const uint64_t buffer_length)\n" + + "{\n" + + " return %1$s_reset(codec, buffer, offset, buffer_length, acting_version);\n" + + "}\n\n" + + "SBE_ONE_DEF uint64_t %1$s_encoded_length(void)\n" + + "{\n" + + " return %2$s;\n" + + "}\n\n" + + "SBE_ONE_DEF uint64_t %1$s_offset(const struct %1$s *const codec)\n" + + "{\n" + + " return codec->offset;\n" + + "}\n\n" + + "SBE_ONE_DEF const char *%1$s_buffer(const struct %1$s *const codec)\n" + + "{\n" + + " return codec->buffer;\n" + + "}\n\n" + + "SBE_ONE_DEF char *%1$s_mut_buffer(struct %1$s *const codec)\n" + + "{\n" + + " return codec->buffer;\n" + + "}\n\n" + + "SBE_ONE_DEF uint64_t %1$s_buffer_length(const struct %1$s *const codec)\n" + + "{\n" + + " return codec->buffer_length;\n" + + "}\n\n" + + "SBE_ONE_DEF %3$s %1$s_sbe_schema_id(void)\n" + + "{\n" + + " return %4$s;\n" + + "}\n\n" + + "SBE_ONE_DEF %5$s %1$s_sbe_schema_version(void)\n" + + "{\n" + + " return %6$s;\n" + + "}\n", + structName, + size, + schemaIdType, + generateLiteral(ir.headerStructure().schemaIdType(), Integer.toString(ir.id())), + schemaVersionType, + generateLiteral(ir.headerStructure().schemaVersionType(), Integer.toString(ir.version()))); + } + + private CharSequence generateMessageFlyweightMembers() + { + return String.format( + " char *buffer;\n" + + " uint64_t buffer_length;\n" + + " uint64_t *position_ptr;\n" + + " uint64_t offset;\n" + + " uint64_t position;\n" + + " uint64_t acting_block_length;\n" + + " uint64_t acting_version;\n"); + } + + private CharSequence generateMessageFlyweightFunctions( + final String structName, + final Token token, + final CharSequence[] scope) + { + final String blockLengthType = cTypeName(ir.headerStructure().blockLengthType()); + final String templateIdType = cTypeName(ir.headerStructure().templateIdType()); + final String schemaIdType = cTypeName(ir.headerStructure().schemaIdType()); + final String schemaVersionType = cTypeName(ir.headerStructure().schemaVersionType()); + final String semanticType = token.encoding().semanticType() == null ? "" : token.encoding().semanticType(); + final String messageHeaderStruct = formatScopedName(scope, "messageHeader"); + + return String.format("\n" + + "SBE_ONE_DEF uint64_t %10$s_sbe_position(const struct %10$s *const codec)\n" + + "{\n" + + " return codec->position;\n" + + "}\n\n" + + + "SBE_ONE_DEF bool %10$s_set_sbe_position(struct %10$s *const codec, const uint64_t position)\n" + + "{\n" + + " if (SBE_BOUNDS_CHECK_EXPECT((position > codec->buffer_length), false))\n" + + " {\n" + + " errno = E100;\n" + + " return false;\n" + + " }\n" + + " codec->position = position;\n" + + " return true;\n" + + "}\n\n" + + + "SBE_ONE_DEF struct %10$s *%10$s_reset(\n" + + " struct %10$s *const codec,\n" + + " char *buffer,\n" + + " const uint64_t offset,\n" + + " const uint64_t buffer_length,\n" + + " const uint64_t acting_block_length,\n" + + " const uint64_t acting_version)\n" + + "{\n" + + " codec->buffer = buffer;\n" + + " codec->offset = offset;\n" + + " codec->buffer_length = buffer_length;\n" + + " codec->acting_block_length = acting_block_length;\n" + + " codec->acting_version = acting_version;\n" + + " codec->position_ptr = &codec->position;\n" + + " if (!%10$s_set_sbe_position(codec, offset + codec->acting_block_length))\n" + + " {\n" + + " return NULL;\n" + + " }\n" + + " return codec;\n" + + "}\n\n" + + + "SBE_ONE_DEF struct %10$s *%10$s_copy(struct %10$s *const codec, const struct %10$s *const other)\n" + + "{\n" + + " codec->buffer = other->buffer;\n" + + " codec->offset = other->offset;\n" + + " codec->buffer_length = other->buffer_length;\n" + + " codec->acting_block_length = other->acting_block_length;\n" + + " codec->acting_version = other->acting_version;\n" + + " codec->position_ptr = &codec->position;\n" + + " codec->position = other->position;\n" + + " return codec;\n" + + "}\n\n" + + + "SBE_ONE_DEF %1$s %10$s_sbe_block_length(void)\n" + + "{\n" + + " return %2$s;\n" + + "}\n\n" + + "SBE_ONE_DEF %3$s %10$s_sbe_template_id(void)\n" + + "{\n" + + " return %4$s;\n" + + "}\n\n" + + "SBE_ONE_DEF %5$s %10$s_sbe_schema_id(void)\n" + + "{\n" + + " return %6$s;\n" + + "}\n\n" + + "SBE_ONE_DEF %7$s %10$s_sbe_schema_version(void)\n" + + "{\n" + + " return %8$s;\n" + + "}\n\n" + + "SBE_ONE_DEF const char *%10$s_sbe_semantic_type(void)\n" + + "{\n" + + " return \"%9$s\";\n" + + "}\n\n" + + "SBE_ONE_DEF uint64_t %10$s_offset(const struct %10$s *const codec)\n" + + "{\n" + + " return codec->offset;\n" + + "}\n\n" + + "SBE_ONE_DEF struct %10$s *%10$s_wrap_and_apply_header(\n" + + " struct %10$s *const codec,\n" + + " char *buffer,\n" + + " const uint64_t offset,\n" + + " const uint64_t buffer_length,\n" + + " struct %11$s *const hdr)\n" + + "{\n" + + " %11$s_wrap(hdr, buffer + offset, 0, buffer_length, %11$s_sbe_schema_version());\n\n" + + " %11$s_set_blockLength(hdr, %10$s_sbe_block_length());\n" + + " %11$s_set_templateId(hdr, %10$s_sbe_template_id());\n" + + " %11$s_set_schemaId(hdr, %10$s_sbe_schema_id());\n" + + " %11$s_set_version(hdr, %10$s_sbe_schema_version());\n\n" + + " %10$s_reset(\n" + + " codec,\n" + + " buffer + offset + %11$s_encoded_length(),\n" + + " 0,\n" + + " buffer_length,\n" + + " %10$s_sbe_block_length(),\n" + + " %10$s_sbe_schema_version());\n" + + " return codec;\n" + + "}\n\n" + + "SBE_ONE_DEF struct %10$s *%10$s_wrap_for_encode(\n" + + " struct %10$s *const codec,\n" + + " char *buffer,\n" + + " const uint64_t offset,\n" + + " const uint64_t buffer_length)\n" + + "{\n" + + " return %10$s_reset(\n" + + " codec,\n" + + " buffer,\n" + + " offset,\n" + + " buffer_length,\n" + + " %10$s_sbe_block_length(),\n" + + " %10$s_sbe_schema_version());\n" + + "}\n\n" + + + "SBE_ONE_DEF struct %10$s *%10$s_wrap_for_decode(\n" + + " struct %10$s *const codec,\n" + + " char *buffer,\n" + + " const uint64_t offset,\n" + + " const uint64_t acting_block_length,\n" + + " const uint64_t acting_version,\n" + + " const uint64_t buffer_length)\n" + + "{\n" + + " return %10$s_reset(\n" + + " codec,\n" + + " buffer,\n" + + " offset,\n" + + " buffer_length,\n" + + " acting_block_length,\n" + + " acting_version);\n" + + "}\n\n" + + "SBE_ONE_DEF uint64_t %10$s_encoded_length(const struct %10$s *const codec)\n" + + "{\n" + + " return %10$s_sbe_position(codec) - codec->offset;\n" + + "}\n\n" + + "SBE_ONE_DEF const char *%10$s_buffer(const struct %10$s *const codec)\n" + + "{\n" + + " return codec->buffer;\n" + + "}\n\n" + + "SBE_ONE_DEF char *%10$s_mut_buffer(struct %10$s *const codec)\n" + + "{\n" + + " return codec->buffer;\n" + + "}\n\n" + + "SBE_ONE_DEF uint64_t %10$s_buffer_length(const struct %10$s *const codec)\n" + + "{\n" + + " return codec->buffer_length;\n" + + "}\n\n" + + "SBE_ONE_DEF uint64_t %10$s_acting_version(const struct %10$s *const codec)\n" + + "{\n" + + " return codec->acting_version;\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, + structName, + messageHeaderStruct, + formatScopedName(scope, "strerror")); + } + + private CharSequence generateFieldMembers(final CharSequence[] scope, final List tokens) + { + final StringBuilder sb = new StringBuilder(); + + 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()); + + generatePropertyMembers(sb, scope, signalToken, propertyName, encodingToken); + } + } + + return sb; + } + + private CharSequence generateFieldFunctions( + final CharSequence[] scope, + final String containingStructName, + final String outermostStruct, + final List tokens) + { + final StringBuilder sb = new StringBuilder(); + + 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()); + + generateFieldMetaAttributeFunction( + sb, + signalToken, + containingStructName, + outermostStruct); + generateFieldCommonFunctions( + sb, + signalToken, + encodingToken, + propertyName, + containingStructName); + generatePropertyFunctions( + sb, + scope, + containingStructName, + outermostStruct, + signalToken, + propertyName, + encodingToken); + } + } + + return sb; + } + + private void generateFieldCommonFunctions( + final StringBuilder sb, + final Token fieldToken, + final Token encodingToken, + final String propertyName, + final String containingStructName) + { + sb.append(String.format("\n" + + "SBE_ONE_DEF uint16_t %3$s_%1$s_id(void)\n" + + "{\n" + + " return %2$d;\n" + + "}\n", + propertyName, + fieldToken.id(), + containingStructName)); + + sb.append(String.format("\n" + + "SBE_ONE_DEF uint64_t %3$s_%1$s_since_version(void)\n" + + "{\n" + + " return %2$d;\n" + + "}\n\n" + + "SBE_ONE_DEF bool %3$s_%1$s_in_acting_version(const struct %3$s *const codec)\n" + + "{\n" + + "#if defined(__clang__)\n" + + "#pragma clang diagnostic push\n" + + "#pragma clang diagnostic ignored \"-Wtautological-compare\"\n" + + "#endif\n" + + " return codec->acting_version >= %3$s_%1$s_since_version();\n" + + "#if defined(__clang__)\n" + + "#pragma clang diagnostic pop\n" + + "#endif\n" + + "}\n", + propertyName, + fieldToken.version(), + containingStructName)); + + sb.append(String.format("\n" + + "SBE_ONE_DEF size_t %2$s_%1$s_encoding_offset(void)\n" + + "{\n" + + " return %3$d;\n" + + "}\n", + propertyName, + containingStructName, + encodingToken.offset())); + } + + private static void generateFieldMetaAttributeFunction( + final StringBuilder sb, final Token token, final String containingStructName, final String outermostStruct) + { + 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(String.format("\n" + + "SBE_ONE_DEF const char *%6$s_%s_meta_attribute(const enum %7$s_meta_attribute attribute)\n" + + "{\n" + + " switch (attribute)\n" + + " {\n" + + " case %7$s_meta_attribute_EPOCH: return \"%s\";\n" + + " case %7$s_meta_attribute_TIME_UNIT: return \"%s\";\n" + + " case %7$s_meta_attribute_SEMANTIC_TYPE: return \"%s\";\n" + + " case %7$s_meta_attribute_PRESENCE: return \"%s\";\n" + + " }\n\n" + + " return \"\";\n" + + "}\n", + token.name(), + epoch, + timeUnit, + semanticType, + encoding.presence().toString().toLowerCase(), + containingStructName, + outermostStruct)); + } + + private static CharSequence generateEnumFieldNotPresentCondition( + final int sinceVersion, + final String enumName, + final String containingStructName) + { + if (0 == sinceVersion) + { + return ""; + } + + return String.format( + "if (codec->acting_version < %1$d)\n" + + "{\n" + + " return %2$s_NULL_VALUE;\n" + + "}\n\n", + sinceVersion, + enumName, + containingStructName); + } + + private CharSequence generateEnumProperty( + final CharSequence[] scope, + final String containingStructName, + final Token signalToken, + final String propertyName, + final Token token) + { + final String enumName = formatScopedName(scope, token.applicableTypeName()); + final String typeName = cTypeName(token.encoding().primitiveType()); + final int offset = token.offset(); + + final StringBuilder sb = new StringBuilder(); + + sb.append(String.format("\n" + + "SBE_ONE_DEF size_t %3$s_%1$s_encoding_length(void)\n" + + "{\n" + + " return %2$d;\n" + + "}\n", + propertyName, + signalToken.encodedLength(), + containingStructName)); + + if (signalToken.isConstantEncoding()) + { + final String constValue = signalToken.encoding().constValue().toString(); + + sb.append(String.format("\n" + + "SBE_ONE_DEF enum %1$s %4$s_%2$s_const_value(void)\n" + + "{\n" + + " return %1$s_%3$s;\n" + + "}\n", + enumName, + propertyName, + constValue.substring(constValue.indexOf(".") + 1), + containingStructName)); + + sb.append(String.format("\n" + + "SBE_ONE_DEF enum %1$s %5$s_%2$s(const struct %5$s *const codec)\n" + + "{\n" + + "%3$s" + + " return %1$s_%4$s;\n" + + "}\n\n", + enumName, + propertyName, + generateEnumFieldNotPresentCondition(token.version(), enumName, containingStructName), + constValue.substring(constValue.indexOf(".") + 1), + containingStructName)); + } + else + { + sb.append(String.format("\n" + + "SBE_ONE_DEF bool %7$s_%2$s(const struct %7$s *const codec, enum %1$s *const out)\n" + + "{\n" + + "%3$s" + + " %5$s val;\n" + + " memcpy(&val, codec->buffer + codec->offset + %6$d, sizeof(%5$s));\n" + + " return %1$s_get(%4$s(val), out);\n" + + "}\n", + enumName, + propertyName, + generateEnumFieldNotPresentCondition(token.version(), enumName, containingStructName), + formatByteOrderEncoding(token.encoding().byteOrder(), token.encoding().primitiveType()), + typeName, + offset, + containingStructName)); + + sb.append(String.format("\n" + + "SBE_ONE_DEF struct %1$s *%1$s_set_%2$s(struct %1$s *const codec, const enum %3$s value)\n" + + "{\n" + + " %4$s val = %6$s(value);\n" + + " memcpy(codec->buffer + codec->offset + %5$d, &val, sizeof(%4$s));\n" + + " return codec;\n" + + "}\n", + containingStructName, + propertyName, + enumName, + typeName, + offset, + formatByteOrderEncoding(token.encoding().byteOrder(), token.encoding().primitiveType()))); + } + + return sb; + } + + private static Object generateBitsetPropertyMember( + final CharSequence[] scope, + final String propertyName, + final Token token) + { + final StringBuilder sb = new StringBuilder(); + + final String bitsetName = formatScopedName(scope, token.applicableTypeName()); + + sb.append(String.format( + " struct %1$s %2$s;\n", + bitsetName, + propertyName)); + + return sb; + } + + private static Object generateBitsetPropertyFunctions( + final CharSequence[] scope, + final String propertyName, + final Token token, + final String containingStructName) + { + final StringBuilder sb = new StringBuilder(); + + final String bitsetName = formatScopedName(scope, token.applicableTypeName()); + final int offset = token.offset(); + + sb.append(String.format("\n" + + "struct %1$s *%4$s_%2$s(struct %4$s *const codec)\n" + + "{\n" + + " return %1$s_wrap(\n" + + " &codec->extras,\n" + + " codec->buffer,\n" + + " codec->offset + %3$d,\n" + + " codec->acting_version,\n" + + " codec->buffer_length);\n" + + "}\n", + bitsetName, + propertyName, + offset, + containingStructName)); + + sb.append(String.format("\n" + + "SBE_ONE_DEF size_t %1$s_encoding_length(void)\n" + + "{\n" + + " return %2$d;\n" + + "}\n", + propertyName, + token.encoding().primitiveType().size())); + + return sb; + } + + private static Object generateCompositePropertyMember( + final CharSequence[] scope, final String propertyName, final Token token) + { + final String compositeName = formatScopedName(scope, token.applicableTypeName()); + final StringBuilder sb = new StringBuilder(); + + sb.append(String.format( + " struct %1$s %2$s;\n", + compositeName, + propertyName)); + + return sb; + } + + private static Object generateCompositePropertyFunction( + final CharSequence[] scope, + final String propertyName, + final Token token, + final String containingStructName) + { + final String compositeName = formatScopedName(scope, token.applicableTypeName()); + final int offset = token.offset(); + final StringBuilder sb = new StringBuilder(); + + sb.append(String.format("\n" + + "struct %1$s *%4$s_%2$s(struct %4$s *const codec)\n" + + "{\n" + + " return %1$s_wrap(\n" + + " &codec->%2$s,\n" + + " codec->buffer,\n" + + " codec->offset + %3$d,\n" + + " codec->acting_version,\n" + + " codec->buffer_length);\n" + + "}\n", + compositeName, + propertyName, + offset, + containingStructName)); + + return sb; + } + + 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 some of the 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 CharSequence generateLiteral(final PrimitiveType type, final String value) + { + String literal = ""; + + final String castType = cTypeName(type); + switch (type) + { + case CHAR: + case UINT8: + case UINT16: + case INT8: + case INT16: + literal = "(" + castType + ")" + value; + break; + + case UINT32: + case INT32: + literal = value; + break; + + case FLOAT: + literal = value.endsWith("NaN") ? "SBE_FLOAT_NAN" : value + "f"; + break; + + case INT64: + literal = value + "L"; + if (value.equals("-9223372036854775808")) + { + literal = "INT64_MIN"; + } + break; + + case UINT64: + literal = "0x" + Long.toHexString(Long.parseLong(value)) + "L"; + break; + + case DOUBLE: + literal = value.endsWith("NaN") ? "SBE_DOUBLE_NAN" : value; + break; + } + + return literal; + } +} diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/c/COutputManager.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/c/COutputManager.java new file mode 100644 index 0000000000..ee5019b716 --- /dev/null +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/c/COutputManager.java @@ -0,0 +1,55 @@ +package uk.co.real_logic.sbe.generation.c; + +import org.agrona.generation.OutputManager; +import org.agrona.Verify; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; + +import static java.io.File.separatorChar; + +/** + * {@link OutputManager} for managing the creation of C11 source files as the target of code generation. + * The character encoding for the {@link java.io.Writer} is UTF-8. + */ +public class COutputManager implements OutputManager +{ + private final File outputDir; + + /** + * Create a new {@link OutputManager} for generating C11 source files into a given package. + * + * @param baseDirName for the generated source code. + * @param namespaceName for the generated source code relative to the baseDirName. + */ + public COutputManager(final String baseDirName, final String namespaceName) + { + Verify.notNull(baseDirName, "baseDirName"); + Verify.notNull(namespaceName, "applicableNamespace"); + + final String dirName = baseDirName.endsWith("" + separatorChar) ? baseDirName : baseDirName + separatorChar; + final String packageDirName = dirName + namespaceName.replace('.', '_'); + + outputDir = new File(packageDirName); + if (!outputDir.exists() && !outputDir.mkdirs()) + { + throw new IllegalStateException("Unable to create directory: " + packageDirName); + } + } + + /** + * Create a new output which will be a C11 source file in the given package. + *

+ * The {@link java.io.Writer} should be closed once the caller has finished with it. The Writer is + * buffer for efficient IO operations. + * + * @param name the name of the C struct. + * @return a {@link java.io.Writer} to which the source code should be written. + */ + public Writer createOutput(final String name) throws IOException + { + final File targetFile = new File(outputDir, name + ".h"); + return Files.newBufferedWriter(targetFile.toPath(), StandardCharsets.UTF_8); + } +} diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/c/CUtil.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/c/CUtil.java new file mode 100644 index 0000000000..4b8106a61a --- /dev/null +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/c/CUtil.java @@ -0,0 +1,144 @@ +package uk.co.real_logic.sbe.generation.c; + +import uk.co.real_logic.sbe.PrimitiveType; +import uk.co.real_logic.sbe.SbeTool; +import uk.co.real_logic.sbe.util.ValidationUtil; + +import java.nio.ByteOrder; +import java.util.EnumMap; +import java.util.Map; + +/** + * Utilities for mapping between IR and the C language. + */ +public class CUtil +{ + private static final Map PRIMITIVE_TYPE_STRING_ENUM_MAP = new EnumMap<>(PrimitiveType.class); + + static + { + PRIMITIVE_TYPE_STRING_ENUM_MAP.put(PrimitiveType.CHAR, "char"); + PRIMITIVE_TYPE_STRING_ENUM_MAP.put(PrimitiveType.INT8, "int8_t"); + PRIMITIVE_TYPE_STRING_ENUM_MAP.put(PrimitiveType.INT16, "int16_t"); + PRIMITIVE_TYPE_STRING_ENUM_MAP.put(PrimitiveType.INT32, "int32_t"); + PRIMITIVE_TYPE_STRING_ENUM_MAP.put(PrimitiveType.INT64, "int64_t"); + PRIMITIVE_TYPE_STRING_ENUM_MAP.put(PrimitiveType.UINT8, "uint8_t"); + PRIMITIVE_TYPE_STRING_ENUM_MAP.put(PrimitiveType.UINT16, "uint16_t"); + PRIMITIVE_TYPE_STRING_ENUM_MAP.put(PrimitiveType.UINT32, "uint32_t"); + PRIMITIVE_TYPE_STRING_ENUM_MAP.put(PrimitiveType.UINT64, "uint64_t"); + PRIMITIVE_TYPE_STRING_ENUM_MAP.put(PrimitiveType.FLOAT, "float"); + PRIMITIVE_TYPE_STRING_ENUM_MAP.put(PrimitiveType.DOUBLE, "double"); + } + + /** + * Map the name of a {@link uk.co.real_logic.sbe.PrimitiveType} to a C11 primitive type name. + * + * @param primitiveType to map. + * @return the name of the Java primitive that most closely maps. + */ + public static String cTypeName(final PrimitiveType primitiveType) + { + return PRIMITIVE_TYPE_STRING_ENUM_MAP.get(primitiveType); + } + + /** + * Uppercase the first character of a given String. + * + * @param str to have the first character upper cased. + * @return a new String with the first character in uppercase. + */ + public static String toUpperFirstChar(final String str) + { + return Character.toUpperCase(str.charAt(0)) + str.substring(1); + } + + /** + * Lowercase the first character of a given String. + * + * @param str to have the first character upper cased. + * @return a new String with the first character in uppercase. + */ + public static String toLowerFirstChar(final String str) + { + return Character.toLowerCase(str.charAt(0)) + str.substring(1); + } + + /** + * Format a String as a property name. + * + * @param value to be formatted. + * @return the string formatted as a property name. + */ + public static String formatPropertyName(final String value) + { + String formattedValue = toLowerFirstChar(value); + + if (ValidationUtil.isCKeyword(formattedValue)) + { + final String keywordAppendToken = System.getProperty(SbeTool.KEYWORD_APPEND_TOKEN); + if (null == keywordAppendToken) + { + throw new IllegalStateException( + "Invalid property name='" + formattedValue + + "' please correct the schema or consider setting system property: " + SbeTool.KEYWORD_APPEND_TOKEN); + } + + formattedValue += keywordAppendToken; + } + + return formattedValue; + } + + /** + * Format a String as a struct name. + * + * @param value to be formatted. + * @return the string formatted as a struct name. + */ + public static String formatName(final String value) + { + return toLowerFirstChar(value); + } + + /** + * Format a String as a struct name prepended with a scope. + * + * @param scope to be prepended. + * @param value to be formatted. + * @return the string formatted as a struct name. + */ + public static String formatScopedName(final CharSequence[] scope, final String value) + { + return String.join("_", scope).toLowerCase() + "_" + formatName(value); + } + + /** + * Return the C99 formatted byte order encoding string to use for a given byte order and primitiveType + * + * @param byteOrder of the {@link uk.co.real_logic.sbe.ir.Token} + * @param primitiveType of the {@link uk.co.real_logic.sbe.ir.Token} + * @return the string formatted as the byte ordering encoding + */ + public static String formatByteOrderEncoding(final ByteOrder byteOrder, final PrimitiveType primitiveType) + { + switch (primitiveType.size()) + { + case 2: + return "SBE_" + byteOrder + "_ENCODE_16"; + + case 4: + return "SBE_" + byteOrder + "_ENCODE_32"; + + case 8: + return "SBE_" + byteOrder + "_ENCODE_64"; + + default: + return ""; + } + } + + public static String closingBraces(final int count) + { + return new String(new char[count]).replace("\0", "}\n"); + } +} diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/util/ValidationUtil.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/util/ValidationUtil.java index 3dc4c7a300..fc263d354b 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/util/ValidationUtil.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/util/ValidationUtil.java @@ -27,6 +27,81 @@ public class ValidationUtil { private static final Pattern PATTERN = Pattern.compile("\\."); + private static final Set C_KEYWORDS = new HashSet<>(Arrays.asList( + "auto", "_Alignas", "_Alignof", "_Atomic", "bool", + "_Bool", "break", "case", "_Complex", + "char", "const", "continue", "default", + "do", "double", "else", "enum", "extern", "false", + "float", "for", "_Generic", "goto", "if", "_Imaginary", "inline", + "int", "long", "_Noreturn", "register", "restrict", "return", "short", + "signed", "sizeof", "static", "_Static_assert", + "struct", "switch", "_Thread_local", "true", "typedef", "union", + "unsigned", "void", "volatile", "wchar_t", "while")); + + /** + * Check value for validity of usage as a C identifier. A programmatic variable + * must have all elements be a letter or digit or '_'. The first character must not be a digit. + * And must not be a C keyword. + *

+ * http://en.cppreference.com/w/cpp/keyword + * + * @param value to check + * @return true for validity as a C name. false if not. + */ + public static boolean isSbeCName(final String value) + { + if (possibleCKeyword(value)) + { + if (isCKeyword(value)) + { + return false; + } + } + else + { + return false; + } + + return true; + } + + public static boolean isCKeyword(final String token) + { + return C_KEYWORDS.contains(token); + } + + private static boolean possibleCKeyword(final String value) + { + for (int i = 0, size = value.length(); i < size; i++) + { + final char c = value.charAt(i); + + if (i == 0 && isSbeCIdentifierStart(c)) + { + continue; + } + + if (isSbeCIdentifierPart(c)) + { + continue; + } + + return false; + } + + return true; + } + + private static boolean isSbeCIdentifierStart(final char c) + { + return Character.isLetter(c) || c == '_'; + } + + private static boolean isSbeCIdentifierPart(final char c) + { + return Character.isLetterOrDigit(c) || c == '_'; + } + private static final Set CPP_KEYWORDS = new HashSet<>(Arrays.asList( "alignas", "and", "and_eq", "asm", "auto", "bitand", "bitor", "bool", "break", "case", diff --git a/sbe-tool/src/test/c/BoundsCheckTest.cpp b/sbe-tool/src/test/c/BoundsCheckTest.cpp new file mode 100644 index 0000000000..cae3ad3ab7 --- /dev/null +++ b/sbe-tool/src/test/c/BoundsCheckTest.cpp @@ -0,0 +1,568 @@ +#include +#include +#include + +#include + +#include +#include + +#define CGT(name) code_generation_test_##name + +#define SERIAL_NUMBER 1234u +#define MODEL_YEAR 2013 +#define AVAILABLE (CGT(booleanType_T)) +#define CODE (CGT(model_A)) +#define CRUISE_CONTROL (true) +#define SPORTS_PACK (true) +#define SUNROOF (false) + +static char VEHICLE_CODE[] = { 'a', 'b', 'c', 'd', 'e', 'f' }; +static char MANUFACTURER_CODE[] = { '1', '2', '3' }; +static const char *MANUFACTURER = "Honda"; +static const char *MODEL = "Civic VTi"; +static const char *ACTIVATION_CODE = "deadbeef"; + +static const std::uint64_t encodedHdrSz = 8; +static const std::uint64_t encodedCarSz = 191; + +class BoundsCheckTest : public testing::Test +{ +protected: + std::uint64_t encodeHdr(char *buffer, std::uint64_t offset, std::uint64_t bufferLength) + { + if (!CGT(messageHeader_wrap)(&m_hdr, buffer, offset, 0, bufferLength)) + { + throw std::runtime_error(sbe_strerror(errno)); + } + CGT(messageHeader_set_blockLength)(&m_hdr, CGT(car_sbe_block_length)()); + CGT(messageHeader_set_templateId)(&m_hdr, CGT(car_sbe_template_id)()); + CGT(messageHeader_set_schemaId)(&m_hdr, CGT(car_sbe_schema_id)()); + CGT(messageHeader_set_version)(&m_hdr, CGT(car_sbe_schema_version)()); + + return CGT(messageHeader_encoded_length)(); + } + + std::uint64_t decodeHdr(char *buffer, std::uint64_t offset, std::uint64_t bufferLength) + { + if (!CGT(messageHeader_wrap)(&m_hdrDecoder, buffer, offset, 0, bufferLength)) + { + throw std::runtime_error(sbe_strerror(errno)); + } + EXPECT_EQ(CGT(messageHeader_blockLength)(&m_hdrDecoder), CGT(car_sbe_block_length)()); + EXPECT_EQ(CGT(messageHeader_templateId)(&m_hdrDecoder), CGT(car_sbe_template_id)()); + EXPECT_EQ(CGT(messageHeader_schemaId)(&m_hdrDecoder), CGT(car_sbe_schema_id)()); + EXPECT_EQ(CGT(messageHeader_version)(&m_hdrDecoder), CGT(car_sbe_schema_version)()); + + return CGT(messageHeader_encoded_length)(); + } + + std::uint64_t encodeCarRoot(char *buffer, std::uint64_t offset, std::uint64_t bufferLength) + { + if (!CGT(car_wrap_for_encode)(&m_car, buffer, offset, bufferLength)) + { + throw std::runtime_error(sbe_strerror(errno)); + } + CGT(car_set_serialNumber)(&m_car, SERIAL_NUMBER); + CGT(car_set_modelYear)(&m_car, MODEL_YEAR); + CGT(car_set_available)(&m_car, AVAILABLE); + CGT(car_set_code)(&m_car, CODE); + CGT(car_put_vehicleCode)(&m_car, VEHICLE_CODE); + + for (uint64_t i = 0; i < CGT(car_someNumbers_length)(); i++) + { + CGT(car_set_someNumbers_unsafe)(&m_car, i, (int32_t)(i)); + } + + CGT(optionalExtras) *const extras = CGT(car_extras)(&m_car); + if (!extras) + { + throw std::runtime_error(sbe_strerror(errno)); + } + CGT(optionalExtras_clear)(extras); + CGT(optionalExtras_set_cruiseControl)(extras, CRUISE_CONTROL); + CGT(optionalExtras_set_sportsPack)(extras, SPORTS_PACK); + CGT(optionalExtras_set_sunRoof)(extras, SUNROOF); + + CGT(engine) *const engine = CGT(car_engine)(&m_car); + if (!engine) + { + throw std::runtime_error(sbe_strerror(errno)); + } + CGT(engine_set_capacity)(engine, 2000); + CGT(engine_set_numCylinders)(engine, (short)4); + CGT(engine_put_manufacturerCode)(engine, MANUFACTURER_CODE); + + CGT(boosterT) *const booster = CGT(engine_booster)(engine); + if (!booster) + { + throw std::runtime_error(sbe_strerror(errno)); + } + CGT(boosterT_set_boostType)(booster, CGT(boostType_NITROUS)); + CGT(boosterT_set_horsePower)(booster, 200); + + return CGT(car_encoded_length)(&m_car); + } + + std::uint64_t encodeCarFuelFigures() + { + CGT(car_fuelFigures) *fuelFigures = CGT(car_fuelFigures_set_count)(&m_car, 3); + if (!fuelFigures) + { + throw std::runtime_error(sbe_strerror(errno)); + } + + if (!CGT(car_fuelFigures_next)(fuelFigures)) + { + throw std::runtime_error(sbe_strerror(errno)); + } + CGT(car_fuelFigures_set_speed)(fuelFigures, 30); + CGT(car_fuelFigures_set_mpg)(fuelFigures, 35.9f); + if (!CGT(car_fuelFigures_put_usageDescription)(fuelFigures, "Urban Cycle", 11)) + { + throw std::runtime_error(sbe_strerror(errno)); + } + + if (!CGT(car_fuelFigures_next)(fuelFigures)) + { + throw std::runtime_error(sbe_strerror(errno)); + } + CGT(car_fuelFigures_set_speed)(fuelFigures, 55); + CGT(car_fuelFigures_set_mpg)(fuelFigures, 49.0f); + if (!CGT(car_fuelFigures_put_usageDescription)(fuelFigures, "Combined Cycle", 14)) + { + throw std::runtime_error(sbe_strerror(errno)); + } + + if (!CGT(car_fuelFigures_next)(fuelFigures)) + { + throw std::runtime_error(sbe_strerror(errno)); + } + CGT(car_fuelFigures_set_speed)(fuelFigures, 75); + CGT(car_fuelFigures_set_mpg)(fuelFigures, 40.0f); + if (!CGT(car_fuelFigures_put_usageDescription)(fuelFigures, "Highway Cycle", 13)) + { + throw std::runtime_error(sbe_strerror(errno)); + } + + return CGT(car_encoded_length)(&m_car); + } + + std::uint64_t encodeCarPerformanceFigures() + { + CGT(car_performanceFigures) *perf_figs = CGT(car_performanceFigures_set_count)(&m_car, 2); + if (!perf_figs) + { + throw std::runtime_error(sbe_strerror(errno)); + } + + if (!CGT(car_performanceFigures_next)(perf_figs)) + { + throw std::runtime_error(sbe_strerror(errno)); + } + + CGT(car_performanceFigures_set_octaneRating)(perf_figs, (short)95); + CGT(car_performanceFigures_acceleration) *acc = CGT(car_performanceFigures_acceleration_set_count)(perf_figs, 3); + if (!acc) + { + throw std::runtime_error(sbe_strerror(errno)); + } + if (!CGT(car_performanceFigures_acceleration_next)(acc)) + { + throw std::runtime_error(sbe_strerror(errno)); + } + CGT(car_performanceFigures_acceleration_set_mph)(acc, 30); + CGT(car_performanceFigures_acceleration_set_seconds)(acc, 4.0f); + if (!CGT(car_performanceFigures_acceleration_next)(acc)) + { + throw std::runtime_error(sbe_strerror(errno)); + } + CGT(car_performanceFigures_acceleration_set_mph)(acc, 60); + CGT(car_performanceFigures_acceleration_set_seconds)(acc, 7.5f); + if (!CGT(car_performanceFigures_acceleration_next)(acc)) + { + throw std::runtime_error(sbe_strerror(errno)); + } + CGT(car_performanceFigures_acceleration_set_mph)(acc, 100); + CGT(car_performanceFigures_acceleration_set_seconds)(acc, 12.2f); + + if (!CGT(car_performanceFigures_next)(perf_figs)) + { + throw std::runtime_error(sbe_strerror(errno)); + } + CGT(car_performanceFigures_set_octaneRating)(perf_figs, (short)99); + acc = CGT(car_performanceFigures_acceleration_set_count)(perf_figs, 3); + if (!acc) + { + throw std::runtime_error(sbe_strerror(errno)); + } + if (!CGT(car_performanceFigures_acceleration_next)(acc)) + { + throw std::runtime_error(sbe_strerror(errno)); + } + CGT(car_performanceFigures_acceleration_set_mph)(acc, 30); + CGT(car_performanceFigures_acceleration_set_seconds)(acc, 3.8f); + if (!CGT(car_performanceFigures_acceleration_next)(acc)) + { + throw std::runtime_error(sbe_strerror(errno)); + } + CGT(car_performanceFigures_acceleration_set_mph)(acc, 60); + CGT(car_performanceFigures_acceleration_set_seconds)(acc, 7.1f); + if (!CGT(car_performanceFigures_acceleration_next)(acc)) + { + throw std::runtime_error(sbe_strerror(errno)); + } + CGT(car_performanceFigures_acceleration_set_mph)(acc, 100); + CGT(car_performanceFigures_acceleration_set_seconds)(acc, 11.8f); + + return CGT(car_encoded_length)(&m_car); + } + + std::uint64_t encodeCarManufacturerModelAndActivationCode() + { + if (!CGT(car_put_manufacturer)(&m_car, MANUFACTURER, (int)(strlen(MANUFACTURER)))) + { + throw std::runtime_error(sbe_strerror(errno)); + } + if (!CGT(car_put_model)(&m_car, MODEL, (int)(strlen(MODEL)))) + { + throw std::runtime_error(sbe_strerror(errno)); + } + if (!CGT(car_put_activationCode)(&m_car, ACTIVATION_CODE, (int)(strlen(ACTIVATION_CODE)))) + { + throw std::runtime_error(sbe_strerror(errno)); + } + + return CGT(car_encoded_length)(&m_car); + } + + std::uint64_t decodeCarRoot(char *buffer, const std::uint64_t offset, const std::uint64_t bufferLength) + { + if (!CGT(car_wrap_for_decode)( + &m_carDecoder, + buffer, + offset, + CGT(car_sbe_block_length)(), + CGT(car_sbe_schema_version)(), + bufferLength)) + { + throw std::runtime_error(sbe_strerror(errno)); + } + EXPECT_EQ(CGT(car_serialNumber)(&m_carDecoder), SERIAL_NUMBER); + EXPECT_EQ(CGT(car_modelYear)(&m_carDecoder), MODEL_YEAR); + { + CGT(booleanType) out; + EXPECT_TRUE(CGT(car_available)(&m_carDecoder, &out)); + EXPECT_EQ(out, AVAILABLE); + } + { + CGT(model) out; + EXPECT_TRUE(CGT(car_code)(&m_carDecoder, &out)); + EXPECT_EQ(out, CODE); + } + + EXPECT_EQ(CGT(car_someNumbers_length)(), 5u); + for (std::uint64_t i = 0; i < 5; i++) + { + EXPECT_EQ(CGT(car_someNumbers_unsafe)(&m_carDecoder, i), (int32_t)(i)); + } + + EXPECT_EQ(CGT(car_vehicleCode_length)(), 6u); + EXPECT_EQ(std::string(CGT(car_vehicleCode_buffer)(&m_carDecoder), 6), std::string(VEHICLE_CODE, 6)); + CGT(optionalExtras) *const extras = CGT(car_extras)(&m_carDecoder); + if (!extras) + { + throw std::runtime_error(sbe_strerror(errno)); + } + EXPECT_TRUE(CGT(optionalExtras_cruiseControl)(extras)); + EXPECT_TRUE(CGT(optionalExtras_sportsPack)(extras)); + EXPECT_FALSE(CGT(optionalExtras_sunRoof)(extras)); + + CGT(engine) *const engine = CGT(car_engine)(&m_carDecoder); + if (!engine) + { + throw std::runtime_error(sbe_strerror(errno)); + } + EXPECT_EQ(CGT(engine_capacity)(engine), 2000); + EXPECT_EQ(CGT(engine_numCylinders)(engine), 4); + EXPECT_EQ(CGT(engine_maxRpm)(), 9000); + EXPECT_EQ(CGT(engine_manufacturerCode_length)(), 3u); + EXPECT_EQ(std::string(CGT(engine_manufacturerCode_buffer)(engine), 3), std::string(MANUFACTURER_CODE, 3)); + EXPECT_EQ(CGT(engine_fuel_length)(), 6u); + EXPECT_EQ(std::string(CGT(engine_fuel)(), 6), "Petrol"); + CGT(boosterT) *const booster = CGT(engine_booster)(engine); + if (!booster) + { + throw std::runtime_error(sbe_strerror(errno)); + } + CGT(boostType) out; + EXPECT_TRUE(CGT(boosterT_boostType)(booster, &out)); + EXPECT_EQ(out, CGT(boostType_NITROUS)); + EXPECT_EQ(CGT(boosterT_horsePower)(booster), 200); + + return CGT(car_encoded_length)(&m_carDecoder); + } + + std::uint64_t decodeCarFuelFigures() + { + char tmp[256]; + CGT(car_fuelFigures) *const fuelFigures = CGT(car_get_fuelFigures)(&m_carDecoder); + if (!fuelFigures) + { + throw std::runtime_error(sbe_strerror(errno)); + } + EXPECT_EQ(CGT(car_fuelFigures_count)(fuelFigures), 3u); + + EXPECT_TRUE(CGT(car_fuelFigures_has_next)(fuelFigures)); + if (!CGT(car_fuelFigures_next)(fuelFigures)) + { + throw std::runtime_error(sbe_strerror(errno)); + } + + EXPECT_EQ(CGT(car_fuelFigures_speed)(fuelFigures), 30); + EXPECT_EQ(CGT(car_fuelFigures_mpg)(fuelFigures), 35.9f); + std::uint64_t bytesToCopy = + CGT(car_fuelFigures_get_usageDescription)(fuelFigures, tmp, sizeof(tmp)); + if (!bytesToCopy) + { + throw std::runtime_error(sbe_strerror(errno)); + } + EXPECT_EQ(bytesToCopy, 11u); + EXPECT_EQ(std::string(tmp, 11), "Urban Cycle"); + + EXPECT_TRUE(CGT(car_fuelFigures_has_next)(fuelFigures)); + if (!CGT(car_fuelFigures_next)(fuelFigures)) + { + throw std::runtime_error(sbe_strerror(errno)); + } + EXPECT_EQ(CGT(car_fuelFigures_speed)(fuelFigures), 55); + EXPECT_EQ(CGT(car_fuelFigures_mpg)(fuelFigures), 49.0f); + bytesToCopy = CGT(car_fuelFigures_get_usageDescription)(fuelFigures, tmp, sizeof(tmp)); + if (!bytesToCopy) + { + throw std::runtime_error(sbe_strerror(errno)); + } + EXPECT_EQ(bytesToCopy, 14u); + EXPECT_EQ(std::string(tmp, 14), "Combined Cycle"); + + EXPECT_TRUE(CGT(car_fuelFigures_has_next)(fuelFigures)); + if (!CGT(car_fuelFigures_next)(fuelFigures)) + { + throw std::runtime_error(sbe_strerror(errno)); + } + EXPECT_EQ(CGT(car_fuelFigures_speed)(fuelFigures), 75); + EXPECT_EQ(CGT(car_fuelFigures_mpg)(fuelFigures), 40.0f); + bytesToCopy = CGT(car_fuelFigures_get_usageDescription)(fuelFigures, tmp, sizeof(tmp)); + if (!bytesToCopy) + { + throw std::runtime_error(sbe_strerror(errno)); + } + EXPECT_EQ(bytesToCopy, 13u); + EXPECT_EQ(std::string(tmp, 13), "Highway Cycle"); + + return CGT(car_encoded_length)(&m_carDecoder); + } + + std::uint64_t decodeCarPerformanceFigures() + { + CGT(car_performanceFigures) *const perfFigs = CGT(car_get_performanceFigures)(&m_carDecoder); + if (!perfFigs) + { + throw std::runtime_error(sbe_strerror(errno)); + } + EXPECT_EQ(CGT(car_performanceFigures_count)(perfFigs), 2u); + + EXPECT_TRUE(CGT(car_performanceFigures_has_next)(perfFigs)); + if (!CGT(car_performanceFigures_next)(perfFigs)) + { + throw std::runtime_error(sbe_strerror(errno)); + } + EXPECT_EQ(CGT(car_performanceFigures_octaneRating)(perfFigs), 95); + + CGT(car_performanceFigures_acceleration) *acc = CGT(car_performanceFigures_get_acceleration)(perfFigs); + if (!acc) + { + throw std::runtime_error(sbe_strerror(errno)); + } + EXPECT_EQ(CGT(car_performanceFigures_acceleration_count)(acc), 3u); + EXPECT_TRUE(CGT(car_performanceFigures_acceleration_has_next)(acc)); + if (!CGT(car_performanceFigures_acceleration_next)(acc)) + { + throw std::runtime_error(sbe_strerror(errno)); + } + EXPECT_EQ(CGT(car_performanceFigures_acceleration_mph)(acc), 30); + EXPECT_EQ(CGT(car_performanceFigures_acceleration_seconds)(acc), 4.0f); + + EXPECT_TRUE(CGT(car_performanceFigures_acceleration_has_next)(acc)); + if (!CGT(car_performanceFigures_acceleration_next)(acc)) + { + throw std::runtime_error(sbe_strerror(errno)); + } + EXPECT_EQ(CGT(car_performanceFigures_acceleration_mph)(acc), 60); + EXPECT_EQ(CGT(car_performanceFigures_acceleration_seconds)(acc), 7.5f); + + EXPECT_TRUE(CGT(car_performanceFigures_acceleration_has_next)(acc)); + if (!CGT(car_performanceFigures_acceleration_next)(acc)) + { + throw std::runtime_error(sbe_strerror(errno)); + } + EXPECT_EQ(CGT(car_performanceFigures_acceleration_mph)(acc), 100); + EXPECT_EQ(CGT(car_performanceFigures_acceleration_seconds)(acc), 12.2f); + + EXPECT_TRUE(CGT(car_performanceFigures_has_next)(perfFigs)); + if (!CGT(car_performanceFigures_next)(perfFigs)) + { + throw std::runtime_error(sbe_strerror(errno)); + } + EXPECT_EQ(CGT(car_performanceFigures_octaneRating)(perfFigs), 99); + + acc = CGT(car_performanceFigures_get_acceleration)(perfFigs); + if (!acc) + { + throw std::runtime_error(sbe_strerror(errno)); + } + EXPECT_EQ(CGT(car_performanceFigures_acceleration_count)(acc), 3u); + EXPECT_TRUE(CGT(car_performanceFigures_acceleration_has_next)(acc)); + if (!CGT(car_performanceFigures_acceleration_next)(acc)) + { + throw std::runtime_error(sbe_strerror(errno)); + } + EXPECT_EQ(CGT(car_performanceFigures_acceleration_mph)(acc), 30); + EXPECT_EQ(CGT(car_performanceFigures_acceleration_seconds)(acc), 3.8f); + + EXPECT_TRUE(CGT(car_performanceFigures_acceleration_has_next)(acc)); + if (!CGT(car_performanceFigures_acceleration_next)(acc)) + { + throw std::runtime_error(sbe_strerror(errno)); + } + EXPECT_EQ(CGT(car_performanceFigures_acceleration_mph)(acc), 60); + EXPECT_EQ(CGT(car_performanceFigures_acceleration_seconds)(acc), 7.1f); + + EXPECT_TRUE(CGT(car_performanceFigures_acceleration_has_next)(acc)); + if (!CGT(car_performanceFigures_acceleration_next)(acc)) + { + throw std::runtime_error(sbe_strerror(errno)); + } + EXPECT_EQ(CGT(car_performanceFigures_acceleration_mph)(acc), 100); + EXPECT_EQ(CGT(car_performanceFigures_acceleration_seconds)(acc), 11.8f); + + return CGT(car_encoded_length)(&m_carDecoder); + } + + std::uint64_t decodeCarManufacturerModelAndActivationCode() + { + char tmp[256]; + std::uint64_t lengthOfField = CGT(car_get_manufacturer)(&m_carDecoder, tmp, sizeof(tmp)); + if (!lengthOfField) + { + throw std::runtime_error(sbe_strerror(errno)); + } + EXPECT_EQ(lengthOfField, 5u); + EXPECT_EQ(std::string(tmp, 5), "Honda"); + + lengthOfField = CGT(car_get_model)(&m_carDecoder, tmp, sizeof(tmp)); + if (!lengthOfField) + { + throw std::runtime_error(sbe_strerror(errno)); + } + EXPECT_EQ(lengthOfField, 9u); + EXPECT_EQ(std::string(tmp, 9), "Civic VTi"); + + lengthOfField = CGT(car_get_activationCode)(&m_carDecoder, tmp, sizeof(tmp)); + if (!lengthOfField) + { + throw std::runtime_error(sbe_strerror(errno)); + } + EXPECT_EQ(lengthOfField, 8u); + EXPECT_EQ(std::string(tmp, 8), "deadbeef"); + + EXPECT_EQ(CGT(car_encoded_length)(&m_carDecoder), encodedCarSz); + + return CGT(car_encoded_length)(&m_carDecoder); + } + +private: + CGT(messageHeader) m_hdr; + CGT(messageHeader) m_hdrDecoder; + CGT(car) m_car; + CGT(car) m_carDecoder; +}; + +class HeaderBoundsCheckTest : public BoundsCheckTest, public ::testing::WithParamInterface +{ +}; + +TEST_P(HeaderBoundsCheckTest, shouldExceptionWhenBufferTooShortForEncodeOfHeader) +{ + const int length = GetParam(); + std::unique_ptr buffer(new char[length]); + + EXPECT_THROW( + { + encodeHdr(buffer.get(), 0, length); + }, std::runtime_error); +} + +TEST_P(HeaderBoundsCheckTest, shouldExceptionWhenBufferTooShortForDecodeOfHeader) +{ + const int length = GetParam(); + char encodeBuffer[8]; + std::unique_ptr buffer(new char[length]); + + encodeHdr(encodeBuffer, 0, sizeof(encodeBuffer)); + + EXPECT_THROW( + { + std::memcpy(buffer.get(), encodeBuffer, length); + decodeHdr(buffer.get(), 0, length); + }, std::runtime_error); +} + +INSTANTIATE_TEST_CASE_P( + HeaderLengthTest, + HeaderBoundsCheckTest, + ::testing::Range(0, static_cast(encodedHdrSz), 1)); + +class MessageBoundsCheckTest : public BoundsCheckTest, public ::testing::WithParamInterface +{ +}; + +TEST_P(MessageBoundsCheckTest, shouldExceptionWhenBufferTooShortForEncodeOfMessage) +{ + const int length = GetParam(); + std::unique_ptr buffer(new char[length]); + + EXPECT_THROW( + { + encodeCarRoot(buffer.get(), 0, length); + encodeCarFuelFigures(); + encodeCarPerformanceFigures(); + encodeCarManufacturerModelAndActivationCode(); + }, std::runtime_error); +} + +TEST_P(MessageBoundsCheckTest, shouldExceptionWhenBufferTooShortForDecodeOfMessage) +{ + const int length = GetParam(); + char encodeBuffer[191]; + std::unique_ptr buffer(new char[length]); + + encodeCarRoot(encodeBuffer, 0, sizeof(encodeBuffer)); + encodeCarFuelFigures(); + encodeCarPerformanceFigures(); + encodeCarManufacturerModelAndActivationCode(); + + EXPECT_THROW( + { + std::memcpy(buffer.get(), encodeBuffer, length); + decodeCarRoot(buffer.get(), 0, length); + decodeCarFuelFigures(); + decodeCarPerformanceFigures(); + decodeCarManufacturerModelAndActivationCode(); + }, std::runtime_error); +} + +INSTANTIATE_TEST_CASE_P( + MessageLengthTest, + MessageBoundsCheckTest, + ::testing::Range(0, static_cast(encodedCarSz), 1)); diff --git a/sbe-tool/src/test/c/CMakeLists.txt b/sbe-tool/src/test/c/CMakeLists.txt new file mode 100644 index 0000000000..eeab846d58 --- /dev/null +++ b/sbe-tool/src/test/c/CMakeLists.txt @@ -0,0 +1,38 @@ +function(sbe_test name) + add_executable("C${name}" "${name}.cpp") + target_include_directories("C${name}" + PRIVATE ${GTEST_SOURCE_DIR}/googletest/include + PRIVATE ${C_CODEC_TARGET_DIR} + ) + target_link_libraries("C${name}" ${GTEST_LIBS} ${CMAKE_THREAD_LIBS_INIT}) + add_test(NAME C${name} COMMAND C${name} WORKING_DIRECTORY ${C_CODEC_TARGET_DIR}) + add_dependencies(C${name} gtest) + if(${ARGC} GREATER 1) + add_dependencies(C${name} ${ARGV1}) + endif() +endfunction() + +find_package(Java REQUIRED) + +set(CODE_GENERATION_SCHEMA ${CODEC_SCHEMA_DIR}/code-generation-schema.xml) +set(GROUP_WITH_DATA_SCHEMA ${CODEC_SCHEMA_DIR}/group-with-data-schema.xml) + +set(GENERATED_CODECS + ${C_CODEC_TARGET_DIR} +) + +add_custom_command( + OUTPUT ${GENERATED_CODECS} + DEPENDS sbe-jar ${SBE_JAR} ${CODE_GENERATION_SCHEMA} ${GROUP_WITH_DATA_SCHEMA} + COMMAND ${Java_JAVA_EXECUTABLE} + -Dsbe.output.dir=${C_CODEC_TARGET_DIR} -Dsbe.target.language="C" -jar ${SBE_JAR} + ${CODE_GENERATION_SCHEMA} + ${GROUP_WITH_DATA_SCHEMA} +) + +add_custom_target(c_codecs DEPENDS ${GENERATED_CODECS}) + +# codec tests +sbe_test(BoundsCheckTest c_codecs) +sbe_test(CodeGenTest c_codecs) +sbe_test(GroupWithDataTest c_codecs) diff --git a/sbe-tool/src/test/c/CodeGenTest.cpp b/sbe-tool/src/test/c/CodeGenTest.cpp new file mode 100644 index 0000000000..686293e93b --- /dev/null +++ b/sbe-tool/src/test/c/CodeGenTest.cpp @@ -0,0 +1,974 @@ +#include + +#include + +#include +#include + +#define CGT(name) code_generation_test_##name + +static const std::size_t BUFFER_LEN = 2048; + +static const std::uint32_t SERIAL_NUMBER = 1234; +static const std::uint16_t MODEL_YEAR = 2013; +static const CGT(booleanType) AVAILABLE = CGT(booleanType_T); +static const CGT(model) CODE = CGT(model_A); +static const bool CRUISE_CONTROL = true; +static const bool SPORTS_PACK = true; +static const bool SUNROOF = false; +static const CGT(boostType) BOOST_TYPE = CGT(boostType_NITROUS); +static const std::uint8_t BOOSTER_HORSEPOWER = 200; + +static char VEHICLE_CODE[] = { 'a', 'b', 'c', 'd', 'e', 'f' }; +static char MANUFACTURER_CODE[] = { '1', '2', '3' }; +static const char FUEL_FIGURES_1_USAGE_DESCRIPTION[] = "Urban Cycle"; +static const char FUEL_FIGURES_2_USAGE_DESCRIPTION[] = "Combined Cycle"; +static const char FUEL_FIGURES_3_USAGE_DESCRIPTION[] = "Highway Cycle"; +static const char MANUFACTURER[] = "Honda"; +static const char MODEL[] = "Civic VTi"; +static const char ACTIVATION_CODE[] = "deadbeef"; + +static const std::uint64_t VEHICLE_CODE_LENGTH = sizeof(VEHICLE_CODE); +static const std::uint64_t MANUFACTURER_CODE_LENGTH = sizeof(MANUFACTURER_CODE); +static const std::uint64_t FUEL_FIGURES_1_USAGE_DESCRIPTION_LENGTH = 11; +static const std::uint64_t FUEL_FIGURES_2_USAGE_DESCRIPTION_LENGTH = 14; +static const std::uint64_t FUEL_FIGURES_3_USAGE_DESCRIPTION_LENGTH = 13; +static const std::uint64_t MANUFACTURER_LENGTH = 5; +static const std::uint64_t MODEL_LENGTH = 9; +static const std::uint64_t ACTIVATION_CODE_LENGTH = 8; +static const std::uint8_t PERFORMANCE_FIGURES_COUNT = 2; +static const std::uint8_t FUEL_FIGURES_COUNT = 3; +static const std::uint8_t ACCELERATION_COUNT = 3; + +static const std::uint64_t expectedHeaderSize = 8; +static const std::uint64_t expectedCarSize = 191; + +static const std::uint16_t fuel1Speed = 30; +static const float fuel1Mpg = 35.9f; +static const std::uint16_t fuel2Speed = 55; +static const float fuel2Mpg = 49.0f; +static const std::uint16_t fuel3Speed = 75; +static const float fuel3Mpg = 40.0f; + +static const std::uint8_t perf1Octane = 95; +static const std::uint16_t perf1aMph = 30; +static const float perf1aSeconds = 4.0f; +static const std::uint16_t perf1bMph = 60; +static const float perf1bSeconds = 7.5f; +static const std::uint16_t perf1cMph = 100; +static const float perf1cSeconds = 12.2f; + +static const std::uint8_t perf2Octane = 99; +static const std::uint16_t perf2aMph = 30; +static const float perf2aSeconds = 3.8f; +static const std::uint16_t perf2bMph = 60; +static const float perf2bSeconds = 7.1f; +static const std::uint16_t perf2cMph = 100; +static const float perf2cSeconds = 11.8f; + +static const std::uint16_t engineCapacity = 2000; +static const std::uint8_t engineNumCylinders = 4; + +class CodeGenTest : public testing::Test +{ +public: + static std::uint64_t encodeHdr(CGT(messageHeader)& hdr) + { + CGT(messageHeader_set_blockLength)(&hdr, CGT(car_sbe_block_length)()); + CGT(messageHeader_set_templateId)(&hdr, CGT(car_sbe_template_id)()); + CGT(messageHeader_set_schemaId)(&hdr, CGT(car_sbe_schema_id)()); + CGT(messageHeader_set_version)(&hdr, CGT(car_sbe_schema_version)()); + + return CGT(messageHeader_encoded_length)(); + } + + static std::uint64_t encodeCar(CGT(car)& car) + { + CGT(car_set_serialNumber)(&car, SERIAL_NUMBER); + CGT(car_set_modelYear)(&car, MODEL_YEAR); + CGT(car_set_available)(&car, AVAILABLE); + CGT(car_set_code)(&car, CODE); + CGT(car_put_vehicleCode)(&car, VEHICLE_CODE); + + for (std::uint64_t i = 0; i < CGT(car_someNumbers_length)(); i++) + { + CGT(car_set_someNumbers_unsafe)(&car, i, static_cast(i)); + } + + CGT(optionalExtras) *const extras = CGT(car_extras)(&car); + if (!extras) + { + throw std::runtime_error(sbe_strerror(errno)); + } + CGT(optionalExtras_clear)(extras); + CGT(optionalExtras_set_cruiseControl)(extras, CRUISE_CONTROL); + CGT(optionalExtras_set_sportsPack)(extras, SPORTS_PACK); + CGT(optionalExtras_set_sunRoof)(extras, SUNROOF); + + CGT(engine) *const engine = CGT(car_engine)(&car); + if (!engine) + { + throw std::runtime_error(sbe_strerror(errno)); + } + CGT(engine_set_capacity)(engine, engineCapacity); + CGT(engine_set_numCylinders)(engine, engineNumCylinders); + CGT(engine_put_manufacturerCode)(engine, MANUFACTURER_CODE); + + CGT(boosterT) *const booster = CGT(engine_booster)(engine); + if (!booster) + { + throw std::runtime_error(sbe_strerror(errno)); + } + CGT(boosterT_set_boostType)(booster, BOOST_TYPE); + CGT(boosterT_set_horsePower)(booster, BOOSTER_HORSEPOWER); + + CGT(car_fuelFigures) *const fuelFigures = CGT(car_fuelFigures_set_count)(&car, FUEL_FIGURES_COUNT); + if (!fuelFigures) + { + throw std::runtime_error(sbe_strerror(errno)); + } + CGT(car_fuelFigures_next)(fuelFigures); + CGT(car_fuelFigures_set_speed)(fuelFigures, fuel1Speed); + CGT(car_fuelFigures_set_mpg)(fuelFigures, fuel1Mpg); + CGT(car_fuelFigures_put_usageDescription)( + fuelFigures, + FUEL_FIGURES_1_USAGE_DESCRIPTION, + static_cast(strlen(FUEL_FIGURES_1_USAGE_DESCRIPTION))); + + CGT(car_fuelFigures_next)(fuelFigures); + CGT(car_fuelFigures_set_speed)(fuelFigures, fuel2Speed); + CGT(car_fuelFigures_set_mpg)(fuelFigures, fuel2Mpg); + CGT(car_fuelFigures_put_usageDescription)( + fuelFigures, + FUEL_FIGURES_2_USAGE_DESCRIPTION, + static_cast(strlen(FUEL_FIGURES_2_USAGE_DESCRIPTION))); + + CGT(car_fuelFigures_next)(fuelFigures); + CGT(car_fuelFigures_set_speed)(fuelFigures, fuel3Speed); + CGT(car_fuelFigures_set_mpg)(fuelFigures, fuel3Mpg); + CGT(car_fuelFigures_put_usageDescription)( + fuelFigures, + FUEL_FIGURES_3_USAGE_DESCRIPTION, + static_cast(strlen(FUEL_FIGURES_3_USAGE_DESCRIPTION))); + + CGT(car_performanceFigures) *const perfFigs = CGT(car_performanceFigures_set_count)( + &car, + PERFORMANCE_FIGURES_COUNT); + if (!perfFigs) + { + throw std::runtime_error(sbe_strerror(errno)); + } + CGT(car_performanceFigures_next)(perfFigs); + CGT(car_performanceFigures_set_octaneRating)(perfFigs, perf1Octane); + + CGT(car_performanceFigures_acceleration) *acc = + CGT(car_performanceFigures_acceleration_set_count)(perfFigs, ACCELERATION_COUNT); + if (!acc) + { + throw std::runtime_error(sbe_strerror(errno)); + } + CGT(car_performanceFigures_acceleration_next)(acc); + CGT(car_performanceFigures_acceleration_set_mph)(acc, perf1aMph); + CGT(car_performanceFigures_acceleration_set_seconds)(acc, perf1aSeconds); + CGT(car_performanceFigures_acceleration_next)(acc); + CGT(car_performanceFigures_acceleration_set_mph)(acc, perf1bMph); + CGT(car_performanceFigures_acceleration_set_seconds)(acc, perf1bSeconds); + CGT(car_performanceFigures_acceleration_next)(acc); + CGT(car_performanceFigures_acceleration_set_mph)(acc, perf1cMph); + CGT(car_performanceFigures_acceleration_set_seconds)(acc, perf1cSeconds); + + CGT(car_performanceFigures_next)(perfFigs); + CGT(car_performanceFigures_set_octaneRating)(perfFigs, perf2Octane); + + acc = CGT(car_performanceFigures_acceleration_set_count)(perfFigs, ACCELERATION_COUNT); + if (!acc) + { + throw std::runtime_error(sbe_strerror(errno)); + } + CGT(car_performanceFigures_acceleration_next)(acc); + CGT(car_performanceFigures_acceleration_set_mph)(acc, perf2aMph); + CGT(car_performanceFigures_acceleration_set_seconds)(acc, perf2aSeconds); + CGT(car_performanceFigures_acceleration_next)(acc); + CGT(car_performanceFigures_acceleration_set_mph)(acc, perf2bMph); + CGT(car_performanceFigures_acceleration_set_seconds)(acc, perf2bSeconds); + CGT(car_performanceFigures_acceleration_next)(acc); + CGT(car_performanceFigures_acceleration_set_mph)(acc, perf2cMph); + CGT(car_performanceFigures_acceleration_set_seconds)(acc, perf2cSeconds); + + CGT(car_put_manufacturer)(&car, MANUFACTURER, static_cast(strlen(MANUFACTURER))); + CGT(car_put_model)(&car, MODEL, static_cast(strlen(MODEL))); + CGT(car_put_activationCode)(&car, ACTIVATION_CODE, static_cast(strlen(ACTIVATION_CODE))); + + return CGT(car_encoded_length)(&car); + } + + std::uint64_t encodeHdr(char *buffer, std::uint64_t offset, std::uint64_t bufferLength) + { + if (!CGT(messageHeader_wrap)(&m_hdr, buffer, offset, 0, bufferLength)) + { + throw std::runtime_error(sbe_strerror(errno)); + } + return encodeHdr(m_hdr); + } + + std::uint64_t encodeCar(char *buffer, std::uint64_t offset, std::uint64_t bufferLength) + { + if (!CGT(car_wrap_for_encode)(&m_car, buffer, offset, bufferLength)) + { + throw std::runtime_error(sbe_strerror(errno)); + } + return encodeCar(m_car); + } + + CGT(messageHeader) m_hdr; + CGT(messageHeader) m_hdrDecoder; + CGT(car) m_car; + CGT(car) m_carDecoder; +}; + +TEST_F(CodeGenTest, shouldReturnCorrectValuesForMessageHeaderStaticFields) +{ + EXPECT_EQ(CGT(messageHeader_encoded_length)(), 8u); + // only checking the block length field + EXPECT_EQ(CGT(messageHeader_blockLength_null_value)(), 65535); + EXPECT_EQ(CGT(messageHeader_blockLength_min_value)(), 0); + EXPECT_EQ(CGT(messageHeader_blockLength_max_value)(), 65534); +} + +TEST_F(CodeGenTest, shouldReturnCorrectValuesForCarStaticFields) +{ + EXPECT_EQ(CGT(car_sbe_block_length)(), 47u); + EXPECT_EQ(CGT(car_sbe_template_id)(), 1u); + EXPECT_EQ(CGT(car_sbe_schema_id)(), 6u); + EXPECT_EQ(CGT(car_sbe_schema_version)(), 0u); + EXPECT_EQ(std::string(CGT(car_sbe_semantic_type)()), std::string("")); +} + +TEST_F(CodeGenTest, shouldBeAbleToEncodeMessageHeaderCorrectly) +{ + char buffer[BUFFER_LEN]; + const char *bp = buffer; + + std::uint64_t sz = encodeHdr(buffer, 0, sizeof(buffer)); + + EXPECT_EQ(*((uint16_t *)bp), CGT(car_sbe_block_length)()); + EXPECT_EQ(*((uint16_t *)(bp + 2)), CGT(car_sbe_template_id)()); + EXPECT_EQ(*((uint16_t *)(bp + 4)), CGT(car_sbe_schema_id)()); + EXPECT_EQ(*((uint16_t *)(bp + 6)), CGT(car_sbe_schema_version)()); + EXPECT_EQ(sz, 8u); +} + +TEST_F(CodeGenTest, shouldBeAbleToEncodeAndDecodeMessageHeaderCorrectly) +{ + char buffer[BUFFER_LEN]; + + encodeHdr(buffer, 0, sizeof(buffer)); + + if (!CGT(messageHeader_wrap)(&m_hdrDecoder, buffer, 0, 0, sizeof(buffer))) + { + throw std::runtime_error(sbe_strerror(errno)); + } + EXPECT_EQ(CGT(messageHeader_blockLength)(&m_hdrDecoder), CGT(car_sbe_block_length)()); + EXPECT_EQ(CGT(messageHeader_templateId)(&m_hdrDecoder), CGT(car_sbe_template_id)()); + EXPECT_EQ(CGT(messageHeader_schemaId)(&m_hdrDecoder), CGT(car_sbe_schema_id)()); + EXPECT_EQ(CGT(messageHeader_version)(&m_hdrDecoder), CGT(car_sbe_schema_version)()); +} + +static const uint8_t fieldIdSerialNumber = 1; +static const uint8_t fieldIdModelYear = 2; +static const uint8_t fieldIdAvailable = 3; +static const uint8_t fieldIdCode = 4; +static const uint8_t fieldIdSomeNumbers = 5; +static const uint8_t fieldIdVehicleCode = 6; +static const uint8_t fieldIdExtras = 7; +static const uint8_t fieldIdDiscountedModel = 8; +static const uint8_t fieldIdEngine = 9; +static const uint8_t fieldIdFuelFigures = 10; +static const uint8_t fieldIdFuelSpeed = 11; +static const uint8_t fieldIdFuelMpg = 12; +static const uint8_t fieldIdFuelUsageDescription = 200; +static const uint8_t fieldIdPerformanceFigures = 13; +static const uint8_t fieldIdPerfOctaneRating = 14; +static const uint8_t fieldIdPerfAcceleration = 15; +static const uint8_t fieldIdPerfAccMph = 16; +static const uint8_t fieldIdPerfAccSeconds = 17; +static const uint8_t fieldIdManufacturer = 18; +static const uint8_t fieldIdModel = 19; +static const uint8_t fieldIdActivationCode = 20; + +TEST_F(CodeGenTest, shouldReturnCorrectValuesForCarFieldIdsAndCharacterEncoding) +{ + EXPECT_EQ(CGT(car_serialNumber_id)(), fieldIdSerialNumber); + EXPECT_EQ(CGT(car_modelYear_id)(), fieldIdModelYear); + EXPECT_EQ(CGT(car_available_id)(), fieldIdAvailable); + EXPECT_EQ(CGT(car_code_id)(), fieldIdCode); + EXPECT_EQ(CGT(car_someNumbers_id)(), fieldIdSomeNumbers); + EXPECT_EQ(CGT(car_vehicleCode_id)(), fieldIdVehicleCode); + EXPECT_EQ(CGT(car_extras_id)(), fieldIdExtras); + EXPECT_EQ(CGT(car_discountedModel_id)(), fieldIdDiscountedModel); + EXPECT_EQ(CGT(car_engine_id)(), fieldIdEngine); + EXPECT_EQ(CGT(car_fuelFigures_id)(), fieldIdFuelFigures); + EXPECT_EQ(CGT(car_fuelFigures_speed_id)(), fieldIdFuelSpeed); + EXPECT_EQ(CGT(car_fuelFigures_mpg_id)(), fieldIdFuelMpg); + EXPECT_EQ(CGT(car_fuelFigures_usageDescription_id)(), fieldIdFuelUsageDescription); + EXPECT_EQ(CGT(car_fuelFigures_usageDescription_character_encoding)(), std::string("UTF-8")); + EXPECT_EQ(CGT(car_performanceFigures_id)(), fieldIdPerformanceFigures); + EXPECT_EQ(CGT(car_performanceFigures_octaneRating_id)(), fieldIdPerfOctaneRating); + EXPECT_EQ(CGT(car_performanceFigures_acceleration_id)(), fieldIdPerfAcceleration); + EXPECT_EQ(CGT(car_performanceFigures_acceleration_mph_id)(), fieldIdPerfAccMph); + EXPECT_EQ(CGT(car_performanceFigures_acceleration_seconds_id)(), fieldIdPerfAccSeconds); + EXPECT_EQ(CGT(car_manufacturer_id)(), fieldIdManufacturer); + EXPECT_EQ(CGT(car_model_id)(), fieldIdModel); + EXPECT_EQ(CGT(car_activationCode_id)(), fieldIdActivationCode); + EXPECT_EQ(std::string(CGT(car_manufacturer_character_encoding())), std::string("UTF-8")); + EXPECT_EQ(std::string(CGT(car_model_character_encoding())), std::string("UTF-8")); + EXPECT_EQ(std::string(CGT(car_activationCode_character_encoding())), std::string("UTF-8")); +} + +TEST_F(CodeGenTest, shouldBeAbleToEncodeCarCorrectly) +{ + char buffer[BUFFER_LEN]; + const char *bp = buffer; + std::uint64_t sz = encodeCar(buffer, 0, sizeof(buffer)); + + std::uint64_t offset = 0; + EXPECT_EQ(*(std::uint64_t *)(bp + offset), SERIAL_NUMBER); + offset += sizeof(std::uint64_t); + EXPECT_EQ(*(std::uint16_t *)(bp + offset), MODEL_YEAR); + offset += sizeof(std::uint16_t); + EXPECT_EQ(*(std::uint8_t *)(bp + offset), 1); + offset += sizeof(std::uint8_t); + EXPECT_EQ(*(bp + offset), 'A'); + offset += sizeof(char); + + EXPECT_EQ(*(std::int32_t *)(bp + offset), 0); + offset += sizeof(std::int32_t); + EXPECT_EQ(*(std::int32_t *)(bp + offset), 1); + offset += sizeof(std::int32_t); + EXPECT_EQ(*(std::int32_t *)(bp + offset), 2); + offset += sizeof(std::int32_t); + EXPECT_EQ(*(std::int32_t *)(bp + offset), 3); + offset += sizeof(std::int32_t); + EXPECT_EQ(*(std::int32_t *)(bp + offset), 4); + offset += sizeof(std::int32_t); + + EXPECT_EQ(std::string(bp + offset, VEHICLE_CODE_LENGTH), std::string(VEHICLE_CODE, VEHICLE_CODE_LENGTH)); + offset += VEHICLE_CODE_LENGTH; + EXPECT_EQ(*(bp + offset), 0x6); + offset += sizeof(std::uint8_t); + EXPECT_EQ(*(std::uint16_t *)(bp + offset), engineCapacity); + offset += sizeof(std::uint16_t); + EXPECT_EQ(*(bp + offset), engineNumCylinders); + offset += sizeof(std::uint8_t); + EXPECT_EQ(std::string(bp + offset, MANUFACTURER_CODE_LENGTH), std::string(MANUFACTURER_CODE, MANUFACTURER_CODE_LENGTH)); + offset += MANUFACTURER_CODE_LENGTH; + EXPECT_EQ(*(bp + offset), 'N'); + offset += sizeof(char); + EXPECT_EQ(*(std::uint8_t *)(bp + offset), BOOSTER_HORSEPOWER); + offset += sizeof(std::uint8_t); + + // fuel figures + EXPECT_EQ(*(std::uint16_t *)(bp + offset), 6); + offset += sizeof(std::uint16_t); + EXPECT_EQ(*(std::uint16_t *)(bp + offset), FUEL_FIGURES_COUNT); + offset += sizeof(std::uint16_t); + + EXPECT_EQ(*(::uint16_t *)(bp + offset), fuel1Speed); + offset += sizeof(std::uint16_t); + EXPECT_EQ(*(float *)(bp + offset), fuel1Mpg); + offset += sizeof(float); + EXPECT_EQ(*(std::uint16_t *)(bp + offset), FUEL_FIGURES_1_USAGE_DESCRIPTION_LENGTH); + offset += sizeof(std::uint16_t); + EXPECT_EQ( + std::string(bp + offset, FUEL_FIGURES_1_USAGE_DESCRIPTION_LENGTH), FUEL_FIGURES_1_USAGE_DESCRIPTION); + offset += FUEL_FIGURES_1_USAGE_DESCRIPTION_LENGTH; + + EXPECT_EQ(*(std::uint16_t *)(bp + offset), fuel2Speed); + offset += sizeof(std::uint16_t); + EXPECT_EQ(*(float *)(bp + offset), fuel2Mpg); + offset += sizeof(float); + EXPECT_EQ(*(std::uint16_t *)(bp + offset), FUEL_FIGURES_2_USAGE_DESCRIPTION_LENGTH); + offset += sizeof(std::uint16_t); + EXPECT_EQ( + std::string(bp + offset, FUEL_FIGURES_2_USAGE_DESCRIPTION_LENGTH), FUEL_FIGURES_2_USAGE_DESCRIPTION); + offset += FUEL_FIGURES_2_USAGE_DESCRIPTION_LENGTH; + + EXPECT_EQ(*(std::uint16_t *)(bp + offset), fuel3Speed); + offset += sizeof(std::uint16_t); + EXPECT_EQ(*(float *)(bp + offset), fuel3Mpg); + offset += sizeof(float); + EXPECT_EQ(*(std::uint16_t *)(bp + offset), FUEL_FIGURES_3_USAGE_DESCRIPTION_LENGTH); + offset += sizeof(std::uint16_t); + EXPECT_EQ( + std::string(bp + offset, FUEL_FIGURES_3_USAGE_DESCRIPTION_LENGTH), FUEL_FIGURES_3_USAGE_DESCRIPTION); + offset += FUEL_FIGURES_3_USAGE_DESCRIPTION_LENGTH; + + // performance figures + EXPECT_EQ(*(std::uint16_t *)(bp + offset), 1); + offset += sizeof(std::uint16_t); + EXPECT_EQ(*(std::uint16_t *)(bp + offset), PERFORMANCE_FIGURES_COUNT); + offset += sizeof(std::uint16_t); + EXPECT_EQ(*(bp + offset), perf1Octane); + offset += sizeof(std::uint8_t); + // acceleration + EXPECT_EQ(*(std::uint16_t *)(bp + offset), 6); + offset += sizeof(std::uint16_t); + EXPECT_EQ(*(std::uint16_t *)(bp + offset), ACCELERATION_COUNT); + offset += sizeof(std::uint16_t); + EXPECT_EQ(*(std::uint16_t *)(bp + offset), perf1aMph); + offset += sizeof(std::uint16_t); + EXPECT_EQ(*(float *)(bp + offset), perf1aSeconds); + offset += sizeof(float); + EXPECT_EQ(*(std::uint16_t *)(bp + offset), perf1bMph); + offset += sizeof(std::uint16_t); + EXPECT_EQ(*(float *)(bp + offset), perf1bSeconds); + offset += sizeof(float); + EXPECT_EQ(*(std::uint16_t *)(bp + offset), perf1cMph); + offset += sizeof(std::uint16_t); + EXPECT_EQ(*(float *)(bp + offset), perf1cSeconds); + offset += sizeof(float); + + EXPECT_EQ(*(bp + offset), perf2Octane); + offset += sizeof(std::uint8_t); + // acceleration + EXPECT_EQ(*(std::uint16_t *)(bp + offset), 6); + offset += sizeof(std::uint16_t); + EXPECT_EQ(*(std::uint16_t *)(bp + offset), ACCELERATION_COUNT); + offset += sizeof(std::uint16_t); + EXPECT_EQ(*(std::uint16_t *)(bp + offset), perf2aMph); + offset += sizeof(std::uint16_t); + EXPECT_EQ(*(float *)(bp + offset), perf2aSeconds); + offset += sizeof(float); + EXPECT_EQ(*(std::uint16_t *)(bp + offset), perf2bMph); + offset += sizeof(std::uint16_t); + EXPECT_EQ(*(float *)(bp + offset), perf2bSeconds); + offset += sizeof(float); + EXPECT_EQ(*(std::uint16_t *)(bp + offset), perf2cMph); + offset += sizeof(std::uint16_t); + EXPECT_EQ(*(float *)(bp + offset), perf2cSeconds); + offset += sizeof(float); + + // manufacturer & model + EXPECT_EQ(*(std::uint16_t *)(bp + offset), MANUFACTURER_LENGTH); + offset += sizeof(std::uint16_t); + EXPECT_EQ(std::string(bp + offset, MANUFACTURER_LENGTH), MANUFACTURER); + offset += MANUFACTURER_LENGTH; + EXPECT_EQ(*(std::uint16_t *)(bp + offset), MODEL_LENGTH); + offset += sizeof(std::uint16_t); + EXPECT_EQ(std::string(bp + offset, MODEL_LENGTH), MODEL); + offset += MODEL_LENGTH; + EXPECT_EQ(*(std::uint16_t *)(bp + offset), ACTIVATION_CODE_LENGTH); + offset += sizeof(std::uint16_t); + EXPECT_EQ(std::string(bp + offset, ACTIVATION_CODE_LENGTH), ACTIVATION_CODE); + offset += ACTIVATION_CODE_LENGTH; + + EXPECT_EQ(sz, offset); +} + +TEST_F(CodeGenTest, shouldBeAbleToEncodeHeaderPlusCarCorrectly) +{ + char buffer[BUFFER_LEN]; + const char *bp = buffer; + + std::uint64_t hdrSz = encodeHdr(buffer, 0, sizeof(buffer)); + std::uint64_t carSz = encodeCar(buffer, CGT(messageHeader_encoded_length)(), sizeof(buffer) - CGT(messageHeader_encoded_length)()); + + EXPECT_EQ(hdrSz, expectedHeaderSize); + EXPECT_EQ(carSz, expectedCarSize); + + EXPECT_EQ(*((std::uint16_t *)bp), CGT(car_sbe_block_length)()); + const size_t activationCodePosition = hdrSz + carSz - ACTIVATION_CODE_LENGTH; + const size_t activationCodeLengthPosition = activationCodePosition - sizeof(std::uint16_t); + EXPECT_EQ(*(std::uint16_t *)(bp + activationCodeLengthPosition), ACTIVATION_CODE_LENGTH); + EXPECT_EQ(std::string(bp + activationCodePosition, ACTIVATION_CODE_LENGTH), ACTIVATION_CODE); +} + +TEST_F(CodeGenTest, shouldbeAbleToEncodeAndDecodeHeaderPlusCarCorrectly) +{ + char buffer[BUFFER_LEN]; + + std::uint64_t hdrSz = encodeHdr(buffer, 0, sizeof(buffer)); + std::uint64_t carSz = encodeCar(buffer, CGT(messageHeader_encoded_length)(), sizeof(buffer) - CGT(messageHeader_encoded_length)()); + + EXPECT_EQ(hdrSz, expectedHeaderSize); + EXPECT_EQ(carSz, expectedCarSize); + + if (!CGT(messageHeader_wrap)(&m_hdrDecoder, buffer, 0, 0, hdrSz)) + { + throw std::runtime_error(sbe_strerror(errno)); + } + + EXPECT_EQ(CGT(messageHeader_blockLength)(&m_hdrDecoder), CGT(car_sbe_block_length)()); + EXPECT_EQ(CGT(messageHeader_templateId)(&m_hdrDecoder), CGT(car_sbe_template_id)()); + EXPECT_EQ(CGT(messageHeader_schemaId)(&m_hdrDecoder), CGT(car_sbe_schema_id)()); + EXPECT_EQ(CGT(messageHeader_version)(&m_hdrDecoder), CGT(car_sbe_schema_version)()); + EXPECT_EQ(CGT(messageHeader_encoded_length)(), expectedHeaderSize); + + if (!CGT(car_wrap_for_decode)( + &m_carDecoder, + buffer, + CGT(messageHeader_encoded_length)(), + CGT(car_sbe_block_length)(), + CGT(car_sbe_schema_version)(), + hdrSz + carSz)) + { + throw std::runtime_error(sbe_strerror(errno)); + } + + EXPECT_EQ(CGT(car_serialNumber)(&m_carDecoder), SERIAL_NUMBER); + EXPECT_EQ(CGT(car_modelYear)(&m_carDecoder), MODEL_YEAR); + { + CGT(booleanType) out; + ASSERT_TRUE(CGT(car_available)(&m_carDecoder, &out)); + EXPECT_EQ(out, AVAILABLE); + } + { + CGT(model) out; + ASSERT_TRUE(CGT(car_code)(&m_carDecoder, &out)); + EXPECT_EQ(out, CODE); + } + EXPECT_EQ(CGT(car_someNumbers_length)(), 5u); + for (std::uint64_t i = 0; i < 5; i++) + { + EXPECT_EQ(CGT(car_someNumbers_unsafe)(&m_carDecoder, i), static_cast(i)); + } + + EXPECT_EQ(CGT(car_vehicleCode_length)(), VEHICLE_CODE_LENGTH); + EXPECT_EQ(std::string(CGT(car_vehicleCode_buffer)(&m_carDecoder), VEHICLE_CODE_LENGTH), std::string(VEHICLE_CODE, VEHICLE_CODE_LENGTH)); + + CGT(optionalExtras) *const extras = CGT(car_extras)(&m_carDecoder); + if (!extras) + { + throw std::runtime_error(sbe_strerror(errno)); + } + + EXPECT_TRUE(CGT(optionalExtras_cruiseControl)(extras)); + EXPECT_TRUE(CGT(optionalExtras_sportsPack)(extras)); + EXPECT_FALSE(CGT(optionalExtras_sunRoof)(extras)); + EXPECT_EQ(CGT(car_discountedModel)(&m_carDecoder), CGT(model_C)); + + CGT(engine) *engine = CGT(car_engine)(&m_carDecoder); + if (!engine) + { + throw std::runtime_error(sbe_strerror(errno)); + } + EXPECT_EQ(CGT(engine_capacity)(engine), engineCapacity); + EXPECT_EQ(CGT(engine_numCylinders)(engine), engineNumCylinders); + EXPECT_EQ(CGT(engine_maxRpm)(), 9000); + EXPECT_EQ(CGT(engine_manufacturerCode_length)(), MANUFACTURER_CODE_LENGTH); + EXPECT_EQ( + std::string( + CGT(engine_manufacturerCode_buffer)(engine), + MANUFACTURER_CODE_LENGTH), + std::string(MANUFACTURER_CODE, MANUFACTURER_CODE_LENGTH)); + EXPECT_EQ(CGT(engine_fuel_length)(), 6u); + EXPECT_EQ(std::string(CGT(engine_fuel)(), 6), std::string("Petrol")); + + CGT(car_fuelFigures) *fuelFigures = CGT(car_get_fuelFigures)(&m_carDecoder); + EXPECT_EQ(CGT(car_fuelFigures_count)(fuelFigures), FUEL_FIGURES_COUNT); + + ASSERT_TRUE(CGT(car_fuelFigures_has_next)(fuelFigures)); + CGT(car_fuelFigures_next)(fuelFigures); + EXPECT_EQ(CGT(car_fuelFigures_speed)(fuelFigures), fuel1Speed); + EXPECT_EQ(CGT(car_fuelFigures_mpg)(fuelFigures), fuel1Mpg); + EXPECT_EQ(CGT(car_fuelFigures_usageDescription_length)(fuelFigures), FUEL_FIGURES_1_USAGE_DESCRIPTION_LENGTH); + EXPECT_EQ(std::string(CGT(car_fuelFigures_usageDescription)(fuelFigures), FUEL_FIGURES_1_USAGE_DESCRIPTION_LENGTH), FUEL_FIGURES_1_USAGE_DESCRIPTION); + + ASSERT_TRUE(CGT(car_fuelFigures_has_next)(fuelFigures)); + CGT(car_fuelFigures_next)(fuelFigures); + EXPECT_EQ(CGT(car_fuelFigures_speed)(fuelFigures), fuel2Speed); + EXPECT_EQ(CGT(car_fuelFigures_mpg)(fuelFigures), fuel2Mpg); + EXPECT_EQ(CGT(car_fuelFigures_usageDescription_length)(fuelFigures), FUEL_FIGURES_2_USAGE_DESCRIPTION_LENGTH); + EXPECT_EQ(std::string(CGT(car_fuelFigures_usageDescription)(fuelFigures), FUEL_FIGURES_2_USAGE_DESCRIPTION_LENGTH), FUEL_FIGURES_2_USAGE_DESCRIPTION); + + ASSERT_TRUE(CGT(car_fuelFigures_has_next)(fuelFigures)); + CGT(car_fuelFigures_next)(fuelFigures); + EXPECT_EQ(CGT(car_fuelFigures_speed)(fuelFigures), fuel3Speed); + EXPECT_EQ(CGT(car_fuelFigures_mpg)(fuelFigures), fuel3Mpg); + EXPECT_EQ(CGT(car_fuelFigures_usageDescription_length)(fuelFigures), FUEL_FIGURES_3_USAGE_DESCRIPTION_LENGTH); + EXPECT_EQ(std::string(CGT(car_fuelFigures_usageDescription)(fuelFigures), FUEL_FIGURES_3_USAGE_DESCRIPTION_LENGTH), FUEL_FIGURES_3_USAGE_DESCRIPTION); + + CGT(car_performanceFigures) *performanceFigures = CGT(car_get_performanceFigures)(&m_carDecoder); + EXPECT_EQ(CGT(car_performanceFigures_count)(performanceFigures), PERFORMANCE_FIGURES_COUNT); + + ASSERT_TRUE(CGT(car_performanceFigures_has_next)(performanceFigures)); + CGT(car_performanceFigures_next)(performanceFigures); + EXPECT_EQ(CGT(car_performanceFigures_octaneRating)(performanceFigures), perf1Octane); + + CGT(car_performanceFigures_acceleration) *acc = CGT(car_performanceFigures_get_acceleration)(performanceFigures); + EXPECT_EQ(CGT(car_performanceFigures_acceleration_count)(acc), ACCELERATION_COUNT); + ASSERT_TRUE(CGT(car_performanceFigures_acceleration_has_next)(acc)); + CGT(car_performanceFigures_acceleration_next)(acc); + EXPECT_EQ(CGT(car_performanceFigures_acceleration_mph)(acc), perf1aMph); + EXPECT_EQ(CGT(car_performanceFigures_acceleration_seconds)(acc), perf1aSeconds); + + ASSERT_TRUE(CGT(car_performanceFigures_acceleration_has_next)(acc)); + CGT(car_performanceFigures_acceleration_next)(acc); + EXPECT_EQ(CGT(car_performanceFigures_acceleration_mph)(acc), perf1bMph); + EXPECT_EQ(CGT(car_performanceFigures_acceleration_seconds)(acc), perf1bSeconds); + + ASSERT_TRUE(CGT(car_performanceFigures_acceleration_has_next)(acc)); + CGT(car_performanceFigures_acceleration_next)(acc); + EXPECT_EQ(CGT(car_performanceFigures_acceleration_mph)(acc), perf1cMph); + EXPECT_EQ(CGT(car_performanceFigures_acceleration_seconds)(acc), perf1cSeconds); + + ASSERT_TRUE(CGT(car_performanceFigures_has_next)(performanceFigures)); + CGT(car_performanceFigures_next)(performanceFigures); + EXPECT_EQ(CGT(car_performanceFigures_octaneRating)(performanceFigures), perf2Octane); + + acc = CGT(car_performanceFigures_get_acceleration)(performanceFigures); + EXPECT_EQ(CGT(car_performanceFigures_acceleration_count)(acc), ACCELERATION_COUNT); + ASSERT_TRUE(CGT(car_performanceFigures_acceleration_has_next)(acc)); + CGT(car_performanceFigures_acceleration_next)(acc); + EXPECT_EQ(CGT(car_performanceFigures_acceleration_mph)(acc), perf2aMph); + EXPECT_EQ(CGT(car_performanceFigures_acceleration_seconds)(acc), perf2aSeconds); + + ASSERT_TRUE(CGT(car_performanceFigures_acceleration_has_next)(acc)); + CGT(car_performanceFigures_acceleration_next)(acc); + EXPECT_EQ(CGT(car_performanceFigures_acceleration_mph)(acc), perf2bMph); + EXPECT_EQ(CGT(car_performanceFigures_acceleration_seconds)(acc), perf2bSeconds); + + ASSERT_TRUE(CGT(car_performanceFigures_acceleration_has_next)(acc)); + CGT(car_performanceFigures_acceleration_next)(acc); + EXPECT_EQ(CGT(car_performanceFigures_acceleration_mph)(acc), perf2cMph); + EXPECT_EQ(CGT(car_performanceFigures_acceleration_seconds)(acc), perf2cSeconds); + + EXPECT_EQ(CGT(car_manufacturer_length)(&m_carDecoder), MANUFACTURER_LENGTH); + EXPECT_EQ(std::string(CGT(car_manufacturer)(&m_carDecoder), MANUFACTURER_LENGTH), MANUFACTURER); + + EXPECT_EQ(CGT(car_model_length)(&m_carDecoder), MODEL_LENGTH); + EXPECT_EQ(std::string(CGT(car_model)(&m_carDecoder), MODEL_LENGTH), MODEL); + + EXPECT_EQ(CGT(car_activationCode_length)(&m_carDecoder), ACTIVATION_CODE_LENGTH); + EXPECT_EQ(std::string(CGT(car_activationCode)(&m_carDecoder), ACTIVATION_CODE_LENGTH), ACTIVATION_CODE); + + EXPECT_EQ(CGT(car_encoded_length)(&m_carDecoder), expectedCarSize); +} + +struct CallbacksForEach +{ + int countOfFuelFigures; + int countOfPerformanceFigures; + int countOfAccelerations; + + CallbacksForEach() : countOfFuelFigures(0), countOfPerformanceFigures(0), countOfAccelerations(0) {} +}; + +TEST_F(CodeGenTest, shouldbeAbleUseOnStackCodecsAndGroupForEach) +{ + char buffer[BUFFER_LEN]; + CGT(messageHeader) hdr; + if (!CGT(messageHeader_reset)(&hdr, buffer, 0, sizeof(buffer), 0)) + { + throw std::runtime_error(sbe_strerror(errno)); + } + + CGT(car) car; + if (!CGT(car_reset)( + &car, + buffer + CGT(messageHeader_encoded_length)(), + 0, + sizeof(buffer) - CGT(messageHeader_encoded_length)(), + CGT(car_sbe_block_length)(), + CGT(car_sbe_schema_version)())) + { + throw std::runtime_error(sbe_strerror(errno)); + } + + std::uint64_t hdrSz = encodeHdr(hdr); + std::uint64_t carSz = encodeCar(car); + + EXPECT_EQ(hdrSz, expectedHeaderSize); + EXPECT_EQ(carSz, expectedCarSize); + + CGT(messageHeader) hdrDecoder; + if (!CGT(messageHeader_reset)(&hdrDecoder, buffer, 0, hdrSz, 0)) + { + throw std::runtime_error(sbe_strerror(errno)); + } + + EXPECT_EQ(CGT(messageHeader_blockLength)(&hdrDecoder), CGT(car_sbe_block_length)()); + EXPECT_EQ(CGT(messageHeader_templateId)(&hdrDecoder), CGT(car_sbe_template_id)()); + EXPECT_EQ(CGT(messageHeader_schemaId)(&hdrDecoder), CGT(car_sbe_schema_id)()); + EXPECT_EQ(CGT(messageHeader_version)(&hdrDecoder), CGT(car_sbe_schema_version)()); + EXPECT_EQ(CGT(messageHeader_encoded_length)(), expectedHeaderSize); + + CGT(car) carDecoder; + if (!CGT(car_reset)( + &carDecoder, + buffer + CGT(messageHeader_encoded_length)(), + 0, + carSz, + CGT(car_sbe_block_length)(), + CGT(car_sbe_schema_version)())) + { + throw std::runtime_error(sbe_strerror(errno)); + } + + CallbacksForEach cbs; + + CGT(car_fuelFigures) *fuelFigures = CGT(car_get_fuelFigures)(&carDecoder); + if (!fuelFigures) + { + throw std::runtime_error(sbe_strerror(errno)); + } + + EXPECT_EQ(CGT(car_fuelFigures_count)(fuelFigures), FUEL_FIGURES_COUNT); + + ASSERT_TRUE(CGT(car_fuelFigures_for_each)( + fuelFigures, + [](CGT(car_fuelFigures) *const figures, void *cbs) + { + reinterpret_cast(cbs)->countOfFuelFigures++; + + char tmp[256]; + CGT(car_fuelFigures_get_usageDescription)(figures, tmp, sizeof(tmp)); + }, + &cbs)); + + CGT(car_performanceFigures) *performanceFigures = CGT(car_get_performanceFigures)(&carDecoder); + if (!performanceFigures) + { + throw std::runtime_error(sbe_strerror(errno)); + } + + EXPECT_EQ(CGT(car_performanceFigures_count)(performanceFigures), PERFORMANCE_FIGURES_COUNT); + + ASSERT_TRUE(CGT(car_performanceFigures_for_each)( + performanceFigures, + [](CGT(car_performanceFigures) *const figures, void *cbs) + { + CGT(car_performanceFigures_acceleration) *const acceleration = + CGT(car_performanceFigures_get_acceleration(figures)); + if (!acceleration) + { + throw std::runtime_error(sbe_strerror(errno)); + } + reinterpret_cast(cbs)->countOfPerformanceFigures++; + ASSERT_TRUE(CGT(car_performanceFigures_acceleration_for_each)( + acceleration, + [](CGT(car_performanceFigures_acceleration) *const, void *cbs) + { + reinterpret_cast(cbs)->countOfAccelerations++; + }, + cbs + )); + }, + &cbs)); + + EXPECT_EQ(cbs.countOfFuelFigures, FUEL_FIGURES_COUNT); + EXPECT_EQ(cbs.countOfPerformanceFigures, PERFORMANCE_FIGURES_COUNT); + EXPECT_EQ(cbs.countOfAccelerations, ACCELERATION_COUNT * PERFORMANCE_FIGURES_COUNT); + + char tmp[256]; + + EXPECT_EQ(CGT(car_get_manufacturer)(&carDecoder, tmp, sizeof(tmp)), MANUFACTURER_LENGTH); + EXPECT_EQ(std::string(tmp, MANUFACTURER_LENGTH), MANUFACTURER); + + EXPECT_EQ(CGT(car_get_model)(&carDecoder, tmp, sizeof(tmp)), MODEL_LENGTH); + EXPECT_EQ(std::string(tmp, MODEL_LENGTH), MODEL); + + EXPECT_EQ(CGT(car_get_manufacturer)(&carDecoder, tmp, sizeof(tmp)), ACTIVATION_CODE_LENGTH); + EXPECT_EQ(std::string(tmp, ACTIVATION_CODE_LENGTH), ACTIVATION_CODE); + + EXPECT_EQ(CGT(car_encoded_length)(&carDecoder), expectedCarSize); +} + +static const std::size_t offsetVehicleCode = 32; +static const std::size_t offsetUsageDesc1Length = 57; +static const std::size_t offsetUsageDesc1Data = offsetUsageDesc1Length + sizeof(std::uint16_t); +static const std::size_t offsetUsageDesc2Length = 76; +static const std::size_t offsetUsageDesc2Data = offsetUsageDesc2Length + sizeof(std::uint16_t); +static const std::size_t offsetUsageDesc3Length = 98; +static const std::size_t offsetUsageDesc3Data = offsetUsageDesc3Length + sizeof(std::uint16_t); +static const std::size_t offsetManufacturerLength = 163; +static const std::size_t offsetManufacturerData = offsetManufacturerLength + sizeof(std::uint16_t); +static const std::size_t offsetModelLength = 170; +static const std::size_t offsetModelData = offsetModelLength + sizeof(std::uint16_t); +static const std::size_t offsetActivationCodeLength = 181; +static const std::size_t offsetActivationCodeData = offsetActivationCodeLength + sizeof(std::uint16_t); + +TEST_F(CodeGenTest, shouldBeAbleToUseStdStringMethodsForEncode) +{ + std::string vehicleCode(VEHICLE_CODE, CGT(car_vehicleCode_length)()); + std::string usageDesc1(FUEL_FIGURES_1_USAGE_DESCRIPTION, FUEL_FIGURES_1_USAGE_DESCRIPTION_LENGTH); + std::string usageDesc2(FUEL_FIGURES_2_USAGE_DESCRIPTION, FUEL_FIGURES_2_USAGE_DESCRIPTION_LENGTH); + std::string usageDesc3(FUEL_FIGURES_3_USAGE_DESCRIPTION, FUEL_FIGURES_3_USAGE_DESCRIPTION_LENGTH); + std::string manufacturer(MANUFACTURER, MANUFACTURER_LENGTH); + std::string model(MODEL, MODEL_LENGTH); + std::string activationCode(ACTIVATION_CODE, ACTIVATION_CODE_LENGTH); + + char buffer[BUFFER_LEN]; + std::uint64_t baseOffset = CGT(messageHeader_encoded_length)(); + CGT(car) car; + if (!CGT(car_wrap_for_encode)(&car, buffer, baseOffset, sizeof(buffer))) + { + throw std::runtime_error(sbe_strerror(errno)); + } + + CGT(car_put_vehicleCode)(&car, vehicleCode.c_str()); + CGT(car_fuelFigures) *const fuelFig = CGT(car_fuelFigures_set_count)(&car, FUEL_FIGURES_COUNT); + if (!fuelFig) + { + throw std::runtime_error(sbe_strerror(errno)); + } + CGT(car_fuelFigures_next)(fuelFig); + const char *desc1 = usageDesc1.c_str(); + CGT(car_fuelFigures_put_usageDescription)(fuelFig, desc1, strlen(desc1)); + CGT(car_fuelFigures_next)(fuelFig); + const char *desc2 = usageDesc2.c_str(); + CGT(car_fuelFigures_put_usageDescription)(fuelFig, desc2, strlen(desc2)); + CGT(car_fuelFigures_next)(fuelFig); + const char *desc3 = usageDesc3.c_str(); + CGT(car_fuelFigures_put_usageDescription)(fuelFig, desc3, strlen(desc3)); + + + CGT(car_performanceFigures) *perfFigs = CGT(car_performanceFigures_set_count)(&car, PERFORMANCE_FIGURES_COUNT); + if (!perfFigs) + { + throw std::runtime_error(sbe_strerror(errno)); + } + CGT(car_performanceFigures_next)(perfFigs); + CGT(car_performanceFigures_acceleration) *acc = CGT(car_performanceFigures_acceleration_set_count)(perfFigs, ACCELERATION_COUNT); + CGT(car_performanceFigures_acceleration_next)(acc); + CGT(car_performanceFigures_acceleration_next)(acc); + CGT(car_performanceFigures_acceleration_next)(acc); + + CGT(car_performanceFigures_next)(perfFigs); + acc = CGT(car_performanceFigures_acceleration_set_count)(perfFigs, ACCELERATION_COUNT); + CGT(car_performanceFigures_acceleration_next)(acc); + CGT(car_performanceFigures_acceleration_next)(acc); + CGT(car_performanceFigures_acceleration_next)(acc); + + const char *manu = manufacturer.c_str(); + CGT(car_put_manufacturer)(&car, manu, strlen(manu)); + const char *model_c = model.c_str(); + CGT(car_put_model)(&car, model_c, strlen(model_c)); + const char *acti = activationCode.c_str(); + CGT(car_put_activationCode)(&car, acti, strlen(acti)); + + EXPECT_EQ(CGT(car_encoded_length)(&car), expectedCarSize); + + EXPECT_EQ(std::string(buffer + baseOffset + offsetVehicleCode, VEHICLE_CODE_LENGTH), vehicleCode); + + EXPECT_EQ(*(std::uint16_t *)(buffer + baseOffset + offsetUsageDesc1Length), FUEL_FIGURES_1_USAGE_DESCRIPTION_LENGTH); + EXPECT_EQ(std::string(buffer + baseOffset + offsetUsageDesc1Data, FUEL_FIGURES_1_USAGE_DESCRIPTION_LENGTH), usageDesc1); + + EXPECT_EQ(*(std::uint16_t *)(buffer + baseOffset + offsetUsageDesc2Length), FUEL_FIGURES_2_USAGE_DESCRIPTION_LENGTH); + EXPECT_EQ(std::string(buffer + baseOffset + offsetUsageDesc2Data, FUEL_FIGURES_2_USAGE_DESCRIPTION_LENGTH), usageDesc2); + + EXPECT_EQ(*(std::uint16_t *)(buffer + baseOffset + offsetUsageDesc3Length), FUEL_FIGURES_3_USAGE_DESCRIPTION_LENGTH); + EXPECT_EQ(std::string(buffer + baseOffset + offsetUsageDesc3Data, FUEL_FIGURES_3_USAGE_DESCRIPTION_LENGTH), usageDesc3); + + EXPECT_EQ(*(std::uint16_t *)(buffer + baseOffset + offsetManufacturerLength), MANUFACTURER_LENGTH); + EXPECT_EQ(std::string(buffer + baseOffset + offsetManufacturerData, MANUFACTURER_LENGTH), manufacturer); + + EXPECT_EQ(*(std::uint16_t *)(buffer + baseOffset + offsetModelLength), MODEL_LENGTH); + EXPECT_EQ(std::string(buffer + baseOffset + offsetModelData, MODEL_LENGTH), model); + + EXPECT_EQ(*(std::uint16_t *)(buffer + baseOffset + offsetActivationCodeLength), ACTIVATION_CODE_LENGTH); + EXPECT_EQ(std::string(buffer + baseOffset + offsetActivationCodeData, ACTIVATION_CODE_LENGTH), activationCode); +} + +void testUsageDescription(CGT(car_fuelFigures) *const fuelFigures, const std::string& expected) +{ + CGT(car_fuelFigures_next)(fuelFigures); + const std::uint16_t length = CGT(car_fuelFigures_usageDescription_length)(fuelFigures); + const char* const ptr = CGT(car_fuelFigures_usageDescription)(fuelFigures); + if (!ptr) + { + throw std::runtime_error(sbe_strerror(errno)); + } + EXPECT_EQ(std::string(ptr, length), expected); +} + +TEST_F(CodeGenTest, shouldBeAbleToUseStdStringMethodsForDecode) +{ + char buffer[2048]; + CGT(car) carEncoder; + CGT(car_reset)(&carEncoder, buffer, 0, sizeof(buffer), CGT(car_sbe_block_length)(), CGT(car_sbe_schema_version)()); + + std::uint64_t carSz = encodeCar(carEncoder); + + EXPECT_EQ(carSz, expectedCarSize); + + CGT(car) carDecoder; + CGT(car_reset)(&carDecoder, buffer, 0, carSz, CGT(car_sbe_block_length)(), CGT(car_sbe_schema_version)()); + + std::string vehicleCode(VEHICLE_CODE, CGT(car_vehicleCode_length)()); + + EXPECT_EQ(std::string(CGT(car_vehicleCode_buffer)(&carDecoder),CGT(car_vehicleCode_length)()), vehicleCode); + + CGT(car_fuelFigures) *const fuelFigures = CGT(car_get_fuelFigures)(&carDecoder); + if (!fuelFigures) + { + throw std::runtime_error(sbe_strerror(errno)); + } + testUsageDescription( + fuelFigures, std::string(FUEL_FIGURES_1_USAGE_DESCRIPTION, FUEL_FIGURES_1_USAGE_DESCRIPTION_LENGTH)); + testUsageDescription( + fuelFigures, std::string(FUEL_FIGURES_2_USAGE_DESCRIPTION, FUEL_FIGURES_2_USAGE_DESCRIPTION_LENGTH)); + testUsageDescription( + fuelFigures, std::string(FUEL_FIGURES_3_USAGE_DESCRIPTION, FUEL_FIGURES_3_USAGE_DESCRIPTION_LENGTH)); + + CGT(car_performanceFigures) *const perfFigures = CGT(car_get_performanceFigures)(&carDecoder); + if (!perfFigures) + { + throw std::runtime_error(sbe_strerror(errno)); + } + + CGT(car_performanceFigures_next)(perfFigures); + CGT(car_performanceFigures_acceleration) *acc = CGT(car_performanceFigures_get_acceleration)(perfFigures); + if (!acc) + { + throw std::runtime_error(sbe_strerror(errno)); + } + CGT(car_performanceFigures_acceleration_next)(acc); + CGT(car_performanceFigures_acceleration_next)(acc); + CGT(car_performanceFigures_acceleration_next)(acc); + CGT(car_performanceFigures_next)(perfFigures); + + acc = CGT(car_performanceFigures_get_acceleration)(perfFigures); + CGT(car_performanceFigures_acceleration_next)(acc); + CGT(car_performanceFigures_acceleration_next)(acc); + CGT(car_performanceFigures_acceleration_next)(acc); + + { + const uint16_t length = CGT(car_manufacturer_length)(&carDecoder); + const char* const ptr = CGT(car_manufacturer)(&carDecoder); + if (!ptr) + { + throw std::runtime_error(sbe_strerror(errno)); + } + + EXPECT_EQ( + std::string(ptr, length), + std::string(MANUFACTURER, MANUFACTURER_LENGTH)); + } + { + const uint16_t length = CGT(car_model_length)(&carDecoder); + const char* const ptr = CGT(car_model)(&carDecoder); + if (!ptr) + { + throw std::runtime_error(sbe_strerror(errno)); + } + + EXPECT_EQ( + std::string(ptr, length), + std::string(MODEL, MODEL_LENGTH)); + } + { + const uint16_t length = CGT(car_activationCode_length)(&carDecoder); + const char* const ptr = CGT(car_activationCode)(&carDecoder); + if (!ptr) + { + throw std::runtime_error(sbe_strerror(errno)); + } + + EXPECT_EQ( + std::string(ptr, length), + std::string(ACTIVATION_CODE, ACTIVATION_CODE_LENGTH)); + } + + EXPECT_EQ(CGT(car_encoded_length)(&carDecoder), expectedCarSize); +} diff --git a/sbe-tool/src/test/c/GroupWithDataTest.cpp b/sbe-tool/src/test/c/GroupWithDataTest.cpp new file mode 100644 index 0000000000..b72757c9e6 --- /dev/null +++ b/sbe-tool/src/test/c/GroupWithDataTest.cpp @@ -0,0 +1,670 @@ +#include +#include + +#include + +#include +#include +#include +#include + +#define GWD(name) group_with_data_##name + +static const std::uint32_t TAG_1 = 32; +static const std::uint64_t ENTRIES_COUNT = 2; + +static const char TAG_GROUP_1_IDX_0[] = { 'T', 'a', 'g', 'G', 'r', 'o', 'u', 'p', '0' }; +static const char TAG_GROUP_1_IDX_1[] = { 'T', 'a', 'g', 'G', 'r', 'o', 'u', 'p', '1' }; +static const std::uint64_t TAG_GROUP_1_IDX_0_LENGTH = sizeof(TAG_GROUP_1_IDX_0); +static const std::uint64_t TAG_GROUP_1_IDX_1_LENGTH = sizeof(TAG_GROUP_1_IDX_1); + +static const std::int64_t TAG_GROUP_2_IDX_0 = -120; +static const std::int64_t TAG_GROUP_2_IDX_1 = 120; +static const std::int64_t TAG_GROUP_2_IDX_2 = 75; + +static const std::int64_t TAG_GROUP_2_IDX_3 = 76; +static const std::int64_t TAG_GROUP_2_IDX_4 = 77; +static const std::int64_t TAG_GROUP_2_IDX_5 = 78; + +static const char *VAR_DATA_FIELD_IDX_0 = "neg idx 0"; +static const std::uint64_t VAR_DATA_FIELD_IDX_0_LENGTH = 9; +static const char *VAR_DATA_FIELD_IDX_1 = "idx 1 positive"; +static const std::uint64_t VAR_DATA_FIELD_IDX_1_LENGTH = 14; + +static const std::uint64_t NESTED_ENTRIES_COUNT = 3; + +static const char *VAR_DATA_FIELD_NESTED_IDX_0 = "zero"; +static const std::uint64_t VAR_DATA_FIELD_NESTED_IDX_0_LENGTH = 4; +static const char *VAR_DATA_FIELD_NESTED_IDX_1 = "one"; +static const std::uint64_t VAR_DATA_FIELD_NESTED_IDX_1_LENGTH = 3; +static const char *VAR_DATA_FIELD_NESTED_IDX_2 = "two"; +static const std::uint64_t VAR_DATA_FIELD_NESTED_IDX_2_LENGTH = 3; + +static const char *VAR_DATA_FIELD_NESTED_IDX_3 = "three"; +static const std::uint64_t VAR_DATA_FIELD_NESTED_IDX_3_LENGTH = 5; +static const char *VAR_DATA_FIELD_NESTED_IDX_4 = "four"; +static const std::uint64_t VAR_DATA_FIELD_NESTED_IDX_4_LENGTH = 4; +static const char *VAR_DATA_FIELD_NESTED_IDX_5 = "five"; +static const std::uint64_t VAR_DATA_FIELD_NESTED_IDX_5_LENGTH = 4; + +static const char *VAR_DATA_FIELD_1_IDX_0 = "neg idx 0"; +static const std::uint64_t VAR_DATA_FIELD_1_IDX_0_LENGTH = 9; +static const char *VAR_DATA_FIELD_1_IDX_1 = "idx 1 positive"; +static const std::uint64_t VAR_DATA_FIELD_1_IDX_1_LENGTH = 14; + +static const char *VAR_DATA_FIELD_2_IDX_0 = "negative index 0"; +static const std::uint64_t VAR_DATA_FIELD_2_IDX_0_LENGTH = 16; +static const char *VAR_DATA_FIELD_2_IDX_1 = "index 1 pos"; +static const std::uint64_t VAR_DATA_FIELD_2_IDX_1_LENGTH = 11; + +static const std::uint64_t expectedTestMessage1Size = 78; +static const std::uint64_t expectedTestMessage2Size = 107; +static const std::uint64_t expectedTestMessage3Size = 145; +static const std::uint64_t expectedTestMessage4Size = 73; + +class GroupWithDataTest : public testing::Test +{ +public: + + std::uint64_t encodeTestMessage1(char *buffer, std::uint64_t offset, std::uint64_t bufferLength) + { + if (!GWD(testMessage1_wrap_for_encode)(&m_msg1, buffer, offset, bufferLength)) + { + throw std::runtime_error(sbe_strerror(errno)); + } + + GWD(testMessage1_set_tag1)(&m_msg1, TAG_1); + + GWD(testMessage1_entries) *entries = GWD(testMessage1_entries_set_count)(&m_msg1, ENTRIES_COUNT); + if (!entries) + { + throw std::runtime_error(sbe_strerror(errno)); + } + + GWD(testMessage1_entries_next)(entries); + GWD(testMessage1_entries_put_tagGroup1)(entries, TAG_GROUP_1_IDX_0); + GWD(testMessage1_entries_set_tagGroup2)(entries, TAG_GROUP_2_IDX_0); + + GWD(testMessage1_entries_put_varDataField)(entries, VAR_DATA_FIELD_IDX_0, VAR_DATA_FIELD_IDX_0_LENGTH); + + GWD(testMessage1_entries_next)(entries); + GWD(testMessage1_entries_put_tagGroup1)(entries, TAG_GROUP_1_IDX_1); + GWD(testMessage1_entries_set_tagGroup2)(entries, TAG_GROUP_2_IDX_1); + + GWD(testMessage1_entries_put_varDataField)(entries, VAR_DATA_FIELD_IDX_1, VAR_DATA_FIELD_IDX_1_LENGTH); + + + return GWD(testMessage1_encoded_length)(&m_msg1); + } + + std::uint64_t encodeTestMessage2(char *buffer, std::uint64_t offset, std::uint64_t bufferLength) + { + if (!GWD(testMessage2_wrap_for_encode)(&m_msg2, buffer, offset, bufferLength)) + { + throw std::runtime_error(sbe_strerror(errno)); + } + + GWD(testMessage2_set_tag1)(&m_msg2, TAG_1); + + GWD(testMessage2_entries) *entries = GWD(testMessage2_entries_set_count)(&m_msg2, ENTRIES_COUNT); + if (!entries) + { + throw std::runtime_error(sbe_strerror(errno)); + } + + GWD(testMessage2_entries_next)(entries); + GWD(testMessage2_entries_put_tagGroup1)(entries, TAG_GROUP_1_IDX_0); + GWD(testMessage2_entries_set_tagGroup2)(entries, TAG_GROUP_2_IDX_0); + + GWD(testMessage2_entries_put_varDataField1)(entries, VAR_DATA_FIELD_1_IDX_0, VAR_DATA_FIELD_1_IDX_0_LENGTH); + GWD(testMessage2_entries_put_varDataField2)(entries, VAR_DATA_FIELD_2_IDX_0, VAR_DATA_FIELD_2_IDX_0_LENGTH); + + GWD(testMessage2_entries_next)(entries); + GWD(testMessage2_entries_put_tagGroup1)(entries, TAG_GROUP_1_IDX_1); + GWD(testMessage2_entries_set_tagGroup2)(entries, TAG_GROUP_2_IDX_1); + + GWD(testMessage2_entries_put_varDataField1)(entries, VAR_DATA_FIELD_1_IDX_1, VAR_DATA_FIELD_1_IDX_1_LENGTH); + GWD(testMessage2_entries_put_varDataField2)(entries, VAR_DATA_FIELD_2_IDX_1, VAR_DATA_FIELD_2_IDX_1_LENGTH); + + return GWD(testMessage2_encoded_length)(&m_msg2); + } + + std::uint64_t encodeTestMessage3(char *buffer, std::uint64_t offset, std::uint64_t bufferLength) + { + if (!GWD(testMessage3_wrap_for_encode)(&m_msg3, buffer, offset, bufferLength)) + { + throw std::runtime_error(sbe_strerror(errno)); + } + + GWD(testMessage3_set_tag1)(&m_msg3, TAG_1); + + GWD(testMessage3_entries) *entries = GWD(testMessage3_entries_set_count)(&m_msg3, ENTRIES_COUNT); + if (!entries) + { + throw std::runtime_error(sbe_strerror(errno)); + } + + GWD(testMessage3_entries_next)(entries); + GWD(testMessage3_entries_put_tagGroup1)(entries, TAG_GROUP_1_IDX_0); + + GWD(testMessage3_entries_nestedEntries) *nestedEntries0 = GWD(testMessage3_entries_nestedEntries_set_count)(entries, NESTED_ENTRIES_COUNT); + if (!nestedEntries0) + { + throw std::runtime_error(sbe_strerror(errno)); + } + GWD(testMessage3_entries_nestedEntries_next)(nestedEntries0); + GWD(testMessage3_entries_nestedEntries_set_tagGroup2)(nestedEntries0, TAG_GROUP_2_IDX_0); + + GWD(testMessage3_entries_nestedEntries_put_varDataFieldNested)(nestedEntries0, VAR_DATA_FIELD_NESTED_IDX_0, VAR_DATA_FIELD_NESTED_IDX_0_LENGTH); + + GWD(testMessage3_entries_nestedEntries_next)(nestedEntries0); + GWD(testMessage3_entries_nestedEntries_set_tagGroup2)(nestedEntries0, TAG_GROUP_2_IDX_1); + + GWD(testMessage3_entries_nestedEntries_put_varDataFieldNested)(nestedEntries0, VAR_DATA_FIELD_NESTED_IDX_1, VAR_DATA_FIELD_NESTED_IDX_1_LENGTH); + + GWD(testMessage3_entries_nestedEntries_next)(nestedEntries0); + GWD(testMessage3_entries_nestedEntries_set_tagGroup2)(nestedEntries0, TAG_GROUP_2_IDX_2); + + GWD(testMessage3_entries_nestedEntries_put_varDataFieldNested)(nestedEntries0, VAR_DATA_FIELD_NESTED_IDX_2, VAR_DATA_FIELD_NESTED_IDX_2_LENGTH); + + GWD(testMessage3_entries_put_varDataField)(entries, VAR_DATA_FIELD_IDX_0, VAR_DATA_FIELD_IDX_0_LENGTH); + + GWD(testMessage3_entries_next)(entries); + GWD(testMessage3_entries_put_tagGroup1)(entries, TAG_GROUP_1_IDX_1); + + GWD(testMessage3_entries_nestedEntries) *nestedEntries1 = GWD(testMessage3_entries_nestedEntries_set_count)(entries, NESTED_ENTRIES_COUNT); + if (!nestedEntries1) + { + throw std::runtime_error(sbe_strerror(errno)); + } + GWD(testMessage3_entries_nestedEntries_next)(nestedEntries1); + GWD(testMessage3_entries_nestedEntries_set_tagGroup2)(nestedEntries1, TAG_GROUP_2_IDX_3); + + GWD(testMessage3_entries_nestedEntries_put_varDataFieldNested)(nestedEntries1, VAR_DATA_FIELD_NESTED_IDX_3, VAR_DATA_FIELD_NESTED_IDX_3_LENGTH); + + GWD(testMessage3_entries_nestedEntries_next)(nestedEntries1); + GWD(testMessage3_entries_nestedEntries_set_tagGroup2)(nestedEntries1, TAG_GROUP_2_IDX_4); + + + GWD(testMessage3_entries_nestedEntries_put_varDataFieldNested)(nestedEntries1, VAR_DATA_FIELD_NESTED_IDX_4, VAR_DATA_FIELD_NESTED_IDX_4_LENGTH); + + GWD(testMessage3_entries_nestedEntries_next)(nestedEntries1); + GWD(testMessage3_entries_nestedEntries_set_tagGroup2)(nestedEntries1, TAG_GROUP_2_IDX_5); + + GWD(testMessage3_entries_nestedEntries_put_varDataFieldNested)(nestedEntries1, VAR_DATA_FIELD_NESTED_IDX_5, VAR_DATA_FIELD_NESTED_IDX_5_LENGTH); + + GWD(testMessage3_entries_put_varDataField)(entries, VAR_DATA_FIELD_IDX_1, VAR_DATA_FIELD_IDX_1_LENGTH); + + return GWD(testMessage3_encoded_length)(&m_msg3); + } + + std::uint64_t encodeTestMessage4(char *buffer, std::uint64_t offset, std::uint64_t bufferLength) + { + if (!GWD(testMessage4_wrap_for_encode)(&m_msg4, buffer, offset, bufferLength)) + { + throw std::runtime_error(sbe_strerror(errno)); + } + + GWD(testMessage4_set_tag1)(&m_msg4, TAG_1); + + GWD(testMessage4_entries) *entries = GWD(testMessage4_entries_set_count)(&m_msg4, ENTRIES_COUNT); + if (!entries) + { + throw std::runtime_error(sbe_strerror(errno)); + } + + GWD(testMessage4_entries_next)(entries); + + GWD(testMessage4_entries_put_varDataField1)(entries, VAR_DATA_FIELD_1_IDX_0, VAR_DATA_FIELD_1_IDX_0_LENGTH); + GWD(testMessage4_entries_put_varDataField2)(entries, VAR_DATA_FIELD_2_IDX_0, VAR_DATA_FIELD_2_IDX_0_LENGTH); + + GWD(testMessage4_entries_next)(entries); + + GWD(testMessage4_entries_put_varDataField1)(entries, VAR_DATA_FIELD_1_IDX_1, VAR_DATA_FIELD_1_IDX_1_LENGTH); + GWD(testMessage4_entries_put_varDataField2)(entries, VAR_DATA_FIELD_2_IDX_1, VAR_DATA_FIELD_2_IDX_1_LENGTH); + + return GWD(testMessage4_encoded_length)(&m_msg4); + } + + GWD(testMessage1) m_msg1; + GWD(testMessage2) m_msg2; + GWD(testMessage3) m_msg3; + GWD(testMessage4) m_msg4; +}; + +TEST_F(GroupWithDataTest, shouldBeAbleToEncodeTestMessage1Correctly) +{ + char buffer[2048]; + const char *bp = buffer; + std::uint64_t sz = encodeTestMessage1(buffer, 0, sizeof(buffer)); + + std::uint64_t offset = 0; + EXPECT_EQ(*(std::uint32_t *)(bp + offset), TAG_1); + EXPECT_EQ(GWD(testMessage1_sbe_block_length)(), 16); + offset += 16; // root blockLength of 16 + + // entries + EXPECT_EQ(*(std::uint16_t *)(bp + offset), TAG_GROUP_1_IDX_0_LENGTH + sizeof(std::int64_t)); + offset += sizeof(std::uint16_t); + EXPECT_EQ(*(std::uint8_t *)(bp + offset), ENTRIES_COUNT); + offset += sizeof(std::uint8_t); + + EXPECT_EQ(std::string(bp + offset, TAG_GROUP_1_IDX_0_LENGTH), std::string(TAG_GROUP_1_IDX_0, TAG_GROUP_1_IDX_0_LENGTH)); + offset += TAG_GROUP_1_IDX_0_LENGTH; + EXPECT_EQ(*(std::int64_t *)(bp + offset), TAG_GROUP_2_IDX_0); + offset += sizeof(std::int64_t); + EXPECT_EQ(*(std::uint8_t *)(bp + offset), VAR_DATA_FIELD_IDX_0_LENGTH); + offset += sizeof(std::uint8_t); + EXPECT_EQ(std::string(bp + offset, VAR_DATA_FIELD_IDX_0_LENGTH), VAR_DATA_FIELD_IDX_0); + offset += VAR_DATA_FIELD_IDX_0_LENGTH; + + EXPECT_EQ(std::string(bp + offset, TAG_GROUP_1_IDX_1_LENGTH), std::string(TAG_GROUP_1_IDX_1, TAG_GROUP_1_IDX_1_LENGTH)); + offset += TAG_GROUP_1_IDX_1_LENGTH; + EXPECT_EQ(*(std::int64_t *)(bp + offset), TAG_GROUP_2_IDX_1); + offset += sizeof(std::int64_t); + EXPECT_EQ(*(std::uint8_t *)(bp + offset), VAR_DATA_FIELD_IDX_1_LENGTH); + offset += sizeof(std::uint8_t); + EXPECT_EQ(std::string(bp + offset, VAR_DATA_FIELD_IDX_1_LENGTH), VAR_DATA_FIELD_IDX_1); + offset += VAR_DATA_FIELD_IDX_1_LENGTH; + + EXPECT_EQ(sz, offset); +} + +TEST_F(GroupWithDataTest, shouldbeAbleToEncodeAndDecodeTestMessage1Correctly) +{ + char buffer[2048]; + std::uint64_t sz = encodeTestMessage1(buffer, 0, sizeof(buffer)); + + EXPECT_EQ(sz, expectedTestMessage1Size); + + GWD(testMessage1) msg1Decoder; + if (!GWD(testMessage1_reset)(&msg1Decoder, buffer, 0, sizeof(buffer), GWD(testMessage1_sbe_block_length)(), GWD(testMessage1_sbe_schema_version)())) + { + throw std::runtime_error(sbe_strerror(errno)); + } + + EXPECT_EQ(GWD(testMessage1_tag1)(&msg1Decoder), TAG_1); + + GWD(testMessage1_entries) *entries = GWD(testMessage1_get_entries)(&msg1Decoder); + if (!entries) + { + throw std::runtime_error(sbe_strerror(errno)); + } + EXPECT_EQ(GWD(testMessage1_entries_count)(entries), ENTRIES_COUNT); + + ASSERT_TRUE(GWD(testMessage1_entries_has_next)(entries)); + GWD(testMessage1_entries_next)(entries); + + EXPECT_EQ(GWD(testMessage1_entries_tagGroup1_length)(), TAG_GROUP_1_IDX_0_LENGTH); + EXPECT_EQ(std::string(GWD(testMessage1_entries_tagGroup1_buffer)(entries), GWD(testMessage1_entries_tagGroup1_length)()), std::string(TAG_GROUP_1_IDX_0, TAG_GROUP_1_IDX_0_LENGTH)); + EXPECT_EQ(GWD(testMessage1_entries_tagGroup2)(entries), TAG_GROUP_2_IDX_0); + EXPECT_EQ(GWD(testMessage1_entries_varDataField_length)(entries), VAR_DATA_FIELD_IDX_0_LENGTH); + EXPECT_EQ(std::string(GWD(testMessage1_entries_varDataField)(entries), VAR_DATA_FIELD_IDX_0_LENGTH), VAR_DATA_FIELD_IDX_0); + + ASSERT_TRUE(GWD(testMessage1_entries_has_next)(entries)); + GWD(testMessage1_entries_next)(entries); + + EXPECT_EQ(GWD(testMessage1_entries_tagGroup1_length)(), TAG_GROUP_1_IDX_1_LENGTH); + EXPECT_EQ(std::string(GWD(testMessage1_entries_tagGroup1_buffer)(entries), GWD(testMessage1_entries_tagGroup1_length)()), std::string(TAG_GROUP_1_IDX_1, TAG_GROUP_1_IDX_1_LENGTH)); + EXPECT_EQ(GWD(testMessage1_entries_tagGroup2)(entries), TAG_GROUP_2_IDX_1); + EXPECT_EQ(GWD(testMessage1_entries_varDataField_length)(entries), VAR_DATA_FIELD_IDX_1_LENGTH); + EXPECT_EQ(std::string(GWD(testMessage1_entries_varDataField)(entries), VAR_DATA_FIELD_IDX_1_LENGTH), VAR_DATA_FIELD_IDX_1); + + EXPECT_EQ(GWD(testMessage1_encoded_length)(&msg1Decoder), expectedTestMessage1Size); +} + +TEST_F(GroupWithDataTest, shouldBeAbleToEncodeTestMessage2Correctly) +{ + char buffer[2048]; + const char *bp = buffer; + std::uint64_t sz = encodeTestMessage2(buffer, 0, sizeof(buffer)); + + std::uint64_t offset = 0; + EXPECT_EQ(*(std::uint32_t *)(bp + offset), TAG_1); + EXPECT_EQ(GWD(testMessage2_sbe_block_length)(), 16); + offset += 16; // root blockLength of 16 + + // entries + EXPECT_EQ(*(std::uint16_t *)(bp + offset), TAG_GROUP_1_IDX_0_LENGTH + sizeof(std::int64_t)); + offset += sizeof(std::uint16_t); + EXPECT_EQ(*(std::uint8_t *)(bp + offset), ENTRIES_COUNT); + offset += sizeof(std::uint8_t); + + EXPECT_EQ(std::string(bp + offset, TAG_GROUP_1_IDX_0_LENGTH), std::string(TAG_GROUP_1_IDX_0, TAG_GROUP_1_IDX_0_LENGTH)); + offset += TAG_GROUP_1_IDX_0_LENGTH; + EXPECT_EQ(*(std::int64_t *)(bp + offset), TAG_GROUP_2_IDX_0); + offset += sizeof(std::int64_t); + EXPECT_EQ(*(std::uint8_t *)(bp + offset), VAR_DATA_FIELD_1_IDX_0_LENGTH); + offset += sizeof(std::uint8_t); + EXPECT_EQ(std::string(bp + offset, VAR_DATA_FIELD_1_IDX_0_LENGTH), VAR_DATA_FIELD_1_IDX_0); + offset += VAR_DATA_FIELD_1_IDX_0_LENGTH; + EXPECT_EQ(*(std::uint8_t *)(bp + offset), VAR_DATA_FIELD_2_IDX_0_LENGTH); + offset += sizeof(std::uint8_t); + EXPECT_EQ(std::string(bp + offset, VAR_DATA_FIELD_2_IDX_0_LENGTH), VAR_DATA_FIELD_2_IDX_0); + offset += VAR_DATA_FIELD_2_IDX_0_LENGTH; + + EXPECT_EQ(std::string(bp + offset, TAG_GROUP_1_IDX_1_LENGTH), std::string(TAG_GROUP_1_IDX_1, TAG_GROUP_1_IDX_1_LENGTH)); + offset += TAG_GROUP_1_IDX_1_LENGTH; + EXPECT_EQ(*(std::int64_t *)(bp + offset), TAG_GROUP_2_IDX_1); + offset += sizeof(std::int64_t); + EXPECT_EQ(*(std::uint8_t *)(bp + offset), VAR_DATA_FIELD_1_IDX_1_LENGTH); + offset += sizeof(std::uint8_t); + EXPECT_EQ(std::string(bp + offset, VAR_DATA_FIELD_1_IDX_1_LENGTH), VAR_DATA_FIELD_1_IDX_1); + offset += VAR_DATA_FIELD_1_IDX_1_LENGTH; + EXPECT_EQ(*(std::uint8_t *)(bp + offset), VAR_DATA_FIELD_2_IDX_1_LENGTH); + offset += sizeof(std::uint8_t); + EXPECT_EQ(std::string(bp + offset, VAR_DATA_FIELD_2_IDX_1_LENGTH), VAR_DATA_FIELD_2_IDX_1); + offset += VAR_DATA_FIELD_2_IDX_1_LENGTH; + + EXPECT_EQ(sz, offset); +} + +TEST_F(GroupWithDataTest, shouldbeAbleToEncodeAndDecodeTestMessage2Correctly) +{ + char buffer[2048]; + std::uint64_t sz = encodeTestMessage2(buffer, 0, sizeof(buffer)); + + EXPECT_EQ(sz, expectedTestMessage2Size); + + GWD(testMessage2) msg2Decoder; + if (!GWD(testMessage2_reset)(&msg2Decoder, buffer, 0, sizeof(buffer), GWD(testMessage2_sbe_block_length)(), GWD(testMessage2_sbe_schema_version)())) + { + throw std::runtime_error(sbe_strerror(errno)); + } + + EXPECT_EQ(GWD(testMessage2_tag1)(&msg2Decoder), TAG_1); + + GWD(testMessage2_entries) *entries = GWD(testMessage2_get_entries)(&msg2Decoder); + if (!entries) + { + throw std::runtime_error(sbe_strerror(errno)); + } + EXPECT_EQ( GWD(testMessage2_entries_count)(entries), ENTRIES_COUNT); + + ASSERT_TRUE(GWD(testMessage2_entries_has_next)(entries)); + GWD(testMessage2_entries_next)(entries); + + EXPECT_EQ(GWD(testMessage2_entries_tagGroup1_length)(), TAG_GROUP_1_IDX_0_LENGTH); + EXPECT_EQ(std::string(GWD(testMessage2_entries_tagGroup1_buffer)(entries), GWD(testMessage2_entries_tagGroup1_length)()), std::string(TAG_GROUP_1_IDX_0, TAG_GROUP_1_IDX_0_LENGTH)); + EXPECT_EQ(GWD(testMessage2_entries_tagGroup2)(entries), TAG_GROUP_2_IDX_0); + EXPECT_EQ(GWD(testMessage2_entries_varDataField1_length)(entries), VAR_DATA_FIELD_1_IDX_0_LENGTH); + EXPECT_EQ(std::string(GWD(testMessage2_entries_varDataField1)(entries), VAR_DATA_FIELD_1_IDX_0_LENGTH), VAR_DATA_FIELD_1_IDX_0); + EXPECT_EQ(GWD(testMessage2_entries_varDataField2_length)(entries), VAR_DATA_FIELD_2_IDX_0_LENGTH); + EXPECT_EQ(std::string(GWD(testMessage2_entries_varDataField2)(entries), VAR_DATA_FIELD_2_IDX_0_LENGTH), VAR_DATA_FIELD_2_IDX_0); + + ASSERT_TRUE(GWD(testMessage2_entries_has_next)(entries)); + GWD(testMessage2_entries_next)(entries); + + EXPECT_EQ(GWD(testMessage2_entries_tagGroup1_length)(), TAG_GROUP_1_IDX_1_LENGTH); + EXPECT_EQ(std::string(GWD(testMessage2_entries_tagGroup1_buffer)(entries), GWD(testMessage2_entries_tagGroup1_length)()), std::string(TAG_GROUP_1_IDX_1, TAG_GROUP_1_IDX_1_LENGTH)); + + + EXPECT_EQ(GWD(testMessage2_entries_tagGroup2)(entries), TAG_GROUP_2_IDX_1); + EXPECT_EQ(GWD(testMessage2_entries_varDataField1_length)(entries), VAR_DATA_FIELD_1_IDX_1_LENGTH); + EXPECT_EQ(std::string(GWD(testMessage2_entries_varDataField1)(entries), VAR_DATA_FIELD_1_IDX_1_LENGTH), VAR_DATA_FIELD_1_IDX_1); + EXPECT_EQ(GWD(testMessage2_entries_varDataField2_length)(entries), VAR_DATA_FIELD_2_IDX_1_LENGTH); + EXPECT_EQ(std::string(GWD(testMessage2_entries_varDataField2)(entries), VAR_DATA_FIELD_2_IDX_1_LENGTH), VAR_DATA_FIELD_2_IDX_1); + + EXPECT_EQ(GWD(testMessage2_encoded_length)(&msg2Decoder), expectedTestMessage2Size); +} + +TEST_F(GroupWithDataTest, shouldBeAbleToEncodeTestMessage3Correctly) +{ + char buffer[2048]; + const char *bp = buffer; + std::uint64_t sz = encodeTestMessage3(buffer, 0, sizeof(buffer)); + + std::uint64_t offset = 0; + EXPECT_EQ(*(std::uint32_t *)(bp + offset), TAG_1); + EXPECT_EQ(GWD(testMessage1_sbe_block_length)(), 16); + offset += 16; // root blockLength of 16 + + // entries + EXPECT_EQ(*(std::uint16_t *)(bp + offset), TAG_GROUP_1_IDX_0_LENGTH); + offset += sizeof(std::uint16_t); + EXPECT_EQ(*(std::uint8_t *)(bp + offset), ENTRIES_COUNT); + offset += sizeof(std::uint8_t); + + EXPECT_EQ(std::string(bp + offset, TAG_GROUP_1_IDX_0_LENGTH), std::string(TAG_GROUP_1_IDX_0, TAG_GROUP_1_IDX_0_LENGTH)); + offset += TAG_GROUP_1_IDX_0_LENGTH; + + // nested entries + EXPECT_EQ(*(std::uint16_t *)(bp + offset), sizeof(std::int64_t)); + offset += sizeof(std::uint16_t); + EXPECT_EQ(*(std::uint8_t *)(bp + offset), NESTED_ENTRIES_COUNT); + offset += sizeof(std::uint8_t); + + EXPECT_EQ(*(std::int64_t *)(bp + offset), TAG_GROUP_2_IDX_0); + offset += sizeof(std::int64_t); + EXPECT_EQ(*(std::uint8_t *)(bp + offset), VAR_DATA_FIELD_NESTED_IDX_0_LENGTH); + offset += sizeof(std::uint8_t); + EXPECT_EQ(std::string(bp + offset, VAR_DATA_FIELD_NESTED_IDX_0_LENGTH), VAR_DATA_FIELD_NESTED_IDX_0); + offset += VAR_DATA_FIELD_NESTED_IDX_0_LENGTH; + + EXPECT_EQ(*(std::int64_t *)(bp + offset), TAG_GROUP_2_IDX_1); + offset += sizeof(std::int64_t); + EXPECT_EQ(*(std::uint8_t *)(bp + offset), VAR_DATA_FIELD_NESTED_IDX_1_LENGTH); + offset += sizeof(std::uint8_t); + EXPECT_EQ(std::string(bp + offset, VAR_DATA_FIELD_NESTED_IDX_1_LENGTH), VAR_DATA_FIELD_NESTED_IDX_1); + offset += VAR_DATA_FIELD_NESTED_IDX_1_LENGTH; + + EXPECT_EQ(*(std::int64_t *)(bp + offset), TAG_GROUP_2_IDX_2); + offset += sizeof(std::int64_t); + EXPECT_EQ(*(std::uint8_t *)(bp + offset), VAR_DATA_FIELD_NESTED_IDX_2_LENGTH); + offset += sizeof(std::uint8_t); + EXPECT_EQ(std::string(bp + offset, VAR_DATA_FIELD_NESTED_IDX_2_LENGTH), VAR_DATA_FIELD_NESTED_IDX_2); + offset += VAR_DATA_FIELD_NESTED_IDX_2_LENGTH; + + EXPECT_EQ(*(std::uint8_t *)(bp + offset), VAR_DATA_FIELD_IDX_0_LENGTH); + offset += sizeof(std::uint8_t); + EXPECT_EQ(std::string(bp + offset, VAR_DATA_FIELD_IDX_0_LENGTH), VAR_DATA_FIELD_IDX_0); + offset += VAR_DATA_FIELD_IDX_0_LENGTH; + + EXPECT_EQ(std::string(bp + offset, TAG_GROUP_1_IDX_1_LENGTH), std::string(TAG_GROUP_1_IDX_1, TAG_GROUP_1_IDX_1_LENGTH)); + offset += TAG_GROUP_1_IDX_1_LENGTH; + + // nested entries + EXPECT_EQ(*(std::uint16_t *)(bp + offset), sizeof(std::int64_t)); + offset += sizeof(std::uint16_t); + EXPECT_EQ(*(std::uint8_t *)(bp + offset), NESTED_ENTRIES_COUNT); + offset += sizeof(std::uint8_t); + + EXPECT_EQ(*(std::int64_t *)(bp + offset), TAG_GROUP_2_IDX_3); + offset += sizeof(std::int64_t); + EXPECT_EQ(*(std::uint8_t *)(bp + offset), VAR_DATA_FIELD_NESTED_IDX_3_LENGTH); + offset += sizeof(std::uint8_t); + EXPECT_EQ(std::string(bp + offset, VAR_DATA_FIELD_NESTED_IDX_3_LENGTH), VAR_DATA_FIELD_NESTED_IDX_3); + offset += VAR_DATA_FIELD_NESTED_IDX_3_LENGTH; + + EXPECT_EQ(*(std::int64_t *)(bp + offset), TAG_GROUP_2_IDX_4); + offset += sizeof(std::int64_t); + EXPECT_EQ(*(std::uint8_t *)(bp + offset), VAR_DATA_FIELD_NESTED_IDX_4_LENGTH); + offset += sizeof(std::uint8_t); + EXPECT_EQ(std::string(bp + offset, VAR_DATA_FIELD_NESTED_IDX_4_LENGTH), VAR_DATA_FIELD_NESTED_IDX_4); + offset += VAR_DATA_FIELD_NESTED_IDX_4_LENGTH; + + EXPECT_EQ(*(std::int64_t *)(bp + offset), TAG_GROUP_2_IDX_5); + offset += sizeof(std::int64_t); + EXPECT_EQ(*(std::uint8_t *)(bp + offset), VAR_DATA_FIELD_NESTED_IDX_5_LENGTH); + offset += sizeof(std::uint8_t); + EXPECT_EQ(std::string(bp + offset, VAR_DATA_FIELD_NESTED_IDX_5_LENGTH), VAR_DATA_FIELD_NESTED_IDX_5); + offset += VAR_DATA_FIELD_NESTED_IDX_5_LENGTH; + + EXPECT_EQ(*(std::uint8_t *)(bp + offset), VAR_DATA_FIELD_IDX_1_LENGTH); + offset += sizeof(std::uint8_t); + EXPECT_EQ(std::string(bp + offset, VAR_DATA_FIELD_IDX_1_LENGTH), VAR_DATA_FIELD_IDX_1); + offset += VAR_DATA_FIELD_IDX_1_LENGTH; + + EXPECT_EQ(sz, offset); +} + +TEST_F(GroupWithDataTest, shouldbeAbleToEncodeAndDecodeTestMessage3Correctly) +{ + char buffer[2048]; + std::uint64_t sz = encodeTestMessage3(buffer, 0, sizeof(buffer)); + + EXPECT_EQ(sz, expectedTestMessage3Size); + + GWD(testMessage3) msg3Decoder; + if (!GWD(testMessage3_reset)(&msg3Decoder, buffer, 0, sizeof(buffer), GWD(testMessage3_sbe_block_length)(), GWD(testMessage3_sbe_schema_version)())) + { + throw std::runtime_error(sbe_strerror(errno)); + } + + EXPECT_EQ(GWD(testMessage3_tag1)(&msg3Decoder), TAG_1); + + GWD(testMessage3_entries) *entries = GWD(testMessage3_get_entries)(&msg3Decoder); + if (!entries) + { + throw std::runtime_error(sbe_strerror(errno)); + } + EXPECT_EQ(GWD(testMessage3_entries_count)(entries), ENTRIES_COUNT); + + ASSERT_TRUE(GWD(testMessage3_entries_has_next)(entries)); + GWD(testMessage3_entries_next)(entries); + + EXPECT_EQ(GWD(testMessage3_entries_tagGroup1_length)(), TAG_GROUP_1_IDX_0_LENGTH); + EXPECT_EQ(std::string(GWD(testMessage3_entries_tagGroup1_buffer)(entries), GWD(testMessage3_entries_tagGroup1_length)()), std::string(TAG_GROUP_1_IDX_0, TAG_GROUP_1_IDX_0_LENGTH)); + + GWD(testMessage3_entries_nestedEntries) *nestedEntries0 = GWD(testMessage3_entries_get_nestedEntries)(entries); + EXPECT_EQ(GWD(testMessage3_entries_nestedEntries_count)(nestedEntries0), NESTED_ENTRIES_COUNT); + + ASSERT_TRUE(GWD(testMessage3_entries_nestedEntries_has_next)(nestedEntries0)); + GWD(testMessage3_entries_nestedEntries_next)(nestedEntries0); + + EXPECT_EQ(GWD(testMessage3_entries_nestedEntries_tagGroup2)(nestedEntries0), TAG_GROUP_2_IDX_0); + EXPECT_EQ(GWD(testMessage3_entries_nestedEntries_varDataFieldNested_length)(nestedEntries0), VAR_DATA_FIELD_NESTED_IDX_0_LENGTH); + EXPECT_EQ(std::string(GWD(testMessage3_entries_nestedEntries_varDataFieldNested)(nestedEntries0), VAR_DATA_FIELD_NESTED_IDX_0_LENGTH), VAR_DATA_FIELD_NESTED_IDX_0); + + ASSERT_TRUE(GWD(testMessage3_entries_nestedEntries_has_next)(nestedEntries0)); + GWD(testMessage3_entries_nestedEntries_next)(nestedEntries0); + + EXPECT_EQ(GWD(testMessage3_entries_nestedEntries_tagGroup2)(nestedEntries0), TAG_GROUP_2_IDX_1); + EXPECT_EQ(GWD(testMessage3_entries_nestedEntries_varDataFieldNested_length)(nestedEntries0), VAR_DATA_FIELD_NESTED_IDX_1_LENGTH); + EXPECT_EQ(std::string(GWD(testMessage3_entries_nestedEntries_varDataFieldNested)(nestedEntries0), VAR_DATA_FIELD_NESTED_IDX_1_LENGTH), VAR_DATA_FIELD_NESTED_IDX_1); + + ASSERT_TRUE(GWD(testMessage3_entries_nestedEntries_has_next)(nestedEntries0)); + GWD(testMessage3_entries_nestedEntries_next)(nestedEntries0); + + EXPECT_EQ(GWD(testMessage3_entries_nestedEntries_tagGroup2)(nestedEntries0), TAG_GROUP_2_IDX_2); + EXPECT_EQ(GWD(testMessage3_entries_nestedEntries_varDataFieldNested_length)(nestedEntries0), VAR_DATA_FIELD_NESTED_IDX_2_LENGTH); + EXPECT_EQ(std::string(GWD(testMessage3_entries_nestedEntries_varDataFieldNested)(nestedEntries0), VAR_DATA_FIELD_NESTED_IDX_2_LENGTH), VAR_DATA_FIELD_NESTED_IDX_2); + + EXPECT_EQ(GWD(testMessage3_entries_varDataField_length)(entries), VAR_DATA_FIELD_IDX_0_LENGTH); + EXPECT_EQ(std::string(GWD(testMessage3_entries_varDataField)(entries), VAR_DATA_FIELD_IDX_0_LENGTH), VAR_DATA_FIELD_IDX_0); + + ASSERT_TRUE(GWD(testMessage3_entries_has_next)(entries)); + GWD(testMessage3_entries_next)(entries); + + GWD(testMessage3_entries_nestedEntries) *nestedEntries1 = GWD(testMessage3_entries_get_nestedEntries)(entries); + if (!nestedEntries1) + { + throw std::runtime_error(sbe_strerror(errno)); + } + EXPECT_EQ(GWD(testMessage3_entries_nestedEntries_count)(nestedEntries1), NESTED_ENTRIES_COUNT); + + ASSERT_TRUE(GWD(testMessage3_entries_nestedEntries_has_next)(nestedEntries1)); + GWD(testMessage3_entries_nestedEntries_next)(nestedEntries1); + + EXPECT_EQ(GWD(testMessage3_entries_nestedEntries_tagGroup2)(nestedEntries1), TAG_GROUP_2_IDX_3); + EXPECT_EQ(GWD(testMessage3_entries_nestedEntries_varDataFieldNested_length)(nestedEntries1), VAR_DATA_FIELD_NESTED_IDX_3_LENGTH); + EXPECT_EQ(std::string(GWD(testMessage3_entries_nestedEntries_varDataFieldNested)(nestedEntries1), VAR_DATA_FIELD_NESTED_IDX_3_LENGTH), VAR_DATA_FIELD_NESTED_IDX_3); + + ASSERT_TRUE(GWD(testMessage3_entries_nestedEntries_has_next)(nestedEntries1)); + GWD(testMessage3_entries_nestedEntries_next)(nestedEntries1); + + EXPECT_EQ(GWD(testMessage3_entries_nestedEntries_tagGroup2)(nestedEntries1), TAG_GROUP_2_IDX_4); + EXPECT_EQ(GWD(testMessage3_entries_nestedEntries_varDataFieldNested_length)(nestedEntries1), VAR_DATA_FIELD_NESTED_IDX_4_LENGTH); + EXPECT_EQ(std::string(GWD(testMessage3_entries_nestedEntries_varDataFieldNested)(nestedEntries1), VAR_DATA_FIELD_NESTED_IDX_4_LENGTH), VAR_DATA_FIELD_NESTED_IDX_4); + + ASSERT_TRUE(GWD(testMessage3_entries_nestedEntries_has_next)(nestedEntries1)); + GWD(testMessage3_entries_nestedEntries_next)(nestedEntries1); + + EXPECT_EQ(GWD(testMessage3_entries_nestedEntries_tagGroup2)(nestedEntries1), TAG_GROUP_2_IDX_5); + EXPECT_EQ(GWD(testMessage3_entries_nestedEntries_varDataFieldNested_length)(nestedEntries1), VAR_DATA_FIELD_NESTED_IDX_5_LENGTH); + EXPECT_EQ(std::string(GWD(testMessage3_entries_nestedEntries_varDataFieldNested)(nestedEntries1), VAR_DATA_FIELD_NESTED_IDX_5_LENGTH), VAR_DATA_FIELD_NESTED_IDX_5); + + EXPECT_EQ(GWD(testMessage3_entries_varDataField_length)(entries), VAR_DATA_FIELD_IDX_1_LENGTH); + EXPECT_EQ(std::string(GWD(testMessage3_entries_varDataField)(entries), VAR_DATA_FIELD_IDX_1_LENGTH), VAR_DATA_FIELD_IDX_1); + + EXPECT_EQ(GWD(testMessage3_encoded_length)(&msg3Decoder), expectedTestMessage3Size); +} + +TEST_F(GroupWithDataTest, shouldBeAbleToEncodeTestMessage4Correctly) +{ + char buffer[2048]; + const char *bp = buffer; + std::uint64_t sz = encodeTestMessage4(buffer, 0, sizeof(buffer)); + + std::uint64_t offset = 0; + EXPECT_EQ(*(std::uint32_t *)(bp + offset), TAG_1); + EXPECT_EQ(GWD(testMessage4_sbe_block_length)(), 16); + offset += 16; // root blockLength of 16 + + // entries + EXPECT_EQ(*(std::uint16_t *)(bp + offset), 0); + offset += sizeof(std::uint16_t); + EXPECT_EQ(*(std::uint8_t *)(bp + offset), ENTRIES_COUNT); + offset += sizeof(std::uint8_t); + + EXPECT_EQ(*(std::uint8_t *)(bp + offset), VAR_DATA_FIELD_1_IDX_0_LENGTH); + offset += sizeof(std::uint8_t); + EXPECT_EQ(std::string(bp + offset, VAR_DATA_FIELD_1_IDX_0_LENGTH), VAR_DATA_FIELD_1_IDX_0); + offset += VAR_DATA_FIELD_1_IDX_0_LENGTH; + EXPECT_EQ(*(std::uint8_t *)(bp + offset), VAR_DATA_FIELD_2_IDX_0_LENGTH); + offset += sizeof(std::uint8_t); + EXPECT_EQ(std::string(bp + offset, VAR_DATA_FIELD_2_IDX_0_LENGTH), VAR_DATA_FIELD_2_IDX_0); + offset += VAR_DATA_FIELD_2_IDX_0_LENGTH; + + EXPECT_EQ(*(std::uint8_t *)(bp + offset), VAR_DATA_FIELD_1_IDX_1_LENGTH); + offset += sizeof(std::uint8_t); + EXPECT_EQ(std::string(bp + offset, VAR_DATA_FIELD_1_IDX_1_LENGTH), VAR_DATA_FIELD_1_IDX_1); + offset += VAR_DATA_FIELD_1_IDX_1_LENGTH; + EXPECT_EQ(*(std::uint8_t *)(bp + offset), VAR_DATA_FIELD_2_IDX_1_LENGTH); + offset += sizeof(std::uint8_t); + EXPECT_EQ(std::string(bp + offset, VAR_DATA_FIELD_2_IDX_1_LENGTH), VAR_DATA_FIELD_2_IDX_1); + offset += VAR_DATA_FIELD_2_IDX_1_LENGTH; + + EXPECT_EQ(sz, offset); +} + +TEST_F(GroupWithDataTest, shouldbeAbleToEncodeAndDecodeTestMessage4Correctly) +{ + char buffer[2048]; + std::uint64_t sz = encodeTestMessage4(buffer, 0, sizeof(buffer)); + + EXPECT_EQ(sz, expectedTestMessage4Size); + + GWD(testMessage4) msg4Decoder; + if (!GWD(testMessage4_reset)(&msg4Decoder, buffer, 0, sizeof(buffer), GWD(testMessage4_sbe_block_length)(), GWD(testMessage4_sbe_schema_version)())) + { + throw std::runtime_error(sbe_strerror(errno)); + } + + EXPECT_EQ(GWD(testMessage4_tag1)(&msg4Decoder), TAG_1); + + GWD(testMessage4_entries) *entries = GWD(testMessage4_get_entries)(&msg4Decoder); + if (!entries) + { + throw std::runtime_error(sbe_strerror(errno)); + } + EXPECT_EQ(GWD(testMessage4_entries_count)(entries), ENTRIES_COUNT); + + ASSERT_TRUE(GWD(testMessage4_entries_has_next)(entries)); + GWD(testMessage4_entries_next)(entries); + + EXPECT_EQ(GWD(testMessage4_entries_varDataField1_length)(entries), VAR_DATA_FIELD_1_IDX_0_LENGTH); + EXPECT_EQ(std::string(GWD(testMessage4_entries_varDataField1)(entries), VAR_DATA_FIELD_1_IDX_0_LENGTH), VAR_DATA_FIELD_1_IDX_0); + EXPECT_EQ(GWD(testMessage4_entries_varDataField2_length)(entries), VAR_DATA_FIELD_2_IDX_0_LENGTH); + EXPECT_EQ(std::string(GWD(testMessage4_entries_varDataField2)(entries), VAR_DATA_FIELD_2_IDX_0_LENGTH), VAR_DATA_FIELD_2_IDX_0); + + ASSERT_TRUE(GWD(testMessage4_entries_has_next)(entries)); + GWD(testMessage4_entries_next)(entries); + + EXPECT_EQ(GWD(testMessage4_entries_varDataField1_length)(entries), VAR_DATA_FIELD_1_IDX_1_LENGTH); + EXPECT_EQ(std::string(GWD(testMessage4_entries_varDataField1)(entries), VAR_DATA_FIELD_1_IDX_1_LENGTH), VAR_DATA_FIELD_1_IDX_1); + EXPECT_EQ(GWD(testMessage4_entries_varDataField2_length)(entries), VAR_DATA_FIELD_2_IDX_1_LENGTH); + EXPECT_EQ(std::string(GWD(testMessage4_entries_varDataField2)(entries), VAR_DATA_FIELD_2_IDX_1_LENGTH), VAR_DATA_FIELD_2_IDX_1); + + EXPECT_EQ(GWD(testMessage4_encoded_length)(&msg4Decoder), expectedTestMessage4Size); +} diff --git a/sbe-tool/src/test/cpp/CMakeLists.txt b/sbe-tool/src/test/cpp/CMakeLists.txt index 4f7446c9db..b530834014 100644 --- a/sbe-tool/src/test/cpp/CMakeLists.txt +++ b/sbe-tool/src/test/cpp/CMakeLists.txt @@ -18,10 +18,10 @@ function(sbe_test name) add_executable("${name}" "${name}.cpp") target_include_directories("${name}" PRIVATE ${GTEST_SOURCE_DIR}/googletest/include - PRIVATE ${CODEC_TARGET_DIR} + PRIVATE ${CXX_CODEC_TARGET_DIR} ) target_link_libraries("${name}" sbe ${GTEST_LIBS} ${CMAKE_THREAD_LIBS_INIT}) - add_test(NAME ${name} COMMAND ${name} WORKING_DIRECTORY ${CODEC_TARGET_DIR}) + add_test(NAME ${name} COMMAND ${name} WORKING_DIRECTORY ${CXX_CODEC_TARGET_DIR}) add_dependencies(${name} gtest) if(${ARGC} GREATER 1) add_dependencies(${name} ${ARGV1}) @@ -37,7 +37,7 @@ set(GROUP_WITH_DATA_SCHEMA ${CODEC_SCHEMA_DIR}/group-with-data-schema.xml) set(COMPOSITE_ELEMENTS_SCHEMA ${CODEC_SCHEMA_DIR}/composite-elements-schema.xml) set(GENERATED_CODECS - ${CODEC_TARGET_DIR} + ${CXX_CODEC_TARGET_DIR} ) add_custom_command( @@ -45,7 +45,7 @@ add_custom_command( DEPENDS ${CODE_GENERATION_SCHEMA} ${CODE_GENERATION_SCHEMA_CPP} ${COMPOSITE_OFFSETS_SCHEMA} ${MESSAGE_BLOCK_LENGTH_TEST} sbe-jar ${SBE_JAR} COMMAND ${Java_JAVA_EXECUTABLE} - -Dsbe.output.dir=${CODEC_TARGET_DIR} -Dsbe.generate.ir="true" -Dsbe.target.language="cpp" -jar ${SBE_JAR} + -Dsbe.output.dir=${CXX_CODEC_TARGET_DIR} -Dsbe.generate.ir="true" -Dsbe.target.language="cpp" -jar ${SBE_JAR} ${CODE_GENERATION_SCHEMA} ${COMPOSITE_OFFSETS_SCHEMA} ${MESSAGE_BLOCK_LENGTH_TEST}