Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-08-15 09:03:45

0001 /* Copyright (c) 2018-2024 Marcelo Zimbres Silva (mzimbres@gmail.com)
0002  *
0003  * Distributed under the Boost Software License, Version 1.0. (See
0004  * accompanying file LICENSE.txt)
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 // See https://stackoverflow.com/a/31658120/1077832
0034 #ifdef _LIBCPP_VERSION
0035 #else
0036 #include <cstdlib>
0037 #endif
0038 
0039 namespace boost::redis::adapter::detail {
0040 
0041 // Exclude bools, char and charXY_t types
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       // The string in node.value is not null terminated and we also
0082       // don't know if there is enough space at the end for a null
0083       // char. The easiest thing to do is to create a temporary.
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  // _LIBCPP_VERSION
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 }  // namespace boost::redis::adapter::detail
0574 
0575 #endif  // BOOST_REDIS_ADAPTER_ADAPTERS_HPP