2
0
mirror of https://github.com/boostorg/redis.git synced 2026-01-19 04:42:09 +00:00

Renames node to basic_node.

This commit is contained in:
Marcelo Zimbres
2023-02-19 20:26:19 +01:00
parent a850a6ed63
commit 1f3ef6b486
30 changed files with 291 additions and 242 deletions

View File

@@ -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)

View File

@@ -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

View File

@@ -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<connection> conn) -> net::awaitable<void>
auto health_check(std::shared_ptr<connection> conn) -> net::awaitable<void>
{
try {
request req;

View File

@@ -26,7 +26,7 @@ connect(
std::string const& host,
std::string const& port) -> boost::asio::awaitable<void>;
auto healthy_checker(std::shared_ptr<connection> conn) -> boost::asio::awaitable<void>;
auto health_check(std::shared_ptr<connection> conn) -> boost::asio::awaitable<void>;
auto run(boost::asio::awaitable<void> op) -> int;

View File

@@ -60,7 +60,7 @@ auto co_main(std::string host, std::string port) -> net::awaitable<void>
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)

View File

@@ -54,7 +54,7 @@ auto co_main(std::string host, std::string port) -> net::awaitable<void>
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));
}

View File

@@ -56,7 +56,7 @@ auto co_main(std::string host, std::string port) -> net::awaitable<void>
// 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});

View File

@@ -75,7 +75,7 @@ private:
public:
explicit general_aggregate(Result* c = nullptr): result_(c) {}
void operator()(resp3::node<std::string_view> const& nd, system::error_code&)
void operator()(resp3::basic_node<std::string_view> 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<std::string_view> const& nd, system::error_code&)
void operator()(resp3::basic_node<std::string_view> 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<std::string_view> const& n,
resp3::basic_node<std::string_view> const& n,
system::error_code& ec)
{
if (is_aggregate(n.data_type)) {
@@ -146,7 +146,7 @@ public:
void
operator()(
Result& result,
resp3::node<std::string_view> const& nd,
resp3::basic_node<std::string_view> const& nd,
system::error_code& ec)
{
if (is_aggregate(nd.data_type)) {
@@ -181,7 +181,7 @@ public:
void
operator()(
Result& result,
resp3::node<std::string_view> const& nd,
resp3::basic_node<std::string_view> const& nd,
system::error_code& ec)
{
if (is_aggregate(nd.data_type)) {
@@ -219,7 +219,7 @@ public:
void
operator()(
Result& result,
resp3::node<std::string_view> const& nd,
resp3::basic_node<std::string_view> const& nd,
system::error_code& ec)
{
if (is_aggregate(nd.data_type)) {
@@ -243,7 +243,7 @@ public:
void
operator()(
Result& result,
resp3::node<std::string_view> const& nd,
resp3::basic_node<std::string_view> 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<std::string_view> const& nd,
resp3::basic_node<std::string_view> const& nd,
system::error_code& ec)
{
if (!is_aggregate(nd.data_type)) {
@@ -348,7 +348,7 @@ private:
response_type* result_;
typename impl_map<Result>::type impl_;
bool set_if_resp3_error(resp3::node<std::string_view> const& nd) noexcept
bool set_if_resp3_error(resp3::basic_node<std::string_view> const& nd) noexcept
{
switch (nd.data_type) {
case resp3::type::null:
@@ -372,7 +372,7 @@ public:
void
operator()(
resp3::node<std::string_view> const& nd,
resp3::basic_node<std::string_view> const& nd,
system::error_code& ec)
{
BOOST_ASSERT_MSG(!!result_, "Unexpected null pointer");
@@ -397,7 +397,7 @@ private:
response_type* result_;
typename impl_map<T>::type impl_{};
bool set_if_resp3_error(resp3::node<std::string_view> const& nd) noexcept
bool set_if_resp3_error(resp3::basic_node<std::string_view> const& nd) noexcept
{
switch (nd.data_type) {
case resp3::type::blob_error:
@@ -414,7 +414,7 @@ public:
void
operator()(
resp3::node<std::string_view> const& nd,
resp3::basic_node<std::string_view> const& nd,
system::error_code& ec)
{
BOOST_ASSERT_MSG(!!result_, "Unexpected null pointer");

View File

@@ -24,7 +24,7 @@ namespace boost::redis::adapter::detail
class ignore_adapter {
public:
void
operator()(std::size_t, resp3::node<std::string_view> const& nd, system::error_code& ec)
operator()(std::size_t, resp3::basic_node<std::string_view> 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<std::string_view> const& nd,
resp3::basic_node<std::string_view> const& nd,
system::error_code& ec)
{
using std::visit;
@@ -91,7 +91,7 @@ public:
void
operator()(
std::size_t,
resp3::node<std::string_view> const& nd,
resp3::basic_node<std::string_view> const& nd,
system::error_code& ec)
{
adapter_(nd, ec);
@@ -120,8 +120,8 @@ struct response_traits<result<ignore_t>> {
};
template <class String, class Allocator>
struct response_traits<result<std::vector<resp3::node<String>, Allocator>>> {
using response_type = result<std::vector<resp3::node<String>, Allocator>>;
struct response_traits<result<std::vector<resp3::basic_node<String>, Allocator>>> {
using response_type = result<std::vector<resp3::basic_node<String>, Allocator>>;
using adapter_type = vector_adapter<response_type>;
static auto adapt(response_type& v) noexcept
@@ -142,8 +142,8 @@ class wrapper {
public:
explicit wrapper(Adapter adapter) : adapter_{adapter} {}
void operator()(resp3::node<std::string_view> const& node, system::error_code& ec)
{ return adapter_(0, node, ec); }
void operator()(resp3::basic_node<std::string_view> 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 <class Adapter>
auto make_adapter_wrapper(Adapter adapter)
{

View File

@@ -48,15 +48,15 @@ struct result_traits<ignore_t> {
};
template <class T>
struct result_traits<result<resp3::node<T>>> {
using response_type = result<resp3::node<T>>;
struct result_traits<result<resp3::basic_node<T>>> {
using response_type = result<resp3::basic_node<T>>;
using adapter_type = adapter::detail::general_simple<response_type>;
static auto adapt(response_type& v) noexcept { return adapter_type{&v}; }
};
template <class String, class Allocator>
struct result_traits<result<std::vector<resp3::node<String>, Allocator>>> {
using response_type = result<std::vector<resp3::node<String>, Allocator>>;
struct result_traits<result<std::vector<resp3::basic_node<String>, Allocator>>> {
using response_type = result<std::vector<resp3::basic_node<String>, Allocator>>;
using adapter_type = adapter::detail::general_aggregate<response_type>;
static auto adapt(response_type& v) noexcept { return adapter_type{&v}; }
};
@@ -115,7 +115,7 @@ public:
}
}
void count(resp3::node<std::string_view> const& nd)
void count(resp3::basic_node<std::string_view> const& nd)
{
if (nd.depth == 1) {
if (is_aggregate(nd.data_type))
@@ -130,7 +130,7 @@ public:
++i_;
}
void operator()(resp3::node<std::string_view> const& nd, system::error_code& ec)
void operator()(resp3::basic_node<std::string_view> const& nd, system::error_code& ec)
{
using std::visit;

View File

@@ -20,7 +20,7 @@ namespace boost::redis::adapter
* RESP3 errors won't be ignored.
*/
struct ignore {
void operator()(resp3::node<std::string_view> const& nd, system::error_code& ec)
void operator()(resp3::basic_node<std::string_view> const& nd, system::error_code& ec)
{
switch (nd.data_type) {
case resp3::type::simple_error: ec = redis::error::resp3_simple_error; break;

View File

@@ -10,7 +10,7 @@
#include <boost/redis/adapter/adapt.hpp>
#include <boost/redis/error.hpp>
#include <boost/redis/resp3/type.hpp>
#include <boost/redis/read.hpp>
#include <boost/redis/detail/read.hpp>
#include <boost/redis/request.hpp>
#include <boost/assert.hpp>
#include <boost/system.hpp>
@@ -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<std::string_view> const& nd, system::error_code& ec) mutable { adpt(i, nd, ec); },
[i = index, adpt = adapter] (resp3::basic_node<std::string_view> 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);

View File

@@ -15,10 +15,9 @@
#include <boost/asio/compose.hpp>
#include <boost/asio/async_result.hpp>
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

View File

@@ -8,8 +8,9 @@
#define BOOST_REDIS_WRITE_HPP
#include <boost/asio/write.hpp>
#include <boost/redis/request.hpp>
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<class SyncWriteStream>
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<class SyncWriteStream>
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<typename AsyncWriteStream::executor_type>
>
auto async_write(
AsyncWriteStream& stream,
Request const& req,
request const& req,
CompletionToken&& token =
asio::default_completion_token_t<typename AsyncWriteStream::executor_type>{})
{
return asio::async_write(stream, asio::buffer(req.payload()), token);
}
} // boost::redis
} // boost::redis::detail
#endif // BOOST_REDIS_WRITE_HPP

View File

@@ -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 <class... Ts>
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<Ts const&>(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 <class Key, class ForwardIterator>
void push_range(std::string_view cmd, Key const& key, ForwardIterator begin, ForwardIterator end,
typename std::iterator_traits<ForwardIterator>::value_type * = nullptr)
template <class ForwardIterator>
void
push_range(
std::string_view const& cmd,
std::string_view const& key,
ForwardIterator begin,
ForwardIterator end,
typename std::iterator_traits<ForwardIterator>::value_type * = nullptr)
{
using value_type = typename std::iterator_traits<ForwardIterator>::value_type;
using resp3::type;
if (begin == end)
return;
auto constexpr size = resp3::bulk_counter<value_type>::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 <class ForwardIterator>
void push_range(std::string_view cmd, ForwardIterator begin, ForwardIterator end,
typename std::iterator_traits<ForwardIterator>::value_type * = nullptr)
void
push_range(
std::string_view const& cmd,
ForwardIterator begin,
ForwardIterator end,
typename std::iterator_traits<ForwardIterator>::value_type * = nullptr)
{
using value_type = typename std::iterator_traits<ForwardIterator>::value_type;
using resp3::type;
if (begin == end)
return;
auto constexpr size = resp3::bulk_counter<value_type>::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 <class Key, class Range>
void push_range(std::string_view cmd, Key const& key, Range const& range,
decltype(std::begin(range)) * = nullptr)
template <class Range>
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 <class Range>
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:

View File

@@ -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 <boost/redis/resp3/serialization.hpp>
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

View File

@@ -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

View File

@@ -25,7 +25,7 @@ namespace boost::redis::resp3 {
* @tparam String A `std::string`-like type.
*/
template <class String>
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 <class String>
auto operator==(node<String> const& a, node<String> const& b)
auto operator==(basic_node<String> const& a, basic_node<String> const& b)
{
return a.aggregate_size == b.aggregate_size
&& a.depth == b.depth
@@ -54,6 +54,11 @@ auto operator==(node<String> const& a, node<String> const& b)
&& a.value == b.value;
};
/** @brief A node in the response tree.
* @ingroup high-level-api
*/
using node = basic_node<std::string>;
} // boost::redis::resp3
#endif // BOOST_REDIS_RESP3_NODE_HPP

View File

@@ -20,7 +20,7 @@ using int_type = std::uint64_t;
class parser {
private:
using node_type = node<std::string_view>;
using node_type = basic_node<std::string_view>;
static constexpr std::size_t max_embedded_depth = 5;
// The current depth. Simple data types will have depth 0, whereas

View File

@@ -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 <class Request>
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 <class Request, class T, typename = typename std::enable_if<std::is_integral<T>::value>::type>
void boost_redis_to_bulk(Request& to, T n)
template <class T, typename = typename std::enable_if<std::is_integral<T>::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 <class T>
struct add_bulk_impl {
template <class Request>
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 <class ...Ts>
struct add_bulk_impl<std::tuple<Ts...>> {
template <class Request>
static void add(Request& to, std::tuple<Ts...> const& t)
static void add(std::string& payload, std::tuple<Ts...> 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<std::tuple<Ts...>> {
template <class U, class V>
struct add_bulk_impl<std::pair<U, V>> {
template <class Request>
static void add(Request& to, std::pair<U, V> const& from)
static void add(std::string& payload, std::pair<U, V> 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 <class Request>
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 <class Request, class T>
void add_bulk(Request& to, T const& data)
template <class T>
void add_bulk(std::string& payload, T const& data)
{
add_bulk_impl<T>::add(to, data);
// TODO: Call reserve.
add_bulk_impl<T>::add(payload, data);
}
template <class>
@@ -121,18 +101,9 @@ struct bulk_counter<std::pair<T, U>> {
static constexpr auto size = 2U;
};
template <class Request>
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 <class Request>
void add_separator(Request& to)
{
to += separator;
}
} // boost::redis::resp3
#endif // BOOST_REDIS_RESP3_SERIALIZATION_HPP

View File

@@ -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

View File

@@ -30,7 +30,7 @@ using response = std::tuple<adapter::result<Ts>...>;
* [pre-order](https://en.wikipedia.org/wiki/Tree_traversal#Pre-order,_NLR)
* view of the response tree.
*/
using generic_response = adapter::result<std::vector<resp3::node<std::string>>>;
using generic_response = adapter::result<std::vector<resp3::node>>;
} // boost::redis::resp3

View File

@@ -9,3 +9,4 @@
#include <boost/redis/impl/ignore.ipp>
#include <boost/redis/resp3/impl/type.ipp>
#include <boost/redis/resp3/impl/parser.ipp>
#include <boost/redis/resp3/impl/serialization.ipp>

View File

@@ -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;

View File

@@ -83,7 +83,7 @@ response_error_tag error_tag_obj;
struct response_error_adapter {
void
operator()(
std::size_t, boost::redis::resp3::node<std::string_view> const&, boost::system::error_code& ec)
std::size_t, boost::redis::resp3::basic_node<std::string_view> const&, boost::system::error_code& ec)
{
ec = boost::redis::error::incompatible_size;
}

View File

@@ -9,7 +9,7 @@
#include <boost/asio/connect.hpp>
#include <boost/redis.hpp>
#include <boost/redis/write.hpp>
#include <boost/redis/detail/write.hpp>
#include <boost/redis/src.hpp>
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<std::string> 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;

View File

@@ -7,7 +7,7 @@
#include <boost/asio.hpp>
#if defined(BOOST_ASIO_HAS_CO_AWAIT)
#include <boost/redis.hpp>
#include <boost/redis/write.hpp>
#include <boost/redis/detail/write.hpp>
#include <string>
#include <iostream>
@@ -34,7 +34,7 @@ auto co_main(std::string host, std::string port) -> net::awaitable<void>
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<void>
// 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;
}

View File

@@ -61,7 +61,7 @@ auto co_main(std::string host, std::string port) -> net::awaitable<void>
// 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();

View File

@@ -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<resp3::node<std::string>>;
using vec_node_type = result<std::vector<resp3::node<std::string>>>;
using node_type = result<resp3::node>;
using vec_node_type = result<std::vector<resp3::node>>;
using vec_type = result<std::vector<std::string>>;
using op_vec_type = result<std::optional<std::vector<std::string>>>;
@@ -100,7 +100,7 @@ void test_sync(net::any_io_executor ex, expect<Result> 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<std::string, int, ignore_t> resp;
auto f = boost_redis_adapt(resp);
f(0, resp3::node<std::string_view>{type::simple_string, 1, 0, "Hello"}, ec);
f(1, resp3::node<std::string_view>{type::number, 1, 0, "42"}, ec);
f(0, resp3::basic_node<std::string_view>{type::simple_string, 1, 0, "Hello"}, ec);
f(1, resp3::basic_node<std::string_view>{type::number, 1, 0, "42"}, ec);
BOOST_CHECK_EQUAL(std::get<0>(resp).value(), "Hello");
BOOST_TEST(!ec);

View File

@@ -12,6 +12,7 @@
#include <boost/redis/request.hpp>
#include <boost/redis/impl/request.ipp>
#include <boost/redis/resp3/impl/type.ipp>
#include <boost/redis/resp3/impl/serialization.ipp>
using boost::redis::request;