mirror of
https://github.com/monero-project/monero.git
synced 2024-12-24 23:19:44 -05:00
Read interface for new serialization system
This commit is contained in:
parent
cc73fe7116
commit
510010cf0c
36
contrib/epee/include/serialization/wire/adapted/asio.h
Normal file
36
contrib/epee/include/serialization/wire/adapted/asio.h
Normal file
@ -0,0 +1,36 @@
|
||||
// Copyright (c) 2021, 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 <boost/asio/ip/address_v6.hpp>
|
||||
#include "serialization/wire/fwd.h"
|
||||
|
||||
namespace wire
|
||||
{
|
||||
WIRE_DECLARE_OBJECT(boost::asio::ip::address_v6);
|
||||
}
|
@ -0,0 +1,91 @@
|
||||
// Copyright (c) 2022, 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 <cstdint>
|
||||
#include <boost/container/static_vector.hpp>
|
||||
#include <boost/utility/string_ref.hpp>
|
||||
|
||||
#include "serialization/wire/read.h"
|
||||
|
||||
namespace wire
|
||||
{
|
||||
// enable writing of static vector arrays
|
||||
template<typename T, std::size_t N>
|
||||
struct is_array<boost::container::static_vector<T, N>>
|
||||
: std::true_type
|
||||
{};
|
||||
|
||||
// `static_vector`s of `char` and `uint8_t` are not arrays
|
||||
template<std::size_t N>
|
||||
struct is_array<boost::container::static_vector<char, N>>
|
||||
: std::false_type
|
||||
{};
|
||||
template<std::size_t N>
|
||||
struct is_array<boost::container::static_vector<std::uint8_t, N>>
|
||||
: std::false_type
|
||||
{};
|
||||
|
||||
// `static_vector` can be used without specialized macro for every type, it provides max element count
|
||||
template<typename R, typename T, std::size_t N>
|
||||
inline void read_bytes(R& source, boost::container::static_vector<T, N>& dest)
|
||||
{
|
||||
wire_read::array(source, dest, min_element_size<0>{}, max_element_count<N>{});
|
||||
}
|
||||
|
||||
/* `static_vector` never allocates, so it is useful for reading small variable
|
||||
sized strings or binary data with a known fixed max. `char` and
|
||||
`std::uint8_t` are not valid types for arrays in this design anyway
|
||||
(because its clearly less efficient in every encoding scheme). */
|
||||
|
||||
template<typename R, std::size_t N>
|
||||
inline void read_bytes(R& source, boost::container::static_vector<char, N>& dest)
|
||||
{
|
||||
dest.resize(N);
|
||||
dest.resize(source.string(epee::to_mut_span(dest), /* exact= */ false));
|
||||
}
|
||||
|
||||
template<typename W, std::size_t N>
|
||||
inline void write_bytes(W& dest, const boost::container::static_vector<char, N>& source)
|
||||
{
|
||||
dest.string(boost::string_ref{source.data(), source.size()});
|
||||
}
|
||||
|
||||
template<typename R, std::size_t N>
|
||||
inline void read_bytes(R& source, boost::container::static_vector<std::uint8_t, N>& dest)
|
||||
{
|
||||
dest.resize(N);
|
||||
dest.resize(source.binary(epee::to_mut_span(dest), /* exact= */ false));
|
||||
}
|
||||
|
||||
template<typename W, std::size_t N>
|
||||
inline void write_bytes(W& dest, const boost::container::static_vector<std::uint8_t, N>& source)
|
||||
{
|
||||
dest.binary(epee::to_span(source));
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
// Copyright (c) 2022, 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 <unordered_map>
|
||||
#include "serialization/wire/write.h"
|
||||
|
||||
namespace wire
|
||||
{
|
||||
// no `read_bytes`, requires implementation with "array read constraints"
|
||||
|
||||
template<typename W, typename K, typename V, typename H, typename E, typename A>
|
||||
inline void write_bytes(W& dest, const std::unordered_map<K, V, H, E, A>& source)
|
||||
{ wire_write::dynamic_object(dest, source); }
|
||||
}
|
64
contrib/epee/include/serialization/wire/basic_value.h
Normal file
64
contrib/epee/include/serialization/wire/basic_value.h
Normal file
@ -0,0 +1,64 @@
|
||||
// Copyright (c) 2022, 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 <boost/variant/variant.hpp>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#include "serialization/wire/fwd.h"
|
||||
|
||||
namespace wire
|
||||
{
|
||||
/*! Can hold any non-recursive value. Implements optional field concept
|
||||
requirements. If used in a `optional_field`, the `nullptr` type/value
|
||||
determines whether the field name is omitted or present in an object. If used
|
||||
in a `field` (required), the field name is always present in the object, and
|
||||
the value could be `null`/`nil`. */
|
||||
struct basic_value
|
||||
{
|
||||
using variant_type =
|
||||
boost::variant<std::nullptr_t, bool, std::uintmax_t, std::intmax_t, double, std::string>;
|
||||
|
||||
variant_type value;
|
||||
|
||||
// concept requirements for optional fields
|
||||
|
||||
explicit operator bool() const noexcept { return value != variant_type{nullptr}; }
|
||||
basic_value& emplace() noexcept { return *this; }
|
||||
|
||||
basic_value& operator*() noexcept { return *this; }
|
||||
const basic_value& operator*() const noexcept { return *this; }
|
||||
|
||||
void reset();
|
||||
};
|
||||
|
||||
void read_bytes(reader& source, basic_value& dest);
|
||||
void write_bytes(writer& dest, const basic_value& source);
|
||||
} // wire
|
@ -32,9 +32,13 @@
|
||||
|
||||
#include "serialization/wire/traits.h"
|
||||
|
||||
//! A required field with the same key name and C/C++ name
|
||||
#define WIRE_FIELD_ID(id, name) \
|
||||
::wire::field< id >( #name , std::ref( self . name ))
|
||||
|
||||
//! A required field has the same key name and C/C++ name
|
||||
#define WIRE_FIELD(name) \
|
||||
::wire::field( #name , std::ref( self . name ))
|
||||
#define WIRE_FIELD(name) \
|
||||
WIRE_FIELD_ID(0, name)
|
||||
|
||||
//! A required field has the same key name and C/C++ name AND is cheap to copy (faster output).
|
||||
#define WIRE_FIELD_COPY(name) \
|
||||
@ -92,7 +96,7 @@ namespace wire
|
||||
|
||||
Basically each input/output format needs a unique type so that the compiler
|
||||
knows how to "dispatch" the read/write calls. */
|
||||
template<typename T, bool Required>
|
||||
template<typename T, bool Required, unsigned I = 0>
|
||||
struct field_
|
||||
{
|
||||
using value_type = unwrap_reference_t<T>;
|
||||
@ -103,6 +107,7 @@ namespace wire
|
||||
|
||||
static constexpr bool is_required() noexcept { return Required && !optional_on_empty(); }
|
||||
static constexpr std::size_t count() noexcept { return 1; }
|
||||
static constexpr unsigned id() noexcept { return I; }
|
||||
|
||||
const char* name;
|
||||
T value;
|
||||
@ -112,15 +117,15 @@ namespace wire
|
||||
};
|
||||
|
||||
//! Links `name` to `value`. Use `std::ref` if de-serializing.
|
||||
template<typename T>
|
||||
constexpr inline field_<T, true> field(const char* name, T value)
|
||||
template<unsigned I = 0, typename T = void>
|
||||
constexpr inline field_<T, true, I> field(const char* name, T value)
|
||||
{
|
||||
return {name, std::move(value)};
|
||||
}
|
||||
|
||||
//! Links `name` to optional `value`. Use `std::ref` if de-serializing.
|
||||
template<typename T>
|
||||
constexpr inline field_<T, false> optional_field(const char* name, T value)
|
||||
template<unsigned I = 0, typename T = void>
|
||||
constexpr inline field_<T, false, I> optional_field(const char* name, T value)
|
||||
{
|
||||
return {name, std::move(value)};
|
||||
}
|
||||
|
547
contrib/epee/include/serialization/wire/read.h
Normal file
547
contrib/epee/include/serialization/wire/read.h
Normal file
@ -0,0 +1,547 @@
|
||||
// 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 <cstdint>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
#include "byte_slice.h"
|
||||
#include "serialization/wire/epee/fwd.h"
|
||||
#include "serialization/wire/error.h"
|
||||
#include "serialization/wire/field.h"
|
||||
#include "serialization/wire/fwd.h"
|
||||
#include "serialization/wire/traits.h"
|
||||
#include "span.h"
|
||||
|
||||
/*
|
||||
Custom types (e.g. `type` in namespace `ns`) can define an input function by:
|
||||
* `namespace wire { template<> struct is_blob<ns::type> : std::true_type {}; }`
|
||||
* `namespace wire { void read_bytes(writer&, ns::type&); }`
|
||||
* `namespace ns { void read_bytes(wire::writer&, type&); }`
|
||||
|
||||
See `traits.h` for `is_blob` requirements. `read_bytes` function can also
|
||||
specify derived type for faster output (i.e.
|
||||
`namespace ns { void read_bytes(wire::epee_reader&, type&); }`). Using the
|
||||
derived type allows the compiler to de-virtualize and allows for custom
|
||||
functions not defined by base interface. Using base interface allows for
|
||||
multiple formats with minimal instruction count. */
|
||||
|
||||
namespace wire
|
||||
{
|
||||
//! Interface for converting "wire" (byte) formats to C/C++ objects without a DOM.
|
||||
class reader
|
||||
{
|
||||
std::size_t depth_; //!< Tracks number of recursive objects and arrays
|
||||
|
||||
//! \throw wire::exception if max depth is reached
|
||||
void increment_depth();
|
||||
//! \throw std::logic_error if already `depth() == 0`.
|
||||
void decrement_depth();
|
||||
|
||||
/*! \param min_element_size of each array element in any format - if known.
|
||||
Derived types with explicit element count should verify available
|
||||
space, and throw a `wire::exception` on issues.
|
||||
\throw wire::exception if next value not array
|
||||
\throw wire::exception if not enough bytes for all array elements
|
||||
(with epee/msgpack which has specified number of elements).
|
||||
\return Number of values to read before calling `is_array_end()`. */
|
||||
virtual std::size_t do_start_array(std::size_t min_element_size) = 0;
|
||||
|
||||
//! \throw wire::exception if not object begin. \return State to be given to `key(...)` function.
|
||||
virtual std::size_t do_start_object() = 0;
|
||||
|
||||
protected:
|
||||
epee::span<const std::uint8_t> remaining_; //!< Derived class tracks unprocessed bytes here
|
||||
|
||||
reader(const epee::span<const std::uint8_t> remaining) noexcept
|
||||
: depth_(0), remaining_(remaining)
|
||||
{}
|
||||
|
||||
reader(const reader&) = delete;
|
||||
reader(reader&&) = delete;
|
||||
reader& operator=(const reader&) = delete;
|
||||
reader& operator=(reader&&) = delete;
|
||||
|
||||
public:
|
||||
struct key_map
|
||||
{
|
||||
const char* name;
|
||||
unsigned id; //<! For integer key formats;
|
||||
};
|
||||
|
||||
//! \return Maximum read depth for both objects and arrays before erroring
|
||||
static constexpr std::size_t max_read_depth() noexcept { return 100; }
|
||||
|
||||
//! \return Assume delimited arrays in generic interface (some optimizations disabled)
|
||||
static constexpr std::true_type delimited_arrays() noexcept { return {}; }
|
||||
|
||||
virtual ~reader() noexcept
|
||||
{}
|
||||
|
||||
//! \return Number of recursive objects and arrays
|
||||
std::size_t depth() const noexcept { return depth_; }
|
||||
|
||||
//! \return Unprocessed bytes
|
||||
epee::span<const std::uint8_t> remaining() const noexcept { return remaining_; }
|
||||
|
||||
//! \throw wire::exception if parsing is incomplete.
|
||||
virtual void check_complete() const = 0;
|
||||
|
||||
//! \throw wire::exception if array, object, or end of stream.
|
||||
virtual basic_value basic() = 0;
|
||||
|
||||
//! \throw wire::exception if next value not a boolean.
|
||||
virtual bool boolean() = 0;
|
||||
|
||||
//! \throw wire::expception if next value not an integer.
|
||||
virtual std::intmax_t integer() = 0;
|
||||
|
||||
//! \throw wire::exception if next value not an unsigned integer.
|
||||
virtual std::uintmax_t unsigned_integer() = 0;
|
||||
|
||||
//! \throw wire::exception if next value not number
|
||||
virtual double real() = 0;
|
||||
|
||||
//! throw wire::exception if next value not string
|
||||
virtual std::string string() = 0;
|
||||
|
||||
/*! Copy upcoming string directly into `dest`.
|
||||
\throw wire::exception if next value not string
|
||||
\throw wire::exception if next string exceeds `dest.size())`
|
||||
\throw wire::exception if `exact == true` and next string is not `dest.size()`
|
||||
\return Number of bytes read into `dest`. */
|
||||
virtual std::size_t string(epee::span<char> dest, bool exact) = 0;
|
||||
|
||||
// ! \throw wire::exception if next value cannot be read as binary
|
||||
virtual epee::byte_slice binary() = 0;
|
||||
|
||||
/*! Copy upcoming binary directly into `dest`.
|
||||
\throw wire::exception if next value not binary
|
||||
\throw wire::exception if next binary exceeds `dest.size()`
|
||||
\throw wire::exception if `exact == true` and next binary is not `dest.size()`.
|
||||
\return Number of bytes read into `dest`. */
|
||||
virtual std::size_t binary(epee::span<std::uint8_t> dest, const bool exact) = 0;
|
||||
|
||||
/*! \param min_element_size of each array element in any format - if known.
|
||||
Derived types with explicit element count should verify available
|
||||
space, and throw a `wire::exception` on issues.
|
||||
\throw wire::exception if next value not array
|
||||
\throw wire::exception if not enough bytes for all array elements
|
||||
(with epee/msgpack which has specified number of elements).
|
||||
\return Number of values to read before calling `is_array_end()`. */
|
||||
std::size_t start_array(std::size_t min_element_size);
|
||||
|
||||
//! \return True if there is another element to read.
|
||||
virtual bool is_array_end(std::size_t count) = 0;
|
||||
|
||||
void end_array() { decrement_depth(); }
|
||||
|
||||
|
||||
//! \throw wire::exception if not object begin. \return State to be given to `key(...)` function.
|
||||
std::size_t start_object();
|
||||
|
||||
/*! Read a key of an object field and match against a known list of keys.
|
||||
Skips or throws exceptions on unknown fields depending on implementation
|
||||
settings.
|
||||
|
||||
\param map of known keys (strings and integer) that are valid.
|
||||
\param[in,out] state returned by `start_object()` or `key(...)` whichever
|
||||
was last.
|
||||
\param[out] index of match found in `map`.
|
||||
|
||||
\throw wire::exception if next value not a key.
|
||||
\throw wire::exception if next key not found in `map` and skipping
|
||||
fields disabled.
|
||||
|
||||
\return True if this function found a field in `map` to process.
|
||||
*/
|
||||
virtual bool key(epee::span<const key_map> map, std::size_t& state, std::size_t& index) = 0;
|
||||
|
||||
void end_object() { decrement_depth(); }
|
||||
};
|
||||
|
||||
template<typename R>
|
||||
inline void read_bytes(R& source, bool& dest)
|
||||
{ dest = source.boolean(); }
|
||||
|
||||
template<typename R>
|
||||
inline void read_bytes(R& source, double& dest)
|
||||
{ dest = source.real(); }
|
||||
|
||||
template<typename R>
|
||||
inline void read_bytes(R& source, std::string& dest)
|
||||
{ dest = source.string(); }
|
||||
|
||||
template<typename R>
|
||||
inline void read_bytes(R& source, epee::byte_slice& dest)
|
||||
{ dest = source.binary(); }
|
||||
|
||||
template<typename R, typename T>
|
||||
inline std::enable_if_t<is_blob<T>::value> read_bytes(R& source, T& dest)
|
||||
{ source.binary(epee::as_mut_byte_span(dest), /*exact=*/ true); }
|
||||
|
||||
//! Use `read_bytes(...)` method if available for `T`.
|
||||
template<typename R, typename T>
|
||||
inline auto read_bytes(R& source, T& dest) -> decltype(dest.read_bytes(source))
|
||||
{ return dest.read_bytes(source); }
|
||||
|
||||
namespace integer
|
||||
{
|
||||
[[noreturn]] void throw_exception(std::intmax_t value, std::intmax_t min, std::intmax_t max);
|
||||
[[noreturn]] void throw_exception(std::uintmax_t value, std::uintmax_t max);
|
||||
|
||||
template<typename T, typename U>
|
||||
inline T cast_signed(const U source)
|
||||
{
|
||||
using limit = std::numeric_limits<T>;
|
||||
static_assert(
|
||||
std::is_signed<T>::value && std::is_integral<T>::value,
|
||||
"target must be signed integer type"
|
||||
);
|
||||
static_assert(
|
||||
std::is_signed<U>::value && std::is_integral<U>::value,
|
||||
"source must be signed integer type"
|
||||
);
|
||||
if (source < limit::min() || limit::max() < source)
|
||||
throw_exception(source, limit::min(), limit::max());
|
||||
return static_cast<T>(source);
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
inline T cast_unsigned(const U source)
|
||||
{
|
||||
using limit = std::numeric_limits<T>;
|
||||
static_assert(
|
||||
std::is_unsigned<T>::value && std::is_integral<T>::value,
|
||||
"target must be unsigned integer type"
|
||||
);
|
||||
static_assert(
|
||||
std::is_unsigned<U>::value && std::is_integral<U>::value,
|
||||
"source must be unsigned integer type"
|
||||
);
|
||||
if (limit::max() < source)
|
||||
throw_exception(source, limit::max());
|
||||
return static_cast<T>(source);
|
||||
}
|
||||
} // integer
|
||||
|
||||
//! read all current and future signed integer types
|
||||
template<typename R, typename T>
|
||||
inline std::enable_if_t<std::is_signed<T>::value && std::is_integral<T>::value>
|
||||
read_bytes(R& source, T& dest)
|
||||
{
|
||||
dest = integer::cast_signed<T>(source.integer());
|
||||
}
|
||||
|
||||
//! read all current and future unsigned integer types
|
||||
template<typename R, typename T>
|
||||
inline std::enable_if_t<std::is_unsigned<T>::value && std::is_integral<T>::value>
|
||||
read_bytes(R& source, T& dest)
|
||||
{
|
||||
dest = integer::cast_unsigned<T>(source.unsigned_integer());
|
||||
}
|
||||
} // wire
|
||||
|
||||
namespace wire_read
|
||||
{
|
||||
/*! Don't add a function called `read_bytes` to this namespace, it will
|
||||
prevent 2-phase lookup. 2-phase lookup delays the function searching until
|
||||
the template is used instead of when its defined. This allows the
|
||||
unqualified calls to `read_bytes` in this namespace to "find" user
|
||||
functions that are declared after these functions (the technique behind
|
||||
`boost::serialization`). */
|
||||
|
||||
[[noreturn]] void throw_exception(wire::error::schema code, const char* display, epee::span<char const* const> name_list);
|
||||
|
||||
template<typename R, typename T>
|
||||
inline void bytes(R& source, T&& dest)
|
||||
{
|
||||
read_bytes(source, dest); // ADL (searches every associated namespace)
|
||||
}
|
||||
|
||||
//! Use `source` to store information at `dest`
|
||||
template<typename R, typename T, typename U>
|
||||
inline std::error_code from_bytes(T&& source, U& dest)
|
||||
{
|
||||
if (wire::is_optional_root<U>::value && source.empty())
|
||||
return {};
|
||||
|
||||
try
|
||||
{
|
||||
R in{std::forward<T>(source)};
|
||||
bytes(in, dest);
|
||||
in.check_complete();
|
||||
}
|
||||
catch (const wire::exception& e)
|
||||
{
|
||||
return e.code();
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
// Trap objects that do not have standard insertion functions
|
||||
template<typename R, typename... T>
|
||||
void array_insert(const R&, const T&...) noexcept
|
||||
{
|
||||
static_assert(std::is_same<R, void>::value, "type T does not have a valid insertion function");
|
||||
}
|
||||
|
||||
// Insert to sorted containers
|
||||
template<typename R, typename T, typename V = typename T::value_type>
|
||||
inline auto array_insert(R& source, T& dest) -> decltype(dest.emplace_hint(dest.end(), std::declval<V>()), bool(true))
|
||||
{
|
||||
V val{};
|
||||
wire_read::bytes(source, val);
|
||||
dest.emplace_hint(dest.end(), std::move(val));
|
||||
return true;
|
||||
}
|
||||
|
||||
// Insert into unsorted containers
|
||||
template<typename R, typename T>
|
||||
inline auto array_insert(R& source, T& dest) -> decltype(dest.emplace_back(), dest.back(), bool(true))
|
||||
{
|
||||
// more efficient to process the object in-place in many cases
|
||||
dest.emplace_back();
|
||||
wire_read::bytes(source, dest.back());
|
||||
return true;
|
||||
}
|
||||
|
||||
// no compile-time checks for the array constraints
|
||||
template<typename R, typename T>
|
||||
inline void array_unchecked(R& source, T& dest, const std::size_t min_element_size, const std::size_t max_element_count)
|
||||
{
|
||||
using value_type = typename T::value_type;
|
||||
static_assert(!std::is_same<value_type, char>::value, "read array of chars as string");
|
||||
static_assert(!std::is_same<value_type, std::int8_t>::value, "read array of signed chars as binary");
|
||||
static_assert(!std::is_same<value_type, std::uint8_t>::value, "read array of unsigned chars as binary");
|
||||
|
||||
std::size_t count = source.start_array(min_element_size);
|
||||
|
||||
// quick check for epee/msgpack formats
|
||||
if (max_element_count < count)
|
||||
throw_exception(wire::error::schema::array_max_element, "", nullptr);
|
||||
|
||||
dest.clear();
|
||||
wire::reserve(dest, count);
|
||||
|
||||
bool more = count;
|
||||
const std::size_t start_bytes = source.remaining().size();
|
||||
while (more || !source.is_array_end(count))
|
||||
{
|
||||
// check for json/cbor formats
|
||||
if (source.delimited_arrays() && max_element_count <= dest.size())
|
||||
throw_exception(wire::error::schema::array_max_element, "", nullptr);
|
||||
|
||||
wire_read::array_insert(source, dest);
|
||||
--count;
|
||||
more &= bool(count);
|
||||
|
||||
if (((start_bytes - source.remaining().size()) / dest.size()) < min_element_size)
|
||||
throw_exception(wire::error::schema::array_min_size, "", nullptr);
|
||||
}
|
||||
|
||||
source.end_array();
|
||||
}
|
||||
|
||||
template<typename R, typename T, std::size_t M, std::size_t N = std::numeric_limits<std::size_t>::max()>
|
||||
inline void array(R& source, T& dest, wire::min_element_size<M> min_element_size, wire::max_element_count<N> max_element_count = {})
|
||||
{
|
||||
using value_type = typename T::value_type;
|
||||
static_assert(
|
||||
min_element_size.template check<value_type>() || max_element_count.template check<value_type>(),
|
||||
"array unpacking memory issues"
|
||||
);
|
||||
// each set of template args generates unique ASM, merge them down
|
||||
array_unchecked(source, dest, min_element_size, max_element_count);
|
||||
}
|
||||
|
||||
template<typename T, unsigned I>
|
||||
inline void reset_field(wire::field_<T, true, I>& dest)
|
||||
{
|
||||
// array fields are always optional, see `wire/field.h`
|
||||
if (dest.optional_on_empty())
|
||||
wire::clear(dest.get_value());
|
||||
}
|
||||
|
||||
template<typename T, unsigned I>
|
||||
inline void reset_field(wire::field_<T, false, I>& dest)
|
||||
{
|
||||
dest.get_value().reset();
|
||||
}
|
||||
|
||||
template<typename R, typename T>
|
||||
inline void unpack_field(R& source, wire::field_<T, true>& dest)
|
||||
{
|
||||
bytes(source, dest.get_value());
|
||||
}
|
||||
|
||||
template<typename R, typename T>
|
||||
inline void unpack_field(R& source, wire::field_<T, false>& dest)
|
||||
{
|
||||
if (!bool(dest.get_value()))
|
||||
dest.get_value().emplace();
|
||||
bytes(source, *dest.get_value());
|
||||
}
|
||||
|
||||
//! Tracks read status of every object field instance.
|
||||
template<typename T>
|
||||
class tracker
|
||||
{
|
||||
T field_;
|
||||
std::size_t our_index_;
|
||||
bool read_;
|
||||
|
||||
public:
|
||||
static constexpr bool is_required() noexcept { return T::is_required(); }
|
||||
static constexpr std::size_t count() noexcept { return T::count(); }
|
||||
|
||||
explicit tracker(T field)
|
||||
: field_(std::move(field)), our_index_(0), read_(false)
|
||||
{}
|
||||
|
||||
//! \return Field name if required and not read, otherwise `nullptr`.
|
||||
const char* name_if_missing() const noexcept
|
||||
{
|
||||
return (is_required() && !read_) ? field_.name : nullptr;
|
||||
}
|
||||
|
||||
//! Set all entries in `map` related to this field (expand variant types!).
|
||||
template<std::size_t N>
|
||||
std::size_t set_mapping(std::size_t index, wire::reader::key_map (&map)[N]) noexcept
|
||||
{
|
||||
our_index_ = index;
|
||||
map[index].id = field_.id();
|
||||
map[index].name = field_.name;
|
||||
return index + count();
|
||||
}
|
||||
|
||||
//! Try to read next value if `index` matches `this`. \return 0 if no match, 1 if optional field read, and 2 if required field read
|
||||
template<typename R>
|
||||
std::size_t try_read(R& source, const std::size_t index)
|
||||
{
|
||||
if (index < our_index_ || our_index_ + count() <= index)
|
||||
return 0;
|
||||
if (read_)
|
||||
throw_exception(wire::error::schema::invalid_key, "duplicate", {std::addressof(field_.name), 1});
|
||||
|
||||
unpack_field(source, field_);
|
||||
read_ = true;
|
||||
return 1 + is_required();
|
||||
}
|
||||
|
||||
//! Reset optional fields that were skipped
|
||||
bool reset_omitted()
|
||||
{
|
||||
if (!is_required() && !read_)
|
||||
reset_field(field_);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
// `expand_tracker_map` writes all `tracker` types to a table
|
||||
|
||||
template<std::size_t N>
|
||||
inline void expand_tracker_map(std::size_t index, const wire::reader::key_map (&)[N]) noexcept
|
||||
{}
|
||||
|
||||
template<std::size_t N, typename T, typename... U>
|
||||
inline void expand_tracker_map(std::size_t index, wire::reader::key_map (&map)[N], tracker<T>& head, tracker<U>&... tail) noexcept
|
||||
{
|
||||
expand_tracker_map(head.set_mapping(index, map), map, tail...);
|
||||
}
|
||||
|
||||
template<typename R, typename... T>
|
||||
inline void object(R& source, tracker<T>... fields)
|
||||
{
|
||||
static constexpr const std::size_t total_subfields = wire::sum(fields.count()...);
|
||||
static_assert(total_subfields < 100, "algorithm uses too much stack space and linear searching");
|
||||
static_assert(sizeof...(T) <= total_subfields, "subfield parameter pack size mismatch");
|
||||
|
||||
std::size_t state = source.start_object();
|
||||
std::size_t required = wire::sum(std::size_t(fields.is_required())...);
|
||||
|
||||
wire::reader::key_map map[total_subfields + 1] = {}; // +1 for empty object
|
||||
expand_tracker_map(0, map, fields...);
|
||||
|
||||
std::size_t next = 0;
|
||||
while (source.key({map, total_subfields}, state, next))
|
||||
{
|
||||
switch (wire::sum(fields.try_read(source, next)...))
|
||||
{
|
||||
default:
|
||||
case 0:
|
||||
throw_exception(wire::error::schema::invalid_key, "bad map setup", nullptr);
|
||||
break;
|
||||
case 2:
|
||||
--required; /* fallthrough */
|
||||
case 1:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (required)
|
||||
{
|
||||
const char* missing[] = {fields.name_if_missing()..., nullptr};
|
||||
throw_exception(wire::error::schema::missing_key, "", missing);
|
||||
}
|
||||
|
||||
wire::sum(fields.reset_omitted()...);
|
||||
source.end_object();
|
||||
}
|
||||
} // wire_read
|
||||
|
||||
namespace wire
|
||||
{
|
||||
template<typename R, typename T>
|
||||
inline std::enable_if_t<is_array<T>::value> read_bytes(R& source, T& dest)
|
||||
{
|
||||
static constexpr const std::size_t wire_size =
|
||||
default_min_element_size<R, typename T::value_type>::value;
|
||||
static_assert(
|
||||
wire_size != 0,
|
||||
"no sane default array constraints for the reader / value_type pair"
|
||||
);
|
||||
|
||||
wire_read::array(source, dest, min_element_size<wire_size>{});
|
||||
}
|
||||
|
||||
template<typename R, typename... T>
|
||||
inline std::enable_if_t<std::is_base_of<reader, R>::value> object(R& source, T... fields)
|
||||
{
|
||||
wire_read::object(source, wire_read::tracker<T>{std::move(fields)}...);
|
||||
}
|
||||
|
||||
template<typename R, typename... T>
|
||||
inline void object_fwd(const std::true_type /*is_read*/, R& source, T&&... fields)
|
||||
{
|
||||
wire::object(source, std::forward<T>(fields)...);
|
||||
}
|
||||
}
|
161
contrib/epee/include/serialization/wire/wrapper/array.h
Normal file
161
contrib/epee/include/serialization/wire/wrapper/array.h
Normal file
@ -0,0 +1,161 @@
|
||||
// Copyright (c) 2021, 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 <cstdint>
|
||||
#include <utility>
|
||||
|
||||
#include "serialization/wire/field.h"
|
||||
#include "serialization/wire/traits.h"
|
||||
|
||||
/*! An array field with read constraint. See `array_` for more info. All (empty)
|
||||
arrays were "optional" (omitted) historically in epee, so this matches prior
|
||||
behavior. */
|
||||
#define WIRE_FIELD_ARRAY(name, read_constraint) \
|
||||
::wire::optional_field( #name , ::wire::array< read_constraint >(std::ref( self . name )))
|
||||
|
||||
namespace wire
|
||||
{
|
||||
/*! A wrapper that ensures `T` is written as an array, with `C` constraints
|
||||
when reading (`max_element_count` or `min_element_size`). `C` can be `void`
|
||||
if write-only.
|
||||
|
||||
This wrapper meets the requirements for an optional field; `wire::field`
|
||||
and `wire::optional_field` determine whether an empty array must be
|
||||
encoded on the wire. Historically, empty arrays were always omitted on
|
||||
the wire (a defacto optional field).
|
||||
|
||||
The `is_array` trait can also be used, but is default treated as an optional
|
||||
field. The trait `is_optional_on_empty` traits can be specialized to disable
|
||||
the optional on empty behavior. See `wire/traits.h` for more ifnormation
|
||||
on the `is_optional_on_empty` trait.
|
||||
|
||||
`container_type` is `T` with optional `std::reference_wrapper` removed.
|
||||
`container_type` concept requirements:
|
||||
* `typedef` `value_type` that specifies inner type.
|
||||
* must have `size()` method that returns number of elements.
|
||||
Additional concept requirements for `container_type` when reading:
|
||||
* must have `clear()` method that removes all elements (`size() == 0`).
|
||||
* must have either: (1) `end()` and `emplace_hint(iterator, value_type&&)`
|
||||
or (2) `emplace_back()` and `back()`:
|
||||
* `end()` method that returns one-past the last element.
|
||||
* `emplace_hint(iterator, value_type&&)` method that move constructs a new
|
||||
element.
|
||||
* `emplace_back()` method that default initializes new element
|
||||
* `back()` method that retrieves last element by reference.
|
||||
Additional concept requirements for `container_type` when writing:
|
||||
* must work with foreach loop (`std::begin` and `std::end`).
|
||||
* must work with `boost::size` (from the `boost::range` library). */
|
||||
template<typename T, typename C = void>
|
||||
struct array_
|
||||
{
|
||||
using constraint = C;
|
||||
using container_type = unwrap_reference_t<T>;
|
||||
using value_type = typename container_type::value_type;
|
||||
|
||||
// See nested `array_` overload below
|
||||
using inner_array = std::reference_wrapper<value_type>;
|
||||
using inner_array_const = std::reference_wrapper<const value_type>;
|
||||
|
||||
T container;
|
||||
|
||||
constexpr const container_type& get_container() const noexcept { return container; }
|
||||
container_type& get_container() noexcept { return container; }
|
||||
|
||||
//! Read directly into the non-nested array
|
||||
container_type& get_read_object() noexcept { return get_container(); }
|
||||
|
||||
|
||||
// concept requirements for optional fields
|
||||
|
||||
explicit operator bool() const noexcept { return !get_container().empty(); }
|
||||
array_& emplace() noexcept { return *this; }
|
||||
|
||||
array_& operator*() noexcept { return *this; }
|
||||
const array_& operator*() const noexcept { return *this; }
|
||||
|
||||
void reset() { get_container().clear(); }
|
||||
};
|
||||
|
||||
//! Nested array case
|
||||
template<typename T, typename C, typename D>
|
||||
struct array_<array_<T, C>, D>
|
||||
{
|
||||
// compute `container_type` and `value_type` recursively
|
||||
using constraint = D;
|
||||
using container_type = typename array_<T, C>::container_type;
|
||||
using value_type = typename container_type::value_type;
|
||||
|
||||
// Re-compute `array_` for inner values
|
||||
using inner_array = array_<typename array_<T, C>::inner_array, C>;
|
||||
using inner_array_const = array_<typename array_<T, C>::inner_array_const, C>;
|
||||
|
||||
array_<T, C> nested;
|
||||
|
||||
const container_type& get_container() const noexcept { return nested.get_container(); }
|
||||
container_type& get_container() noexcept { return nested.get_container(); }
|
||||
|
||||
//! Read through this proxy to track nested array
|
||||
array_& get_read_object() noexcept { return *this; }
|
||||
|
||||
|
||||
// concept requirements for optional fields
|
||||
|
||||
explicit operator bool() const noexcept { return !empty(); }
|
||||
array_& emplace() noexcept { return *this; }
|
||||
|
||||
array_& operator*() noexcept { return *this; }
|
||||
const array_& operator*() const noexcept { return *this; }
|
||||
|
||||
void reset() { clear(); }
|
||||
|
||||
|
||||
/* For reading nested arrays. writing nested arrays is handled in
|
||||
`wrappers_impl.h` with range transform. */
|
||||
|
||||
void clear() { get_container().clear(); }
|
||||
bool empty() const noexcept { return get_container().empty(); }
|
||||
std::size_t size() const noexcept { return get_container().size(); }
|
||||
|
||||
void emplace_back() { get_container().emplace_back(); }
|
||||
|
||||
//! \return A proxy object for tracking inner-array constraints
|
||||
inner_array back() noexcept { return {std::ref(get_container().back())}; }
|
||||
};
|
||||
|
||||
//! Treat `value` as an array when reading/writing, and constrain reading with `C`.
|
||||
template<typename C = void, typename T = void>
|
||||
inline constexpr array_<T, C> array(T value)
|
||||
{
|
||||
return {std::move(value)};
|
||||
}
|
||||
|
||||
/* Do not register with `is_optional_on_empty` trait, this allows selection
|
||||
on whether an array is mandatory on wire. */
|
||||
|
||||
} // wire
|
95
contrib/epee/include/serialization/wire/wrapper/array_blob.h
Normal file
95
contrib/epee/include/serialization/wire/wrapper/array_blob.h
Normal file
@ -0,0 +1,95 @@
|
||||
// Copyright (c) 2021, 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 <cstdint>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include "serialization/wire/field.h"
|
||||
#include "serialization/wire/traits.h"
|
||||
|
||||
/*! A required field, where the array contents are written as a single binary
|
||||
blob. All (empty) arrays-as-blobs were "optional" (omitted) historically in
|
||||
epee, so this matches prior behavior. */
|
||||
#define WIRE_FIELD_ARRAY_AS_BLOB(name) \
|
||||
::wire::optional_field( #name , ::wire::array_as_blob(std::ref( self . name )))
|
||||
|
||||
namespace wire
|
||||
{
|
||||
/*! A wrapper that tells `wire::writer`s` and `wire::reader`s to encode a
|
||||
container as a single binary blob. This wrapper meets the requirements for
|
||||
an optional field - currently the type is always considered optional by
|
||||
the input/output system to match the old input/output engine.
|
||||
|
||||
`container_type` is `T` with optional `std::reference_wrapper` removed.
|
||||
`container_type` concept requirements:
|
||||
* `typedef` `value_type` that specifies inner type.
|
||||
* `std::is_pod<value_type>::value` must be true.
|
||||
Additional concept requirements for `container_type` when reading:
|
||||
* must have `clear()` method that removes all elements (`size() == 0`).
|
||||
* must have `emplace_back()` method that default initializes new element
|
||||
* must have `back()` method that retrieves last element by reference.
|
||||
Additional concept requirements for `container_type` when writing:
|
||||
* must work with foreach loop (`std::begin` and `std::end`).
|
||||
* must have `size()` method that returns number of elements. */
|
||||
template<typename T>
|
||||
struct array_as_blob_
|
||||
{
|
||||
using container_type = unwrap_reference_t<T>;
|
||||
using value_type = typename container_type::value_type;
|
||||
static constexpr std::size_t value_size() noexcept { return sizeof(value_type); }
|
||||
static_assert(std::is_pod<value_type>::value, "container value must be POD");
|
||||
|
||||
T container;
|
||||
|
||||
constexpr const container_type& get_container() const noexcept { return container; }
|
||||
container_type& get_container() noexcept { return container; }
|
||||
|
||||
// concept requirements for optional fields
|
||||
|
||||
explicit operator bool() const noexcept { return !get_container().empty(); }
|
||||
container_type& emplace() noexcept { return get_container(); }
|
||||
|
||||
array_as_blob_& operator*() noexcept { return *this; }
|
||||
const array_as_blob_& operator*() const noexcept { return *this; }
|
||||
|
||||
void reset() { get_container().clear(); }
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
inline array_as_blob_<T> array_as_blob(T value)
|
||||
{
|
||||
return {std::move(value)};
|
||||
}
|
||||
|
||||
// `read_bytes` / `write_bytes` in `wire/wrappers_impl.h`
|
||||
|
||||
// Do not specialize `is_optional_on_empty`; allow selection
|
||||
|
||||
} // wire
|
87
contrib/epee/include/serialization/wire/wrapper/blob.h
Normal file
87
contrib/epee/include/serialization/wire/wrapper/blob.h
Normal file
@ -0,0 +1,87 @@
|
||||
// Copyright (c) 2022, 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 <type_traits>
|
||||
#include <utility>
|
||||
#include "serialization/wire/field.h"
|
||||
#include "serialization/wire/traits.h"
|
||||
#include "span.h"
|
||||
|
||||
//
|
||||
// Also see `wire/traits.h` to register a type as a blob (no wrapper needed).
|
||||
//
|
||||
|
||||
|
||||
/*! A required field, where the wire content is expected to be a binary blob,
|
||||
and the C++ data is a pod type with no padding. */
|
||||
#define WIRE_FIELD_BLOB(name) \
|
||||
::wire::field( #name , ::wire::blob(std::ref( self . name )))
|
||||
|
||||
namespace wire
|
||||
{
|
||||
/*! A wrapper that tells `wire::writer`s` and `wire::reader`s to encode a
|
||||
type as a binary blob. If the encoded size on the wire is not exactly the
|
||||
size of the blob, it is considered an error.
|
||||
|
||||
`value_type` is `T` with optional `std::reference_wrapper` removed.
|
||||
`value_type` concept requirements:
|
||||
* `epee::has_padding<value_type>()` must return false. */
|
||||
template<typename T>
|
||||
struct blob_
|
||||
{
|
||||
using value_type = unwrap_reference_t<T>;
|
||||
static_assert(!epee::has_padding<value_type>(), "expected safe pod type");
|
||||
|
||||
T value;
|
||||
|
||||
//! \return `value` with `std::reference_wrapper` removed.
|
||||
constexpr const value_type& get_value() const noexcept { return value; }
|
||||
|
||||
//! \return `value` with `std::reference_wrapper` removed.
|
||||
value_type& get_value() noexcept { return value; }
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
constexpr inline blob_<T> blob(T value)
|
||||
{
|
||||
return {std::move(value)};
|
||||
}
|
||||
|
||||
template<typename R, typename T>
|
||||
inline void read_bytes(R& source, blob_<T> dest)
|
||||
{
|
||||
source.binary(epee::as_mut_byte_span(dest.get_value()), /*exact=*/ true);
|
||||
}
|
||||
|
||||
template<typename W, typename T>
|
||||
inline void write_bytes(W& dest, const blob_<T> source)
|
||||
{
|
||||
dest.binary(epee::as_byte_span(source.get_value()));
|
||||
}
|
||||
} // wire
|
205
contrib/epee/include/serialization/wire/wrapper/variant.h
Normal file
205
contrib/epee/include/serialization/wire/wrapper/variant.h
Normal file
@ -0,0 +1,205 @@
|
||||
// Copyright (c) 2022, 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 <boost/variant/get.hpp>
|
||||
#include <boost/variant/variant_fwd.hpp>
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
|
||||
#include "serialization/wire/error.h"
|
||||
#include "serialization/wire/fwd.h"
|
||||
#include "serialization/wire/field.h"
|
||||
|
||||
#define WIRE_OPTION(name, type, cpp_name) \
|
||||
wire::optional_field(name, wire::option<type>(std::ref(cpp_name)))
|
||||
|
||||
namespace wire
|
||||
{
|
||||
[[noreturn]] void throw_variant_exception(error::schema type, const char* variant_name);
|
||||
|
||||
/*! Wrapper for any C++ variant type that tracks if a `read_bytes` call has
|
||||
completed on wrapped `value`. This wrapper is not needed if the variant is
|
||||
being used for writes only - see `wire::option_` below for more information.
|
||||
|
||||
`variant_type` is `T` with optional `std::reference_wrapper` removed. See
|
||||
`option_` for concept requirements of `variant_type`.
|
||||
|
||||
Example usage:
|
||||
```
|
||||
template<typename F, typename T>
|
||||
void type_map(F& format, T& self)
|
||||
{
|
||||
auto variant = wire::variant(std::ref(self.field3));
|
||||
wire::object(format,
|
||||
...
|
||||
WIRE_OPTION("type1", type1, variant),
|
||||
WIRE_OPTION("type2", type2, variant)
|
||||
);
|
||||
}
|
||||
``` */
|
||||
template<typename T>
|
||||
struct variant_
|
||||
{
|
||||
using variant_type = unwrap_reference_t<T>;
|
||||
|
||||
//! \throw wire::exception with `type` and mangled C++ name of `variant_type`.
|
||||
[[noreturn]] static void throw_exception(const error::schema type)
|
||||
{ throw_variant_exception(type, typeid(variant_type).name()); }
|
||||
|
||||
constexpr variant_(T&& value)
|
||||
: value(std::move(value)), read(false)
|
||||
{}
|
||||
|
||||
T value;
|
||||
bool read;
|
||||
|
||||
constexpr const variant_type& get_variant() const noexcept { return value; }
|
||||
variant_type& get_variant() noexcept { return value; }
|
||||
|
||||
//! Makes `variant_` compatible with `emplace()` in `option_`.
|
||||
template<typename U>
|
||||
variant_& operator=(U&& rhs)
|
||||
{
|
||||
get_variant() = std::forward<U>(rhs);
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
inline constexpr variant_<T> variant(T value)
|
||||
{ return {std::move(value)}; }
|
||||
|
||||
namespace adapt
|
||||
{
|
||||
template<typename T>
|
||||
inline void throw_if_not_read(const T&)
|
||||
{ throw_variant_exception(error::schema::missing_key, typeid(T).name()); }
|
||||
|
||||
template<typename T>
|
||||
inline void throw_if_not_read(const variant_<T>& value)
|
||||
{
|
||||
if (!value.read)
|
||||
value.throw_exception(error::schema::missing_key);
|
||||
}
|
||||
|
||||
|
||||
// other variant overloads can be added here as needed
|
||||
|
||||
template<typename U, typename... T>
|
||||
inline const U* get_if(const boost::variant<T...>& value)
|
||||
{ return boost::get<U>(std::addressof(value)); }
|
||||
|
||||
template<typename U, typename T>
|
||||
inline const U* get_if(const variant_<T>& value)
|
||||
{ return adapt::get_if<U>(value.get_variant()); }
|
||||
}
|
||||
|
||||
/*! Wrapper that makes a variant compatible with `wire::optional_field`.
|
||||
Currently `wire::variant_` and `boost::variant` are valid variant types
|
||||
for writing, and only `wire::variant_` is valid for reading.
|
||||
|
||||
`variant_type` is `T` with optional `std::reference_wrapper` removed.
|
||||
`variant_type` concept requirements:
|
||||
* must have two overloads for `get<U>` function in `adapt` namespace - one
|
||||
`const` and one non-`const` that returns `const U&` and `U&` respectively
|
||||
iff `variant_type` is storing type `U`. Otherwise, the function should
|
||||
throw an exception.
|
||||
* must have overload for `get_if<U>` function in `adapt` namespace that
|
||||
returns `const U*` when `variant_type` is storing type `U`. Otherwise, the
|
||||
function should return `nullptr`.
|
||||
* must have a member function `operator=(U&&)` that changes the stored type
|
||||
to `U` (`get<U>` and `get_if<U>` will return `U` after `operator=`
|
||||
completion).
|
||||
|
||||
The `wire::variant(std::ref(self.field3))` step in the example above can be
|
||||
omitted if only writing is needed. The `boost::variant` value should be
|
||||
given directly to `wire::option<U>(...)` or `WIRE_OPTION` macro - only one
|
||||
type is active so `wire::optional_field` will omit all other types/fields. */
|
||||
template<typename T, typename U>
|
||||
struct option_
|
||||
{
|
||||
using variant_type = unwrap_reference_t<T>;
|
||||
using option_type = U;
|
||||
|
||||
T value;
|
||||
|
||||
constexpr const variant_type& get_variant() const noexcept { return value; }
|
||||
variant_type& get_variant() noexcept { return value; }
|
||||
|
||||
//! \return `true` iff `U` is active type in variant.
|
||||
bool is_active() const { return adapt::get_if<U>(get_variant()) != nullptr; }
|
||||
|
||||
|
||||
// concept requirements for optional fields
|
||||
|
||||
explicit operator bool() const { return is_active(); }
|
||||
void emplace() { get_variant() = U{}; }
|
||||
|
||||
const option_& operator*() const { return *this; }
|
||||
option_& operator*() { return *this; }
|
||||
|
||||
//! \throw wire::exception iff no variant type was read.
|
||||
void reset() { adapt::throw_if_not_read(get_variant()); }
|
||||
};
|
||||
|
||||
template<typename U, typename T>
|
||||
inline constexpr option_<T, U> option(T value)
|
||||
{ return {std::move(value)}; }
|
||||
|
||||
namespace adapt
|
||||
{
|
||||
// other variant overloads can be added here as needed
|
||||
|
||||
template<typename U, typename... T>
|
||||
inline U& get(boost::variant<T...>& value)
|
||||
{ return boost::get<U>(value); }
|
||||
|
||||
template<typename U, typename... T>
|
||||
inline const U& get(const boost::variant<T...>& value)
|
||||
{ return boost::get<U>(value); }
|
||||
|
||||
template<typename U, typename T>
|
||||
inline const U& get(const variant_<T>& value)
|
||||
{ return adapt::get<U>(value.get_variant()); }
|
||||
}
|
||||
|
||||
//! \throw wire::exception if `dest.get_variant()` has already been used in `read_bytes`.
|
||||
template<typename R, typename T, typename U>
|
||||
inline void read_bytes(R& source, option_<std::reference_wrapper<variant_<T>>, U> dest)
|
||||
{
|
||||
if (dest.get_variant().read)
|
||||
dest.get_variant().throw_exception(error::schema::invalid_key);
|
||||
wire_read::bytes(source, adapt::get<U>(dest.get_variant().get_variant()));
|
||||
dest.get_variant().read = true;
|
||||
}
|
||||
|
||||
template<typename W, typename T, typename U>
|
||||
inline void write_bytes(W& dest, const option_<T, U>& source)
|
||||
{ wire_write::bytes(dest, adapt::get<U>(source.get_variant())); }
|
||||
}
|
48
contrib/epee/src/wire/asio.cpp
Normal file
48
contrib/epee/src/wire/asio.cpp
Normal file
@ -0,0 +1,48 @@
|
||||
// Copyright (c) 2021-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.
|
||||
|
||||
#include "serialization/wire/adapted/asio.h"
|
||||
|
||||
#include "serialization/wire.h"
|
||||
#include "span.h"
|
||||
|
||||
namespace wire
|
||||
{
|
||||
void read_bytes(reader& source, boost::asio::ip::address_v6& dest)
|
||||
{
|
||||
boost::asio::ip::address_v6::bytes_type value{};
|
||||
source.binary(epee::to_mut_span(value), true);
|
||||
dest = boost::asio::ip::address_v6(value);
|
||||
}
|
||||
|
||||
void write_bytes(writer& dest, const boost::asio::ip::address_v6& source)
|
||||
{
|
||||
const auto bytes = source.to_bytes();
|
||||
dest.binary(epee::to_span(bytes));
|
||||
}
|
||||
}
|
55
contrib/epee/src/wire/basic_value.cpp
Normal file
55
contrib/epee/src/wire/basic_value.cpp
Normal file
@ -0,0 +1,55 @@
|
||||
// Copyright (c) 2022, 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.
|
||||
|
||||
#include "serialization/wire/basic_value.h"
|
||||
|
||||
#include <boost/variant/apply_visitor.hpp>
|
||||
#include <stdexcept>
|
||||
#include "serialization/wire/read.h"
|
||||
#include "serialization/wire/write.h"
|
||||
|
||||
namespace wire
|
||||
{
|
||||
static void write_bytes(writer& dest, const std::nullptr_t&)
|
||||
{
|
||||
throw std::logic_error{"nullptr output not yet defined for wire::writer"};
|
||||
}
|
||||
|
||||
void basic_value::reset()
|
||||
{
|
||||
value = nullptr;
|
||||
}
|
||||
|
||||
void read_bytes(reader& source, basic_value& dest)
|
||||
{
|
||||
dest = source.basic();
|
||||
}
|
||||
void write_bytes(writer& dest, const basic_value& source)
|
||||
{
|
||||
boost::apply_visitor([&dest] (const auto& val) { wire_write::bytes(dest, val); }, source.value);
|
||||
}
|
||||
}
|
88
contrib/epee/src/wire/read.cpp
Normal file
88
contrib/epee/src/wire/read.cpp
Normal file
@ -0,0 +1,88 @@
|
||||
// Copyright (c) 2021, 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.
|
||||
|
||||
#include "serialization/wire/read.h"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
void wire::reader::increment_depth()
|
||||
{
|
||||
if (++depth_ == max_read_depth())
|
||||
WIRE_DLOG_THROW_(error::schema::maximum_depth);
|
||||
}
|
||||
|
||||
void wire::reader::decrement_depth()
|
||||
{
|
||||
if (!depth_)
|
||||
throw std::logic_error{"reader::decrement_depth() already at zero"};
|
||||
--depth_;
|
||||
}
|
||||
|
||||
std::size_t wire::reader::start_array(std::size_t min_element_size)
|
||||
{
|
||||
increment_depth();
|
||||
return do_start_array(min_element_size);
|
||||
}
|
||||
|
||||
std::size_t wire::reader::start_object()
|
||||
{
|
||||
increment_depth();
|
||||
return do_start_object();
|
||||
}
|
||||
|
||||
[[noreturn]] void wire::integer::throw_exception(const std::intmax_t source, const std::intmax_t min, const std::intmax_t max)
|
||||
{
|
||||
static_assert(0 <= std::numeric_limits<std::intmax_t>::max(), "expected 0 <= intmax_t::max");
|
||||
static_assert(
|
||||
std::numeric_limits<std::intmax_t>::max() <= std::numeric_limits<std::uintmax_t>::max(),
|
||||
"expected intmax_t::max <= uintmax_t::max"
|
||||
);
|
||||
if (source < 0)
|
||||
WIRE_DLOG_THROW(error::schema::larger_integer, source << " given when " << min << " is minimum permitted");
|
||||
else
|
||||
throw_exception(std::uintmax_t(source), std::uintmax_t(max));
|
||||
}
|
||||
[[noreturn]] void wire::integer::throw_exception(const std::uintmax_t source, const std::uintmax_t max)
|
||||
{
|
||||
WIRE_DLOG_THROW(error::schema::smaller_integer, source << " given when " << max << " is maximum permitted");
|
||||
}
|
||||
|
||||
[[noreturn]] void wire_read::throw_exception(const wire::error::schema code, const char* display, epee::span<char const* const> names)
|
||||
{
|
||||
const char* name = nullptr;
|
||||
for (const char* elem : names)
|
||||
{
|
||||
if (elem != nullptr)
|
||||
{
|
||||
name = elem;
|
||||
break;
|
||||
}
|
||||
}
|
||||
WIRE_DLOG_THROW(code, display << (name ? name : ""));
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user