From 1f3ef6b486cad18032568ed0ff85ea668f46302b Mon Sep 17 00:00:00 2001 From: Marcelo Zimbres Date: Sun, 19 Feb 2023 20:26:19 +0100 Subject: [PATCH] Renames node to basic_node. --- CMakeLists.txt | 4 +- README.md | 6 +- examples/common/common.cpp | 2 +- examples/common/common.hpp | 2 +- examples/cpp20_chat_room.cpp | 2 +- examples/cpp20_echo_server.cpp | 2 +- examples/cpp20_subscriber.cpp | 2 +- .../boost/redis/adapter/detail/adapters.hpp | 24 ++--- .../redis/adapter/detail/response_traits.hpp | 15 ++- .../redis/adapter/detail/result_traits.hpp | 12 +-- include/boost/redis/adapter/ignore.hpp | 2 +- include/boost/redis/detail/connection_ops.hpp | 8 +- include/boost/redis/{ => detail}/read.hpp | 11 +- include/boost/redis/{ => detail}/write.hpp | 25 ++--- include/boost/redis/request.hpp | 102 +++++++++++++----- .../boost/redis/resp3/impl/serialization.ipp | 42 ++++++++ include/boost/redis/resp3/impl/type.ipp | 70 +----------- include/boost/redis/resp3/node.hpp | 9 +- include/boost/redis/resp3/parser.hpp | 2 +- include/boost/redis/resp3/serialization.hpp | 73 ++++--------- include/boost/redis/resp3/type.hpp | 70 +++++++++++- include/boost/redis/response.hpp | 2 +- include/boost/redis/src.hpp | 1 + tests/conn_exec.cpp | 2 + tests/conn_push.cpp | 2 +- {examples => tests}/cpp17_low_level_sync.cpp | 10 +- {examples => tests}/cpp20_low_level_async.cpp | 10 +- tests/issue_50.cpp | 2 +- tests/low_level.cpp | 18 ++-- tests/request.cpp | 1 + 30 files changed, 291 insertions(+), 242 deletions(-) rename include/boost/redis/{ => detail}/read.hpp (96%) rename include/boost/redis/{ => detail}/write.hpp (77%) create mode 100644 include/boost/redis/resp3/impl/serialization.ipp rename {examples => tests}/cpp17_low_level_sync.cpp (85%) rename {examples => tests}/cpp20_low_level_async.cpp (83%) diff --git a/CMakeLists.txt b/CMakeLists.txt index d964a77a..588b3279 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -161,7 +161,7 @@ if (MSVC) target_compile_definitions(cpp20_intro_tls PRIVATE _WIN32_WINNT=0x0601) endif() -add_executable(cpp20_low_level_async examples/cpp20_low_level_async.cpp) +add_executable(cpp20_low_level_async tests/cpp20_low_level_async.cpp) target_compile_features(cpp20_low_level_async PUBLIC cxx_std_20) add_test(cpp20_low_level_async cpp20_low_level_async) target_link_libraries(cpp20_low_level_async common) @@ -184,7 +184,7 @@ if (MSVC) target_compile_definitions(echo_server_direct PRIVATE _WIN32_WINNT=0x0601) endif() -add_executable(cpp17_low_level_sync examples/cpp17_low_level_sync.cpp) +add_executable(cpp17_low_level_sync tests/cpp17_low_level_sync.cpp) target_compile_features(cpp17_low_level_sync PUBLIC cxx_std_17) add_test(cpp17_low_level_sync cpp17_low_level_sync) if (MSVC) diff --git a/README.md b/README.md index 3076add4..da615b2a 100644 --- a/README.md +++ b/README.md @@ -844,7 +844,11 @@ in no more than one source file in your applications. To build the examples and tests cmake is supported, for example ```cpp -BOOST_ROOT=/opt/boost_1_80_0 cmake --preset dev +# Linux +$ BOOST_ROOT=/opt/boost_1_80_0 cmake --preset dev + +# Windows +$ cmake -G "Visual Studio 17 2022" -A x64 -B bin64 -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake ``` ## Acknowledgement diff --git a/examples/common/common.cpp b/examples/common/common.cpp index 1f305c32..3a3a2532 100644 --- a/examples/common/common.cpp +++ b/examples/common/common.cpp @@ -24,7 +24,7 @@ auto redir(boost::system::error_code& ec) { return net::redirect_error(net::use_awaitable, ec); } } -auto healthy_checker(std::shared_ptr conn) -> net::awaitable +auto health_check(std::shared_ptr conn) -> net::awaitable { try { request req; diff --git a/examples/common/common.hpp b/examples/common/common.hpp index 3efbcea5..4b00ec47 100644 --- a/examples/common/common.hpp +++ b/examples/common/common.hpp @@ -26,7 +26,7 @@ connect( std::string const& host, std::string const& port) -> boost::asio::awaitable; -auto healthy_checker(std::shared_ptr conn) -> boost::asio::awaitable; +auto health_check(std::shared_ptr conn) -> boost::asio::awaitable; auto run(boost::asio::awaitable op) -> int; diff --git a/examples/cpp20_chat_room.cpp b/examples/cpp20_chat_room.cpp index 61b13ae7..d770fcc8 100644 --- a/examples/cpp20_chat_room.cpp +++ b/examples/cpp20_chat_room.cpp @@ -60,7 +60,7 @@ auto co_main(std::string host, std::string port) -> net::awaitable co_await connect(conn, host, port); co_await ((conn->async_run() || publisher(stream, conn) || receiver(conn) || - healthy_checker(conn) || sig.async_wait()) && conn->async_exec(req)); + health_check(conn) || sig.async_wait()) && conn->async_exec(req)); } #else // defined(BOOST_ASIO_HAS_POSIX_STREAM_DESCRIPTOR) diff --git a/examples/cpp20_echo_server.cpp b/examples/cpp20_echo_server.cpp index dc53e2b8..b21d24d5 100644 --- a/examples/cpp20_echo_server.cpp +++ b/examples/cpp20_echo_server.cpp @@ -54,7 +54,7 @@ auto co_main(std::string host, std::string port) -> net::awaitable req.push("HELLO", 3); co_await connect(conn, host, port); - co_await ((conn->async_run() || listener(conn) || healthy_checker(conn) || + co_await ((conn->async_run() || listener(conn) || health_check(conn) || sig.async_wait()) && conn->async_exec(req)); } diff --git a/examples/cpp20_subscriber.cpp b/examples/cpp20_subscriber.cpp index a0b0a52e..ce578206 100644 --- a/examples/cpp20_subscriber.cpp +++ b/examples/cpp20_subscriber.cpp @@ -56,7 +56,7 @@ auto co_main(std::string host, std::string port) -> net::awaitable // The loop will reconnect on connection lost. To exit type Ctrl-C twice. for (;;) { co_await connect(conn, host, port); - co_await ((conn->async_run() || healthy_checker(conn) || receiver(conn)) && conn->async_exec(req)); + co_await ((conn->async_run() || health_check(conn) || receiver(conn)) && conn->async_exec(req)); conn->reset_stream(); timer.expires_after(std::chrono::seconds{1}); diff --git a/include/boost/redis/adapter/detail/adapters.hpp b/include/boost/redis/adapter/detail/adapters.hpp index fe26c10f..b0579aa7 100644 --- a/include/boost/redis/adapter/detail/adapters.hpp +++ b/include/boost/redis/adapter/detail/adapters.hpp @@ -75,7 +75,7 @@ private: public: explicit general_aggregate(Result* c = nullptr): result_(c) {} - void operator()(resp3::node const& nd, system::error_code&) + void operator()(resp3::basic_node const& nd, system::error_code&) { BOOST_ASSERT_MSG(!!result_, "Unexpected null pointer"); switch (nd.data_type) { @@ -97,7 +97,7 @@ private: public: explicit general_simple(Node* t = nullptr) : result_(t) {} - void operator()(resp3::node const& nd, system::error_code&) + void operator()(resp3::basic_node const& nd, system::error_code&) { BOOST_ASSERT_MSG(!!result_, "Unexpected null pointer"); switch (nd.data_type) { @@ -122,7 +122,7 @@ public: void operator()( Result& result, - resp3::node const& n, + resp3::basic_node const& n, system::error_code& ec) { if (is_aggregate(n.data_type)) { @@ -146,7 +146,7 @@ public: void operator()( Result& result, - resp3::node const& nd, + resp3::basic_node const& nd, system::error_code& ec) { if (is_aggregate(nd.data_type)) { @@ -181,7 +181,7 @@ public: void operator()( Result& result, - resp3::node const& nd, + resp3::basic_node const& nd, system::error_code& ec) { if (is_aggregate(nd.data_type)) { @@ -219,7 +219,7 @@ public: void operator()( Result& result, - resp3::node const& nd, + resp3::basic_node const& nd, system::error_code& ec) { if (is_aggregate(nd.data_type)) { @@ -243,7 +243,7 @@ public: void operator()( Result& result, - resp3::node const& nd, + resp3::basic_node const& nd, system::error_code& ec) { if (is_aggregate(nd.data_type)) { @@ -278,7 +278,7 @@ struct list_impl { void operator()( Result& result, - resp3::node const& nd, + resp3::basic_node const& nd, system::error_code& ec) { if (!is_aggregate(nd.data_type)) { @@ -348,7 +348,7 @@ private: response_type* result_; typename impl_map::type impl_; - bool set_if_resp3_error(resp3::node const& nd) noexcept + bool set_if_resp3_error(resp3::basic_node const& nd) noexcept { switch (nd.data_type) { case resp3::type::null: @@ -372,7 +372,7 @@ public: void operator()( - resp3::node const& nd, + resp3::basic_node const& nd, system::error_code& ec) { BOOST_ASSERT_MSG(!!result_, "Unexpected null pointer"); @@ -397,7 +397,7 @@ private: response_type* result_; typename impl_map::type impl_{}; - bool set_if_resp3_error(resp3::node const& nd) noexcept + bool set_if_resp3_error(resp3::basic_node const& nd) noexcept { switch (nd.data_type) { case resp3::type::blob_error: @@ -414,7 +414,7 @@ public: void operator()( - resp3::node const& nd, + resp3::basic_node const& nd, system::error_code& ec) { BOOST_ASSERT_MSG(!!result_, "Unexpected null pointer"); diff --git a/include/boost/redis/adapter/detail/response_traits.hpp b/include/boost/redis/adapter/detail/response_traits.hpp index 51ce9083..919ed255 100644 --- a/include/boost/redis/adapter/detail/response_traits.hpp +++ b/include/boost/redis/adapter/detail/response_traits.hpp @@ -24,7 +24,7 @@ namespace boost::redis::adapter::detail class ignore_adapter { public: void - operator()(std::size_t, resp3::node const& nd, system::error_code& ec) + operator()(std::size_t, resp3::basic_node const& nd, system::error_code& ec) { switch (nd.data_type) { case resp3::type::simple_error: ec = redis::error::resp3_simple_error; break; @@ -62,7 +62,7 @@ public: void operator()( std::size_t i, - resp3::node const& nd, + resp3::basic_node const& nd, system::error_code& ec) { using std::visit; @@ -91,7 +91,7 @@ public: void operator()( std::size_t, - resp3::node const& nd, + resp3::basic_node const& nd, system::error_code& ec) { adapter_(nd, ec); @@ -120,8 +120,8 @@ struct response_traits> { }; template -struct response_traits, Allocator>>> { - using response_type = result, Allocator>>; +struct response_traits, Allocator>>> { + using response_type = result, Allocator>>; using adapter_type = vector_adapter; static auto adapt(response_type& v) noexcept @@ -142,8 +142,8 @@ class wrapper { public: explicit wrapper(Adapter adapter) : adapter_{adapter} {} - void operator()(resp3::node const& node, system::error_code& ec) - { return adapter_(0, node, ec); } + void operator()(resp3::basic_node const& nd, system::error_code& ec) + { return adapter_(0, nd, ec); } [[nodiscard]] auto get_supported_response_size() const noexcept @@ -153,7 +153,6 @@ private: Adapter adapter_; }; -// TODO: Move this to adapt.hpp. template auto make_adapter_wrapper(Adapter adapter) { diff --git a/include/boost/redis/adapter/detail/result_traits.hpp b/include/boost/redis/adapter/detail/result_traits.hpp index 4bd356f2..2cc6afc9 100644 --- a/include/boost/redis/adapter/detail/result_traits.hpp +++ b/include/boost/redis/adapter/detail/result_traits.hpp @@ -48,15 +48,15 @@ struct result_traits { }; template -struct result_traits>> { - using response_type = result>; +struct result_traits>> { + using response_type = result>; using adapter_type = adapter::detail::general_simple; static auto adapt(response_type& v) noexcept { return adapter_type{&v}; } }; template -struct result_traits, Allocator>>> { - using response_type = result, Allocator>>; +struct result_traits, Allocator>>> { + using response_type = result, Allocator>>; using adapter_type = adapter::detail::general_aggregate; static auto adapt(response_type& v) noexcept { return adapter_type{&v}; } }; @@ -115,7 +115,7 @@ public: } } - void count(resp3::node const& nd) + void count(resp3::basic_node const& nd) { if (nd.depth == 1) { if (is_aggregate(nd.data_type)) @@ -130,7 +130,7 @@ public: ++i_; } - void operator()(resp3::node const& nd, system::error_code& ec) + void operator()(resp3::basic_node const& nd, system::error_code& ec) { using std::visit; diff --git a/include/boost/redis/adapter/ignore.hpp b/include/boost/redis/adapter/ignore.hpp index bca815d2..a468e966 100644 --- a/include/boost/redis/adapter/ignore.hpp +++ b/include/boost/redis/adapter/ignore.hpp @@ -20,7 +20,7 @@ namespace boost::redis::adapter * RESP3 errors won't be ignored. */ struct ignore { - void operator()(resp3::node const& nd, system::error_code& ec) + void operator()(resp3::basic_node const& nd, system::error_code& ec) { switch (nd.data_type) { case resp3::type::simple_error: ec = redis::error::resp3_simple_error; break; diff --git a/include/boost/redis/detail/connection_ops.hpp b/include/boost/redis/detail/connection_ops.hpp index e237bc69..36ee4336 100644 --- a/include/boost/redis/detail/connection_ops.hpp +++ b/include/boost/redis/detail/connection_ops.hpp @@ -10,7 +10,7 @@ #include #include #include -#include +#include #include #include #include @@ -94,10 +94,10 @@ struct exec_read_op { //----------------------------------- BOOST_ASIO_CORO_YIELD - redis::async_read( + redis::detail::async_read( conn->next_layer(), conn->make_dynamic_buffer(), - [i = index, adpt = adapter] (resp3::node const& nd, system::error_code& ec) mutable { adpt(i, nd, ec); }, + [i = index, adpt = adapter] (resp3::basic_node const& nd, system::error_code& ec) mutable { adpt(i, nd, ec); }, std::move(self)); ++index; @@ -135,7 +135,7 @@ struct receive_op { AEDIS_CHECK_OP1(;); BOOST_ASIO_CORO_YIELD - redis::async_read(conn->next_layer(), conn->make_dynamic_buffer(), adapter, std::move(self)); + redis::detail::async_read(conn->next_layer(), conn->make_dynamic_buffer(), adapter, std::move(self)); if (ec || is_cancelled(self)) { conn->cancel(operation::run); conn->cancel(operation::receive); diff --git a/include/boost/redis/read.hpp b/include/boost/redis/detail/read.hpp similarity index 96% rename from include/boost/redis/read.hpp rename to include/boost/redis/detail/read.hpp index a1d5900f..0dfad6bc 100644 --- a/include/boost/redis/read.hpp +++ b/include/boost/redis/detail/read.hpp @@ -15,10 +15,9 @@ #include #include -namespace boost::redis { +namespace boost::redis::detail { /** \brief Reads a complete response to a command sychronously. - * \ingroup low-level-api * * This function reads a complete response to a command or a * server push synchronously. For example @@ -50,7 +49,7 @@ namespace boost::redis { template < class SyncReadStream, class DynamicBuffer, - class ResponseAdapter + class ResponseAdapter > auto read( @@ -99,7 +98,6 @@ read( } /** \brief Reads a complete response to a command sychronously. - * \ingroup low-level-api * * Same as the error_code overload but throws on error. */ @@ -114,7 +112,7 @@ read( ResponseAdapter adapter = ResponseAdapter{}) { system::error_code ec; - auto const n = redis::read(stream, buf, adapter, ec); + auto const n = redis::detail::read(stream, buf, adapter, ec); if (ec) BOOST_THROW_EXCEPTION(system::system_error{ec}); @@ -123,7 +121,6 @@ read( } /** \brief Reads a complete response to a Redis command asynchronously. - * \ingroup low-level-api * * This function reads a complete response to a command or a * server push asynchronously. For example @@ -181,6 +178,6 @@ auto async_read( stream); } -} // boost::redis +} // boost::redis::detail #endif // BOOST_REDIS_READ_HPP diff --git a/include/boost/redis/write.hpp b/include/boost/redis/detail/write.hpp similarity index 77% rename from include/boost/redis/write.hpp rename to include/boost/redis/detail/write.hpp index 1af38640..a7b95d78 100644 --- a/include/boost/redis/write.hpp +++ b/include/boost/redis/detail/write.hpp @@ -8,8 +8,9 @@ #define BOOST_REDIS_WRITE_HPP #include +#include -namespace boost::redis { +namespace boost::redis::detail { /** \brief Writes a request synchronously. * \ingroup low-level-api @@ -17,23 +18,14 @@ namespace boost::redis { * \param stream Stream to write the request to. * \param req Request to write. */ -template< - class SyncWriteStream, - class Request - > -auto write(SyncWriteStream& stream, Request const& req) +template +auto write(SyncWriteStream& stream, request const& req) { return asio::write(stream, asio::buffer(req.payload())); } -template< - class SyncWriteStream, - class Request - > -auto write( - SyncWriteStream& stream, - Request const& req, - system::error_code& ec) +template +auto write(SyncWriteStream& stream, request const& req, system::error_code& ec) { return asio::write(stream, asio::buffer(req.payload()), ec); } @@ -47,18 +39,17 @@ auto write( */ template< class AsyncWriteStream, - class Request, class CompletionToken = asio::default_completion_token_t > auto async_write( AsyncWriteStream& stream, - Request const& req, + request const& req, CompletionToken&& token = asio::default_completion_token_t{}) { return asio::async_write(stream, asio::buffer(req.payload()), token); } -} // boost::redis +} // boost::redis::detail #endif // BOOST_REDIS_WRITE_HPP diff --git a/include/boost/redis/request.hpp b/include/boost/redis/request.hpp index 3b83aa40..69f269f0 100644 --- a/include/boost/redis/request.hpp +++ b/include/boost/redis/request.hpp @@ -40,9 +40,7 @@ auto has_response(std::string_view cmd) -> bool; * * \remarks * - * \li Non-string types will be converted to string by using \c - * boost_redis_to_bulk, which must be made available over ADL. - * \li Uses a std::string for internal storage. + * Uses a std::string for internal storage. */ class request { public: @@ -89,7 +87,7 @@ public: [[nodiscard]] auto size() const noexcept -> std::size_t { return commands_;}; - [[nodiscard]] auto payload() const noexcept -> auto const& + [[nodiscard]] auto payload() const noexcept -> std::string_view { return payload_;} [[nodiscard]] auto has_hello_priority() const noexcept -> auto const& @@ -121,19 +119,27 @@ public: * req.push("SET", "key", "some string", "EX", "2"); * \endcode * - * will add the \c set command with value "some string" and an + * will add the `set` command with value "some string" and an * expiration of 2 seconds. * * \param cmd The command e.g redis or sentinel command. * \param args Command arguments. + * \tparam Ts Non-string types will be converted to string by calling `boost_redis_to_bulk` on each argument. This function must be made available over ADL and must have the following signature + * + * @code + * void boost_redis_to_bulk(std::string& to, T const& t); + * { + * boost::redis::resp3::boost_redis_to_bulk(to, serialize(t)); + * } + * @endcode + * + * See cpp20_serialization.cpp */ template void push(std::string_view cmd, Ts const&... args) { - using resp3::type; - auto constexpr pack_size = sizeof...(Ts); - resp3::add_header(payload_, type::array, 1 + pack_size); + resp3::add_header(payload_, resp3::type::array, 1 + pack_size); resp3::add_bulk(payload_, cmd); resp3::add_bulk(payload_, std::tie(std::forward(args)...)); @@ -160,20 +166,34 @@ public: * \param key The command key. * \param begin Iterator to the begin of the range. * \param end Iterator to the end of the range. + * \tparam Ts Non-string types will be converted to string by calling `boost_redis_to_bulk` on each argument. This function must be made available over ADL and must have the following signature + * + * @code + * void boost_redis_to_bulk(std::string& to, T const& t); + * { + * boost::redis::resp3::boost_redis_to_bulk(to, serialize(t)); + * } + * @endcode + * + * See cpp20_serialization.cpp */ - template - void push_range(std::string_view cmd, Key const& key, ForwardIterator begin, ForwardIterator end, - typename std::iterator_traits::value_type * = nullptr) + template + void + push_range( + std::string_view const& cmd, + std::string_view const& key, + ForwardIterator begin, + ForwardIterator end, + typename std::iterator_traits::value_type * = nullptr) { using value_type = typename std::iterator_traits::value_type; - using resp3::type; if (begin == end) return; auto constexpr size = resp3::bulk_counter::size; auto const distance = std::distance(begin, end); - resp3::add_header(payload_, type::array, 2 + size * distance); + resp3::add_header(payload_, resp3::type::array, 2 + size * distance); resp3::add_bulk(payload_, cmd); resp3::add_bulk(payload_, key); @@ -199,20 +219,33 @@ public: * \param cmd The Redis command * \param begin Iterator to the begin of the range. * \param end Iterator to the end of the range. + * \tparam ForwardIterator If the value type is not a std::string it will be converted to a string by calling `boost_redis_to_bulk`. This function must be made available over ADL and must have the following signature + * + * @code + * void boost_redis_to_bulk(std::string& to, T const& t); + * { + * boost::redis::resp3::boost_redis_to_bulk(to, serialize(t)); + * } + * @endcode + * + * See cpp20_serialization.cpp */ template - void push_range(std::string_view cmd, ForwardIterator begin, ForwardIterator end, - typename std::iterator_traits::value_type * = nullptr) + void + push_range( + std::string_view const& cmd, + ForwardIterator begin, + ForwardIterator end, + typename std::iterator_traits::value_type * = nullptr) { using value_type = typename std::iterator_traits::value_type; - using resp3::type; if (begin == end) return; auto constexpr size = resp3::bulk_counter::size; auto const distance = std::distance(begin, end); - resp3::add_header(payload_, type::array, 1 + size * distance); + resp3::add_header(payload_, resp3::type::array, 1 + size * distance); resp3::add_bulk(payload_, cmd); for (; begin != end; ++begin) @@ -223,15 +256,21 @@ public: /** @brief Appends a new command to the end of the request. * - * Equivalent to the overload taking a range (i.e. send_range2). + * Equivalent to the overload taking a range of begin and end + * iterators. * * \param cmd Redis command. * \param key Redis key. - * \param range Range to send e.g. and \c std::map. + * \param range Range to send e.g. `std::map`. + * \tparam A type that can be passed to `std::cbegin()` and `std::cend()`. */ - template - void push_range(std::string_view cmd, Key const& key, Range const& range, - decltype(std::begin(range)) * = nullptr) + template + void + push_range( + std::string_view const& cmd, + std::string_view const& key, + Range const& range, + decltype(std::begin(range)) * = nullptr) { using std::begin; using std::end; @@ -240,18 +279,23 @@ public: /** @brief Appends a new command to the end of the request. * - * Equivalent to the overload taking a range (i.e. send_range2). + * Equivalent to the overload taking a range of begin and end + * iterators. * * \param cmd Redis command. - * \param range Range to send e.g. and \c std::map. + * \param range Range to send e.g. `std::map`. + * \tparam A type that can be passed to `std::cbegin()` and `std::cend()`. */ template - void push_range(std::string_view cmd, Range const& range, - decltype(std::begin(range)) * = nullptr) + void + push_range( + std::string_view cmd, + Range const& range, + decltype(std::cbegin(range)) * = nullptr) { - using std::begin; - using std::end; - push_range(cmd, begin(range), end(range)); + using std::cbegin; + using std::cend; + push_range(cmd, cbegin(range), cend(range)); } private: diff --git a/include/boost/redis/resp3/impl/serialization.ipp b/include/boost/redis/resp3/impl/serialization.ipp new file mode 100644 index 00000000..b4efc6d2 --- /dev/null +++ b/include/boost/redis/resp3/impl/serialization.ipp @@ -0,0 +1,42 @@ +/* Copyright (c) 2018-2022 Marcelo Zimbres Silva (mzimbres@gmail.com) + * + * Distributed under the Boost Software License, Version 1.0. (See + * accompanying file LICENSE.txt) + */ + +#include + +namespace boost::redis::resp3 { + +void boost_redis_to_bulk(std::string& payload, std::string_view data) +{ + auto const str = std::to_string(data.size()); + + payload += to_code(type::blob_string); + payload.append(std::cbegin(str), std::cend(str)); + payload += separator; + payload.append(std::cbegin(data), std::cend(data)); + payload += separator; +} + +void add_header(std::string& payload, type t, std::size_t size) +{ + // TODO: Call reserve. + auto const str = std::to_string(size); + + payload += to_code(t); + payload.append(std::cbegin(str), std::cend(str)); + payload += separator; +} + +void add_blob(std::string& payload, std::string_view blob) +{ + payload.append(std::cbegin(blob), std::cend(blob)); + payload += separator; +} + +void add_separator(std::string& payload) +{ + payload += separator; +} +} // boost::redis::resp3 diff --git a/include/boost/redis/resp3/impl/type.ipp b/include/boost/redis/resp3/impl/type.ipp index d80cd855..7a01ba0e 100644 --- a/include/boost/redis/resp3/impl/type.ipp +++ b/include/boost/redis/resp3/impl/type.ipp @@ -9,7 +9,7 @@ namespace boost::redis::resp3 { -auto to_string(type t) -> char const* +auto to_string(type t) noexcept -> char const* { switch (t) { case type::array: return "array"; @@ -39,72 +39,4 @@ auto operator<<(std::ostream& os, type t) -> std::ostream& return os; } -auto is_aggregate(type t) -> bool -{ - switch (t) { - case type::array: - case type::push: - case type::set: - case type::map: - case type::attribute: return true; - default: return false; - } -} - -auto element_multiplicity(type t) -> std::size_t -{ - switch (t) { - case type::map: - case type::attribute: return 2ULL; - default: return 1ULL; - } -} - -auto to_code(type t) -> char -{ - switch (t) { - case type::blob_error: return '!'; - case type::verbatim_string: return '='; - case type::blob_string: return '$'; - case type::streamed_string_part: return ';'; - case type::simple_error: return '-'; - case type::number: return ':'; - case type::doublean: return ','; - case type::boolean: return '#'; - case type::big_number: return '('; - case type::simple_string: return '+'; - case type::null: return '_'; - case type::push: return '>'; - case type::set: return '~'; - case type::array: return '*'; - case type::attribute: return '|'; - case type::map: return '%'; - - default: BOOST_ASSERT(false); return ' '; - } -} - -auto to_type(char c) -> type -{ - switch (c) { - case '!': return type::blob_error; - case '=': return type::verbatim_string; - case '$': return type::blob_string; - case ';': return type::streamed_string_part; - case '-': return type::simple_error; - case ':': return type::number; - case ',': return type::doublean; - case '#': return type::boolean; - case '(': return type::big_number; - case '+': return type::simple_string; - case '_': return type::null; - case '>': return type::push; - case '~': return type::set; - case '*': return type::array; - case '|': return type::attribute; - case '%': return type::map; - default: return type::invalid; - } -} - } // boost::redis::resp3 diff --git a/include/boost/redis/resp3/node.hpp b/include/boost/redis/resp3/node.hpp index 84004442..239d08b7 100644 --- a/include/boost/redis/resp3/node.hpp +++ b/include/boost/redis/resp3/node.hpp @@ -25,7 +25,7 @@ namespace boost::redis::resp3 { * @tparam String A `std::string`-like type. */ template -struct node { +struct basic_node { /// The RESP3 type of the data in this node. type data_type = type::invalid; @@ -46,7 +46,7 @@ struct node { * @param b Right hand side node object. */ template -auto operator==(node const& a, node const& b) +auto operator==(basic_node const& a, basic_node const& b) { return a.aggregate_size == b.aggregate_size && a.depth == b.depth @@ -54,6 +54,11 @@ auto operator==(node const& a, node const& b) && a.value == b.value; }; +/** @brief A node in the response tree. + * @ingroup high-level-api + */ +using node = basic_node; + } // boost::redis::resp3 #endif // BOOST_REDIS_RESP3_NODE_HPP diff --git a/include/boost/redis/resp3/parser.hpp b/include/boost/redis/resp3/parser.hpp index 8212b705..07517f3d 100644 --- a/include/boost/redis/resp3/parser.hpp +++ b/include/boost/redis/resp3/parser.hpp @@ -20,7 +20,7 @@ using int_type = std::uint64_t; class parser { private: - using node_type = node; + using node_type = basic_node; static constexpr std::size_t max_embedded_depth = 5; // The current depth. Simple data types will have depth 0, whereas diff --git a/include/boost/redis/resp3/serialization.hpp b/include/boost/redis/resp3/serialization.hpp index bdd92ff1..3977cb2c 100644 --- a/include/boost/redis/resp3/serialization.hpp +++ b/include/boost/redis/resp3/serialization.hpp @@ -25,56 +25,44 @@ constexpr char const* separator = "\r\n"; * structures in a request. For example * * @code - * void boost_redis_to_bulk(std::string& to, mystruct const& obj) + * void boost_redis_to_bulk(std::string& payload, mystruct const& obj) * { * auto const str = // Convert obj to a string. - * boost_redis_to_bulk(to, str); + * boost_redis_to_bulk(payload, str); * } * @endcode * - * @param to Storage on which data will be copied into. - * @param data Data that will be serialized and stored in @c to. + * @param payload Storage on which data will be copied into. + * @param data Data that will be serialized and stored in `payload`. * * See more in @ref serialization. */ -template -void boost_redis_to_bulk(Request& to, std::string_view data) -{ - auto const str = std::to_string(data.size()); +void boost_redis_to_bulk(std::string& payload, std::string_view data); - to += to_code(type::blob_string); - to.append(std::cbegin(str), std::cend(str)); - to += separator; - to.append(std::cbegin(data), std::cend(data)); - to += separator; -} - -template ::value>::type> -void boost_redis_to_bulk(Request& to, T n) +template ::value>::type> +void boost_redis_to_bulk(std::string& payload, T n) { auto const s = std::to_string(n); - boost_redis_to_bulk(to, std::string_view{s}); + boost_redis_to_bulk(payload, std::string_view{s}); } template struct add_bulk_impl { - template - static void add(Request& to, T const& from) + static void add(std::string& payload, T const& from) { using namespace boost::redis::resp3; - boost_redis_to_bulk(to, from); + boost_redis_to_bulk(payload, from); } }; template struct add_bulk_impl> { - template - static void add(Request& to, std::tuple const& t) + static void add(std::string& payload, std::tuple const& t) { auto f = [&](auto const&... vs) { using namespace boost::redis::resp3; - (boost_redis_to_bulk(to, vs), ...); + (boost_redis_to_bulk(payload, vs), ...); }; std::apply(f, t); @@ -83,29 +71,21 @@ struct add_bulk_impl> { template struct add_bulk_impl> { - template - static void add(Request& to, std::pair const& from) + static void add(std::string& payload, std::pair const& from) { using namespace boost::redis::resp3; - boost_redis_to_bulk(to, from.first); - boost_redis_to_bulk(to, from.second); + boost_redis_to_bulk(payload, from.first); + boost_redis_to_bulk(payload, from.second); } }; -template -void add_header(Request& to, type t, std::size_t size) -{ - auto const str = std::to_string(size); +void add_header(std::string& payload, type t, std::size_t size); - to += to_code(t); - to.append(std::cbegin(str), std::cend(str)); - to += separator; -} - -template -void add_bulk(Request& to, T const& data) +template +void add_bulk(std::string& payload, T const& data) { - add_bulk_impl::add(to, data); + // TODO: Call reserve. + add_bulk_impl::add(payload, data); } template @@ -121,18 +101,9 @@ struct bulk_counter> { static constexpr auto size = 2U; }; -template -void add_blob(Request& to, std::string_view blob) -{ - to.append(std::cbegin(blob), std::cend(blob)); - to += separator; -} +void add_blob(std::string& payload, std::string_view blob); +void add_separator(std::string& payload); -template -void add_separator(Request& to) -{ - to += separator; -} } // boost::redis::resp3 #endif // BOOST_REDIS_RESP3_SERIALIZATION_HPP diff --git a/include/boost/redis/resp3/type.hpp b/include/boost/redis/resp3/type.hpp index 340082c0..4ea37432 100644 --- a/include/boost/redis/resp3/type.hpp +++ b/include/boost/redis/resp3/type.hpp @@ -61,7 +61,7 @@ enum class type * \ingroup high-level-api * \param t RESP3 type. */ -auto to_string(type t) -> char const*; +auto to_string(type t) noexcept -> char const*; /** \brief Writes the type to the output stream. * \ingroup high-level-api @@ -72,17 +72,77 @@ auto operator<<(std::ostream& os, type t) -> std::ostream&; /* Checks whether the data type is an aggregate. */ -auto is_aggregate(type t) -> bool; +constexpr auto is_aggregate(type t) noexcept -> bool +{ + switch (t) { + case type::array: + case type::push: + case type::set: + case type::map: + case type::attribute: return true; + default: return false; + } +} // For map and attribute data types this function returns 2. All // other types have value 1. -auto element_multiplicity(type t) -> std::size_t; +constexpr auto element_multiplicity(type t) noexcept -> std::size_t +{ + switch (t) { + case type::map: + case type::attribute: return 2ULL; + default: return 1ULL; + } +} // Returns the wire code of a given type. -auto to_code(type t) -> char; +constexpr auto to_code(type t) noexcept -> char +{ + switch (t) { + case type::blob_error: return '!'; + case type::verbatim_string: return '='; + case type::blob_string: return '$'; + case type::streamed_string_part: return ';'; + case type::simple_error: return '-'; + case type::number: return ':'; + case type::doublean: return ','; + case type::boolean: return '#'; + case type::big_number: return '('; + case type::simple_string: return '+'; + case type::null: return '_'; + case type::push: return '>'; + case type::set: return '~'; + case type::array: return '*'; + case type::attribute: return '|'; + case type::map: return '%'; + + default: BOOST_ASSERT(false); return ' '; + } +} // Converts a wire-format RESP3 type (char) to a resp3 type. -auto to_type(char c) -> type; +constexpr auto to_type(char c) noexcept -> type +{ + switch (c) { + case '!': return type::blob_error; + case '=': return type::verbatim_string; + case '$': return type::blob_string; + case ';': return type::streamed_string_part; + case '-': return type::simple_error; + case ':': return type::number; + case ',': return type::doublean; + case '#': return type::boolean; + case '(': return type::big_number; + case '+': return type::simple_string; + case '_': return type::null; + case '>': return type::push; + case '~': return type::set; + case '*': return type::array; + case '|': return type::attribute; + case '%': return type::map; + default: return type::invalid; + } +} } // boost::redis::resp3 diff --git a/include/boost/redis/response.hpp b/include/boost/redis/response.hpp index 6e8f8fe5..5f6c5c37 100644 --- a/include/boost/redis/response.hpp +++ b/include/boost/redis/response.hpp @@ -30,7 +30,7 @@ using response = std::tuple...>; * [pre-order](https://en.wikipedia.org/wiki/Tree_traversal#Pre-order,_NLR) * view of the response tree. */ -using generic_response = adapter::result>>; +using generic_response = adapter::result>; } // boost::redis::resp3 diff --git a/include/boost/redis/src.hpp b/include/boost/redis/src.hpp index c066d2a9..bec18b70 100644 --- a/include/boost/redis/src.hpp +++ b/include/boost/redis/src.hpp @@ -9,3 +9,4 @@ #include #include #include +#include diff --git a/tests/conn_exec.cpp b/tests/conn_exec.cpp index f3e71706..e575e96b 100644 --- a/tests/conn_exec.cpp +++ b/tests/conn_exec.cpp @@ -18,6 +18,8 @@ // TODO: Test whether HELLO won't be inserted passt commands that have // been already writen. +// TODO: Test async_exec with empty request e.g. hgetall with an empty +// container. namespace net = boost::asio; using error_code = boost::system::error_code; diff --git a/tests/conn_push.cpp b/tests/conn_push.cpp index 2907588a..08d1f304 100644 --- a/tests/conn_push.cpp +++ b/tests/conn_push.cpp @@ -83,7 +83,7 @@ response_error_tag error_tag_obj; struct response_error_adapter { void operator()( - std::size_t, boost::redis::resp3::node const&, boost::system::error_code& ec) + std::size_t, boost::redis::resp3::basic_node const&, boost::system::error_code& ec) { ec = boost::redis::error::incompatible_size; } diff --git a/examples/cpp17_low_level_sync.cpp b/tests/cpp17_low_level_sync.cpp similarity index 85% rename from examples/cpp17_low_level_sync.cpp rename to tests/cpp17_low_level_sync.cpp index 5dbc8a13..434441af 100644 --- a/examples/cpp17_low_level_sync.cpp +++ b/tests/cpp17_low_level_sync.cpp @@ -9,7 +9,7 @@ #include #include -#include +#include #include namespace net = boost::asio; @@ -40,16 +40,16 @@ auto main(int argc, char * argv[]) -> int req.push("HELLO", 3); req.push("PING", "Hello world"); req.push("QUIT"); - redis::write(socket, req); + redis::detail::write(socket, req); std::string buffer; result resp; // Reads the responses to all commands in the request. auto dbuffer = net::dynamic_buffer(buffer); - redis::read(socket, dbuffer); - redis::read(socket, dbuffer, adapt2(resp)); - redis::read(socket, dbuffer); + redis::detail::read(socket, dbuffer); + redis::detail::read(socket, dbuffer, adapt2(resp)); + redis::detail::read(socket, dbuffer); std::cout << "Ping: " << resp.value() << std::endl; diff --git a/examples/cpp20_low_level_async.cpp b/tests/cpp20_low_level_async.cpp similarity index 83% rename from examples/cpp20_low_level_async.cpp rename to tests/cpp20_low_level_async.cpp index 44712855..f357eb16 100644 --- a/examples/cpp20_low_level_async.cpp +++ b/tests/cpp20_low_level_async.cpp @@ -7,7 +7,7 @@ #include #if defined(BOOST_ASIO_HAS_CO_AWAIT) #include -#include +#include #include #include @@ -34,7 +34,7 @@ auto co_main(std::string host, std::string port) -> net::awaitable req.push("HELLO", 3); req.push("PING", "Hello world"); req.push("QUIT"); - co_await redis::async_write(socket, req); + co_await redis::detail::async_write(socket, req); // Responses std::string buffer; @@ -42,9 +42,9 @@ auto co_main(std::string host, std::string port) -> net::awaitable // Reads the responses to all commands in the request. auto dbuffer = net::dynamic_buffer(buffer); - co_await redis::async_read(socket, dbuffer); - co_await redis::async_read(socket, dbuffer, adapt2(resp)); - co_await redis::async_read(socket, dbuffer); + co_await redis::detail::async_read(socket, dbuffer); + co_await redis::detail::async_read(socket, dbuffer, adapt2(resp)); + co_await redis::detail::async_read(socket, dbuffer); std::cout << "Ping: " << resp.value() << std::endl; } diff --git a/tests/issue_50.cpp b/tests/issue_50.cpp index 602c4dfa..5f600341 100644 --- a/tests/issue_50.cpp +++ b/tests/issue_50.cpp @@ -61,7 +61,7 @@ auto co_main(std::string host, std::string port) -> net::awaitable // The loop will reconnect on connection lost. To exit type Ctrl-C twice. for (int i = 0; i < 10; ++i) { co_await connect(conn, host, port); - co_await ((conn->async_run() || receiver(conn) || healthy_checker(conn) || periodic_task(conn)) && + co_await ((conn->async_run() || receiver(conn) || health_check(conn) || periodic_task(conn)) && conn->async_exec(req)); conn->reset_stream(); diff --git a/tests/low_level.cpp b/tests/low_level.cpp index f19e4353..1e57e3fd 100644 --- a/tests/low_level.cpp +++ b/tests/low_level.cpp @@ -41,8 +41,8 @@ using boost::redis::adapter::result; using test_stream = boost::beast::test::stream; using boost::redis::adapter::adapt2; -using node_type = result>; -using vec_node_type = result>>; +using node_type = result; +using vec_node_type = result>; using vec_type = result>; using op_vec_type = result>>; @@ -100,7 +100,7 @@ void test_sync(net::any_io_executor ex, expect e) ts.append(e.in); Result result; boost::system::error_code ec; - redis::read(ts, net::dynamic_buffer(rbuffer), adapt2(result), ec); + redis::detail::read(ts, net::dynamic_buffer(rbuffer), adapt2(result), ec); if (e.ec) { BOOST_CHECK_EQUAL(ec, e.ec); return; @@ -156,7 +156,7 @@ public: } }; - redis::async_read( + redis::detail::async_read( ts_, net::dynamic_buffer(rbuffer_), adapt2(result_), @@ -532,7 +532,7 @@ BOOST_AUTO_TEST_CASE(ignore_adapter_simple_error) test_stream ts {ioc}; ts.append(S10a); - redis::read(ts, net::dynamic_buffer(rbuffer), adapt2(ignore), ec); + redis::detail::read(ts, net::dynamic_buffer(rbuffer), adapt2(ignore), ec); BOOST_CHECK_EQUAL(ec, boost::redis::error::resp3_simple_error); BOOST_TEST(!rbuffer.empty()); } @@ -545,7 +545,7 @@ BOOST_AUTO_TEST_CASE(ignore_adapter_blob_error) test_stream ts {ioc}; ts.append(S12a); - redis::read(ts, net::dynamic_buffer(rbuffer), adapt2(ignore), ec); + redis::detail::read(ts, net::dynamic_buffer(rbuffer), adapt2(ignore), ec); BOOST_CHECK_EQUAL(ec, boost::redis::error::resp3_blob_error); BOOST_TEST(!rbuffer.empty()); } @@ -558,7 +558,7 @@ BOOST_AUTO_TEST_CASE(ignore_adapter_no_error) test_stream ts {ioc}; ts.append(S05b); - redis::read(ts, net::dynamic_buffer(rbuffer), adapt2(ignore), ec); + redis::detail::read(ts, net::dynamic_buffer(rbuffer), adapt2(ignore), ec); BOOST_TEST(!ec); BOOST_TEST(rbuffer.empty()); } @@ -662,8 +662,8 @@ BOOST_AUTO_TEST_CASE(adapter) response resp; auto f = boost_redis_adapt(resp); - f(0, resp3::node{type::simple_string, 1, 0, "Hello"}, ec); - f(1, resp3::node{type::number, 1, 0, "42"}, ec); + f(0, resp3::basic_node{type::simple_string, 1, 0, "Hello"}, ec); + f(1, resp3::basic_node{type::number, 1, 0, "42"}, ec); BOOST_CHECK_EQUAL(std::get<0>(resp).value(), "Hello"); BOOST_TEST(!ec); diff --git a/tests/request.cpp b/tests/request.cpp index 781a1039..68958536 100644 --- a/tests/request.cpp +++ b/tests/request.cpp @@ -12,6 +12,7 @@ #include #include #include +#include using boost::redis::request;