forked from such-gitea/wownero-lws
Initial working base separated from top-level monero project
This commit is contained in:
36
src/wire/CMakeLists.txt
Normal file
36
src/wire/CMakeLists.txt
Normal file
@@ -0,0 +1,36 @@
|
||||
# Copyright (c) 2020, 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.
|
||||
|
||||
set(monero-lws-wire_sources error.cpp read.cpp write.cpp)
|
||||
set(monero-lws-wire_headers crypto.h error.h field.h filters.h fwd.h json.h read.h traits.h vector.h write.h)
|
||||
|
||||
add_library(monero-lws-wire ${monero-lws-wire_sources} ${monero-lws-wire_headers})
|
||||
target_include_directories(monero-lws-wire PUBLIC "${LMDB_INCLUDE}")
|
||||
target_link_libraries(monero-lws-wire PRIVATE monero::libraries)
|
||||
|
||||
add_subdirectory(json)
|
||||
72
src/wire/crypto.h
Normal file
72
src/wire/crypto.h
Normal file
@@ -0,0 +1,72 @@
|
||||
// Copyright (c) 2020, 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 "crypto/crypto.h" // monero/src
|
||||
#include "ringct/rctTypes.h" // monero/src
|
||||
#include "wire/traits.h"
|
||||
|
||||
namespace wire
|
||||
{
|
||||
template<>
|
||||
struct is_blob<crypto::ec_scalar>
|
||||
: std::true_type
|
||||
{};
|
||||
|
||||
template<>
|
||||
struct is_blob<crypto::hash>
|
||||
: std::true_type
|
||||
{};
|
||||
|
||||
template<>
|
||||
struct is_blob<crypto::key_derivation>
|
||||
: std::true_type
|
||||
{};
|
||||
|
||||
template<>
|
||||
struct is_blob<crypto::key_image>
|
||||
: std::true_type
|
||||
{};
|
||||
|
||||
template<>
|
||||
struct is_blob<crypto::public_key>
|
||||
: std::true_type
|
||||
{};
|
||||
|
||||
template<>
|
||||
struct is_blob<crypto::signature>
|
||||
: std::true_type
|
||||
{};
|
||||
|
||||
template<>
|
||||
struct is_blob<rct::key>
|
||||
: std::true_type
|
||||
{};
|
||||
}
|
||||
93
src/wire/error.cpp
Normal file
93
src/wire/error.cpp
Normal file
@@ -0,0 +1,93 @@
|
||||
// Copyright (c) 2020, 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 "wire/error.h"
|
||||
|
||||
namespace wire
|
||||
{
|
||||
namespace error
|
||||
{
|
||||
const char* get_string(const schema value) noexcept
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
default:
|
||||
break;
|
||||
|
||||
case schema::none:
|
||||
return "No schema errors";
|
||||
case schema::array:
|
||||
return "Schema expected array";
|
||||
case schema::binary:
|
||||
return "Schema expected binary value of variable size";
|
||||
case schema::boolean:
|
||||
return "Schema expected boolean value";
|
||||
case schema::enumeration:
|
||||
return "Schema expected a specific of enumeration value(s)";
|
||||
case schema::fixed_binary:
|
||||
return "Schema expected binary of fixed size";
|
||||
case schema::integer:
|
||||
return "Schema expected integer value";
|
||||
case schema::invalid_key:
|
||||
return "Schema does not allow object field key";
|
||||
case schema::larger_integer:
|
||||
return "Schema expected a larger integer value";
|
||||
case schema::maximum_depth:
|
||||
return "Schema hit maximum array+object depth tracking";
|
||||
case schema::missing_key:
|
||||
return "Schema missing required field key";
|
||||
case schema::number:
|
||||
return "Schema expected number (integer or float) value";
|
||||
case schema::object:
|
||||
return "Schema expected object";
|
||||
case schema::smaller_integer:
|
||||
return "Schema expected a smaller integer value";
|
||||
case schema::string:
|
||||
return "Schema expected string";
|
||||
}
|
||||
return "Unknown schema error";
|
||||
}
|
||||
|
||||
const std::error_category& schema_category() noexcept
|
||||
{
|
||||
struct category final : std::error_category
|
||||
{
|
||||
virtual const char* name() const noexcept override final
|
||||
{
|
||||
return "wire::error::schema_category()";
|
||||
}
|
||||
|
||||
virtual std::string message(int value) const override final
|
||||
{
|
||||
return get_string(schema(value));
|
||||
}
|
||||
};
|
||||
static const category instance{};
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
135
src/wire/error.h
Normal file
135
src/wire/error.h
Normal file
@@ -0,0 +1,135 @@
|
||||
// Copyright (c) 2020, 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 <exception>
|
||||
#include <system_error>
|
||||
#include <type_traits>
|
||||
|
||||
#include "misc_log_ex.h" // monero/contrib/epee/include
|
||||
|
||||
//! Print default `code` message followed by optional message to debug log then throw `code`.
|
||||
#define WIRE_DLOG_THROW_(code, ...) \
|
||||
do \
|
||||
{ \
|
||||
MDEBUG( get_string(code) __VA_ARGS__ ); \
|
||||
throw ::wire::exception_t<decltype(code)>{code}; \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
//! Print default `code` message followed by `msg` to debug log then throw `code`.
|
||||
#define WIRE_DLOG_THROW(code, msg) \
|
||||
WIRE_DLOG_THROW_(code, << ": " << msg)
|
||||
|
||||
namespace wire
|
||||
{
|
||||
namespace error
|
||||
{
|
||||
enum class schema : int
|
||||
{
|
||||
none = 0, //!< Must be zero for `expect<..>`
|
||||
array, //!< Expected an array value
|
||||
binary, //!< Expected a binary value of variable length
|
||||
boolean, //!< Expected a boolean value
|
||||
enumeration, //!< Expected a value from a specific set
|
||||
fixed_binary, //!< Expected a binary value of fixed length
|
||||
integer, //!< Expected an integer value
|
||||
invalid_key, //!< Key for object is invalid
|
||||
larger_integer, //!< Expected a larger integer value
|
||||
maximum_depth, //!< Hit maximum number of object+array tracking
|
||||
missing_key, //!< Missing required key for object
|
||||
number, //!< Expected a number (integer or float) value
|
||||
object, //!< Expected object value
|
||||
smaller_integer, //!< Expected a smaller integer value
|
||||
string, //!< Expected string value
|
||||
};
|
||||
|
||||
//! \return Error message string.
|
||||
const char* get_string(schema value) noexcept;
|
||||
|
||||
//! \return Category for `schema_error`.
|
||||
const std::error_category& schema_category() noexcept;
|
||||
|
||||
//! \return Error code with `value` and `schema_category()`.
|
||||
inline std::error_code make_error_code(const schema value) noexcept
|
||||
{
|
||||
return std::error_code{int(value), schema_category()};
|
||||
}
|
||||
} // error
|
||||
|
||||
//! `std::exception` doesn't require dynamic memory like `std::runtime_error`
|
||||
struct exception : std::exception
|
||||
{
|
||||
exception() noexcept
|
||||
: std::exception()
|
||||
{}
|
||||
|
||||
exception(const exception&) = default;
|
||||
exception& operator=(const exception&) = default;
|
||||
virtual ~exception() noexcept
|
||||
{}
|
||||
|
||||
virtual std::error_code code() const noexcept = 0;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class exception_t final : public wire::exception
|
||||
{
|
||||
static_assert(std::is_enum<T>(), "only enumerated types allowed");
|
||||
T value;
|
||||
|
||||
public:
|
||||
exception_t(T value) noexcept
|
||||
: value(value)
|
||||
{}
|
||||
|
||||
exception_t(const exception_t&) = default;
|
||||
~exception_t() = default;
|
||||
exception_t& operator=(const exception_t&) = default;
|
||||
|
||||
const char* what() const noexcept override final
|
||||
{
|
||||
static_assert(noexcept(noexcept(get_string(value))), "get_string function must be noexcept");
|
||||
return get_string(value);
|
||||
}
|
||||
|
||||
std::error_code code() const noexcept override final
|
||||
{
|
||||
static_assert(noexcept(noexcept(make_error_code(value))), "make_error_code funcion must be noexcept");
|
||||
return make_error_code(value);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
namespace std
|
||||
{
|
||||
template<>
|
||||
struct is_error_code_enum<wire::error::schema>
|
||||
: true_type
|
||||
{};
|
||||
}
|
||||
280
src/wire/field.h
Normal file
280
src/wire/field.h
Normal file
@@ -0,0 +1,280 @@
|
||||
// Copyright (c) 2020, 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 <functional>
|
||||
#include <utility>
|
||||
|
||||
#include "wire/filters.h"
|
||||
#include "wire/traits.h"
|
||||
|
||||
//! A required field has the same key name and C/C++ name
|
||||
#define WIRE_FIELD(name) \
|
||||
::wire::field( #name , std::ref( self . 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) \
|
||||
::wire::field( #name , self . name )
|
||||
|
||||
//! The optional field has the same key name and C/C++ name
|
||||
#define WIRE_OPTIONAL_FIELD(name) \
|
||||
::wire::optional_field( #name , std::ref( self . name ))
|
||||
|
||||
namespace wire
|
||||
{
|
||||
template<typename T>
|
||||
struct unwrap_reference
|
||||
{
|
||||
using type = T;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct unwrap_reference<std::reference_wrapper<T>>
|
||||
{
|
||||
using type = T;
|
||||
};
|
||||
|
||||
|
||||
//! Links `name` to a `value` for object serialization.
|
||||
template<typename T, bool Required>
|
||||
struct field_
|
||||
{
|
||||
using value_type = typename unwrap_reference<T>::type;
|
||||
static constexpr bool is_required() noexcept { return Required; }
|
||||
static constexpr std::size_t count() noexcept { return 1; }
|
||||
|
||||
const char* name;
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
//! Links `name` to `value`. Use `std::ref` if de-serializing.
|
||||
template<typename T>
|
||||
constexpr inline field_<T, true> field(const char* name, T value)
|
||||
{
|
||||
return {name, std::move(value)};
|
||||
}
|
||||
|
||||
//! Links `name` to `value`. Use `std::ref` if de-serializing.
|
||||
template<typename T>
|
||||
constexpr inline field_<T, false> optional_field(const char* name, T value)
|
||||
{
|
||||
return {name, std::move(value)};
|
||||
}
|
||||
|
||||
|
||||
//! Links `name` to a type `T` for variant serialization.
|
||||
template<typename T>
|
||||
struct option
|
||||
{
|
||||
const char* name;
|
||||
};
|
||||
|
||||
//! \return Name associated with type `T` for variant `field`.
|
||||
template<typename T, typename U>
|
||||
constexpr const char* get_option_name(const U& field) noexcept
|
||||
{
|
||||
return static_cast< const option<T>& >(field).name;
|
||||
}
|
||||
|
||||
//! Links each type in a variant to a string key.
|
||||
template<typename T, bool Required, typename... U>
|
||||
struct variant_field_ : option<U>...
|
||||
{
|
||||
using value_type = typename unwrap_reference<T>::type;
|
||||
static constexpr bool is_required() noexcept { return Required; }
|
||||
static constexpr std::size_t count() noexcept { return sizeof...(U); }
|
||||
|
||||
constexpr variant_field_(const char* name, T value, option<U>... opts)
|
||||
: option<U>(std::move(opts))..., name(name), value(std::move(value))
|
||||
{}
|
||||
|
||||
const char* name;
|
||||
T value;
|
||||
|
||||
constexpr const value_type& get_value() const noexcept
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
value_type& get_value() noexcept
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
template<typename V>
|
||||
struct wrap
|
||||
{
|
||||
using result_type = void;
|
||||
|
||||
variant_field_ self;
|
||||
V visitor;
|
||||
|
||||
template<typename X>
|
||||
void operator()(const X& value) const
|
||||
{
|
||||
visitor(get_option_name<X>(self), value);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename V>
|
||||
void visit(V visitor) const
|
||||
{
|
||||
apply_visitor(wrap<V>{*this, std::move(visitor)}, get_value());
|
||||
}
|
||||
};
|
||||
|
||||
//! Links variant `value` to a unique name per type in `opts`. Use `std::ref` for `value` if de-serializing.
|
||||
template<typename T, typename... U>
|
||||
constexpr inline variant_field_<T, true, U...> variant_field(const char* name, T value, option<U>... opts)
|
||||
{
|
||||
return {name, std::move(value), std::move(opts)...};
|
||||
}
|
||||
|
||||
|
||||
//! Indicates a field value should be written as an array
|
||||
template<typename T, typename F>
|
||||
struct as_array_
|
||||
{
|
||||
using value_type = typename unwrap_reference<T>::type;
|
||||
|
||||
T value;
|
||||
F filter; //!< Each element in `value` given to this callable before `write_bytes`.
|
||||
|
||||
//! \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;
|
||||
}
|
||||
};
|
||||
|
||||
//! Callable that can filter `as_object` values or be used immediately.
|
||||
template<typename Default>
|
||||
struct as_array_filter
|
||||
{
|
||||
Default default_filter;
|
||||
|
||||
template<typename T>
|
||||
constexpr as_array_<T, Default> operator()(T value) const
|
||||
{
|
||||
return {std::move(value), default_filter};
|
||||
}
|
||||
|
||||
template<typename T, typename F>
|
||||
constexpr as_array_<T, F> operator()(T value, F filter) const
|
||||
{
|
||||
return {std::move(value), std::move(filter)};
|
||||
}
|
||||
};
|
||||
//! Usage: `wire::field("foo", wire::as_array(self.foo, to_string{})`. Consider `std::ref`.
|
||||
constexpr as_array_filter<identity_> as_array{};
|
||||
|
||||
|
||||
//! Indicates a field value should be written as an object
|
||||
template<typename T, typename F, typename G>
|
||||
struct as_object_
|
||||
{
|
||||
using map_type = typename unwrap_reference<T>::type;
|
||||
|
||||
T map;
|
||||
F key_filter; //!< Each key (`.first`) in `map` given to this callable before writing field key.
|
||||
G value_filter; //!< Each value (`.second`) in `map` given to this callable before `write_bytes`.
|
||||
|
||||
//! \return `map` with `std::reference_wrapper` removed.
|
||||
constexpr const map_type& get_map() const noexcept
|
||||
{
|
||||
return map;
|
||||
}
|
||||
|
||||
//! \return `map` with `std::reference_wrapper` removed.
|
||||
map_type& get_map() noexcept
|
||||
{
|
||||
return map;
|
||||
}
|
||||
};
|
||||
|
||||
//! Usage: `wire::field("foo", wire::as_object(self.foo, to_string{}, wire::as_array))`. Consider `std::ref`.
|
||||
template<typename T, typename F = identity_, typename G = identity_>
|
||||
inline constexpr as_object_<T, F, G> as_object(T map, F key_filter = F{}, G value_filter = G{})
|
||||
{
|
||||
return {std::move(map), std::move(key_filter), std::move(value_filter)};
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
inline constexpr bool available(const field_<T, true>&) noexcept
|
||||
{
|
||||
return true;
|
||||
}
|
||||
template<typename T>
|
||||
inline bool available(const field_<T, false>& elem)
|
||||
{
|
||||
return bool(elem.get_value());
|
||||
}
|
||||
template<typename T, typename... U>
|
||||
inline constexpr bool available(const variant_field_<T, true, U...>&) noexcept
|
||||
{
|
||||
return true;
|
||||
}
|
||||
template<typename T, typename... U>
|
||||
inline constexpr bool available(const variant_field_<T, false, U...>& elem)
|
||||
{
|
||||
return elem != nullptr;
|
||||
}
|
||||
|
||||
|
||||
// example usage : `wire::sum(std::size_t(wire::available(fields))...)`
|
||||
|
||||
inline constexpr int sum() noexcept
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
template<typename T, typename... U>
|
||||
inline constexpr T sum(const T head, const U... tail) noexcept
|
||||
{
|
||||
return head + sum(tail...);
|
||||
}
|
||||
}
|
||||
|
||||
73
src/wire/filters.h
Normal file
73
src/wire/filters.h
Normal file
@@ -0,0 +1,73 @@
|
||||
// Copyright (c) 2020, 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 <cassert>
|
||||
#include <type_traits>
|
||||
|
||||
#include "lmdb/util.h"
|
||||
|
||||
// These functions are to be used with `wire::as_object(...)` key filtering
|
||||
|
||||
namespace wire
|
||||
{
|
||||
//! Callable that returns the value unchanged; default filter for `as_array` and `as_object`.
|
||||
struct identity_
|
||||
{
|
||||
template<typename T>
|
||||
const T& operator()(const T& value) const noexcept
|
||||
{
|
||||
return value;
|
||||
}
|
||||
};
|
||||
constexpr const identity_ identity{};
|
||||
|
||||
//! Callable that forwards enum to get_string.
|
||||
struct enum_as_string_
|
||||
{
|
||||
template<typename T>
|
||||
auto operator()(const T value) const noexcept(noexcept(get_string(value))) -> decltype(get_string(value))
|
||||
{
|
||||
return get_string(value);
|
||||
}
|
||||
};
|
||||
constexpr const enum_as_string_ enum_as_string{};
|
||||
|
||||
//! Callable that converts C++11 enum class or integer to integer value.
|
||||
struct as_integer_
|
||||
{
|
||||
template<typename T>
|
||||
lmdb::native_type<T> operator()(const T value) const noexcept
|
||||
{
|
||||
using native = lmdb::native_type<T>;
|
||||
static_assert(!std::is_signed<native>::value, "integer cannot be signed");
|
||||
return native(value);
|
||||
}
|
||||
};
|
||||
constexpr const as_integer_ as_integer{};
|
||||
}
|
||||
68
src/wire/fwd.h
Normal file
68
src/wire/fwd.h
Normal file
@@ -0,0 +1,68 @@
|
||||
// Copyright (c) 2020, 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/utility/string_ref.hpp>
|
||||
#include <type_traits>
|
||||
|
||||
#include "common/expect.h" // monero/src
|
||||
|
||||
//! Declare an enum to be serialized as an integer
|
||||
#define WIRE_AS_INTEGER(type_) \
|
||||
static_assert(std::is_enum<type_>(), "AS_INTEGER only enum types"); \
|
||||
template<typename R> \
|
||||
inline void read_bytes(R& source, type_& dest) \
|
||||
{ \
|
||||
std::underlying_type<type_>::type temp{}; \
|
||||
read_bytes(source, temp); \
|
||||
dest = type_(temp); \
|
||||
} \
|
||||
template<typename W> \
|
||||
inline void write_bytes(W& dest, const type_ source) \
|
||||
{ \
|
||||
write_bytes(dest, std::underlying_type<type_>::type(source)); \
|
||||
}
|
||||
|
||||
//! Declare an enum to be serialized as a string (json) or integer (msgpack)
|
||||
#define WIRE_DECLARE_ENUM(type) \
|
||||
const char* get_string(type) noexcept; \
|
||||
expect<type> type ## _from_string(const boost::string_ref) noexcept; \
|
||||
void read_bytes(::wire::reader&, type&); \
|
||||
void write_bytes(::wire::writer&, type)
|
||||
|
||||
//! Declare a class/struct serialization for all available formats
|
||||
#define WIRE_DECLARE_OBJECT(type) \
|
||||
void read_bytes(::wire::reader&, type&); \
|
||||
void write_bytes(::wire::writer&, const type&)
|
||||
|
||||
namespace wire
|
||||
{
|
||||
class reader;
|
||||
struct writer;
|
||||
}
|
||||
|
||||
54
src/wire/json.h
Normal file
54
src/wire/json.h
Normal file
@@ -0,0 +1,54 @@
|
||||
// Copyright (c) 2020, 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 "wire/json/base.h"
|
||||
#include "wire/json/error.h"
|
||||
#include "wire/json/read.h"
|
||||
#include "wire/json/write.h"
|
||||
|
||||
#define WIRE_JSON_DEFINE_ENUM(type, map) \
|
||||
void read_bytes(::wire::json_reader& source, type& dest) \
|
||||
{ \
|
||||
dest = type(source.enumeration(map)); \
|
||||
} \
|
||||
void write_bytes(::wire::json_writer& dest, const type source) \
|
||||
{ \
|
||||
dest.enumeration(std::size_t(source), map); \
|
||||
}
|
||||
|
||||
#define WIRE_JSON_DEFINE_OBJECT(type, map) \
|
||||
void read_bytes(::wire::json_reader& source, type& dest) \
|
||||
{ \
|
||||
map(source, dest); \
|
||||
} \
|
||||
void write_bytes(::wire::json_writer& dest, const type& source) \
|
||||
{ \
|
||||
map(dest, source); \
|
||||
}
|
||||
|
||||
33
src/wire/json/CMakeLists.txt
Normal file
33
src/wire/json/CMakeLists.txt
Normal file
@@ -0,0 +1,33 @@
|
||||
# Copyright (c) 2020, 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.
|
||||
|
||||
set(monero-lws_wire-json_sources error.cpp read.cpp write.cpp)
|
||||
set(monero-lws_wire-json_headers base.h error.h fwd.h read.h write.h)
|
||||
|
||||
add_library(monero-lws-wire-json ${monero-lws_wire-json_sources} ${monero-lws-wire-json_headers})
|
||||
target_link_libraries(monero-lws-wire-json monero::libraries monero-lws-wire)
|
||||
50
src/wire/json/base.h
Normal file
50
src/wire/json/base.h
Normal file
@@ -0,0 +1,50 @@
|
||||
// Copyright (c) 2020, 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 <string>
|
||||
|
||||
#include "byte_slice.h"
|
||||
#include "common/expect.h"
|
||||
#include "wire/json/fwd.h"
|
||||
|
||||
namespace wire
|
||||
{
|
||||
struct json
|
||||
{
|
||||
using input_type = json_reader;
|
||||
using output_type = json_writer;
|
||||
|
||||
template<typename T>
|
||||
static expect<T> from_bytes(std::string&& source);
|
||||
|
||||
template<typename T>
|
||||
static epee::byte_slice to_bytes(const T& source);
|
||||
};
|
||||
}
|
||||
|
||||
108
src/wire/json/error.cpp
Normal file
108
src/wire/json/error.cpp
Normal file
@@ -0,0 +1,108 @@
|
||||
// Copyright (c) 2020, 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 "error.h"
|
||||
|
||||
namespace wire
|
||||
{
|
||||
namespace error
|
||||
{
|
||||
const char* get_string(const rapidjson_e value) noexcept
|
||||
{
|
||||
switch (rapidjson::ParseErrorCode(value))
|
||||
{
|
||||
default:
|
||||
break;
|
||||
|
||||
case rapidjson::kParseErrorNone:
|
||||
return "No JSON parsing errors";
|
||||
|
||||
// from rapidjson
|
||||
case rapidjson::kParseErrorDocumentEmpty:
|
||||
return "JSON parser expected non-empty document";
|
||||
case rapidjson::kParseErrorDocumentRootNotSingular:
|
||||
return "JSON parser expected one value at root level";
|
||||
|
||||
case rapidjson::kParseErrorValueInvalid:
|
||||
return "JSON parser found invalid value";
|
||||
|
||||
case rapidjson::kParseErrorObjectMissName:
|
||||
return "JSON parser expected name for object field";
|
||||
case rapidjson::kParseErrorObjectMissColon:
|
||||
return "JSON parser expected ':' between name and value";
|
||||
case rapidjson::kParseErrorObjectMissCommaOrCurlyBracket:
|
||||
return "JSON parser expected ',' or '}'";
|
||||
|
||||
case rapidjson::kParseErrorArrayMissCommaOrSquareBracket:
|
||||
return "JSON parser expected ',' or ']'";
|
||||
|
||||
case rapidjson::kParseErrorStringUnicodeEscapeInvalidHex:
|
||||
return "JSON parser found invalid unicode escape";
|
||||
case rapidjson::kParseErrorStringUnicodeSurrogateInvalid:
|
||||
return "JSON parser found invalid unicode surrogate value";
|
||||
case rapidjson::kParseErrorStringEscapeInvalid:
|
||||
return "JSON parser found invalid escape sequence in string value";
|
||||
case rapidjson::kParseErrorStringMissQuotationMark:
|
||||
return "JSON parser expected '\"'";
|
||||
case rapidjson::kParseErrorStringInvalidEncoding:
|
||||
return "JSON parser found invalid encoding";
|
||||
|
||||
case rapidjson::kParseErrorNumberTooBig:
|
||||
return "JSON parser found number value larger than double float precision";
|
||||
case rapidjson::kParseErrorNumberMissFraction:
|
||||
return "JSON parser found number missing fractional component";
|
||||
case rapidjson::kParseErrorNumberMissExponent:
|
||||
return "JSON parser found number missing exponent";
|
||||
|
||||
case rapidjson::kParseErrorTermination:
|
||||
return "JSON parser was stopped";
|
||||
case rapidjson::kParseErrorUnspecificSyntaxError:
|
||||
return "JSON parser found syntax error";
|
||||
}
|
||||
|
||||
return "Unknown JSON parser error";
|
||||
}
|
||||
|
||||
const std::error_category& rapidjson_category() noexcept
|
||||
{
|
||||
struct category final : std::error_category
|
||||
{
|
||||
virtual const char* name() const noexcept override final
|
||||
{
|
||||
return "wire::error::rapidjson_category()";
|
||||
}
|
||||
|
||||
virtual std::string message(int value) const override final
|
||||
{
|
||||
return get_string(rapidjson_e(value));
|
||||
}
|
||||
};
|
||||
static const category instance{};
|
||||
return instance;
|
||||
}
|
||||
} // error
|
||||
} // wire
|
||||
53
src/wire/json/error.h
Normal file
53
src/wire/json/error.h
Normal file
@@ -0,0 +1,53 @@
|
||||
// Copyright (c) 2020, 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 <rapidjson/error/error.h>
|
||||
#include <system_error>
|
||||
|
||||
namespace wire
|
||||
{
|
||||
namespace error
|
||||
{
|
||||
//! Type wrapper to "grab" rapidjson errors
|
||||
enum class rapidjson_e : int {};
|
||||
|
||||
//! \return Static string describing error `value`.
|
||||
const char* get_string(rapidjson_e value) noexcept;
|
||||
|
||||
//! \return Category for rapidjson generated errors.
|
||||
const std::error_category& rapidjson_category() noexcept;
|
||||
|
||||
//! \return Error code with `value` and `rapidjson_category()`.
|
||||
inline std::error_code make_error_code(rapidjson_e value) noexcept
|
||||
{
|
||||
return std::error_code{int(value), rapidjson_category()};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
45
src/wire/json/fwd.h
Normal file
45
src/wire/json/fwd.h
Normal file
@@ -0,0 +1,45 @@
|
||||
// Copyright (c) 2020, 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
|
||||
|
||||
#define WIRE_JSON_DECLARE_ENUM(type) \
|
||||
const char* get_string(type) noexcept; \
|
||||
void read_bytes(::wire::json_reader&, type&); \
|
||||
void write_bytes(:wire::json_writer&, type)
|
||||
|
||||
#define WIRE_JSON_DECLARE_OBJECT(type) \
|
||||
void read_bytes(::wire::json_reader&, type&); \
|
||||
void write_bytes(::wire::json_writer&, const type&)
|
||||
|
||||
namespace wire
|
||||
{
|
||||
struct json;
|
||||
class json_reader;
|
||||
class json_writer;
|
||||
}
|
||||
|
||||
413
src/wire/json/read.cpp
Normal file
413
src/wire/json/read.cpp
Normal file
@@ -0,0 +1,413 @@
|
||||
// Copyright (c) 2020, 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 "read.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
#include <rapidjson/memorystream.h>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "common/expect.h" // monero/src
|
||||
#include "hex.h" // monero/contrib/epee/include
|
||||
#include "wire/error.h"
|
||||
#include "wire/json/error.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
//! Maximum number of bytes to display "near" JSON error.
|
||||
constexpr const std::size_t snippet_size = 30;
|
||||
|
||||
struct json_default_reject : rapidjson::BaseReaderHandler<rapidjson::UTF8<>, json_default_reject>
|
||||
{
|
||||
bool Default() const noexcept { return false; }
|
||||
};
|
||||
|
||||
//! \throw std::system_error by converting `code` into a std::error_code
|
||||
[[noreturn]] void throw_json_error(const epee::span<char> source, const rapidjson::Reader& reader, const wire::error::schema expected)
|
||||
{
|
||||
const std::size_t offset = std::min(source.size(), reader.GetErrorOffset());
|
||||
const std::size_t start = offset;//std::max(snippet_size / 2, offset) - (snippet_size / 2);
|
||||
const std::size_t end = start + std::min(snippet_size, source.size() - start);
|
||||
|
||||
const boost::string_ref text{source.data() + start, end - start};
|
||||
const rapidjson::ParseErrorCode parse_error = reader.GetParseErrorCode();
|
||||
switch (parse_error)
|
||||
{
|
||||
default:
|
||||
WIRE_DLOG_THROW(wire::error::rapidjson_e(parse_error), "near \"" << text << '"');
|
||||
case rapidjson::kParseErrorNone:
|
||||
case rapidjson::kParseErrorTermination: // the handler returned false
|
||||
break;
|
||||
}
|
||||
WIRE_DLOG_THROW(expected, "near '" << text << '\'');
|
||||
}
|
||||
}
|
||||
|
||||
namespace wire
|
||||
{
|
||||
struct json_reader::rapidjson_sax
|
||||
{
|
||||
struct string_contents
|
||||
{
|
||||
const char* ptr;
|
||||
std::size_t length;
|
||||
};
|
||||
|
||||
union
|
||||
{
|
||||
bool boolean;
|
||||
std::intmax_t integer;
|
||||
std::uintmax_t unsigned_integer;
|
||||
double number;
|
||||
string_contents string;
|
||||
} value;
|
||||
|
||||
error::schema expected_;
|
||||
bool negative;
|
||||
|
||||
explicit rapidjson_sax(error::schema expected) noexcept
|
||||
: expected_(expected), negative(false)
|
||||
{}
|
||||
|
||||
bool Null() const noexcept
|
||||
{
|
||||
return expected_ == error::schema::none;
|
||||
}
|
||||
|
||||
bool Bool(bool i) noexcept
|
||||
{
|
||||
value.boolean = i;
|
||||
return expected_ == error::schema::boolean || expected_ == error::schema::none;
|
||||
}
|
||||
|
||||
bool Int(int i) noexcept
|
||||
{
|
||||
return Int64(i);
|
||||
}
|
||||
bool Uint(unsigned i) noexcept
|
||||
{
|
||||
return Uint64(i);
|
||||
}
|
||||
bool Int64(std::int64_t i) noexcept
|
||||
{
|
||||
negative = true;
|
||||
switch(expected_)
|
||||
{
|
||||
default:
|
||||
return false;
|
||||
case error::schema::integer:
|
||||
value.integer = i;
|
||||
break;
|
||||
case error::schema::number:
|
||||
value.number = i;
|
||||
break;
|
||||
case error::schema::none:
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool Uint64(std::uint64_t i) noexcept
|
||||
{
|
||||
switch (expected_)
|
||||
{
|
||||
default:
|
||||
return false;
|
||||
case error::schema::integer:
|
||||
value.unsigned_integer = i;
|
||||
break;
|
||||
case error::schema::number:
|
||||
value.number = i;
|
||||
break;
|
||||
case error::schema::none:
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Double(double i) noexcept
|
||||
{
|
||||
value.number = i;
|
||||
return expected_ == error::schema::number || expected_ == error::schema::none;
|
||||
}
|
||||
|
||||
bool RawNumber(const char*, std::size_t, bool) const noexcept
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool String(const char* str, std::size_t length, bool) noexcept
|
||||
{
|
||||
value.string = {str, length};
|
||||
return expected_ == error::schema::string || expected_ == error::schema::none;
|
||||
}
|
||||
bool Key(const char* str, std::size_t length, bool)
|
||||
{
|
||||
return String(str, length, true);
|
||||
}
|
||||
|
||||
bool StartArray() const noexcept { return expected_ == error::schema::none; }
|
||||
bool EndArray(std::size_t) const noexcept { return expected_ == error::schema::none; }
|
||||
bool StartObject() const noexcept { return expected_ == error::schema::none; }
|
||||
bool EndObject(std::size_t) const noexcept { return expected_ == error::schema::none; }
|
||||
};
|
||||
|
||||
void json_reader::read_next_value(rapidjson_sax& handler)
|
||||
{
|
||||
rapidjson::InsituStringStream stream{current_.data()};
|
||||
if (!reader_.Parse<rapidjson::kParseStopWhenDoneFlag>(stream, handler))
|
||||
throw_json_error(current_, reader_, handler.expected_);
|
||||
current_.remove_prefix(stream.Tell());
|
||||
}
|
||||
|
||||
char json_reader::get_next_token()
|
||||
{
|
||||
rapidjson::InsituStringStream stream{current_.data()};
|
||||
rapidjson::SkipWhitespace(stream);
|
||||
current_.remove_prefix(stream.Tell());
|
||||
return stream.Peek();
|
||||
}
|
||||
|
||||
boost::string_ref json_reader::get_next_string()
|
||||
{
|
||||
if (get_next_token() != '"')
|
||||
WIRE_DLOG_THROW_(error::schema::string);
|
||||
current_.remove_prefix(1);
|
||||
|
||||
void const* const end = std::memchr(current_.data(), '"', current_.size());
|
||||
if (!end)
|
||||
WIRE_DLOG_THROW_(error::rapidjson_e(rapidjson::kParseErrorStringMissQuotationMark));
|
||||
|
||||
char const* const begin = current_.data();
|
||||
const std::size_t length = current_.remove_prefix(static_cast<const char*>(end) - current_.data() + 1);
|
||||
return {begin, length - 1};
|
||||
}
|
||||
|
||||
void json_reader::skip_value()
|
||||
{
|
||||
rapidjson_sax accept_all{error::schema::none};
|
||||
read_next_value(accept_all);
|
||||
}
|
||||
|
||||
json_reader::json_reader(std::string&& source)
|
||||
: reader(),
|
||||
source_(std::move(source)),
|
||||
current_(std::addressof(source_[0]), source_.size()),
|
||||
reader_()
|
||||
{}
|
||||
|
||||
void json_reader::check_complete() const
|
||||
{
|
||||
if (depth())
|
||||
WIRE_DLOG_THROW(error::rapidjson_e(rapidjson::kParseErrorUnspecificSyntaxError), "Unexpected end");
|
||||
}
|
||||
|
||||
bool json_reader::boolean()
|
||||
{
|
||||
rapidjson_sax json_bool{error::schema::boolean};
|
||||
read_next_value(json_bool);
|
||||
return json_bool.value.boolean;
|
||||
}
|
||||
|
||||
std::intmax_t json_reader::integer()
|
||||
{
|
||||
rapidjson_sax json_int{error::schema::integer};
|
||||
read_next_value(json_int);
|
||||
if (json_int.negative)
|
||||
return json_int.value.integer;
|
||||
return integer::convert_to<std::intmax_t>(json_int.value.unsigned_integer);
|
||||
}
|
||||
|
||||
std::uintmax_t json_reader::unsigned_integer()
|
||||
{
|
||||
rapidjson_sax json_uint{error::schema::integer};
|
||||
read_next_value(json_uint);
|
||||
if (!json_uint.negative)
|
||||
return json_uint.value.unsigned_integer;
|
||||
return integer::convert_to<std::uintmax_t>(json_uint.value.integer);
|
||||
}
|
||||
/*
|
||||
const std::vector<std::uintmax_t>& json_reader::unsigned_integer_array()
|
||||
{
|
||||
read_next_unsigned_array(
|
||||
}*/
|
||||
|
||||
std::uintmax_t json_reader::safe_unsigned_integer()
|
||||
{
|
||||
if (get_next_token() != '"')
|
||||
WIRE_DLOG_THROW_(error::schema::string);
|
||||
current_.remove_prefix(1);
|
||||
|
||||
const std::uintmax_t out = unsigned_integer();
|
||||
|
||||
if (get_next_token() != '"')
|
||||
WIRE_DLOG_THROW_(error::rapidjson_e(rapidjson::kParseErrorStringMissQuotationMark));
|
||||
current_.remove_prefix(1);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
double json_reader::real()
|
||||
{
|
||||
rapidjson_sax json_number{error::schema::number};
|
||||
read_next_value(json_number);
|
||||
return json_number.value.number;
|
||||
}
|
||||
|
||||
std::string json_reader::string()
|
||||
{
|
||||
rapidjson_sax json_string{error::schema::string};
|
||||
read_next_value(json_string);
|
||||
return std::string{json_string.value.string.ptr, json_string.value.string.length};
|
||||
}
|
||||
|
||||
std::vector<std::uint8_t> json_reader::binary()
|
||||
{
|
||||
const boost::string_ref value = get_next_string();
|
||||
|
||||
std::vector<std::uint8_t> out;
|
||||
out.resize(value.size() / 2);
|
||||
|
||||
if (!epee::from_hex::to_buffer(epee::to_mut_span(out), value))
|
||||
WIRE_DLOG_THROW_(error::schema::binary);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
void json_reader::binary(epee::span<std::uint8_t> dest)
|
||||
{
|
||||
const boost::string_ref value = get_next_string();
|
||||
if (!epee::from_hex::to_buffer(dest, value))
|
||||
WIRE_DLOG_THROW(error::schema::fixed_binary, "of size" << dest.size() * 2 << " but got " << value.size());
|
||||
}
|
||||
|
||||
std::size_t json_reader::enumeration(epee::span<char const* const> enums)
|
||||
{
|
||||
rapidjson_sax json_enum{error::schema::string};
|
||||
read_next_value(json_enum);
|
||||
|
||||
const boost::string_ref value{json_enum.value.string.ptr, json_enum.value.string.length};
|
||||
for (std::size_t i = 0; i < enums.size(); ++i)
|
||||
{
|
||||
if (value == enums[i])
|
||||
return i;
|
||||
}
|
||||
|
||||
WIRE_DLOG_THROW(error::schema::enumeration, value << " is not a valid enum");
|
||||
return enums.size();
|
||||
}
|
||||
|
||||
std::size_t json_reader::start_array()
|
||||
{
|
||||
if (get_next_token() != '[')
|
||||
WIRE_DLOG_THROW_(error::schema::array);
|
||||
current_.remove_prefix(1);
|
||||
increment_depth();
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool json_reader::is_array_end(const std::size_t count)
|
||||
{
|
||||
const char next = get_next_token();
|
||||
if (next == 0)
|
||||
WIRE_DLOG_THROW_(error::rapidjson_e(rapidjson::kParseErrorArrayMissCommaOrSquareBracket));
|
||||
if (next == ']')
|
||||
{
|
||||
current_.remove_prefix(1);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (count)
|
||||
{
|
||||
if (next != ',')
|
||||
WIRE_DLOG_THROW_(error::rapidjson_e(rapidjson::kParseErrorArrayMissCommaOrSquareBracket));
|
||||
current_.remove_prefix(1);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::size_t json_reader::start_object()
|
||||
{
|
||||
if (get_next_token() != '{')
|
||||
WIRE_DLOG_THROW_(error::schema::object);
|
||||
current_.remove_prefix(1);
|
||||
increment_depth();
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool json_reader::key(const epee::span<const key_map> map, std::size_t& state, std::size_t& index)
|
||||
{
|
||||
rapidjson_sax json_key{error::schema::string};
|
||||
const auto process_key = [map] (const rapidjson_sax::string_contents value)
|
||||
{
|
||||
const boost::string_ref key{value.ptr, value.length};
|
||||
for (std::size_t i = 0; i < map.size(); ++i)
|
||||
{
|
||||
if (map[i].name == key)
|
||||
return i;
|
||||
}
|
||||
return map.size();
|
||||
};
|
||||
|
||||
index = map.size();
|
||||
for (;;)
|
||||
{
|
||||
// check for object or text end
|
||||
const char next = get_next_token();
|
||||
if (next == 0)
|
||||
WIRE_DLOG_THROW_(error::rapidjson_e(rapidjson::kParseErrorObjectMissCommaOrCurlyBracket));
|
||||
if (next == '}')
|
||||
{
|
||||
current_.remove_prefix(1);
|
||||
return false;
|
||||
}
|
||||
|
||||
// parse next field token
|
||||
if (state)
|
||||
{
|
||||
if (next != ',')
|
||||
WIRE_DLOG_THROW_(error::rapidjson_e(rapidjson::kParseErrorObjectMissCommaOrCurlyBracket));
|
||||
current_.remove_prefix(1);
|
||||
}
|
||||
++state;
|
||||
|
||||
// parse key
|
||||
read_next_value(json_key);
|
||||
index = process_key(json_key.value.string);
|
||||
if (get_next_token() != ':')
|
||||
WIRE_DLOG_THROW_(error::rapidjson_e(rapidjson::kParseErrorObjectMissColon));
|
||||
current_.remove_prefix(1);
|
||||
|
||||
// parse value
|
||||
if (index != map.size())
|
||||
break;
|
||||
skip_value();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
134
src/wire/json/read.h
Normal file
134
src/wire/json/read.h
Normal file
@@ -0,0 +1,134 @@
|
||||
// Copyright (c) 2020, 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/utility/string_ref.hpp>
|
||||
#include <cstddef>
|
||||
#include <rapidjson/reader.h>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "wire/field.h"
|
||||
#include "wire/json/base.h"
|
||||
#include "wire/read.h"
|
||||
#include "wire/traits.h"
|
||||
|
||||
namespace wire
|
||||
{
|
||||
//! Reads JSON tokens one-at-a-time for DOMless parsing
|
||||
class json_reader : public reader
|
||||
{
|
||||
struct rapidjson_sax;
|
||||
|
||||
std::string source_;
|
||||
epee::span<char> current_;
|
||||
rapidjson::Reader reader_;
|
||||
|
||||
void read_next_value(rapidjson_sax& handler);
|
||||
char get_next_token();
|
||||
boost::string_ref get_next_string();
|
||||
|
||||
//! Skips next value. \throw wire::exception if invalid JSON syntax.
|
||||
void skip_value();
|
||||
|
||||
public:
|
||||
explicit json_reader(std::string&& source);
|
||||
|
||||
//! \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;
|
||||
|
||||
//! \throw wire::exception if next token not an unsigned integer.
|
||||
std::uintmax_t unsigned_integer() override final;
|
||||
|
||||
//! \throw wire::exception if next token is not an integer encoded as string
|
||||
std::uintmax_t safe_unsigned_integer();
|
||||
|
||||
//! \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<std::uint8_t> binary() override final;
|
||||
|
||||
//! \throw wire::exception if next token cannot be read as hex into `dest`.
|
||||
void binary(epee::span<std::uint8_t> dest) override final;
|
||||
|
||||
//! \throw wire::exception if invalid next token invalid enum. \return Index in `enums`.
|
||||
std::size_t enumeration(epee::span<char const* const> enums) override final;
|
||||
|
||||
|
||||
//! \throw wire::exception if next token not `[`.
|
||||
std::size_t start_array() override final;
|
||||
|
||||
//! Skips whitespace to next token. \return True if next token is eof or ']'.
|
||||
bool is_array_end(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<const key_map> map, std::size_t&, std::size_t& index) override final;
|
||||
};
|
||||
|
||||
|
||||
// Don't call `read` directly in this namespace, do it from `wire_read`.
|
||||
|
||||
template<typename T>
|
||||
expect<T> json::from_bytes(std::string&& bytes)
|
||||
{
|
||||
json_reader source{std::move(bytes)};
|
||||
return wire_read::to<T>(source);
|
||||
}
|
||||
|
||||
// specialization prevents type "downgrading" to base type in cpp files
|
||||
|
||||
template<typename T>
|
||||
inline void array(json_reader& source, T& dest)
|
||||
{
|
||||
wire_read::array(source, dest);
|
||||
}
|
||||
|
||||
template<typename... T>
|
||||
inline void object(json_reader& source, T... fields)
|
||||
{
|
||||
wire_read::object(source, wire_read::tracker<T>{std::move(fields)}...);
|
||||
}
|
||||
} // wire
|
||||
167
src/wire/json/write.cpp
Normal file
167
src/wire/json/write.cpp
Normal file
@@ -0,0 +1,167 @@
|
||||
// Copyright (c) 2020, 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 "write.h"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include "hex.h" // monero/contrib/epee/include
|
||||
|
||||
namespace
|
||||
{
|
||||
constexpr const unsigned flush_threshold = 100;
|
||||
}
|
||||
|
||||
namespace wire
|
||||
{
|
||||
void json_writer::do_flush(epee::span<const std::uint8_t>)
|
||||
{}
|
||||
|
||||
void json_writer::check_flush()
|
||||
{
|
||||
if (needs_flush_ && (bytes_.increase_size() < flush_threshold || bytes_.increase_size() - flush_threshold < bytes_.size()))
|
||||
flush();
|
||||
}
|
||||
|
||||
void json_writer::check_complete()
|
||||
{
|
||||
if (!formatter_.IsComplete())
|
||||
throw std::logic_error{"json_writer::take_json() failed with incomplete JSON tree"};
|
||||
}
|
||||
epee::byte_slice json_writer::take_json()
|
||||
{
|
||||
check_complete();
|
||||
epee::byte_slice out{std::move(bytes_)};
|
||||
formatter_.Reset(bytes_);
|
||||
return out;
|
||||
}
|
||||
|
||||
json_writer::~json_writer() noexcept
|
||||
{}
|
||||
|
||||
std::array<char, uint_to_string_size> json_writer::to_string(const std::uintmax_t value) noexcept
|
||||
{
|
||||
static_assert(std::numeric_limits<std::uintmax_t>::max() <= std::numeric_limits<std::uint64_t>::max(), "bad uint conversion");
|
||||
std::array<char, uint_to_string_size> buf{{}};
|
||||
rapidjson::internal::u64toa(std::uint64_t(value), buf.data());
|
||||
return buf;
|
||||
}
|
||||
|
||||
void json_writer::integer(const int source)
|
||||
{
|
||||
formatter_.Int(source);
|
||||
check_flush();
|
||||
}
|
||||
void json_writer::integer(const std::intmax_t source)
|
||||
{
|
||||
static_assert(std::numeric_limits<std::int64_t>::min() <= std::numeric_limits<std::intmax_t>::min(), "too small");
|
||||
static_assert(std::numeric_limits<std::intmax_t>::max() <= std::numeric_limits<std::int64_t>::max(), "too large");
|
||||
formatter_.Int64(source);
|
||||
check_flush();
|
||||
}
|
||||
void json_writer::unsigned_integer(const unsigned source)
|
||||
{
|
||||
formatter_.Uint(source);
|
||||
check_flush();
|
||||
}
|
||||
void json_writer::unsigned_integer(const std::uintmax_t source)
|
||||
{
|
||||
static_assert(std::numeric_limits<std::uintmax_t>::max() <= std::numeric_limits<std::uint64_t>::max(), "too large");
|
||||
formatter_.Uint64(source);
|
||||
check_flush();
|
||||
}
|
||||
void json_writer::real(const double source)
|
||||
{
|
||||
formatter_.Double(source);
|
||||
check_flush();
|
||||
}
|
||||
|
||||
void json_writer::string(const boost::string_ref source)
|
||||
{
|
||||
formatter_.String(source.data(), source.size());
|
||||
check_flush();
|
||||
}
|
||||
void json_writer::binary(epee::span<const std::uint8_t> source)
|
||||
{/* TODO update monero project
|
||||
std::array<char, 256> buffer;
|
||||
if (source.size() <= buffer.size() / 2)
|
||||
{
|
||||
if (!epee::to_hex::buffer({buffer.data(), source.size() * 2}, source))
|
||||
throw std::logic_error{"Invalid buffer size for binary->hex conversion"};
|
||||
string({buffer.data(), source.size() * 2});
|
||||
}
|
||||
else
|
||||
{*/
|
||||
const auto hex = epee::to_hex::string(source);
|
||||
string(hex);
|
||||
//}
|
||||
}
|
||||
|
||||
void json_writer::enumeration(const std::size_t index, const epee::span<char const* const> enums)
|
||||
{
|
||||
if (enums.size() < index)
|
||||
throw std::logic_error{"Invalid enum/string value"};
|
||||
string({enums[index], std::strlen(enums[index])});
|
||||
}
|
||||
|
||||
void json_writer::start_array(std::size_t)
|
||||
{
|
||||
formatter_.StartArray();
|
||||
}
|
||||
void json_writer::end_array()
|
||||
{
|
||||
formatter_.EndArray();
|
||||
}
|
||||
|
||||
void json_writer::start_object(std::size_t)
|
||||
{
|
||||
formatter_.StartObject();
|
||||
}
|
||||
void json_writer::key(const boost::string_ref str)
|
||||
{
|
||||
formatter_.Key(str.data(), str.size());
|
||||
check_flush();
|
||||
}
|
||||
void json_writer::key(const std::uintmax_t id)
|
||||
{
|
||||
auto str = json_writer::to_string(id);
|
||||
key(str.data());
|
||||
}
|
||||
void json_writer::key(unsigned, const boost::string_ref str)
|
||||
{
|
||||
key(str);
|
||||
}
|
||||
void json_writer::end_object()
|
||||
{
|
||||
formatter_.EndObject();
|
||||
}
|
||||
|
||||
void json_stream_writer::do_flush(epee::span<const std::uint8_t> bytes)
|
||||
{
|
||||
dest.write(reinterpret_cast<const char*>(bytes.data()), bytes.size());
|
||||
}
|
||||
}
|
||||
184
src/wire/json/write.h
Normal file
184
src/wire/json/write.h
Normal file
@@ -0,0 +1,184 @@
|
||||
// Copyright (c) 2020, 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 <array>
|
||||
#include <boost/utility/string_ref.hpp>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <rapidjson/writer.h>
|
||||
|
||||
#include "byte_stream.h" // monero/contrib/epee/include
|
||||
#include "span.h" // monero/contrib/epee/include
|
||||
#include "wire/field.h"
|
||||
#include "wire/filters.h"
|
||||
#include "wire/json/base.h"
|
||||
#include "wire/traits.h"
|
||||
#include "wire/write.h"
|
||||
|
||||
namespace wire
|
||||
{
|
||||
constexpr const std::size_t uint_to_string_size =
|
||||
std::numeric_limits<std::uintmax_t>::digits10 + 2;
|
||||
|
||||
//! Writes JSON tokens one-at-a-time for DOMless output.
|
||||
class json_writer : public writer
|
||||
{
|
||||
epee::byte_stream bytes_;
|
||||
rapidjson::Writer<epee::byte_stream> formatter_;
|
||||
bool needs_flush_;
|
||||
|
||||
//! \return True if buffer needs to be cleared
|
||||
virtual void do_flush(epee::span<const uint8_t>);
|
||||
|
||||
//! Flush written bytes to `do_flush(...)` if configured
|
||||
void check_flush();
|
||||
|
||||
protected:
|
||||
json_writer(bool needs_flush)
|
||||
: writer(), bytes_(), formatter_(bytes_), needs_flush_(needs_flush)
|
||||
{}
|
||||
|
||||
//! \throw std::logic_error if incomplete JSON tree
|
||||
void check_complete();
|
||||
|
||||
//! \throw std::logic_error if incomplete JSON tree. \return JSON bytes
|
||||
epee::byte_slice take_json();
|
||||
|
||||
//! Flush bytes in local buffer to `do_flush(...)`
|
||||
void flush()
|
||||
{
|
||||
do_flush({bytes_.data(), bytes_.size()});
|
||||
bytes_ = epee::byte_stream{}; // TODO create .clear() method in monero project
|
||||
}
|
||||
|
||||
public:
|
||||
json_writer(const json_writer&) = delete;
|
||||
virtual ~json_writer() noexcept;
|
||||
json_writer& operator=(const json_writer&) = delete;
|
||||
|
||||
//! \return Null-terminated buffer containing uint as decimal ascii
|
||||
static std::array<char, uint_to_string_size> to_string(std::uintmax_t) noexcept;
|
||||
|
||||
void integer(int) override final;
|
||||
void integer(std::intmax_t) override final;
|
||||
|
||||
void unsigned_integer(unsigned) override final;
|
||||
void unsigned_integer(std::uintmax_t) override final;
|
||||
|
||||
void real(double) override final;
|
||||
|
||||
void string(boost::string_ref) override final;
|
||||
void binary(epee::span<const std::uint8_t> source) override final;
|
||||
|
||||
void enumeration(std::size_t index, epee::span<char const* const> enums) override final;
|
||||
|
||||
void start_array(std::size_t) override final;
|
||||
void end_array() override final;
|
||||
|
||||
void start_object(std::size_t) override final;
|
||||
void key(std::uintmax_t) override final;
|
||||
void key(boost::string_ref) override final;
|
||||
void key(unsigned, boost::string_ref) override final;
|
||||
void end_object() override final;
|
||||
};
|
||||
|
||||
//! Buffers entire JSON message in memory
|
||||
struct json_slice_writer final : json_writer
|
||||
{
|
||||
explicit json_slice_writer()
|
||||
: json_writer(false)
|
||||
{}
|
||||
|
||||
//! \throw std::logic_error if incomplete JSON tree \return JSON bytes
|
||||
epee::byte_slice take_bytes()
|
||||
{
|
||||
return json_writer::take_json();
|
||||
}
|
||||
};
|
||||
|
||||
//! Periodically flushes JSON data to `std::ostream`
|
||||
class json_stream_writer final : public json_writer
|
||||
{
|
||||
std::ostream& dest;
|
||||
|
||||
virtual void do_flush(epee::span<const std::uint8_t>) override final;
|
||||
public:
|
||||
explicit json_stream_writer(std::ostream& dest)
|
||||
: json_writer(true), dest(dest)
|
||||
{}
|
||||
|
||||
//! Flush remaining bytes to stream \throw std::logic_error if incomplete JSON tree
|
||||
void finish()
|
||||
{
|
||||
check_complete();
|
||||
flush();
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
epee::byte_slice json::to_bytes(const T& source)
|
||||
{
|
||||
return wire_write::to_bytes<json_slice_writer>(source);
|
||||
}
|
||||
|
||||
template<typename T, typename F = identity_>
|
||||
inline void array(json_writer& dest, const T& source, F filter = F{})
|
||||
{
|
||||
// works with "lazily" computed ranges
|
||||
wire_write::array(dest, source, 0, std::move(filter));
|
||||
}
|
||||
template<typename T, typename F>
|
||||
inline void write_bytes(json_writer& dest, as_array_<T, F> source)
|
||||
{
|
||||
wire::array(dest, source.get_value(), std::move(source.filter));
|
||||
}
|
||||
template<typename T>
|
||||
inline enable_if<is_array<T>::value> write_bytes(json_writer& dest, const T& source)
|
||||
{
|
||||
wire::array(dest, source);
|
||||
}
|
||||
|
||||
template<typename T, typename F = identity_, typename G = identity_>
|
||||
inline void dynamic_object(json_writer& dest, const T& source, F key_filter = F{}, G value_filter = G{})
|
||||
{
|
||||
// works with "lazily" computed ranges
|
||||
wire_write::dynamic_object(dest, source, 0, std::move(key_filter), std::move(value_filter));
|
||||
}
|
||||
template<typename T, typename F, typename G>
|
||||
inline void write_bytes(json_writer& dest, as_object_<T, F, G> source)
|
||||
{
|
||||
wire::dynamic_object(dest, source.get_map(), std::move(source.key_filter), std::move(source.value_filter));
|
||||
}
|
||||
|
||||
template<typename... T>
|
||||
inline void object(json_writer& dest, T... fields)
|
||||
{
|
||||
wire_write::object(dest, std::move(fields)...);
|
||||
}
|
||||
}
|
||||
61
src/wire/read.cpp
Normal file
61
src/wire/read.cpp
Normal file
@@ -0,0 +1,61 @@
|
||||
// Copyright (c) 2020, 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 "wire/read.h"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
void wire::reader::increment_depth()
|
||||
{
|
||||
if (++depth_ == max_read_depth())
|
||||
WIRE_DLOG_THROW_(error::schema::maximum_depth);
|
||||
}
|
||||
|
||||
[[noreturn]] void wire::integer::throw_exception(std::intmax_t source, std::intmax_t min)
|
||||
{
|
||||
WIRE_DLOG_THROW(error::schema::larger_integer, source << " given when " << min << " is minimum permitted");
|
||||
}
|
||||
[[noreturn]] void wire::integer::throw_exception(std::uintmax_t source, 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 : ""));
|
||||
}
|
||||
|
||||
|
||||
471
src/wire/read.h
Normal file
471
src/wire/read.h
Normal file
@@ -0,0 +1,471 @@
|
||||
// Copyright (c) 2020, 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 <tuple>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include "common/expect.h" // monero/src
|
||||
#include "span.h" // monero/contrib/epee/include
|
||||
#include "wire/error.h"
|
||||
#include "wire/field.h"
|
||||
#include "wire/traits.h"
|
||||
|
||||
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
|
||||
|
||||
protected:
|
||||
//! \throw wire::exception if max depth is reached
|
||||
void increment_depth();
|
||||
void decrement_depth() noexcept { --depth_; }
|
||||
|
||||
reader(const reader&) = default;
|
||||
reader(reader&&) = default;
|
||||
reader& operator=(const reader&) = default;
|
||||
reader& operator=(reader&&) = default;
|
||||
|
||||
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; }
|
||||
|
||||
reader() noexcept
|
||||
: depth_(0)
|
||||
{}
|
||||
|
||||
virtual ~reader() noexcept
|
||||
{}
|
||||
|
||||
//! \return Number of recursive objects and arrays
|
||||
std::size_t depth() const noexcept { return depth_; }
|
||||
|
||||
//! \throw wire::exception if parsing is incomplete.
|
||||
virtual void check_complete() const = 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;
|
||||
|
||||
// ! \throw wire::exception if next value cannot be read as binary
|
||||
virtual std::vector<std::uint8_t> binary() = 0;
|
||||
|
||||
//! \throw wire::exception if next value cannot be read as binary into `dest`.
|
||||
virtual void binary(epee::span<std::uint8_t> dest) = 0;
|
||||
|
||||
//! \throw wire::exception if next value invalid enum. \return Index in `enums`.
|
||||
virtual std::size_t enumeration(epee::span<char const* const> enums) = 0;
|
||||
|
||||
/*! \throw wire::exception if next value not array
|
||||
\return Number of values to read before calling `is_array_end()`. */
|
||||
virtual std::size_t start_array() = 0;
|
||||
|
||||
//! \return True if there is another element to read.
|
||||
virtual bool is_array_end(std::size_t count) = 0;
|
||||
|
||||
//! \throw wire::exception if array end delimiter not present.
|
||||
void end_array() noexcept { decrement_depth(); }
|
||||
|
||||
|
||||
//! \throw wire::exception if not object begin. \return State to be given to `key(...)` function.
|
||||
virtual std::size_t start_object() = 0;
|
||||
|
||||
/*! 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() noexcept { decrement_depth(); }
|
||||
};
|
||||
|
||||
inline void read_bytes(reader& source, bool& dest)
|
||||
{
|
||||
dest = source.boolean();
|
||||
}
|
||||
|
||||
inline void read_bytes(reader& source, double& dest)
|
||||
{
|
||||
dest = source.real();
|
||||
}
|
||||
|
||||
inline void read_bytes(reader& source, std::string& dest)
|
||||
{
|
||||
dest = source.string();
|
||||
}
|
||||
|
||||
template<typename R>
|
||||
inline void read_bytes(R& source, std::vector<std::uint8_t>& dest)
|
||||
{
|
||||
dest = source.binary();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline enable_if<is_blob<T>::value> read_bytes(reader& source, T& dest)
|
||||
{
|
||||
source.binary(epee::as_mut_byte_span(dest));
|
||||
}
|
||||
|
||||
namespace integer
|
||||
{
|
||||
[[noreturn]] void throw_exception(std::intmax_t source, std::intmax_t min);
|
||||
[[noreturn]] void throw_exception(std::uintmax_t source, std::uintmax_t max);
|
||||
|
||||
template<typename Target, typename U>
|
||||
inline Target convert_to(const U source)
|
||||
{
|
||||
using common = typename std::common_type<Target, U>::type;
|
||||
static constexpr const Target target_min = std::numeric_limits<Target>::min();
|
||||
static constexpr const Target target_max = std::numeric_limits<Target>::max();
|
||||
|
||||
/* After optimizations, this is:
|
||||
* 1 check for unsigned -> unsigned (uint, uint)
|
||||
* 2 checks for signed -> signed (int, int)
|
||||
* 2 checks for signed -> unsigned-- (
|
||||
* 1 check for unsigned -> signed (uint, uint)
|
||||
|
||||
Put `WIRE_DLOG_THROW` in cpp to reduce code/ASM duplication. Do not
|
||||
remove first check, signed values can be implicitly converted to
|
||||
unsigned in some checks. */
|
||||
if (!std::numeric_limits<Target>::is_signed && source < 0)
|
||||
throw_exception(std::intmax_t(source), std::intmax_t(0));
|
||||
else if (common(source) < common(target_min))
|
||||
throw_exception(std::intmax_t(source), std::intmax_t(target_min));
|
||||
else if (common(target_max) < common(source))
|
||||
throw_exception(std::uintmax_t(source), std::uintmax_t(target_max));
|
||||
|
||||
return Target(source);
|
||||
}
|
||||
}
|
||||
|
||||
inline void read_bytes(reader& source, char& dest)
|
||||
{
|
||||
dest = integer::convert_to<char>(source.integer());
|
||||
}
|
||||
inline void read_bytes(reader& source, short& dest)
|
||||
{
|
||||
dest = integer::convert_to<short>(source.integer());
|
||||
}
|
||||
inline void read_bytes(reader& source, int& dest)
|
||||
{
|
||||
dest = integer::convert_to<int>(source.integer());
|
||||
}
|
||||
inline void read_bytes(reader& source, long& dest)
|
||||
{
|
||||
dest = integer::convert_to<long>(source.integer());
|
||||
}
|
||||
inline void read_bytes(reader& source, long long& dest)
|
||||
{
|
||||
dest = integer::convert_to<long long>(source.integer());
|
||||
}
|
||||
|
||||
inline void read_bytes(reader& source, unsigned char& dest)
|
||||
{
|
||||
dest = integer::convert_to<unsigned char>(source.unsigned_integer());
|
||||
}
|
||||
inline void read_bytes(reader& source, unsigned short& dest)
|
||||
{
|
||||
dest = integer::convert_to<unsigned short>(source.unsigned_integer());
|
||||
}
|
||||
inline void read_bytes(reader& source, unsigned& dest)
|
||||
{
|
||||
dest = integer::convert_to<unsigned>(source.unsigned_integer());
|
||||
}
|
||||
inline void read_bytes(reader& source, unsigned long& dest)
|
||||
{
|
||||
dest = integer::convert_to<unsigned long>(source.unsigned_integer());
|
||||
}
|
||||
inline void read_bytes(reader& source, unsigned long long& dest)
|
||||
{
|
||||
dest = integer::convert_to<unsigned long long>(source.unsigned_integer());
|
||||
}
|
||||
} // wire
|
||||
|
||||
namespace wire_read
|
||||
{
|
||||
/*! Don't add a function called `read_bytes` to this namespace, it will prevent
|
||||
ADL lookup. ADL 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);
|
||||
|
||||
//! \return `T` converted from `source` or error.
|
||||
template<typename T, typename R>
|
||||
inline expect<T> to(R& source)
|
||||
{
|
||||
try
|
||||
{
|
||||
T dest{};
|
||||
read_bytes(source, dest);
|
||||
source.check_complete();
|
||||
return dest;
|
||||
}
|
||||
catch (const wire::exception& e)
|
||||
{
|
||||
return e.code();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename R, typename T>
|
||||
inline void array(R& source, T& dest)
|
||||
{
|
||||
using value_type = typename T::value_type;
|
||||
static_assert(!std::is_same<value_type, char>::value, "read array of 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();
|
||||
|
||||
dest.clear();
|
||||
dest.reserve(count);
|
||||
|
||||
bool more = count;
|
||||
while (more || !source.is_array_end(count))
|
||||
{
|
||||
dest.emplace_back();
|
||||
read_bytes(source, dest.back());
|
||||
--count;
|
||||
more &= bool(count);
|
||||
}
|
||||
|
||||
return source.end_array();
|
||||
}
|
||||
|
||||
// `unpack_variant_field` identifies which of the variant types was selected. starts with index-0
|
||||
|
||||
template<typename R, typename T>
|
||||
inline void unpack_variant_field(std::size_t, R&, const T&)
|
||||
{}
|
||||
|
||||
template<typename R, typename T, typename U, typename... X>
|
||||
inline void unpack_variant_field(const std::size_t index, R& source, T& variant, const wire::option<U>& head, const wire::option<X>&... tail)
|
||||
{
|
||||
if (index)
|
||||
unpack_variant_field(index - 1, source, variant, tail...);
|
||||
else
|
||||
{
|
||||
U dest{};
|
||||
read_bytes(source, dest);
|
||||
variant = std::move(dest);
|
||||
}
|
||||
}
|
||||
|
||||
// `unpack_field` expands `variant_field_`s or reads `field_`s directly
|
||||
|
||||
template<typename R, typename T, bool Required, typename... U>
|
||||
inline void unpack_field(const std::size_t index, R& source, wire::variant_field_<T, Required, U...>& dest)
|
||||
{
|
||||
unpack_variant_field(index, source, dest.get_value(), static_cast< const wire::option<U>& >(dest)...);
|
||||
}
|
||||
|
||||
template<typename R, typename T>
|
||||
inline void unpack_field(std::size_t, R& source, wire::field_<T, true>& dest)
|
||||
{
|
||||
read_bytes(source, dest.get_value());
|
||||
}
|
||||
|
||||
template<typename R, typename T>
|
||||
inline void unpack_field(std::size_t, R& source, wire::field_<T, false>& dest)
|
||||
{
|
||||
dest.get_value().emplace();
|
||||
read_bytes(source, *dest.get_value());
|
||||
}
|
||||
|
||||
// `expand_field_map` writes a single `field_` name or all option names in a `variant_field_` to a table
|
||||
|
||||
template<std::size_t N>
|
||||
inline void expand_field_map(std::size_t, wire::reader::key_map (&)[N])
|
||||
{}
|
||||
|
||||
template<std::size_t N, typename T, typename... U>
|
||||
inline void expand_field_map(std::size_t index, wire::reader::key_map (&map)[N], const T& head, const U&... tail)
|
||||
{
|
||||
map[index].name = head.name;
|
||||
map[index].id = 0;
|
||||
expand_field_map(index + 1, map, tail...);
|
||||
}
|
||||
|
||||
template<std::size_t N, typename T, bool Required, typename... U>
|
||||
inline void expand_field_map(std::size_t index, wire::reader::key_map (&map)[N], const wire::variant_field_<T, Required, U...>& field)
|
||||
{
|
||||
expand_field_map(index, map, static_cast< const wire::option<U> & >(field)...);
|
||||
}
|
||||
|
||||
//! 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])
|
||||
{
|
||||
our_index_ = index;
|
||||
expand_field_map(index, map, field_); // expands possible inner options
|
||||
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(index - our_index_, source, field_);
|
||||
read_ = true;
|
||||
return 1 + is_required();
|
||||
}
|
||||
};
|
||||
|
||||
// `expand_tracker_map` writes all `tracker` types to a table
|
||||
|
||||
template<std::size_t N>
|
||||
inline constexpr std::size_t expand_tracker_map(std::size_t index, const wire::reader::key_map (&)[N])
|
||||
{
|
||||
return index;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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");
|
||||
|
||||
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] = {};
|
||||
expand_tracker_map(0, map, fields...);
|
||||
|
||||
std::size_t next = 0;
|
||||
while (source.key(map, 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()...};
|
||||
throw_exception(wire::error::schema::missing_key, "", missing);
|
||||
}
|
||||
|
||||
source.end_object();
|
||||
}
|
||||
} // wire_read
|
||||
|
||||
namespace wire
|
||||
{
|
||||
template<typename T>
|
||||
inline void array(reader& source, T& dest)
|
||||
{
|
||||
wire_read::array(source, dest);
|
||||
}
|
||||
template<typename R, typename T>
|
||||
inline enable_if<is_array<T>::value> read_bytes(R& source, T& dest)
|
||||
{
|
||||
wire_read::array(source, dest);
|
||||
}
|
||||
|
||||
template<typename... T>
|
||||
inline void object(reader& source, T... fields)
|
||||
{
|
||||
wire_read::object(source, wire_read::tracker<T>{std::move(fields)}...);
|
||||
}
|
||||
}
|
||||
46
src/wire/traits.h
Normal file
46
src/wire/traits.h
Normal file
@@ -0,0 +1,46 @@
|
||||
// Copyright (c) 2020, 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>
|
||||
|
||||
namespace wire
|
||||
{
|
||||
template<bool C>
|
||||
using enable_if = typename std::enable_if<C>::type;
|
||||
|
||||
template<typename T>
|
||||
struct is_array : std::false_type
|
||||
{};
|
||||
|
||||
template<typename T>
|
||||
struct is_blob : std::false_type
|
||||
{};
|
||||
}
|
||||
|
||||
41
src/wire/vector.h
Normal file
41
src/wire/vector.h
Normal file
@@ -0,0 +1,41 @@
|
||||
// Copyright (c) 2020, 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 <vector>
|
||||
|
||||
#include "wire/traits.h"
|
||||
|
||||
namespace wire
|
||||
{
|
||||
template<typename T>
|
||||
struct is_array<std::vector<T>>
|
||||
: std::true_type
|
||||
{};
|
||||
}
|
||||
31
src/wire/write.cpp
Normal file
31
src/wire/write.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
// Copyright (c) 2020, 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 "write.h"
|
||||
|
||||
wire::writer::~writer() noexcept
|
||||
{}
|
||||
224
src/wire/write.h
Normal file
224
src/wire/write.h
Normal file
@@ -0,0 +1,224 @@
|
||||
// Copyright (c) 2020, 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 <array>
|
||||
#include <boost/utility/string_ref.hpp>
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
|
||||
#include "byte_slice.h" // monero/contrib/epee/include
|
||||
#include "span.h" // monero/contrib/epee/include
|
||||
#include "wire/field.h"
|
||||
#include "wire/filters.h"
|
||||
#include "wire/traits.h"
|
||||
|
||||
namespace wire
|
||||
{
|
||||
//! Interface for converting C/C++ objects to "wire" (byte) formats.
|
||||
struct writer
|
||||
{
|
||||
writer() = default;
|
||||
|
||||
virtual ~writer() noexcept;
|
||||
|
||||
virtual void integer(int) = 0;
|
||||
virtual void integer(std::intmax_t) = 0;
|
||||
|
||||
virtual void unsigned_integer(unsigned) = 0;
|
||||
virtual void unsigned_integer(std::uintmax_t) = 0;
|
||||
|
||||
virtual void real(double) = 0;
|
||||
|
||||
virtual void string(boost::string_ref) = 0;
|
||||
virtual void binary(epee::span<const std::uint8_t> bytes) = 0;
|
||||
|
||||
virtual void enumeration(std::size_t index, epee::span<char const* const> enums) = 0;
|
||||
|
||||
virtual void start_array(std::size_t) = 0;
|
||||
virtual void end_array() = 0;
|
||||
|
||||
virtual void start_object(std::size_t) = 0;
|
||||
virtual void key(std::uintmax_t) = 0;
|
||||
virtual void key(boost::string_ref) = 0;
|
||||
virtual void key(unsigned, boost::string_ref) = 0; //!< Implementation should output fastest key
|
||||
virtual void end_object() = 0;
|
||||
|
||||
protected:
|
||||
writer(const writer&) = default;
|
||||
writer(writer&&) = default;
|
||||
writer& operator=(const writer&) = default;
|
||||
writer& operator=(writer&&) = default;
|
||||
};
|
||||
|
||||
// leave in header, compiler can de-virtualize when final type is given
|
||||
|
||||
inline void write_bytes(writer& dest, const int source)
|
||||
{
|
||||
dest.integer(source);
|
||||
}
|
||||
inline void write_bytes(writer& dest, const std::intmax_t source)
|
||||
{
|
||||
dest.integer(source);
|
||||
}
|
||||
|
||||
inline void write_bytes(writer& dest, const unsigned source)
|
||||
{
|
||||
dest.unsigned_integer(source);
|
||||
}
|
||||
inline void write_bytes(writer& dest, const std::uintmax_t source)
|
||||
{
|
||||
dest.unsigned_integer(source);
|
||||
}
|
||||
|
||||
inline void write_bytes(writer& dest, const double source)
|
||||
{
|
||||
dest.real(source);
|
||||
}
|
||||
|
||||
inline void write_bytes(writer& dest, const boost::string_ref source)
|
||||
{
|
||||
dest.string(source);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline enable_if<is_blob<T>::value> write_bytes(writer& dest, const T& source)
|
||||
{
|
||||
dest.binary(epee::as_byte_span(source));
|
||||
}
|
||||
|
||||
inline void write_bytes(writer& dest, const epee::span<const std::uint8_t> source)
|
||||
{
|
||||
dest.binary(source);
|
||||
}
|
||||
}
|
||||
|
||||
namespace wire_write
|
||||
{
|
||||
/*! Don't add a function called `write_bytes` to this namespace, it will
|
||||
prevent ADL lookup. ADL lookup delays the function searching until the
|
||||
template is used instead of when its defined. This allows the unqualified
|
||||
calls to `write_bytes` in this namespace to "find" user functions that are
|
||||
declared after these functions. */
|
||||
|
||||
template<typename W, typename T>
|
||||
inline epee::byte_slice to_bytes(const T& value)
|
||||
{
|
||||
W dest{};
|
||||
write_bytes(dest, value);
|
||||
return dest.take_bytes();
|
||||
}
|
||||
|
||||
template<typename W, typename T, typename F = wire::identity_>
|
||||
inline void array(W& dest, const T& source, const std::size_t count, F filter = F{})
|
||||
{
|
||||
using value_type = typename T::value_type;
|
||||
static_assert(!std::is_same<value_type, char>::value, "write array of chars as binary");
|
||||
static_assert(!std::is_same<value_type, std::uint8_t>::value, "write array of unsigned chars as binary");
|
||||
|
||||
dest.start_array(count);
|
||||
for (const auto& elem : source)
|
||||
write_bytes(dest, filter(elem));
|
||||
dest.end_array();
|
||||
}
|
||||
|
||||
template<typename W, typename T>
|
||||
inline bool field(W& dest, const wire::field_<T, true> elem)
|
||||
{
|
||||
dest.key(0, elem.name);
|
||||
write_bytes(dest, elem.get_value());
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename W, typename T>
|
||||
inline bool field(W& dest, const wire::field_<T, false> elem)
|
||||
{
|
||||
if (bool(elem.get_value()))
|
||||
{
|
||||
dest.key(0, elem.name);
|
||||
write_bytes(dest, *elem.get_value());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename W, typename... T>
|
||||
inline void object(W& dest, T... fields)
|
||||
{
|
||||
dest.start_object(wire::sum(std::size_t(wire::available(fields))...));
|
||||
const bool dummy[] = {field(dest, std::move(fields))...};
|
||||
dest.end_object();
|
||||
}
|
||||
|
||||
template<typename W, typename T, typename F, typename G>
|
||||
inline void dynamic_object(W& dest, const T& values, const std::size_t count, F key_filter, G value_filter)
|
||||
{
|
||||
dest.start_object(count);
|
||||
for (const auto& elem : values)
|
||||
{
|
||||
dest.key(key_filter(elem.first));
|
||||
write_bytes(dest, value_filter(elem.second));
|
||||
}
|
||||
dest.end_object();
|
||||
}
|
||||
} // wire_write
|
||||
|
||||
namespace wire
|
||||
{
|
||||
template<typename T, typename F = identity_>
|
||||
inline void array(writer& dest, const T& source, F filter = F{})
|
||||
{
|
||||
wire_write::array(dest, source, source.size(), std::move(filter));
|
||||
}
|
||||
template<typename T, typename F>
|
||||
inline void write_bytes(writer& dest, as_array_<T, F> source)
|
||||
{
|
||||
wire::array(dest, source.get_value(), std::move(source.filter));
|
||||
}
|
||||
template<typename T>
|
||||
inline enable_if<is_array<T>::value> write_bytes(writer& dest, const T& source)
|
||||
{
|
||||
wire::array(dest, source);
|
||||
}
|
||||
|
||||
template<typename T, typename F = identity_, typename G = identity_>
|
||||
inline void dynamic_object(writer& dest, const T& source, F key_filter = F{}, G value_filter = G{})
|
||||
{
|
||||
wire_write::dynamic_object(dest, source, source.size(), std::move(key_filter), std::move(value_filter));
|
||||
}
|
||||
template<typename T, typename F, typename G>
|
||||
inline void write_bytes(writer& dest, as_object_<T, F, G> source)
|
||||
{
|
||||
wire::dynamic_object(dest, source.get_map(), std::move(source.key_filter), std::move(source.value_filter));
|
||||
}
|
||||
|
||||
template<typename... T>
|
||||
inline void object(writer& dest, T... fields)
|
||||
{
|
||||
wire_write::object(dest, std::move(fields)...);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user