Boost.Redis 1.4.2
A redis client library
Loading...
Searching...
No Matches
adapters.hpp
1/* Copyright (c) 2018-2023 Marcelo Zimbres Silva (mzimbres@gmail.com)
2 *
3 * Distributed under the Boost Software License, Version 1.0. (See
4 * accompanying file LICENSE.txt)
5 */
6
7#ifndef BOOST_REDIS_ADAPTER_ADAPTERS_HPP
8#define BOOST_REDIS_ADAPTER_ADAPTERS_HPP
9
10#include <boost/redis/error.hpp>
11#include <boost/redis/resp3/type.hpp>
12#include <boost/redis/resp3/serialization.hpp>
13#include <boost/redis/resp3/node.hpp>
14#include <boost/redis/adapter/result.hpp>
15#include <boost/assert.hpp>
16
17#include <set>
18#include <optional>
19#include <unordered_set>
20#include <forward_list>
21#include <system_error>
22#include <map>
23#include <unordered_map>
24#include <list>
25#include <deque>
26#include <vector>
27#include <array>
28#include <string_view>
29#include <charconv>
30
31// See https://stackoverflow.com/a/31658120/1077832
32#include<ciso646>
33#ifdef _LIBCPP_VERSION
34#else
35#include <cstdlib>
36#endif
37
38namespace boost::redis::adapter::detail
39{
40
41// Serialization.
42
43template <class T>
44auto boost_redis_from_bulk(T& i, std::string_view sv, system::error_code& ec) -> typename std::enable_if<std::is_integral<T>::value, void>::type
45{
46 auto const res = std::from_chars(sv.data(), sv.data() + std::size(sv), i);
47 if (res.ec != std::errc())
49}
50
51inline
52void boost_redis_from_bulk(bool& t, std::string_view sv, system::error_code&)
53{
54 t = *sv.data() == 't';
55}
56
57inline
58void boost_redis_from_bulk(double& d, std::string_view sv, system::error_code& ec)
59{
60#ifdef _LIBCPP_VERSION
61 // The string in sv is not null terminated and we also don't know
62 // if there is enough space at the end for a null char. The easiest
63 // thing to do is to create a temporary.
64 std::string const tmp{sv.data(), sv.data() + std::size(sv)};
65 char* end{};
66 d = std::strtod(tmp.data(), &end);
67 if (d == HUGE_VAL || d == 0)
69#else
70 auto const res = std::from_chars(sv.data(), sv.data() + std::size(sv), d);
71 if (res.ec != std::errc())
73#endif // _LIBCPP_VERSION
74}
75
76template <class CharT, class Traits, class Allocator>
77void
78boost_redis_from_bulk(
79 std::basic_string<CharT, Traits, Allocator>& s,
80 std::string_view sv,
81 system::error_code&)
82{
83 s.append(sv.data(), sv.size());
84}
85
86//================================================
87
88template <class Result>
89class general_aggregate {
90private:
91 Result* result_;
92
93public:
94 explicit general_aggregate(Result* c = nullptr): result_(c) {}
95 template <class String>
96 void operator()(resp3::basic_node<String> const& nd, system::error_code&)
97 {
98 BOOST_ASSERT_MSG(!!result_, "Unexpected null pointer");
99 switch (nd.data_type) {
102 *result_ = error{nd.data_type, std::string{std::cbegin(nd.value), std::cend(nd.value)}};
103 break;
104 default:
105 result_->value().push_back({nd.data_type, nd.aggregate_size, nd.depth, std::string{std::cbegin(nd.value), std::cend(nd.value)}});
106 }
107 }
108};
109
110template <class Node>
111class general_simple {
112private:
113 Node* result_;
114
115public:
116 explicit general_simple(Node* t = nullptr) : result_(t) {}
117
118 template <class String>
119 void operator()(resp3::basic_node<String> const& nd, system::error_code&)
120 {
121 BOOST_ASSERT_MSG(!!result_, "Unexpected null pointer");
122 switch (nd.data_type) {
125 *result_ = error{nd.data_type, std::string{std::cbegin(nd.value), std::cend(nd.value)}};
126 break;
127 default:
128 result_->value().data_type = nd.data_type;
129 result_->value().aggregate_size = nd.aggregate_size;
130 result_->value().depth = nd.depth;
131 result_->value().value.assign(nd.value.data(), nd.value.size());
132 }
133 }
134};
135
136template <class Result>
137class simple_impl {
138public:
139 void on_value_available(Result&) {}
140
141 template <class String>
142 void operator()(Result& result, resp3::basic_node<String> const& n, system::error_code& ec)
143 {
144 if (is_aggregate(n.data_type)) {
146 return;
147 }
148
149 boost_redis_from_bulk(result, n.value, ec);
150 }
151};
152
153template <class Result>
154class set_impl {
155private:
156 typename Result::iterator hint_;
157
158public:
159 void on_value_available(Result& result)
160 { hint_ = std::end(result); }
161
162 template <class String>
163 void operator()(Result& result, resp3::basic_node<String> const& nd, system::error_code& ec)
164 {
165 if (is_aggregate(nd.data_type)) {
166 if (nd.data_type != resp3::type::set)
168 return;
169 }
170
171 BOOST_ASSERT(nd.aggregate_size == 1);
172
173 if (nd.depth < 1) {
175 return;
176 }
177
178 typename Result::key_type obj;
179 boost_redis_from_bulk(obj, nd.value, ec);
180 hint_ = result.insert(hint_, std::move(obj));
181 }
182};
183
184template <class Result>
185class map_impl {
186private:
187 typename Result::iterator current_;
188 bool on_key_ = true;
189
190public:
191 void on_value_available(Result& result)
192 { current_ = std::end(result); }
193
194 template <class String>
195 void operator()(Result& result, resp3::basic_node<String> const& nd, system::error_code& ec)
196 {
197 if (is_aggregate(nd.data_type)) {
198 if (element_multiplicity(nd.data_type) != 2)
200 return;
201 }
202
203 BOOST_ASSERT(nd.aggregate_size == 1);
204
205 if (nd.depth < 1) {
207 return;
208 }
209
210 if (on_key_) {
211 typename Result::key_type obj;
212 boost_redis_from_bulk(obj, nd.value, ec);
213 current_ = result.insert(current_, {std::move(obj), {}});
214 } else {
215 typename Result::mapped_type obj;
216 boost_redis_from_bulk(obj, nd.value, ec);
217 current_->second = std::move(obj);
218 }
219
220 on_key_ = !on_key_;
221 }
222};
223
224template <class Result>
225class vector_impl {
226public:
227 void on_value_available(Result& ) { }
228
229 template <class String>
230 void operator()(Result& result, resp3::basic_node<String> const& nd, system::error_code& ec)
231 {
232 if (is_aggregate(nd.data_type)) {
233 auto const m = element_multiplicity(nd.data_type);
234 result.reserve(result.size() + m * nd.aggregate_size);
235 } else {
236 result.push_back({});
237 boost_redis_from_bulk(result.back(), nd.value, ec);
238 }
239 }
240};
241
242template <class Result>
243class array_impl {
244private:
245 int i_ = -1;
246
247public:
248 void on_value_available(Result& ) { }
249
250 template <class String>
251 void operator()(Result& result, resp3::basic_node<String> const& nd, system::error_code& ec)
252 {
253 if (is_aggregate(nd.data_type)) {
254 if (i_ != -1) {
256 return;
257 }
258
259 if (result.size() != nd.aggregate_size * element_multiplicity(nd.data_type)) {
261 return;
262 }
263 } else {
264 if (i_ == -1) {
266 return;
267 }
268
269 BOOST_ASSERT(nd.aggregate_size == 1);
270 boost_redis_from_bulk(result.at(i_), nd.value, ec);
271 }
272
273 ++i_;
274 }
275};
276
277template <class Result>
278struct list_impl {
279
280 void on_value_available(Result& ) { }
281
282 template <class String>
283 void operator()(Result& result, resp3::basic_node<String> const& nd, system::error_code& ec)
284 {
285 if (!is_aggregate(nd.data_type)) {
286 BOOST_ASSERT(nd.aggregate_size == 1);
287 if (nd.depth < 1) {
289 return;
290 }
291
292 result.push_back({});
293 boost_redis_from_bulk(result.back(), nd.value, ec);
294 }
295 }
296};
297
298//---------------------------------------------------
299
300template <class T>
301struct impl_map { using type = simple_impl<T>; };
302
303template <class Key, class Compare, class Allocator>
304struct impl_map<std::set<Key, Compare, Allocator>> { using type = set_impl<std::set<Key, Compare, Allocator>>; };
305
306template <class Key, class Compare, class Allocator>
307struct impl_map<std::multiset<Key, Compare, Allocator>> { using type = set_impl<std::multiset<Key, Compare, Allocator>>; };
308
309template <class Key, class Hash, class KeyEqual, class Allocator>
310struct impl_map<std::unordered_set<Key, Hash, KeyEqual, Allocator>> { using type = set_impl<std::unordered_set<Key, Hash, KeyEqual, Allocator>>; };
311
312template <class Key, class Hash, class KeyEqual, class Allocator>
313struct impl_map<std::unordered_multiset<Key, Hash, KeyEqual, Allocator>> { using type = set_impl<std::unordered_multiset<Key, Hash, KeyEqual, Allocator>>; };
314
315template <class Key, class T, class Compare, class Allocator>
316struct impl_map<std::map<Key, T, Compare, Allocator>> { using type = map_impl<std::map<Key, T, Compare, Allocator>>; };
317
318template <class Key, class T, class Compare, class Allocator>
319struct impl_map<std::multimap<Key, T, Compare, Allocator>> { using type = map_impl<std::multimap<Key, T, Compare, Allocator>>; };
320
321template <class Key, class Hash, class KeyEqual, class Allocator>
322struct impl_map<std::unordered_map<Key, Hash, KeyEqual, Allocator>> { using type = map_impl<std::unordered_map<Key, Hash, KeyEqual, Allocator>>; };
323
324template <class Key, class Hash, class KeyEqual, class Allocator>
325struct impl_map<std::unordered_multimap<Key, Hash, KeyEqual, Allocator>> { using type = map_impl<std::unordered_multimap<Key, Hash, KeyEqual, Allocator>>; };
326
327template <class T, class Allocator>
328struct impl_map<std::vector<T, Allocator>> { using type = vector_impl<std::vector<T, Allocator>>; };
329
330template <class T, std::size_t N>
331struct impl_map<std::array<T, N>> { using type = array_impl<std::array<T, N>>; };
332
333template <class T, class Allocator>
334struct impl_map<std::list<T, Allocator>> { using type = list_impl<std::list<T, Allocator>>; };
335
336template <class T, class Allocator>
337struct impl_map<std::deque<T, Allocator>> { using type = list_impl<std::deque<T, Allocator>>; };
338
339//---------------------------------------------------
340
341template <class>
342class wrapper;
343
344template <class Result>
345class wrapper<result<Result>> {
346public:
347 using response_type = result<Result>;
348private:
349 response_type* result_;
350 typename impl_map<Result>::type impl_;
351
352 template <class String>
353 bool set_if_resp3_error(resp3::basic_node<String> const& nd) noexcept
354 {
355 switch (nd.data_type) {
359 *result_ = error{nd.data_type, {std::cbegin(nd.value), std::cend(nd.value)}};
360 return true;
361 default:
362 return false;
363 }
364 }
365
366public:
367 explicit wrapper(response_type* t = nullptr) : result_(t)
368 {
369 if (result_) {
370 result_->value() = Result{};
371 impl_.on_value_available(result_->value());
372 }
373 }
374
375 template <class String>
376 void operator()(resp3::basic_node<String> const& nd, system::error_code& ec)
377 {
378 BOOST_ASSERT_MSG(!!result_, "Unexpected null pointer");
379
380 if (result_->has_error())
381 return;
382
383 if (set_if_resp3_error(nd))
384 return;
385
386 BOOST_ASSERT(result_);
387 impl_(result_->value(), nd, ec);
388 }
389};
390
391template <class T>
392class wrapper<result<std::optional<T>>> {
393public:
394 using response_type = result<std::optional<T>>;
395
396private:
397 response_type* result_;
398 typename impl_map<T>::type impl_{};
399
400 template <class String>
401 bool set_if_resp3_error(resp3::basic_node<String> const& nd) noexcept
402 {
403 switch (nd.data_type) {
406 *result_ = error{nd.data_type, {std::cbegin(nd.value), std::cend(nd.value)}};
407 return true;
408 default:
409 return false;
410 }
411 }
412
413public:
414 explicit wrapper(response_type* o = nullptr) : result_(o) {}
415
416 template <class String>
417 void
418 operator()(
419 resp3::basic_node<String> const& nd,
420 system::error_code& ec)
421 {
422 BOOST_ASSERT_MSG(!!result_, "Unexpected null pointer");
423
424 if (result_->has_error())
425 return;
426
427 if (set_if_resp3_error(nd))
428 return;
429
430 if (nd.data_type == resp3::type::null)
431 return;
432
433 if (!result_->value().has_value()) {
434 result_->value() = T{};
435 impl_.on_value_available(result_->value().value());
436 }
437
438 impl_(result_->value().value(), nd, ec);
439 }
440};
441
442} // boost::redis::adapter::detail
443
444#endif // BOOST_REDIS_ADAPTER_ADAPTERS_HPP
type
RESP3 data types.
Definition: type.hpp:23
system::result< Value, error > result
Stores response to individual Redis commands.
Definition: result.hpp:56
error
Generic errors.
Definition: error.hpp:18
@ expects_resp3_set
Expects a set aggregate but got something else.
@ incompatible_size
Aggregate container has incompatible size.
@ not_a_number
Can't parse the string as a number.
@ nested_aggregate_not_supported
Nested response not supported.
@ expects_resp3_map
Expects a map but got other aggregate.
@ not_a_double
Not a double.
@ expects_resp3_aggregate
Expects aggregate.
@ expects_resp3_simple_type
Expects a simple RESP3 type but got an aggregate.