File indexing completed on 2025-01-31 10:01:18
0001
0002
0003
0004
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;
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 {};
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:
0095 {
0096 auto const span = bulk_length_ + 2;
0097 if ((std::size(view) - consumed_) < span)
0098 return {};
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;
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
0142
0143
0144
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 }