Skip to content

Commit 9f49a5e

Browse files
committed
added calcBufferSize
1 parent 7a20f32 commit 9f49a5e

7 files changed

Lines changed: 187 additions & 2 deletions

File tree

CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ cmake_minimum_required(VERSION 3.14)
22

33
project(sbe-code-gen)
44

5+
include(cmake/Utils.cmake)
6+
57
# TODO: setup env should be done once?
68

79
function(sbe_make_codec TARGET)
@@ -54,6 +56,7 @@ function(sbe_make_codec TARGET)
5456
target_compile_features(${TARGET} INTERFACE cxx_std_23)
5557
target_sources(${TARGET} INTERFACE ${destDir}/schema.h)
5658
target_include_directories(${TARGET} INTERFACE "${PARSED_OUTPUT}")
59+
target_precompile_headers(${TARGET} INTERFACE ${destDir}/schema.h)
5760
endfunction()
5861

5962
enable_testing()

app/generation/cpp-min/generator.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ def add_filters(self) -> None:
4141
self.env.filters['fmt_class_group'] = lambda s: s[0].upper() + s[1:] + 'Group'
4242
self.env.filters['fmt_class_data'] = lambda s: s[0].upper() + s[1:] + 'Data'
4343
self.env.filters['fmt_enum_value'] = lambda s: s
44-
self.env.filters['fmt_header_name'] = lambda s: s + '.h'
4544
self.env.filters['to_cpp_type'] = Generator.to_cpp_type
4645
self.env.filters['to_cpp_value'] = Generator.to_cpp_value
4746

app/generation/cpp-min/templates/generate.tmpl

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,64 @@ if constexpr (std::endian::native == std::endian::little) {
107107

108108
{# ------------------------------------ #}
109109

110+
{% macro decl_calc_args_class(group) %}
111+
{% set va_args_count = group.fields | selectattr('token', 'in', ['group', 'data']) | list | length %}
112+
{% if va_args_count > 0 -%}
113+
std::tuple<
114+
{% for field in group.fields | selectattr('token', 'in', ['group', 'data']) %}
115+
{% if field.token == 'group' %}
116+
{% if field.fields | selectattr('token', 'in', ['group', 'data' ]) | list | length > 0 %}
117+
std::vector<
118+
{{ decl_calc_args_class(field) | indent(8) }}
119+
> /* {{ field.name }} */{% if not loop.last %},{% endif +%}
120+
{% else %}
121+
std::size_t /* {{ field.name }} */{% if not loop.last %},{% endif +%}
122+
{% endif %}
123+
{% elif field.token == 'data' %}
124+
std::size_t /* {{ field.name }} */{% if not loop.last %},{% endif +%}
125+
{% endif %}
126+
{% endfor %}
127+
>
128+
{%- endif %}
129+
{%- endmacro %}
130+
131+
{% macro decl_calc_args(group, argName) %}
132+
{% set va_args_count = group.fields | selectattr('token', 'in', ['group', 'data']) | list | length %}
133+
{% if va_args_count > 0 -%}
134+
std::apply([](
135+
{%- for field in group.fields | selectattr('token', 'in', ['group', 'data']) -%}
136+
auto const& field_{{ field.name -}}{%- if not loop.last %}, {% endif -%}
137+
{%- endfor -%}
138+
) -> std::size_t {
139+
std::size_t result{0};
140+
{% for field in group.fields | selectattr('token', 'in', ['group', 'data']) %}
141+
// {{ field.name }} ({{ field.token }})
142+
{% if field.token == 'group' %}
143+
{% if field.fields | selectattr('token', 'in', ['group', 'data' ]) | list | length > 0 %}
144+
result += {{ field.dimension_type.encoded_length }};
145+
for (auto const& entry : field_{{ field.name }}) {
146+
result += {{ field.block_length }} + {{ decl_calc_args(field, 'entry') | indent(8) }};
147+
}
148+
{% else %}
149+
result += field_{{ field.name}} * {{ field.block_length }} + {{ field.dimension_type.encoded_length }};
150+
{% endif %}
151+
{% elif field.token == 'data' %}
152+
{% set length = (field.type.contained_types | selectattr('reference_name', 'equalto', 'length') | first) %}
153+
{% set varData = (field.type.contained_types | selectattr('reference_name', 'equalto', 'varData') | first) %}
154+
{% if varData.primitive_type.size > 1 %}
155+
result += field_{{ field.name }} * {{ varData.primitive_type.size }} + {{ length.encoded_length }};
156+
{% else %}
157+
result += field_{{ field.name }} + {{ length.encoded_length }};
158+
{% endif %}
159+
{% endif %}
160+
{% endfor %}
161+
return result;
162+
}, {{ argName }})
163+
{%- endif %}
164+
{%- endmacro %}
165+
166+
{# ------------------------------------ #}
167+
110168
{% macro decl_enum(type) %}
111169
{% set class_cpp_t = type.name | fmt_class_type %}
112170
{% set enc_cpp_t = type.encoding_type | to_cpp_type %}
@@ -271,6 +329,8 @@ public:
271329

272330
{% macro decl_message(message) %}
273331
{% set class_cpp_t = message.name | fmt_class_message %}
332+
{# count of variable-length fields (groups and data)#}
333+
{% set va_args_count = message.fields | selectattr('token', 'in', ['group', 'data']) | list | length %}
274334
class {{ class_cpp_t }} {
275335
private:
276336
std::span<std::byte> buffer_;
@@ -447,6 +507,24 @@ public:
447507
static_assert(static_cast<std::string_view>(N).size() + 1 < 0, "Field not found ({{ class_cpp_t }})");
448508
}
449509
{% endif %}
510+
511+
{% if va_args_count > 0 %}
512+
513+
using CalcArgsHelper = {{ decl_calc_args_class(message) | indent(4) }};
514+
515+
[[nodiscard]] static constexpr auto calcBufferSize(CalcArgsHelper const& info) noexcept -> std::size_t {
516+
std::size_t bufferSize{ {{- message.block_length -}} };
517+
518+
bufferSize += {{ decl_calc_args(message, 'info') | indent(8) }};
519+
520+
return bufferSize;
521+
}
522+
{% else %}
523+
524+
[[nodiscard]] static constexpr auto calcBufferSize() noexcept -> std::size_t {
525+
return {{ message.block_length }};
526+
}
527+
{% endif %}
450528
};
451529
{%- endmacro %}
452530

app/generation/cpp-min/templates/schema.tmpl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include <limits>
1717
#include <string_view>
1818
#include <tuple>
19+
#include <vector>
1920

2021
namespace {{ generate.namespace }} {
2122

cmake/Utils.cmake

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
include(CMakeParseArguments)
2+
3+
function(SbeCodeGenRemoveMatchesFromList)
4+
set(options)
5+
set(oneValueArgs)
6+
set(multiValueArgs MATCHES)
7+
cmake_parse_arguments(_scg_PARSED "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
8+
9+
foreach (_scg_LIST ${_scg_PARSED_UNPARSED_ARGUMENTS})
10+
foreach (_scg_ENTRY ${${_scg_LIST}})
11+
foreach (_scg_MATCH ${_scg_PARSED_MATCHES})
12+
if (${_scg_ENTRY} MATCHES ${_scg_MATCH})
13+
list(REMOVE_ITEM ${_scg_LIST} ${_scg_ENTRY})
14+
endif()
15+
endforeach()
16+
endforeach()
17+
set(${_scg_LIST} ${${_scg_LIST}} PARENT_SCOPE)
18+
endforeach()
19+
endfunction()
20+
21+
function(SbeCodeGenAddTestsFromSourceList)
22+
set(options)
23+
set(oneValueArgs PREFIX)
24+
set(multiValueArgs LIBS OPTIONS DEFINITIONS)
25+
26+
cmake_parse_arguments(_scg_PARSED "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
27+
28+
foreach (_scg_LIST ${_scg_PARSED_UNPARSED_ARGUMENTS})
29+
foreach (_scg_ENTRY ${${_scg_LIST}})
30+
if (${_scg_ENTRY} MATCHES ".*_test.cpp")
31+
string(REGEX REPLACE "^.*\/(.*)_test\.cpp$" "\\1" testName "${_scg_ENTRY}")
32+
set(testName "${_scg_PARSED_PREFIX}-${testName}-test")
33+
34+
add_executable(${testName} ${_scg_ENTRY})
35+
target_compile_options(${testName} PRIVATE ${_scg_PARSED_OPTIONS})
36+
target_compile_definitions(${testName} PRIVATE ${_scg_PARSED_DEFINITIONS})
37+
target_link_libraries(${testName} PRIVATE ${_scg_PARSED_LIBS})
38+
39+
add_test(${testName} ${testName})
40+
endif()
41+
endforeach()
42+
endforeach()
43+
endfunction()
44+
45+
function(SbeCodeGenAddBenchmarksFromSourceList)
46+
set(options)
47+
set(oneValueArgs PREFIX)
48+
set(multiValueArgs LIBS COMPILE_OPTIONS LINK_OPTIONS DEFINITIONS)
49+
50+
cmake_parse_arguments(_scg_PARSED "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
51+
52+
foreach (_scg_LIST ${_scg_PARSED_UNPARSED_ARGUMENTS})
53+
foreach (_scg_ENTRY ${${_scg_LIST}})
54+
if (${_scg_ENTRY} MATCHES ".*_bm.cpp")
55+
string(REGEX REPLACE "^.*\/(.*)_bm\.cpp$" "\\1" benchmarkName "${_scg_ENTRY}")
56+
set(benchmarkName "${_scg_PARSED_PREFIX}-${benchmarkName}-bm")
57+
58+
add_executable(${benchmarkName} ${_scg_ENTRY})
59+
target_compile_options(${benchmarkName} PRIVATE ${_scg_PARSED_COMPILE_OPTIONS})
60+
target_compile_definitions(${benchmarkName} PRIVATE ${_scg_PARSED_DEFINITIONS})
61+
target_link_options(${benchmarkName} PRIVATE ${_scg_PARSED_LINK_OPTIONS})
62+
target_link_libraries(${benchmarkName} PRIVATE ${_scg_PARSED_LIBS})
63+
endif()
64+
endforeach()
65+
endforeach()
66+
endfunction()
67+
68+
function(SbeCodeGenExcludeTestsAndBenchmarksFromSourceList _scg_LIST)
69+
list(FILTER ${_scg_LIST} EXCLUDE REGEX ".*_test.cpp")
70+
list(FILTER ${_scg_LIST} EXCLUDE REGEX ".*_bm.cpp")
71+
set(${_scg_LIST} ${${_scg_LIST}} PARENT_SCOPE)
72+
endfunction()

tests/CMakeLists.txt

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
include(FetchContent)
22

3-
if (NOT TARGET doctest_with_main)
3+
if (NOT TARGET doctest::doctest_with_main)
44
FetchContent_Declare(doctest
55
URL https://github.com/doctest/doctest/archive/refs/tags/v2.4.12.tar.gz
66
DOWNLOAD_EXTRACT_TIMESTAMP ON
@@ -25,3 +25,17 @@ if (NOT TARGET nlohmann_json::nlohmann_json)
2525
endif()
2626

2727
add_subdirectory(binance)
28+
29+
file(GLOB Sources "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp")
30+
31+
sbe_make_codec(spot_3_1
32+
SCHEMA ${CMAKE_CURRENT_SOURCE_DIR}/../resources/spot_3_1.xml
33+
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/spot_3_1
34+
GENERATOR cpp-min
35+
)
36+
37+
SbeCodeGenAddTestsFromSourceList(Sources
38+
PREFIX sbe-code-gen
39+
COMPILE_OPTIONS -Wall -Wextra -g
40+
LIBS doctest::doctest_with_main spot_3_1
41+
)

tests/calc_size_test.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright (c) Sergey Kovalevich <inndie@gmail.com>
2+
3+
#include <doctest/doctest.h>
4+
5+
#include "schema.h"
6+
7+
TEST_CASE("calc_size: simple") {
8+
REQUIRE_EQ(spot_sbe::NonRepresentableMessage::calcBufferSize(), 0);
9+
REQUIRE_EQ(spot_sbe::PriceFilter::calcBufferSize(), 1 + 8 + 8 + 8);
10+
REQUIRE_EQ(spot_sbe::IcebergPartsFilter::calcBufferSize(), 8);
11+
REQUIRE_EQ(spot_sbe::PingResponse::calcBufferSize(), 0);
12+
}
13+
14+
TEST_CASE("calc_size: repeating groups") {
15+
REQUIRE_EQ(spot_sbe::WebSocketSessionSubscriptionsResponse::calcBufferSize({4}), (6 + 4 * (2 + 8)));
16+
REQUIRE_EQ(spot_sbe::TradesResponse::calcBufferSize({1}), 1 + 1 + (6 + 1 * (8 + 8 + 8 + 8 + 8 + 1 + 1)));
17+
REQUIRE_EQ(spot_sbe::TradesResponse::calcBufferSize({3}), 1 + 1 + (6 + 3 * (8 + 8 + 8 + 8 + 8 + 1 + 1)));
18+
}

0 commit comments

Comments
 (0)