// Copyright (c) 2023, The Monero Project // All rights reserved. // // Redistribution and use in source and binary forms, with or without modification, are // permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, this list of // conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright notice, this list // of conditions and the following disclaimer in the documentation and/or other // materials provided with the distribution. // // 3. Neither the name of the copyright holder nor the names of its contributors may be // used to endorse or promote products derived from this software without specific // prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL // THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #pragma once #include #include #include #include #include #include #include "wire/field.h" #include "wire/msgpack/base.h" #include "wire/read.h" #include "wire/traits.h" namespace wire { //! Reads MSGPACK tokens one-at-a-time for DOMless parsing class msgpack_reader : public reader { epee::byte_slice source_; std::size_t remaining_; //!< Expected number of elements remaining //! \throw std::logic_error [[noreturn]] void throw_logic_error(); //! Decrement remaining_ if not zero, \throw std::logic_error when `remaining_ == 0`. void update_remaining() { if (remaining_) --remaining_; else throw_logic_error(); } //! Skips next value. \throw wire::exception if invalid JSON syntax. void skip_value(); //! \return Next tag but leave `source_` untouched. msgpack::tag peek_tag(); //! \return Next tag and remove first byte from `source_`. msgpack::tag get_tag(); //! \return Integer from `soure_` where positive fixed tag has been checked. std::intmax_t do_integer(msgpack::tag); //! \return Integer from `source_` where fixed tag has been checked. std::uintmax_t do_unsigned_integer(msgpack::tag); //! \return Number of items determined by `T` fixed tag and `U` tuple of tags. template std::size_t read_count(error::schema); public: explicit msgpack_reader(epee::byte_slice&& source) : reader(), source_(std::move(source)), remaining_(1) {} //! \throw wire::exception if JSON parsing is incomplete. void check_complete() const override final; //! \throw wire::exception if next token not a boolean. bool boolean() override final; //! \throw wire::expception if next token not an integer. std::intmax_t integer() override final { update_remaining(); const msgpack::tag next = get_tag(); if (std::uint8_t(next) <= msgpack::ftag_unsigned::max()) return std::uint8_t(next); return do_integer(next); } //! \throw wire::exception if next token not an unsigned integer. std::uintmax_t unsigned_integer() override final { update_remaining(); const msgpack::tag next = get_tag(); if (std::uint8_t(next) <= msgpack::ftag_unsigned::max()) return std::uint8_t(next); return do_unsigned_integer(next); } //! \throw wire::exception if next token not a valid real number double real() override final; //! \throw wire::exception if next token not a string std::string string() override final; //! \throw wire::exception if next token cannot be read as hex std::vector binary() override final; //! \throw wire::exception if next token cannot be read as hex into `dest`. void binary(epee::span dest) override final; //! \throw wire::exception if next token not `[`. std::size_t start_array() override final; //! \return true when `count == 0`. bool is_array_end(const std::size_t count) override final; //! \throw wire::exception if next token not `{`. std::size_t start_object() override final; /*! \throw wire::exception if next token not key or `}`. \param[out] index of key match within `map`. \return True if another value to read. */ bool key(epee::span map, std::size_t&, std::size_t& index) override final; }; } // wire