Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-31 10:01:18

0001 /* Copyright (c) 2018-2023 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 #include <boost/redis/resp3/parser.hpp>
0008 #include <boost/redis/error.hpp>
0009 #include <boost/assert.hpp>
0010 
0011 #include <charconv>
0012 #include <limits>
0013 
0014 namespace boost::redis::resp3 {
0015 
0016 void to_int(int_type& i, std::string_view sv, system::error_code& ec)
0017 {
0018    auto const res = std::from_chars(sv.data(), sv.data() + std::size(sv), i);
0019    if (res.ec != std::errc())
0020       ec = error::not_a_number;
0021 }
0022 
0023 parser::parser()
0024 {
0025    reset();
0026 }
0027 
0028 void parser::reset()
0029 {
0030    depth_ = 0;
0031    sizes_ = {{1}};
0032    bulk_length_ = (std::numeric_limits<unsigned long>::max)();
0033    bulk_ = type::invalid;
0034    consumed_ = 0;
0035    sizes_[0] = 2; // The sentinel must be more than 1.
0036 }
0037 
0038 std::size_t
0039 parser::get_suggested_buffer_growth(std::size_t hint) const noexcept
0040 {
0041    if (!bulk_expected())
0042       return hint;
0043 
0044    if (hint < bulk_length_ + 2)
0045       return bulk_length_ + 2;
0046 
0047    return hint;
0048 }
0049 
0050 std::size_t
0051 parser::get_consumed() const noexcept
0052 {
0053    return consumed_;
0054 }
0055 
0056 bool
0057 parser::done() const noexcept
0058 {
0059    return depth_ == 0 && bulk_ == type::invalid && consumed_ != 0;
0060 }
0061 
0062 void
0063 parser::commit_elem() noexcept
0064 {
0065    --sizes_[depth_];
0066    while (sizes_[depth_] == 0) {
0067       --depth_;
0068       --sizes_[depth_];
0069    }
0070 }
0071 
0072 auto
0073 parser::consume(std::string_view view, system::error_code& ec) noexcept -> parser::result
0074 {
0075    switch (bulk_) {
0076       case type::invalid:
0077       {
0078          auto const pos = view.find(sep, consumed_);
0079          if (pos == std::string::npos)
0080             return {}; // Needs more data to proceeed.
0081 
0082          auto const t = to_type(view.at(consumed_));
0083          auto const content = view.substr(consumed_ + 1, pos - 1 - consumed_);
0084          auto const ret = consume_impl(t, content, ec);
0085          if (ec)
0086             return {};
0087 
0088          consumed_ = pos + 2;
0089          if (!bulk_expected())
0090             return ret;
0091 
0092       } [[fallthrough]];
0093 
0094       default: // Handles bulk.
0095       {
0096          auto const span = bulk_length_ + 2;
0097          if ((std::size(view) - consumed_) < span)
0098             return {}; // Needs more data to proceeed.
0099 
0100          auto const bulk_view = view.substr(consumed_, bulk_length_);
0101          node_type const ret = {bulk_, 1, depth_, bulk_view};
0102          bulk_ = type::invalid;
0103          commit_elem();
0104 
0105          consumed_ += span;
0106          return ret;
0107       }
0108    }
0109 }
0110 
0111 auto
0112 parser::consume_impl(
0113    type t,
0114    std::string_view elem,
0115    system::error_code& ec) -> parser::node_type
0116 {
0117    BOOST_ASSERT(!bulk_expected());
0118 
0119    node_type ret;
0120    switch (t) {
0121       case type::streamed_string_part:
0122       {
0123          to_int(bulk_length_ , elem, ec);
0124          if (ec)
0125             return {};
0126 
0127          if (bulk_length_ == 0) {
0128             ret = {type::streamed_string_part, 1, depth_, {}};
0129             sizes_[depth_] = 1; // We are done.
0130             bulk_ = type::invalid;
0131             commit_elem();
0132          } else {
0133             bulk_ = type::streamed_string_part;
0134          }
0135       } break;
0136       case type::blob_error:
0137       case type::verbatim_string:
0138       case type::blob_string:
0139       {
0140          if (elem.at(0) == '?') {
0141             // NOTE: This can only be triggered with blob_string.
0142             // Trick: A streamed string is read as an aggregate of
0143             // infinite length. When the streaming is done the server
0144             // is supposed to send a part with length 0.
0145             sizes_[++depth_] = (std::numeric_limits<std::size_t>::max)();
0146             ret = {type::streamed_string, 0, depth_, {}};
0147          } else {
0148             to_int(bulk_length_ , elem , ec);
0149             if (ec)
0150                return {};
0151 
0152             bulk_ = t;
0153          }
0154       } break;
0155       case type::boolean:
0156       {
0157          if (std::empty(elem)) {
0158              ec = error::empty_field;
0159              return {};
0160          }
0161 
0162          if (elem.at(0) != 'f' && elem.at(0) != 't') {
0163              ec = error::unexpected_bool_value;
0164              return {};
0165          }
0166 
0167          ret = {t, 1, depth_, elem};
0168          commit_elem();
0169       } break;
0170       case type::doublean:
0171       case type::big_number:
0172       case type::number:
0173       {
0174          if (std::empty(elem)) {
0175              ec = error::empty_field;
0176              return {};
0177          }
0178       } [[fallthrough]];
0179       case type::simple_error:
0180       case type::simple_string:
0181       case type::null:
0182       {
0183          ret = {t, 1, depth_, elem};
0184          commit_elem();
0185       } break;
0186       case type::push:
0187       case type::set:
0188       case type::array:
0189       case type::attribute:
0190       case type::map:
0191       {
0192          int_type l = -1;
0193          to_int(l, elem, ec);
0194          if (ec)
0195             return {};
0196 
0197          ret = {t, l, depth_, {}};
0198          if (l == 0) {
0199             commit_elem();
0200          } else {
0201             if (depth_ == max_embedded_depth) {
0202                ec = error::exceeeds_max_nested_depth;
0203                return {};
0204             }
0205 
0206             ++depth_;
0207 
0208             sizes_[depth_] = l * element_multiplicity(t);
0209          }
0210       } break;
0211       default:
0212       {
0213          ec = error::invalid_data_type;
0214          return {};
0215       }
0216    }
0217 
0218    return ret;
0219 }
0220 } // boost::redis::resp3