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

More improvements in the organization.

This commit is contained in:
Marcelo Zimbres
2021-12-26 21:54:51 +01:00
parent f4798e757f
commit e24fb3323b
9 changed files with 189 additions and 103 deletions

View File

@@ -23,3 +23,8 @@ div.contents {
background-color: #f9fafc;
padding: 15px;
}
code
{
background-color:#EFD25E;
}

View File

@@ -0,0 +1,47 @@
/* Copyright (c) 2019 - 2021 Marcelo Zimbres Silva (mzimbres at gmail dot com)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#pragma once
#include <aedis/resp3/detail/response_traits.hpp>
/** \file adapt.hpp
*/
namespace aedis {
namespace resp3 {
/** \brief Creates a void adapter
The adapter returned by this function ignores any data and is
useful to avoid wasting time with responses on which the user is
insterested in.
@code
co_await async_read(socket, buffer, adapt());
@endcode
*/
inline
auto adapt() noexcept
{ return response_traits<void>::adapt(); }
/** \brief Adapts user data to the resp3 parser.
For the types supported by this function see `response_traits`.
For example
@code
std::unordered_map<std::string, std::string> cont;
co_await async_read(socket, buffer, adapt(cont));
@endcode
*/
template<class T>
auto adapt(T& t) noexcept
{ return response_traits<T>::adapt(t); }
} // resp3
} // aedis

View File

@@ -18,39 +18,25 @@ namespace resp3 {
namespace detail {
template <class>
struct needs_to_string {
static constexpr auto value = true;
};
struct needs_to_string : std::true_type {};
template <>
struct needs_to_string<std::string> {
static constexpr auto value = false;
};
struct needs_to_string<std::string> : std::false_type {};
template <>
struct needs_to_string<std::string_view> {
static constexpr auto value = false;
};
struct needs_to_string<std::string_view> : std::false_type {};
template <>
struct needs_to_string<char const*> {
static constexpr auto value = false;
};
struct needs_to_string<char const*> : std::false_type {};
template <>
struct needs_to_string<char*> {
static constexpr auto value = false;
};
struct needs_to_string<char*> : std::false_type {};
template <std::size_t N>
struct needs_to_string<char[N]> {
static constexpr auto value = false;
};
struct needs_to_string<char[N]> : std::false_type {};
template <std::size_t N>
struct needs_to_string<char const[N]> {
static constexpr auto value = false;
};
struct needs_to_string<char const[N]> : std::false_type {};
void add_header(std::string& to, int size);
void add_bulk(std::string& to, std::string_view param);
@@ -64,6 +50,7 @@ void add_bulk(std::string& to, T const& data, typename std::enable_if<needs_to_s
}
// Overload for pairs.
// TODO: Overload for tuples.
template <class T1, class T2>
void add_bulk(std::string& to, std::pair<T1, T2> const& pair)
{
@@ -83,6 +70,18 @@ struct value_type_size<std::pair<T, U>> {
bool has_push_response(command cmd);
template <class T>
struct request_get_command {
static command apply(T const& e) noexcept
{ return e.get_command(); }
};
template <>
struct request_get_command<command> {
static command apply(command e) noexcept
{ return e; }
};
} // detail
} // resp3
} // aedis

View File

@@ -32,8 +32,11 @@ bool has_push_response(command cmd)
switch (cmd) {
case command::subscribe:
case command::unsubscribe:
case command::psubscribe: return true;
default: return false;
case command::psubscribe:
return true;
default:
return false;
}
};

View File

@@ -0,0 +1,66 @@
/* Copyright (c) 2019 - 2021 Marcelo Zimbres Silva (mzimbres at gmail dot com)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#pragma once
#include <aedis/net.hpp>
#include <boost/asio/yield.hpp>
#include <boost/core/ignore_unused.hpp>
namespace aedis {
namespace resp3 {
namespace detail {
template<
class AsyncWriteStream,
class Queue
>
struct write_some_op {
AsyncWriteStream& stream;
Queue& reqs;
std::size_t total_writen_ = 0;
net::coroutine coro_ = net::coroutine();
void
operator()(
auto& self,
boost::system::error_code const& ec = {},
std::size_t n = 0)
{
boost::ignore_unused(n);
reenter (coro_) {
do {
assert(!std::empty(reqs));
assert(!std::empty(reqs.front().request()));
yield net::async_write(
stream,
net::buffer(reqs.front().request()),
std::move(self));
if (ec)
break;
total_writen_ += n;
// Pops the request if no response is expected.
if (std::empty(reqs.front().commands))
reqs.pop();
} while (!std::empty(reqs) && std::empty(reqs.front().commands));
self.complete(ec, total_writen_);
}
}
};
} // detail
} // resp3
} // aedis
#include <boost/asio/unyield.hpp>

View File

@@ -21,8 +21,33 @@
namespace aedis {
namespace resp3 {
/** \brief Adapts C++ data structures to resp3 parser.
/** \brief Adapts C++ data structures to read operations.
*
* This class adapts C++ types to read operations. Users are advised
* to use `adapt()` function below for type deduction. The following
* types are supported.
*
* 1. Integer data types e.g. `int`, `unsigned`, etc.
*
* 1. `std::string`
*
* We also support the following C++ containers
*
* 1. `std::vector<T>`. Can be used with any RESP3 aggregate type.
*
* 1. `std::deque<T>`. Can be used with any RESP3 aggregate type.
*
* 1. `std::list<T>`. Can be used with any RESP3 aggregate type.
*
* 1. `std::set<T>`. Can be used with RESP3 set type.
*
* 1. `std::unordered_set<T>`. Can be used with RESP3 set type.
*
* 1. `std::map<T>`. Can be used with RESP3 hash type.
*
* 1. `std::unordered_map<T>`. Can be used with RESP3 hash type.
*
* All these types can be wrapped in an `std::optional<T>`.
*/
template <class T>
struct response_traits

View File

@@ -21,23 +21,6 @@
namespace aedis {
namespace resp3 {
// TODO: move to detail directory.
namespace detail {
template <class T>
struct request_get_command {
static command apply(T const& e) noexcept
{ return e.get_command(); }
};
template <>
struct request_get_command<command> {
static command apply(command e) noexcept
{ return e; }
};
} // detail
/** @brief Serializers user data into a redis request.
*
* This class offers functions to serialize user data into a redis
@@ -73,14 +56,14 @@ struct request_get_command<command> {
* Notice users will be required to define the get_command member
* function for their custom types.
*/
template <class QueueElem>
template <class ResponseId>
class serializer {
private:
std::string request_;
public:
/// The commands that have been queued in this request.
std::queue<QueueElem> commands;
std::queue<ResponseId> commands;
public:
/** Clears the serializer.
@@ -104,7 +87,7 @@ public:
* to_string which must be made available by the user.
*/
template <class... Ts>
void push(QueueElem qelem, Ts const&... args)
void push(ResponseId qelem, Ts const&... args)
{
// Note: Should we detect any std::pair in the type in the pack
// to calculate the herader size correctly or let users handle
@@ -113,7 +96,7 @@ public:
auto constexpr pack_size = sizeof...(Ts);
detail::add_header(request_, 1 + pack_size);
auto const cmd = detail::request_get_command<QueueElem>::apply(qelem);
auto const cmd = detail::request_get_command<ResponseId>::apply(qelem);
detail::add_bulk(request_, to_string(cmd));
(detail::add_bulk(request_, args), ...);
@@ -139,7 +122,7 @@ public:
\endcode
*/
template <class Key, class ForwardIterator>
void push_range(QueueElem qelem, Key const& key, ForwardIterator begin, ForwardIterator end)
void push_range(ResponseId qelem, Key const& key, ForwardIterator begin, ForwardIterator end)
{
// Note: For some commands like hset it would helpful to users
// to assert the value type is a pair.
@@ -149,7 +132,7 @@ public:
auto constexpr size = detail::value_type_size<value_type>::size;
auto const distance = std::distance(begin, end);
detail::add_header(request_, 2 + size * distance);
auto const cmd = detail::request_get_command<QueueElem>::apply(qelem);
auto const cmd = detail::request_get_command<ResponseId>::apply(qelem);
detail::add_bulk(request_, to_string(cmd));
detail::add_bulk(request_, key);
@@ -176,7 +159,7 @@ public:
\endcode
*/
template <class ForwardIterator>
void push_range(QueueElem qelem, ForwardIterator begin, ForwardIterator end)
void push_range(ResponseId qelem, ForwardIterator begin, ForwardIterator end)
{
// Note: For some commands like hset it would be a good idea to assert
// the value type is a pair.
@@ -186,7 +169,7 @@ public:
auto constexpr size = detail::value_type_size<value_type>::size;
auto const distance = std::distance(begin, end);
detail::add_header(request_, 1 + size * distance);
auto const cmd = detail::request_get_command<QueueElem>::apply(qelem);
auto const cmd = detail::request_get_command<ResponseId>::apply(qelem);
detail::add_bulk(request_, to_string(cmd));
for (; begin != end; ++begin)

View File

@@ -11,11 +11,13 @@
#include <vector>
#include <string>
/** \file type.hpp
*/
namespace aedis {
namespace resp3 {
/** \file type.hpp
\brief Enum that describes the redis data types and some helper functions.
/** \brief Enum that describes the redis data types and some helper functions.
This file contains the enum used to identify the redis data type
and some helper functions.

View File

@@ -7,60 +7,18 @@
#pragma once
#include <chrono>
#include <aedis/net.hpp>
#include <aedis/resp3/serializer.hpp>
#include <aedis/resp3/detail/write_ops.hpp>
#include <boost/beast/core/stream_traits.hpp>
#include <boost/asio/yield.hpp>
/** \file write.hpp
*/
namespace aedis {
namespace resp3 {
// TODO: return the number of bytes written.
template<
class AsyncWriteStream,
class Queue
>
struct write_some_op {
AsyncWriteStream& stream;
Queue& reqs;
net::coroutine coro_ = net::coroutine();
void
operator()(
auto& self,
boost::system::error_code const& ec = {},
std::size_t n = 0)
{
boost::ignore_unused(n);
reenter (coro_) {
do {
assert(!std::empty(reqs));
assert(!std::empty(reqs.front().request()));
yield net::async_write(
stream,
net::buffer(reqs.front().request()),
std::move(self));
if (ec)
break;
// Pops the request if no response is expected.
if (std::empty(reqs.front().commands))
reqs.pop();
} while (!std::empty(reqs) && std::empty(reqs.front().commands));
self.complete(ec);
}
}
};
/** @brief Writes the some request from the queue in the stream.
/** @brief Writes requests from the queue to the stream.
*/
template<
class AsyncWriteStream,
@@ -77,12 +35,10 @@ async_write_some(
{
return net::async_compose<
CompletionToken,
void(boost::system::error_code)>(
write_some_op<AsyncWriteStream, Queue>{stream, reqs},
void(boost::system::error_code, std::size_t)>(
detail::write_some_op<AsyncWriteStream, Queue>{stream, reqs},
token, stream);
}
} // resp3
} // aedis
#include <boost/asio/unyield.hpp>