File indexing completed on 2026-08-15 09:03:45
0001
0002
0003
0004
0005
0006
0007 #ifndef BOOST_REDIS_ADAPTER_ADAPTERS_HPP
0008 #define BOOST_REDIS_ADAPTER_ADAPTERS_HPP
0009
0010 #include <boost/redis/adapter/result.hpp>
0011 #include <boost/redis/error.hpp>
0012 #include <boost/redis/resp3/node.hpp>
0013 #include <boost/redis/resp3/serialization.hpp>
0014 #include <boost/redis/resp3/type.hpp>
0015
0016 #include <boost/assert.hpp>
0017
0018 #include <array>
0019 #include <charconv>
0020 #include <deque>
0021 #include <forward_list>
0022 #include <list>
0023 #include <map>
0024 #include <optional>
0025 #include <set>
0026 #include <string_view>
0027 #include <system_error>
0028 #include <type_traits>
0029 #include <unordered_map>
0030 #include <unordered_set>
0031 #include <vector>
0032
0033
0034 #ifdef _LIBCPP_VERSION
0035 #else
0036 #include <cstdlib>
0037 #endif
0038
0039 namespace boost::redis::adapter::detail {
0040
0041
0042 template <class T> struct is_integral_number : std::is_integral<T> { };
0043 template <> struct is_integral_number<bool> : std::false_type { };
0044 template <> struct is_integral_number<char> : std::false_type { };
0045 template <> struct is_integral_number<char16_t> : std::false_type { };
0046 template <> struct is_integral_number<char32_t> : std::false_type { };
0047 template <> struct is_integral_number<wchar_t> : std::false_type { };
0048 #ifdef __cpp_char8_t
0049 template <> struct is_integral_number<char8_t> : std::false_type { };
0050 #endif
0051
0052 template <class T, bool = is_integral_number<T>::value>
0053 struct converter;
0054
0055 template <class T>
0056 struct converter<T, true> {
0057 template <class String>
0058 static void apply(T& i, resp3::basic_node<String> const& node, system::error_code& ec)
0059 {
0060 auto const res = std::from_chars(node.value.data(), node.value.data() + node.value.size(), i);
0061 if (res.ec != std::errc())
0062 ec = redis::error::not_a_number;
0063 }
0064 };
0065
0066 template <>
0067 struct converter<bool, false> {
0068 template <class String>
0069 static void apply(bool& t, resp3::basic_node<String> const& node, system::error_code&)
0070 {
0071 t = *node.value.data() == 't';
0072 }
0073 };
0074
0075 template <>
0076 struct converter<double, false> {
0077 template <class String>
0078 static void apply(double& d, resp3::basic_node<String> const& node, system::error_code& ec)
0079 {
0080 #ifdef _LIBCPP_VERSION
0081
0082
0083
0084 std::string const tmp{node.value.data(), node.value.data() + node.value.size()};
0085 char* end{};
0086 d = std::strtod(tmp.data(), &end);
0087 if (d == HUGE_VAL || d == 0)
0088 ec = redis::error::not_a_double;
0089 #else
0090 auto const res = std::from_chars(node.value.data(), node.value.data() + node.value.size(), d);
0091 if (res.ec != std::errc())
0092 ec = redis::error::not_a_double;
0093 #endif
0094 }
0095 };
0096
0097 template <class CharT, class Traits, class Allocator>
0098 struct converter<std::basic_string<CharT, Traits, Allocator>, false> {
0099 template <class String>
0100 static void apply(
0101 std::basic_string<CharT, Traits, Allocator>& s,
0102 resp3::basic_node<String> const& node,
0103 system::error_code&)
0104 {
0105 s.append(node.value.data(), node.value.size());
0106 }
0107 };
0108
0109 template <class T>
0110 struct from_bulk_impl {
0111 template <class String>
0112 static void apply(T& t, resp3::basic_node<String> const& node, system::error_code& ec)
0113 {
0114 converter<T>::apply(t, node, ec);
0115 }
0116 };
0117
0118 template <class T>
0119 struct from_bulk_impl<std::optional<T>> {
0120 template <class String>
0121 static void apply(
0122 std::optional<T>& op,
0123 resp3::basic_node<String> const& node,
0124 system::error_code& ec)
0125 {
0126 if (node.data_type != resp3::type::null) {
0127 op.emplace(T{});
0128 converter<T>::apply(op.value(), node, ec);
0129 }
0130 }
0131 };
0132
0133 template <class T, class String>
0134 void boost_redis_from_bulk(T& t, resp3::basic_node<String> const& node, system::error_code& ec)
0135 {
0136 from_bulk_impl<T>::apply(t, node, ec);
0137 }
0138
0139
0140
0141 template <class Result>
0142 class general_aggregate {
0143 private:
0144 Result* result_;
0145
0146 public:
0147 explicit general_aggregate(Result* c = nullptr)
0148 : result_(c)
0149 { }
0150
0151 void on_init() { }
0152 void on_done() { }
0153
0154 template <class String>
0155 void on_node(resp3::basic_node<String> const& nd, system::error_code&)
0156 {
0157 BOOST_ASSERT_MSG(!!result_, "Unexpected null pointer");
0158 switch (nd.data_type) {
0159 case resp3::type::blob_error:
0160 case resp3::type::simple_error:
0161 *result_ = error{
0162 nd.data_type,
0163 std::string{std::cbegin(nd.value), std::cend(nd.value)}
0164 };
0165 break;
0166 default:
0167 if (result_->has_value()) {
0168 (**result_).push_back({
0169 nd.data_type,
0170 nd.aggregate_size,
0171 nd.depth,
0172 std::string{std::cbegin(nd.value), std::cend(nd.value)}
0173 });
0174 }
0175 }
0176 }
0177 };
0178
0179 template <class Node>
0180 class general_simple {
0181 private:
0182 Node* result_;
0183
0184 public:
0185 explicit general_simple(Node* t = nullptr)
0186 : result_(t)
0187 { }
0188
0189 void on_init() { }
0190 void on_done() { }
0191
0192 template <class String>
0193 void on_node(resp3::basic_node<String> const& nd, system::error_code&)
0194 {
0195 BOOST_ASSERT_MSG(!!result_, "Unexpected null pointer");
0196 switch (nd.data_type) {
0197 case resp3::type::blob_error:
0198 case resp3::type::simple_error:
0199 *result_ = error{
0200 nd.data_type,
0201 std::string{std::cbegin(nd.value), std::cend(nd.value)}
0202 };
0203 break;
0204 default:
0205 result_->value().data_type = nd.data_type;
0206 result_->value().aggregate_size = nd.aggregate_size;
0207 result_->value().depth = nd.depth;
0208 result_->value().value.assign(nd.value.data(), nd.value.size());
0209 }
0210 }
0211 };
0212
0213 template <class Result>
0214 class simple_impl {
0215 public:
0216 void on_value_available(Result&) { }
0217
0218 void on_init() { }
0219 void on_done() { }
0220
0221 template <class String>
0222 void on_node(Result& result, resp3::basic_node<String> const& node, system::error_code& ec)
0223 {
0224 if (is_aggregate(node.data_type)) {
0225 ec = redis::error::expects_resp3_simple_type;
0226 return;
0227 }
0228
0229 boost_redis_from_bulk(result, node, ec);
0230 }
0231 };
0232
0233 template <class Result>
0234 class set_impl {
0235 private:
0236 typename Result::iterator hint_;
0237
0238 public:
0239 void on_value_available(Result& result) { hint_ = std::end(result); }
0240
0241 void on_init() { }
0242 void on_done() { }
0243
0244 template <class String>
0245 void on_node(Result& result, resp3::basic_node<String> const& nd, system::error_code& ec)
0246 {
0247 if (is_aggregate(nd.data_type)) {
0248 if (nd.data_type != resp3::type::set)
0249 ec = redis::error::expects_resp3_set;
0250 return;
0251 }
0252
0253 BOOST_ASSERT(nd.aggregate_size == 1);
0254
0255 if (nd.depth < 1) {
0256 ec = redis::error::expects_resp3_set;
0257 return;
0258 }
0259
0260 typename Result::key_type obj;
0261 boost_redis_from_bulk(obj, nd, ec);
0262 hint_ = result.insert(hint_, std::move(obj));
0263 }
0264 };
0265
0266 template <class Result>
0267 class map_impl {
0268 private:
0269 typename Result::iterator current_;
0270 bool on_key_ = true;
0271
0272 public:
0273 void on_value_available(Result& result) { current_ = std::end(result); }
0274
0275 void on_init() { }
0276 void on_done() { }
0277
0278 template <class String>
0279 void on_node(Result& result, resp3::basic_node<String> const& nd, system::error_code& ec)
0280 {
0281 if (is_aggregate(nd.data_type)) {
0282 if (element_multiplicity(nd.data_type) != 2)
0283 ec = redis::error::expects_resp3_map;
0284 return;
0285 }
0286
0287 BOOST_ASSERT(nd.aggregate_size == 1);
0288
0289 if (nd.depth < 1) {
0290 ec = redis::error::expects_resp3_map;
0291 return;
0292 }
0293
0294 if (on_key_) {
0295 typename Result::key_type obj;
0296 boost_redis_from_bulk(obj, nd, ec);
0297 current_ = result.insert(current_, {std::move(obj), {}});
0298 } else {
0299 typename Result::mapped_type obj;
0300 boost_redis_from_bulk(obj, nd, ec);
0301 current_->second = std::move(obj);
0302 }
0303
0304 on_key_ = !on_key_;
0305 }
0306 };
0307
0308 template <class Result>
0309 class vector_impl {
0310 public:
0311 void on_value_available(Result&) { }
0312
0313 void on_init() { }
0314 void on_done() { }
0315
0316 template <class String>
0317 void on_node(Result& result, resp3::basic_node<String> const& nd, system::error_code& ec)
0318 {
0319 if (is_aggregate(nd.data_type)) {
0320 auto const m = element_multiplicity(nd.data_type);
0321 result.reserve(result.size() + m * nd.aggregate_size);
0322 } else {
0323 result.push_back({});
0324 boost_redis_from_bulk(result.back(), nd, ec);
0325 }
0326 }
0327 };
0328
0329 template <class Result>
0330 class array_impl {
0331 private:
0332 int i_ = -1;
0333
0334 public:
0335 void on_value_available(Result&) { }
0336
0337 void on_init() { }
0338 void on_done() { }
0339
0340 template <class String>
0341 void on_node(Result& result, resp3::basic_node<String> const& nd, system::error_code& ec)
0342 {
0343 if (is_aggregate(nd.data_type)) {
0344 if (i_ != -1) {
0345 ec = redis::error::nested_aggregate_not_supported;
0346 return;
0347 }
0348
0349 if (result.size() != nd.aggregate_size * element_multiplicity(nd.data_type)) {
0350 ec = redis::error::incompatible_size;
0351 return;
0352 }
0353 } else {
0354 if (i_ == -1) {
0355 ec = redis::error::expects_resp3_aggregate;
0356 return;
0357 }
0358
0359 BOOST_ASSERT(nd.aggregate_size == 1);
0360 boost_redis_from_bulk(result.at(i_), nd, ec);
0361 }
0362
0363 ++i_;
0364 }
0365 };
0366
0367 template <class Result>
0368 struct list_impl {
0369 void on_value_available(Result&) { }
0370
0371 void on_init() { }
0372 void on_done() { }
0373
0374 template <class String>
0375 void on_node(Result& result, resp3::basic_node<String> const& nd, system::error_code& ec)
0376 {
0377 if (!is_aggregate(nd.data_type)) {
0378 BOOST_ASSERT(nd.aggregate_size == 1);
0379 if (nd.depth < 1) {
0380 ec = redis::error::expects_resp3_aggregate;
0381 return;
0382 }
0383
0384 result.push_back({});
0385 boost_redis_from_bulk(result.back(), nd, ec);
0386 }
0387 }
0388 };
0389
0390
0391
0392 template <class T>
0393 struct impl_map {
0394 using type = simple_impl<T>;
0395 };
0396
0397 template <class Key, class Compare, class Allocator>
0398 struct impl_map<std::set<Key, Compare, Allocator>> {
0399 using type = set_impl<std::set<Key, Compare, Allocator>>;
0400 };
0401
0402 template <class Key, class Compare, class Allocator>
0403 struct impl_map<std::multiset<Key, Compare, Allocator>> {
0404 using type = set_impl<std::multiset<Key, Compare, Allocator>>;
0405 };
0406
0407 template <class Key, class Hash, class KeyEqual, class Allocator>
0408 struct impl_map<std::unordered_set<Key, Hash, KeyEqual, Allocator>> {
0409 using type = set_impl<std::unordered_set<Key, Hash, KeyEqual, Allocator>>;
0410 };
0411
0412 template <class Key, class Hash, class KeyEqual, class Allocator>
0413 struct impl_map<std::unordered_multiset<Key, Hash, KeyEqual, Allocator>> {
0414 using type = set_impl<std::unordered_multiset<Key, Hash, KeyEqual, Allocator>>;
0415 };
0416
0417 template <class Key, class T, class Compare, class Allocator>
0418 struct impl_map<std::map<Key, T, Compare, Allocator>> {
0419 using type = map_impl<std::map<Key, T, Compare, Allocator>>;
0420 };
0421
0422 template <class Key, class T, class Compare, class Allocator>
0423 struct impl_map<std::multimap<Key, T, Compare, Allocator>> {
0424 using type = map_impl<std::multimap<Key, T, Compare, Allocator>>;
0425 };
0426
0427 template <class Key, class Hash, class KeyEqual, class Allocator>
0428 struct impl_map<std::unordered_map<Key, Hash, KeyEqual, Allocator>> {
0429 using type = map_impl<std::unordered_map<Key, Hash, KeyEqual, Allocator>>;
0430 };
0431
0432 template <class Key, class Hash, class KeyEqual, class Allocator>
0433 struct impl_map<std::unordered_multimap<Key, Hash, KeyEqual, Allocator>> {
0434 using type = map_impl<std::unordered_multimap<Key, Hash, KeyEqual, Allocator>>;
0435 };
0436
0437 template <class T, class Allocator>
0438 struct impl_map<std::vector<T, Allocator>> {
0439 using type = vector_impl<std::vector<T, Allocator>>;
0440 };
0441
0442 template <class T, std::size_t N>
0443 struct impl_map<std::array<T, N>> {
0444 using type = array_impl<std::array<T, N>>;
0445 };
0446
0447 template <class T, class Allocator>
0448 struct impl_map<std::list<T, Allocator>> {
0449 using type = list_impl<std::list<T, Allocator>>;
0450 };
0451
0452 template <class T, class Allocator>
0453 struct impl_map<std::deque<T, Allocator>> {
0454 using type = list_impl<std::deque<T, Allocator>>;
0455 };
0456
0457
0458
0459 template <class>
0460 class wrapper;
0461
0462 template <class T>
0463 class wrapper<result<T>> {
0464 public:
0465 using response_type = result<T>;
0466
0467 private:
0468 response_type* result_;
0469 typename impl_map<T>::type impl_;
0470 bool called_once_ = false;
0471
0472 template <class String>
0473 bool set_if_resp3_error(resp3::basic_node<String> const& nd) noexcept
0474 {
0475 switch (nd.data_type) {
0476 case resp3::type::null:
0477 case resp3::type::simple_error:
0478 case resp3::type::blob_error:
0479 *result_ = error{
0480 nd.data_type,
0481 {std::cbegin(nd.value), std::cend(nd.value)}
0482 };
0483 return true;
0484 default: return false;
0485 }
0486 }
0487
0488 public:
0489 explicit wrapper(response_type* t = nullptr)
0490 : result_(t)
0491 {
0492 if (result_) {
0493 result_->value() = T{};
0494 impl_.on_value_available(result_->value());
0495 }
0496 }
0497
0498 void on_init() { impl_.on_init(); }
0499 void on_done() { impl_.on_done(); }
0500
0501 template <class String>
0502 void on_node(resp3::basic_node<String> const& nd, system::error_code& ec)
0503 {
0504 BOOST_ASSERT_MSG(!!result_, "Unexpected null pointer");
0505
0506 if (result_->has_error())
0507 return;
0508
0509 if (!std::exchange(called_once_, true) && set_if_resp3_error(nd))
0510 return;
0511
0512 BOOST_ASSERT(result_);
0513 impl_.on_node(result_->value(), nd, ec);
0514 }
0515 };
0516
0517 template <class T>
0518 class wrapper<result<std::optional<T>>> {
0519 public:
0520 using response_type = result<std::optional<T>>;
0521
0522 private:
0523 response_type* result_;
0524 typename impl_map<T>::type impl_{};
0525 bool called_once_ = false;
0526
0527 template <class String>
0528 bool set_if_resp3_error(resp3::basic_node<String> const& nd) noexcept
0529 {
0530 switch (nd.data_type) {
0531 case resp3::type::blob_error:
0532 case resp3::type::simple_error:
0533 *result_ = error{
0534 nd.data_type,
0535 {std::cbegin(nd.value), std::cend(nd.value)}
0536 };
0537 return true;
0538 default: return false;
0539 }
0540 }
0541
0542 public:
0543 explicit wrapper(response_type* o = nullptr)
0544 : result_(o)
0545 { }
0546
0547 void on_init() { impl_.on_init(); }
0548 void on_done() { impl_.on_done(); }
0549
0550 template <class String>
0551 void on_node(resp3::basic_node<String> const& nd, system::error_code& ec)
0552 {
0553 BOOST_ASSERT_MSG(!!result_, "Unexpected null pointer");
0554
0555 if (result_->has_error())
0556 return;
0557
0558 if (set_if_resp3_error(nd))
0559 return;
0560
0561 if (!std::exchange(called_once_, true) && nd.data_type == resp3::type::null)
0562 return;
0563
0564 if (!result_->value().has_value()) {
0565 result_->value() = T{};
0566 impl_.on_value_available(result_->value().value());
0567 }
0568
0569 impl_.on_node(result_->value().value(), nd, ec);
0570 }
0571 };
0572
0573 }
0574
0575 #endif