From d7833401a0ade8ea17997b7aebc9c62850b95ff3 Mon Sep 17 00:00:00 2001 From: Ruben Perez Date: Fri, 29 Jul 2022 17:37:24 +0200 Subject: [PATCH] read_one_row algorithm --- .../network_algorithms/impl/read_one_row.hpp | 180 ++++++++++++++++ .../network_algorithms/impl/read_row.hpp | 199 ------------------ .../{read_row.hpp => read_one_row.hpp} | 30 +-- include/boost/mysql/resultset.hpp | 7 + include/boost/mysql/row.hpp | 37 +++- 5 files changed, 228 insertions(+), 225 deletions(-) create mode 100644 include/boost/mysql/detail/network_algorithms/impl/read_one_row.hpp delete mode 100644 include/boost/mysql/detail/network_algorithms/impl/read_row.hpp rename include/boost/mysql/detail/network_algorithms/{read_row.hpp => read_one_row.hpp} (64%) diff --git a/include/boost/mysql/detail/network_algorithms/impl/read_one_row.hpp b/include/boost/mysql/detail/network_algorithms/impl/read_one_row.hpp new file mode 100644 index 00000000..5a9ee12f --- /dev/null +++ b/include/boost/mysql/detail/network_algorithms/impl/read_one_row.hpp @@ -0,0 +1,180 @@ +// +// Copyright (c) 2019-2022 Ruben Perez Hidalgo (rubenperez038 at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_IMPL_READ_ROW_HPP +#define BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_IMPL_READ_ROW_HPP + +#pragma once + +#include +#include +#include +#include +#include + +namespace boost { +namespace mysql { +namespace detail { + +inline bool process_read_message( + boost::asio::const_buffer read_message, + capabilities current_capabilities, + resultset& resultset, + row& output, + error_code& err, + error_info& info +) +{ + assert(resultset.valid()); + + // Message type: row, error or eof? + std::uint8_t msg_type = 0; + deserialization_context ctx (read_message, current_capabilities); + err = make_error_code(deserialize(ctx, msg_type)); + if (err) + return false; + if (msg_type == eof_packet_header) + { + // end of resultset => this is a ok_packet, not a row + ok_packet ok_pack; + err = deserialize_message(ctx, ok_pack); + if (err) + return false; + resultset.complete(ok_pack); + output.clear(); + return false; + } + else if (msg_type == error_packet_header) + { + // An error occurred during the generation of the rows + err = process_error_packet(ctx, info); + return false; + } + else + { + // An actual row + output.clear(); + ctx.rewind(1); // keep the 'message type' byte, as it is part of the actual message + err = resultset.deserialize_row(ctx, output.values()); + if (err) + return false; + output.copy_strings(); + return true; + } +} + +template +struct read_one_row_op : boost::asio::coroutine +{ + channel& chan_; + error_info& output_info_; + resultset& resultset_; + row& output_; + + read_one_row_op( + channel& chan, + error_info& output_info, + resultset& resultset, + row& output + ) noexcept : + chan_(chan), + output_info_(output_info), + resultset_(resultset), + output_(output) + { + } + + template + void operator()( + Self& self, + error_code err = {}, + boost::asio::const_buffer read_message = {} + ) + { + // Error checking + if (err) + { + self.complete(err, false); + return; + } + + // Normal path + bool result; + BOOST_ASIO_CORO_REENTER(*this) + { + // Read the message + BOOST_ASIO_CORO_YIELD chan_.async_read_one(resultset_.sequence_number(), std::move(self)); + + // Process it + result = process_read_message( + read_message, + chan_.current_capabilities(), + resultset_, + output_, + err, + output_info_ + ); + self.complete(err, result); + } + } +}; + +} // detail +} // mysql +} // boost + + +template +bool boost::mysql::detail::read_one_row( + channel& channel, + resultset& resultset, + row& output, + error_code& err, + error_info& info +) +{ + // Read a packet + auto read_message = channel.read_one(resultset.sequence_number(), err); + if (err) + return false; + + return process_read_message( + read_message, + channel.current_capabilities(), + resultset, + output, + err, + info + ); +} + +template +BOOST_ASIO_INITFN_AUTO_RESULT_TYPE( + CompletionToken, + void(boost::mysql::error_code, bool) +) +boost::mysql::detail::async_read_one_row( + channel& channel, + resultset& resultset, + row& output, + error_info& output_info, + CompletionToken&& token +) +{ + return boost::asio::async_compose ( + read_one_row_op( + channel, + output_info, + resultset, + output + ), + token, + channel + ); +} + +#endif /* INCLUDE_MYSQL_IMPL_NETWORK_ALGORITHMS_READ_TEXT_ROW_IPP_ */ diff --git a/include/boost/mysql/detail/network_algorithms/impl/read_row.hpp b/include/boost/mysql/detail/network_algorithms/impl/read_row.hpp deleted file mode 100644 index be047579..00000000 --- a/include/boost/mysql/detail/network_algorithms/impl/read_row.hpp +++ /dev/null @@ -1,199 +0,0 @@ -// -// Copyright (c) 2019-2022 Ruben Perez Hidalgo (rubenperez038 at gmail dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// - -#ifndef BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_IMPL_READ_ROW_HPP -#define BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_IMPL_READ_ROW_HPP - -#pragma once - -#include -#include - -namespace boost { -namespace mysql { -namespace detail { - -inline read_row_result process_read_message( - deserialize_row_fn deserializer, - capabilities current_capabilities, - const std::vector& meta, - row& output, - bytestring& ok_packet_buffer, - ok_packet& output_ok_packet, - error_code& err, - error_info& info -) -{ - assert(deserializer); - - // Message type: row, error or eof? - std::uint8_t msg_type = 0; - deserialization_context ctx (boost::asio::buffer(output.buffer()), current_capabilities); - err = make_error_code(deserialize(ctx, msg_type)); - if (err) - return read_row_result::error; - if (msg_type == eof_packet_header) - { - // end of resultset => we read the packet against the row, but it is the ok_packet, instead - err = deserialize_message(ctx, output_ok_packet); - if (err) - return read_row_result::error; - std::swap(output.buffer(), ok_packet_buffer); - output.buffer().clear(); - output.values().clear(); - return read_row_result::eof; - } - else if (msg_type == error_packet_header) - { - // An error occurred during the generation of the rows - err = process_error_packet(ctx, info); - return read_row_result::error; - } - else - { - // An actual row - ctx.rewind(1); // keep the 'message type' byte, as it is part of the actual message - err = deserializer(ctx, meta, output.values()); - if (err) - return read_row_result::error; - return read_row_result::row; - } -} - -template -struct read_row_op : boost::asio::coroutine -{ - channel& chan_; - error_info& output_info_; - deserialize_row_fn deserializer_; - const std::vector& meta_; - row& output_; - bytestring& ok_packet_buffer_; - ok_packet& output_ok_packet_; - - read_row_op( - channel& chan, - error_info& output_info, - deserialize_row_fn deserializer, - const std::vector& meta, - row& output, - bytestring& ok_packet_buffer, - ok_packet& output_ok_packet - ) : - chan_(chan), - output_info_(output_info), - deserializer_(deserializer), - meta_(meta), - output_(output), - ok_packet_buffer_(ok_packet_buffer), - output_ok_packet_(output_ok_packet) - { - } - - template - void operator()( - Self& self, - error_code err = {} - ) - { - read_row_result result = read_row_result::error; - - // Error checking - if (err) - { - self.complete(err, result); - return; - } - - // Normal path - BOOST_ASIO_CORO_REENTER(*this) - { - // Read the message - BOOST_ASIO_CORO_YIELD chan_.async_read(output_.buffer(), std::move(self)); - - // Process it - result = process_read_message( - deserializer_, - chan_.current_capabilities(), - meta_, - output_, - ok_packet_buffer_, - output_ok_packet_, - err, - output_info_ - ); - self.complete(err, result); - } - } -}; - -} // detail -} // mysql -} // boost - - -template -boost::mysql::detail::read_row_result boost::mysql::detail::read_row( - deserialize_row_fn deserializer, - channel& channel, - const std::vector& meta, - row& output, - bytestring& ok_packet_buffer, - ok_packet& output_ok_packet, - error_code& err, - error_info& info -) -{ - // Read a packet - channel.read(output.buffer(), err); - if (err) - return read_row_result::error; - - return process_read_message( - deserializer, - channel.current_capabilities(), - meta, - output, - ok_packet_buffer, - output_ok_packet, - err, - info - ); -} - -template -BOOST_ASIO_INITFN_AUTO_RESULT_TYPE( - CompletionToken, - void(boost::mysql::error_code, boost::mysql::detail::read_row_result) -) -boost::mysql::detail::async_read_row( - deserialize_row_fn deserializer, - channel& chan, - const std::vector& meta, - row& output, - bytestring& ok_packet_buffer, - ok_packet& output_ok_packet, - CompletionToken&& token, - error_info& output_info -) -{ - return boost::asio::async_compose ( - read_row_op( - chan, - output_info, - deserializer, - meta, - output, - ok_packet_buffer, - output_ok_packet - ), - token, - chan - ); -} - -#endif /* INCLUDE_MYSQL_IMPL_NETWORK_ALGORITHMS_READ_TEXT_ROW_IPP_ */ diff --git a/include/boost/mysql/detail/network_algorithms/read_row.hpp b/include/boost/mysql/detail/network_algorithms/read_one_row.hpp similarity index 64% rename from include/boost/mysql/detail/network_algorithms/read_row.hpp rename to include/boost/mysql/detail/network_algorithms/read_one_row.hpp index 5810ae90..727bf144 100644 --- a/include/boost/mysql/detail/network_algorithms/read_row.hpp +++ b/include/boost/mysql/detail/network_algorithms/read_one_row.hpp @@ -9,6 +9,7 @@ #define BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_READ_ROW_HPP #include +#include #include #include #include @@ -18,36 +19,23 @@ namespace boost { namespace mysql { namespace detail { -enum class read_row_result -{ - error, - row, - eof -}; - template -read_row_result read_row( - deserialize_row_fn deserializer, +bool read_one_row( channel& channel, - const std::vector& meta, + resultset& resultset, row& output, - bytestring& ok_packet_buffer, - ok_packet& output_ok_packet, error_code& err, error_info& info ); template -BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code, read_row_result)) -async_read_row( - deserialize_row_fn deserializer, +BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code, bool)) +async_read_one_row( channel& channel, - const std::vector& meta, + resultset& resultset, row& output, - bytestring& ok_packet_buffer, - ok_packet& output_ok_packet, - CompletionToken&& token, - error_info& output_info + error_info& output_info, + CompletionToken&& token ); @@ -55,7 +43,7 @@ async_read_row( } // mysql } // boost -#include +#include diff --git a/include/boost/mysql/resultset.hpp b/include/boost/mysql/resultset.hpp index 6de2e95b..3337430e 100644 --- a/include/boost/mysql/resultset.hpp +++ b/include/boost/mysql/resultset.hpp @@ -8,8 +8,10 @@ #ifndef BOOST_MYSQL_RESULTSET_HPP #define BOOST_MYSQL_RESULTSET_HPP +#include #include #include +#include #include #include #include @@ -122,6 +124,11 @@ public: meta_.emplace_back(pack, true); } + error_code deserialize_row(detail::deserialization_context& ctx, std::vector& output) + { + return deserializer_(ctx, meta_, output); + } + std::uint8_t& sequence_number() noexcept { return seqnum_; } std::vector& meta() noexcept { return meta_; } diff --git a/include/boost/mysql/row.hpp b/include/boost/mysql/row.hpp index d8027831..57ac7970 100644 --- a/include/boost/mysql/row.hpp +++ b/include/boost/mysql/row.hpp @@ -14,8 +14,10 @@ #include #include #include +#include #include #include +#include namespace boost { namespace mysql { @@ -46,11 +48,9 @@ namespace mysql { class row { std::vector values_; - detail::bytestring buffer_; + std::vector buffer_; public: row() = default; - row(std::vector&& values, detail::bytestring&& buffer) noexcept : - values_(std::move(values)), buffer_(std::move(buffer)) {}; // TODO: hide this row(const row&) = delete; // TODO row(row&&) = default; row& operator=(const row&) = delete; // TODO @@ -104,8 +104,35 @@ public: // TODO: hide these const std::vector& values() const noexcept { return values_; } std::vector& values() noexcept { return values_; } - const detail::bytestring& buffer() const noexcept { return buffer_; } - detail::bytestring& buffer() noexcept { return buffer_; } + void copy_strings() + { + // Calculate size + std::size_t size = 0; + for (const auto& v: values_) + { + auto typed_value = v.get_optional(); + if (typed_value.has_value()) + { + size += typed_value->size(); + } + } + + // Make space + buffer_.resize(size); + + // Copy the strings + std::size_t offset = 0; + for (auto& v: values_) + { + auto typed_value = v.get_optional(); + if (typed_value.has_value()) + { + std::memcpy(buffer_.data() + offset, typed_value->data(), typed_value->size()); + v = value(boost::string_view(buffer_.data() + offset, typed_value->size())); + offset += typed_value->size(); + } + } + } };