105 lines
2.7 KiB
C++
105 lines
2.7 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)
|
||
|
*/
|
||
|
|
||
|
#ifndef BOOST_REDIS_RESP3_PARSER_HPP
|
||
|
#define BOOST_REDIS_RESP3_PARSER_HPP
|
||
|
|
||
|
#include <boost/redis/resp3/node.hpp>
|
||
|
#include <boost/system/error_code.hpp>
|
||
|
#include <array>
|
||
|
#include <string_view>
|
||
|
#include <cstdint>
|
||
|
#include <optional>
|
||
|
|
||
|
namespace boost::redis::resp3 {
|
||
|
|
||
|
class parser {
|
||
|
public:
|
||
|
using node_type = basic_node<std::string_view>;
|
||
|
using result = std::optional<node_type>;
|
||
|
|
||
|
static constexpr std::size_t max_embedded_depth = 5;
|
||
|
static constexpr std::string_view sep = "\r\n";
|
||
|
|
||
|
private:
|
||
|
// The current depth. Simple data types will have depth 0, whereas
|
||
|
// the elements of aggregates will have depth 1. Embedded types
|
||
|
// will have increasing depth.
|
||
|
std::size_t depth_;
|
||
|
|
||
|
// The parser supports up to 5 levels of nested structures. The
|
||
|
// first element in the sizes stack is a sentinel and must be
|
||
|
// different from 1.
|
||
|
std::array<std::size_t, max_embedded_depth + 1> sizes_;
|
||
|
|
||
|
// Contains the length expected in the next bulk read.
|
||
|
std::size_t bulk_length_;
|
||
|
|
||
|
// The type of the next bulk. Contains type::invalid if no bulk is
|
||
|
// expected.
|
||
|
type bulk_;
|
||
|
|
||
|
// The number of bytes consumed from the buffer.
|
||
|
std::size_t consumed_;
|
||
|
|
||
|
// Returns the number of bytes that have been consumed.
|
||
|
auto consume_impl(type t, std::string_view elem, system::error_code& ec) -> node_type;
|
||
|
|
||
|
void commit_elem() noexcept;
|
||
|
|
||
|
// The bulk type expected in the next read. If none is expected
|
||
|
// returns type::invalid.
|
||
|
[[nodiscard]]
|
||
|
auto bulk_expected() const noexcept -> bool
|
||
|
{ return bulk_ != type::invalid; }
|
||
|
|
||
|
public:
|
||
|
parser();
|
||
|
|
||
|
// Returns true when the parser is done with the current message.
|
||
|
[[nodiscard]]
|
||
|
auto done() const noexcept -> bool;
|
||
|
|
||
|
auto get_suggested_buffer_growth(std::size_t hint) const noexcept -> std::size_t;
|
||
|
|
||
|
auto get_consumed() const noexcept -> std::size_t;
|
||
|
|
||
|
auto consume(std::string_view view, system::error_code& ec) noexcept -> result;
|
||
|
|
||
|
void reset();
|
||
|
};
|
||
|
|
||
|
// Returns false if more data is needed. If true is returned the
|
||
|
// parser is either done or an error occured, that can be checked on
|
||
|
// ec.
|
||
|
template <class Adapter>
|
||
|
bool
|
||
|
parse(
|
||
|
resp3::parser& p,
|
||
|
std::string_view const& msg,
|
||
|
Adapter& adapter,
|
||
|
system::error_code& ec)
|
||
|
{
|
||
|
while (!p.done()) {
|
||
|
auto const res = p.consume(msg, ec);
|
||
|
if (ec)
|
||
|
return true;
|
||
|
|
||
|
if (!res)
|
||
|
return false;
|
||
|
|
||
|
adapter(res.value(), ec);
|
||
|
if (ec)
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
} // boost::redis::resp3
|
||
|
|
||
|
#endif // BOOST_REDIS_RESP3_PARSER_HPP
|