From 7e7e3ddf09aedb148321d08f2c4a8c4b2a452370 Mon Sep 17 00:00:00 2001 From: Nikolai Vladimirov Date: Fri, 27 Jun 2025 19:37:37 +0000 Subject: [PATCH 01/14] Draft version without consume_one implementation --- .../boost/redis/adapter/detail/adapters.hpp | 28 +++++++ .../redis/adapter/detail/response_traits.hpp | 8 ++ .../redis/adapter/detail/result_traits.hpp | 8 ++ include/boost/redis/resp3/node.hpp | 8 ++ include/boost/redis/response.hpp | 77 +++++++++++++++++++ 5 files changed, 129 insertions(+) diff --git a/include/boost/redis/adapter/detail/adapters.hpp b/include/boost/redis/adapter/detail/adapters.hpp index 5bb8af76..0c8d6822 100644 --- a/include/boost/redis/adapter/detail/adapters.hpp +++ b/include/boost/redis/adapter/detail/adapters.hpp @@ -12,6 +12,7 @@ #include #include #include +#include #include @@ -170,6 +171,33 @@ class general_aggregate { } }; +template <> +class general_aggregate> { +private: + result* result_; + +public: + explicit general_aggregate(result* c = nullptr) + : result_(c) + { } + template + void operator()(resp3::basic_node const& nd, system::error_code&) + { + BOOST_ASSERT_MSG(!!result_, "Unexpected null pointer"); + switch (nd.data_type) { + case resp3::type::blob_error: + case resp3::type::simple_error: + *result_ = error{ + nd.data_type, + std::string{std::cbegin(nd.value), std::cend(nd.value)} + }; + break; + default: + result_->value().push_back(nd); + } + } +}; + template class general_simple { private: diff --git a/include/boost/redis/adapter/detail/response_traits.hpp b/include/boost/redis/adapter/detail/response_traits.hpp index 66684a70..9959b1cd 100644 --- a/include/boost/redis/adapter/detail/response_traits.hpp +++ b/include/boost/redis/adapter/detail/response_traits.hpp @@ -133,6 +133,14 @@ struct response_traits> { static auto adapt(response_type& r) noexcept { return adapter_type{r}; } }; +template <> +struct response_traits { + using response_type = generic_flat_response; + using adapter_type = vector_adapter; + + static auto adapt(response_type& v) noexcept { return adapter_type{v}; } +}; + template class wrapper { public: diff --git a/include/boost/redis/adapter/detail/result_traits.hpp b/include/boost/redis/adapter/detail/result_traits.hpp index 723ca2aa..425c107f 100644 --- a/include/boost/redis/adapter/detail/result_traits.hpp +++ b/include/boost/redis/adapter/detail/result_traits.hpp @@ -13,6 +13,7 @@ #include #include #include +#include #include @@ -62,6 +63,13 @@ struct result_traits, Allocator>>> static auto adapt(response_type& v) noexcept { return adapter_type{&v}; } }; +template <> +struct result_traits { + using response_type = generic_flat_response; + using adapter_type = adapter::detail::general_aggregate; + static auto adapt(response_type& v) noexcept { return adapter_type{&v}; } +}; + template using adapter_t = typename result_traits>::adapter_type; diff --git a/include/boost/redis/resp3/node.hpp b/include/boost/redis/resp3/node.hpp index f11083c2..e7fca4ee 100644 --- a/include/boost/redis/resp3/node.hpp +++ b/include/boost/redis/resp3/node.hpp @@ -66,6 +66,14 @@ using node = basic_node; */ using node_view = basic_node; +/** + * TODO: documentation + */ +struct offset_node : node_view { + std::size_t offset{}; + std::size_t size{}; +}; + } // namespace boost::redis::resp3 #endif // BOOST_REDIS_RESP3_NODE_HPP diff --git a/include/boost/redis/response.hpp b/include/boost/redis/response.hpp index 79e1d909..ff52a258 100644 --- a/include/boost/redis/response.hpp +++ b/include/boost/redis/response.hpp @@ -34,6 +34,83 @@ using response = std::tuple...>; */ using generic_response = adapter::result>; +struct flat_response_impl { +private: + class iterator { + public: + using value_type = resp3::node_view; + using difference_type = std::ptrdiff_t; + using pointer = void; + using reference = value_type; + using iterator_category = std::forward_iterator_tag; + + explicit iterator(flat_response_impl* owner, std::size_t i) noexcept + : owner_(owner) + , index_(i) + { } + + value_type operator*() const { return owner_->operator[](index_); } + + iterator& operator++() + { + ++index_; + return *this; + } + + bool operator==(const iterator& other) const { return index_ == other.index_; } + bool operator!=(const iterator& other) const { return !(*this == other); } + + private: + flat_response_impl* owner_; + std::size_t index_; + }; + +public: + resp3::node_view at(std::size_t index) { return make_node_view(view_.at(index)); } + + std::size_t size() { return view_.size(); } + + resp3::node_view operator[](std::size_t index) { return make_node_view(view_[index]); } + + iterator begin() { return iterator{this, 0}; } + + iterator end() { return iterator{this, view_.size()}; } + + template + void push_back(const resp3::basic_node& nd) + { + resp3::offset_node new_node; + new_node.data_type = nd.data_type; + new_node.aggregate_size = nd.aggregate_size; + new_node.depth = nd.depth; + new_node.offset = data_.size(); + new_node.size = nd.value.size(); + + data_ += std::string{std::cbegin(nd.value), std::cend(nd.value)}; + + view_.push_back(std::move(new_node)); + } + +private: + resp3::node_view make_node_view(const resp3::offset_node& n) + { + return resp3::node_view{ + .data_type = n.data_type, + .aggregate_size = n.aggregate_size, + .depth = n.depth, + .value = std::string_view{data_.data() + n.offset, n.size} + }; + } + + std::string data_; + std::vector view_; +}; + +/** + * TODO: documentation + */ +using generic_flat_response = adapter::result; + /** @brief Consume on response from a generic response * * This function rotates the elements so that the start of the next From da13036b7b17bbc539bd76f42ed4eab5ea4b391f Mon Sep 17 00:00:00 2001 From: Nikolai Vladimirov Date: Fri, 27 Jun 2025 23:28:48 +0000 Subject: [PATCH 02/14] Addressed some comments --- .../boost/redis/adapter/detail/adapters.hpp | 9 +++-- include/boost/redis/resp3/node.hpp | 8 ----- include/boost/redis/response.hpp | 34 +++++++++++-------- 3 files changed, 23 insertions(+), 28 deletions(-) diff --git a/include/boost/redis/adapter/detail/adapters.hpp b/include/boost/redis/adapter/detail/adapters.hpp index 0c8d6822..63add978 100644 --- a/include/boost/redis/adapter/detail/adapters.hpp +++ b/include/boost/redis/adapter/detail/adapters.hpp @@ -172,12 +172,12 @@ class general_aggregate { }; template <> -class general_aggregate> { +class general_aggregate> { private: - result* result_; + result* result_; public: - explicit general_aggregate(result* c = nullptr) + explicit general_aggregate(result* c = nullptr) : result_(c) { } template @@ -192,8 +192,7 @@ class general_aggregate> { std::string{std::cbegin(nd.value), std::cend(nd.value)} }; break; - default: - result_->value().push_back(nd); + default: result_->value().push_back(nd); } } }; diff --git a/include/boost/redis/resp3/node.hpp b/include/boost/redis/resp3/node.hpp index e7fca4ee..f11083c2 100644 --- a/include/boost/redis/resp3/node.hpp +++ b/include/boost/redis/resp3/node.hpp @@ -66,14 +66,6 @@ using node = basic_node; */ using node_view = basic_node; -/** - * TODO: documentation - */ -struct offset_node : node_view { - std::size_t offset{}; - std::size_t size{}; -}; - } // namespace boost::redis::resp3 #endif // BOOST_REDIS_RESP3_NODE_HPP diff --git a/include/boost/redis/response.hpp b/include/boost/redis/response.hpp index ff52a258..631f1029 100644 --- a/include/boost/redis/response.hpp +++ b/include/boost/redis/response.hpp @@ -34,7 +34,7 @@ using response = std::tuple...>; */ using generic_response = adapter::result>; -struct flat_response_impl { +struct flat_response_value { private: class iterator { public: @@ -44,7 +44,7 @@ struct flat_response_impl { using reference = value_type; using iterator_category = std::forward_iterator_tag; - explicit iterator(flat_response_impl* owner, std::size_t i) noexcept + explicit iterator(flat_response_value* owner, std::size_t i) noexcept : owner_(owner) , index_(i) { } @@ -61,10 +61,15 @@ struct flat_response_impl { bool operator!=(const iterator& other) const { return !(*this == other); } private: - flat_response_impl* owner_; + flat_response_value* owner_; std::size_t index_; }; + struct offset_node : resp3::node_view { + std::size_t offset{}; + std::size_t size{}; + }; + public: resp3::node_view at(std::size_t index) { return make_node_view(view_.at(index)); } @@ -79,37 +84,36 @@ struct flat_response_impl { template void push_back(const resp3::basic_node& nd) { - resp3::offset_node new_node; + offset_node new_node; new_node.data_type = nd.data_type; new_node.aggregate_size = nd.aggregate_size; new_node.depth = nd.depth; new_node.offset = data_.size(); new_node.size = nd.value.size(); - data_ += std::string{std::cbegin(nd.value), std::cend(nd.value)}; - + data_.append(nd.value.data()); view_.push_back(std::move(new_node)); } private: - resp3::node_view make_node_view(const resp3::offset_node& n) + resp3::node_view make_node_view(const offset_node& nd) { - return resp3::node_view{ - .data_type = n.data_type, - .aggregate_size = n.aggregate_size, - .depth = n.depth, - .value = std::string_view{data_.data() + n.offset, n.size} - }; + resp3::node_view result; + result.data_type = nd.data_type; + result.aggregate_size = nd.aggregate_size; + result.depth = nd.depth; + result.value = std::string_view{data_.data() + nd.offset, nd.size}; + return result; } std::string data_; - std::vector view_; + std::vector view_; }; /** * TODO: documentation */ -using generic_flat_response = adapter::result; +using generic_flat_response = adapter::result; /** @brief Consume on response from a generic response * From 6971fa66ba41f55b6fc740d71c274c507c51f851 Mon Sep 17 00:00:00 2001 From: Nikolai Vladimirov Date: Sat, 28 Jun 2025 04:59:37 +0000 Subject: [PATCH 03/14] reserve function --- include/boost/redis/response.hpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/include/boost/redis/response.hpp b/include/boost/redis/response.hpp index 631f1029..6faf2b49 100644 --- a/include/boost/redis/response.hpp +++ b/include/boost/redis/response.hpp @@ -71,6 +71,12 @@ struct flat_response_value { }; public: + void reserve(std::size_t new_cap, std::size_t elem_length) + { + data_.reserve(new_cap * elem_length); + view_.reserve(new_cap); + } + resp3::node_view at(std::size_t index) { return make_node_view(view_.at(index)); } std::size_t size() { return view_.size(); } From 7b59e2870f1fa42819e8560b402347124bfd8659 Mon Sep 17 00:00:00 2001 From: Nikolai Vladimirov Date: Wed, 2 Jul 2025 18:39:43 +0000 Subject: [PATCH 04/14] Corrected and cleaner implementation with comments --- example/cpp20_subscriber.cpp | 3 + include/boost/redis/impl/response.ipp | 37 ++++++---- include/boost/redis/resp3/node.hpp | 8 +++ include/boost/redis/response.hpp | 99 +++++++++------------------ 4 files changed, 70 insertions(+), 77 deletions(-) diff --git a/example/cpp20_subscriber.cpp b/example/cpp20_subscriber.cpp index d2ec462f..09536dd1 100644 --- a/example/cpp20_subscriber.cpp +++ b/example/cpp20_subscriber.cpp @@ -23,6 +23,7 @@ namespace asio = boost::asio; using namespace std::chrono_literals; using boost::redis::request; using boost::redis::generic_response; +using boost::redis::generic_flat_response; using boost::redis::consume_one; using boost::redis::logger; using boost::redis::config; @@ -54,6 +55,8 @@ auto receiver(std::shared_ptr conn) -> asio::awaitable request req; req.push("SUBSCRIBE", "channel"); + // Alternatively, you can use generic_flat_response here, but keep in mind + // that to access elements you need to call .view() on resp.value() generic_response resp; conn->set_receive_response(resp); diff --git a/include/boost/redis/impl/response.ipp b/include/boost/redis/impl/response.ipp index a4b09a6e..19194c68 100644 --- a/include/boost/redis/impl/response.ipp +++ b/include/boost/redis/impl/response.ipp @@ -11,15 +11,30 @@ namespace boost::redis { -void consume_one(generic_response& r, system::error_code& ec) +namespace { +template +auto& get_value(Container& c) +{ + return c; +} + +template <> +auto& get_value(flat_response_value& c) +{ + return c.view(); +} + +template +void consume_one_impl(Response& r, system::error_code& ec) { if (r.has_error()) return; // Nothing to consume. - if (std::empty(r.value())) + auto& value = get_value(r.value()); + if (std::empty(value)) return; // Nothing to consume. - auto const depth = r.value().front().depth; + auto const depth = value.front().depth; // To simplify we will refuse to consume any data-type that is not // a root node. I think there is no use for that and it is complex @@ -33,17 +48,15 @@ void consume_one(generic_response& r, system::error_code& ec) return e.depth == depth; }; - auto match = std::find_if(std::next(std::cbegin(r.value())), std::cend(r.value()), f); + auto match = std::find_if(std::next(std::cbegin(value)), std::cend(value), f); - r.value().erase(std::cbegin(r.value()), match); + value.erase(std::cbegin(value), match); } -void consume_one(generic_response& r) -{ - system::error_code ec; - consume_one(r, ec); - if (ec) - throw system::system_error(ec); -} +} // namespace + +void consume_one(generic_response& r, system::error_code& ec) { consume_one_impl(r, ec); } + +void consume_one(generic_flat_response& r, system::error_code& ec) { consume_one_impl(r, ec); } } // namespace boost::redis diff --git a/include/boost/redis/resp3/node.hpp b/include/boost/redis/resp3/node.hpp index f11083c2..8088f4e6 100644 --- a/include/boost/redis/resp3/node.hpp +++ b/include/boost/redis/resp3/node.hpp @@ -66,6 +66,14 @@ using node = basic_node; */ using node_view = basic_node; +struct offset_string { + std::string_view data; + std::size_t offset{}; + std::size_t size{}; +}; + +using offset_node = basic_node; + } // namespace boost::redis::resp3 #endif // BOOST_REDIS_RESP3_NODE_HPP diff --git a/include/boost/redis/response.hpp b/include/boost/redis/response.hpp index 6faf2b49..3c0b7f84 100644 --- a/include/boost/redis/response.hpp +++ b/include/boost/redis/response.hpp @@ -35,89 +35,48 @@ using response = std::tuple...>; using generic_response = adapter::result>; struct flat_response_value { -private: - class iterator { - public: - using value_type = resp3::node_view; - using difference_type = std::ptrdiff_t; - using pointer = void; - using reference = value_type; - using iterator_category = std::forward_iterator_tag; - - explicit iterator(flat_response_value* owner, std::size_t i) noexcept - : owner_(owner) - , index_(i) - { } - - value_type operator*() const { return owner_->operator[](index_); } - - iterator& operator++() - { - ++index_; - return *this; - } - - bool operator==(const iterator& other) const { return index_ == other.index_; } - bool operator!=(const iterator& other) const { return !(*this == other); } - - private: - flat_response_value* owner_; - std::size_t index_; - }; - - struct offset_node : resp3::node_view { - std::size_t offset{}; - std::size_t size{}; - }; - public: - void reserve(std::size_t new_cap, std::size_t elem_length) + /// Reserve capacity for nodes and data storage. + void reserve(std::size_t num_nodes, std::size_t string_size) { - data_.reserve(new_cap * elem_length); - view_.reserve(new_cap); + data_.reserve(num_nodes * string_size); + view_.reserve(num_nodes); } - resp3::node_view at(std::size_t index) { return make_node_view(view_.at(index)); } - - std::size_t size() { return view_.size(); } - - resp3::node_view operator[](std::size_t index) { return make_node_view(view_[index]); } - - iterator begin() { return iterator{this, 0}; } - - iterator end() { return iterator{this, view_.size()}; } + std::vector const& view() const { return view_; } + std::vector& view() { return view_; } template void push_back(const resp3::basic_node& nd) { - offset_node new_node; + resp3::offset_string offset_string; + offset_string.offset = data_.size(); + offset_string.size = nd.value.size(); + + data_.append(nd.value.data()); + + offset_string.data = std::string_view{ + data_.data() + offset_string.offset, + offset_string.size}; + + resp3::offset_node new_node; new_node.data_type = nd.data_type; new_node.aggregate_size = nd.aggregate_size; new_node.depth = nd.depth; - new_node.offset = data_.size(); - new_node.size = nd.value.size(); + new_node.value = std::move(offset_string); - data_.append(nd.value.data()); view_.push_back(std::move(new_node)); } private: - resp3::node_view make_node_view(const offset_node& nd) - { - resp3::node_view result; - result.data_type = nd.data_type; - result.aggregate_size = nd.aggregate_size; - result.depth = nd.depth; - result.value = std::string_view{data_.data() + nd.offset, nd.size}; - return result; - } - std::string data_; - std::vector view_; + std::vector view_; }; -/** - * TODO: documentation +/** @brief A memory-efficient generic response to a request. + * @ingroup high-level-api + * + * Uses a compact buffer to store RESP3 data with reduced allocations. */ using generic_flat_response = adapter::result; @@ -159,8 +118,18 @@ using generic_flat_response = adapter::result; */ void consume_one(generic_response& r, system::error_code& ec); +/// Consume on response from a generic flat response +void consume_one(generic_flat_response& r, system::error_code& ec); + /// Throwing overload of `consume_one`. -void consume_one(generic_response& r); +template +void consume_one(Response& r) +{ + system::error_code ec; + consume_one(r, ec); + if (ec) + throw system::system_error(ec); +} } // namespace boost::redis From 359c8bc445b2ef8c7b25210535a8bea1279ad2a1 Mon Sep 17 00:00:00 2001 From: Nikolai Vladimirov Date: Sat, 5 Jul 2025 18:30:04 +0000 Subject: [PATCH 05/14] Call prepare_done function to form response --- include/boost/redis/adapter/any_adapter.hpp | 9 ++++++--- .../boost/redis/adapter/detail/adapters.hpp | 18 ++++++++++++++++++ include/boost/redis/connection.hpp | 3 ++- include/boost/redis/response.hpp | 16 +++++++++++----- 4 files changed, 37 insertions(+), 9 deletions(-) diff --git a/include/boost/redis/adapter/any_adapter.hpp b/include/boost/redis/adapter/any_adapter.hpp index b35b4895..55461824 100644 --- a/include/boost/redis/adapter/any_adapter.hpp +++ b/include/boost/redis/adapter/any_adapter.hpp @@ -34,10 +34,13 @@ namespace boost::redis { */ class any_adapter { public: - using fn_type = std::function; + using adapt_fn_type = std::function< + void(std::size_t, resp3::node_view const&, system::error_code&)>; + using done_fn_type = std::function; struct impl_t { - fn_type adapt_fn; + adapt_fn_type adapt_fn; + done_fn_type prepare_done_fn; std::size_t supported_response_size; } impl_; @@ -47,7 +50,7 @@ class any_adapter { using namespace boost::redis::adapter; auto adapter = boost_redis_adapt(resp); std::size_t size = adapter.get_supported_response_size(); - return {std::move(adapter), size}; + return {std::move(adapter), detail::prepare_done(resp), size}; } template diff --git a/include/boost/redis/adapter/detail/adapters.hpp b/include/boost/redis/adapter/detail/adapters.hpp index 63add978..0be97684 100644 --- a/include/boost/redis/adapter/detail/adapters.hpp +++ b/include/boost/redis/adapter/detail/adapters.hpp @@ -139,6 +139,24 @@ void boost_redis_from_bulk(T& t, resp3::basic_node const& node, system:: //================================================ +template +auto prepare_done(T&) noexcept -> std::function +{ + return [] { }; +} + +template +auto prepare_done(generic_flat_response& resp) noexcept -> std::function +{ + return [resp]() mutable { + if (resp.has_value()) { + resp.value().set_view(); + } + }; +} + +//================================================ + template class general_aggregate { private: diff --git a/include/boost/redis/connection.hpp b/include/boost/redis/connection.hpp index 63fceda1..54b0f8d8 100644 --- a/include/boost/redis/connection.hpp +++ b/include/boost/redis/connection.hpp @@ -694,7 +694,8 @@ class basic_connection { 1); auto info = detail::make_elem(req, std::move(adapter_impl.adapt_fn)); - info->set_done_callback([notifier]() { + info->set_done_callback([notifier, prepare_done = std::move(adapter_impl.prepare_done_fn)]() { + prepare_done(); notifier->try_send(std::error_code{}, 0); }); diff --git a/include/boost/redis/response.hpp b/include/boost/redis/response.hpp index 3c0b7f84..203766c0 100644 --- a/include/boost/redis/response.hpp +++ b/include/boost/redis/response.hpp @@ -53,11 +53,7 @@ struct flat_response_value { offset_string.offset = data_.size(); offset_string.size = nd.value.size(); - data_.append(nd.value.data()); - - offset_string.data = std::string_view{ - data_.data() + offset_string.offset, - offset_string.size}; + data_.append(nd.value.data(), nd.value.size()); resp3::offset_node new_node; new_node.data_type = nd.data_type; @@ -68,6 +64,16 @@ struct flat_response_value { view_.push_back(std::move(new_node)); } + void set_view() + { + for (auto& node : view_) { + auto& offset_string = node.value; + offset_string.data = std::string_view{ + data_.data() + offset_string.offset, + offset_string.size}; + } + } + private: std::string data_; std::vector view_; From 1e5803b1e328c26124df2ebdf98898170cfa6a44 Mon Sep 17 00:00:00 2001 From: Nikolai Vladimirov Date: Sat, 5 Jul 2025 19:31:23 +0000 Subject: [PATCH 06/14] Defined done_fn_type in adapters.hpp --- include/boost/redis/adapter/any_adapter.hpp | 2 +- include/boost/redis/adapter/detail/adapters.hpp | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/include/boost/redis/adapter/any_adapter.hpp b/include/boost/redis/adapter/any_adapter.hpp index 32718f1d..ed80aa7d 100644 --- a/include/boost/redis/adapter/any_adapter.hpp +++ b/include/boost/redis/adapter/any_adapter.hpp @@ -36,7 +36,7 @@ class any_adapter { public: using adapt_fn_type = std::function< void(std::size_t, resp3::node_view const&, system::error_code&)>; - using done_fn_type = std::function; + using done_fn_type = adapter::detail::done_fn_type; struct impl_t { adapt_fn_type adapt_fn; diff --git a/include/boost/redis/adapter/detail/adapters.hpp b/include/boost/redis/adapter/detail/adapters.hpp index 0be97684..ec8bac62 100644 --- a/include/boost/redis/adapter/detail/adapters.hpp +++ b/include/boost/redis/adapter/detail/adapters.hpp @@ -139,14 +139,16 @@ void boost_redis_from_bulk(T& t, resp3::basic_node const& node, system:: //================================================ +using done_fn_type = std::function; + template -auto prepare_done(T&) noexcept -> std::function +auto prepare_done(T&) noexcept -> done_fn_type { return [] { }; } template -auto prepare_done(generic_flat_response& resp) noexcept -> std::function +auto prepare_done(generic_flat_response& resp) noexcept -> done_fn_type { return [resp]() mutable { if (resp.has_value()) { From 3545f89f42e74fd523a7d1e42402ed93546727fa Mon Sep 17 00:00:00 2001 From: Nikolai Vladimirov Date: Sat, 5 Jul 2025 20:18:28 +0000 Subject: [PATCH 07/14] Moved implementation of push_back to general_aggregate --- .../boost/redis/adapter/detail/adapters.hpp | 17 ++++++++++- include/boost/redis/response.hpp | 30 ++++++++----------- 2 files changed, 28 insertions(+), 19 deletions(-) diff --git a/include/boost/redis/adapter/detail/adapters.hpp b/include/boost/redis/adapter/detail/adapters.hpp index ec8bac62..eb7a8e53 100644 --- a/include/boost/redis/adapter/detail/adapters.hpp +++ b/include/boost/redis/adapter/detail/adapters.hpp @@ -212,7 +212,22 @@ class general_aggregate> { std::string{std::cbegin(nd.value), std::cend(nd.value)} }; break; - default: result_->value().push_back(nd); + default: + auto& data = result_->value().data_; + + resp3::offset_string offset_string; + offset_string.offset = data.size(); + offset_string.size = nd.value.size(); + + data.append(nd.value.data(), nd.value.size()); + + resp3::offset_node new_node; + new_node.data_type = nd.data_type; + new_node.aggregate_size = nd.aggregate_size; + new_node.depth = nd.depth; + new_node.value = std::move(offset_string); + + result_->value().view_.push_back(std::move(new_node)); } } }; diff --git a/include/boost/redis/response.hpp b/include/boost/redis/response.hpp index be4d4815..edbde76f 100644 --- a/include/boost/redis/response.hpp +++ b/include/boost/redis/response.hpp @@ -31,6 +31,15 @@ using response = std::tuple...>; */ using generic_response = adapter::result>; +/** + * Forward declaration to allow friendship with the template class + * that manages filling of flat_response_value. + */ +namespace adapter::detail { +template +class general_aggregate; +} + struct flat_response_value { public: /// Reserve capacity for nodes and data storage. @@ -43,24 +52,6 @@ struct flat_response_value { std::vector const& view() const { return view_; } std::vector& view() { return view_; } - template - void push_back(const resp3::basic_node& nd) - { - resp3::offset_string offset_string; - offset_string.offset = data_.size(); - offset_string.size = nd.value.size(); - - data_.append(nd.value.data(), nd.value.size()); - - resp3::offset_node new_node; - new_node.data_type = nd.data_type; - new_node.aggregate_size = nd.aggregate_size; - new_node.depth = nd.depth; - new_node.value = std::move(offset_string); - - view_.push_back(std::move(new_node)); - } - void set_view() { for (auto& node : view_) { @@ -72,6 +63,9 @@ struct flat_response_value { } private: + template + friend class adapter::detail::general_aggregate; + std::string data_; std::vector view_; }; From 79e262ac2e246d1073d8d40c1d772880db05e595 Mon Sep 17 00:00:00 2001 From: Nikolai Vladimirov Date: Sat, 12 Jul 2025 14:23:42 +0000 Subject: [PATCH 08/14] Avoid turning throwing consume_one into template --- include/boost/redis/impl/response.ipp | 16 ++++++++++++++++ include/boost/redis/response.hpp | 12 +++--------- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/include/boost/redis/impl/response.ipp b/include/boost/redis/impl/response.ipp index 19194c68..21e7b287 100644 --- a/include/boost/redis/impl/response.ipp +++ b/include/boost/redis/impl/response.ipp @@ -59,4 +59,20 @@ void consume_one(generic_response& r, system::error_code& ec) { consume_one_impl void consume_one(generic_flat_response& r, system::error_code& ec) { consume_one_impl(r, ec); } +void consume_one(generic_response& r) +{ + system::error_code ec; + consume_one(r, ec); + if (ec) + throw system::system_error(ec); +} + +void consume_one(generic_flat_response& r) +{ + system::error_code ec; + consume_one(r, ec); + if (ec) + throw system::system_error(ec); +} + } // namespace boost::redis diff --git a/include/boost/redis/response.hpp b/include/boost/redis/response.hpp index edbde76f..f4927007 100644 --- a/include/boost/redis/response.hpp +++ b/include/boost/redis/response.hpp @@ -122,18 +122,12 @@ void consume_one(generic_response& r, system::error_code& ec); void consume_one(generic_flat_response& r, system::error_code& ec); /** - * @brief Throwing overload of `consume_one`. + * @brief Throwing overloads of `consume_one`. * * @param r The response to modify. */ -template -void consume_one(Response& r) -{ - system::error_code ec; - consume_one(r, ec); - if (ec) - throw system::system_error(ec); -} +void consume_one(generic_response& r); +void consume_one(generic_flat_response& r); } // namespace boost::redis From 2d866f15c39d877cdc5ffab7905860352bdd1f72 Mon Sep 17 00:00:00 2001 From: Nikolai Vladimirov Date: Sat, 12 Jul 2025 15:42:42 +0000 Subject: [PATCH 09/14] Get rid of type erasion in details::prepare_done --- include/boost/redis/adapter/any_adapter.hpp | 2 +- include/boost/redis/adapter/detail/adapters.hpp | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/include/boost/redis/adapter/any_adapter.hpp b/include/boost/redis/adapter/any_adapter.hpp index ed80aa7d..32718f1d 100644 --- a/include/boost/redis/adapter/any_adapter.hpp +++ b/include/boost/redis/adapter/any_adapter.hpp @@ -36,7 +36,7 @@ class any_adapter { public: using adapt_fn_type = std::function< void(std::size_t, resp3::node_view const&, system::error_code&)>; - using done_fn_type = adapter::detail::done_fn_type; + using done_fn_type = std::function; struct impl_t { adapt_fn_type adapt_fn; diff --git a/include/boost/redis/adapter/detail/adapters.hpp b/include/boost/redis/adapter/detail/adapters.hpp index eb7a8e53..5bf415b0 100644 --- a/include/boost/redis/adapter/detail/adapters.hpp +++ b/include/boost/redis/adapter/detail/adapters.hpp @@ -139,16 +139,14 @@ void boost_redis_from_bulk(T& t, resp3::basic_node const& node, system:: //================================================ -using done_fn_type = std::function; - template -auto prepare_done(T&) noexcept -> done_fn_type +auto prepare_done(T&) noexcept { return [] { }; } template -auto prepare_done(generic_flat_response& resp) noexcept -> done_fn_type +auto prepare_done(generic_flat_response& resp) noexcept { return [resp]() mutable { if (resp.has_value()) { From 51add319925874a5dd57fbfff23b005267d3c566 Mon Sep 17 00:00:00 2001 From: Nikolai Vladimirov Date: Sat, 12 Jul 2025 16:22:00 +0000 Subject: [PATCH 10/14] Added API for generic_flat_response, replaced generic_response with new response type in tests --- include/boost/redis/adapter/any_adapter.hpp | 2 +- include/boost/redis/adapter/detail/adapters.hpp | 13 +++++++------ include/boost/redis/resp3/node.hpp | 4 ++++ include/boost/redis/response.hpp | 12 ++++++++++++ test/test_any_adapter.cpp | 3 +++ test/test_conn_check_health.cpp | 8 ++++---- test/test_conn_exec.cpp | 6 +++--- test/test_conn_exec_cancel.cpp | 2 +- test/test_conn_exec_cancel2.cpp | 4 ++-- test/test_conn_exec_error.cpp | 4 ++-- test/test_reader_fsm.cpp | 12 ++++++------ 11 files changed, 45 insertions(+), 25 deletions(-) diff --git a/include/boost/redis/adapter/any_adapter.hpp b/include/boost/redis/adapter/any_adapter.hpp index 32718f1d..a6ea29ff 100644 --- a/include/boost/redis/adapter/any_adapter.hpp +++ b/include/boost/redis/adapter/any_adapter.hpp @@ -50,7 +50,7 @@ class any_adapter { using namespace boost::redis::adapter; auto adapter = boost_redis_adapt(resp); std::size_t size = adapter.get_supported_response_size(); - return {std::move(adapter), detail::prepare_done(resp), size}; + return {std::move(adapter), detail::prepare_done(&resp), size}; } template diff --git a/include/boost/redis/adapter/detail/adapters.hpp b/include/boost/redis/adapter/detail/adapters.hpp index 5bf415b0..3167c61a 100644 --- a/include/boost/redis/adapter/detail/adapters.hpp +++ b/include/boost/redis/adapter/detail/adapters.hpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -140,17 +141,17 @@ void boost_redis_from_bulk(T& t, resp3::basic_node const& node, system:: //================================================ template -auto prepare_done(T&) noexcept +inline auto prepare_done(T*) noexcept { return [] { }; } -template -auto prepare_done(generic_flat_response& resp) noexcept +template <> +inline auto prepare_done(generic_flat_response* resp) noexcept { - return [resp]() mutable { - if (resp.has_value()) { - resp.value().set_view(); + return [resp] { + if (resp->has_value()) { + resp->value().set_view(); } }; } diff --git a/include/boost/redis/resp3/node.hpp b/include/boost/redis/resp3/node.hpp index 85147b1c..34a99aa6 100644 --- a/include/boost/redis/resp3/node.hpp +++ b/include/boost/redis/resp3/node.hpp @@ -63,8 +63,12 @@ struct offset_string { std::string_view data; std::size_t offset{}; std::size_t size{}; + + operator std::string() const { return std::string{data}; } }; +inline std::ostream& operator<<(std::ostream& os, offset_string const& s) { return os << s.data; } + using offset_node = basic_node; } // namespace boost::redis::resp3 diff --git a/include/boost/redis/response.hpp b/include/boost/redis/response.hpp index f4927007..77f0feee 100644 --- a/include/boost/redis/response.hpp +++ b/include/boost/redis/response.hpp @@ -49,6 +49,18 @@ struct flat_response_value { view_.reserve(num_nodes); } + void clear() + { + data_.clear(); + view_.clear(); + } + + std::size_t size() const noexcept { return view_.size(); } + bool empty() noexcept { return view_.empty(); } + + resp3::offset_node& at(std::size_t index) { return view_.at(index); } + resp3::offset_node const& at(std::size_t index) const { return view_.at(index); } + std::vector const& view() const { return view_; } std::vector& view() { return view_; } diff --git a/test/test_any_adapter.cpp b/test/test_any_adapter.cpp index b360b744..b0c9a55f 100644 --- a/test/test_any_adapter.cpp +++ b/test/test_any_adapter.cpp @@ -13,6 +13,7 @@ #include using boost::redis::generic_response; +using boost::redis::generic_flat_response; using boost::redis::response; using boost::redis::ignore; using boost::redis::any_adapter; @@ -23,10 +24,12 @@ BOOST_AUTO_TEST_CASE(any_adapter_response_types) response r1; response r2; generic_response r3; + generic_flat_response r4; BOOST_CHECK_NO_THROW(any_adapter{r1}); BOOST_CHECK_NO_THROW(any_adapter{r2}); BOOST_CHECK_NO_THROW(any_adapter{r3}); + BOOST_CHECK_NO_THROW(any_adapter{r4}); BOOST_CHECK_NO_THROW(any_adapter{ignore}); } diff --git a/test/test_conn_check_health.cpp b/test/test_conn_check_health.cpp index 78efa82c..62ff3f96 100644 --- a/test/test_conn_check_health.cpp +++ b/test/test_conn_check_health.cpp @@ -23,7 +23,7 @@ using connection = boost::redis::connection; using boost::redis::request; using boost::redis::ignore; using boost::redis::operation; -using boost::redis::generic_response; +using boost::redis::generic_flat_response; using boost::redis::consume_one; using namespace std::chrono_literals; @@ -34,7 +34,7 @@ namespace { struct push_callback { connection* conn1; connection* conn2; - generic_response* resp2; + generic_flat_response* resp2; request* req1; int i = 0; boost::asio::coroutine coro{}; @@ -52,7 +52,7 @@ struct push_callback { BOOST_TEST(resp2->has_value()); BOOST_TEST(!resp2->value().empty()); - std::clog << "Event> " << resp2->value().front().value << std::endl; + std::clog << "Event> " << resp2->value().view().front().value << std::endl; consume_one(*resp2); ++i; @@ -110,7 +110,7 @@ BOOST_AUTO_TEST_CASE(check_health) request req2; req2.push("MONITOR"); - generic_response resp2; + generic_flat_response resp2; conn2.set_receive_response(resp2); conn2.async_exec(req2, ignore, [&exec_finished](error_code ec, std::size_t) { diff --git a/test/test_conn_exec.cpp b/test/test_conn_exec.cpp index e02c3bce..5bab3f5d 100644 --- a/test/test_conn_exec.cpp +++ b/test/test_conn_exec.cpp @@ -26,7 +26,7 @@ namespace net = boost::asio; using boost::redis::config; using boost::redis::connection; -using boost::redis::generic_response; +using boost::redis::generic_flat_response; using boost::redis::ignore; using boost::redis::operation; using boost::redis::request; @@ -152,7 +152,7 @@ BOOST_AUTO_TEST_CASE(correct_database) request req; req.push("CLIENT", "LIST"); - generic_response resp; + generic_flat_response resp; bool exec_finished = false, run_finished = false; @@ -173,7 +173,7 @@ BOOST_AUTO_TEST_CASE(correct_database) BOOST_TEST_REQUIRE(run_finished); BOOST_TEST_REQUIRE(!resp.value().empty()); - auto const& value = resp.value().front().value; + std::string value = resp.value().view().front().value; auto const pos = value.find("db="); auto const index_str = value.substr(pos + 3, 1); auto const index = std::stoi(index_str); diff --git a/test/test_conn_exec_cancel.cpp b/test/test_conn_exec_cancel.cpp index 9c32a8cb..3e84218c 100644 --- a/test/test_conn_exec_cancel.cpp +++ b/test/test_conn_exec_cancel.cpp @@ -38,7 +38,7 @@ using boost::redis::operation; using boost::redis::error; using boost::redis::request; using boost::redis::response; -using boost::redis::generic_response; +using boost::redis::generic_flat_response; using boost::redis::ignore; using boost::redis::ignore_t; using boost::redis::logger; diff --git a/test/test_conn_exec_cancel2.cpp b/test/test_conn_exec_cancel2.cpp index 7b745fc6..08337461 100644 --- a/test/test_conn_exec_cancel2.cpp +++ b/test/test_conn_exec_cancel2.cpp @@ -26,7 +26,7 @@ using error_code = boost::system::error_code; using boost::redis::operation; using boost::redis::request; using boost::redis::response; -using boost::redis::generic_response; +using boost::redis::generic_flat_response; using boost::redis::ignore; using boost::redis::ignore_t; using boost::redis::config; @@ -40,7 +40,7 @@ auto async_ignore_explicit_cancel_of_req_written() -> net::awaitable { auto ex = co_await net::this_coro::executor; - generic_response gresp; + generic_flat_response gresp; auto conn = std::make_shared(ex); run(conn); diff --git a/test/test_conn_exec_error.cpp b/test/test_conn_exec_error.cpp index 183ce509..94b912a6 100644 --- a/test/test_conn_exec_error.cpp +++ b/test/test_conn_exec_error.cpp @@ -21,7 +21,7 @@ using error_code = boost::system::error_code; using boost::redis::connection; using boost::redis::request; using boost::redis::response; -using boost::redis::generic_response; +using boost::redis::generic_flat_response; using boost::redis::ignore; using boost::redis::ignore_t; using boost::redis::error; @@ -266,7 +266,7 @@ BOOST_AUTO_TEST_CASE(subscriber_wrong_syntax) conn->async_exec(req1, ignore, c1); - generic_response gresp; + generic_flat_response gresp; conn->set_receive_response(gresp); auto c3 = [&](error_code ec, std::size_t) { diff --git a/test/test_reader_fsm.cpp b/test/test_reader_fsm.cpp index 4aa0c430..8ff1ed75 100644 --- a/test/test_reader_fsm.cpp +++ b/test/test_reader_fsm.cpp @@ -18,7 +18,7 @@ using boost::system::error_code; using net::cancellation_type_t; using redis::detail::reader_fsm; using redis::detail::multiplexer; -using redis::generic_response; +using redis::generic_flat_response; using action = redis::detail::reader_fsm::action; namespace boost::redis::detail { @@ -38,7 +38,7 @@ namespace { void test_push() { multiplexer mpx; - generic_response resp; + generic_flat_response resp; mpx.set_receive_response(resp); reader_fsm fsm{mpx}; error_code ec; @@ -83,7 +83,7 @@ void test_push() void test_read_needs_more() { multiplexer mpx; - generic_response resp; + generic_flat_response resp; mpx.set_receive_response(resp); reader_fsm fsm{mpx}; error_code ec; @@ -128,7 +128,7 @@ void test_read_needs_more() void test_read_error() { multiplexer mpx; - generic_response resp; + generic_flat_response resp; mpx.set_receive_response(resp); reader_fsm fsm{mpx}; error_code ec; @@ -158,7 +158,7 @@ void test_read_error() void test_parse_error() { multiplexer mpx; - generic_response resp; + generic_flat_response resp; mpx.set_receive_response(resp); reader_fsm fsm{mpx}; error_code ec; @@ -188,7 +188,7 @@ void test_parse_error() void test_push_deliver_error() { multiplexer mpx; - generic_response resp; + generic_flat_response resp; mpx.set_receive_response(resp); reader_fsm fsm{mpx}; error_code ec; From babbbdd129aee18700bb75177a1e568f9330d797 Mon Sep 17 00:00:00 2001 From: Nikolai Vladimirov Date: Mon, 18 Aug 2025 17:17:47 +0000 Subject: [PATCH 11/14] Adjust code base to new changes --- example/cpp20_chat_room.cpp | 4 +-- example/cpp20_streams.cpp | 6 ++--- .../boost/redis/adapter/detail/adapters.hpp | 27 +++++-------------- include/boost/redis/response.hpp | 2 +- 4 files changed, 12 insertions(+), 27 deletions(-) diff --git a/example/cpp20_chat_room.cpp b/example/cpp20_chat_room.cpp index 17dfd0f5..3e3a0d17 100644 --- a/example/cpp20_chat_room.cpp +++ b/example/cpp20_chat_room.cpp @@ -31,7 +31,7 @@ using boost::asio::redirect_error; using boost::asio::use_awaitable; using boost::redis::config; using boost::redis::connection; -using boost::redis::generic_response; +using boost::redis::generic_flat_response; using boost::redis::ignore; using boost::redis::request; using boost::system::error_code; @@ -45,7 +45,7 @@ auto receiver(std::shared_ptr conn) -> awaitable request req; req.push("SUBSCRIBE", "channel"); - generic_response resp; + generic_flat_response resp; conn->set_receive_response(resp); while (conn->will_reconnect()) { diff --git a/example/cpp20_streams.cpp b/example/cpp20_streams.cpp index 1cef143f..14c14087 100644 --- a/example/cpp20_streams.cpp +++ b/example/cpp20_streams.cpp @@ -23,7 +23,7 @@ namespace net = boost::asio; using boost::redis::config; -using boost::redis::generic_response; +using boost::redis::generic_flat_response; using boost::redis::operation; using boost::redis::request; using boost::redis::connection; @@ -33,7 +33,7 @@ auto stream_reader(std::shared_ptr conn) -> net::awaitable { std::string redisStreamKey_; request req; - generic_response resp; + generic_flat_response resp; std::string stream_id{"$"}; std::string const field = "myfield"; @@ -51,7 +51,7 @@ auto stream_reader(std::shared_ptr conn) -> net::awaitable // The following approach was taken in order to be able to // deal with the responses, as generated by redis in the case // that there are multiple stream 'records' within a single - // generic_response. The nesting and number of values in + // generic_flat_response. The nesting and number of values in // resp.value() are different, depending on the contents // of the stream in redis. Uncomment the above commented-out // code for examples while running the XADD command. diff --git a/include/boost/redis/adapter/detail/adapters.hpp b/include/boost/redis/adapter/detail/adapters.hpp index 8e74aaf8..553bb817 100644 --- a/include/boost/redis/adapter/detail/adapters.hpp +++ b/include/boost/redis/adapter/detail/adapters.hpp @@ -138,26 +138,6 @@ void boost_redis_from_bulk(T& t, resp3::basic_node const& node, system:: from_bulk_impl::apply(t, node, ec); } -//================================================ - -template -inline auto prepare_done(T*) noexcept -{ - return [] { }; -} - -template <> -inline auto prepare_done(generic_flat_response* resp) noexcept -{ - return [resp] { - if (resp->has_value()) { - resp->value().set_view(); - } - }; -} - -//================================================ - template class general_aggregate { private: @@ -205,7 +185,12 @@ class general_aggregate> { { } void on_init() { } - void on_done() { } + void on_done() + { + if (result_->has_value()) { + result_->value().set_view(); + } + } template void on_node(resp3::basic_node const& nd, system::error_code&) diff --git a/include/boost/redis/response.hpp b/include/boost/redis/response.hpp index 3053bbae..350d864f 100644 --- a/include/boost/redis/response.hpp +++ b/include/boost/redis/response.hpp @@ -64,6 +64,7 @@ struct flat_response_value { std::vector const& view() const { return view_; } std::vector& view() { return view_; } +private: void set_view() { for (auto& node : view_) { @@ -74,7 +75,6 @@ struct flat_response_value { } } -private: template friend class adapter::detail::general_aggregate; From 3ae996d51e095c1a15406d658392094674b39488 Mon Sep 17 00:00:00 2001 From: Nikolai Vladimirov Date: Mon, 18 Aug 2025 19:35:48 +0000 Subject: [PATCH 12/14] Replaced generic_response with generic_flat_response in tests --- example/cpp20_subscriber.cpp | 5 +---- test/test_low_level.cpp | 5 +++-- test/test_low_level_sync_sans_io.cpp | 20 ++++++++++---------- test/test_reader_fsm.cpp | 6 +++--- 4 files changed, 17 insertions(+), 19 deletions(-) diff --git a/example/cpp20_subscriber.cpp b/example/cpp20_subscriber.cpp index 09536dd1..40017835 100644 --- a/example/cpp20_subscriber.cpp +++ b/example/cpp20_subscriber.cpp @@ -22,7 +22,6 @@ namespace asio = boost::asio; using namespace std::chrono_literals; using boost::redis::request; -using boost::redis::generic_response; using boost::redis::generic_flat_response; using boost::redis::consume_one; using boost::redis::logger; @@ -55,9 +54,7 @@ auto receiver(std::shared_ptr conn) -> asio::awaitable request req; req.push("SUBSCRIBE", "channel"); - // Alternatively, you can use generic_flat_response here, but keep in mind - // that to access elements you need to call .view() on resp.value() - generic_response resp; + generic_flat_response resp; conn->set_receive_response(resp); // Loop while reconnection is enabled diff --git a/test/test_low_level.cpp b/test/test_low_level.cpp index c173d750..5d5dab2e 100644 --- a/test/test_low_level.cpp +++ b/test/test_low_level.cpp @@ -29,6 +29,7 @@ using boost::system::error_code; using boost::redis::request; using boost::redis::response; using boost::redis::generic_response; +using boost::redis::generic_flat_response; using boost::redis::ignore; using boost::redis::ignore_t; using boost::redis::adapter::result; @@ -633,7 +634,7 @@ BOOST_AUTO_TEST_CASE(cancel_one_1) BOOST_AUTO_TEST_CASE(cancel_one_empty) { - generic_response resp; + generic_flat_response resp; BOOST_TEST(resp.has_value()); consume_one(resp); @@ -642,7 +643,7 @@ BOOST_AUTO_TEST_CASE(cancel_one_empty) BOOST_AUTO_TEST_CASE(cancel_one_has_error) { - generic_response resp = boost::redis::adapter::error{resp3::type::simple_string, {}}; + generic_flat_response resp = boost::redis::adapter::error{resp3::type::simple_string, {}}; BOOST_TEST(resp.has_error()); consume_one(resp); diff --git a/test/test_low_level_sync_sans_io.cpp b/test/test_low_level_sync_sans_io.cpp index 9b198c4b..e169eb5c 100644 --- a/test/test_low_level_sync_sans_io.cpp +++ b/test/test_low_level_sync_sans_io.cpp @@ -25,7 +25,7 @@ using boost::redis::adapter::result; using boost::redis::config; using boost::redis::detail::multiplexer; using boost::redis::detail::push_hello; -using boost::redis::generic_response; +using boost::redis::generic_flat_response; using boost::redis::ignore_t; using boost::redis::request; using boost::redis::resp3::detail::deserialize; @@ -264,7 +264,7 @@ std::ostream& operator<<(std::ostream& os, node const& nd) BOOST_AUTO_TEST_CASE(multiplexer_push) { multiplexer mpx; - generic_response resp; + generic_flat_response resp; mpx.set_receive_adapter(any_adapter{resp}); boost::system::error_code ec; @@ -276,17 +276,17 @@ BOOST_AUTO_TEST_CASE(multiplexer_push) // TODO: Provide operator << for generic_response so we can compare // the whole vector. BOOST_CHECK_EQUAL(resp.value().size(), 3u); - BOOST_CHECK_EQUAL(resp.value().at(1).value, "one"); - BOOST_CHECK_EQUAL(resp.value().at(2).value, "two"); + BOOST_CHECK_EQUAL(resp.value().at(1).value.data, "one"); + BOOST_CHECK_EQUAL(resp.value().at(2).value.data, "two"); - for (auto const& e : resp.value()) - std::cout << e << std::endl; + for (auto const& e : resp.value().view()) + std::cout << e.value.data << std::endl; } BOOST_AUTO_TEST_CASE(multiplexer_push_needs_more) { multiplexer mpx; - generic_response resp; + generic_flat_response resp; mpx.set_receive_adapter(any_adapter{resp}); std::string msg; @@ -307,13 +307,13 @@ BOOST_AUTO_TEST_CASE(multiplexer_push_needs_more) // TODO: Provide operator << for generic_response so we can compare // the whole vector. BOOST_CHECK_EQUAL(resp.value().size(), 3u); - BOOST_CHECK_EQUAL(resp.value().at(1).value, "one"); - BOOST_CHECK_EQUAL(resp.value().at(2).value, "two"); + BOOST_CHECK_EQUAL(resp.value().at(1).value.data, "one"); + BOOST_CHECK_EQUAL(resp.value().at(2).value.data, "two"); } struct test_item { request req; - generic_response resp; + generic_flat_response resp; std::shared_ptr elem_ptr; bool done = false; diff --git a/test/test_reader_fsm.cpp b/test/test_reader_fsm.cpp index 8c75a92f..f8cdef8a 100644 --- a/test/test_reader_fsm.cpp +++ b/test/test_reader_fsm.cpp @@ -23,7 +23,7 @@ using redis::detail::reader_fsm; using redis::detail::multiplexer; using redis::generic_flat_response; using redis::detail::read_buffer; -using redis::generic_response; +using redis::generic_flat_response; using redis::any_adapter; using action = redis::detail::reader_fsm::action; @@ -170,7 +170,7 @@ void test_parse_error() { read_buffer rbuf; multiplexer mpx; - generic_response resp; + generic_flat_response resp; mpx.set_receive_adapter(any_adapter{resp}); reader_fsm fsm{rbuf, mpx}; error_code ec; @@ -237,7 +237,7 @@ void test_max_read_buffer_size() read_buffer rbuf; rbuf.set_config({5, 7}); multiplexer mpx; - generic_response resp; + generic_flat_response resp; mpx.set_receive_adapter(any_adapter{resp}); reader_fsm fsm{rbuf, mpx}; error_code ec; From b228fd5410eba9e6efc3d9b9d2f615dab3e433df Mon Sep 17 00:00:00 2001 From: Nikolai Vladimirov Date: Mon, 18 Aug 2025 19:48:09 +0000 Subject: [PATCH 13/14] create flat_response_value::add_node() --- .../boost/redis/adapter/detail/adapters.hpp | 17 +--------------- include/boost/redis/response.hpp | 20 ++++++++++++++++++- 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/include/boost/redis/adapter/detail/adapters.hpp b/include/boost/redis/adapter/detail/adapters.hpp index 553bb817..ea0d655c 100644 --- a/include/boost/redis/adapter/detail/adapters.hpp +++ b/include/boost/redis/adapter/detail/adapters.hpp @@ -204,22 +204,7 @@ class general_aggregate> { std::string{std::cbegin(nd.value), std::cend(nd.value)} }; break; - default: - auto& data = result_->value().data_; - - resp3::offset_string offset_string; - offset_string.offset = data.size(); - offset_string.size = nd.value.size(); - - data.append(nd.value.data(), nd.value.size()); - - resp3::offset_node new_node; - new_node.data_type = nd.data_type; - new_node.aggregate_size = nd.aggregate_size; - new_node.depth = nd.depth; - new_node.value = std::move(offset_string); - - result_->value().view_.push_back(std::move(new_node)); + default: result_->value().add_node(nd); } } }; diff --git a/include/boost/redis/response.hpp b/include/boost/redis/response.hpp index 350d864f..f22e672a 100644 --- a/include/boost/redis/response.hpp +++ b/include/boost/redis/response.hpp @@ -75,7 +75,25 @@ struct flat_response_value { } } - template + template + void add_node(resp3::basic_node const& nd) + { + resp3::offset_string offset_string; + offset_string.offset = data_.size(); + offset_string.size = nd.value.size(); + + data_.append(nd.value.data(), nd.value.size()); + + resp3::offset_node new_node; + new_node.data_type = nd.data_type; + new_node.aggregate_size = nd.aggregate_size; + new_node.depth = nd.depth; + new_node.value = std::move(offset_string); + + view_.push_back(std::move(new_node)); + } + + template friend class adapter::detail::general_aggregate; std::string data_; From d8bf31da264394ae6380a6d1e21c81abf8c2039c Mon Sep 17 00:00:00 2001 From: Nikolai Vladimirov Date: Mon, 18 Aug 2025 19:57:51 +0000 Subject: [PATCH 14/14] Cosmetic changes --- include/boost/redis/resp3/node.hpp | 7 +++++-- include/boost/redis/response.hpp | 2 +- test/test_low_level_sync_sans_io.cpp | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/include/boost/redis/resp3/node.hpp b/include/boost/redis/resp3/node.hpp index 34a99aa6..84fb5fca 100644 --- a/include/boost/redis/resp3/node.hpp +++ b/include/boost/redis/resp3/node.hpp @@ -65,9 +65,12 @@ struct offset_string { std::size_t size{}; operator std::string() const { return std::string{data}; } -}; -inline std::ostream& operator<<(std::ostream& os, offset_string const& s) { return os << s.data; } + friend std::ostream& operator<<(std::ostream& os, offset_string const& s) + { + return os << s.data; + } +}; using offset_node = basic_node; diff --git a/include/boost/redis/response.hpp b/include/boost/redis/response.hpp index f22e672a..304ca1e6 100644 --- a/include/boost/redis/response.hpp +++ b/include/boost/redis/response.hpp @@ -36,7 +36,7 @@ using generic_response = adapter::result>; * that manages filling of flat_response_value. */ namespace adapter::detail { -template +template class general_aggregate; } diff --git a/test/test_low_level_sync_sans_io.cpp b/test/test_low_level_sync_sans_io.cpp index e169eb5c..6a0e39dd 100644 --- a/test/test_low_level_sync_sans_io.cpp +++ b/test/test_low_level_sync_sans_io.cpp @@ -280,7 +280,7 @@ BOOST_AUTO_TEST_CASE(multiplexer_push) BOOST_CHECK_EQUAL(resp.value().at(2).value.data, "two"); for (auto const& e : resp.value().view()) - std::cout << e.value.data << std::endl; + std::cout << e.value << std::endl; } BOOST_AUTO_TEST_CASE(multiplexer_push_needs_more)