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:
@@ -23,3 +23,8 @@ div.contents {
|
||||
background-color: #f9fafc;
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
code
|
||||
{
|
||||
background-color:#EFD25E;
|
||||
}
|
||||
|
||||
47
include/aedis/resp3/adapt.hpp
Normal file
47
include/aedis/resp3/adapt.hpp
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
66
include/aedis/resp3/detail/write_ops.hpp
Normal file
66
include/aedis/resp3/detail/write_ops.hpp
Normal 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>
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user