From e3d9ca5b10f00397b3077972549bde00e9e964c2 Mon Sep 17 00:00:00 2001 From: Yura Sorokin Date: Thu, 26 Feb 2026 16:09:37 +0100 Subject: [PATCH] PS-10246 feature: Implement fixing next_event_position fields / checksums in the event common_header / footer in case of reconnecting to another node (part 2) https://perconadev.atlassian.net/browse/PS-10246 Global refactoring of how events and their components are constructed. Introduced the following lightweight classes for quick event data extraction / insertion without full parsing: - binsrv::events::common_header_view - binsrv::events::common_header_updatable_view - binsrv::events::footer_view - binsrv::events::footer_updatable_view - binsrv::events::event_view - binsrv::events::event_updatable_view It is now also possible to construct complete event objects / subobjects from their corresponding views. Added mechanism for safe event data manipulation which would perform automatic checksum re-calculation - all the modifications in the 'event_updatable_view' object are expected to be performed via an instance of the 'event_updatable_view::write_proxy' class acquired via 'event_updatable_view::get_write_proxy()' method. Fixed an issue with the checksum verification logic in the 'binsrv::events::reader_context'. We now differentiate between user-requested intention to verify received event checksums (the "replication.verify_checksum" parameter in the configuration file, corresponding to the 'binsrv::events::reader_context::is_checksum_verification_enabled()' method) and the value of the 'chacksum_algorithm' field from the body of the last encountered FORMAT_DESCRIPTION event binsrv::events::reader_context::is_footer_expected() method). Fixed 'requires' clauses in some of the 'util::insert_fixed_int_to_byte_span()' / 'util::extract_fixed_int_from_byte_span()' function overloads. --- CMakeLists.txt | 12 + src/binsrv/events/common_header.cpp | 83 +----- src/binsrv/events/common_header.hpp | 23 +- src/binsrv/events/common_header_view.cpp | 221 ++++++++++++++++ src/binsrv/events/common_header_view.hpp | 200 +++++++++++++++ src/binsrv/events/common_header_view_fwd.hpp | 30 +++ src/binsrv/events/event.cpp | 94 +------ src/binsrv/events/event.hpp | 8 +- src/binsrv/events/event_view.cpp | 142 +++++++++++ src/binsrv/events/event_view.hpp | 251 +++++++++++++++++++ src/binsrv/events/event_view_fwd.hpp | 30 +++ src/binsrv/events/footer.cpp | 30 +-- src/binsrv/events/footer.hpp | 8 +- src/binsrv/events/footer_view.cpp | 66 +++++ src/binsrv/events/footer_view.hpp | 91 +++++++ src/binsrv/events/footer_view_fwd.hpp | 30 +++ src/binsrv/events/protocol_traits_fwd.hpp | 2 + src/binsrv/events/reader_context.cpp | 9 +- src/binsrv/events/reader_context.hpp | 20 +- src/util/byte_span_extractors.hpp | 8 +- src/util/byte_span_inserters.hpp | 8 +- 21 files changed, 1161 insertions(+), 205 deletions(-) create mode 100644 src/binsrv/events/common_header_view.cpp create mode 100644 src/binsrv/events/common_header_view.hpp create mode 100644 src/binsrv/events/common_header_view_fwd.hpp create mode 100644 src/binsrv/events/event_view.cpp create mode 100644 src/binsrv/events/event_view.hpp create mode 100644 src/binsrv/events/event_view_fwd.hpp create mode 100644 src/binsrv/events/footer_view.cpp create mode 100644 src/binsrv/events/footer_view.hpp create mode 100644 src/binsrv/events/footer_view_fwd.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 69f3be2..69e6398 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -102,6 +102,10 @@ set(source_files src/binsrv/events/common_header.hpp src/binsrv/events/common_header.cpp + src/binsrv/events/common_header_view_fwd.hpp + src/binsrv/events/common_header_view.hpp + src/binsrv/events/common_header_view.cpp + src/binsrv/events/common_header_flag_type_fwd.hpp src/binsrv/events/common_header_flag_type.hpp @@ -117,10 +121,18 @@ set(source_files src/binsrv/events/event.hpp src/binsrv/events/event.cpp + src/binsrv/events/event_view_fwd.hpp + src/binsrv/events/event_view.hpp + src/binsrv/events/event_view.cpp + src/binsrv/events/footer_fwd.hpp src/binsrv/events/footer.hpp src/binsrv/events/footer.cpp + src/binsrv/events/footer_view_fwd.hpp + src/binsrv/events/footer_view.hpp + src/binsrv/events/footer_view.cpp + src/binsrv/events/format_description_body_impl_fwd.hpp src/binsrv/events/format_description_body_impl.hpp src/binsrv/events/format_description_body_impl.cpp diff --git a/src/binsrv/events/common_header.cpp b/src/binsrv/events/common_header.cpp index 537ae42..a3569f1 100644 --- a/src/binsrv/events/common_header.cpp +++ b/src/binsrv/events/common_header.cpp @@ -15,54 +15,25 @@ #include "binsrv/events/common_header.hpp" -#include #include -#include -#include -#include #include #include "binsrv/ctime_timestamp.hpp" -#include "binsrv/events/code_type.hpp" #include "binsrv/events/common_header_flag_type.hpp" +#include "binsrv/events/common_header_view.hpp" -#include "util/byte_span_extractors.hpp" #include "util/byte_span_fwd.hpp" -#include "util/conversion_helpers.hpp" -#include "util/exception_location_helpers.hpp" -#include "util/flag_set.hpp" namespace binsrv::events { -common_header::common_header(util::const_byte_span portion) { - // TODO: rework with direct member initialization - - /* - https://github.com/mysql/mysql-server/blob/mysql-8.0.43/libbinlogevents/src/binlog_event.cpp#L198 - https://github.com/mysql/mysql-server/blob/mysql-8.4.6/libs/mysql/binlog/event/binlog_event.cpp#L242 - - The first 19 bytes in the header is as follows: - +============================================+ - | member_variable offset : len | - +============================================+ - | when.tv_sec 0 : 4 | - +--------------------------------------------+ - | type_code EVENT_TYPE_OFFSET(4) : 1 | - +--------------------------------------------+ - | server_id SERVER_ID_OFFSET(5) : 4 | - +--------------------------------------------+ - | data_written EVENT_LEN_OFFSET(9) : 4 | - +--------------------------------------------+ - | log_pos LOG_POS_OFFSET(13) : 4 | - +--------------------------------------------+ - | flags FLAGS_OFFSET(17) : 2 | - +--------------------------------------------+ - | extra_headers 19 : x-19| - +============================================+ - */ - +common_header::common_header(const common_header_view &view) + : timestamp_{view.get_timestamp_raw()}, + server_id_{view.get_server_id_raw()}, + event_size_{view.get_event_size_raw()}, + next_event_position_{view.get_next_event_position_raw()}, + flags_{view.get_flags_raw()}, type_code_{view.get_type_code_raw()} { // TODO: initialize size_in_bytes directly based on the sum of fields // widths instead of this static_assert static_assert(sizeof timestamp_ + sizeof type_code_ + sizeof server_id_ + @@ -74,47 +45,17 @@ common_header::common_header(util::const_byte_span portion) { static_assert(sizeof *this == boost::alignment::align_up( size_in_bytes, alignof(decltype(*this))), "inefficient data member reordering in common_header"); - - if (std::size(portion) != size_in_bytes) { - util::exception_location().raise( - "invalid event common header length"); - } - - auto remainder = portion; - util::extract_fixed_int_from_byte_span(remainder, timestamp_); - util::extract_fixed_int_from_byte_span(remainder, type_code_); - util::extract_fixed_int_from_byte_span(remainder, server_id_); - util::extract_fixed_int_from_byte_span(remainder, event_size_); - util::extract_fixed_int_from_byte_span(remainder, next_event_position_); - util::extract_fixed_int_from_byte_span(remainder, flags_); - - if (get_type_code_raw() >= util::enum_to_index(code_type::delimiter) || - to_string_view(get_type_code()).empty()) { - util::exception_location().raise( - "invalid event code in event header"); - } - // TODO: check if flags are valid (all the bits have corresponding enum) } -[[nodiscard]] ctime_timestamp common_header::get_timestamp() const noexcept { - return ctime_timestamp{static_cast(get_timestamp_raw())}; -} - -[[nodiscard]] std::string common_header::get_readable_timestamp() const { - return get_timestamp().str(); -} +common_header::common_header(util::const_byte_span portion) + : common_header{common_header_view{portion}} {} -[[nodiscard]] std::string_view -common_header::get_readable_type_code() const noexcept { - return to_string_view(get_type_code()); +[[nodiscard]] ctime_timestamp common_header::get_timestamp() const noexcept { + return common_header_view_base::get_timestamp_from_raw(get_timestamp_raw()); } [[nodiscard]] common_header_flag_set common_header::get_flags() const noexcept { - return common_header_flag_set{get_flags_raw()}; -} - -[[nodiscard]] std::string common_header::get_readable_flags() const { - return to_string(get_flags()); + return common_header_view_base::get_flags_from_raw(get_flags_raw()); } std::ostream &operator<<(std::ostream &output, const common_header &obj) { diff --git a/src/binsrv/events/common_header.hpp b/src/binsrv/events/common_header.hpp index 65e544c..85a5128 100644 --- a/src/binsrv/events/common_header.hpp +++ b/src/binsrv/events/common_header.hpp @@ -27,6 +27,7 @@ #include "binsrv/events/code_type_fwd.hpp" #include "binsrv/events/common_header_flag_type_fwd.hpp" +#include "binsrv/events/common_header_view.hpp" #include "binsrv/events/protocol_traits_fwd.hpp" #include "util/byte_span_fwd.hpp" @@ -35,24 +36,31 @@ namespace binsrv::events { class [[nodiscard]] common_header { public: - static constexpr std::size_t size_in_bytes{default_common_header_length}; + static constexpr std::size_t size_in_bytes{ + common_header_view_base::size_in_bytes}; + explicit common_header(const common_header_view &view); explicit common_header(util::const_byte_span portion); [[nodiscard]] std::uint32_t get_timestamp_raw() const noexcept { return timestamp_; } [[nodiscard]] ctime_timestamp get_timestamp() const noexcept; - - [[nodiscard]] std::string get_readable_timestamp() const; + [[nodiscard]] std::string get_readable_timestamp() const { + return common_header_view_base::get_readable_timestamp_from_raw( + get_timestamp_raw()); + } [[nodiscard]] std::uint8_t get_type_code_raw() const noexcept { return type_code_; } [[nodiscard]] code_type get_type_code() const noexcept { - return static_cast(get_type_code_raw()); + return common_header_view_base::get_type_code_from_raw(get_type_code_raw()); + } + [[nodiscard]] std::string_view get_readable_type_code() const noexcept { + return common_header_view_base::get_readable_type_code_from_raw( + get_type_code_raw()); } - [[nodiscard]] std::string_view get_readable_type_code() const noexcept; [[nodiscard]] std::uint32_t get_server_id_raw() const noexcept { return server_id_; @@ -68,7 +76,10 @@ class [[nodiscard]] common_header { [[nodiscard]] std::uint16_t get_flags_raw() const noexcept { return flags_; } [[nodiscard]] common_header_flag_set get_flags() const noexcept; - [[nodiscard]] std::string get_readable_flags() const; + [[nodiscard]] std::string get_readable_flags() const { + return common_header_view_base::get_readable_flags_from_raw( + get_flags_raw()); + } private: // the members are deliberately reordered for better packing diff --git a/src/binsrv/events/common_header_view.cpp b/src/binsrv/events/common_header_view.cpp new file mode 100644 index 0000000..21ccbec --- /dev/null +++ b/src/binsrv/events/common_header_view.cpp @@ -0,0 +1,221 @@ +// Copyright (c) 2023-2024 Percona and/or its affiliates. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License, version 2.0, +// as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License, version 2.0, for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +#include "binsrv/events/common_header_view.hpp" + +#include +#include +#include +#include +#include +#include + +#include "binsrv/ctime_timestamp.hpp" + +#include "binsrv/events/code_type.hpp" +#include "binsrv/events/common_header_flag_type.hpp" + +#include "util/byte_span_extractors.hpp" +#include "util/byte_span_fwd.hpp" +#include "util/byte_span_inserters.hpp" +#include "util/conversion_helpers.hpp" +#include "util/exception_location_helpers.hpp" +#include "util/flag_set.hpp" + +namespace binsrv::events { + +[[nodiscard]] ctime_timestamp common_header_view_base::get_timestamp_from_raw( + std::uint32_t timestamp) noexcept { + return ctime_timestamp{static_cast(timestamp)}; +} + +[[nodiscard]] std::string +common_header_view_base::get_readable_timestamp_from_raw( + std::uint32_t timestamp) { + return get_timestamp_from_raw(timestamp).str(); +} + +[[nodiscard]] code_type common_header_view_base::get_type_code_from_raw( + std::uint8_t type_code) noexcept { + return static_cast(type_code); +} + +[[nodiscard]] std::string_view +common_header_view_base::get_readable_type_code_from_raw( + std::uint8_t type_code) noexcept { + return to_string_view(get_type_code_from_raw(type_code)); +} + +[[nodiscard]] common_header_flag_set +common_header_view_base::get_flags_from_raw(std::uint16_t flags) noexcept { + return common_header_flag_set{flags}; +} + +[[nodiscard]] std::string +common_header_view_base::get_readable_flags_from_raw(std::uint16_t flags) { + return to_string(get_flags_from_raw(flags)); +} + +common_header_view_base::common_header_view_base(util::byte_span portion) + : portion_{portion} { + /* + https://github.com/mysql/mysql-server/blob/mysql-8.0.43/libbinlogevents/src/binlog_event.cpp#L198 + https://github.com/mysql/mysql-server/blob/mysql-8.4.6/libs/mysql/binlog/event/binlog_event.cpp#L242 + + The first 19 bytes in the header is as follows: + +============================================+ + | member_variable offset : len | + +============================================+ + | when.tv_sec 0 : 4 | + +--------------------------------------------+ + | type_code EVENT_TYPE_OFFSET(4) : 1 | + +--------------------------------------------+ + | server_id SERVER_ID_OFFSET(5) : 4 | + +--------------------------------------------+ + | data_written EVENT_LEN_OFFSET(9) : 4 | + +--------------------------------------------+ + | log_pos LOG_POS_OFFSET(13) : 4 | + +--------------------------------------------+ + | flags FLAGS_OFFSET(17) : 2 | + +--------------------------------------------+ + | extra_headers 19 : x-19| + +============================================+ + */ + + if (std::size(portion) != size_in_bytes) { + util::exception_location().raise( + "invalid event common header length"); + } + + if (get_type_code_raw() >= util::enum_to_index(code_type::delimiter) || + to_string_view(get_type_code()).empty()) { + util::exception_location().raise( + "invalid event code in event header"); + } + // TODO: check if flags are valid (all the bits have corresponding enum) +} + +[[nodiscard]] std::uint32_t +common_header_view_base::get_timestamp_raw() const noexcept { + util::const_byte_span remainder{ + portion_.subspan(timestamp_offset, sizeof(std::uint32_t))}; + std::uint32_t timestamp{}; + util::extract_fixed_int_from_byte_span(remainder, timestamp); + return timestamp; +} +[[nodiscard]] ctime_timestamp +common_header_view_base::get_timestamp() const noexcept { + return get_timestamp_from_raw(get_timestamp_raw()); +} + +void common_header_view_base::set_timestamp_raw( + std::uint32_t timestamp) const noexcept { + util::byte_span remainder{ + portion_.subspan(timestamp_offset, sizeof timestamp)}; + util::insert_fixed_int_to_byte_span(remainder, timestamp); +} + +[[nodiscard]] std::uint8_t +common_header_view_base::get_type_code_raw() const noexcept { + util::const_byte_span remainder{ + portion_.subspan(type_code_offset, sizeof(std::uint8_t))}; + std::uint8_t type_code{}; + util::extract_fixed_int_from_byte_span(remainder, type_code); + return type_code; +} +void common_header_view_base::set_type_code_raw( + std::uint8_t type_code) const noexcept { + util::byte_span remainder{ + portion_.subspan(type_code_offset, sizeof type_code)}; + util::insert_fixed_int_to_byte_span(remainder, type_code); +} + +[[nodiscard]] std::uint32_t +common_header_view_base::get_server_id_raw() const noexcept { + util::const_byte_span remainder{ + portion_.subspan(server_id_offset, sizeof(std::uint32_t))}; + std::uint32_t server_id{}; + util::extract_fixed_int_from_byte_span(remainder, server_id); + return server_id; +} + +void common_header_view_base::set_server_id_raw( + std::uint32_t server_id) const noexcept { + util::byte_span remainder{ + portion_.subspan(server_id_offset, sizeof server_id)}; + util::insert_fixed_int_to_byte_span(remainder, server_id); +} + +[[nodiscard]] std::uint32_t +common_header_view_base::get_event_size_raw() const noexcept { + util::const_byte_span remainder{ + portion_.subspan(event_size_offset, sizeof(std::uint32_t))}; + std::uint32_t event_size{}; + util::extract_fixed_int_from_byte_span(remainder, event_size); + return event_size; +} +void common_header_view_base::set_event_size_raw( + std::uint32_t event_size) const noexcept { + util::byte_span remainder{ + portion_.subspan(event_size_offset, sizeof event_size)}; + util::insert_fixed_int_to_byte_span(remainder, event_size); +} + +[[nodiscard]] std::uint32_t +common_header_view_base::get_next_event_position_raw() const noexcept { + util::const_byte_span remainder{ + portion_.subspan(next_event_position_offset, sizeof(std::uint32_t))}; + std::uint32_t next_event_position{}; + util::extract_fixed_int_from_byte_span(remainder, next_event_position); + return next_event_position; +} + +void common_header_view_base::set_next_event_position_raw( + std::uint32_t next_event_position) const noexcept { + util::byte_span remainder{ + portion_.subspan(next_event_position_offset, sizeof next_event_position)}; + util::insert_fixed_int_to_byte_span(remainder, next_event_position); +} + +[[nodiscard]] std::uint16_t +common_header_view_base::get_flags_raw() const noexcept { + util::const_byte_span remainder{ + portion_.subspan(flags_offset, sizeof(std::uint16_t))}; + std::uint16_t flags{}; + util::extract_fixed_int_from_byte_span(remainder, flags); + return flags; +} +[[nodiscard]] common_header_flag_set +common_header_view_base::get_flags() const noexcept { + return get_flags_from_raw(get_flags_raw()); +} + +void common_header_view_base::set_flags_raw( + std::uint16_t flags) const noexcept { + util::byte_span remainder{portion_.subspan(flags_offset, sizeof flags)}; + util::insert_fixed_int_to_byte_span(remainder, flags); +} + +std::ostream &operator<<(std::ostream &output, const common_header_view &obj) { + return output << "ts: " << obj.get_readable_timestamp() + << ", type: " << obj.get_readable_type_code() + << ", server id: " << obj.get_server_id_raw() + << ", event size: " << obj.get_event_size_raw() + << ", next event position: " + << obj.get_next_event_position_raw() + << ", flags: " << obj.get_readable_flags(); +} + +} // namespace binsrv::events diff --git a/src/binsrv/events/common_header_view.hpp b/src/binsrv/events/common_header_view.hpp new file mode 100644 index 0000000..e2f23a3 --- /dev/null +++ b/src/binsrv/events/common_header_view.hpp @@ -0,0 +1,200 @@ +// Copyright (c) 2023-2024 Percona and/or its affiliates. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License, version 2.0, +// as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License, version 2.0, for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +#ifndef BINSRV_EVENTS_COMMON_HEADER_VIEW_HPP +#define BINSRV_EVENTS_COMMON_HEADER_VIEW_HPP + +#include "binsrv/events/common_header_view_fwd.hpp" // IWYU pragma: export + +#include +#include +#include + +#include "binsrv/ctime_timestamp_fwd.hpp" + +#include "binsrv/events/code_type_fwd.hpp" +#include "binsrv/events/common_header_flag_type_fwd.hpp" +#include "binsrv/events/protocol_traits_fwd.hpp" + +#include "util/byte_span_fwd.hpp" + +namespace binsrv::events { + +class [[nodiscard]] common_header_view_base { +public: + static constexpr std::size_t timestamp_offset{0U}; + static constexpr std::size_t type_code_offset{timestamp_offset + + sizeof(std::uint32_t)}; + static constexpr std::size_t server_id_offset{type_code_offset + + sizeof(std::uint8_t)}; + static constexpr std::size_t event_size_offset{server_id_offset + + sizeof(std::uint32_t)}; + static constexpr std::size_t next_event_position_offset{ + event_size_offset + sizeof(std::uint32_t)}; + static constexpr std::size_t flags_offset{next_event_position_offset + + sizeof(std::uint32_t)}; + + static constexpr std::size_t size_in_bytes{flags_offset + + sizeof(std::uint16_t)}; + static_assert(size_in_bytes == default_common_header_length); + + // timestamp section + [[nodiscard]] static ctime_timestamp + get_timestamp_from_raw(std::uint32_t timestamp) noexcept; + [[nodiscard]] static std::string + get_readable_timestamp_from_raw(std::uint32_t timestamp); + + // type_code section + [[nodiscard]] static code_type + get_type_code_from_raw(std::uint8_t type_code) noexcept; + [[nodiscard]] static std::string_view + get_readable_type_code_from_raw(std::uint8_t type_code) noexcept; + + // flags section + [[nodiscard]] static common_header_flag_set + get_flags_from_raw(std::uint16_t flags) noexcept; + [[nodiscard]] static std::string + get_readable_flags_from_raw(std::uint16_t flags); + +protected: + explicit common_header_view_base(util::byte_span portion); + + // timestamp section + [[nodiscard]] std::uint32_t get_timestamp_raw() const noexcept; + [[nodiscard]] ctime_timestamp get_timestamp() const noexcept; + [[nodiscard]] std::string get_readable_timestamp() const { + return get_readable_timestamp_from_raw(get_timestamp_raw()); + } + void set_timestamp_raw(std::uint32_t timestamp) const noexcept; + + // type_code section + [[nodiscard]] std::uint8_t get_type_code_raw() const noexcept; + [[nodiscard]] code_type get_type_code() const noexcept { + return get_type_code_from_raw(get_type_code_raw()); + } + [[nodiscard]] std::string_view get_readable_type_code() const noexcept { + return get_readable_type_code_from_raw(get_type_code_raw()); + } + void set_type_code_raw(std::uint8_t type_code) const noexcept; + + // server_id section + [[nodiscard]] std::uint32_t get_server_id_raw() const noexcept; + void set_server_id_raw(std::uint32_t server_id) const noexcept; + + // event_size section + [[nodiscard]] std::uint32_t get_event_size_raw() const noexcept; + void set_event_size_raw(std::uint32_t event_size) const noexcept; + + // next_event_position section + [[nodiscard]] std::uint32_t get_next_event_position_raw() const noexcept; + void + set_next_event_position_raw(std::uint32_t next_event_position) const noexcept; + + // flags section + [[nodiscard]] std::uint16_t get_flags_raw() const noexcept; + [[nodiscard]] common_header_flag_set get_flags() const noexcept; + [[nodiscard]] std::string get_readable_flags() const { + return get_readable_flags_from_raw(get_flags_raw()); + } + void set_flags_raw(std::uint16_t flags) const noexcept; + +private: + util::byte_span portion_{}; +}; + +class [[nodiscard]] common_header_updatable_view + : private common_header_view_base { + friend class common_header_view; + +public: + explicit common_header_updatable_view(util::byte_span portion) + : common_header_view_base{portion} {} + + // clang-format off + // timestamp section + using common_header_view_base::get_timestamp_raw; + using common_header_view_base::get_timestamp; + using common_header_view_base::get_readable_timestamp; + using common_header_view_base::set_timestamp_raw; + + // type_code section + using common_header_view_base::get_type_code_raw; + using common_header_view_base::get_type_code; + using common_header_view_base::get_readable_type_code; + using common_header_view_base::set_type_code_raw; + + // server_id section + using common_header_view_base::get_server_id_raw; + using common_header_view_base::set_server_id_raw; + + // event_size section + using common_header_view_base::get_event_size_raw; + using common_header_view_base::set_event_size_raw; + + // next_event_position section + using common_header_view_base::get_next_event_position_raw; + using common_header_view_base::set_next_event_position_raw; + + // flags section + using common_header_view_base::get_flags_raw; + using common_header_view_base::get_flags; + using common_header_view_base::get_readable_flags; + using common_header_view_base::set_flags_raw; + // clang-format on +}; + +class [[nodiscard]] common_header_view : private common_header_view_base { +public: + explicit common_header_view(util::const_byte_span portion) + : common_header_view_base{util::byte_span{ + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast) + const_cast(std::data(portion)), std::size(portion)}} {} + + // deliberately implicit to allow seamless convertion from + // common_header_updatable_view to common_header_view + // NOLINTNEXTLINE(hicpp-explicit-conversions) + common_header_view(const common_header_updatable_view &other) + : common_header_view_base{other} {} + + // clang-format off + // timestamp section + using common_header_view_base::get_timestamp_raw; + using common_header_view_base::get_timestamp; + using common_header_view_base::get_readable_timestamp; + + // type_code section + using common_header_view_base::get_type_code_raw; + using common_header_view_base::get_type_code; + using common_header_view_base::get_readable_type_code; + + // server_id section + using common_header_view_base::get_server_id_raw; + + // event_size section + using common_header_view_base::get_event_size_raw; + + // next_event_position section + using common_header_view_base::get_next_event_position_raw; + + // flags section + using common_header_view_base::get_flags_raw; + using common_header_view_base::get_flags; + using common_header_view_base::get_readable_flags; + // clang-format on +}; + +} // namespace binsrv::events + +#endif // BINSRV_EVENTS_COMMON_HEADER_VIEW_HPP diff --git a/src/binsrv/events/common_header_view_fwd.hpp b/src/binsrv/events/common_header_view_fwd.hpp new file mode 100644 index 0000000..1cc8eb0 --- /dev/null +++ b/src/binsrv/events/common_header_view_fwd.hpp @@ -0,0 +1,30 @@ +// Copyright (c) 2023-2024 Percona and/or its affiliates. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License, version 2.0, +// as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License, version 2.0, for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +#ifndef BINSRV_EVENTS_COMMON_HEADER_VIEW_FWD_HPP +#define BINSRV_EVENTS_COMMON_HEADER_VIEW_FWD_HPP + +#include + +namespace binsrv::events { + +class common_header_updatable_view; +class common_header_view; + +std::ostream &operator<<(std::ostream &output, const common_header_view &obj); + +} // namespace binsrv::events + +#endif // BINSRV_EVENTS_COMMON_HEADER_VIEW_FWD_HPP diff --git a/src/binsrv/events/event.cpp b/src/binsrv/events/event.cpp index ac9beb1..0b35d83 100644 --- a/src/binsrv/events/event.cpp +++ b/src/binsrv/events/event.cpp @@ -20,13 +20,11 @@ #include #include #include -#include -#include -#include #include #include #include "binsrv/events/code_type.hpp" +#include "binsrv/events/event_view.hpp" #include "binsrv/events/generic_body.hpp" #include "binsrv/events/generic_post_header.hpp" #include "binsrv/events/protocol_traits_fwd.hpp" @@ -34,97 +32,27 @@ #include "util/byte_span_fwd.hpp" #include "util/conversion_helpers.hpp" -#include "util/crc_helpers.hpp" -#include "util/exception_location_helpers.hpp" namespace binsrv::events { -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) < - decltype(common_header_)::size_in_bytes) { - util::exception_location().raise( - "not enough data for event common header"); - } - return event_portion.subspan( - 0, decltype(common_header_)::size_in_bytes); - }(portion)} { - // TODO: rework with direct member initialization - - const auto code = common_header_.get_type_code(); - - std::size_t footer_size{0U}; - if (code == code_type::format_description) { - // format_description events always include event footers with checksums - // even if their bodies contain 'checksum_algorithm' set to 'none' - footer_size = footer::size_in_bytes; - } else { - // we determine whether there is a footer in the event from the - // reader_context - footer_size = - (context.get_current_verify_checksum() ? footer::size_in_bytes : 0U); - } - - const std::size_t event_size = std::size(portion); - if (event_size != common_header_.get_event_size_raw()) { - util::exception_location().raise( - "actual event size does not match the one specified in event common " - "header"); - } - +event::event(reader_context &context, const event_view &view) + : common_header_{view.get_common_header_raw()} { const auto encoded_server_version{ context.get_current_encoded_server_version()}; + const auto code{common_header_.get_type_code()}; - std::size_t post_header_size{0U}; - post_header_size = context.get_current_post_header_length(code); - if (post_header_size == unspecified_post_header_length) { - util::exception_location().raise( - "received event of type " + std::to_string(util::enum_to_index(code)) + - " \"" + std::string{to_string_view(code)} + - "\" " - "is not known in server version " + - std::to_string(encoded_server_version)); - } + emplace_post_header(encoded_server_version, code, view.get_post_header_raw()); + emplace_body(encoded_server_version, code, view.get_body_raw()); - const std::size_t group_size = - common_header::size_in_bytes + post_header_size + footer_size; - if (event_size < group_size) { - util::exception_location().raise( - "not enough data for event post-header + body + footer"); - } - const std::size_t body_size = event_size - group_size; - - const auto post_header_portion = - portion.subspan(common_header::size_in_bytes, post_header_size); - emplace_post_header(encoded_server_version, code, post_header_portion); - - const auto body_portion = portion.subspan( - common_header::size_in_bytes + post_header_size, body_size); - emplace_body(encoded_server_version, code, body_portion); - - if (footer_size != 0U) { - const auto size_wo_footer{common_header::size_in_bytes + post_header_size + - body_size}; - const auto footer_portion = portion.subspan(size_wo_footer, footer_size); - footer_.emplace(footer_portion); - const bool need_checksum_verification{ - code == code_type::format_description - ? get_body().has_checksum_algorithm() - : context.get_current_verify_checksum()}; - if (need_checksum_verification) { - const auto event_wo_footer_portion = portion.subspan(0U, size_wo_footer); - const auto calculated_crc{util::calculate_crc32(event_wo_footer_portion)}; - const auto expected_crc{footer_->get_crc_raw()}; - if (calculated_crc != expected_crc) { - util::exception_location().raise( - "event checksum mismatch"); - } - } + if (view.has_footer()) { + footer_.emplace(view.get_footer_view()); }; context.process_event(*this); } +event::event(reader_context &context, util::const_byte_span portion) + : event{context, event_view{context, portion}} {} + void event::emplace_post_header(std::uint32_t encoded_server_version, code_type code, util::const_byte_span portion) { // our goal here is to initialize (emplace) a specific class inside diff --git a/src/binsrv/events/event.hpp b/src/binsrv/events/event.hpp index afb39ab..a94b428 100644 --- a/src/binsrv/events/event.hpp +++ b/src/binsrv/events/event.hpp @@ -35,6 +35,7 @@ #include "binsrv/events/anonymous_gtid_log_post_header_impl.hpp" // IWYU pragma: export #include "binsrv/events/code_type.hpp" #include "binsrv/events/common_header.hpp" // IWYU pragma: export +#include "binsrv/events/event_view_fwd.hpp" #include "binsrv/events/footer.hpp" // IWYU pragma: export #include "binsrv/events/format_description_body_impl.hpp" // IWYU pragma: export #include "binsrv/events/format_description_post_header_impl.hpp" // IWYU pragma: export @@ -105,6 +106,7 @@ class [[nodiscard]] event { using optional_footer = std::optional