gnss-sim/3rdparty/boost/redis/resp3/impl/parser.ipp

221 lines
5.1 KiB
C++

/* Copyright (c) 2018-2024 Marcelo Zimbres Silva (mzimbres@gmail.com)
*
* Distributed under the Boost Software License, Version 1.0. (See
* accompanying file LICENSE.txt)
*/
#include <boost/redis/resp3/parser.hpp>
#include <boost/redis/error.hpp>
#include <boost/assert.hpp>
#include <charconv>
#include <limits>
namespace boost::redis::resp3 {
void to_int(std::size_t& i, std::string_view sv, system::error_code& ec)
{
auto const res = std::from_chars(sv.data(), sv.data() + std::size(sv), i);
if (res.ec != std::errc())
ec = error::not_a_number;
}
parser::parser()
{
reset();
}
void parser::reset()
{
depth_ = 0;
sizes_ = {{1}};
bulk_length_ = (std::numeric_limits<std::size_t>::max)();
bulk_ = type::invalid;
consumed_ = 0;
sizes_[0] = 2; // The sentinel must be more than 1.
}
std::size_t
parser::get_suggested_buffer_growth(std::size_t hint) const noexcept
{
if (!bulk_expected())
return hint;
if (hint < bulk_length_ + 2)
return bulk_length_ + 2;
return hint;
}
std::size_t
parser::get_consumed() const noexcept
{
return consumed_;
}
bool
parser::done() const noexcept
{
return depth_ == 0 && bulk_ == type::invalid && consumed_ != 0;
}
void
parser::commit_elem() noexcept
{
--sizes_[depth_];
while (sizes_[depth_] == 0) {
--depth_;
--sizes_[depth_];
}
}
auto
parser::consume(std::string_view view, system::error_code& ec) noexcept -> parser::result
{
switch (bulk_) {
case type::invalid:
{
auto const pos = view.find(sep, consumed_);
if (pos == std::string::npos)
return {}; // Needs more data to proceeed.
auto const t = to_type(view.at(consumed_));
auto const content = view.substr(consumed_ + 1, pos - 1 - consumed_);
auto const ret = consume_impl(t, content, ec);
if (ec)
return {};
consumed_ = pos + 2;
if (!bulk_expected())
return ret;
} [[fallthrough]];
default: // Handles bulk.
{
auto const span = bulk_length_ + 2;
if ((std::size(view) - consumed_) < span)
return {}; // Needs more data to proceeed.
auto const bulk_view = view.substr(consumed_, bulk_length_);
node_type const ret = {bulk_, 1, depth_, bulk_view};
bulk_ = type::invalid;
commit_elem();
consumed_ += span;
return ret;
}
}
}
auto
parser::consume_impl(
type t,
std::string_view elem,
system::error_code& ec) -> parser::node_type
{
BOOST_ASSERT(!bulk_expected());
node_type ret;
switch (t) {
case type::streamed_string_part:
{
to_int(bulk_length_ , elem, ec);
if (ec)
return {};
if (bulk_length_ == 0) {
ret = {type::streamed_string_part, 1, depth_, {}};
sizes_[depth_] = 1; // We are done.
bulk_ = type::invalid;
commit_elem();
} else {
bulk_ = type::streamed_string_part;
}
} break;
case type::blob_error:
case type::verbatim_string:
case type::blob_string:
{
if (elem.at(0) == '?') {
// NOTE: This can only be triggered with blob_string.
// Trick: A streamed string is read as an aggregate of
// infinite length. When the streaming is done the server
// is supposed to send a part with length 0.
sizes_[++depth_] = (std::numeric_limits<std::size_t>::max)();
ret = {type::streamed_string, 0, depth_, {}};
} else {
to_int(bulk_length_ , elem , ec);
if (ec)
return {};
bulk_ = t;
}
} break;
case type::boolean:
{
if (std::empty(elem)) {
ec = error::empty_field;
return {};
}
if (elem.at(0) != 'f' && elem.at(0) != 't') {
ec = error::unexpected_bool_value;
return {};
}
ret = {t, 1, depth_, elem};
commit_elem();
} break;
case type::doublean:
case type::big_number:
case type::number:
{
if (std::empty(elem)) {
ec = error::empty_field;
return {};
}
} [[fallthrough]];
case type::simple_error:
case type::simple_string:
case type::null:
{
ret = {t, 1, depth_, elem};
commit_elem();
} break;
case type::push:
case type::set:
case type::array:
case type::attribute:
case type::map:
{
std::size_t l = -1;
to_int(l, elem, ec);
if (ec)
return {};
ret = {t, l, depth_, {}};
if (l == 0) {
commit_elem();
} else {
if (depth_ == max_embedded_depth) {
ec = error::exceeeds_max_nested_depth;
return {};
}
++depth_;
sizes_[depth_] = l * element_multiplicity(t);
}
} break;
default:
{
ec = error::invalid_data_type;
return {};
}
}
return ret;
}
} // boost::redis::resp3