Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-09-18 09:04:21

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/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 // See https://stackoverflow.com/a/31658120/1077832
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       // The string in node.value is not null terminated and we also
0089       // don't know if there is enough space at the end for a null
0090       // char. The easiest thing to do is to create a temporary.
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 // _LIBCPP_VERSION
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 } // boost::redis::adapter::detail
0515 
0516 #endif // BOOST_REDIS_ADAPTER_ADAPTERS_HPP