mirror of
https://github.com/vtnerd/monero-lws.git
synced 2024-12-22 19:39:23 +00:00
Adding msgpack support to ::wire:: library (#63)
This commit is contained in:
parent
64f5d4a9ab
commit
3ad71ba01e
19 changed files with 1876 additions and 50 deletions
|
@ -34,3 +34,4 @@ target_include_directories(monero-lws-wire PUBLIC "${LMDB_INCLUDE}")
|
|||
target_link_libraries(monero-lws-wire PRIVATE monero::libraries)
|
||||
|
||||
add_subdirectory(json)
|
||||
add_subdirectory(msgpack)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2020, The Monero Project
|
||||
// Copyright (c) 2020-2023, The Monero Project
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
|
@ -33,9 +33,13 @@
|
|||
#include "wire/filters.h"
|
||||
#include "wire/traits.h"
|
||||
|
||||
//! A required field with the same key name and C/C++ name
|
||||
#define WIRE_FIELD_ID(id, name) \
|
||||
::wire::field< id >( #name , std::ref( self . name ))
|
||||
|
||||
//! A required field has the same key name and C/C++ name
|
||||
#define WIRE_FIELD(name) \
|
||||
::wire::field( #name , std::ref( self . name ))
|
||||
#define WIRE_FIELD(name) \
|
||||
WIRE_FIELD_ID(0, name)
|
||||
|
||||
//! A required field has the same key name and C/C++ name AND is cheap to copy (faster output).
|
||||
#define WIRE_FIELD_COPY(name) \
|
||||
|
@ -61,12 +65,13 @@ namespace wire
|
|||
|
||||
|
||||
//! Links `name` to a `value` for object serialization.
|
||||
template<typename T, bool Required>
|
||||
template<typename T, bool Required, unsigned I = 0>
|
||||
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; }
|
||||
static constexpr unsigned id() noexcept { return I; }
|
||||
|
||||
const char* name;
|
||||
T value;
|
||||
|
@ -85,15 +90,15 @@ namespace wire
|
|||
};
|
||||
|
||||
//! Links `name` to `value`. Use `std::ref` if de-serializing.
|
||||
template<typename T>
|
||||
constexpr inline field_<T, true> field(const char* name, T value)
|
||||
template<unsigned I = 0, typename T = void>
|
||||
constexpr inline field_<T, true, I> field(const char* name, T value)
|
||||
{
|
||||
return {name, std::move(value)};
|
||||
}
|
||||
|
||||
//! Links `name` to `value`. Use `std::ref` if de-serializing.
|
||||
template<typename T>
|
||||
constexpr inline field_<T, false> optional_field(const char* name, T value)
|
||||
template<unsigned I = 0, typename T = void>
|
||||
constexpr inline field_<T, false, I> optional_field(const char* name, T value)
|
||||
{
|
||||
return {name, std::move(value)};
|
||||
}
|
||||
|
@ -103,6 +108,7 @@ namespace wire
|
|||
template<typename T>
|
||||
struct option
|
||||
{
|
||||
static constexpr unsigned id() noexcept { return 0; }
|
||||
const char* name;
|
||||
};
|
||||
|
||||
|
@ -243,13 +249,13 @@ namespace wire
|
|||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
inline constexpr bool available(const field_<T, true>&) noexcept
|
||||
template<typename T, unsigned I>
|
||||
inline constexpr bool available(const field_<T, true, I>&) noexcept
|
||||
{
|
||||
return true;
|
||||
}
|
||||
template<typename T>
|
||||
inline bool available(const field_<T, false>& elem)
|
||||
template<typename T, unsigned I>
|
||||
inline bool available(const field_<T, false, I>& elem)
|
||||
{
|
||||
return bool(elem.get_value());
|
||||
}
|
||||
|
|
54
src/wire/msgpack.h
Normal file
54
src/wire/msgpack.h
Normal file
|
@ -0,0 +1,54 @@
|
|||
// Copyright (c) 2023, The Monero Project
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "wire/msgpack/base.h"
|
||||
#include "wire/msgpack/error.h"
|
||||
#include "wire/msgpack/read.h"
|
||||
#include "wire/msgpack/write.h"
|
||||
|
||||
#define WIRE_MSGPACK_DEFINE_ENUM(type, map) \
|
||||
void read_bytes(::wire::msgpack_reader& source, type& dest) \
|
||||
{ \
|
||||
dest = type(source.enumeration(map)); \
|
||||
} \
|
||||
void write_bytes(::wire::msgpack_writer& dest, const type source) \
|
||||
{ \
|
||||
dest.enumeration(std::size_t(source), map); \
|
||||
}
|
||||
|
||||
#define WIRE_MSGPACK_DEFINE_OBJECT(type, map) \
|
||||
void read_bytes(::wire::msgpack_reader& source, type& dest) \
|
||||
{ \
|
||||
map(source, dest); \
|
||||
} \
|
||||
void write_bytes(::wire::msgpack_writer& dest, const type& source) \
|
||||
{ \
|
||||
map(dest, source); \
|
||||
}
|
||||
|
33
src/wire/msgpack/CMakeLists.txt
Normal file
33
src/wire/msgpack/CMakeLists.txt
Normal file
|
@ -0,0 +1,33 @@
|
|||
# Copyright (c) 2023, The Monero Project
|
||||
#
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification, are
|
||||
# permitted provided that the following conditions are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
# conditions and the following disclaimer.
|
||||
#
|
||||
# 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
# of conditions and the following disclaimer in the documentation and/or other
|
||||
# materials provided with the distribution.
|
||||
#
|
||||
# 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
# used to endorse or promote products derived from this software without specific
|
||||
# prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
# THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
set(monero-lws_wire-msgpack_sources error.cpp read.cpp write.cpp)
|
||||
set(monero-lws_wire-msgpack_headers base.h error.h fwd.h read.h write.h)
|
||||
|
||||
add_library(monero-lws-wire-msgpack ${monero-lws_wire-msgpack_sources} ${monero-lws-wire-msgpack_headers})
|
||||
target_link_libraries(monero-lws-wire-msgpack monero::libraries monero-lws-wire)
|
171
src/wire/msgpack/base.h
Normal file
171
src/wire/msgpack/base.h
Normal file
|
@ -0,0 +1,171 @@
|
|||
// Copyright (c) 2023, The Monero Project
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
|
||||
#include "byte_slice.h"
|
||||
#include "common/expect.h"
|
||||
#include "wire/msgpack/fwd.h"
|
||||
|
||||
namespace wire
|
||||
{
|
||||
struct msgpack
|
||||
{
|
||||
using input_type = msgpack_reader;
|
||||
using output_type = msgpack_writer;
|
||||
|
||||
//! Tags that do not require bitmask to identify
|
||||
enum class tag : std::uint8_t
|
||||
{
|
||||
nil = 0xc0,
|
||||
unused,
|
||||
False,
|
||||
True,
|
||||
binary8,
|
||||
binary16,
|
||||
binary32,
|
||||
extension8,
|
||||
extension16,
|
||||
extension32,
|
||||
float32,
|
||||
float64,
|
||||
uint8,
|
||||
uint16,
|
||||
uint32,
|
||||
uint64,
|
||||
int8,
|
||||
int16,
|
||||
int32,
|
||||
int64,
|
||||
fixed_extension1,
|
||||
fixed_extension2,
|
||||
fixed_extension4,
|
||||
fixed_extension8,
|
||||
fixed_extension16,
|
||||
string8,
|
||||
string16,
|
||||
string32,
|
||||
array16,
|
||||
array32,
|
||||
object16,
|
||||
object32
|
||||
};
|
||||
|
||||
//! Link a fixed tag `T` to its corresponding mask `M` and max value `N`
|
||||
template<std::uint8_t T, std::uint8_t M, std::uint8_t N>
|
||||
struct fixed_tag
|
||||
{
|
||||
static constexpr std::uint8_t tag() noexcept { return T; }
|
||||
static constexpr std::uint8_t mask() noexcept { return M; }
|
||||
static constexpr std::uint8_t max() noexcept { return N; }
|
||||
|
||||
//! \return True if `value` is fixed tag `T`
|
||||
static constexpr bool matches(const std::uint8_t value) noexcept
|
||||
{ return (value & mask()) == tag(); }
|
||||
|
||||
//! \return True if `value` is fixed tag `T`
|
||||
static constexpr bool matches(const msgpack::tag value) noexcept
|
||||
{ return matches(std::uint8_t(value)); }
|
||||
|
||||
//! \return Value encoded in fixed tag
|
||||
static constexpr std::uint8_t extract(const std::uint8_t value) noexcept
|
||||
{ return value & ~mask(); }
|
||||
|
||||
//! \return Value encoded in fixed tag
|
||||
static constexpr std::uint8_t extract(const msgpack::tag value) noexcept
|
||||
{ return extract(std::uint8_t(value)); }
|
||||
};
|
||||
|
||||
// Tags requiring bitmask to identify
|
||||
using ftag_unsigned = fixed_tag<0x00, 0x80, 0x7f>;
|
||||
using ftag_signed = fixed_tag<0xe0, 0xe0, 0>;
|
||||
using ftag_string = fixed_tag<0xa0, 0xe0, 31>;
|
||||
using ftag_array = fixed_tag<0x90, 0xf0, 15>;
|
||||
using ftag_object = fixed_tag<0x80, 0xf0, 15>;
|
||||
|
||||
//! Link a msgpack tag to a C++ numeric
|
||||
template<typename T, tag V>
|
||||
struct type
|
||||
{
|
||||
static constexpr bool is_signed() noexcept { return std::numeric_limits<T>::is_signed; }
|
||||
static constexpr T min() noexcept { return std::numeric_limits<T>::min(); }
|
||||
static constexpr T max() noexcept { return std::numeric_limits<T>::max(); }
|
||||
static constexpr tag Tag() noexcept { return V; }
|
||||
};
|
||||
|
||||
using int8 = type<std::int8_t, tag::int8>;
|
||||
using int16 = type<std::int16_t, tag::int16>;
|
||||
using int32 = type<std::int32_t, tag::int32>;
|
||||
using int64 = type<std::int64_t, tag::int64>;
|
||||
using signed_types = std::tuple<int8, int16, int32, int64>;
|
||||
|
||||
using uint8 = type<std::uint8_t, tag::uint8>;
|
||||
using uint16 = type<std::uint16_t, tag::uint16>;
|
||||
using uint32 = type<std::uint32_t, tag::uint32>;
|
||||
using uint64 = type<std::uint64_t, tag::uint64>;
|
||||
using unsigned_types = std::tuple<uint8, uint16, uint32, uint64>;
|
||||
|
||||
using integer_types = std::tuple<
|
||||
msgpack::uint8, msgpack::int8, msgpack::uint16, msgpack::int16,
|
||||
msgpack::uint32, msgpack::int32, msgpack::uint64, msgpack::int64
|
||||
>;
|
||||
|
||||
using string8 = type<std::uint8_t, tag::string8>;
|
||||
using string16 = type<std::uint16_t, tag::string16>;
|
||||
using string32 = type<std::uint32_t, tag::string32>;
|
||||
using string_types = std::tuple<string8, string16, string32>;
|
||||
|
||||
using binary8 = type<std::uint8_t, tag::binary8>;
|
||||
using binary16 = type<std::uint16_t, tag::binary16>;
|
||||
using binary32 = type<std::uint32_t, tag::binary32>;
|
||||
using binary_types = std::tuple<binary8, binary16, binary32>;
|
||||
|
||||
using extension8 = type<std::uint8_t, tag::extension8>;
|
||||
using extension16 = type<std::uint16_t, tag::extension16>;
|
||||
using extension32 = type<std::uint32_t, tag::extension32>;
|
||||
using extension_types = std::tuple<extension8, extension16, extension32>;
|
||||
|
||||
using array16 = type<std::uint16_t, tag::array16>;
|
||||
using array32 = type<std::uint32_t, tag::array32>;
|
||||
using array_types = std::tuple<array16, array32>;
|
||||
|
||||
using object16 = type<std::uint16_t, tag::object16>;
|
||||
using object32 = type<std::uint32_t, tag::object32>;
|
||||
using object_types = std::tuple<object16, object32>;
|
||||
|
||||
template<typename T>
|
||||
static expect<T> from_bytes(epee::byte_slice&& source);
|
||||
|
||||
template<typename T>
|
||||
static epee::byte_slice to_bytes(const T& source);
|
||||
};
|
||||
}
|
||||
|
71
src/wire/msgpack/error.cpp
Normal file
71
src/wire/msgpack/error.cpp
Normal file
|
@ -0,0 +1,71 @@
|
|||
// Copyright (c) 2023, The Monero Project
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "error.h"
|
||||
|
||||
namespace wire
|
||||
{
|
||||
namespace error
|
||||
{
|
||||
const char* get_string(const msgpack value) noexcept
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
default:
|
||||
break;
|
||||
case msgpack::incomplete:
|
||||
return "Incomplete msgpack tree structure";
|
||||
case msgpack::integer_encoding:
|
||||
return "Unable to encode integer in msgpack";
|
||||
case msgpack::invalid:
|
||||
return "Invalid msgpack encoding";
|
||||
case msgpack::not_enough_bytes:
|
||||
return "Expected more bytes in the msgpack stream";
|
||||
}
|
||||
|
||||
return "Unknown msgpack error";
|
||||
}
|
||||
|
||||
const std::error_category& msgpack_category() noexcept
|
||||
{
|
||||
struct category final : std::error_category
|
||||
{
|
||||
virtual const char* name() const noexcept override final
|
||||
{
|
||||
return "wire::error::msgpack_category()";
|
||||
}
|
||||
|
||||
virtual std::string message(int value) const override final
|
||||
{
|
||||
return get_string(msgpack(value));
|
||||
}
|
||||
};
|
||||
static const category instance{};
|
||||
return instance;
|
||||
}
|
||||
} // error
|
||||
} // wire
|
59
src/wire/msgpack/error.h
Normal file
59
src/wire/msgpack/error.h
Normal file
|
@ -0,0 +1,59 @@
|
|||
// Copyright (c) 2023, The Monero Project
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <system_error>
|
||||
|
||||
namespace wire
|
||||
{
|
||||
namespace error
|
||||
{
|
||||
//! Type wrapper to "grab" rapidjson errors
|
||||
enum class msgpack : int
|
||||
{
|
||||
success = 0, // required for `expected<T>`
|
||||
incomplete,
|
||||
integer_encoding,
|
||||
invalid,
|
||||
not_enough_bytes
|
||||
};
|
||||
|
||||
//! \return Static string describing error `value`.
|
||||
const char* get_string(msgpack value) noexcept;
|
||||
|
||||
//! \return Category for msgpack generated errors.
|
||||
const std::error_category& msgpack_category() noexcept;
|
||||
|
||||
//! \return Error code with `value` and `rapidjson_category()`.
|
||||
inline std::error_code make_error_code(msgpack value) noexcept
|
||||
{
|
||||
return std::error_code{int(value), msgpack_category()};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
45
src/wire/msgpack/fwd.h
Normal file
45
src/wire/msgpack/fwd.h
Normal file
|
@ -0,0 +1,45 @@
|
|||
// Copyright (c) 2023, The Monero Project
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#define WIRE_MSGPACK_DECLARE_ENUM(type) \
|
||||
const char* get_string(type) noexcept; \
|
||||
void read_bytes(::wire::msgpack_reader&, type&); \
|
||||
void write_bytes(:wire::msgpack_writer&, type)
|
||||
|
||||
#define WIRE_MSGPACK_DECLARE_OBJECT(type) \
|
||||
void read_bytes(::wire::msgpack_reader&, type&); \
|
||||
void write_bytes(::wire::msgpack_writer&, const type&)
|
||||
|
||||
namespace wire
|
||||
{
|
||||
struct msgpack;
|
||||
class msgpack_reader;
|
||||
class msgpack_writer;
|
||||
}
|
||||
|
517
src/wire/msgpack/read.cpp
Normal file
517
src/wire/msgpack/read.cpp
Normal file
|
@ -0,0 +1,517 @@
|
|||
// Copyright (c) 2023, The Monero Project
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "read.h"
|
||||
|
||||
#include <boost/endian/buffers.hpp>
|
||||
#include <boost/fusion/include/any.hpp>
|
||||
#include <boost/fusion/include/std_tuple.hpp>
|
||||
#include <boost/numeric/conversion/cast.hpp>
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
|
||||
#include "wire/error.h"
|
||||
#include "wire/msgpack/error.h"
|
||||
|
||||
// Expands to every possible fixed string tag value
|
||||
#define MLWS_FIXED_STRING_TAGS() \
|
||||
case wire::msgpack::tag(0xa0): case wire::msgpack::tag(0xa1): \
|
||||
case wire::msgpack::tag(0xa2): case wire::msgpack::tag(0xa3): \
|
||||
case wire::msgpack::tag(0xa4): case wire::msgpack::tag(0xa5): \
|
||||
case wire::msgpack::tag(0xa6): case wire::msgpack::tag(0xa7): \
|
||||
case wire::msgpack::tag(0xa8): case wire::msgpack::tag(0xa9): \
|
||||
case wire::msgpack::tag(0xaa): case wire::msgpack::tag(0xab): \
|
||||
case wire::msgpack::tag(0xac): case wire::msgpack::tag(0xad): \
|
||||
case wire::msgpack::tag(0xae): case wire::msgpack::tag(0xaf): \
|
||||
case wire::msgpack::tag(0xb0): case wire::msgpack::tag(0xb1): \
|
||||
case wire::msgpack::tag(0xb2): case wire::msgpack::tag(0xb3): \
|
||||
case wire::msgpack::tag(0xb4): case wire::msgpack::tag(0xb5): \
|
||||
case wire::msgpack::tag(0xb6): case wire::msgpack::tag(0xb7): \
|
||||
case wire::msgpack::tag(0xb8): case wire::msgpack::tag(0xb9): \
|
||||
case wire::msgpack::tag(0xba): case wire::msgpack::tag(0xbb): \
|
||||
case wire::msgpack::tag(0xbc): case wire::msgpack::tag(0xbd): \
|
||||
case wire::msgpack::tag(0xbe): case wire::msgpack::tag(0xbf):
|
||||
|
||||
namespace
|
||||
{
|
||||
template<typename T>
|
||||
using limits = std::numeric_limits<T>;
|
||||
|
||||
//! \return True iif `value` matches a tag in `T` tuple.
|
||||
template<typename T>
|
||||
bool matches(const wire::msgpack::tag tag)
|
||||
{
|
||||
const auto matched_type = [tag] (const auto type)
|
||||
{
|
||||
return type.Tag() == tag;
|
||||
};
|
||||
// NOTE: This is slower than a switch but more flexible/reusable
|
||||
return boost::fusion::any(T{}, matched_type);
|
||||
}
|
||||
|
||||
//! \return Integer `T` encoded as big endian in `source`.
|
||||
template<typename T>
|
||||
T read_endian(epee::byte_slice& source)
|
||||
{
|
||||
static_assert(std::is_integral<T>::value, "must be integral type");
|
||||
static constexpr const std::size_t bits = 8 * sizeof(T);
|
||||
using buffer_type =
|
||||
boost::endian::endian_buffer<boost::endian::order::big, T, bits>;
|
||||
|
||||
buffer_type buffer;
|
||||
static_assert(sizeof(buffer) == sizeof(T), "unexpected buffer size");
|
||||
if (source.size() < sizeof(buffer))
|
||||
WIRE_DLOG_THROW_(wire::error::msgpack::not_enough_bytes);
|
||||
std::memcpy(std::addressof(buffer), source.data(), sizeof(buffer));
|
||||
source.remove_prefix(sizeof(buffer));
|
||||
return buffer.value();
|
||||
}
|
||||
|
||||
//! \return Integer `T` encoded as big endian in `source`.
|
||||
template<typename T, wire::msgpack::tag U>
|
||||
T read_endian(epee::byte_slice& source, const wire::msgpack::type<T, U>)
|
||||
{ return read_endian<T>(source); }
|
||||
|
||||
//! \return Integer `T` whose encoding is specified by tag `next`
|
||||
template<typename T>
|
||||
T read_integer(epee::byte_slice& source, const wire::msgpack::tag next)
|
||||
{
|
||||
try
|
||||
{
|
||||
// msgpack::integer_types
|
||||
switch (next)
|
||||
{
|
||||
default:
|
||||
break;
|
||||
case wire::msgpack::tag::int8:
|
||||
return boost::numeric_cast<T>(read_endian<std::int8_t>(source));
|
||||
case wire::msgpack::tag::uint8:
|
||||
return boost::numeric_cast<T>(read_endian<std::uint8_t>(source));
|
||||
case wire::msgpack::tag::int16:
|
||||
return boost::numeric_cast<T>(read_endian<std::int16_t>(source));
|
||||
case wire::msgpack::tag::uint16:
|
||||
return boost::numeric_cast<T>(read_endian<std::uint16_t>(source));
|
||||
case wire::msgpack::tag::int32:
|
||||
return boost::numeric_cast<T>(read_endian<std::int32_t>(source));
|
||||
case wire::msgpack::tag::uint32:
|
||||
return boost::numeric_cast<T>(read_endian<std::uint32_t>(source));
|
||||
case wire::msgpack::tag::int64:
|
||||
return boost::numeric_cast<T>(read_endian<std::int64_t>(source));
|
||||
case wire::msgpack::tag::uint64:
|
||||
return boost::numeric_cast<T>(read_endian<std::uint64_t>(source));
|
||||
}
|
||||
}
|
||||
catch (const boost::numeric::positive_overflow&)
|
||||
{ WIRE_DLOG_THROW_(wire::error::schema::smaller_integer); }
|
||||
catch (const boost::numeric::negative_overflow&)
|
||||
{ WIRE_DLOG_THROW_(wire::error::schema::larger_integer); }
|
||||
|
||||
WIRE_DLOG_THROW_(wire::error::schema::integer);
|
||||
}
|
||||
|
||||
epee::byte_slice read_raw(epee::byte_slice& source, const std::size_t bytes)
|
||||
{
|
||||
if (source.size() < bytes)
|
||||
WIRE_DLOG_THROW_(wire::error::msgpack::not_enough_bytes);
|
||||
return source.take_slice(bytes);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
epee::byte_slice read_raw(epee::byte_slice& source)
|
||||
{
|
||||
return read_raw(source, wire::integer::cast_unsigned<std::size_t>(read_endian<T>(source)));
|
||||
}
|
||||
|
||||
epee::byte_slice read_string(epee::byte_slice& source, const wire::msgpack::tag next)
|
||||
{
|
||||
switch (next)
|
||||
{
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wswitch"
|
||||
MLWS_FIXED_STRING_TAGS()
|
||||
return read_raw(source, wire::msgpack::ftag_string::extract(next));
|
||||
#pragma GCC diagnostic pop
|
||||
case wire::msgpack::tag::string8:
|
||||
return read_raw<std::uint8_t>(source);
|
||||
case wire::msgpack::tag::string16:
|
||||
return read_raw<std::uint16_t>(source);
|
||||
case wire::msgpack::tag::string32:
|
||||
return read_raw<std::uint32_t>(source);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
WIRE_DLOG_THROW_(wire::error::schema::string);
|
||||
}
|
||||
|
||||
//! \return Binary blob encoded message
|
||||
epee::byte_slice read_binary(epee::byte_slice& source, const wire::msgpack::tag next)
|
||||
{
|
||||
switch (next)
|
||||
{
|
||||
case wire::msgpack::tag::binary8:
|
||||
return read_raw<std::uint8_t>(source);
|
||||
case wire::msgpack::tag::binary16:
|
||||
return read_raw<std::uint16_t>(source);
|
||||
case wire::msgpack::tag::binary32:
|
||||
return read_raw<std::uint32_t>(source);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
WIRE_DLOG_THROW_(wire::error::schema::string);
|
||||
}
|
||||
}
|
||||
|
||||
namespace wire
|
||||
{
|
||||
void msgpack_reader::throw_logic_error()
|
||||
{
|
||||
throw std::logic_error{"Bug in msgpack_reader usage"};
|
||||
}
|
||||
|
||||
void msgpack_reader::skip_value()
|
||||
{
|
||||
assert(remaining_);
|
||||
if (limits<std::size_t>::max() == remaining_)
|
||||
throw std::runtime_error{"msgpack_reader exceeded tree tracking"};
|
||||
|
||||
const std::size_t initial = remaining_;
|
||||
do
|
||||
{
|
||||
const std::size_t size = source_.size();
|
||||
const msgpack::tag next = peek_tag();
|
||||
switch (next)
|
||||
{
|
||||
default:
|
||||
break;
|
||||
case msgpack::tag::nil:
|
||||
case msgpack::tag::unused:
|
||||
case msgpack::tag::False:
|
||||
case msgpack::tag::True:
|
||||
source_.remove_prefix(1);
|
||||
break;
|
||||
case msgpack::tag::binary8:
|
||||
case msgpack::tag::binary16:
|
||||
case msgpack::tag::binary32:
|
||||
source_.remove_prefix(1);
|
||||
read_binary(source_, next);
|
||||
break;
|
||||
case msgpack::tag::extension8:
|
||||
source_.remove_prefix(1);
|
||||
read_raw<std::uint8_t>(source_);
|
||||
source_.remove_prefix(1);
|
||||
break;
|
||||
case msgpack::tag::extension16:
|
||||
source_.remove_prefix(1);
|
||||
read_raw<std::uint16_t>(source_);
|
||||
source_.remove_prefix(1);
|
||||
break;
|
||||
case msgpack::tag::extension32:
|
||||
source_.remove_prefix(1);
|
||||
read_raw<std::uint32_t>(source_);
|
||||
source_.remove_prefix(1);
|
||||
break;
|
||||
case msgpack::tag::int8:
|
||||
case msgpack::tag::uint8:
|
||||
source_.remove_prefix(2);
|
||||
break;
|
||||
case msgpack::tag::int16:
|
||||
case msgpack::tag::uint16:
|
||||
case msgpack::tag::fixed_extension1:
|
||||
source_.remove_prefix(3);
|
||||
break;
|
||||
case msgpack::tag::int32:
|
||||
case msgpack::tag::uint32:
|
||||
case msgpack::tag::float32:
|
||||
source_.remove_prefix(5);
|
||||
break;
|
||||
case msgpack::tag::int64:
|
||||
case msgpack::tag::uint64:
|
||||
case msgpack::tag::float64:
|
||||
source_.remove_prefix(9);
|
||||
break;
|
||||
case msgpack::tag::fixed_extension2:
|
||||
source_.remove_prefix(4);
|
||||
break;
|
||||
case msgpack::tag::fixed_extension4:
|
||||
source_.remove_prefix(6);
|
||||
break;
|
||||
case msgpack::tag::fixed_extension8:
|
||||
source_.remove_prefix(10);
|
||||
break;
|
||||
case msgpack::tag::fixed_extension16:
|
||||
source_.remove_prefix(18);
|
||||
break;
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wswitch"
|
||||
MLWS_FIXED_STRING_TAGS()
|
||||
case msgpack::tag::string8:
|
||||
case msgpack::tag::string16:
|
||||
case msgpack::tag::string32:
|
||||
source_.remove_prefix(1);
|
||||
read_string(source_, next);
|
||||
break;
|
||||
case msgpack::tag(0x90): case msgpack::tag(0x91): case msgpack::tag(0x92):
|
||||
case msgpack::tag(0x93): case msgpack::tag(0x94): case msgpack::tag(0x95):
|
||||
case msgpack::tag(0x96): case msgpack::tag(0x97): case msgpack::tag(0x98):
|
||||
case msgpack::tag(0x99): case msgpack::tag(0x9a): case msgpack::tag(0x9b):
|
||||
case msgpack::tag(0x9c): case msgpack::tag(0x9d): case msgpack::tag(0x9e):
|
||||
case msgpack::tag(0x9f):
|
||||
case msgpack::tag::array16:
|
||||
case msgpack::tag::array32:
|
||||
start_array();
|
||||
break;
|
||||
case msgpack::tag(0x80): case msgpack::tag(0x81): case msgpack::tag(0x82):
|
||||
case msgpack::tag(0x83): case msgpack::tag(0x84): case msgpack::tag(0x85):
|
||||
case msgpack::tag(0x86): case msgpack::tag(0x87): case msgpack::tag(0x88):
|
||||
case msgpack::tag(0x89): case msgpack::tag(0x8a): case msgpack::tag(0x8b):
|
||||
case msgpack::tag(0x8c): case msgpack::tag(0x8d): case msgpack::tag(0x8e):
|
||||
case msgpack::tag(0x8f):
|
||||
case msgpack::tag::object16:
|
||||
case msgpack::tag::object32:
|
||||
start_object();
|
||||
break;
|
||||
#pragma GCC diagnostic pop
|
||||
};
|
||||
|
||||
if (size == source_.size())
|
||||
{
|
||||
if (!msgpack::ftag_unsigned::matches(next) && !msgpack::ftag_signed::matches(next))
|
||||
WIRE_DLOG_THROW_(error::msgpack::invalid);
|
||||
source_.remove_prefix(1);
|
||||
}
|
||||
update_remaining();
|
||||
} while (initial <= remaining_);
|
||||
}
|
||||
|
||||
msgpack::tag msgpack_reader::peek_tag()
|
||||
{
|
||||
if (source_.empty())
|
||||
WIRE_DLOG_THROW_(error::msgpack::not_enough_bytes);
|
||||
return msgpack::tag(*source_.data());
|
||||
}
|
||||
|
||||
msgpack::tag msgpack_reader::get_tag()
|
||||
{
|
||||
const msgpack::tag next = peek_tag();
|
||||
source_.remove_prefix(1);
|
||||
return next;
|
||||
}
|
||||
|
||||
std::intmax_t msgpack_reader::do_integer(const msgpack::tag next)
|
||||
{
|
||||
if (msgpack::ftag_signed::matches(next))
|
||||
return *reinterpret_cast<const std::int8_t*>(std::addressof(next)); // special case
|
||||
return read_integer<std::intmax_t>(source_, next);
|
||||
}
|
||||
|
||||
std::uintmax_t msgpack_reader::do_unsigned_integer(const msgpack::tag next)
|
||||
{
|
||||
return read_integer<std::uintmax_t>(source_, next);
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
std::size_t msgpack_reader::read_count(const error::schema expected)
|
||||
{
|
||||
const msgpack::tag next = get_tag();
|
||||
if (T::matches(next))
|
||||
return T::extract(next);
|
||||
|
||||
std::size_t out = 0;
|
||||
const auto matched_type = [this, &out, next](const auto type)
|
||||
{
|
||||
if (type.Tag() == next)
|
||||
{
|
||||
out = integer::cast_unsigned<std::size_t>(read_endian(source_, type));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
if (!boost::fusion::any(U{}, matched_type))
|
||||
WIRE_DLOG_THROW_(expected);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
void msgpack_reader::check_complete() const
|
||||
{
|
||||
if (remaining_)
|
||||
WIRE_DLOG_THROW_(error::msgpack::incomplete);
|
||||
}
|
||||
|
||||
bool msgpack_reader::boolean()
|
||||
{
|
||||
update_remaining();
|
||||
switch (get_tag())
|
||||
{
|
||||
case msgpack::tag::True:
|
||||
return true;
|
||||
case msgpack::tag::False:
|
||||
return false;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
WIRE_DLOG_THROW_(error::schema::boolean);
|
||||
}
|
||||
|
||||
double msgpack_reader::real()
|
||||
{
|
||||
update_remaining();
|
||||
|
||||
const auto read_float = [this](auto value)
|
||||
{
|
||||
if (source_.size() < sizeof(value))
|
||||
WIRE_DLOG_THROW_(error::msgpack::not_enough_bytes);
|
||||
std::memcpy(std::addressof(value), source_.data(), sizeof(value));
|
||||
source_.remove_prefix(sizeof(value));
|
||||
return value;
|
||||
};
|
||||
|
||||
switch (get_tag())
|
||||
{
|
||||
case msgpack::tag::float32:
|
||||
return read_float(float(0));
|
||||
case msgpack::tag::float64:
|
||||
return read_float(double(0));
|
||||
default:
|
||||
break;
|
||||
}
|
||||
WIRE_DLOG_THROW_(error::schema::number);
|
||||
}
|
||||
|
||||
std::string msgpack_reader::string()
|
||||
{
|
||||
update_remaining();
|
||||
const epee::byte_slice bytes = read_string(source_, get_tag());
|
||||
return std::string{reinterpret_cast<const char*>(bytes.data()), bytes.size()};
|
||||
}
|
||||
|
||||
std::vector<std::uint8_t> msgpack_reader::binary()
|
||||
{
|
||||
update_remaining();
|
||||
const epee::byte_slice bytes = read_binary(source_, get_tag());
|
||||
return std::vector<std::uint8_t>{bytes.begin(), bytes.end()};
|
||||
}
|
||||
|
||||
void msgpack_reader::binary(epee::span<std::uint8_t> dest)
|
||||
{
|
||||
update_remaining();
|
||||
const epee::byte_slice bytes = read_binary(source_, get_tag());
|
||||
if (dest.size() != bytes.size())
|
||||
WIRE_DLOG_THROW(error::schema::fixed_binary, "of size " << dest.size() << " but got " << bytes.size());
|
||||
std::memcpy(dest.data(), bytes.data(), dest.size());
|
||||
}
|
||||
|
||||
std::size_t msgpack_reader::enumeration(const epee::span<char const* const> enums)
|
||||
{
|
||||
const std::uintmax_t value = unsigned_integer();
|
||||
if (enums.size() < value)
|
||||
WIRE_DLOG_THROW(error::schema::enumeration, value << " is not a valid enum");
|
||||
return std::size_t(value);
|
||||
}
|
||||
|
||||
std::size_t msgpack_reader::start_array()
|
||||
{
|
||||
const std::size_t upcoming =
|
||||
read_count<msgpack::ftag_array, msgpack::array_types>(error::schema::array);
|
||||
if (limits<std::size_t>::max() - remaining_ < upcoming)
|
||||
throw std::runtime_error{"Exceeded max tree tracking for msgpack_reader"};
|
||||
remaining_ += upcoming;
|
||||
return upcoming;
|
||||
}
|
||||
|
||||
bool msgpack_reader::is_array_end(const std::size_t count)
|
||||
{
|
||||
if (count)
|
||||
return false;
|
||||
update_remaining();
|
||||
return true;
|
||||
}
|
||||
|
||||
std::size_t msgpack_reader::start_object()
|
||||
{
|
||||
const std::size_t upcoming =
|
||||
read_count<msgpack::ftag_object, msgpack::object_types>(error::schema::object);
|
||||
if (limits<std::size_t>::max() / 2 < upcoming)
|
||||
throw std::runtime_error{"Exceeded max object tracking for msgpack_reader"};
|
||||
if (limits<std::size_t>::max() - remaining_ < upcoming * 2)
|
||||
throw std::runtime_error{"Exceeded msgpack_reader:: tree tracking"};
|
||||
remaining_ += upcoming * 2;
|
||||
return upcoming;
|
||||
}
|
||||
|
||||
bool msgpack_reader::key(const epee::span<const key_map> map, std::size_t& state, std::size_t& index)
|
||||
{
|
||||
index = map.size();
|
||||
for ( ;state; --state)
|
||||
{
|
||||
update_remaining(); // for key
|
||||
const msgpack::tag next = get_tag();
|
||||
const bool single = msgpack::ftag_unsigned::matches(next);
|
||||
if (single || matches<msgpack::unsigned_types>(next))
|
||||
{
|
||||
unsigned key = std::uint8_t(next);
|
||||
if (!single)
|
||||
key = read_integer<unsigned>(source_, next);
|
||||
for (const key_map& elem : map)
|
||||
{
|
||||
if (elem.id == key)
|
||||
{
|
||||
index = std::addressof(elem) - map.begin();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (msgpack::ftag_string::matches(next) || matches<msgpack::string_types>(next))
|
||||
{
|
||||
const epee::byte_slice key = read_string(source_, next);
|
||||
for (const key_map& elem : map)
|
||||
{
|
||||
const boost::string_ref elem_{elem.name};
|
||||
if (key.size() == elem_.size() && std::memcmp(key.data(), elem_.data(), key.size()) == 0)
|
||||
{
|
||||
index = std::addressof(elem) - map.begin();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
WIRE_DLOG_THROW(error::schema::invalid_key, "Invalid key type");
|
||||
|
||||
if (index < map.size())
|
||||
{
|
||||
--state;
|
||||
return true;
|
||||
}
|
||||
skip_value();
|
||||
} // until state == 0
|
||||
update_remaining(); // for end of object
|
||||
return false;
|
||||
}
|
||||
}
|
164
src/wire/msgpack/read.h
Normal file
164
src/wire/msgpack/read.h
Normal file
|
@ -0,0 +1,164 @@
|
|||
// Copyright (c) 2023, The Monero Project
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <boost/utility/string_ref.hpp>
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "wire/field.h"
|
||||
#include "wire/msgpack/base.h"
|
||||
#include "wire/read.h"
|
||||
#include "wire/traits.h"
|
||||
|
||||
namespace wire
|
||||
{
|
||||
//! Reads MSGPACK tokens one-at-a-time for DOMless parsing
|
||||
class msgpack_reader : public reader
|
||||
{
|
||||
epee::byte_slice source_;
|
||||
std::size_t remaining_; //!< Expected number of elements remaining
|
||||
|
||||
//! \throw std::logic_error
|
||||
[[noreturn]] void throw_logic_error();
|
||||
//! Decrement remaining_ if not zero, \throw std::logic_error when `remaining_ == 0`.
|
||||
void update_remaining()
|
||||
{
|
||||
if (remaining_)
|
||||
--remaining_;
|
||||
else
|
||||
throw_logic_error();
|
||||
}
|
||||
|
||||
//! Skips next value. \throw wire::exception if invalid JSON syntax.
|
||||
void skip_value();
|
||||
|
||||
//! \return Next tag but leave `source_` untouched.
|
||||
msgpack::tag peek_tag();
|
||||
//! \return Next tag and remove first byte from `source_`.
|
||||
msgpack::tag get_tag();
|
||||
|
||||
//! \return Integer from `soure_` where positive fixed tag has been checked.
|
||||
std::intmax_t do_integer(msgpack::tag);
|
||||
//! \return Integer from `source_` where fixed tag has been checked.
|
||||
std::uintmax_t do_unsigned_integer(msgpack::tag);
|
||||
|
||||
//! \return Number of items determined by `T` fixed tag and `U` tuple of tags.
|
||||
template<typename T, typename U>
|
||||
std::size_t read_count(error::schema);
|
||||
|
||||
public:
|
||||
explicit msgpack_reader(epee::byte_slice&& source)
|
||||
: reader(), source_(std::move(source)), remaining_(1)
|
||||
{}
|
||||
|
||||
//! \throw wire::exception if JSON parsing is incomplete.
|
||||
void check_complete() const override final;
|
||||
|
||||
//! \throw wire::exception if next token not a boolean.
|
||||
bool boolean() override final;
|
||||
|
||||
//! \throw wire::expception if next token not an integer.
|
||||
std::intmax_t integer() override final
|
||||
{
|
||||
update_remaining();
|
||||
const msgpack::tag next = get_tag();
|
||||
if (std::uint8_t(next) <= msgpack::ftag_unsigned::max())
|
||||
return std::uint8_t(next);
|
||||
return do_integer(next);
|
||||
}
|
||||
|
||||
//! \throw wire::exception if next token not an unsigned integer.
|
||||
std::uintmax_t unsigned_integer() override final
|
||||
{
|
||||
update_remaining();
|
||||
const msgpack::tag next = get_tag();
|
||||
if (std::uint8_t(next) <= msgpack::ftag_unsigned::max())
|
||||
return std::uint8_t(next);
|
||||
return do_unsigned_integer(next);
|
||||
}
|
||||
|
||||
//! \throw wire::exception if next token not a valid real number
|
||||
double real() override final;
|
||||
|
||||
//! \throw wire::exception if next token not a string
|
||||
std::string string() override final;
|
||||
|
||||
//! \throw wire::exception if next token cannot be read as hex
|
||||
std::vector<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;
|
||||
|
||||
//! \return true when `count == 0`.
|
||||
bool is_array_end(const std::size_t count) override final;
|
||||
|
||||
|
||||
//! \throw wire::exception if next token not `{`.
|
||||
std::size_t start_object() override final;
|
||||
|
||||
/*! \throw wire::exception if next token not key or `}`.
|
||||
\param[out] index of key match within `map`.
|
||||
\return True if another value to read. */
|
||||
bool key(epee::span<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> msgpack::from_bytes(epee::byte_slice&& bytes)
|
||||
{
|
||||
msgpack_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(msgpack_reader& source, T& dest)
|
||||
{
|
||||
wire_read::array(source, dest);
|
||||
}
|
||||
|
||||
template<typename... T>
|
||||
inline void object(msgpack_reader& source, T... fields)
|
||||
{
|
||||
wire_read::object(source, wire_read::tracker<T>{std::move(fields)}...);
|
||||
}
|
||||
} // wire
|
219
src/wire/msgpack/write.cpp
Normal file
219
src/wire/msgpack/write.cpp
Normal file
|
@ -0,0 +1,219 @@
|
|||
// Copyright (c) 2023, The Monero Project
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "write.h"
|
||||
|
||||
#include <boost/endian/buffers.hpp>
|
||||
#include <boost/fusion/include/any.hpp>
|
||||
#include <boost/fusion/include/std_tuple.hpp>
|
||||
#include <cassert>
|
||||
#include <limits>
|
||||
#include <ostream>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
|
||||
#include "wire/error.h"
|
||||
#include "wire/msgpack/error.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
template<typename T>
|
||||
using limits = std::numeric_limits<T>;
|
||||
|
||||
constexpr const unsigned flush_threshold = 100;
|
||||
constexpr const unsigned max_buffer = 4096;
|
||||
|
||||
void write_tag(epee::byte_stream& bytes, const wire::msgpack::tag value)
|
||||
{
|
||||
bytes.put(std::uint8_t(value));
|
||||
}
|
||||
|
||||
template<typename T, typename U, wire::msgpack::tag tag>
|
||||
void write_endian(epee::byte_stream& bytes, const T value, const wire::msgpack::type<U, tag> type)
|
||||
{
|
||||
static_assert(std::is_integral<T>::value, "input not integral");
|
||||
static_assert(std::is_integral<U>::value, "output not integral");
|
||||
|
||||
using in_limits = std::numeric_limits<T>;
|
||||
using out_limits = std::numeric_limits<U>;
|
||||
static_assert(in_limits::is_signed == out_limits::is_signed, "signs must match");
|
||||
|
||||
assert(type.min() <= value);
|
||||
assert(value <= type.max());
|
||||
|
||||
static constexpr const std::size_t bits = 8 * sizeof(U);
|
||||
using buffer_type =
|
||||
boost::endian::endian_buffer<boost::endian::order::big, U, bits>;
|
||||
|
||||
buffer_type buffer(value);
|
||||
write_tag(bytes, type.Tag());
|
||||
bytes.write(buffer.data(), sizeof(buffer));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void write_count(epee::byte_stream& bytes, const std::uintmax_t items)
|
||||
{
|
||||
const auto match_size = [&bytes, items] (const auto type) -> bool
|
||||
{
|
||||
if (type.max() < items)
|
||||
return false;
|
||||
write_endian(bytes, items, type);
|
||||
return true;
|
||||
};
|
||||
if (!boost::fusion::any(T{}, match_size))
|
||||
WIRE_DLOG_THROW_(wire::error::msgpack::integer_encoding);
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
void write_count(epee::byte_stream& bytes, const std::uintmax_t items)
|
||||
{
|
||||
if (items <= T::max())
|
||||
bytes.put(T::tag() | std::uint8_t(items));
|
||||
else
|
||||
write_count<U>(bytes, items);
|
||||
}
|
||||
}
|
||||
|
||||
namespace wire
|
||||
{
|
||||
void msgpack_writer::do_flush(epee::span<const std::uint8_t>)
|
||||
{}
|
||||
|
||||
void msgpack_writer::check_flush()
|
||||
{
|
||||
if (needs_flush_ && (max_buffer < bytes_.size() || bytes_.available() < flush_threshold))
|
||||
flush();
|
||||
}
|
||||
|
||||
void msgpack_writer::do_integer(const std::intmax_t value)
|
||||
{
|
||||
assert(value < 0); // constraint checked in header
|
||||
if (0xe0 < value) // 0xe0 needs to be type `int` to work
|
||||
{
|
||||
bytes_.put(std::uint8_t(value));
|
||||
return;
|
||||
}
|
||||
|
||||
const auto match_size = [this, value] (const auto type) -> bool
|
||||
{
|
||||
if (value < type.min())
|
||||
return false;
|
||||
write_endian(bytes_, value, type);
|
||||
return true;
|
||||
};
|
||||
if (!boost::fusion::any(wire::msgpack::signed_types{}, match_size))
|
||||
WIRE_DLOG_THROW_(wire::error::msgpack::integer_encoding);
|
||||
}
|
||||
|
||||
void msgpack_writer::do_unsigned_integer(const std::uintmax_t value)
|
||||
{
|
||||
const auto match_size = [this, value] (const auto type) -> bool
|
||||
{
|
||||
if (type.max() < value)
|
||||
return false;
|
||||
write_endian(bytes_, value, type);
|
||||
return true;
|
||||
};
|
||||
if (!boost::fusion::any(wire::msgpack::unsigned_types{}, match_size))
|
||||
WIRE_DLOG_THROW_(wire::error::msgpack::integer_encoding);
|
||||
}
|
||||
|
||||
void msgpack_writer::check_complete()
|
||||
{
|
||||
if (expected_)
|
||||
throw std::logic_error{"msgpack_writer::take_msgpack() failed with incomplete tree"};
|
||||
}
|
||||
epee::byte_slice msgpack_writer::take_msgpack()
|
||||
{
|
||||
check_complete();
|
||||
epee::byte_slice out{std::move(bytes_)};
|
||||
bytes_.clear();
|
||||
return out;
|
||||
}
|
||||
|
||||
msgpack_writer::~msgpack_writer() noexcept
|
||||
{}
|
||||
|
||||
void msgpack_writer::real(const double source)
|
||||
{
|
||||
write_tag(bytes_, msgpack::tag::float64);
|
||||
bytes_.write(reinterpret_cast<const char*>(std::addressof(source)), sizeof(source));
|
||||
--expected_;
|
||||
}
|
||||
|
||||
void msgpack_writer::string(const boost::string_ref source)
|
||||
{
|
||||
write_count<msgpack::ftag_string, msgpack::string_types>(bytes_, source.size());
|
||||
bytes_.write(source.data(), source.size());
|
||||
--expected_;
|
||||
}
|
||||
void msgpack_writer::binary(epee::span<const std::uint8_t> source)
|
||||
{
|
||||
write_count<msgpack::binary_types>(bytes_, source.size());
|
||||
bytes_.write(source);
|
||||
--expected_;
|
||||
}
|
||||
|
||||
void msgpack_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"};
|
||||
unsigned_integer(index);
|
||||
}
|
||||
|
||||
void msgpack_writer::start_array(const std::size_t items)
|
||||
{
|
||||
write_count<msgpack::ftag_array, msgpack::array_types>(bytes_, items);
|
||||
expected_ += items;
|
||||
}
|
||||
|
||||
void msgpack_writer::start_object(const std::size_t items)
|
||||
{
|
||||
write_count<msgpack::ftag_object, msgpack::object_types>(bytes_, items);
|
||||
expected_ += items * 2;
|
||||
}
|
||||
void msgpack_writer::key(const boost::string_ref str)
|
||||
{
|
||||
string(str);
|
||||
}
|
||||
void msgpack_writer::key(const std::uintmax_t id)
|
||||
{
|
||||
unsigned_integer(id);
|
||||
}
|
||||
void msgpack_writer::key(const unsigned id, boost::string_ref str)
|
||||
{
|
||||
if (integer_keys_)
|
||||
key(id);
|
||||
else
|
||||
key(str);
|
||||
}
|
||||
|
||||
void msgpack_stream_writer::do_flush(epee::span<const std::uint8_t> bytes)
|
||||
{
|
||||
dest.write(reinterpret_cast<const char*>(bytes.data()), bytes.size());
|
||||
}
|
||||
}
|
225
src/wire/msgpack/write.h
Normal file
225
src/wire/msgpack/write.h
Normal file
|
@ -0,0 +1,225 @@
|
|||
// Copyright (c) 2023, The Monero Project
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <boost/utility/string_ref.hpp>
|
||||
#include <cstdint>
|
||||
#include <iosfwd>
|
||||
#include <limits>
|
||||
|
||||
#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/msgpack/base.h"
|
||||
#include "wire/traits.h"
|
||||
#include "wire/write.h"
|
||||
|
||||
namespace wire
|
||||
{
|
||||
//! Writes MSGPACK tokens one-at-a-time for DOMless output.
|
||||
class msgpack_writer : public writer
|
||||
{
|
||||
epee::byte_stream bytes_;
|
||||
std::size_t expected_; //! Tracks number of expected elements remaining
|
||||
const bool integer_keys_;
|
||||
bool needs_flush_;
|
||||
|
||||
//! Provided data currently in `bytes_`.
|
||||
virtual void do_flush(epee::span<const uint8_t>);
|
||||
|
||||
//! Flush written bytes to `do_flush(...)` if configured
|
||||
void check_flush();
|
||||
|
||||
void do_integer(std::intmax_t);
|
||||
void do_unsigned_integer(std::uintmax_t);
|
||||
|
||||
template<typename T>
|
||||
void integer_t(const T value)
|
||||
{
|
||||
if (0 <= value)
|
||||
{
|
||||
if (value <= msgpack::ftag_unsigned::max())
|
||||
bytes_.put(std::uint8_t(value));
|
||||
else // if multibyte
|
||||
do_unsigned_integer(std::uintmax_t(value));
|
||||
}
|
||||
else // if negative
|
||||
do_integer(value);
|
||||
--expected_;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void unsigned_integer_t(const T value)
|
||||
{
|
||||
if (value <= msgpack::ftag_unsigned::max())
|
||||
bytes_.put(std::uint8_t(value));
|
||||
else // if multibyte
|
||||
do_unsigned_integer(value);
|
||||
--expected_;
|
||||
}
|
||||
|
||||
protected:
|
||||
msgpack_writer(bool integer_keys, bool needs_flush)
|
||||
: writer(), bytes_(), expected_(1), integer_keys_(integer_keys), needs_flush_(needs_flush)
|
||||
{}
|
||||
|
||||
//! \throw std::logic_error if tree was not completed
|
||||
void check_complete();
|
||||
|
||||
//! \throw std::logic_error if incomplete msgpack tree. \return msgpack bytes
|
||||
epee::byte_slice take_msgpack();
|
||||
|
||||
//! Flush bytes in local buffer to `do_flush(...)`
|
||||
void flush()
|
||||
{
|
||||
do_flush({bytes_.data(), bytes_.size()});
|
||||
bytes_.clear();
|
||||
}
|
||||
|
||||
public:
|
||||
msgpack_writer(const msgpack_writer&) = delete;
|
||||
virtual ~msgpack_writer() noexcept;
|
||||
msgpack_writer& operator=(const msgpack_writer&) = delete;
|
||||
|
||||
void boolean(const bool value) override final
|
||||
{
|
||||
if (value)
|
||||
bytes_.put(std::uint8_t(msgpack::tag::True));
|
||||
else
|
||||
bytes_.put(std::uint8_t(msgpack::tag::False));
|
||||
--expected_;
|
||||
}
|
||||
|
||||
void integer(const int value) override final
|
||||
{ integer_t(value); }
|
||||
|
||||
void integer(const std::intmax_t value) override final
|
||||
{ integer_t(value); }
|
||||
|
||||
void unsigned_integer(const unsigned value) override final
|
||||
{ unsigned_integer_t(value); }
|
||||
|
||||
void unsigned_integer(const std::uintmax_t value) override final
|
||||
{ unsigned_integer_t(value); }
|
||||
|
||||
void real(double) override final;
|
||||
|
||||
//! \throw wire::exception if `source.size()` exceeds 2^32-1
|
||||
void string(boost::string_ref source) override final;
|
||||
//! \throw wire::exception if `source.size()` exceeds 2^32-1
|
||||
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;
|
||||
|
||||
//! \throw wire::exception if `items` exceeds 2^32-1.
|
||||
void start_array(std::size_t items) override final;
|
||||
void end_array() override final { --expected_; }
|
||||
|
||||
//! \throw wire::exception if `items` exceeds 2^32-1
|
||||
void start_object(std::size_t items) 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 { --expected_; }
|
||||
};
|
||||
|
||||
//! Buffers entire JSON message in memory
|
||||
struct msgpack_slice_writer final : msgpack_writer
|
||||
{
|
||||
explicit msgpack_slice_writer(bool integer_keys = false)
|
||||
: msgpack_writer(integer_keys, false)
|
||||
{}
|
||||
|
||||
//! \throw std::logic_error if incomplete JSON tree \return JSON bytes
|
||||
epee::byte_slice take_bytes()
|
||||
{
|
||||
return msgpack_writer::take_msgpack();
|
||||
}
|
||||
};
|
||||
|
||||
//! Periodically flushes JSON data to `std::ostream`
|
||||
class msgpack_stream_writer final : public msgpack_writer
|
||||
{
|
||||
std::ostream& dest;
|
||||
|
||||
virtual void do_flush(epee::span<const std::uint8_t>) override final;
|
||||
public:
|
||||
explicit msgpack_stream_writer(std::ostream& dest, bool integer_keys = false)
|
||||
: msgpack_writer(integer_keys, 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 msgpack::to_bytes(const T& source)
|
||||
{
|
||||
return wire_write::to_bytes<msgpack_slice_writer>(source);
|
||||
}
|
||||
|
||||
template<typename T, typename F = identity_>
|
||||
inline void array(msgpack_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(msgpack_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(msgpack_writer& dest, const T& source)
|
||||
{
|
||||
wire::array(dest, source);
|
||||
}
|
||||
|
||||
template<typename T, typename F = identity_, typename G = identity_>
|
||||
inline void dynamic_object(msgpack_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(msgpack_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(msgpack_writer& dest, T... fields)
|
||||
{
|
||||
wire_write::object(dest, std::move(fields)...);
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2020, The Monero Project
|
||||
// Copyright (c) 2020-2023, The Monero Project
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
|
@ -299,14 +299,14 @@ namespace wire_read
|
|||
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)
|
||||
template<typename R, typename T, unsigned I>
|
||||
inline void unpack_field(std::size_t, R& source, wire::field_<T, true, I>& 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)
|
||||
template<typename R, typename T, unsigned I>
|
||||
inline void unpack_field(std::size_t, R& source, wire::field_<T, false, I>& dest)
|
||||
{
|
||||
dest.get_value().emplace();
|
||||
read_bytes(source, *dest.get_value());
|
||||
|
@ -322,7 +322,7 @@ namespace wire_read
|
|||
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;
|
||||
map[index].id = head.id();
|
||||
expand_field_map(index + 1, map, tail...);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2020, The Monero Project
|
||||
// Copyright (c) 2020-2023, The Monero Project
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
|
@ -142,13 +142,18 @@ namespace wire_write
|
|||
declared after these functions. */
|
||||
|
||||
template<typename W, typename T>
|
||||
inline epee::byte_slice to_bytes(const T& value)
|
||||
inline epee::byte_slice to_bytes(W&& dest, const T& value)
|
||||
{
|
||||
W dest{};
|
||||
write_bytes(dest, value);
|
||||
return dest.take_bytes();
|
||||
}
|
||||
|
||||
template<typename W, typename T>
|
||||
inline epee::byte_slice to_bytes(const T& value)
|
||||
{
|
||||
return wire_write::to_bytes(W{}, value);
|
||||
}
|
||||
|
||||
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{})
|
||||
{
|
||||
|
@ -162,20 +167,20 @@ namespace wire_write
|
|||
dest.end_array();
|
||||
}
|
||||
|
||||
template<typename W, typename T>
|
||||
inline bool field(W& dest, const wire::field_<T, true> elem)
|
||||
template<typename W, typename T, unsigned I>
|
||||
inline bool field(W& dest, const wire::field_<T, true, I> elem)
|
||||
{
|
||||
dest.key(0, elem.name);
|
||||
dest.key(I, 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)
|
||||
template<typename W, typename T, unsigned I>
|
||||
inline bool field(W& dest, const wire::field_<T, false, I> elem)
|
||||
{
|
||||
if (bool(elem.get_value()))
|
||||
{
|
||||
dest.key(0, elem.name);
|
||||
dest.key(I, elem.name);
|
||||
write_bytes(dest, *elem.get_value());
|
||||
}
|
||||
return true;
|
||||
|
|
|
@ -33,5 +33,5 @@ target_link_libraries(monero-lws-unit-framework)
|
|||
add_subdirectory(wire)
|
||||
|
||||
add_executable(monero-lws-unit main.cpp)
|
||||
target_link_libraries(monero-lws-unit monero-lws-unit-framework monero-lws-unit-wire monero-lws-unit-wire-json)
|
||||
target_link_libraries(monero-lws-unit monero-lws-unit-framework monero-lws-unit-wire monero-lws-unit-wire-json monero-lws-unit-wire-msgpack)
|
||||
add_test(NAME monero-lws-unit COMMAND monero-lws-unit -v)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Copyright (c) 2022, The Monero Project
|
||||
# Copyright (c) 2022-2023, The Monero Project
|
||||
#
|
||||
# All rights reserved.
|
||||
#
|
||||
|
@ -28,6 +28,7 @@
|
|||
|
||||
|
||||
add_subdirectory(json)
|
||||
add_subdirectory(msgpack)
|
||||
|
||||
add_library(monero-lws-unit-wire OBJECT read.write.test.cpp read.test.cpp)
|
||||
target_link_libraries(
|
||||
|
|
37
tests/unit/wire/msgpack/CMakeLists.txt
Normal file
37
tests/unit/wire/msgpack/CMakeLists.txt
Normal file
|
@ -0,0 +1,37 @@
|
|||
# Copyright (c) 2023, The Monero Project
|
||||
#
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification, are
|
||||
# permitted provided that the following conditions are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
# conditions and the following disclaimer.
|
||||
#
|
||||
# 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
# of conditions and the following disclaimer in the documentation and/or other
|
||||
# materials provided with the distribution.
|
||||
#
|
||||
# 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
# used to endorse or promote products derived from this software without specific
|
||||
# prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
# THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
add_library(monero-lws-unit-wire-msgpack OBJECT read.write.test.cpp)
|
||||
target_link_libraries(
|
||||
monero-lws-unit-wire-msgpack
|
||||
monero-lws-unit-framework
|
||||
monero-lws-wire-msgpack
|
||||
monero::libraries
|
||||
)
|
||||
|
183
tests/unit/wire/msgpack/read.write.test.cpp
Normal file
183
tests/unit/wire/msgpack/read.write.test.cpp
Normal file
|
@ -0,0 +1,183 @@
|
|||
// Copyright (c) 2023, The Monero Project
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#include "framework.test.h"
|
||||
|
||||
#include <boost/core/demangle.hpp>
|
||||
#include <boost/range/algorithm/equal.hpp>
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
#include "wire/traits.h"
|
||||
#include "wire/msgpack.h"
|
||||
#include "wire/vector.h"
|
||||
|
||||
#include "wire/base.test.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
constexpr const char basic_string[] = u8"my_string_data";
|
||||
//constexpr const char basic_[] =
|
||||
// u8"{\"utf8\":\"my_string_data\",\"vec\":[0,127],\"data\":\"00ff2211\",\"choice\":true}";
|
||||
constexpr const std::uint8_t basic_msgpack[] = {
|
||||
0x84, 0xa4, 0x75, 0x74, 0x66, 0x38, 0xae, 0x6d, 0x79, 0x5f, 0x73, 0x74,
|
||||
0x72, 0x69, 0x6e, 0x67, 0x5f, 0x64, 0x61, 0x74, 0x61, 0xa3, 0x76, 0x65,
|
||||
0x63, 0x92, 0x00, 0x7f, 0xa4, 0x64, 0x61, 0x74, 0x61, 0xc4, 0x04, 0x00,
|
||||
0xff, 0x22, 0x11, 0xa6, 0x63, 0x68, 0x6f, 0x69, 0x63, 0x65, 0xc3
|
||||
};
|
||||
constexpr const std::uint8_t advanced_msgpack[] = {
|
||||
0x84, 0x00, 0xae, 0x6d, 0x79, 0x5f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67,
|
||||
0x5f, 0x64, 0x61, 0x74, 0x61, 0x01, 0x92, 0x00, 0x7f, 0x02, 0xc4, 0x04,
|
||||
0x00, 0xff, 0x22, 0x11, 0xcc, 0xfe, 0xc3
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct basic_object
|
||||
{
|
||||
std::string utf8;
|
||||
std::vector<T> vec;
|
||||
lws_test::small_blob data;
|
||||
bool choice;
|
||||
};
|
||||
|
||||
template<typename F, typename T>
|
||||
void basic_object_map(F& format, T& self)
|
||||
{
|
||||
wire::object(format,
|
||||
WIRE_FIELD_ID(0, utf8),
|
||||
WIRE_FIELD_ID(1, vec),
|
||||
WIRE_FIELD_ID(2, data),
|
||||
WIRE_FIELD_ID(254, choice)
|
||||
);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void read_bytes(wire::msgpack_reader& source, basic_object<T>& dest)
|
||||
{ basic_object_map(source, dest); }
|
||||
|
||||
template<typename T>
|
||||
void write_bytes(wire::msgpack_writer& dest, const basic_object<T>& source)
|
||||
{ basic_object_map(dest, source); }
|
||||
|
||||
template<typename T>
|
||||
void test_basic_reading(lest::env& lest_env)
|
||||
{
|
||||
SETUP("Basic (string keys) with " + boost::core::demangle(typeid(T).name()) + " integers")
|
||||
{
|
||||
const auto result =
|
||||
wire::msgpack::from_bytes<basic_object<T>>(epee::byte_slice{{basic_msgpack}});
|
||||
EXPECT(result);
|
||||
EXPECT(result->utf8 == basic_string);
|
||||
{
|
||||
const std::vector<T> expected{0, 127};
|
||||
EXPECT(result->vec == expected);
|
||||
}
|
||||
EXPECT(result->data == lws_test::blob_test1);
|
||||
EXPECT(result->choice);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void test_advanced_reading(lest::env& lest_env)
|
||||
{
|
||||
SETUP("Advanced (integer keys) with " + boost::core::demangle(typeid(T).name()) + " integers")
|
||||
{
|
||||
const auto result =
|
||||
wire::msgpack::from_bytes<basic_object<T>>(epee::byte_slice{{advanced_msgpack}});
|
||||
EXPECT(result);
|
||||
EXPECT(result->utf8 == basic_string);
|
||||
{
|
||||
const std::vector<T> expected{0, 127};
|
||||
EXPECT(result->vec == expected);
|
||||
}
|
||||
EXPECT(result->data == lws_test::blob_test1);
|
||||
EXPECT(result->choice);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void test_basic_writing(lest::env& lest_env)
|
||||
{
|
||||
SETUP("Basic (string keys) with " + boost::core::demangle(typeid(T).name()) + " integers")
|
||||
{
|
||||
const basic_object<T> val{basic_string, std::vector<T>{0, 127}, lws_test::blob_test1, true};
|
||||
const auto result = wire::msgpack::to_bytes(val);
|
||||
EXPECT(boost::range::equal(result, epee::byte_slice{{basic_msgpack}}));
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void test_advanced_writing(lest::env& lest_env)
|
||||
{
|
||||
SETUP("Advanced (integer keys) with " + boost::core::demangle(typeid(T).name()) + " integers")
|
||||
{
|
||||
const basic_object<T> val{basic_string, std::vector<T>{0, 127}, lws_test::blob_test1, true};
|
||||
const auto result = wire_write::to_bytes(wire::msgpack_slice_writer{true}, val);
|
||||
EXPECT(boost::range::equal(result, epee::byte_slice{{advanced_msgpack}}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LWS_CASE("wire::msgpack_reader")
|
||||
{
|
||||
test_basic_reading<std::int16_t>(lest_env);
|
||||
test_basic_reading<std::int32_t>(lest_env);
|
||||
test_basic_reading<std::int64_t>(lest_env);
|
||||
test_basic_reading<std::intmax_t>(lest_env);
|
||||
test_basic_reading<std::uint16_t>(lest_env);
|
||||
test_basic_reading<std::uint32_t>(lest_env);
|
||||
test_basic_reading<std::uint64_t>(lest_env);
|
||||
test_basic_reading<std::uintmax_t>(lest_env);
|
||||
|
||||
test_advanced_reading<std::int16_t>(lest_env);
|
||||
test_advanced_reading<std::int32_t>(lest_env);
|
||||
test_advanced_reading<std::int64_t>(lest_env);
|
||||
test_advanced_reading<std::intmax_t>(lest_env);
|
||||
test_advanced_reading<std::uint16_t>(lest_env);
|
||||
test_advanced_reading<std::uint32_t>(lest_env);
|
||||
test_advanced_reading<std::uint64_t>(lest_env);
|
||||
test_advanced_reading<std::uintmax_t>(lest_env);
|
||||
}
|
||||
|
||||
LWS_CASE("wire::msgpack_writer")
|
||||
{
|
||||
test_basic_writing<std::int16_t>(lest_env);
|
||||
test_basic_writing<std::int32_t>(lest_env);
|
||||
test_basic_writing<std::int64_t>(lest_env);
|
||||
test_basic_writing<std::intmax_t>(lest_env);
|
||||
test_basic_writing<std::uint16_t>(lest_env);
|
||||
test_basic_writing<std::uint32_t>(lest_env);
|
||||
test_basic_writing<std::uint64_t>(lest_env);
|
||||
test_basic_writing<std::uintmax_t>(lest_env);
|
||||
|
||||
test_advanced_writing<std::int16_t>(lest_env);
|
||||
test_advanced_writing<std::int32_t>(lest_env);
|
||||
test_advanced_writing<std::int64_t>(lest_env);
|
||||
test_advanced_writing<std::intmax_t>(lest_env);
|
||||
test_advanced_writing<std::uint16_t>(lest_env);
|
||||
test_advanced_writing<std::uint32_t>(lest_env);
|
||||
test_advanced_writing<std::uint64_t>(lest_env);
|
||||
test_advanced_writing<std::uintmax_t>(lest_env);
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2022, The Monero Project
|
||||
// Copyright (c) 2022-2023, The Monero Project
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
|
@ -34,6 +34,7 @@
|
|||
#include <vector>
|
||||
#include "wire.h"
|
||||
#include "wire/json.h"
|
||||
#include "wire/msgpack.h"
|
||||
#include "wire/vector.h"
|
||||
|
||||
#include "wire/base.test.h"
|
||||
|
@ -93,7 +94,7 @@ namespace
|
|||
void fill(complex& self)
|
||||
{
|
||||
self.objects = std::vector<inner>{inner{0, limit<std::uint32_t>::max()}, inner{100, 200}, inner{44444, 83434}};
|
||||
self.ints = std::vector<std::int16_t>{limit<std::int16_t>::min(), limit<std::int16_t>::max(), 0, 31234};
|
||||
self.ints = std::vector<std::int16_t>{limit<std::int16_t>::min(), limit<std::int16_t>::max(), -3, 31234};
|
||||
self.uints = std::vector<std::uint64_t>{0, limit<std::uint64_t>::max(), 34234234, 33};
|
||||
self.blobs = {lws_test::blob_test1, lws_test::blob_test2, lws_test::blob_test3};
|
||||
self.strings = {"string1", "string2", "string3", "string4"};
|
||||
|
@ -113,7 +114,7 @@ namespace
|
|||
EXPECT(self.ints.size() == 4);
|
||||
EXPECT(self.ints.at(0) == limit<std::int16_t>::min());
|
||||
EXPECT(self.ints.at(1) == limit<std::int16_t>::max());
|
||||
EXPECT(self.ints.at(2) == 0);
|
||||
EXPECT(self.ints.at(2) == -3);
|
||||
EXPECT(self.ints.at(3) == 31234);
|
||||
|
||||
EXPECT(self.uints.size() == 4);
|
||||
|
@ -136,7 +137,7 @@ namespace
|
|||
EXPECT(self.choice == true);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
template<typename T, typename U>
|
||||
void run_complex(lest::env& lest_env)
|
||||
{
|
||||
SETUP("Complex test for " + boost::core::demangle(typeid(T).name()))
|
||||
|
@ -148,7 +149,7 @@ namespace
|
|||
const expect<epee::byte_slice> bytes = T::to_bytes(base);
|
||||
EXPECT(bytes);
|
||||
|
||||
const expect<complex> derived = T::template from_bytes<complex>(std::string{bytes->begin(), bytes->end()});
|
||||
const expect<complex> derived = T::template from_bytes<complex>(U{std::string{bytes->begin(), bytes->end()}});
|
||||
EXPECT(derived);
|
||||
verify_initial(lest_env, *derived);
|
||||
}
|
||||
|
@ -159,7 +160,7 @@ namespace
|
|||
const expect<epee::byte_slice> bytes = T::to_bytes(base);
|
||||
EXPECT(bytes);
|
||||
|
||||
const expect<complex> derived = T::template from_bytes<complex>(std::string{bytes->begin(), bytes->end()});
|
||||
const expect<complex> derived = T::template from_bytes<complex>(U{std::string{bytes->begin(), bytes->end()}});
|
||||
EXPECT(derived);
|
||||
verify_filled(lest_env, *derived);
|
||||
}
|
||||
|
@ -180,7 +181,7 @@ namespace
|
|||
WIRE_DEFINE_OBJECT(big, big_map)
|
||||
WIRE_DEFINE_OBJECT(small, small_map)
|
||||
|
||||
template<typename T>
|
||||
template<typename T, typename U>
|
||||
expect<small> round_trip(lest::env& lest_env, std::int64_t value)
|
||||
{
|
||||
expect<small> out = small{0};
|
||||
|
@ -188,43 +189,77 @@ namespace
|
|||
{
|
||||
const expect<epee::byte_slice> bytes = T::template to_bytes(big{value});
|
||||
EXPECT(bytes);
|
||||
out = T::template from_bytes<small>(std::string{bytes->begin(), bytes->end()});
|
||||
out = T::template from_bytes<small>(U{std::string{bytes->begin(), bytes->end()}});
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
template<typename T, typename U>
|
||||
void not_overflow(lest::env& lest_env, std::int64_t value)
|
||||
{
|
||||
const expect<small> result = round_trip<T>(lest_env, value);
|
||||
const expect<small> result = round_trip<T, U>(lest_env, value);
|
||||
EXPECT(result);
|
||||
EXPECT(result->value == value);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
template<typename T, typename U>
|
||||
void overflow(lest::env& lest_env, std::int64_t value, const std::error_code error)
|
||||
{
|
||||
const expect<small> result = round_trip<T>(lest_env, value);
|
||||
const expect<small> result = round_trip<T, U>(lest_env, value);
|
||||
EXPECT(result == error);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
template<typename T, typename U>
|
||||
void run_overflow(lest::env& lest_env)
|
||||
{
|
||||
SETUP("Overflow test for " + boost::core::demangle(typeid(T).name()))
|
||||
{
|
||||
not_overflow<T>(lest_env, limit<std::int32_t>::min());
|
||||
not_overflow<T>(lest_env, 0);
|
||||
not_overflow<T>(lest_env, limit<std::int32_t>::max());
|
||||
not_overflow<T, U>(lest_env, limit<std::int32_t>::min());
|
||||
not_overflow<T, U>(lest_env, 0);
|
||||
not_overflow<T, U>(lest_env, limit<std::int32_t>::max());
|
||||
|
||||
overflow<T>(lest_env, std::int64_t(limit<std::int32_t>::min()) - 1, wire::error::schema::larger_integer);
|
||||
overflow<T>(lest_env, std::int64_t(limit<std::int32_t>::max()) + 1, wire::error::schema::smaller_integer);
|
||||
overflow<T, U>(lest_env, std::int64_t(limit<std::int32_t>::min()) - 1, wire::error::schema::larger_integer);
|
||||
overflow<T, U>(lest_env, std::int64_t(limit<std::int32_t>::max()) + 1, wire::error::schema::smaller_integer);
|
||||
}
|
||||
}
|
||||
|
||||
struct simple { bool choice; };
|
||||
static void read_bytes(wire::reader& source, simple& self)
|
||||
{
|
||||
wire::object(source, WIRE_FIELD(choice));
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
void run_skip(lest::env& lest_env)
|
||||
{
|
||||
complex base{};
|
||||
verify_initial(lest_env, base);
|
||||
fill(base);
|
||||
|
||||
const expect<epee::byte_slice> bytes = T::to_bytes(base);
|
||||
EXPECT(bytes);
|
||||
|
||||
const expect<simple> derived = T::template from_bytes<simple>(U{std::string{bytes->begin(), bytes->end()}});
|
||||
EXPECT(derived);
|
||||
EXPECT(derived->choice);
|
||||
}
|
||||
}
|
||||
|
||||
LWS_CASE("wire::reader and wire::writer")
|
||||
LWS_CASE("wire::reader and wire::writer complex")
|
||||
{
|
||||
run_complex<wire::json>(lest_env);
|
||||
run_overflow<wire::json>(lest_env);
|
||||
run_complex<wire::json, std::string>(lest_env);
|
||||
run_complex<wire::msgpack, epee::byte_slice>(lest_env);
|
||||
}
|
||||
|
||||
LWS_CASE("wire::reader and wire::writer overflow")
|
||||
{
|
||||
run_overflow<wire::json, std::string>(lest_env);
|
||||
run_overflow<wire::msgpack, epee::byte_slice>(lest_env);
|
||||
}
|
||||
|
||||
LWS_CASE("wire::reader and wire::writer skip")
|
||||
{
|
||||
run_skip<wire::json, std::string>(lest_env);
|
||||
run_skip<wire::msgpack, epee::byte_slice>(lest_env);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue