Skip to content

Commit a21fe87

Browse files
authored
Merge pull request #761 from agmt/master
[C++] Methods to calculate the length of an SBE message
2 parents 2240074 + 0d83eef commit a21fe87

File tree

4 files changed

+340
-10
lines changed

4 files changed

+340
-10
lines changed

sbe-samples/src/main/cpp/GeneratedStubExample.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -314,8 +314,9 @@ int main(int argc, const char* argv[])
314314

315315
std::size_t encodeHdrLength = encodeHdr(hdr, car, buffer, 0, sizeof(buffer));
316316
std::size_t encodeMsgLength = encodeCar(car, buffer, hdr.encodedLength(), sizeof(buffer));
317+
std::size_t predictedLength = Car::computeLength({11, 14, 13}, {3, 3}, 5, 9, 8);
317318

318-
cout << "Encoded Lengths are " << encodeHdrLength << " + " << encodeMsgLength << endl;
319+
cout << "Encoded Lengths are " << encodeHdrLength << " + " << encodeMsgLength << " (" << predictedLength << ")" << endl;
319320

320321
std::size_t decodeHdrLength = decodeHdr(hdr, buffer, 0, sizeof(buffer));
321322
std::size_t decodeMsgLength = decodeCar(car, buffer, hdr.encodedLength(), hdr.blockLength(), hdr.version(), sizeof(buffer));

sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/cpp/CppGenerator.java

Lines changed: 314 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ public void generate() throws IOException
138138
generateGroups(sb, groups, BASE_INDENT);
139139
generateVarData(sb, className, varData, BASE_INDENT);
140140
generateDisplay(sb, msgToken.name(), fields, groups, varData, BASE_INDENT + INDENT);
141+
sb.append(generateMessageLength(fields, groups, varData, BASE_INDENT + INDENT));
141142
sb.append("};\n");
142143
sb.append(CppUtil.closingBraces(ir.namespaces().length)).append("#endif\n");
143144
out.append(sb);
@@ -178,6 +179,7 @@ private void generateGroups(final StringBuilder sb, final List<Token> tokens, fi
178179
generateVarData(sb, formatClassName(groupName), varData, indent + INDENT);
179180

180181
sb.append(generateGroupDisplay(groupName, fields, groups, varData, indent + INDENT + INDENT));
182+
sb.append(generateMessageLength(fields, groups, varData, indent + INDENT + INDENT));
181183

182184
sb.append(indent).append(" };\n");
183185
generateGroupProperty(sb, groupName, groupToken, cppTypeForNumInGroup, indent);
@@ -1034,12 +1036,6 @@ private static CharSequence generateFileHeader(
10341036
"# define SBE_DOUBLE_NAN NAN\n" +
10351037
"#endif\n\n" +
10361038

1037-
"#if __cplusplus >= 201103L\n" +
1038-
"# include <cstdint>\n" +
1039-
"# include <string>\n" +
1040-
"# include <cstring>\n" +
1041-
"#endif\n\n" +
1042-
10431039
"#if __cplusplus >= 201103L\n" +
10441040
"# define SBE_CONSTEXPR constexpr\n" +
10451041
"# define SBE_NOEXCEPT noexcept\n" +
@@ -1048,22 +1044,33 @@ private static CharSequence generateFileHeader(
10481044
"# define SBE_NOEXCEPT\n" +
10491045
"#endif\n\n" +
10501046

1047+
"#if __cplusplus >= 201402L\n" +
1048+
"# define SBE_CONSTEXPR_14 constexpr\n" +
1049+
"#else\n" +
1050+
"# define SBE_CONSTEXPR_14\n" +
1051+
"#endif\n\n" +
1052+
10511053
"#if __cplusplus >= 201703L\n" +
1054+
"# include <string_view>\n" +
10521055
"# define SBE_NODISCARD [[nodiscard]]\n" +
10531056
"#else\n" +
10541057
"# define SBE_NODISCARD\n" +
10551058
"#endif\n\n" +
10561059

10571060
"#if !defined(__STDC_LIMIT_MACROS)\n" +
10581061
"# define __STDC_LIMIT_MACROS 1\n" +
1059-
"#endif\n" +
1062+
"#endif\n\n" +
1063+
10601064
"#include <cstdint>\n" +
10611065
"#include <cstring>\n" +
1066+
"#include <iomanip>\n" +
10621067
"#include <limits>\n" +
1063-
"#include <stdexcept>\n\n" +
10641068
"#include <ostream>\n" +
1069+
"#include <stdexcept>\n" +
10651070
"#include <sstream>\n" +
1066-
"#include <iomanip>\n\n" +
1071+
"#include <string>\n" +
1072+
"#include <vector>\n" +
1073+
"\n" +
10671074

10681075
"#if defined(WIN32) || defined(_WIN32)\n" +
10691076
"# define SBE_BIG_ENDIAN_ENCODE_16(v) _byteswap_ushort(v)\n" +
@@ -1934,6 +1941,11 @@ private CharSequence generateMessageFlyweightCode(final String className, final
19341941
" return %2$s;\n" +
19351942
" }\n\n" +
19361943

1944+
" SBE_NODISCARD static SBE_CONSTEXPR %1$s sbeBlockAndHeaderLength() SBE_NOEXCEPT\n" +
1945+
" {\n" +
1946+
" return MessageHeader::encodedLength() + sbeBlockLength();\n" +
1947+
" }\n\n" +
1948+
19371949
" SBE_NODISCARD static SBE_CONSTEXPR %3$s sbeTemplateId() SBE_NOEXCEPT\n" +
19381950
" {\n" +
19391951
" return %4$s;\n" +
@@ -2018,6 +2030,14 @@ private CharSequence generateMessageFlyweightCode(final String className, final
20182030
" return sbePosition() - m_offset;\n" +
20192031
" }\n\n" +
20202032

2033+
" SBE_NODISCARD std::uint64_t decodeLength() const\n" +
2034+
" {\n" +
2035+
" %10$s skipper(m_buffer, m_offset,\n" +
2036+
" m_bufferLength, sbeBlockLength(), m_actingVersion);\n" +
2037+
" skipper.skip();\n" +
2038+
" return skipper.encodedLength();\n" +
2039+
" }\n\n" +
2040+
20212041
" SBE_NODISCARD const char * buffer() const SBE_NOEXCEPT\n" +
20222042
" {\n" +
20232043
" return m_buffer;\n" +
@@ -2756,4 +2776,289 @@ private CharSequence generateEnumDisplay(final List<Token> tokens, final Token e
27562776

27572777
return sb;
27582778
}
2779+
2780+
private Object[] generateMessageLengthArgs(
2781+
final List<Token> fields,
2782+
final List<Token> groups,
2783+
final List<Token> varData,
2784+
final String indent,
2785+
final boolean withName)
2786+
{
2787+
final StringBuilder sb = new StringBuilder();
2788+
int count = 0;
2789+
2790+
for (int i = 0, size = groups.size(); i < size; i++)
2791+
{
2792+
final Token groupToken = groups.get(i);
2793+
if (groupToken.signal() != Signal.BEGIN_GROUP)
2794+
{
2795+
throw new IllegalStateException("tokens must begin with BEGIN_GROUP: token=" + groupToken);
2796+
}
2797+
2798+
final int endSignal = findEndSignal(groups, i, Signal.END_GROUP, groupToken.name());
2799+
2800+
if (count > 0)
2801+
{
2802+
sb.append(",\n" + indent);
2803+
}
2804+
2805+
final List<Token> thisGroup = groups.subList(i, endSignal + 1);
2806+
2807+
if (isMessageConstLength(thisGroup))
2808+
{
2809+
sb.append("std::size_t");
2810+
if (withName)
2811+
{
2812+
sb.append(" " + groupToken.name() + "Length = 0");
2813+
}
2814+
}
2815+
else
2816+
{
2817+
sb.append("const std::vector<std::tuple<");
2818+
sb.append(generateMessageLengthArgs(thisGroup, indent + INDENT, false)[0]);
2819+
sb.append(">>&");
2820+
2821+
if (withName)
2822+
{
2823+
sb.append(" " + groupToken.name() + "ItemLengths = {}");
2824+
}
2825+
}
2826+
2827+
count += 1;
2828+
2829+
i = endSignal;
2830+
}
2831+
2832+
for (int i = 0, size = varData.size(); i < size;)
2833+
{
2834+
final Token varDataToken = varData.get(i);
2835+
if (varDataToken.signal() != Signal.BEGIN_VAR_DATA)
2836+
{
2837+
throw new IllegalStateException("tokens must begin with BEGIN_VAR_DATA: token=" + varDataToken);
2838+
}
2839+
2840+
if (count > 0)
2841+
{
2842+
sb.append(",\n" + indent);
2843+
}
2844+
2845+
sb.append("std::size_t");
2846+
if (withName)
2847+
{
2848+
sb.append(" " + varDataToken.name() + "Length = 0");
2849+
}
2850+
2851+
count += 1;
2852+
2853+
i += varDataToken.componentTokenCount();
2854+
}
2855+
2856+
CharSequence result = sb;
2857+
if (count > 1)
2858+
{
2859+
result = "\n" + indent + result;
2860+
}
2861+
2862+
return new Object[]{result, count};
2863+
}
2864+
2865+
private Object[] generateMessageLengthArgs(
2866+
final List<Token> tokens,
2867+
final String indent,
2868+
final boolean withName)
2869+
{
2870+
int i = 0;
2871+
2872+
final Token groupToken = tokens.get(i);
2873+
if (groupToken.signal() != Signal.BEGIN_GROUP)
2874+
{
2875+
throw new IllegalStateException("tokens must begin with BEGIN_GROUP: token=" + groupToken);
2876+
}
2877+
2878+
++i;
2879+
final int groupHeaderTokenCount = tokens.get(i).componentTokenCount();
2880+
i += groupHeaderTokenCount;
2881+
2882+
final List<Token> fields = new ArrayList<>();
2883+
i = collectFields(tokens, i, fields);
2884+
2885+
final List<Token> groups = new ArrayList<>();
2886+
i = collectGroups(tokens, i, groups);
2887+
2888+
final List<Token> varData = new ArrayList<>();
2889+
i = collectVarData(tokens, i, varData);
2890+
2891+
return generateMessageLengthArgs(fields, groups, varData, indent, withName);
2892+
}
2893+
2894+
private boolean isMessageConstLength(final List<Token> tokens)
2895+
{
2896+
final Integer count = (Integer)generateMessageLengthArgs(tokens, "", false)[1];
2897+
2898+
return (count == 0);
2899+
}
2900+
2901+
private CharSequence generateMessageLengthCallPre17Helper(final List<Token> tokens)
2902+
{
2903+
final StringBuilder sb = new StringBuilder();
2904+
2905+
final Integer count = (Integer)generateMessageLengthArgs(tokens, "", false)[1];
2906+
2907+
for (int i = 0; i < count; i++)
2908+
{
2909+
if (i > 0)
2910+
{
2911+
sb.append(", ");
2912+
}
2913+
new Formatter(sb).format("std::get<%1$d>(e)", i);
2914+
}
2915+
2916+
return sb;
2917+
}
2918+
2919+
private CharSequence generateMessageLength(
2920+
final List<Token> fields,
2921+
final List<Token> groups,
2922+
final List<Token> varData,
2923+
final String indent)
2924+
{
2925+
final StringBuilder sbEncode = new StringBuilder();
2926+
final StringBuilder sbSkip = new StringBuilder();
2927+
2928+
for (int i = 0, size = groups.size(); i < size; i++)
2929+
{
2930+
final Token groupToken = groups.get(i);
2931+
2932+
if (groupToken.signal() != Signal.BEGIN_GROUP)
2933+
{
2934+
throw new IllegalStateException("tokens must begin with BEGIN_GROUP: token=" + groupToken);
2935+
}
2936+
2937+
final int endSignal = findEndSignal(groups, i, Signal.END_GROUP, groupToken.name());
2938+
final List<Token> thisGroup = groups.subList(i, endSignal + 1);
2939+
2940+
final Token numInGroupToken = Generators.findFirst("numInGroup", groups, i);
2941+
final long minCount = numInGroupToken.encoding().applicableMinValue().longValue();
2942+
final long maxCount = numInGroupToken.encoding().applicableMaxValue().longValue();
2943+
2944+
final String countName = groupToken.name() +
2945+
(isMessageConstLength(thisGroup) ? "Length" : "ItemLengths.size()");
2946+
2947+
final String minCheck = minCount > 0 ? countName + " < " + minCount + " || " : "";
2948+
final String maxCheck = countName + " > " + maxCount;
2949+
2950+
new Formatter(sbEncode).format("\n" +
2951+
indent + " length += %1$s::sbeHeaderSize();\n",
2952+
formatClassName(groupToken.name()));
2953+
2954+
if (isMessageConstLength(thisGroup))
2955+
{
2956+
new Formatter(sbEncode).format(
2957+
indent + " if (%3$s%4$s)\n" +
2958+
indent + " {\n" +
2959+
indent + " throw std::runtime_error(\"%5$s outside of allowed range [E110]\");\n" +
2960+
indent + " }\n" +
2961+
indent + " length += %1$sLength * %2$s::sbeBlockLength();\n",
2962+
groupToken.name(),
2963+
formatClassName(groupToken.name()),
2964+
minCheck,
2965+
maxCheck,
2966+
countName);
2967+
}
2968+
else
2969+
{
2970+
new Formatter(sbEncode).format(
2971+
indent + " if (%3$s%4$s)\n" +
2972+
indent + " {\n" +
2973+
indent + " throw std::runtime_error(\"%5$s outside of allowed range [E110]\");\n" +
2974+
indent + " }\n" +
2975+
indent + " for (const auto& e: %1$sItemLengths)\n" +
2976+
indent + " {\n" +
2977+
indent + " #if __cpluplus >= 201703L\n" +
2978+
indent + " length += std::apply(%2$s::computeLength, e);\n" +
2979+
indent + " #else\n" +
2980+
indent + " length += %2$s::computeLength(%6$s);\n" +
2981+
indent + " #endif\n" +
2982+
indent + " }\n",
2983+
groupToken.name(),
2984+
formatClassName(groupToken.name()),
2985+
minCheck,
2986+
maxCheck,
2987+
countName,
2988+
generateMessageLengthCallPre17Helper(thisGroup));
2989+
}
2990+
2991+
new Formatter(sbSkip).format(
2992+
indent + " %2$s().forEach([](%1$s e)" +
2993+
indent + " {\n" +
2994+
indent + " e.skip();\n" +
2995+
indent + " });\n",
2996+
formatClassName(groupToken.name()),
2997+
formatPropertyName(groupToken.name()),
2998+
groupToken.name());
2999+
3000+
i = endSignal;
3001+
}
3002+
3003+
for (int i = 0, size = varData.size(); i < size;)
3004+
{
3005+
final Token varDataToken = varData.get(i);
3006+
3007+
if (varDataToken.signal() != Signal.BEGIN_VAR_DATA)
3008+
{
3009+
throw new IllegalStateException("tokens must begin with BEGIN_VAR_DATA: token=" + varDataToken);
3010+
}
3011+
3012+
final String propertyName = toUpperFirstChar(varDataToken.name());
3013+
final Token lengthToken = Generators.findFirst("length", varData, i);
3014+
3015+
new Formatter(sbEncode).format("\n" +
3016+
indent + " length += %1$sHeaderLength();\n" +
3017+
indent + " if (%1$sLength > %2$d)\n" +
3018+
indent + " {\n" +
3019+
indent + " throw std::runtime_error(\"%1$sLength too long for length type [E109]\");\n" +
3020+
indent + " }\n" +
3021+
indent + " length += %1$sLength;\n",
3022+
varDataToken.name(),
3023+
lengthToken.encoding().applicableMaxValue().longValue());
3024+
3025+
new Formatter(sbSkip).format(
3026+
indent + " skip%1$s();\n",
3027+
propertyName);
3028+
3029+
i += varDataToken.componentTokenCount();
3030+
}
3031+
3032+
final StringBuilder sb = new StringBuilder();
3033+
3034+
new Formatter(sb).format("\n" +
3035+
indent + "void skip()\n" +
3036+
indent + "{\n" +
3037+
"%3$s" +
3038+
indent + "}\n\n" +
3039+
3040+
indent + "SBE_NODISCARD static SBE_CONSTEXPR bool isConstLength() SBE_NOEXCEPT\n" +
3041+
indent + "{\n" +
3042+
indent + " return " + ((groups.isEmpty() && varData.isEmpty()) ? "true" : "false") + ";\n" +
3043+
indent + "}\n\n" +
3044+
3045+
indent + "SBE_NODISCARD static SBE_CONSTEXPR_14 size_t computeLength(%1$s)\n" +
3046+
indent + "{\n" +
3047+
"#if defined(__GNUG__) && !defined(__clang__)\n" +
3048+
"#pragma GCC diagnostic push\n" +
3049+
"#pragma GCC diagnostic ignored \"-Wtype-limits\"\n" +
3050+
"#endif\n" +
3051+
indent + " size_t length = sbeBlockLength();\n\n" +
3052+
"%2$s" +
3053+
indent + " return length;\n" +
3054+
"#if defined(__GNUG__) && !defined(__clang__)\n" +
3055+
"#pragma GCC diagnostic pop\n" +
3056+
"#endif\n" +
3057+
indent + "}\n",
3058+
generateMessageLengthArgs(fields, groups, varData, indent + INDENT, true)[0],
3059+
sbEncode.toString(),
3060+
sbSkip.toString());
3061+
3062+
return sb;
3063+
}
27593064
}

0 commit comments

Comments
 (0)