Skip to content

Commit

Permalink
Added reader_context class (#27)
Browse files Browse the repository at this point in the history
Introduced "prorocol_traits_fwd.hpp" / "prorocol_traits.hpp" headers with
some expected values for Binary Log Protocol V4 constants.

Introduced 'binsrv::event::reader_context' class that is expected to be used for
storing information about the sequence of encountered events as well as
data that may affect parsing / analyzing subsequent events in the stream.

'binsrv::event::event' constructor now accepts an reference to an instance of
the 'reader_context' class where the information about the most recently
encountered format description event is kept.

After some bugs were fixed in 'clang-tidy-17', 'bugprone-exception-escape'
NOLINT suppressions are no longer needed and were removed.
  • Loading branch information
percona-ysorokin authored Dec 19, 2023
1 parent 19bafd4 commit a385fee
Show file tree
Hide file tree
Showing 16 changed files with 216 additions and 83 deletions.
8 changes: 8 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,14 @@ set(source_files
src/binsrv/event/generic_post_header_fwd.hpp
src/binsrv/event/generic_post_header.hpp

src/binsrv/event/protocol_traits_fwd.hpp
src/binsrv/event/protocol_traits.hpp
src/binsrv/event/protocol_traits.cpp

src/binsrv/event/reader_context_fwd.hpp
src/binsrv/event/reader_context.hpp
src/binsrv/event/reader_context.cpp

src/binsrv/event/rotate_body_impl_fwd.hpp
src/binsrv/event/rotate_body_impl.hpp
src/binsrv/event/rotate_body_impl.cpp
Expand Down
26 changes: 11 additions & 15 deletions src/app.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

#include "binsrv/event/code_type.hpp"
#include "binsrv/event/event.hpp"
#include "binsrv/event/reader_context.hpp"

#include "easymysql/binlog.hpp"
#include "easymysql/connection.hpp"
Expand Down Expand Up @@ -220,8 +221,8 @@ int main(int argc, char *argv[]) {
static constexpr std::byte expected_event_packet_prefix{'\0'};

util::const_byte_span portion;
binsrv::event::optional_format_description_post_header fde_post_header{};
binsrv::event::optional_format_description_body fde_body{};

binsrv::event::reader_context context{};

while (!(portion = binlog.fetch()).empty()) {
if (portion[0] != expected_event_packet_prefix) {
Expand All @@ -233,24 +234,19 @@ int main(int argc, char *argv[]) {
"fetched " + std::to_string(std::size(portion)) +
"-byte(s) event from binlog");

const binsrv::event::event generic_event{portion, fde_post_header,
fde_body};
const binsrv::event::event current_event{context, portion};

log_event_common_header(*logger, generic_event.get_common_header());
if (generic_event.get_common_header().get_type_code() ==
log_event_common_header(*logger, current_event.get_common_header());
if (current_event.get_common_header().get_type_code() ==
binsrv::event::code_type::format_description) {
const auto &local_fde_post_header =
std::get<binsrv::event::generic_post_header<
binsrv::event::code_type::format_description>>(
generic_event.get_post_header());
const auto &local_fde_body = std::get<binsrv::event::generic_body<
binsrv::event::code_type::format_description>>(
generic_event.get_body());
const auto &local_fde_post_header = current_event.get_post_header<
binsrv::event::code_type::format_description>();
const auto &local_fde_body =
current_event
.get_body<binsrv::event::code_type::format_description>();

log_format_description_event(*logger, local_fde_post_header,
local_fde_body);
fde_post_header = local_fde_post_header;
fde_body = local_fde_body;
}
log_span_dump(*logger, portion);
}
Expand Down
3 changes: 2 additions & 1 deletion src/binsrv/event/common_header.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@

#include "binsrv/event/code_type_fwd.hpp"
#include "binsrv/event/flag_type_fwd.hpp"
#include "binsrv/event/protocol_traits_fwd.hpp"

#include "util/byte_span_fwd.hpp"

namespace binsrv::event {

class [[nodiscard]] common_header {
public:
static constexpr std::size_t size_in_bytes{19U};
static constexpr std::size_t size_in_bytes{default_common_header_length};

explicit common_header(util::const_byte_span portion);

Expand Down
25 changes: 13 additions & 12 deletions src/binsrv/event/event.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,16 @@
#include "binsrv/event/flag_type.hpp"
#include "binsrv/event/generic_body.hpp"
#include "binsrv/event/generic_post_header.hpp"
#include "binsrv/event/protocol_traits_fwd.hpp"
#include "binsrv/event/reader_context.hpp"

#include "util/byte_span_fwd.hpp"
#include "util/conversion_helpers.hpp"
#include "util/exception_location_helpers.hpp"

namespace binsrv::event {

event::event(util::const_byte_span portion,
const optional_format_description_post_header &fde_post_header,
const optional_format_description_body &fde_body)
event::event(reader_context &context, util::const_byte_span portion)
: common_header_{
[](util::const_byte_span event_portion) -> util::const_byte_span {
if (std::size(event_portion) <
Expand All @@ -42,13 +42,13 @@ event::event(util::const_byte_span portion,
// format_description_events always include event footers with checksums
footer_size = footer::size_in_bytes;
} else {
if (fde_body) {
if (context.has_fde_processed()) {
// if format_description event has already been encountered, we determine
// whether there is a footer in the event from it
footer_size =
(fde_body->get_checksum_algorithm() == checksum_algorithm_type::crc32
? footer::size_in_bytes
: 0U);
footer_size = (context.get_current_checksum_algorithm() ==
checksum_algorithm_type::crc32
? footer::size_in_bytes
: 0U);
} else {
// we get in this branch only for the very first artificial rotate event
// and in this case it does not include the footer
Expand All @@ -63,10 +63,10 @@ event::event(util::const_byte_span portion,
"header");
}
std::size_t post_header_size{0U};
if (fde_post_header) {
if (context.has_fde_processed()) {
// if format_description event has already been encountered in the stream,
// we take post-header length from it
post_header_size = fde_post_header->get_post_header_length(type_code);
post_header_size = context.get_current_post_header_length(type_code);
} else {
// we expect that we can receive only 2 events before there is a
// format_description event we can refer to: rotate (with artificial
Expand Down Expand Up @@ -112,6 +112,7 @@ event::event(util::const_byte_span portion,
footer_size);
footer_.emplace(footer_portion);
};
context.process_event(*this);
}

void event::emplace_post_header(code_type code, util::const_byte_span portion) {
Expand All @@ -126,7 +127,7 @@ void event::emplace_post_header(code_type code, util::const_byte_span portion) {
// then, we define an alias for a container that can store
// '<number_of_events>' such member function pointers
using emplace_function_container =
std::array<emplace_function, number_of_events>;
std::array<emplace_function, default_number_of_events>;
// after that we declare a constexpr instance of such array and initialize it
// with immediately invoked lambda
static constexpr emplace_function_container emplace_functions{
Expand Down Expand Up @@ -168,7 +169,7 @@ void event::emplace_body(code_type code, util::const_byte_span portion) {
// here we use the same technique as in 'emplace_post_header()'
using emplace_function = void (event::*)(util::const_byte_span);
using emplace_function_container =
std::array<emplace_function, number_of_events>;
std::array<emplace_function, default_number_of_events>;
static constexpr emplace_function_container emplace_functions{
[]<std::size_t... IndexPack>(
std::index_sequence<IndexPack...>) -> emplace_function_container {
Expand Down
36 changes: 19 additions & 17 deletions src/binsrv/event/event.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,29 +22,22 @@
#include "binsrv/event/format_description_post_header_impl.hpp" // IWYU pragma: export
#include "binsrv/event/generic_body.hpp" // IWYU pragma: export
#include "binsrv/event/generic_post_header.hpp" // IWYU pragma: export
#include "binsrv/event/reader_context_fwd.hpp"
#include "binsrv/event/rotate_body_impl.hpp" // IWYU pragma: export
#include "binsrv/event/rotate_post_header_impl.hpp" // IWYU pragma: export
#include "binsrv/event/unknown_body.hpp" // IWYU pragma: export
#include "binsrv/event/unknown_post_header.hpp" // IWYU pragma: export

#include "util/byte_span_fwd.hpp"
#include "util/conversion_helpers.hpp"

namespace binsrv::event {

using optional_format_description_post_header =
std::optional<generic_post_header<code_type::format_description>>;
using optional_format_description_body =
std::optional<generic_body<code_type::format_description>>;

class [[nodiscard]] event {
private:
static constexpr std::size_t number_of_events{
util::enum_to_index(code_type::delimiter)};

// here we create an index sequence (std::index_sequence) specialized
// with the following std::size_t constant pack: 0 .. <number_of_events>
using code_index_sequence = std::make_index_sequence<number_of_events>;
using code_index_sequence =
std::make_index_sequence<default_number_of_events>;
// almost all boost::mp11 algorithms accept lists of types (rather than
// lists of constants), so we convert std::index_sequence into
// boost::mp11::mp_list of std::integral_constant types
Expand Down Expand Up @@ -72,8 +65,8 @@ class [[nodiscard]] event {
using post_header_variant =
boost::mp11::mp_rename<unique_post_header_type_list, std::variant>;

// identical techniqure is used to obtain the std::variant of all possible
// unique event bodyies
// identical technique is used to obtain the std::variant of all possible
// unique event bodies
template <typename Index>
using index_to_body_mf =
generic_body<util::index_to_enum<code_type>(Index::value)>;
Expand All @@ -86,17 +79,26 @@ class [[nodiscard]] event {
using optional_footer = std::optional<footer>;

public:
event(util::const_byte_span portion,
const optional_format_description_post_header &fde_post_header,
const optional_format_description_body &fde_body);
event(reader_context &context, util::const_byte_span portion);

[[nodiscard]] const common_header &get_common_header() const noexcept {
return common_header_;
}
[[nodiscard]] const post_header_variant &get_post_header() const noexcept {
[[nodiscard]] const post_header_variant &
get_generic_post_header() const noexcept {
return post_header_;
}
[[nodiscard]] const body_variant &get_body() const noexcept { return body_; }
template <code_type Code> [[nodiscard]] const auto &get_post_header() const {
return std::get<generic_post_header<Code>>(get_generic_post_header());
}

[[nodiscard]] const body_variant &get_generic_body() const noexcept {
return body_;
}
template <code_type Code> [[nodiscard]] const auto &get_body() const {
return std::get<generic_body<Code>>(get_generic_body());
}

[[nodiscard]] const optional_footer &get_footer() const noexcept {
return footer_;
}
Expand Down
21 changes: 0 additions & 21 deletions src/binsrv/event/format_description_post_header_impl.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
#include "binsrv/event/format_description_post_header_impl.hpp"

#include <cstddef>
#include <iterator>
#include <stdexcept>
#include <string>
#include <string_view>
Expand All @@ -16,7 +14,6 @@

#include "util/byte_span.hpp"
#include "util/byte_span_extractors.hpp"
#include "util/conversion_helpers.hpp"
#include "util/exception_location_helpers.hpp"

namespace binsrv::event {
Expand Down Expand Up @@ -49,11 +46,6 @@ generic_post_header_impl<code_type::format_description>::
"mismatch in "
"generic_post_header_impl<code_type::format_description>::"
"server_version_length");
static_assert(number_of_events ==
util::enum_to_index(code_type::delimiter) - 1U,
"mismatch in "
"generic_post_header_impl<code_type::format_description>::"
"number_of_events");

// TODO: initialize size_in_bytes directly based on the sum of fields
// widths instead of this static_assert
Expand Down Expand Up @@ -103,17 +95,4 @@ generic_post_header_impl<code_type::format_description>::get_server_version()
boost::posix_time::from_time_t(get_create_timestamp()));
}

[[nodiscard]] std::size_t
generic_post_header_impl<code_type::format_description>::get_post_header_length(
code_type code) const noexcept {
// here the very first "unknown" code is not included in the array by the
// spec
const auto index{util::enum_to_index(code)};
if (index == 0U || index >= util::enum_to_index(code_type::delimiter)) {
return 0U;
}
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-constant-array-index)
return static_cast<std::size_t>(post_header_lengths_[index - 1U]);
}

} // namespace binsrv::event
15 changes: 7 additions & 8 deletions src/binsrv/event/format_description_post_header_impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
#include <string>
#include <string_view>

#include "binsrv/event/protocol_traits.hpp"

#include "util/byte_span_fwd.hpp"

namespace binsrv::event {
Expand All @@ -18,15 +20,10 @@ template <>
class [[nodiscard]] generic_post_header_impl<code_type::format_description> {
public:
static constexpr std::size_t server_version_length{50U};
static constexpr std::size_t number_of_events{41U};
static constexpr std::size_t size_in_bytes{98U};

private:
using server_version_storage = std::array<std::byte, server_version_length>;
using post_header_lengths_storage =
std::array<std::uint8_t, number_of_events>;

public:
explicit generic_post_header_impl(util::const_byte_span portion);

[[nodiscard]] std::uint16_t get_binlog_version_raw() const noexcept {
Expand Down Expand Up @@ -55,19 +52,21 @@ class [[nodiscard]] generic_post_header_impl<code_type::format_description> {
return static_cast<std::size_t>(get_common_header_length_raw());
}

[[nodiscard]] const post_header_lengths_storage &
[[nodiscard]] const post_header_length_container &
get_post_header_lengths_raw() const noexcept {
return post_header_lengths_;
}
[[nodiscard]] std::size_t
get_post_header_length(code_type code) const noexcept;
get_post_header_length(code_type code) const noexcept {
return get_post_header_length_for_code(post_header_lengths_, code);
}

private:
// the members are deliberately reordered for better packing
std::uint32_t create_timestamp_{}; // 2
server_version_storage server_version_{}; // 1
std::uint16_t binlog_version_{}; // 0
post_header_lengths_storage post_header_lengths_{}; // 4
post_header_length_container post_header_lengths_{}; // 4
std::uint8_t common_header_length_{}; // 3
};

Expand Down
28 changes: 28 additions & 0 deletions src/binsrv/event/protocol_traits.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#include "binsrv/event/protocol_traits.hpp"

#include <cstddef>

#include "binsrv/event/code_type.hpp"

#include "util/conversion_helpers.hpp"

namespace binsrv::event {

[[nodiscard]] std::size_t
get_post_header_length_for_code(const post_header_length_container &storage,
code_type code) noexcept {
static_assert(default_number_of_events ==
util::enum_to_index(code_type::delimiter),
"mismatch between number_of_events and code_type enum");

// here the very first "unknown" code is not included in the array by the
// spec
const auto index{util::enum_to_index(code)};
if (index == 0U || index >= default_number_of_events) {
return 0U;
}
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-constant-array-index)
return static_cast<std::size_t>(storage[index - 1U]);
}

} // namespace binsrv::event
24 changes: 24 additions & 0 deletions src/binsrv/event/protocol_traits.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#ifndef BINSRV_EVENT_PROTOCOL_TRAITS_HPP
#define BINSRV_EVENT_PROTOCOL_TRAITS_HPP

#include "binsrv/event/protocol_traits_fwd.hpp" // IWYU pragma: export

#include <array>
#include <cstddef>
#include <cstdint>

#include "binsrv/event/code_type_fwd.hpp"

namespace binsrv::event {

// we do not store length for the first element which is the "unknown" event
using post_header_length_container =
std::array<std::uint8_t, default_number_of_events - 1U>;

[[nodiscard]] std::size_t
get_post_header_length_for_code(const post_header_length_container &storage,
code_type code) noexcept;

} // namespace binsrv::event

#endif // BINSRV_EVENT_PROTOCOL_TRAITS_HPP
Loading

0 comments on commit a385fee

Please sign in to comment.