parser.ipp 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. /* Copyright (c) 2018-2024 Marcelo Zimbres Silva ([email protected])
  2. *
  3. * Distributed under the Boost Software License, Version 1.0. (See
  4. * accompanying file LICENSE.txt)
  5. */
  6. #include <boost/redis/resp3/parser.hpp>
  7. #include <boost/redis/error.hpp>
  8. #include <boost/assert.hpp>
  9. #include <charconv>
  10. #include <limits>
  11. namespace boost::redis::resp3 {
  12. void to_int(std::size_t& i, std::string_view sv, system::error_code& ec)
  13. {
  14. auto const res = std::from_chars(sv.data(), sv.data() + std::size(sv), i);
  15. if (res.ec != std::errc())
  16. ec = error::not_a_number;
  17. }
  18. parser::parser()
  19. {
  20. reset();
  21. }
  22. void parser::reset()
  23. {
  24. depth_ = 0;
  25. sizes_ = {{1}};
  26. bulk_length_ = (std::numeric_limits<std::size_t>::max)();
  27. bulk_ = type::invalid;
  28. consumed_ = 0;
  29. sizes_[0] = 2; // The sentinel must be more than 1.
  30. }
  31. std::size_t
  32. parser::get_suggested_buffer_growth(std::size_t hint) const noexcept
  33. {
  34. if (!bulk_expected())
  35. return hint;
  36. if (hint < bulk_length_ + 2)
  37. return bulk_length_ + 2;
  38. return hint;
  39. }
  40. std::size_t
  41. parser::get_consumed() const noexcept
  42. {
  43. return consumed_;
  44. }
  45. bool
  46. parser::done() const noexcept
  47. {
  48. return depth_ == 0 && bulk_ == type::invalid && consumed_ != 0;
  49. }
  50. void
  51. parser::commit_elem() noexcept
  52. {
  53. --sizes_[depth_];
  54. while (sizes_[depth_] == 0) {
  55. --depth_;
  56. --sizes_[depth_];
  57. }
  58. }
  59. auto
  60. parser::consume(std::string_view view, system::error_code& ec) noexcept -> parser::result
  61. {
  62. switch (bulk_) {
  63. case type::invalid:
  64. {
  65. auto const pos = view.find(sep, consumed_);
  66. if (pos == std::string::npos)
  67. return {}; // Needs more data to proceeed.
  68. auto const t = to_type(view.at(consumed_));
  69. auto const content = view.substr(consumed_ + 1, pos - 1 - consumed_);
  70. auto const ret = consume_impl(t, content, ec);
  71. if (ec)
  72. return {};
  73. consumed_ = pos + 2;
  74. if (!bulk_expected())
  75. return ret;
  76. } [[fallthrough]];
  77. default: // Handles bulk.
  78. {
  79. auto const span = bulk_length_ + 2;
  80. if ((std::size(view) - consumed_) < span)
  81. return {}; // Needs more data to proceeed.
  82. auto const bulk_view = view.substr(consumed_, bulk_length_);
  83. node_type const ret = {bulk_, 1, depth_, bulk_view};
  84. bulk_ = type::invalid;
  85. commit_elem();
  86. consumed_ += span;
  87. return ret;
  88. }
  89. }
  90. }
  91. auto
  92. parser::consume_impl(
  93. type t,
  94. std::string_view elem,
  95. system::error_code& ec) -> parser::node_type
  96. {
  97. BOOST_ASSERT(!bulk_expected());
  98. node_type ret;
  99. switch (t) {
  100. case type::streamed_string_part:
  101. {
  102. to_int(bulk_length_ , elem, ec);
  103. if (ec)
  104. return {};
  105. if (bulk_length_ == 0) {
  106. ret = {type::streamed_string_part, 1, depth_, {}};
  107. sizes_[depth_] = 1; // We are done.
  108. bulk_ = type::invalid;
  109. commit_elem();
  110. } else {
  111. bulk_ = type::streamed_string_part;
  112. }
  113. } break;
  114. case type::blob_error:
  115. case type::verbatim_string:
  116. case type::blob_string:
  117. {
  118. if (elem.at(0) == '?') {
  119. // NOTE: This can only be triggered with blob_string.
  120. // Trick: A streamed string is read as an aggregate of
  121. // infinite length. When the streaming is done the server
  122. // is supposed to send a part with length 0.
  123. sizes_[++depth_] = (std::numeric_limits<std::size_t>::max)();
  124. ret = {type::streamed_string, 0, depth_, {}};
  125. } else {
  126. to_int(bulk_length_ , elem , ec);
  127. if (ec)
  128. return {};
  129. bulk_ = t;
  130. }
  131. } break;
  132. case type::boolean:
  133. {
  134. if (std::empty(elem)) {
  135. ec = error::empty_field;
  136. return {};
  137. }
  138. if (elem.at(0) != 'f' && elem.at(0) != 't') {
  139. ec = error::unexpected_bool_value;
  140. return {};
  141. }
  142. ret = {t, 1, depth_, elem};
  143. commit_elem();
  144. } break;
  145. case type::doublean:
  146. case type::big_number:
  147. case type::number:
  148. {
  149. if (std::empty(elem)) {
  150. ec = error::empty_field;
  151. return {};
  152. }
  153. } [[fallthrough]];
  154. case type::simple_error:
  155. case type::simple_string:
  156. case type::null:
  157. {
  158. ret = {t, 1, depth_, elem};
  159. commit_elem();
  160. } break;
  161. case type::push:
  162. case type::set:
  163. case type::array:
  164. case type::attribute:
  165. case type::map:
  166. {
  167. std::size_t l = -1;
  168. to_int(l, elem, ec);
  169. if (ec)
  170. return {};
  171. ret = {t, l, depth_, {}};
  172. if (l == 0) {
  173. commit_elem();
  174. } else {
  175. if (depth_ == max_embedded_depth) {
  176. ec = error::exceeeds_max_nested_depth;
  177. return {};
  178. }
  179. ++depth_;
  180. sizes_[depth_] = l * element_multiplicity(t);
  181. }
  182. } break;
  183. default:
  184. {
  185. ec = error::invalid_data_type;
  186. return {};
  187. }
  188. }
  189. return ret;
  190. }
  191. } // boost::redis::resp3