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