From 68fcceccb35f55749324474354e78fca18119f64 Mon Sep 17 00:00:00 2001 From: Ruben Perez Date: Mon, 8 Aug 2022 19:01:23 +0200 Subject: [PATCH] row basics --- include/boost/mysql/impl/resultset.hpp | 230 -------------- include/boost/mysql/impl/row.ipp | 144 +++++++++ .../mysql/impl/{row_view.hpp => row_view.ipp} | 0 include/boost/mysql/row.hpp | 298 ++---------------- include/boost/mysql/row_view.hpp | 2 +- 5 files changed, 171 insertions(+), 503 deletions(-) delete mode 100644 include/boost/mysql/impl/resultset.hpp create mode 100644 include/boost/mysql/impl/row.ipp rename include/boost/mysql/impl/{row_view.hpp => row_view.ipp} (100%) diff --git a/include/boost/mysql/impl/resultset.hpp b/include/boost/mysql/impl/resultset.hpp deleted file mode 100644 index 226feaa8..00000000 --- a/include/boost/mysql/impl/resultset.hpp +++ /dev/null @@ -1,230 +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_IMPL_RESULTSET_HPP -#define BOOST_MYSQL_IMPL_RESULTSET_HPP - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include - -template -std::vector boost::mysql::resultset::read_many( - std::size_t count, - error_code& err, - error_info& info -) -{ - assert(valid()); - - detail::clear_errors(err, info); - - std::vector res; - - for (std::size_t i = 0; i < count; ++i) - { - row r; - if (this->read_one(r, err, info)) - { - res.push_back(std::move(r)); - } - else - { - break; - } - } - - return res; -} - -template -std::vector boost::mysql::resultset::read_many( - std::size_t count -) -{ - detail::error_block blk; - auto res = read_many(count, blk.err, blk.info); - blk.check(); - return res; -} - -template -std::vector boost::mysql::resultset::read_all( - error_code& err, - error_info& info -) -{ - return read_many(std::numeric_limits::max(), err, info); -} - -template -std::vector boost::mysql::resultset::read_all() -{ - return read_many(std::numeric_limits::max()); -} - - -template -struct boost::mysql::resultset::read_many_op - : boost::asio::coroutine -{ - struct impl_struct - { - resultset& parent_resultset; - std::vector rows; - row current_row; - std::size_t remaining; - error_info& output_info; - bool cont {false}; - - impl_struct(resultset& obj, error_info& output_info, std::size_t count): - parent_resultset(obj), - remaining(count), - output_info(output_info) - { - }; - - void row_received() - { - rows.push_back(std::move(current_row)); - current_row = row(); - --remaining; - } - }; - - std::shared_ptr impl_; - - read_many_op( - resultset& obj, - error_info& output_info, - std::size_t count - ) : - impl_(std::make_shared(obj, output_info, count)) - { - }; - - template - auto bind_handler(Self& self, error_code err) -> - boost::asio::executor_binder< - decltype(std::bind(std::move(self), err, detail::read_row_result::eof)), - decltype(boost::asio::get_associated_executor(self, impl_->parent_resultset.get_executor())) - > - { - auto executor = boost::asio::get_associated_executor( - self, - impl_->parent_resultset.get_executor() - ); - return boost::asio::bind_executor( - executor, - std::bind(std::move(self), err, detail::read_row_result::eof) - ); - } - - template - void operator()( - Self& self, - error_code err = {}, - detail::read_row_result result = detail::read_row_result::error - ) - { - impl_struct& impl = *impl_; - BOOST_ASIO_CORO_REENTER(*this) - { - while (!impl.parent_resultset.complete() && impl.remaining > 0) - { - impl.cont = true; - BOOST_ASIO_CORO_YIELD detail::async_read_row( - impl.parent_resultset.deserializer_, - *impl.parent_resultset.channel_, - impl.parent_resultset.meta_.fields(), - impl.current_row, - impl.parent_resultset.ok_packet_buffer_, - impl.parent_resultset.ok_packet_, - std::move(self), - impl.output_info - ); - if (result == detail::read_row_result::error) - { - self.complete(err, std::move(impl.rows)); - BOOST_ASIO_CORO_YIELD break; - } - else if (result == detail::read_row_result::eof) - { - impl.parent_resultset.eof_received_ = true; - } - else - { - impl.row_received(); - } - } - - if (!impl.cont) - { - // Ensure we call handler as if dispatched using post - // through the correct executor - BOOST_ASIO_CORO_YIELD - boost::asio::post(bind_handler(self, err)); - } - - self.complete(err, std::move(impl.rows)); - } - } -}; - -template -template )) CompletionToken> -BOOST_ASIO_INITFN_AUTO_RESULT_TYPE( - CompletionToken, - void(boost::mysql::error_code, std::vector) -) -boost::mysql::resultset::async_read_many( - std::size_t count, - error_info& output_info, - CompletionToken&& token -) -{ - assert(valid()); - output_info.clear(); - return boost::asio::async_compose< - CompletionToken, - void(error_code, std::vector) - >( - read_many_op(*this, output_info, count), - token, - *this - ); -} - -template -template )) CompletionToken> -BOOST_ASIO_INITFN_AUTO_RESULT_TYPE( - CompletionToken, - void(boost::mysql::error_code, std::vector) -) -boost::mysql::resultset::async_read_all( - error_info& info, - CompletionToken&& token -) -{ - return async_read_many( - std::numeric_limits::max(), - info, - std::forward(token) - ); -} - - -#endif diff --git a/include/boost/mysql/impl/row.ipp b/include/boost/mysql/impl/row.ipp new file mode 100644 index 00000000..2d4d6df5 --- /dev/null +++ b/include/boost/mysql/impl/row.ipp @@ -0,0 +1,144 @@ +// +// 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_IMPL_ROW_HPP +#define BOOST_MYSQL_IMPL_ROW_HPP + +#pragma once + +#include + + +inline void boost::mysql::row::rebase_strings( + const char* old_buffer_base +) noexcept +{ + auto diff = string_buffer_.data() - old_buffer_base; + for (auto& f: fields_) + { + const boost::string_view* str = f.if_string(); + if (str) + { + f = field_view(boost::string_view( + str->data() + diff, + str->size() + )); + } + } +} + +inline void boost::mysql::row::copy_strings() +{ + // Calculate size + std::size_t size = 0; + for (const auto& f: fields_) + { + const boost::string_view* str = f.if_string(); + if (str) + { + size += str->size(); + } + } + + // Make space + string_buffer_.resize(size); + + // Copy the strings + std::size_t offset = 0; + for (auto& f: fields_) + { + const boost::string_view* str = f.if_string(); + if (str) + { + std::memcpy(string_buffer_.data() + offset, str->data(), str->size()); + f = field_view(boost::string_view(string_buffer_.data() + offset, str->size())); + offset += str->size(); + } + } +} + +inline boost::mysql::row::iterator boost::mysql::row::to_row_it( + std::vector::iterator it +) noexcept +{ + return it == fields_.end() ? nullptr : fields_.data() + (it - fields_.begin()); +} + +inline std::vector::iterator boost::mysql::row::to_vector_it( + iterator it +) noexcept +{ + return it == nullptr ? fields_.end() : fields_.begin() + (it - fields_.data()); +} + +boost::mysql::row::row( + const row& other +) : + fields_(other.fields_), + string_buffer_(other.string_buffer_) +{ + rebase_strings(other.string_buffer_.data()); +} + + +const boost::mysql::row& boost::mysql::row::operator=( + const row& rhs +) +{ + fields_ = rhs.fields_; + string_buffer_ = rhs.string_buffer_; + rebase_strings(rhs.string_buffer_.data()); + return *this; +} + +inline boost::string_view boost::mysql::row::copy_string( + boost::string_view v +) +{ + const char* old_buffer_base = string_buffer_.data(); + std::size_t old_buffer_size = string_buffer_.size(); + string_buffer_.insert(string_buffer_.end(), v.data(), v.data() + v.size()); + if (string_buffer_.data() != old_buffer_base) + rebase_strings(old_buffer_base); + return boost::string_view(string_buffer_.data() + old_buffer_size, v.size()); +} + +inline boost::mysql::row::iterator boost::mysql::row::insert( + iterator before, + field_view v +) +{ + const auto* str = v.if_string(); + if (str) + { + v = field_view(copy_string(*str)); + } + auto res = fields_.insert(to_vector_it(before), v); + return to_row_it(res); +} + +// inline boost::mysql::row::iterator boost::mysql::row::insert( +// iterator before, +// std::initializer_list v +// ) +// { +// // Calculate the extra size required for the strings +// std::size_t new_string_size = 0; +// for (const auto& f: v) +// { +// const auto* str = f.if_string(); +// if (str) +// { +// new_string_size += str->size(); +// } +// } + + +// } + + +#endif diff --git a/include/boost/mysql/impl/row_view.hpp b/include/boost/mysql/impl/row_view.ipp similarity index 100% rename from include/boost/mysql/impl/row_view.hpp rename to include/boost/mysql/impl/row_view.ipp diff --git a/include/boost/mysql/row.hpp b/include/boost/mysql/row.hpp index 667709d8..95b179b9 100644 --- a/include/boost/mysql/row.hpp +++ b/include/boost/mysql/row.hpp @@ -8,18 +8,12 @@ #ifndef BOOST_MYSQL_ROW_HPP #define BOOST_MYSQL_ROW_HPP -#include -#include #include -#include +#include #include -#include -#include -#include -#include -#include #include + namespace boost { namespace mysql { @@ -48,13 +42,11 @@ namespace mysql { */ class row { - std::vector fields_; - std::vector string_buffer_; public: row() = default; - row(const row&) = delete; // TODO + inline row(const row&); row(row&&) = default; - row& operator=(const row&) = delete; // TODO + inline const row& operator=(const row&); row& operator=(row&&) = default; ~row() = default; @@ -71,21 +63,26 @@ public: bool empty() const noexcept { return fields_.empty(); } std::size_t size() const noexcept { return fields_.size(); } - iterator insert(iterator before, field_view v); - iterator insert(iterator before, std::initializer_list v); + inline iterator insert(iterator before, field_view v); + inline iterator insert(iterator before, std::initializer_list v); template iterator insert(iterator before, FwdIt first, FwdIt last); - iterator replace(iterator pos, field_view v); - iterator replace(iterator first, iterator last, std::initializer_list v); + inline iterator replace(iterator pos, field_view v); + inline iterator replace(iterator first, iterator last, std::initializer_list v); template - iterator replace(iterator first, iterator last, FwdIt other_first, FwdIt other_last); + inline iterator replace(iterator first, iterator last, FwdIt other_first, FwdIt other_last); - iterator erase(iterator pos); - iterator erase(iterator first, iterator last); + inline iterator erase(iterator pos); + inline iterator erase(iterator first, iterator last); - void push_back(field_view v); - void pop_back(); + inline void push_back(field_view v); + inline void pop_back(); + + operator row_view() const noexcept + { + return row_view(fields_.data(), fields_.size()); + } @@ -105,247 +102,21 @@ public: // TODO: hide these const std::vector& fields() const noexcept { return fields_; } std::vector& values() noexcept { return fields_; } - void copy_strings() - { - // Calculate size - std::size_t size = 0; - for (const auto& f: fields_) - { - const boost::string_view* str = f.if_string(); - if (str) - { - size += str->size(); - } - } - - // Make space - string_buffer_.resize(size); - - // Copy the strings - std::size_t offset = 0; - for (auto& f: fields_) - { - const boost::string_view* str = f.if_string(); - if (str) - { - std::memcpy(string_buffer_.data() + offset, str->data(), str->size()); - f = field_view(boost::string_view(string_buffer_.data() + offset, str->size())); - offset += str->size(); - } - } - } -}; - - -class row_view -{ - const field_view* fields_ {}; - std::size_t size_ {}; -public: - row_view() = default; - row_view(const field_view* f, std::size_t size) noexcept : fields_ {f}, size_{size} {} - row_view(const row& r) noexcept : - fields_(r.begin()), - size_(r.size()) - { - } - - using iterator = const field_view*; - using const_iterator = iterator; - - iterator begin() const noexcept { return fields_; } - iterator end() const noexcept { return fields_ + size_; } - field_view at(std::size_t i) const; - field_view operator[](std::size_t i) const noexcept { return fields_[i]; } - field_view front() const noexcept { return fields_[0]; } - field_view back() const noexcept { return fields_[size_ - 1]; } - bool empty() const noexcept { return size_ == 0; } - std::size_t size() const noexcept { return size_; } -}; - - - -class rows -{ + inline void copy_strings(); +private: std::vector fields_; std::vector string_buffer_; - std::size_t num_columns_ {}; - void rebase_strings(const char* old_buffer_base) - { - const char* new_buffer_base = string_buffer_.data(); - auto diff = new_buffer_base - old_buffer_base; - if (diff) - { - for (auto& f: fields_) - { - const boost::string_view* str = f.if_string(); - if (str) - { - f = field_view(boost::string_view( - str->data() + diff, - str->size() - )); - } - } - } - } - -public: - rows() = default; - rows(std::size_t num_columns) noexcept : num_columns_(num_columns) {} - rows(const rows&) = delete; // TODO - rows(rows&&) = default; - const rows& operator=(const rows&) = delete; // TODO - rows& operator=(rows&&) = default; - - class iterator; - using const_iterator = iterator; - // TODO: add other standard container members - - iterator begin() const noexcept { return iterator(this, 0); } - iterator end() const noexcept { return iterator(this, size()); } - row_view at(std::size_t i) const { /* TODO: check idx */ return (*this)[i]; } - row_view operator[](std::size_t i) const noexcept - { - std::size_t offset = num_columns_ * i; - return row_view(fields_.data() + offset, num_columns_); - } - row_view front() const noexcept { return (*this)[0]; } - row_view back() const noexcept { return (*this)[size() - 1]; } - bool empty() const noexcept { return fields_.empty(); } - std::size_t size() const noexcept { return fields_.size() / num_columns_; } - - // TODO: hide these - std::vector& fields() noexcept { return fields_; } - void copy_strings(std::size_t field_offset) - { - // Calculate the extra size required for the new strings - std::size_t size = 0; - for (const auto* f = fields_.data() + field_offset; f != fields_.data() + fields_.size(); ++f) - { - const boost::string_view* str = f->if_string(); - if (str) - { - size += str->size(); - } - } - - // Make space and rebase the old strings - const char* old_buffer_base = string_buffer_.data(); - std::size_t old_buffer_size = string_buffer_.size(); - string_buffer_.resize(size); - rebase_strings(old_buffer_base); - - // Copy the strings - std::size_t offset = old_buffer_size; - for (auto& f: fields_) - { - const boost::string_view* str = f.if_string(); - if (str) - { - std::memcpy(string_buffer_.data() + offset, str->data(), str->size()); - f = field_view(boost::string_view(string_buffer_.data() + offset, str->size())); - offset += str->size(); - } - } - } - void clear() noexcept - { - fields_.clear(); - string_buffer_.clear(); - } - - - - class iterator - { - const rows* obj_; - std::size_t row_num_; - public: - using value_type = row_view; // TODO: should this be row? - using reference = row_view; - using pointer = void const*; - using difference_type = std::ptrdiff_t; - using iterator_category = std::bidirectional_iterator_tag; - // TODO: this can be made random access - - iterator(const rows* obj, std::size_t rownum) noexcept : obj_(obj), row_num_(rownum) {} - - iterator& operator++() noexcept { ++row_num_; return *this; } - iterator operator++(int) noexcept { return iterator(obj_, row_num_ + 1); } - iterator& operator--() noexcept { --row_num_; return *this; } - iterator operator--(int) noexcept { return iterator(obj_, row_num_ - 1); } - reference operator*() const noexcept { return (*obj_)[row_num_]; } - bool operator==(const iterator& rhs) const noexcept { return obj_ == rhs.obj_&& row_num_ == rhs.row_num_; } - bool operator!=(const iterator& rhs) const noexcept { return !(*this == rhs); } - }; + inline iterator to_row_it(std::vector::iterator it) noexcept; + inline std::vector::iterator to_vector_it(iterator it) noexcept; + inline boost::string_view copy_string(boost::string_view v); + inline void rebase_strings(const char* old_buffer_base) noexcept; }; -class rows_view -{ - const field_view* fields_ {}; - std::size_t num_values_ {}; - std::size_t num_columns_ {}; -public: - rows_view() = default; - rows_view(const field_view* fields, std::size_t num_values, std::size_t num_columns) noexcept : - fields_(fields), - num_values_(num_values), - num_columns_(num_columns) - { - } - - class iterator; - using const_iterator = iterator; - // TODO: add other standard container members - - iterator begin() const noexcept { return iterator(this, 0); } - iterator end() const noexcept { return iterator(this, size()); } - row_view at(std::size_t i) const { /* TODO: check idx */ return (*this)[i]; } - row_view operator[](std::size_t i) const noexcept - { - std::size_t offset = num_columns_ * i; - return row_view(fields_ + offset, num_columns_); - } - row_view front() const noexcept { return (*this)[0]; } - row_view back() const noexcept { return (*this)[size() - 1]; } - bool empty() const noexcept { return num_values_ == 0; } - std::size_t size() const noexcept { return num_values_ / num_columns_; } - - class iterator - { - const rows_view* obj_; - std::size_t row_num_; - public: - using value_type = row_view; // TODO: should this be row? - using reference = row_view; - using pointer = void const*; - using difference_type = std::ptrdiff_t; - using iterator_category = std::bidirectional_iterator_tag; - // TODO: this can be made random access - - iterator(const rows_view* obj, std::size_t rownum) noexcept : obj_(obj), row_num_(rownum) {} - - iterator& operator++() noexcept { ++row_num_; return *this; } - iterator operator++(int) noexcept { return iterator(obj_, row_num_ + 1); } - iterator& operator--() noexcept { --row_num_; return *this; } - iterator operator--(int) noexcept { return iterator(obj_, row_num_ - 1); } - reference operator*() const noexcept { return (*obj_)[row_num_]; } - bool operator==(const iterator& rhs) const noexcept { return obj_ == rhs.obj_&& row_num_ == rhs.row_num_; } - bool operator!=(const iterator& rhs) const noexcept { return !(*this == rhs); } - }; -}; - - -/** - * \relates row - * \brief Compares two rows. - */ inline bool operator==(const row& lhs, const row& rhs) { - return detail::container_equals(lhs.fields(), rhs.fields()); + return row_view(lhs) == row_view(rhs); } /** @@ -354,28 +125,11 @@ inline bool operator==(const row& lhs, const row& rhs) */ inline bool operator!=(const row& lhs, const row& rhs) { return !(lhs == rhs); } -/** - * \relates row - * \brief Streams a row. - */ -inline std::ostream& operator<<(std::ostream& os, const row& value) -{ - os << '{'; - const auto& arr = value.fields(); - if (!arr.empty()) - { - os << arr[0]; - for (auto it = std::next(arr.begin()); it != arr.end(); ++it) - { - os << ", " << *it; - } - } - return os << '}'; -} } // mysql } // boost +#include #endif diff --git a/include/boost/mysql/row_view.hpp b/include/boost/mysql/row_view.hpp index 616e0f76..9a71193c 100644 --- a/include/boost/mysql/row_view.hpp +++ b/include/boost/mysql/row_view.hpp @@ -58,6 +58,6 @@ inline std::ostream& operator<<(std::ostream& os, const row_view& value); } // mysql } // boost -#include +#include #endif