Compare commits
No commits in common. "main" and "mw2" have entirely different histories.
@ -18,7 +18,7 @@ BreakConstructorInitializers: BeforeComma
|
|||||||
BreakInheritanceList: BeforeComma
|
BreakInheritanceList: BeforeComma
|
||||||
ConstructorInitializerIndentWidth: 8
|
ConstructorInitializerIndentWidth: 8
|
||||||
ContinuationIndentWidth: 8
|
ContinuationIndentWidth: 8
|
||||||
#IncludeBlocks: Regroup
|
IncludeBlocks: Regroup
|
||||||
IncludeCategories:
|
IncludeCategories:
|
||||||
- Regex: '^"parselink/'
|
- Regex: '^"parselink/'
|
||||||
Priority: 1
|
Priority: 1
|
||||||
@ -29,8 +29,8 @@ IncludeCategories:
|
|||||||
SortPriority: 2
|
SortPriority: 2
|
||||||
CaseSensitive: false
|
CaseSensitive: false
|
||||||
- Regex: '.*'
|
- Regex: '.*'
|
||||||
Priority: 3
|
Priority: 1
|
||||||
SortPriority: 3
|
SortPriority: 0
|
||||||
IncludeIsMainRegex: '(_test)?$'
|
IncludeIsMainRegex: '(_test)?$'
|
||||||
IndentAccessModifiers: false
|
IndentAccessModifiers: false
|
||||||
IndentCaseLabels: true
|
IndentCaseLabels: true
|
||||||
|
|||||||
@ -1,65 +0,0 @@
|
|||||||
|
|
||||||
cc_library(
|
|
||||||
name = "headers",
|
|
||||||
includes = ["include"],
|
|
||||||
visibility = ["//visibility:private"],
|
|
||||||
copts = ["-DRC_DONT_USE_RTTI"],
|
|
||||||
hdrs = glob([
|
|
||||||
"include/**/*.hpp",
|
|
||||||
"include/**/*.h",
|
|
||||||
]),
|
|
||||||
)
|
|
||||||
|
|
||||||
cc_library(
|
|
||||||
name = "random",
|
|
||||||
deps = [":headers"],
|
|
||||||
visibility = ["//visibility:private"],
|
|
||||||
copts = ["-DRC_DONT_USE_RTTI", "-O3"],
|
|
||||||
srcs = [
|
|
||||||
"src/Random.cpp",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
cc_library(
|
|
||||||
name = "rapidcheck",
|
|
||||||
visibility = ["//visibility:public"],
|
|
||||||
deps = [":headers", ":random"],
|
|
||||||
copts = ["-DRC_DONT_USE_RTTI"],
|
|
||||||
includes = ["include"],
|
|
||||||
srcs = [
|
|
||||||
"src/BeforeMinimalTestCase.cpp",
|
|
||||||
"src/Check.cpp",
|
|
||||||
"src/Classify.cpp",
|
|
||||||
"src/GenerationFailure.cpp",
|
|
||||||
"src/Log.cpp",
|
|
||||||
"src/Show.cpp",
|
|
||||||
"src/detail/Any.cpp",
|
|
||||||
"src/detail/Assertions.cpp",
|
|
||||||
"src/detail/Base64.cpp",
|
|
||||||
"src/detail/Configuration.cpp",
|
|
||||||
"src/detail/DefaultTestListener.cpp",
|
|
||||||
"src/detail/FrequencyMap.cpp",
|
|
||||||
"src/detail/ImplicitParam.cpp",
|
|
||||||
"src/detail/LogTestListener.cpp",
|
|
||||||
"src/detail/MapParser.cpp",
|
|
||||||
"src/detail/MulticastTestListener.cpp",
|
|
||||||
"src/detail/ParseException.cpp",
|
|
||||||
"src/detail/Platform.cpp",
|
|
||||||
"src/detail/Property.cpp",
|
|
||||||
"src/detail/PropertyContext.cpp",
|
|
||||||
"src/detail/ReproduceListener.cpp",
|
|
||||||
"src/detail/Results.cpp",
|
|
||||||
"src/detail/Serialization.cpp",
|
|
||||||
"src/detail/StringSerialization.cpp",
|
|
||||||
"src/detail/TestMetadata.cpp",
|
|
||||||
"src/detail/TestParams.cpp",
|
|
||||||
"src/detail/Testing.cpp",
|
|
||||||
"src/gen/Numeric.cpp",
|
|
||||||
"src/gen/Text.cpp",
|
|
||||||
"src/gen/detail/ExecHandler.cpp",
|
|
||||||
"src/gen/detail/GenerationHandler.cpp",
|
|
||||||
"src/gen/detail/Recipe.cpp",
|
|
||||||
"src/gen/detail/ScaleInteger.cpp",
|
|
||||||
] + glob(["src/detail/*.h"]),
|
|
||||||
)
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
###############################################################################
|
|
||||||
# Bazel now uses Bzlmod by default to manage external dependencies.
|
|
||||||
# Please consider migrating your external dependencies from WORKSPACE to MODULE.bazel.
|
|
||||||
#
|
|
||||||
# For more details, please check https://github.com/bazelbuild/bazel/issues/18958
|
|
||||||
###############################################################################
|
|
||||||
1245
MODULE.bazel.lock
generated
1245
MODULE.bazel.lock
generated
File diff suppressed because it is too large
Load Diff
22
WORKSPACE
22
WORKSPACE
@ -25,7 +25,7 @@ boost_deps()
|
|||||||
#-------------------------------------------------------------------------------
|
#-------------------------------------------------------------------------------
|
||||||
# magic_enum: Used in logging implementation for enum names.
|
# magic_enum: Used in logging implementation for enum names.
|
||||||
#-------------------------------------------------------------------------------
|
#-------------------------------------------------------------------------------
|
||||||
magic_enum_version = "0.9.5"
|
magic_enum_version = "0.8.2"
|
||||||
magic_enum_base_url = \
|
magic_enum_base_url = \
|
||||||
"https://github.com/Neargye/magic_enum/archive/refs/tags/v"
|
"https://github.com/Neargye/magic_enum/archive/refs/tags/v"
|
||||||
magic_enum_sha256 = \
|
magic_enum_sha256 = \
|
||||||
@ -33,7 +33,7 @@ magic_enum_sha256 = \
|
|||||||
|
|
||||||
http_archive(
|
http_archive(
|
||||||
name = "magic_enum",
|
name = "magic_enum",
|
||||||
# sha256 = magic_enum_sha256,
|
sha256 = magic_enum_sha256,
|
||||||
url = magic_enum_base_url + magic_enum_version + ".zip",
|
url = magic_enum_base_url + magic_enum_version + ".zip",
|
||||||
strip_prefix = "magic_enum-" + magic_enum_version,
|
strip_prefix = "magic_enum-" + magic_enum_version,
|
||||||
)
|
)
|
||||||
@ -101,7 +101,7 @@ cc_library(
|
|||||||
)
|
)
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
#-------------------------------------------------------------------------------
|
||||||
# ut: Testing framework.
|
# ut: Unit test framework.
|
||||||
# TODO(kss): Only if tests are needed?
|
# TODO(kss): Only if tests are needed?
|
||||||
#-------------------------------------------------------------------------------
|
#-------------------------------------------------------------------------------
|
||||||
ut_version = "1.1.9"
|
ut_version = "1.1.9"
|
||||||
@ -124,22 +124,6 @@ cc_library(
|
|||||||
strip_prefix = "ut-" + ut_version,
|
strip_prefix = "ut-" + ut_version,
|
||||||
)
|
)
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
|
||||||
# rapidcheck: Property-based-testing framework.
|
|
||||||
# TODO(kss): Only if tests are needed?
|
|
||||||
#-------------------------------------------------------------------------------
|
|
||||||
rapidcheck_commit = "ff6af6fc683159deb51c543b065eba14dfcf329b"
|
|
||||||
rapidcheck_base_url = "https://github.com/emil-e/rapidcheck"
|
|
||||||
rapidcheck_branch = "master"
|
|
||||||
|
|
||||||
git_repository(
|
|
||||||
name = "rapidcheck",
|
|
||||||
remote = rapidcheck_base_url,
|
|
||||||
commit = rapidcheck_commit,
|
|
||||||
build_file = "@//:BUILD.rapidcheck"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
#-------------------------------------------------------------------------------
|
||||||
# Support compile_commands.json generation for LSP.
|
# Support compile_commands.json generation for LSP.
|
||||||
#-------------------------------------------------------------------------------
|
#-------------------------------------------------------------------------------
|
||||||
|
|||||||
@ -15,14 +15,6 @@ cc_library(
|
|||||||
visibility = ["//visibility:public"],
|
visibility = ["//visibility:public"],
|
||||||
)
|
)
|
||||||
|
|
||||||
cc_library(
|
|
||||||
name = "server",
|
|
||||||
hdrs = glob(["server/**/*.h"]),
|
|
||||||
includes = ["."],
|
|
||||||
deps = ["@expected", "//include:path"],
|
|
||||||
visibility = ["//visibility:public"],
|
|
||||||
)
|
|
||||||
|
|
||||||
cc_library(
|
cc_library(
|
||||||
name = "proto",
|
name = "proto",
|
||||||
hdrs = glob(["proto/**/*.h"]),
|
hdrs = glob(["proto/**/*.h"]),
|
||||||
|
|||||||
@ -91,7 +91,7 @@ struct fmt::formatter<std::errc> : fmt::formatter<std::error_code> {
|
|||||||
template <typename FormatContext>
|
template <typename FormatContext>
|
||||||
auto format(std::errc const& v, FormatContext& ctx) const {
|
auto format(std::errc const& v, FormatContext& ctx) const {
|
||||||
return fmt::formatter<std::error_code>::format(
|
return fmt::formatter<std::error_code>::format(
|
||||||
std::make_error_code(v), ctx);
|
std::make_error_code(v), ctx);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -118,6 +118,7 @@ struct fmt::formatter<T> : fmt::formatter<void const*> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO(ksassenrath): Re-enable when expected has been integrated
|
||||||
template <typename T, typename Err>
|
template <typename T, typename Err>
|
||||||
struct fmt::formatter<tl::expected<T, Err>> {
|
struct fmt::formatter<tl::expected<T, Err>> {
|
||||||
template <typename ParseContext>
|
template <typename ParseContext>
|
||||||
|
|||||||
@ -35,7 +35,6 @@
|
|||||||
#include <system_error>
|
#include <system_error>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
#include <fmt/chrono.h>
|
|
||||||
#include <fmt/color.h>
|
#include <fmt/color.h>
|
||||||
#include <tl/expected.hpp>
|
#include <tl/expected.hpp>
|
||||||
|
|
||||||
|
|||||||
@ -1,288 +0,0 @@
|
|||||||
//-----------------------------------------------------------------------------
|
|
||||||
// ___ __ _ _
|
|
||||||
// / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __
|
|
||||||
// / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ /
|
|
||||||
// / ___/ (_| | | \__ \ __/ /__| | | | | <
|
|
||||||
// \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ .
|
|
||||||
//
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Author: Kurt Sassenrath
|
|
||||||
// Module: msgpack
|
|
||||||
//
|
|
||||||
// Support for packing various types.
|
|
||||||
//
|
|
||||||
// Copyright (c) 2023 Kurt Sassenrath.
|
|
||||||
//
|
|
||||||
// License TBD.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
#ifndef msgpack_core_detail_builtin_packable_types_41bd88d512497527
|
|
||||||
#define msgpack_core_detail_builtin_packable_types_41bd88d512497527
|
|
||||||
|
|
||||||
#include "parselink/msgpack/core/detail/packable_concepts.h"
|
|
||||||
#include "parselink/msgpack/core/format.h"
|
|
||||||
#include "parselink/msgpack/util/endianness.h"
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <limits>
|
|
||||||
#include <ranges>
|
|
||||||
|
|
||||||
namespace msgpack {
|
|
||||||
namespace detail {
|
|
||||||
|
|
||||||
// This is a generic helper function for writing integral bytes
|
|
||||||
template <typename T, typename Itr>
|
|
||||||
constexpr auto pack_integral(T value, Itr out, std::size_t sz) noexcept {
|
|
||||||
auto bytes = ::detail::as_bytes(host_to_be(value));
|
|
||||||
for (std::size_t i = 0; i < sz; ++i) {
|
|
||||||
*out++ = *(bytes.end() - sz + i);
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct dummy_packer {
|
|
||||||
std::byte* out;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Defines a range of bytes for binary formats.
|
|
||||||
template <typename T>
|
|
||||||
concept byte_range = std::ranges::contiguous_range<T>
|
|
||||||
&& std::same_as<std::ranges::range_value_t<T>, std::byte>;
|
|
||||||
|
|
||||||
} // namespace detail
|
|
||||||
|
|
||||||
struct builtin_packer_base {
|
|
||||||
static constexpr std::size_t rep_size(auto const&) noexcept { return 0; }
|
|
||||||
|
|
||||||
static constexpr std::size_t len_size(auto const& value) noexcept {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static constexpr auto total_size(auto const& value) noexcept {
|
|
||||||
return 1 + len_size(value) + rep_size(value);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename>
|
|
||||||
struct builtin_packer;
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Built-in packer specializations
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct builtin_packer<invalid> : builtin_packer_base {
|
|
||||||
static constexpr void pack(auto, auto& packer) noexcept {
|
|
||||||
*(packer.out)++ = format::invalid::marker;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct builtin_packer<nil> : builtin_packer_base {
|
|
||||||
static constexpr void pack(auto, auto& packer) noexcept {
|
|
||||||
*(packer.out)++ = format::nil::marker;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct builtin_packer<bool> : builtin_packer_base {
|
|
||||||
static constexpr void pack(bool value, auto& packer) noexcept {
|
|
||||||
*(packer.out)++ =
|
|
||||||
format::boolean::marker | static_cast<std::byte>(value);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <std::unsigned_integral T>
|
|
||||||
struct builtin_packer<T> : builtin_packer_base {
|
|
||||||
static constexpr std::size_t rep_size(std::uint64_t value) noexcept {
|
|
||||||
if (value < 128) return 0;
|
|
||||||
auto bytes_needed =
|
|
||||||
static_cast<std::uint64_t>((std::bit_width(value) + 7) >> 3);
|
|
||||||
return std::bit_ceil(bytes_needed);
|
|
||||||
}
|
|
||||||
|
|
||||||
static constexpr std::byte marker(std::uint64_t value) noexcept {
|
|
||||||
switch (rep_size(value)) {
|
|
||||||
case 0: return static_cast<std::byte>(value);
|
|
||||||
case 1: return format::uint8::marker;
|
|
||||||
case 2: return format::uint16::marker;
|
|
||||||
case 4: return format::uint32::marker;
|
|
||||||
default: return format::uint64::marker;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static constexpr void pack(T const& value, auto& packer) noexcept {
|
|
||||||
*(packer.out)++ = marker(value);
|
|
||||||
packer.out = detail::pack_integral(value, packer.out, rep_size(value));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <std::signed_integral T>
|
|
||||||
struct builtin_packer<T> : builtin_packer_base {
|
|
||||||
static constexpr std::size_t rep_size(std::int64_t value) noexcept {
|
|
||||||
// Probably a better way to do this; fixints
|
|
||||||
if (value < 128 && value >= -32) return 0;
|
|
||||||
auto underlying = static_cast<std::uint64_t>(value);
|
|
||||||
|
|
||||||
// save a branch; these should be cheap on modern hardware.
|
|
||||||
std::uint64_t counts[2] = {
|
|
||||||
static_cast<std::uint64_t>(std::countl_zero(underlying)),
|
|
||||||
static_cast<std::uint64_t>(std::countl_one(underlying))};
|
|
||||||
|
|
||||||
std::uint64_t width = 1 + std::numeric_limits<std::uint64_t>::digits
|
|
||||||
- counts[underlying >> 63];
|
|
||||||
|
|
||||||
return std::bit_ceil((width + 7) >> 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
static constexpr std::byte marker(std::int64_t value) noexcept {
|
|
||||||
switch (rep_size(value)) {
|
|
||||||
case 0: return static_cast<std::byte>(value);
|
|
||||||
case 1: return format::int8::marker;
|
|
||||||
case 2: return format::int16::marker;
|
|
||||||
case 4: return format::int32::marker;
|
|
||||||
default: return format::int64::marker;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static constexpr void pack(T const& value, auto& packer) noexcept {
|
|
||||||
*(packer.out)++ = marker(value);
|
|
||||||
packer.out = detail::pack_integral(value, packer.out, rep_size(value));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <detail::byte_range T>
|
|
||||||
struct builtin_packer<T> : builtin_packer_base {
|
|
||||||
static constexpr uint32_t max_payload_length = 4;
|
|
||||||
|
|
||||||
static constexpr std::size_t rep_size(T const& value) noexcept {
|
|
||||||
return std::ranges::size(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
static constexpr std::size_t len_size(T const& value) noexcept {
|
|
||||||
auto const len = rep_size(value);
|
|
||||||
auto const rounded_bytes = std::bit_ceil(static_cast<std::uint32_t>(
|
|
||||||
((std::bit_width(std::ranges::size(value)) + 7) >> 3)));
|
|
||||||
return std::min(max_payload_length, rounded_bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
static constexpr auto marker(T const& value) noexcept {
|
|
||||||
switch (len_size(value)) {
|
|
||||||
case 1: return format::bin8::marker;
|
|
||||||
case 2: return format::bin16::marker;
|
|
||||||
case 4:
|
|
||||||
default: return format::bin32::marker;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static constexpr void pack(T const& value, auto& packer) noexcept {
|
|
||||||
*(packer.out)++ = marker(value);
|
|
||||||
packer.out = detail::pack_integral(
|
|
||||||
std::ranges::size(value), packer.out, len_size(value));
|
|
||||||
packer.out = std::ranges::copy(value, packer.out).out;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct builtin_packer<std::string_view> : builtin_packer_base {
|
|
||||||
static constexpr uint32_t max_payload_length = 4;
|
|
||||||
|
|
||||||
static constexpr std::size_t rep_size(std::string_view value) noexcept {
|
|
||||||
return std::size(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
static constexpr std::size_t len_size(std::string_view value) noexcept {
|
|
||||||
auto const len = std::size(value);
|
|
||||||
if (len <= static_cast<std::uint32_t>(format::fixstr::mask)) return 0;
|
|
||||||
return std::min(max_payload_length,
|
|
||||||
std::bit_ceil(std::uint32_t((std::bit_width(len) + 7) >> 3)));
|
|
||||||
}
|
|
||||||
|
|
||||||
static constexpr auto marker(std::string_view value) noexcept {
|
|
||||||
switch (len_size(value)) {
|
|
||||||
case 0:
|
|
||||||
return format::fixstr::marker
|
|
||||||
| static_cast<std::byte>(rep_size(value));
|
|
||||||
case 1: return format::str8::marker;
|
|
||||||
case 2: return format::str16::marker;
|
|
||||||
case 4:
|
|
||||||
default: return format::str32::marker;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static constexpr void pack(std::string_view value, auto& packer) noexcept {
|
|
||||||
*(packer.out)++ = marker(value);
|
|
||||||
auto const size = std::size(value);
|
|
||||||
packer.out = detail::pack_integral(size, packer.out, len_size(value));
|
|
||||||
auto const* beg = reinterpret_cast<std::byte const*>(&*value.begin());
|
|
||||||
packer.out = std::ranges::copy(beg, beg + size, packer.out).out;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// All things that convert to std::string_view should use the string_view
|
|
||||||
// overload of builtin_packer.
|
|
||||||
template <std::convertible_to<std::string_view> T>
|
|
||||||
struct builtin_packer<T> : builtin_packer<std::string_view> {};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct builtin_packer<array_desc> : builtin_packer_base {
|
|
||||||
static constexpr std::size_t rep_size(array_desc value) noexcept {
|
|
||||||
auto const len = value.count;
|
|
||||||
if (len <= static_cast<std::size_t>(format::fixarray::mask)) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (len < 0x10000) return 2;
|
|
||||||
return 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
static constexpr auto marker(array_desc value) noexcept {
|
|
||||||
switch (rep_size(value)) {
|
|
||||||
case 0:
|
|
||||||
return format::fixarray::marker
|
|
||||||
| static_cast<std::byte>(value.count);
|
|
||||||
case 2: return format::array16::marker;
|
|
||||||
case 4:
|
|
||||||
default: return format::array32::marker;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static constexpr auto pack(array_desc value, auto& packer) noexcept {
|
|
||||||
*(packer.out)++ = marker(value);
|
|
||||||
packer.out =
|
|
||||||
detail::pack_integral(value.count, packer.out, rep_size(value));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct builtin_packer<map_desc> : builtin_packer_base {
|
|
||||||
static constexpr std::size_t rep_size(map_desc value) noexcept {
|
|
||||||
auto const len = value.count;
|
|
||||||
if (len <= static_cast<std::size_t>(format::fixmap::mask)) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (len < 0x10000) return 2;
|
|
||||||
return 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
static constexpr auto marker(map_desc value) noexcept {
|
|
||||||
switch (rep_size(value)) {
|
|
||||||
case 0:
|
|
||||||
return format::fixmap::marker
|
|
||||||
| static_cast<std::byte>(value.count);
|
|
||||||
case 2: return format::map16::marker;
|
|
||||||
case 4:
|
|
||||||
default: return format::map32::marker;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static constexpr void pack(map_desc value, auto& packer) noexcept {
|
|
||||||
*(packer.out)++ = marker(value);
|
|
||||||
packer.out =
|
|
||||||
detail::pack_integral(value.count, packer.out, rep_size(value));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace msgpack
|
|
||||||
|
|
||||||
#endif // msgpack_core_detail_builtin_packable_types_41bd88d512497527
|
|
||||||
@ -1,347 +0,0 @@
|
|||||||
//-----------------------------------------------------------------------------
|
|
||||||
// ___ __ _ _
|
|
||||||
// / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __
|
|
||||||
// / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ /
|
|
||||||
// / ___/ (_| | | \__ \ __/ /__| | | | | <
|
|
||||||
// \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ .
|
|
||||||
//
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Author: Kurt Sassenrath
|
|
||||||
// Module: msgpack
|
|
||||||
//
|
|
||||||
// Support for unpacking various types.
|
|
||||||
//
|
|
||||||
// Copyright (c) 2023 Kurt Sassenrath.
|
|
||||||
//
|
|
||||||
// License TBD.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
#ifndef msgpack_core_detail_builtin_unpackable_types_bf5de632f088a7d8
|
|
||||||
#define msgpack_core_detail_builtin_unpackable_types_bf5de632f088a7d8
|
|
||||||
|
|
||||||
#include "parselink/msgpack/core/detail/unpackable_concepts.h"
|
|
||||||
#include "parselink/msgpack/core/error.h"
|
|
||||||
#include "parselink/msgpack/core/format.h"
|
|
||||||
#include "parselink/msgpack/util/endianness.h"
|
|
||||||
|
|
||||||
#include <tl/expected.hpp>
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <limits>
|
|
||||||
#include <ranges>
|
|
||||||
|
|
||||||
namespace msgpack {
|
|
||||||
namespace detail {
|
|
||||||
|
|
||||||
// Generic mechanism for reading a number of bytes out of an interator.
|
|
||||||
template <std::size_t N, typename Itr>
|
|
||||||
constexpr inline decltype(auto) read_bytes(Itr& inp) noexcept {
|
|
||||||
std::array<std::byte, N> out;
|
|
||||||
auto const end = inp + N;
|
|
||||||
auto dst = out.begin();
|
|
||||||
while (inp != end) {
|
|
||||||
*dst = std::byte{*inp};
|
|
||||||
++dst;
|
|
||||||
++inp;
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is a generic helper function for writing integral bytes
|
|
||||||
template <std::integral T, typename Itr>
|
|
||||||
constexpr decltype(auto) unpack_integral(Itr& inp) noexcept {
|
|
||||||
constexpr auto N = sizeof(T);
|
|
||||||
if constexpr (sizeof(T) == 1) {
|
|
||||||
return T(*inp++);
|
|
||||||
} else {
|
|
||||||
return be_to_host(::detail::value_cast<T>(read_bytes<N>(inp)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Defines a range of bytes for binary formats.
|
|
||||||
template <typename T>
|
|
||||||
concept unpackable_byte_range =
|
|
||||||
std::ranges::contiguous_range<T>
|
|
||||||
&& std::same_as<std::ranges::range_value_t<T>, std::byte>;
|
|
||||||
|
|
||||||
template <format_type F, typename Iter>
|
|
||||||
constexpr inline bool accept_format(Iter& itr) noexcept {
|
|
||||||
if constexpr (is_fixtype<F>) {
|
|
||||||
// Don't advance the iterator -- part of the format is locked away
|
|
||||||
// within the format byte.
|
|
||||||
return (std::byte{*itr} & ~F::mask) == F::marker;
|
|
||||||
} else {
|
|
||||||
// Advance the iterator past the format byte.
|
|
||||||
return std::byte{*itr++} == F::marker;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This helper structure provides a customization point for converting an
|
|
||||||
// iterator to some other type (e.g. a pointer) in the event that the
|
|
||||||
// final format does not have a suitable constructor. It's only currently
|
|
||||||
// used for string_view, but may be overridden on platforms that provide
|
|
||||||
// their own types.
|
|
||||||
template <typename T, typename Iter>
|
|
||||||
struct iterator_adapter {
|
|
||||||
static constexpr auto convert(Iter itr) { return itr; }
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Iter>
|
|
||||||
struct iterator_adapter<std::string_view, Iter> {
|
|
||||||
static constexpr auto convert(Iter itr) {
|
|
||||||
return ::detail::value_cast<std::string_view::pointer>(&*itr);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Using information expressed within each format type, this singular template
|
|
||||||
// can instantiate the unpacking of most formats. Notable exceptions are
|
|
||||||
// the array/map types and extension types.
|
|
||||||
template <format_type F>
|
|
||||||
constexpr inline auto unpack_format(auto& unpacker) noexcept
|
|
||||||
-> tl::expected<typename F::value_type, error> {
|
|
||||||
if (!accept_format<F>(unpacker.in)) {
|
|
||||||
return tl::make_unexpected(error::wrong_type);
|
|
||||||
}
|
|
||||||
|
|
||||||
// First thing's first. Read the format's "first_type", which is either
|
|
||||||
// the payload or the length of the payload. Ensure we have enough data
|
|
||||||
// from the span before we do so, though.
|
|
||||||
using first_type = typename F::first_type;
|
|
||||||
using value_type = typename F::value_type;
|
|
||||||
using diff_type = decltype(unpacker.remaining());
|
|
||||||
if (unpacker.remaining() < diff_type{sizeof(first_type)}) {
|
|
||||||
return tl::make_unexpected(error::incomplete_message);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto f = unpack_integral<first_type>(unpacker.in);
|
|
||||||
if constexpr (is_fixtype<F> && (F::flags & format::flag::apply_mask)) {
|
|
||||||
f &= decltype(f)(F::mask);
|
|
||||||
}
|
|
||||||
|
|
||||||
if constexpr (F::payload_type == format::payload::fixed) {
|
|
||||||
// Prefer constructions that utilize the unpacked value, but allow
|
|
||||||
// tag types (e.g. invalid, nil) to be directly constructed.
|
|
||||||
if constexpr (std::constructible_from<value_type, decltype(f)>) {
|
|
||||||
return value_type(f);
|
|
||||||
} else {
|
|
||||||
return value_type{};
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// We're reading a variable length payload. `f` is the length of the
|
|
||||||
// payload. Ensure that the span has enough data and construct the
|
|
||||||
// value_type accordingly.
|
|
||||||
if (unpacker.remaining() < diff_type(f)) {
|
|
||||||
return tl::make_unexpected(error::incomplete_message);
|
|
||||||
}
|
|
||||||
|
|
||||||
using adapt = iterator_adapter<value_type, decltype(unpacker.in)>;
|
|
||||||
auto value = value_type(adapt::convert(unpacker.in), f);
|
|
||||||
unpacker.in += f;
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace detail
|
|
||||||
|
|
||||||
template <typename>
|
|
||||||
struct builtin_unpacker;
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Built-in unpacker specializations
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
template <>
|
|
||||||
struct builtin_unpacker<invalid> {
|
|
||||||
static constexpr auto unpack(auto& unpacker) noexcept {
|
|
||||||
return detail::unpack_format<format::invalid>(unpacker);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct builtin_unpacker<nil> {
|
|
||||||
static constexpr auto unpack(auto& unpacker) noexcept {
|
|
||||||
return detail::unpack_format<format::nil>(unpacker);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct builtin_unpacker<bool> {
|
|
||||||
static constexpr auto unpack(auto& unpacker) noexcept {
|
|
||||||
return detail::unpack_format<format::boolean>(unpacker);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct builtin_unpacker<map_desc> {
|
|
||||||
static constexpr auto unpack(auto& unpacker) noexcept
|
|
||||||
-> tl::expected<map_desc, msgpack::error> {
|
|
||||||
auto marker = *(unpacker.in);
|
|
||||||
if ((marker & ~format::fixmap::mask) == format::fixmap::marker) {
|
|
||||||
return detail::unpack_format<format::fixmap>(unpacker);
|
|
||||||
}
|
|
||||||
switch (marker) {
|
|
||||||
case format::map16::marker:
|
|
||||||
return detail::unpack_format<format::map16>(unpacker);
|
|
||||||
case format::map32::marker:
|
|
||||||
return detail::unpack_format<format::map32>(unpacker);
|
|
||||||
}
|
|
||||||
return tl::make_unexpected(msgpack::error::wrong_type);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// This unpacker will accept any uintX, positive_fixint types.
|
|
||||||
struct unsigned_int_unpacker {
|
|
||||||
static constexpr auto unpack(auto& unpacker) noexcept
|
|
||||||
-> tl::expected<std::uint64_t, msgpack::error> {
|
|
||||||
auto marker = *(unpacker.in);
|
|
||||||
if ((marker & std::byte{0x80}) == std::byte{}) {
|
|
||||||
return detail::unpack_format<format::positive_fixint>(unpacker);
|
|
||||||
}
|
|
||||||
switch (marker) {
|
|
||||||
case format::uint8::marker:
|
|
||||||
return detail::unpack_format<format::uint8>(unpacker);
|
|
||||||
case format::uint16::marker:
|
|
||||||
return detail::unpack_format<format::uint16>(unpacker);
|
|
||||||
case format::uint32::marker:
|
|
||||||
return detail::unpack_format<format::uint32>(unpacker);
|
|
||||||
case format::uint64::marker:
|
|
||||||
return detail::unpack_format<format::uint64>(unpacker);
|
|
||||||
}
|
|
||||||
return tl::make_unexpected(msgpack::error::wrong_type);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <std::unsigned_integral T>
|
|
||||||
struct builtin_unpacker<T> {
|
|
||||||
static constexpr auto max = std::numeric_limits<T>::max();
|
|
||||||
|
|
||||||
static constexpr auto unpack(auto& unpacker) noexcept {
|
|
||||||
constexpr auto convert =
|
|
||||||
[](auto value) -> tl::expected<T, msgpack::error> {
|
|
||||||
if (value > max) {
|
|
||||||
return tl::make_unexpected(error::will_truncate);
|
|
||||||
}
|
|
||||||
return static_cast<T>(value);
|
|
||||||
};
|
|
||||||
return unsigned_int_unpacker::unpack(unpacker).and_then(convert);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct signed_int_unpacker {
|
|
||||||
static constexpr auto unpack(auto& unpacker) noexcept
|
|
||||||
-> tl::expected<std::int64_t, msgpack::error> {
|
|
||||||
auto marker = *(unpacker.in);
|
|
||||||
if ((marker & std::byte{0x80}) == std::byte{}) {
|
|
||||||
return detail::unpack_format<format::positive_fixint>(unpacker);
|
|
||||||
} else if ((marker & std::byte{0xe0}) == std::byte{0xe0}) {
|
|
||||||
return detail::unpack_format<format::negative_fixint>(unpacker);
|
|
||||||
}
|
|
||||||
switch (marker) {
|
|
||||||
case format::int8::marker:
|
|
||||||
return detail::unpack_format<format::int8>(unpacker);
|
|
||||||
case format::int16::marker:
|
|
||||||
return detail::unpack_format<format::int16>(unpacker);
|
|
||||||
case format::int32::marker:
|
|
||||||
return detail::unpack_format<format::int32>(unpacker);
|
|
||||||
case format::int64::marker:
|
|
||||||
return detail::unpack_format<format::int64>(unpacker);
|
|
||||||
}
|
|
||||||
return tl::make_unexpected(msgpack::error::wrong_type);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <std::signed_integral T>
|
|
||||||
struct builtin_unpacker<T> {
|
|
||||||
static constexpr auto max = std::numeric_limits<T>::max();
|
|
||||||
static constexpr auto min = std::numeric_limits<T>::min();
|
|
||||||
|
|
||||||
static constexpr auto unpack(auto& unpacker) noexcept {
|
|
||||||
constexpr auto convert =
|
|
||||||
[](auto value) -> tl::expected<T, msgpack::error> {
|
|
||||||
if (value > max || value < min) {
|
|
||||||
return tl::make_unexpected(error::will_truncate);
|
|
||||||
}
|
|
||||||
return static_cast<T>(value);
|
|
||||||
};
|
|
||||||
return signed_int_unpacker::unpack(unpacker).and_then(convert);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct builtin_unpacker<std::string_view> {
|
|
||||||
static constexpr auto unpack(auto& unpacker) noexcept
|
|
||||||
-> tl::expected<std::string_view, msgpack::error> {
|
|
||||||
auto marker = *(unpacker.in);
|
|
||||||
if ((marker & ~format::fixstr::mask) == format::fixstr::marker) {
|
|
||||||
return detail::unpack_format<format::fixstr>(unpacker);
|
|
||||||
}
|
|
||||||
switch (marker) {
|
|
||||||
case format::str8::marker:
|
|
||||||
return detail::unpack_format<format::str8>(unpacker);
|
|
||||||
case format::str16::marker:
|
|
||||||
return detail::unpack_format<format::str16>(unpacker);
|
|
||||||
case format::str32::marker:
|
|
||||||
return detail::unpack_format<format::str32>(unpacker);
|
|
||||||
}
|
|
||||||
return tl::make_unexpected(msgpack::error::wrong_type);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T, std::size_t Extent>
|
|
||||||
requires(std::same_as<std::byte, std::remove_const_t<T>>)
|
|
||||||
struct builtin_unpacker<std::span<T, Extent>> {
|
|
||||||
template <std::size_t E = Extent>
|
|
||||||
using expected_type = tl::expected<std::span<T, E>, msgpack::error>;
|
|
||||||
|
|
||||||
static constexpr auto unpack_bytes(auto& unpacker) noexcept
|
|
||||||
-> expected_type<std::dynamic_extent> {
|
|
||||||
auto marker = *(unpacker.in);
|
|
||||||
switch (marker) {
|
|
||||||
case format::bin8::marker:
|
|
||||||
return detail::unpack_format<format::bin8>(unpacker);
|
|
||||||
case format::bin16::marker:
|
|
||||||
return detail::unpack_format<format::bin16>(unpacker);
|
|
||||||
case format::bin32::marker:
|
|
||||||
return detail::unpack_format<format::bin32>(unpacker);
|
|
||||||
}
|
|
||||||
return tl::make_unexpected(msgpack::error::wrong_type);
|
|
||||||
}
|
|
||||||
|
|
||||||
static constexpr expected_type<> unpack(auto& unpacker) noexcept {
|
|
||||||
auto ensure_extent = [](std::span<std::byte const> value) noexcept
|
|
||||||
-> expected_type<> {
|
|
||||||
if constexpr (Extent != std::dynamic_extent) {
|
|
||||||
if (value.size() != Extent) {
|
|
||||||
return tl::make_unexpected(msgpack::error::wrong_length);
|
|
||||||
}
|
|
||||||
return value.template first<Extent>();
|
|
||||||
} else {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return unpack_bytes(unpacker).and_then(ensure_extent);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T, std::size_t Extent>
|
|
||||||
requires(std::same_as<std::byte, std::remove_const_t<T>> && Extent > 0)
|
|
||||||
struct builtin_unpacker<std::array<T, Extent>> {
|
|
||||||
using expected_type = tl::expected<std::array<T, Extent>, msgpack::error>;
|
|
||||||
|
|
||||||
static constexpr expected_type unpack(auto& unpacker) noexcept {
|
|
||||||
constexpr auto copy_to_array = [](auto span) noexcept -> expected_type {
|
|
||||||
if (span.size() != Extent) {
|
|
||||||
return tl::make_unexpected(msgpack::error::wrong_length);
|
|
||||||
} else {
|
|
||||||
std::array<std::byte, Extent> dest;
|
|
||||||
std::copy(span.begin(), span.end(), dest.begin());
|
|
||||||
return dest;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return builtin_unpacker<std::span<std::byte const>>::unpack(unpacker)
|
|
||||||
.and_then(copy_to_array);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace msgpack
|
|
||||||
|
|
||||||
#endif // msgpack_core_detail_builtin_unpackable_types_bf5de632f088a7d8
|
|
||||||
@ -1,85 +0,0 @@
|
|||||||
//-----------------------------------------------------------------------------
|
|
||||||
// ___ __ _ _
|
|
||||||
// / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __
|
|
||||||
// / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ /
|
|
||||||
// / ___/ (_| | | \__ \ __/ /__| | | | | <
|
|
||||||
// \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ .
|
|
||||||
//
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Author: Kurt Sassenrath
|
|
||||||
// Module: msgpack
|
|
||||||
//
|
|
||||||
// Concepts used for types that are packable by a packer.
|
|
||||||
//
|
|
||||||
// Copyright (c) 2023 Kurt Sassenrath.
|
|
||||||
//
|
|
||||||
// License TBD.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
#ifndef msgpack_core_detail_packable_concepts_397b5e0ae6de61bf
|
|
||||||
#define msgpack_core_detail_packable_concepts_397b5e0ae6de61bf
|
|
||||||
|
|
||||||
#include <concepts>
|
|
||||||
#include <cstddef>
|
|
||||||
#include <type_traits>
|
|
||||||
|
|
||||||
namespace msgpack {
|
|
||||||
namespace detail {
|
|
||||||
|
|
||||||
// This structure models a minimal packer context. It is used to validate
|
|
||||||
// whether a particular type is considered packable or not.
|
|
||||||
struct packer_context_model {
|
|
||||||
std::byte* out;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace detail
|
|
||||||
|
|
||||||
// Packers with built-in support will specialize builtin_packer.
|
|
||||||
template <typename>
|
|
||||||
struct builtin_packer;
|
|
||||||
|
|
||||||
// Packers
|
|
||||||
template <typename>
|
|
||||||
struct packer_adapter;
|
|
||||||
|
|
||||||
template <typename T, template <typename> typename Packer,
|
|
||||||
typename Context = detail::packer_context_model>
|
|
||||||
concept packable_with = requires(T&& value, Context& dest) {
|
|
||||||
{
|
|
||||||
Packer<std::remove_cvref_t<T>>::total_size(value)
|
|
||||||
} -> std::same_as<std::size_t>;
|
|
||||||
{
|
|
||||||
Packer<std::remove_cvref_t<T>>::pack(value, dest)
|
|
||||||
} -> std::same_as<void>;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
concept custom_packable = packable_with<T, packer_adapter>;
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
concept builtin_packable = packable_with<T, builtin_packer>;
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
concept packable = builtin_packable<T> || custom_packable<T>;
|
|
||||||
|
|
||||||
namespace detail {
|
|
||||||
|
|
||||||
template <typename>
|
|
||||||
struct packer_impl_s;
|
|
||||||
|
|
||||||
template <custom_packable T>
|
|
||||||
struct packer_impl_s<T> {
|
|
||||||
using type = packer_adapter<T>;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <builtin_packable T>
|
|
||||||
struct packer_impl_s<T> {
|
|
||||||
using type = builtin_packer<T>;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
using packer_impl = packer_impl_s<T>::type;
|
|
||||||
} // namespace detail
|
|
||||||
|
|
||||||
} // namespace msgpack
|
|
||||||
|
|
||||||
#endif // msgpack_core_detail_packable_concepts_397b5e0ae6de61bf
|
|
||||||
@ -1,104 +0,0 @@
|
|||||||
//-----------------------------------------------------------------------------
|
|
||||||
// ___ __ _ _
|
|
||||||
// / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __
|
|
||||||
// / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ /
|
|
||||||
// / ___/ (_| | | \__ \ __/ /__| | | | | <
|
|
||||||
// \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ .
|
|
||||||
//
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Author: Kurt Sassenrath
|
|
||||||
// Module: msgpack
|
|
||||||
//
|
|
||||||
// Support for packing ranges of packable types into array/map formats.
|
|
||||||
//
|
|
||||||
// If the range size is known at compile time or can be queried at runtime, it
|
|
||||||
// will be leveraged to find the most efficient version of the format.
|
|
||||||
//
|
|
||||||
// Copyright (c) 2023 Kurt Sassenrath.
|
|
||||||
//
|
|
||||||
// License TBD.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
#ifndef msgpack_core_detail_packable_ranges_10d28c24498828c7
|
|
||||||
#define msgpack_core_detail_packable_ranges_10d28c24498828c7
|
|
||||||
|
|
||||||
#include "parselink/msgpack/core/detail/builtin_packable_types.h"
|
|
||||||
#include "parselink/msgpack/core/detail/packable_concepts.h"
|
|
||||||
|
|
||||||
#include <ranges>
|
|
||||||
#include <tuple>
|
|
||||||
|
|
||||||
namespace msgpack {
|
|
||||||
namespace detail {
|
|
||||||
|
|
||||||
//
|
|
||||||
template <typename T>
|
|
||||||
concept packable_array =
|
|
||||||
std::ranges::input_range<T> && !std::convertible_to<T, std::string_view>
|
|
||||||
&& packable<std::ranges::range_value_t<T>>;
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
concept key_value_pairlike = requires(T t) {
|
|
||||||
typename std::tuple_size<T>::type;
|
|
||||||
} && std::tuple_size_v<T> == 2;
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
concept packable_map =
|
|
||||||
std::ranges::input_range<T>
|
|
||||||
&& key_value_pairlike<std::ranges::range_value_t<T>>
|
|
||||||
&& builtin_packable<
|
|
||||||
std::tuple_element_t<0, std::ranges::range_value_t<T>>>
|
|
||||||
&& builtin_packable<
|
|
||||||
std::tuple_element_t<1, std::ranges::range_value_t<T>>>;
|
|
||||||
|
|
||||||
template <typename>
|
|
||||||
struct desc_traits;
|
|
||||||
|
|
||||||
template <packable_map T>
|
|
||||||
struct desc_traits<T> {
|
|
||||||
using type = map_desc;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <packable_array T>
|
|
||||||
struct desc_traits<T> {
|
|
||||||
using type = array_desc;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace detail
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
concept packable_range = detail::packable_array<T> || detail::packable_map<T>;
|
|
||||||
|
|
||||||
template <packable_range T>
|
|
||||||
struct builtin_packer<T> : builtin_packer_base {
|
|
||||||
using desc_type = detail::desc_traits<T>::type;
|
|
||||||
|
|
||||||
static constexpr std::size_t rep_size(T const& value) {
|
|
||||||
return std::ranges::size(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
static constexpr auto desc(T const& value) {
|
|
||||||
return desc_type(rep_size(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
static constexpr auto marker(T const& value) {
|
|
||||||
return builtin_packer<desc_type>::marker(desc(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
static constexpr void pack(T const& value, auto& packer) noexcept {
|
|
||||||
packer.pack(desc(value));
|
|
||||||
if constexpr (detail::packable_map<T>) {
|
|
||||||
for (auto const& [key, value] : value) {
|
|
||||||
packer.pack(key);
|
|
||||||
packer.pack(value);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (auto const& element : value) {
|
|
||||||
packer.pack(element);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace msgpack
|
|
||||||
|
|
||||||
#endif // msgpack_core_detail_packable_ranges_10d28c24498828c7
|
|
||||||
@ -1,92 +0,0 @@
|
|||||||
//-----------------------------------------------------------------------------
|
|
||||||
// ___ __ _ _
|
|
||||||
// / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __
|
|
||||||
// / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ /
|
|
||||||
// / ___/ (_| | | \__ \ __/ /__| | | | | <
|
|
||||||
// \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ .
|
|
||||||
//
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Author: Kurt Sassenrath
|
|
||||||
// Module: msgpack
|
|
||||||
//
|
|
||||||
// Concepts used for types that are deserializable by an unpacker.
|
|
||||||
//
|
|
||||||
// Copyright (c) 2023 Kurt Sassenrath.
|
|
||||||
//
|
|
||||||
// License TBD.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
#ifndef msgpack_core_detail_unpackable_concepts_cf142e37b34a598f
|
|
||||||
#define msgpack_core_detail_unpackable_concepts_cf142e37b34a598f
|
|
||||||
|
|
||||||
#include "parselink/msgpack/core/error.h"
|
|
||||||
|
|
||||||
#include <tl/expected.hpp>
|
|
||||||
|
|
||||||
#include <concepts>
|
|
||||||
#include <cstddef>
|
|
||||||
#include <type_traits>
|
|
||||||
|
|
||||||
namespace msgpack {
|
|
||||||
namespace detail {
|
|
||||||
|
|
||||||
// This structure models a minimal packer context. It is used to validate
|
|
||||||
// whether a particular type is considered packable or not.
|
|
||||||
struct unpacker_context_model {
|
|
||||||
std::byte* in;
|
|
||||||
|
|
||||||
std::size_t remaining() { return 1; }
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace detail
|
|
||||||
|
|
||||||
// Datatypes with built-in support will specialize builtin_unpacker.
|
|
||||||
template <typename>
|
|
||||||
struct builtin_unpacker;
|
|
||||||
|
|
||||||
// Datatypes can have a custom adapter for unpacking. This _can_ be used to
|
|
||||||
// override default behavior if necessary, but its intended for custom types.
|
|
||||||
template <typename>
|
|
||||||
struct unpacker_adapter;
|
|
||||||
|
|
||||||
template <typename T, typename... U>
|
|
||||||
concept is_any_of = (std::same_as<T, U> || ...);
|
|
||||||
|
|
||||||
template <typename T, template <typename> typename Unpacker,
|
|
||||||
typename Context = detail::unpacker_context_model>
|
|
||||||
concept unpackable_with = requires(T&& value, Context& dest) {
|
|
||||||
{
|
|
||||||
Unpacker<std::remove_cvref_t<T>>::unpack(dest)
|
|
||||||
} -> is_any_of<T, tl::expected<T, msgpack::error>>;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
concept custom_unpackable = unpackable_with<T, unpacker_adapter>;
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
concept builtin_unpackable = unpackable_with<T, builtin_unpacker>;
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
concept supports_unpack = builtin_unpackable<T> || custom_unpackable<T>;
|
|
||||||
|
|
||||||
namespace detail {
|
|
||||||
|
|
||||||
template <typename>
|
|
||||||
struct unpacker_impl_s;
|
|
||||||
|
|
||||||
template <custom_unpackable T>
|
|
||||||
struct unpacker_impl_s<T> {
|
|
||||||
using type = unpacker_adapter<T>;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <builtin_unpackable T>
|
|
||||||
struct unpacker_impl_s<T> {
|
|
||||||
using type = builtin_unpacker<T>;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
using unpacker_impl = unpacker_impl_s<T>::type;
|
|
||||||
} // namespace detail
|
|
||||||
|
|
||||||
} // namespace msgpack
|
|
||||||
|
|
||||||
#endif // msgpack_core_detail_unpackable_concepts_cf142e37b34a598f
|
|
||||||
@ -29,7 +29,6 @@ enum class error {
|
|||||||
wrong_type, // The format is incompatible with requested type.
|
wrong_type, // The format is incompatible with requested type.
|
||||||
bad_value, // Value does not fit within constraints.
|
bad_value, // Value does not fit within constraints.
|
||||||
will_truncate, // Integer return type is smaller than stored value.
|
will_truncate, // Integer return type is smaller than stored value.
|
||||||
wrong_length, // Variable-length format does not match desired length.
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace msgpack
|
} // namespace msgpack
|
||||||
|
|||||||
@ -31,14 +31,14 @@ namespace msgpack {
|
|||||||
// Supporting types/tags for formats
|
// Supporting types/tags for formats
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
struct nil {
|
struct nil {
|
||||||
constexpr nil() noexcept = default;
|
nil() = default;
|
||||||
constexpr bool operator==(nil const& other) const noexcept = default;
|
// This constructor is used by the reader implementation.
|
||||||
|
nil(auto){};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct invalid {
|
struct invalid {};
|
||||||
constexpr invalid() noexcept = default;
|
|
||||||
constexpr bool operator==(invalid const& other) const noexcept = default;
|
using boolean = bool;
|
||||||
};
|
|
||||||
|
|
||||||
struct array_desc {
|
struct array_desc {
|
||||||
constexpr array_desc() noexcept = default;
|
constexpr array_desc() noexcept = default;
|
||||||
@ -225,12 +225,12 @@ struct int64 : format<0xd3, std::int64_t> {};
|
|||||||
*/
|
*/
|
||||||
struct nil : fixtype<0xc0, 0x00> {
|
struct nil : fixtype<0xc0, 0x00> {
|
||||||
using value_type = msgpack::nil;
|
using value_type = msgpack::nil;
|
||||||
constexpr static flag flags{flag::fixed_size};
|
constexpr static flag flags{flag::fixed_size | flag::apply_mask};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct invalid : fixtype<0xc1, 0x00> {
|
struct invalid : fixtype<0xc1, 0x00> {
|
||||||
using value_type = msgpack::invalid;
|
using value_type = msgpack::invalid;
|
||||||
constexpr static flag flags{flag::fixed_size};
|
constexpr static flag flags{flag::fixed_size | flag::apply_mask};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct boolean : fixtype<0xc2, 0x01> {
|
struct boolean : fixtype<0xc2, 0x01> {
|
||||||
|
|||||||
@ -1,113 +0,0 @@
|
|||||||
//-----------------------------------------------------------------------------
|
|
||||||
// ___ __ _ _
|
|
||||||
// / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __
|
|
||||||
// / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ /
|
|
||||||
// / ___/ (_| | | \__ \ __/ /__| | | | | <
|
|
||||||
// \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ .
|
|
||||||
//
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Author: Kurt Sassenrath
|
|
||||||
// Module: msgpack
|
|
||||||
//
|
|
||||||
// Default packer implementation, which aims to deduce the best format to use
|
|
||||||
// for a given value. For example, if a 32-bit unsigned integer type only
|
|
||||||
// contains the value 5, a uint32 format would serialize into:
|
|
||||||
//
|
|
||||||
// 0xce, 0x00, 0x00, 0x00, 0x05
|
|
||||||
//
|
|
||||||
// Instead, the packer will note that this value could be stored in a positive
|
|
||||||
// fixint, which is simply:
|
|
||||||
//
|
|
||||||
// 0x05
|
|
||||||
//
|
|
||||||
// The same optimization will be applied to variable-length types, like strings,
|
|
||||||
// bytes, arrays, and maps.
|
|
||||||
//
|
|
||||||
// This flexibility comes at the cost of CPU instructions. For embedded targets,
|
|
||||||
// writer (to be renamed verbatim_packer in the future) may be a better choice.
|
|
||||||
//
|
|
||||||
// Future goals for this particular packer:
|
|
||||||
// 1. Support containers/ranges seamlessly.
|
|
||||||
// 2. Support packing of trivial POD structures without an explicit
|
|
||||||
// pack_adapter.
|
|
||||||
//
|
|
||||||
// Copyright (c) 2023 Kurt Sassenrath.
|
|
||||||
//
|
|
||||||
// License TBD.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
#ifndef msgpack_core_packer_1d5939e9c1498568
|
|
||||||
#define msgpack_core_packer_1d5939e9c1498568
|
|
||||||
|
|
||||||
#include "parselink/msgpack/core/detail/packable_concepts.h"
|
|
||||||
#include "parselink/msgpack/core/detail/builtin_packable_types.h"
|
|
||||||
#include "parselink/msgpack/core/detail/packable_ranges.h"
|
|
||||||
|
|
||||||
#include "parselink/msgpack/core/error.h"
|
|
||||||
#include "parselink/msgpack/core/format.h"
|
|
||||||
#include "parselink/msgpack/util/endianness.h"
|
|
||||||
|
|
||||||
#include <tl/expected.hpp>
|
|
||||||
|
|
||||||
#include <limits>
|
|
||||||
#include <type_traits>
|
|
||||||
|
|
||||||
namespace msgpack {
|
|
||||||
|
|
||||||
class packer {
|
|
||||||
// Replace with output_range?
|
|
||||||
using BufferType = std::span<std::byte>;
|
|
||||||
|
|
||||||
public:
|
|
||||||
constexpr packer(BufferType buff)
|
|
||||||
: buff_(buff)
|
|
||||||
, curr_(std::begin(buff_))
|
|
||||||
, end_(std::end(buff_)) {}
|
|
||||||
|
|
||||||
struct context {
|
|
||||||
decltype(std::begin(BufferType{})) out;
|
|
||||||
decltype(std::begin(BufferType{})) end;
|
|
||||||
|
|
||||||
constexpr auto remaining() noexcept {
|
|
||||||
return std::ranges::distance(out, end);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <packable T>
|
|
||||||
constexpr tl::expected<tl::monostate, error> pack(T const& v) noexcept {
|
|
||||||
auto rem = remaining();
|
|
||||||
using packer_impl = detail::packer_impl<T>;
|
|
||||||
decltype(rem) needed = packer_impl::total_size(v);
|
|
||||||
if (needed > rem) {
|
|
||||||
return tl::make_unexpected(error::out_of_space);
|
|
||||||
} else {
|
|
||||||
packer_impl::pack(v, *this);
|
|
||||||
return tl::monostate{};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <packable T>
|
|
||||||
constexpr tl::expected<tl::monostate, error> pack(T&& v) noexcept {
|
|
||||||
context ctx{curr_, end_};
|
|
||||||
// If packing is successful, it updates iterators.
|
|
||||||
auto ret = ctx.pack(std::forward<T>(v));
|
|
||||||
if (ret) {
|
|
||||||
curr_ = ctx.out;
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr auto tell() const noexcept {
|
|
||||||
return std::distance(std::begin(buff_), curr_);
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr auto subspan() const noexcept { return buff_.subspan(0, tell()); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
BufferType buff_;
|
|
||||||
decltype(std::begin(buff_)) curr_;
|
|
||||||
decltype(std::end(buff_)) end_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace msgpack
|
|
||||||
|
|
||||||
#endif // msgpack_core_packer_1d5939e9c1498568
|
|
||||||
@ -1,94 +0,0 @@
|
|||||||
//-----------------------------------------------------------------------------
|
|
||||||
// ___ __ _ _
|
|
||||||
// / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __
|
|
||||||
// / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ /
|
|
||||||
// / ___/ (_| | | \__ \ __/ /__| | | | | <
|
|
||||||
// \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ .
|
|
||||||
//
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Author: Kurt Sassenrath
|
|
||||||
// Module: msgpack
|
|
||||||
//
|
|
||||||
// Default unpacker implementation, utilizing pull-style parsing semantics for
|
|
||||||
// deserializing data with an expected structure akin to a schema.
|
|
||||||
//
|
|
||||||
// The default unpacker maintains some flexibility in how data was originally
|
|
||||||
// packed.
|
|
||||||
//
|
|
||||||
// E.g. If a std::uint8_t is requested, it is perfectly valid to read a uint32
|
|
||||||
// format, provided the stored value is less than 255.
|
|
||||||
//
|
|
||||||
// core/reader (to be renamed verbatim_unpacker) can be utilized in scenarios
|
|
||||||
// where code size is critical and exact type matching is not a considerable
|
|
||||||
// downside.
|
|
||||||
//
|
|
||||||
// For schemaless data, the token API can be used instead.
|
|
||||||
//
|
|
||||||
// Copyright (c) 2023 Kurt Sassenrath.
|
|
||||||
//
|
|
||||||
// License TBD.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
#ifndef msgpack_core_unpacker_910ec357d397ac7e
|
|
||||||
#define msgpack_core_unpacker_910ec357d397ac7e
|
|
||||||
|
|
||||||
#include "parselink/msgpack/core/detail/builtin_unpackable_types.h"
|
|
||||||
#include "parselink/msgpack/core/detail/unpackable_concepts.h"
|
|
||||||
#include "parselink/msgpack/core/error.h"
|
|
||||||
|
|
||||||
#include <tl/expected.hpp>
|
|
||||||
|
|
||||||
#include <cstddef>
|
|
||||||
#include <span>
|
|
||||||
|
|
||||||
namespace msgpack {
|
|
||||||
|
|
||||||
class unpacker {
|
|
||||||
using BufferType = std::span<std::byte const>;
|
|
||||||
|
|
||||||
public:
|
|
||||||
constexpr unpacker(BufferType buff)
|
|
||||||
: buff_(buff)
|
|
||||||
, curr_(std::begin(buff_))
|
|
||||||
, end_(std::end(buff_)) {}
|
|
||||||
|
|
||||||
// IDK if we need this yet
|
|
||||||
struct context {
|
|
||||||
decltype(std::begin(BufferType{})) in;
|
|
||||||
decltype(std::begin(BufferType{})) end;
|
|
||||||
|
|
||||||
constexpr auto remaining() noexcept {
|
|
||||||
return std::ranges::distance(in, end);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <supports_unpack T>
|
|
||||||
constexpr tl::expected<T, error> unpack() noexcept {
|
|
||||||
if (remaining() == 0)
|
|
||||||
return tl::make_unexpected(error::end_of_message);
|
|
||||||
using unpacker_impl = detail::unpacker_impl<T>;
|
|
||||||
return unpacker_impl::unpack(*this);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <supports_unpack T>
|
|
||||||
constexpr tl::expected<T, error> unpack() noexcept {
|
|
||||||
context ctx{curr_, end_};
|
|
||||||
auto ret = ctx.unpack<T>();
|
|
||||||
if (ret) {
|
|
||||||
curr_ = ctx.in;
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr auto remaining() noexcept {
|
|
||||||
return std::ranges::distance(curr_, end_);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
BufferType buff_;
|
|
||||||
decltype(std::begin(buff_)) curr_;
|
|
||||||
decltype(std::end(buff_)) end_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace msgpack
|
|
||||||
|
|
||||||
#endif // msgpack_core_unpacker_910ec357d397ac7e
|
|
||||||
@ -18,14 +18,14 @@
|
|||||||
#ifndef msgpack_core_writer_ce48a51aa6ed0858
|
#ifndef msgpack_core_writer_ce48a51aa6ed0858
|
||||||
#define msgpack_core_writer_ce48a51aa6ed0858
|
#define msgpack_core_writer_ce48a51aa6ed0858
|
||||||
|
|
||||||
#include <tl/expected.hpp>
|
|
||||||
|
|
||||||
#include "../util/endianness.h"
|
#include "../util/endianness.h"
|
||||||
#include "error.h"
|
#include "error.h"
|
||||||
#include "format.h"
|
#include "format.h"
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
|
#include <tl/expected.hpp>
|
||||||
|
|
||||||
namespace msgpack {
|
namespace msgpack {
|
||||||
|
|
||||||
enum class writer_error {
|
enum class writer_error {
|
||||||
@ -52,147 +52,19 @@ constexpr inline decltype(auto) write_bytes(
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace detail {
|
|
||||||
|
|
||||||
constexpr std::size_t bytes_needed_for_str(std::uint32_t v) {
|
|
||||||
if (v <= std::uint32_t(format::fixstr::mask)) return 0;
|
|
||||||
return std::bit_ceil(std::uint32_t((std::bit_width(v) + 7) >> 3));
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr std::size_t bytes_needed(std::uint64_t v) {
|
|
||||||
if (v <= std::uint64_t(format::positive_fixint::mask)) return 0;
|
|
||||||
return std::bit_ceil(std::uint64_t((std::bit_width(v) + 7) >> 3));
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr std::size_t bytes_needed(std::int64_t v) {
|
|
||||||
if (v < 0 && v >= -32) return 0;
|
|
||||||
auto width = 1 + std::numeric_limits<std::uint64_t>::digits
|
|
||||||
- (v < 0 ? std::countl_one(std::uint64_t(v))
|
|
||||||
: std::countl_zero(std::uint64_t(v)));
|
|
||||||
return std::bit_ceil(std::uint64_t((width + 7) >> 3));
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr std::byte get_format_signed(auto value) {
|
|
||||||
switch (bytes_needed(std::int64_t{value})) {
|
|
||||||
case 0: return std::byte(value);
|
|
||||||
case 1: return format::int8::marker;
|
|
||||||
case 2: return format::int16::marker;
|
|
||||||
case 4: return format::int32::marker;
|
|
||||||
default: return format::int64::marker;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr std::byte get_format(auto value) {
|
|
||||||
switch (bytes_needed(std::uint64_t{value})) {
|
|
||||||
case 0: return std::byte(value);
|
|
||||||
case 1: return format::uint8::marker;
|
|
||||||
case 2: return format::uint16::marker;
|
|
||||||
case 4: return format::uint32::marker;
|
|
||||||
default: return format::uint64::marker;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace detail
|
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct write_adapter {};
|
struct write_adapter {};
|
||||||
|
|
||||||
template <std::integral T>
|
template <std::integral T>
|
||||||
struct write_adapter<T> {
|
struct write_adapter<T> {
|
||||||
static constexpr auto size(T t) noexcept { return sizeof(T); }
|
static constexpr auto size(T) noexcept { return sizeof(T); }
|
||||||
|
|
||||||
template <typename Itr>
|
template <typename Itr>
|
||||||
static constexpr auto write(T t, Itr out) noexcept {
|
static constexpr auto write(T t, Itr out) noexcept {
|
||||||
return write_bytes(::detail::raw_cast(host_to_be(t)), out);
|
return write_bytes(detail::raw_cast(host_to_be(t)), out);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct deduced_write_adapter {};
|
|
||||||
|
|
||||||
template <std::signed_integral T>
|
|
||||||
struct deduced_write_adapter<T> {
|
|
||||||
static constexpr auto size(T value) noexcept {
|
|
||||||
return detail::bytes_needed(std::int64_t{value});
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Itr>
|
|
||||||
static constexpr auto write(T value, Itr out) noexcept {
|
|
||||||
auto bytes = ::detail::as_bytes(host_to_be(value));
|
|
||||||
auto sz = size(value);
|
|
||||||
for (std::size_t i = 0; i < sz; ++i) {
|
|
||||||
*out++ = *(bytes.end() - sz + i);
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
static constexpr auto format_hint(T value) noexcept {
|
|
||||||
return detail::get_format_signed(value);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <std::unsigned_integral T>
|
|
||||||
struct deduced_write_adapter<T> {
|
|
||||||
static constexpr auto size(T value) noexcept {
|
|
||||||
return detail::bytes_needed(std::uint64_t{value});
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Itr>
|
|
||||||
static constexpr auto write(T value, Itr out) noexcept {
|
|
||||||
auto bytes = ::detail::as_bytes(host_to_be(value));
|
|
||||||
auto sz = size(value);
|
|
||||||
for (std::size_t i = 0; i < sz; ++i) {
|
|
||||||
*out++ = *(bytes.end() - sz + i);
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Itr>
|
|
||||||
static constexpr auto write(T value, Itr out, std::size_t sz) noexcept {
|
|
||||||
auto bytes = ::detail::as_bytes(host_to_be(value));
|
|
||||||
for (std::size_t i = 0; i < sz; ++i) {
|
|
||||||
*out++ = *(bytes.end() - sz + i);
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
static constexpr auto format_hint(T value) noexcept {
|
|
||||||
return detail::get_format(value);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct deduced_write_adapter<std::string_view> {
|
|
||||||
static constexpr auto size(std::string_view value) noexcept {
|
|
||||||
return value.size() + detail::bytes_needed_for_str(value.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Itr>
|
|
||||||
static constexpr auto write(std::string_view value, Itr out) noexcept {
|
|
||||||
auto bytes_needed = detail::bytes_needed_for_str(value.size());
|
|
||||||
if (bytes_needed) {
|
|
||||||
out = deduced_write_adapter<std::uint64_t>::write(
|
|
||||||
value.size(), out, bytes_needed);
|
|
||||||
}
|
|
||||||
auto const* beg = reinterpret_cast<std::byte const*>(&*value.begin());
|
|
||||||
std::copy(beg, beg + value.size(), out);
|
|
||||||
return out + value.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
static constexpr auto format_hint(std::string_view value) noexcept {
|
|
||||||
switch (detail::bytes_needed_for_str(value.size())) {
|
|
||||||
case 0: return format::fixstr::marker | std::byte(value.size());
|
|
||||||
case 1: return format::str8::marker;
|
|
||||||
case 2: return format::str16::marker;
|
|
||||||
case 4:
|
|
||||||
default: return format::str32::marker;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <std::convertible_to<std::string_view> T>
|
|
||||||
struct deduced_write_adapter<T> : deduced_write_adapter<std::string_view> {};
|
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct write_adapter<std::string_view> {
|
struct write_adapter<std::string_view> {
|
||||||
static constexpr auto size(std::string_view str) noexcept {
|
static constexpr auto size(std::string_view str) noexcept {
|
||||||
@ -204,7 +76,7 @@ struct write_adapter<std::string_view> {
|
|||||||
std::byte const* beg =
|
std::byte const* beg =
|
||||||
reinterpret_cast<std::byte const*>(&*str.begin());
|
reinterpret_cast<std::byte const*>(&*str.begin());
|
||||||
std::copy(beg, beg + str.size(), out);
|
std::copy(beg, beg + str.size(), out);
|
||||||
return out + str.size();
|
return out += str.size();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -285,13 +157,6 @@ constexpr inline expected<typename F::first_type> pack_first(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
concept v2_adapted = requires(T const& t, std::byte* b) {
|
|
||||||
{ deduced_write_adapter<T>::size(t) } -> std::same_as<std::size_t>;
|
|
||||||
{ deduced_write_adapter<T>::write(t, b) } -> std::same_as<decltype(b)>;
|
|
||||||
{ deduced_write_adapter<T>::format_hint(t) } -> std::same_as<std::byte>;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <format_type F, typename Itr>
|
template <format_type F, typename Itr>
|
||||||
constexpr inline expected<Itr> write(
|
constexpr inline expected<Itr> write(
|
||||||
typename F::value_type&& value, Itr out, Itr const end) {
|
typename F::value_type&& value, Itr out, Itr const end) {
|
||||||
@ -363,6 +228,7 @@ public:
|
|||||||
constexpr expected<tl::monostate> write(
|
constexpr expected<tl::monostate> write(
|
||||||
typename F::value_type&& v) noexcept {
|
typename F::value_type&& v) noexcept {
|
||||||
using value_type = typename F::value_type;
|
using value_type = typename F::value_type;
|
||||||
|
if (curr == end) return tl::make_unexpected(error::out_of_space);
|
||||||
auto result = detail::write<F>(std::forward<value_type>(v), curr, end);
|
auto result = detail::write<F>(std::forward<value_type>(v), curr, end);
|
||||||
if (!result) {
|
if (!result) {
|
||||||
return tl::make_unexpected(result.error());
|
return tl::make_unexpected(result.error());
|
||||||
@ -372,20 +238,10 @@ public:
|
|||||||
return tl::monostate{};
|
return tl::monostate{};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deduced-type write, automatically chooses smallest representative format
|
template <typename T>
|
||||||
template <detail::v2_adapted T>
|
requires requires { typename detail::format_hint<T>::type; }
|
||||||
constexpr expected<tl::monostate> write2(T&& v) {
|
constexpr expected<tl::monostate> write(T&& v) {
|
||||||
using diff_type = std::iterator_traits<decltype(curr)>::difference_type;
|
return write<typename detail::format_hint<T>::type>(std::forward<T>(v));
|
||||||
auto const space_needed =
|
|
||||||
diff_type(1 + deduced_write_adapter<T>::size(v));
|
|
||||||
if (space_needed > std::distance(curr, end)) {
|
|
||||||
return tl::make_unexpected(error::out_of_space);
|
|
||||||
}
|
|
||||||
*curr++ = deduced_write_adapter<T>::format_hint(v);
|
|
||||||
if (space_needed > 1) {
|
|
||||||
curr = deduced_write_adapter<T>::write(v, curr);
|
|
||||||
}
|
|
||||||
return tl::monostate{};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr auto pos() const noexcept { return curr; }
|
constexpr auto pos() const noexcept { return curr; }
|
||||||
|
|||||||
@ -1,121 +0,0 @@
|
|||||||
//-----------------------------------------------------------------------------
|
|
||||||
// ___ __ _ _
|
|
||||||
// / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __
|
|
||||||
// / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ /
|
|
||||||
// / ___/ (_| | | \__ \ __/ /__| | | | | <
|
|
||||||
// \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ .
|
|
||||||
//
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Author: Kurt Sassenrath
|
|
||||||
// Module: msgpack
|
|
||||||
//
|
|
||||||
// Extra functionality: fmt formatters for msgpack types.
|
|
||||||
//
|
|
||||||
// Copyright (c) 2023 Kurt Sassenrath.
|
|
||||||
//
|
|
||||||
// License TBD.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
#ifndef msgpack_extra_formatters_d5fd427a7f749dcb
|
|
||||||
#define msgpack_extra_formatters_d5fd427a7f749dcb
|
|
||||||
|
|
||||||
#define FMT_VERSION
|
|
||||||
|
|
||||||
#ifdef FMT_VERSION
|
|
||||||
|
|
||||||
#include "parselink/msgpack/core/format.h"
|
|
||||||
|
|
||||||
#include <fmt/format.h>
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct fmt::formatter<msgpack::invalid> : fmt::formatter<std::string_view> {
|
|
||||||
template <typename FormatContext>
|
|
||||||
auto format(msgpack::nil const&, FormatContext& ctx) const noexcept {
|
|
||||||
return fmt::format_to(ctx.out(), "{{msgpack::invalid}}");
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct fmt::formatter<msgpack::nil> : fmt::formatter<std::string_view> {
|
|
||||||
template <typename FormatContext>
|
|
||||||
auto format(msgpack::nil const&, FormatContext& ctx) const {
|
|
||||||
return fmt::format_to(ctx.out(), "{{msgpack::nil}}");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct fmt::formatter<msgpack::map_desc> : fmt::formatter<std::string_view> {
|
|
||||||
template <typename FormatContext>
|
|
||||||
auto format(msgpack::map_desc const& value, FormatContext& ctx) const {
|
|
||||||
return fmt::format_to(
|
|
||||||
ctx.out(), "{{msgpack::map_desc: {}}}", value.count);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct fmt::formatter<msgpack::array_desc> : fmt::formatter<std::string_view> {
|
|
||||||
template <typename FormatContext>
|
|
||||||
auto format(msgpack::array_desc const& value, FormatContext& ctx) const {
|
|
||||||
return fmt::format_to(
|
|
||||||
ctx.out(), "{{msgpack::array_desc: {}}}", value.count);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#ifdef PARSELINK_MSGPACK_TOKEN_API
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct fmt::formatter<msgpack::token> {
|
|
||||||
template <typename ParseContext>
|
|
||||||
constexpr auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
|
||||||
return ctx.begin();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename FormatContext>
|
|
||||||
auto format(msgpack::token const& v, FormatContext& ctx) const {
|
|
||||||
using parselink::logging::themed_arg;
|
|
||||||
auto out = fmt::format_to(
|
|
||||||
ctx.out(), "<msgpack {} = ", themed_arg(v.type()));
|
|
||||||
switch (v.type()) {
|
|
||||||
case msgpack::format::type::unsigned_int:
|
|
||||||
fmt::format_to(
|
|
||||||
out, "{}", themed_arg(*(v.get<std::uint64_t>())));
|
|
||||||
break;
|
|
||||||
case msgpack::format::type::signed_int:
|
|
||||||
out = fmt::format_to(
|
|
||||||
out, "{}", themed_arg(*(v.get<std::uint64_t>())));
|
|
||||||
break;
|
|
||||||
case msgpack::format::type::boolean:
|
|
||||||
out = fmt::format_to(out, "{}", themed_arg(*(v.get<bool>())));
|
|
||||||
break;
|
|
||||||
case msgpack::format::type::string:
|
|
||||||
out = fmt::format_to(
|
|
||||||
out, "{}", themed_arg(*(v.get<std::string_view>())));
|
|
||||||
break;
|
|
||||||
case msgpack::format::type::binary:
|
|
||||||
out = fmt::format_to(out, "{}",
|
|
||||||
themed_arg(*(v.get<std::span<std::byte const>>())));
|
|
||||||
break;
|
|
||||||
case msgpack::format::type::map:
|
|
||||||
out = fmt::format_to(out, "(arity: {})",
|
|
||||||
themed_arg(v.get<msgpack::map_desc>()->count));
|
|
||||||
break;
|
|
||||||
case msgpack::format::type::array:
|
|
||||||
out = fmt::format_to(out, "(arity: {})",
|
|
||||||
themed_arg(v.get<msgpack::array_desc>()->count));
|
|
||||||
break;
|
|
||||||
case msgpack::format::type::nil:
|
|
||||||
out = fmt::format_to(out, "(nil)");
|
|
||||||
break;
|
|
||||||
case msgpack::format::type::invalid:
|
|
||||||
out = fmt::format_to(out, "(invalid)");
|
|
||||||
break;
|
|
||||||
default: break;
|
|
||||||
}
|
|
||||||
return fmt::format_to(out, ">");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // PARSELINK_MSGPACK_TOKEN_API
|
|
||||||
|
|
||||||
#endif // FMT_VERSION
|
|
||||||
|
|
||||||
#endif // msgpack_extra_formatters_d5fd427a7f749dcb
|
|
||||||
@ -1,267 +0,0 @@
|
|||||||
//-----------------------------------------------------------------------------
|
|
||||||
// ___ __ _ _
|
|
||||||
// / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __
|
|
||||||
// / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ /
|
|
||||||
// / ___/ (_| | | \__ \ __/ /__| | | | | <
|
|
||||||
// \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ .
|
|
||||||
//
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Author: Kurt Sassenrath
|
|
||||||
// Module: msgpack
|
|
||||||
//
|
|
||||||
// "Token" writing implementation, which supports both tokens and deduction for
|
|
||||||
// common types.
|
|
||||||
//
|
|
||||||
// Copyright (c) 2023 Kurt Sassenrath.
|
|
||||||
//
|
|
||||||
// License TBD.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
#ifndef msgpack_core_writer_ce48a51aa6ed0858
|
|
||||||
#define msgpack_core_writer_ce48a51aa6ed0858
|
|
||||||
|
|
||||||
#include "parselink/msgpack/core/error.h"
|
|
||||||
#include "parselink/msgpack/core/format.h"
|
|
||||||
#include "parselink/msgpack/util/endianness.h"
|
|
||||||
|
|
||||||
#include <tl/expected.hpp>
|
|
||||||
|
|
||||||
#include <limits>
|
|
||||||
#include <type_traits>
|
|
||||||
|
|
||||||
namespace msgpack {
|
|
||||||
|
|
||||||
enum class writer_error {
|
|
||||||
none,
|
|
||||||
unsupported,
|
|
||||||
not_implemented,
|
|
||||||
bad_value,
|
|
||||||
out_of_space
|
|
||||||
};
|
|
||||||
|
|
||||||
// Helper template for writing a non-integral datatype to an output.
|
|
||||||
// template <typename T>
|
|
||||||
// struct write_adapter {
|
|
||||||
// template <typename Itr>
|
|
||||||
// static constexpr tl::expected<Itr, error> write(T const& t);
|
|
||||||
//};
|
|
||||||
|
|
||||||
template <std::size_t N, typename Itr>
|
|
||||||
constexpr inline decltype(auto) write_bytes(
|
|
||||||
std::array<std::byte, N>&& data, Itr out) noexcept {
|
|
||||||
for (auto b : data) {
|
|
||||||
*out++ = b;
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct write_adapter {};
|
|
||||||
|
|
||||||
template <std::integral T>
|
|
||||||
struct write_adapter<T> {
|
|
||||||
static constexpr auto size(T) noexcept { return sizeof(T); }
|
|
||||||
|
|
||||||
template <typename Itr>
|
|
||||||
static constexpr auto write(T t, Itr out) noexcept {
|
|
||||||
return write_bytes(detail::raw_cast(host_to_be(t)), out);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct write_adapter<std::string_view> {
|
|
||||||
static constexpr auto size(std::string_view str) noexcept {
|
|
||||||
return str.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Itr>
|
|
||||||
static constexpr auto write(std::string_view str, Itr out) noexcept {
|
|
||||||
std::byte const* beg =
|
|
||||||
reinterpret_cast<std::byte const*>(&*str.begin());
|
|
||||||
std::copy(beg, beg + str.size(), out);
|
|
||||||
return out += str.size();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct write_adapter<std::span<std::byte const>> {
|
|
||||||
static constexpr auto size(std::span<std::byte const> bytes) noexcept {
|
|
||||||
return bytes.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Itr>
|
|
||||||
static constexpr auto write(
|
|
||||||
std::span<std::byte const> bytes, Itr out) noexcept {
|
|
||||||
std::copy(bytes.begin(), bytes.end(), out);
|
|
||||||
return out += bytes.size();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct write_adapter<map_desc> {
|
|
||||||
static constexpr auto value(map_desc desc) noexcept { return desc.count; }
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct write_adapter<array_desc> {
|
|
||||||
static constexpr auto value(array_desc desc) noexcept { return desc.count; }
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: These could be optimized away because invalid/nil never contain real
|
|
||||||
// data.
|
|
||||||
template <>
|
|
||||||
struct write_adapter<invalid> {
|
|
||||||
static constexpr auto value(invalid) noexcept { return 0; }
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct write_adapter<nil> {
|
|
||||||
static constexpr auto value(nil) noexcept { return 0; }
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace detail {
|
|
||||||
template <typename T>
|
|
||||||
using expected = tl::expected<T, error>;
|
|
||||||
|
|
||||||
template <format_type F>
|
|
||||||
constexpr inline std::size_t calculate_space(
|
|
||||||
typename F::value_type val) noexcept {
|
|
||||||
// At a minimum, one byte is needed to store the format.
|
|
||||||
std::size_t size = sizeof(typename F::first_type);
|
|
||||||
|
|
||||||
if (!is_fixtype<F>) {
|
|
||||||
++size; // For format
|
|
||||||
}
|
|
||||||
|
|
||||||
if constexpr (F::payload_type == format::payload::variable) {
|
|
||||||
size += write_adapter<typename F::value_type>::size(val);
|
|
||||||
}
|
|
||||||
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The "first type" is either the size of the variable length payload or
|
|
||||||
// a fixed-length value. Additionally, this "first type" may be inlined
|
|
||||||
// into the marker byte if it's a fix type.
|
|
||||||
|
|
||||||
template <format_type F>
|
|
||||||
constexpr inline expected<typename F::first_type> pack_first(
|
|
||||||
typename F::value_type value) noexcept {
|
|
||||||
using value_type = typename F::value_type;
|
|
||||||
if constexpr (F::payload_type == format::payload::variable) {
|
|
||||||
return typename F::first_type(write_adapter<value_type>::size(value));
|
|
||||||
} else {
|
|
||||||
if constexpr (requires { write_adapter<value_type>::value; }) {
|
|
||||||
return typename F::first_type(
|
|
||||||
write_adapter<value_type>::value(value));
|
|
||||||
} else {
|
|
||||||
return typename F::first_type(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <format_type F, typename Itr>
|
|
||||||
constexpr inline expected<Itr> write(
|
|
||||||
typename F::value_type&& value, Itr out, Itr const end) {
|
|
||||||
using diff_type = typename std::iterator_traits<Itr>::difference_type;
|
|
||||||
if (diff_type(calculate_space<F>(value)) > std::distance(out, end)) {
|
|
||||||
return tl::make_unexpected(error::out_of_space);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto marker = F::marker;
|
|
||||||
|
|
||||||
auto result = pack_first<F>(value);
|
|
||||||
if (!result) {
|
|
||||||
return tl::make_unexpected(result.error());
|
|
||||||
}
|
|
||||||
|
|
||||||
if constexpr (is_fixtype<F>) {
|
|
||||||
if (*result > 0xff) {
|
|
||||||
return tl::make_unexpected(error::bad_value);
|
|
||||||
}
|
|
||||||
if constexpr (F::flags & format::flag::apply_mask) {
|
|
||||||
marker |= std::byte(*result);
|
|
||||||
} else {
|
|
||||||
marker = std::byte(*result);
|
|
||||||
}
|
|
||||||
if ((marker & ~F::mask) != F::marker) {
|
|
||||||
return tl::make_unexpected(error::bad_value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
*out++ = marker;
|
|
||||||
|
|
||||||
if constexpr (!is_fixtype<F>) {
|
|
||||||
out = write_adapter<typename decltype(result)::value_type>::write(
|
|
||||||
*result, out);
|
|
||||||
}
|
|
||||||
|
|
||||||
if constexpr (F::payload_type == format::payload::variable) {
|
|
||||||
out = write_adapter<typename F::value_type>::write(value, out);
|
|
||||||
}
|
|
||||||
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct format_hint;
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct format_hint<std::uint8_t> {
|
|
||||||
using type = format::positive_fixint;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct format_hint<std::uint16_t> {
|
|
||||||
using type = format::uint16;
|
|
||||||
};
|
|
||||||
} // namespace detail
|
|
||||||
|
|
||||||
class writer {
|
|
||||||
public:
|
|
||||||
template <typename T>
|
|
||||||
using expected = detail::expected<T>;
|
|
||||||
|
|
||||||
constexpr writer(std::span<std::byte> dest)
|
|
||||||
: data(dest)
|
|
||||||
, curr(std::begin(data))
|
|
||||||
, end(std::end(data)) {}
|
|
||||||
|
|
||||||
template <format_type F>
|
|
||||||
constexpr expected<tl::monostate> write(
|
|
||||||
typename F::value_type&& v) noexcept {
|
|
||||||
using value_type = typename F::value_type;
|
|
||||||
if (curr == end) return tl::make_unexpected(error::out_of_space);
|
|
||||||
auto result = detail::write<F>(std::forward<value_type>(v), curr, end);
|
|
||||||
if (!result) {
|
|
||||||
return tl::make_unexpected(result.error());
|
|
||||||
}
|
|
||||||
|
|
||||||
curr = *result;
|
|
||||||
return tl::monostate{};
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
requires requires { typename detail::format_hint<T>::type; }
|
|
||||||
constexpr expected<tl::monostate> write(T&& v) {
|
|
||||||
return write<typename detail::format_hint<T>::type>(std::forward<T>(v));
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr auto pos() const noexcept { return curr; }
|
|
||||||
|
|
||||||
constexpr auto tell() const noexcept {
|
|
||||||
return std::distance(std::begin(data), curr);
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr auto subspan() const noexcept {
|
|
||||||
return std::span<std::byte>(std::begin(data), tell());
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::span<std::byte> data;
|
|
||||||
decltype(data)::iterator curr;
|
|
||||||
decltype(data)::iterator end;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace msgpack
|
|
||||||
|
|
||||||
#endif // msgpack_core_writer_ce48a51aa6ed0858
|
|
||||||
@ -99,11 +99,6 @@ constexpr auto raw_cast(T val) noexcept {
|
|||||||
return u.data;
|
return u.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
constexpr auto as_bytes(T val) noexcept {
|
|
||||||
return std::bit_cast<std::array<std::byte, sizeof(T)>>(val);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A helper function for converting a std::array into some arbitrary type.
|
* A helper function for converting a std::array into some arbitrary type.
|
||||||
* Beware, this must be used on trivial data types.
|
* Beware, this must be used on trivial data types.
|
||||||
|
|||||||
@ -1,35 +0,0 @@
|
|||||||
//-----------------------------------------------------------------------------
|
|
||||||
// ___ __ _ _
|
|
||||||
// / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __
|
|
||||||
// / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ /
|
|
||||||
// / ___/ (_| | | \__ \ __/ /__| | | | | <
|
|
||||||
// \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ .
|
|
||||||
//
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Author: Kurt Sassenrath
|
|
||||||
// Module: Proto
|
|
||||||
//
|
|
||||||
// Error definitions for protocol-related functionality
|
|
||||||
//
|
|
||||||
// Copyright (c) 2023 Kurt Sassenrath.
|
|
||||||
//
|
|
||||||
// License TBD.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
#ifndef parselink_proto_error_adef9d32bb51411a
|
|
||||||
#define parselink_proto_error_adef9d32bb51411a
|
|
||||||
|
|
||||||
namespace parselink {
|
|
||||||
namespace proto {
|
|
||||||
|
|
||||||
enum class error {
|
|
||||||
system_error,
|
|
||||||
incomplete,
|
|
||||||
unsupported,
|
|
||||||
bad_data,
|
|
||||||
too_large,
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace proto
|
|
||||||
} // namespace parselink
|
|
||||||
|
|
||||||
#endif // parselink_proto_error_adef9d32bb51411a
|
|
||||||
@ -21,6 +21,9 @@
|
|||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <span>
|
#include <span>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
#include <variant>
|
||||||
|
|
||||||
|
#include <tl/expected.hpp>
|
||||||
|
|
||||||
namespace parselink {
|
namespace parselink {
|
||||||
namespace proto {
|
namespace proto {
|
||||||
@ -34,40 +37,35 @@ namespace proto {
|
|||||||
// This may be revised in the future. The header could remain as msgpack, or
|
// This may be revised in the future. The header could remain as msgpack, or
|
||||||
// switch to something hand-crafted for saving bits.
|
// switch to something hand-crafted for saving bits.
|
||||||
|
|
||||||
struct base_message {};
|
struct error_message {
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
concept message_type = std::is_base_of_v<base_message, T>;
|
|
||||||
|
|
||||||
struct error_message : base_message {
|
|
||||||
std::uint32_t code; // An error code
|
std::uint32_t code; // An error code
|
||||||
std::string_view what; // A string
|
std::string_view what; // A string
|
||||||
};
|
};
|
||||||
|
|
||||||
// C->S: Request to (re)connect.
|
// C->S: Request to (re)connect.
|
||||||
struct connect_message : base_message {
|
struct connect_message {
|
||||||
|
std::uint32_t user_id; // The user id.
|
||||||
std::uint32_t version; // The version of the client.
|
std::uint32_t version; // The version of the client.
|
||||||
std::string_view user_id; // The user id.
|
|
||||||
std::span<std::byte> session_token; // An optional existing session token.
|
std::span<std::byte> session_token; // An optional existing session token.
|
||||||
};
|
};
|
||||||
|
|
||||||
// S->C: Challenge to authenticate client as user_id
|
// S->C: Challenge to authenticate client as user_id
|
||||||
struct challenge_message : base_message {
|
struct challenge_message {
|
||||||
std::uint32_t version;
|
std::uint32_t version;
|
||||||
std::span<std::byte> challenge;
|
std::span<std::byte> challenge;
|
||||||
};
|
};
|
||||||
|
|
||||||
// C->S: Calculated response to a challenge.
|
// C->S: Calculated response to a challenge.
|
||||||
struct response_message : base_message {
|
struct response_message {
|
||||||
std::span<std::byte> response;
|
std::span<std::byte> response;
|
||||||
};
|
};
|
||||||
|
|
||||||
// S->C: Session token.
|
// S->C: Session token.
|
||||||
struct session_established_message : base_message {
|
struct session_established_message {
|
||||||
std::span<std::byte> session_token;
|
std::span<std::byte> session_token;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct parser_data_message : base_message {
|
struct parser_data_message {
|
||||||
std::string_view opts;
|
std::string_view opts;
|
||||||
std::span<std::byte> payload;
|
std::span<std::byte> payload;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,60 +0,0 @@
|
|||||||
//-----------------------------------------------------------------------------
|
|
||||||
// ___ __ _ _
|
|
||||||
// / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __
|
|
||||||
// / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ /
|
|
||||||
// / ___/ (_| | | \__ \ __/ /__| | | | | <
|
|
||||||
// \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ .
|
|
||||||
//
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Author: Kurt Sassenrath
|
|
||||||
// Module: Proto
|
|
||||||
//
|
|
||||||
// Parser for extracting messages from msgpack.
|
|
||||||
//
|
|
||||||
// Note: Eventually, it would be nice to automatically generate the parser for
|
|
||||||
// message structures. Definitions below are templated to allow for this.
|
|
||||||
//
|
|
||||||
// Copyright (c) 2023 Kurt Sassenrath.
|
|
||||||
//
|
|
||||||
// License TBD.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
#ifndef parselink_proto_parser_ad351d41fe4c72dd
|
|
||||||
#define parselink_proto_parser_ad351d41fe4c72dd
|
|
||||||
|
|
||||||
#include "parselink/proto/error.h"
|
|
||||||
#include "parselink/proto/message.h"
|
|
||||||
|
|
||||||
#include <tl/expected.hpp>
|
|
||||||
|
|
||||||
namespace parselink {
|
|
||||||
namespace proto {
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Default parse template instantiation -- unsupported message.
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
template <message_type M>
|
|
||||||
tl::expected<M, error> parse(std::span<std::byte const> data) noexcept {
|
|
||||||
return tl::make_unexpected(error::unsupported);
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Currently-implemented message types.
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
#define PARSELINK_PROTO_SUPPORTED_MESSAGE(msgtype) \
|
|
||||||
tl::expected<msgtype, error> parse_##msgtype( \
|
|
||||||
std::span<std::byte const> data) noexcept; \
|
|
||||||
template <> \
|
|
||||||
constexpr tl::expected<msgtype, error> parse( \
|
|
||||||
std::span<std::byte const> data) noexcept { \
|
|
||||||
return parse_##msgtype(data); \
|
|
||||||
}
|
|
||||||
|
|
||||||
PARSELINK_PROTO_SUPPORTED_MESSAGE(connect_message);
|
|
||||||
|
|
||||||
#undef PARSELINK_PROTO_SUPPORTED_MESSAGE
|
|
||||||
|
|
||||||
} // namespace proto
|
|
||||||
} // namespace parselink
|
|
||||||
|
|
||||||
#endif // parselink_proto_parser_ad351d41fe4c72dd
|
|
||||||
@ -19,17 +19,14 @@
|
|||||||
#define session_07eae057feface79
|
#define session_07eae057feface79
|
||||||
|
|
||||||
#include "parselink/msgpack/token.h"
|
#include "parselink/msgpack/token.h"
|
||||||
#include "parselink/proto/error.h"
|
|
||||||
#include "parselink/proto/session_id.h"
|
|
||||||
|
|
||||||
#include <tl/expected.hpp>
|
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <span>
|
#include <span>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
#include <tl/expected.hpp>
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct std::hash<std::span<std::byte const>> {
|
struct std::hash<std::span<std::byte const>> {
|
||||||
constexpr static std::uint32_t seed_var = 0x811c9dc5;
|
constexpr static std::uint32_t seed_var = 0x811c9dc5;
|
||||||
@ -47,6 +44,14 @@ struct std::hash<std::span<std::byte const>> {
|
|||||||
namespace parselink {
|
namespace parselink {
|
||||||
namespace proto {
|
namespace proto {
|
||||||
|
|
||||||
|
enum class error {
|
||||||
|
system_error,
|
||||||
|
incomplete,
|
||||||
|
unsupported,
|
||||||
|
bad_data,
|
||||||
|
too_large,
|
||||||
|
};
|
||||||
|
|
||||||
// Structure containing header information parsed from a buffer.
|
// Structure containing header information parsed from a buffer.
|
||||||
struct header_info {
|
struct header_info {
|
||||||
std::uint32_t message_size; // Size of the message, minus the header.
|
std::uint32_t message_size; // Size of the message, minus the header.
|
||||||
@ -76,34 +81,36 @@ struct transparent_hash {
|
|||||||
class session {
|
class session {
|
||||||
public:
|
public:
|
||||||
using close_handle = std::function<void(std::string_view)>;
|
using close_handle = std::function<void(std::string_view)>;
|
||||||
session(std::string_view user_id) noexcept;
|
session(std::string_view user_id, close_handle hdl) noexcept;
|
||||||
|
|
||||||
~session();
|
~session();
|
||||||
|
|
||||||
session_id id() const noexcept { return id_; }
|
std::span<std::byte const> id() const noexcept { return id_; }
|
||||||
|
|
||||||
std::string_view user_id() const noexcept { return user_id_; }
|
std::string_view user_id() const noexcept { return user_id_; }
|
||||||
|
|
||||||
auto last_activity() const noexcept { return last_activity_; }
|
auto last_activity() const noexcept { return last_activity_; }
|
||||||
|
|
||||||
void update_last_activity(
|
void update_last_activity(
|
||||||
std::chrono::system_clock::time_point =
|
std::chrono::steady_clock::time_point =
|
||||||
std::chrono::system_clock::now()) noexcept {}
|
std::chrono::steady_clock::now()) noexcept {}
|
||||||
|
|
||||||
|
constexpr bool operator==(std::span<std::byte const> id) const noexcept {
|
||||||
|
return std::ranges::equal(id, id_);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
session_id id_;
|
std::array<std::byte, 32> id_;
|
||||||
std::string user_id_;
|
std::string user_id_;
|
||||||
std::chrono::system_clock::time_point last_activity_;
|
close_handle closer_;
|
||||||
|
std::chrono::steady_clock::time_point last_activity_;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct transparent_hash<std::string> : transparent_hash<std::string_view> {};
|
struct transparent_hash<session>
|
||||||
|
|
||||||
template <>
|
|
||||||
struct transparent_hash<session_id>
|
|
||||||
: transparent_hash<std::span<std::byte const>> {
|
: transparent_hash<std::span<std::byte const>> {
|
||||||
[[nodiscard]] auto operator()(session_id const& s) const {
|
[[nodiscard]] auto operator()(session const& s) const {
|
||||||
return std::hash<std::span<std::byte const>>{}(s.raw());
|
return std::hash<std::span<std::byte const>>{}(s.id());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -1,68 +0,0 @@
|
|||||||
//-----------------------------------------------------------------------------
|
|
||||||
// ___ __ _ _
|
|
||||||
// / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __
|
|
||||||
// / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ /
|
|
||||||
// / ___/ (_| | | \__ \ __/ /__| | | | | <
|
|
||||||
// \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ .
|
|
||||||
//
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Author: Kurt Sassenrath
|
|
||||||
// Module: Server
|
|
||||||
//
|
|
||||||
// Simple in-memory session manager.
|
|
||||||
//
|
|
||||||
// Copyright (c) 2023 Kurt Sassenrath.
|
|
||||||
//
|
|
||||||
// License TBD.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
#ifndef memory_session_manager_b3851872babe001d
|
|
||||||
#define memory_session_manager_b3851872babe001d
|
|
||||||
|
|
||||||
#include <boost/asio/io_context.hpp>
|
|
||||||
#include <boost/asio/strand.hpp>
|
|
||||||
#include <parselink/server/session_manager.h>
|
|
||||||
#include <unordered_set>
|
|
||||||
|
|
||||||
namespace parselink {
|
|
||||||
|
|
||||||
class memory_session_manager {
|
|
||||||
public:
|
|
||||||
memory_session_manager(boost::asio::io_context& ctx);
|
|
||||||
~memory_session_manager();
|
|
||||||
|
|
||||||
// Allocate a new session.
|
|
||||||
tl::expected<proto::session*, proto::error> create_session(
|
|
||||||
std::string_view user_id);
|
|
||||||
|
|
||||||
// Destroy an existing session.
|
|
||||||
tl::expected<tl::monostate, proto::error> destroy_session(
|
|
||||||
proto::session* session);
|
|
||||||
|
|
||||||
// Find a session by its user id.
|
|
||||||
tl::expected<proto::session*, proto::error> find(std::string_view user_id);
|
|
||||||
|
|
||||||
// Find a session by its ID.
|
|
||||||
tl::expected<proto::session*, proto::error> find(
|
|
||||||
proto::session_id const& session);
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::unordered_map<std::string, proto::session,
|
|
||||||
proto::transparent_hash<std::string>, std::equal_to<>>
|
|
||||||
sessions_;
|
|
||||||
|
|
||||||
std::unordered_map<proto::session_id, proto::session*,
|
|
||||||
proto::transparent_hash<proto::session_id>, std::equal_to<>>
|
|
||||||
lookup_by_sid_;
|
|
||||||
|
|
||||||
boost::asio::io_context& ctx_;
|
|
||||||
boost::asio::io_context::strand strand_;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Sanity check
|
|
||||||
static_assert(session_manager<memory_session_manager>);
|
|
||||||
static_assert(sync_session_manager<memory_session_manager>);
|
|
||||||
static_assert(!async_session_manager<memory_session_manager>);
|
|
||||||
|
|
||||||
} // namespace parselink
|
|
||||||
|
|
||||||
#endif // memory_session_manager_b3851872babe001d
|
|
||||||
@ -1,42 +0,0 @@
|
|||||||
//-----------------------------------------------------------------------------
|
|
||||||
// ___ __ _ _
|
|
||||||
// / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __
|
|
||||||
// / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ /
|
|
||||||
// / ___/ (_| | | \__ \ __/ /__| | | | | <
|
|
||||||
// \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ .
|
|
||||||
//
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Author: Kurt Sassenrath
|
|
||||||
// Module: Server
|
|
||||||
//
|
|
||||||
// Server interface.
|
|
||||||
//
|
|
||||||
// Copyright (c) 2023 Kurt Sassenrath.
|
|
||||||
//
|
|
||||||
// License TBD.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
#ifndef server_5b46f075be3caa00
|
|
||||||
#define server_5b46f075be3caa00
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
namespace parselink {
|
|
||||||
|
|
||||||
template <typename Server>
|
|
||||||
concept server_concept = requires(Server& srv) {
|
|
||||||
{ srv.run() } -> std::same_as<std::error_code>;
|
|
||||||
};
|
|
||||||
|
|
||||||
class server {
|
|
||||||
public:
|
|
||||||
virtual ~server() = default;
|
|
||||||
virtual std::error_code run() noexcept = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::unique_ptr<server> make_server(std::string_view address,
|
|
||||||
std::uint16_t user_port, std::uint16_t websocket_port);
|
|
||||||
|
|
||||||
} // namespace parselink
|
|
||||||
|
|
||||||
#endif // server_5b46f075be3caa00
|
|
||||||
@ -1,69 +0,0 @@
|
|||||||
//-----------------------------------------------------------------------------
|
|
||||||
// ___ __ _ _
|
|
||||||
// / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __
|
|
||||||
// / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ /
|
|
||||||
// / ___/ (_| | | \__ \ __/ /__| | | | | <
|
|
||||||
// \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ .
|
|
||||||
//
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Author: Kurt Sassenrath
|
|
||||||
// Module: Server
|
|
||||||
//
|
|
||||||
// Session manager concept.
|
|
||||||
//
|
|
||||||
// Copyright (c) 2023 Kurt Sassenrath.
|
|
||||||
//
|
|
||||||
// License TBD.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
#ifndef session_manager_779b9fc5781fb66f
|
|
||||||
#define session_manager_779b9fc5781fb66f
|
|
||||||
|
|
||||||
#include <boost/asio/awaitable.hpp>
|
|
||||||
#include <parselink/proto/session.h>
|
|
||||||
#include <parselink/proto/session_id.h>
|
|
||||||
|
|
||||||
namespace parselink {
|
|
||||||
|
|
||||||
using boost::asio::awaitable;
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
concept sync_session_manager =
|
|
||||||
requires(T& mgr, std::string_view sv, proto::session* s) {
|
|
||||||
{
|
|
||||||
mgr.create_session(sv)
|
|
||||||
} -> std::same_as<tl::expected<proto::session*, proto::error>>;
|
|
||||||
} && requires(T& mgr, proto::session* s) {
|
|
||||||
{
|
|
||||||
mgr.destroy_session(s)
|
|
||||||
} -> std::same_as<tl::expected<tl::monostate, proto::error>>;
|
|
||||||
} && requires(T& mgr, std::string_view user_id) {
|
|
||||||
{
|
|
||||||
mgr.find(user_id)
|
|
||||||
} -> std::same_as<tl::expected<proto::session*, proto::error>>;
|
|
||||||
} && requires(T& mgr, proto::session_id const& sid) {
|
|
||||||
{
|
|
||||||
mgr.find(sid)
|
|
||||||
} -> std::same_as<tl::expected<proto::session*, proto::error>>;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
concept async_session_manager = requires(T& mgr, proto::session* s) {
|
|
||||||
{
|
|
||||||
mgr.create_session()
|
|
||||||
} -> std::same_as<awaitable<tl::expected<proto::session*, proto::error>>>;
|
|
||||||
} && requires(T& mgr, proto::session* s) {
|
|
||||||
{
|
|
||||||
mgr.destroy_session(s)
|
|
||||||
} -> std::same_as<awaitable<tl::expected<tl::monostate, proto::error>>>;
|
|
||||||
} && requires(T& mgr, proto::session_id const& sid) {
|
|
||||||
{
|
|
||||||
mgr.find(sid)
|
|
||||||
} -> std::same_as<awaitable<tl::expected<proto::session*, proto::error>>>;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
concept session_manager = sync_session_manager<T> || async_session_manager<T>;
|
|
||||||
|
|
||||||
} // namespace parselink
|
|
||||||
|
|
||||||
#endif // session_manager_779b9fc5781fb66f
|
|
||||||
@ -1,156 +0,0 @@
|
|||||||
//-----------------------------------------------------------------------------
|
|
||||||
// ___ __ _ _
|
|
||||||
// / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __
|
|
||||||
// / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ /
|
|
||||||
// / ___/ (_| | | \__ \ __/ /__| | | | | <
|
|
||||||
// \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ .
|
|
||||||
//
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Author: Kurt Sassenrath
|
|
||||||
// Module: Utility
|
|
||||||
//
|
|
||||||
// Utility wrappers for dealing with files.
|
|
||||||
//
|
|
||||||
// Copyright (c) 2023 Kurt Sassenrath.
|
|
||||||
//
|
|
||||||
// License TBD.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
#ifndef utility_file_8b49e5e471a7c3e5
|
|
||||||
#define utility_file_8b49e5e471a7c3e5
|
|
||||||
|
|
||||||
#include <fmt/format.h>
|
|
||||||
#include <tl/expected.hpp>
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <span>
|
|
||||||
#include <system_error>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace parselink {
|
|
||||||
namespace utility {
|
|
||||||
namespace file {
|
|
||||||
|
|
||||||
struct [[nodiscard]] handle {
|
|
||||||
constexpr handle() noexcept = default;
|
|
||||||
|
|
||||||
constexpr handle(int fd) noexcept
|
|
||||||
: fd_(fd) {
|
|
||||||
if (fd_ < 0) fd_ = -errno;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr handle(handle const&) noexcept = delete;
|
|
||||||
|
|
||||||
constexpr handle(handle&& other) noexcept
|
|
||||||
: fd_(other.fd_) {
|
|
||||||
other.fd_ = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr handle& operator=(handle const&) noexcept = delete;
|
|
||||||
|
|
||||||
constexpr handle& operator=(handle&& other) noexcept {
|
|
||||||
close();
|
|
||||||
fd_ = other.fd_;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr operator bool() const noexcept { return fd_ >= 0; }
|
|
||||||
|
|
||||||
constexpr int operator*() const noexcept { return fd_; }
|
|
||||||
|
|
||||||
void close() {
|
|
||||||
if (*this) {
|
|
||||||
::close(fd_);
|
|
||||||
fd_ = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int fd_{-1};
|
|
||||||
};
|
|
||||||
|
|
||||||
inline tl::expected<handle, std::errc> open(
|
|
||||||
std::string_view path, int flags, int mode = {}) noexcept {
|
|
||||||
handle file(::open(std::string{path}.c_str(), flags, mode));
|
|
||||||
if (file) {
|
|
||||||
return file;
|
|
||||||
} else {
|
|
||||||
return tl::make_unexpected(static_cast<std::errc>(-*file));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline tl::expected<std::vector<std::byte>, std::errc> read_some(
|
|
||||||
handle file, std::size_t amount) noexcept {
|
|
||||||
std::vector<std::byte> data;
|
|
||||||
|
|
||||||
if (!file) {
|
|
||||||
return tl::make_unexpected(static_cast<std::errc>(-*file));
|
|
||||||
}
|
|
||||||
|
|
||||||
data.resize(amount);
|
|
||||||
|
|
||||||
auto amt_read = ::read(*file, data.data(), amount);
|
|
||||||
|
|
||||||
if (amt_read < data.size()) {
|
|
||||||
data.resize(amt_read);
|
|
||||||
}
|
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
requires(std::is_trivially_default_constructible_v<T> && sizeof(T) == 1)
|
|
||||||
inline tl::expected<std::vector<T>, std::errc> read(
|
|
||||||
handle const& file) noexcept {
|
|
||||||
std::vector<T> data;
|
|
||||||
|
|
||||||
if (!file) {
|
|
||||||
return tl::make_unexpected(static_cast<std::errc>(-*file));
|
|
||||||
}
|
|
||||||
|
|
||||||
auto cur = lseek(*file, 0, SEEK_CUR);
|
|
||||||
auto end = lseek(*file, 0, SEEK_END);
|
|
||||||
std::size_t amount = end - cur;
|
|
||||||
lseek(*file, SEEK_SET, cur);
|
|
||||||
data.resize(amount);
|
|
||||||
|
|
||||||
auto amt_read = ::read(*file, data.data(), amount);
|
|
||||||
|
|
||||||
if (amt_read < data.size()) {
|
|
||||||
data.resize(amt_read);
|
|
||||||
}
|
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
requires(std::is_trivially_default_constructible_v<T> && sizeof(T) == 1)
|
|
||||||
inline tl::expected<std::size_t, std::errc> write(
|
|
||||||
handle const& file, std::span<T> data) noexcept {
|
|
||||||
if (!file) {
|
|
||||||
return tl::make_unexpected(static_cast<std::errc>(-*file));
|
|
||||||
}
|
|
||||||
|
|
||||||
lseek(*file, 0, SEEK_SET);
|
|
||||||
|
|
||||||
auto result = ftruncate(*file, 0);
|
|
||||||
|
|
||||||
if (result < 0) {
|
|
||||||
return tl::make_unexpected(static_cast<std::errc>(errno));
|
|
||||||
}
|
|
||||||
|
|
||||||
auto amt_written = ::write(*file, data.data(), data.size());
|
|
||||||
|
|
||||||
if (result < 0) {
|
|
||||||
return tl::make_unexpected(static_cast<std::errc>(errno));
|
|
||||||
}
|
|
||||||
|
|
||||||
return static_cast<std::size_t>(amt_written);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace file
|
|
||||||
} // namespace utility
|
|
||||||
} // namespace parselink
|
|
||||||
|
|
||||||
#endif // utility_file_8b49e5e471a7c3e5
|
|
||||||
@ -18,7 +18,6 @@ cc_binary(
|
|||||||
"//include/parselink:utility",
|
"//include/parselink:utility",
|
||||||
"//source/logging",
|
"//source/logging",
|
||||||
"//source/proto",
|
"//source/proto",
|
||||||
"//source/server",
|
|
||||||
"@boost//:beast",
|
"@boost//:beast",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|||||||
@ -3,7 +3,6 @@
|
|||||||
cc_library(
|
cc_library(
|
||||||
name = "proto",
|
name = "proto",
|
||||||
srcs = [
|
srcs = [
|
||||||
"parser.cpp",
|
|
||||||
"session.cpp",
|
"session.cpp",
|
||||||
"session_id.cpp",
|
"session_id.cpp",
|
||||||
],
|
],
|
||||||
|
|||||||
@ -1,70 +0,0 @@
|
|||||||
//-----------------------------------------------------------------------------
|
|
||||||
// ___ __ _ _
|
|
||||||
// / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __
|
|
||||||
// / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ /
|
|
||||||
// / ___/ (_| | | \__ \ __/ /__| | | | | <
|
|
||||||
// \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ .
|
|
||||||
//
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Author: Kurt Sassenrath
|
|
||||||
// Module: Proto
|
|
||||||
//
|
|
||||||
// Parser implementations for various msgpack messages.
|
|
||||||
//
|
|
||||||
// Copyright (c) 2023 Kurt Sassenrath.
|
|
||||||
//
|
|
||||||
// License TBD.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
#include "parselink/proto/parser.h"
|
|
||||||
#include "parselink/logging.h"
|
|
||||||
|
|
||||||
#include "parselink/msgpack/core/unpacker.h"
|
|
||||||
|
|
||||||
namespace parselink {
|
|
||||||
namespace proto {
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
logging::logger logger{"parser"};
|
|
||||||
|
|
||||||
constexpr auto assign(auto& param, msgpack::unpacker& unpacker) noexcept
|
|
||||||
-> tl::expected<void, error> {
|
|
||||||
auto do_assign = [¶m](auto&& v) { param = v; };
|
|
||||||
return unpacker.unpack<std::decay_t<decltype(param)>>()
|
|
||||||
.map(do_assign)
|
|
||||||
.map_error([](auto) { return error::bad_data; });
|
|
||||||
}
|
|
||||||
|
|
||||||
} // anonymous namespace
|
|
||||||
|
|
||||||
tl::expected<connect_message, error> parse_connect_message(
|
|
||||||
std::span<std::byte const> data) noexcept {
|
|
||||||
msgpack::unpacker unpacker(data);
|
|
||||||
connect_message msg;
|
|
||||||
|
|
||||||
constexpr static tl::unexpected<error> bad_data(error::bad_data);
|
|
||||||
|
|
||||||
auto type = unpacker.unpack<std::string_view>();
|
|
||||||
if (!type || type != "connect") return bad_data;
|
|
||||||
|
|
||||||
auto entries = unpacker.unpack<msgpack::map_desc>();
|
|
||||||
if (!entries) return bad_data;
|
|
||||||
|
|
||||||
for (auto i = entries->count; i != 0; --i) {
|
|
||||||
auto key = unpacker.unpack<std::string_view>();
|
|
||||||
if (!key) return bad_data;
|
|
||||||
if (key == "version") {
|
|
||||||
if (auto val = assign(msg.version, unpacker)) continue;
|
|
||||||
} else if (key == "user_id") {
|
|
||||||
if (auto val = assign(msg.user_id, unpacker)) continue;
|
|
||||||
}
|
|
||||||
logger.debug("Unknown key: {}", *key);
|
|
||||||
return bad_data;
|
|
||||||
}
|
|
||||||
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace proto
|
|
||||||
} // namespace parselink
|
|
||||||
@ -19,13 +19,12 @@
|
|||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
#include "parselink/proto/session.h"
|
#include "parselink/proto/session.h"
|
||||||
|
|
||||||
|
#include "hydrogen.h"
|
||||||
#include "parselink/logging.h"
|
#include "parselink/logging.h"
|
||||||
#include "parselink/msgpack/token.h"
|
#include "parselink/msgpack/token.h"
|
||||||
|
|
||||||
#include <fmt/ranges.h>
|
#include <fmt/ranges.h>
|
||||||
|
|
||||||
#include "hydrogen.h"
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
void ensure_initialized() {
|
void ensure_initialized() {
|
||||||
@ -188,11 +187,15 @@ tl::expected<connect_info, error> proto::parse_connect(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
session::session(std::string_view user_id) noexcept
|
session::session(std::string_view user_id, close_handle hdl) noexcept
|
||||||
: id_()
|
: id_(get_random_bytes<32>())
|
||||||
, user_id_(std::string{user_id})
|
, user_id_(std::string{user_id})
|
||||||
, last_activity_(std::chrono::system_clock::now()) {
|
, closer_(std::move(hdl))
|
||||||
logger.debug("New session with id {} created for {}", id_.raw(), user_id_);
|
, last_activity_(std::chrono::steady_clock::now()) {
|
||||||
|
logger.debug("New session with id {} created for {} {:x}", id_, user_id_,
|
||||||
|
transparent_hash<session>{}(*this));
|
||||||
}
|
}
|
||||||
|
|
||||||
session::~session() {}
|
session::~session() {
|
||||||
|
if (closer_) closer_("Destroyed");
|
||||||
|
}
|
||||||
|
|||||||
@ -21,15 +21,9 @@
|
|||||||
#include "parselink/server.h"
|
#include "parselink/server.h"
|
||||||
|
|
||||||
#include "parselink/logging.h"
|
#include "parselink/logging.h"
|
||||||
#include "parselink/msgpack/core/packer.h"
|
#include "parselink/msgpack/token/reader.h"
|
||||||
#include "parselink/msgpack/core/unpacker.h"
|
#include "parselink/msgpack/token/views.h"
|
||||||
#include "parselink/proto/parser.h"
|
|
||||||
#include "parselink/proto/session.h"
|
#include "parselink/proto/session.h"
|
||||||
#include "parselink/utility/file.h"
|
|
||||||
|
|
||||||
#include <fmt/ranges.h>
|
|
||||||
|
|
||||||
#include "hydrogen.h"
|
|
||||||
#include <boost/asio/as_tuple.hpp>
|
#include <boost/asio/as_tuple.hpp>
|
||||||
#include <boost/asio/bind_executor.hpp>
|
#include <boost/asio/bind_executor.hpp>
|
||||||
#include <boost/asio/co_spawn.hpp>
|
#include <boost/asio/co_spawn.hpp>
|
||||||
@ -44,6 +38,8 @@
|
|||||||
#include <boost/asio/write.hpp>
|
#include <boost/asio/write.hpp>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
|
|
||||||
|
#include <fmt/ranges.h>
|
||||||
|
|
||||||
using namespace parselink;
|
using namespace parselink;
|
||||||
#include <fmt/ranges.h>
|
#include <fmt/ranges.h>
|
||||||
|
|
||||||
@ -75,13 +71,6 @@ struct fmt::formatter<boost::system::error_code>
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
|
||||||
struct fmt::formatter<tl::monostate> : fmt::formatter<std::string_view> {
|
|
||||||
constexpr auto format(auto const&, auto& ctx) const {
|
|
||||||
return fmt::formatter<std::string_view>::format(".", ctx);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct fmt::formatter<msgpack::token> {
|
struct fmt::formatter<msgpack::token> {
|
||||||
template <typename ParseContext>
|
template <typename ParseContext>
|
||||||
@ -161,11 +150,8 @@ namespace {
|
|||||||
logging::logger logger("server");
|
logging::logger logger("server");
|
||||||
constexpr auto no_ex_coro = net::as_tuple(use_awaitable);
|
constexpr auto no_ex_coro = net::as_tuple(use_awaitable);
|
||||||
constexpr auto no_ex_defer = net::as_tuple(deferred);
|
constexpr auto no_ex_defer = net::as_tuple(deferred);
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
#include <parselink/server/memory_session_manager.h>
|
|
||||||
|
|
||||||
class user_connection;
|
class user_connection;
|
||||||
|
|
||||||
class monolithic_server : public server {
|
class monolithic_server : public server {
|
||||||
@ -177,17 +163,17 @@ public:
|
|||||||
|
|
||||||
net::awaitable<tl::expected<proto::session*, proto::error>> create_session(
|
net::awaitable<tl::expected<proto::session*, proto::error>> create_session(
|
||||||
std::shared_ptr<user_connection> const& conn,
|
std::shared_ptr<user_connection> const& conn,
|
||||||
proto::connect_message const& info);
|
proto::connect_info const& info);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
awaitable<void> user_listen();
|
awaitable<void> user_listen();
|
||||||
|
|
||||||
tl::expected<tl::monostate, std::errc> load_keys() noexcept;
|
std::unordered_set<proto::session, proto::transparent_hash<proto::session>,
|
||||||
|
std::equal_to<>>
|
||||||
|
sessions_;
|
||||||
|
|
||||||
hydro_kx_keypair kp_;
|
|
||||||
net::io_context io_context_;
|
net::io_context io_context_;
|
||||||
net::io_context::strand session_strand_;
|
net::io_context::strand session_strand_;
|
||||||
memory_session_manager session_mgr_;
|
|
||||||
net::ip::address addr_;
|
net::ip::address addr_;
|
||||||
std::uint16_t user_port_;
|
std::uint16_t user_port_;
|
||||||
std::uint16_t websocket_port_;
|
std::uint16_t websocket_port_;
|
||||||
@ -275,38 +261,35 @@ public:
|
|||||||
co_return;
|
co_return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto connect_info = proto::parse<proto::connect_message>(msgbuf);
|
auto reader = msgpack::token_reader(msgbuf);
|
||||||
|
std::array<msgpack::token, 32> tokens;
|
||||||
|
auto connect_info =
|
||||||
|
reader.read_some(tokens)
|
||||||
|
.map_error([](auto) { return proto::error::bad_data; })
|
||||||
|
.and_then(proto::parse_connect);
|
||||||
|
|
||||||
if (!connect_info) {
|
if (!connect_info) {
|
||||||
logger.error("Session failed: {}", connect_info.error());
|
logger.error("Session failed: {}", connect_info.error());
|
||||||
co_return;
|
co_return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto session = co_await server_.create_session(
|
co_await server_.create_session(shared_from_this(), *connect_info);
|
||||||
shared_from_this(), *connect_info);
|
|
||||||
if (!session) {
|
|
||||||
co_return;
|
|
||||||
}
|
|
||||||
session_ = *session;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class state { init, authenticated, active };
|
enum class state { init, authenticated, active };
|
||||||
|
|
||||||
monolithic_server& server_;
|
monolithic_server& server_;
|
||||||
net::ip::tcp::socket socket_;
|
net::ip::tcp::socket socket_;
|
||||||
proto::session* session_{};
|
proto::session* session_;
|
||||||
};
|
};
|
||||||
|
|
||||||
monolithic_server::monolithic_server(std::string_view address,
|
monolithic_server::monolithic_server(std::string_view address,
|
||||||
std::uint16_t user_port, std::uint16_t websocket_port)
|
std::uint16_t user_port, std::uint16_t websocket_port)
|
||||||
: io_context_{1}
|
: io_context_{1}
|
||||||
, session_strand_(io_context_)
|
, session_strand_(io_context_)
|
||||||
, session_mgr_(io_context_)
|
|
||||||
, addr_(net::ip::address::from_string(std::string{address}))
|
, addr_(net::ip::address::from_string(std::string{address}))
|
||||||
, user_port_{user_port}
|
, user_port_{user_port}
|
||||||
, websocket_port_{websocket_port} {
|
, websocket_port_{websocket_port} {
|
||||||
logger.debug("Loaded keys: {}", load_keys());
|
|
||||||
|
|
||||||
logger.debug("Creating monolithic_server(address = {}, user_port = {}, "
|
logger.debug("Creating monolithic_server(address = {}, user_port = {}, "
|
||||||
"websocket_port = {})",
|
"websocket_port = {})",
|
||||||
address, user_port_, websocket_port_);
|
address, user_port_, websocket_port_);
|
||||||
@ -338,79 +321,17 @@ std::error_code monolithic_server::run() noexcept {
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
tl::expected<tl::monostate, std::errc> monolithic_server::load_keys() noexcept {
|
|
||||||
std::string_view filename = "/home/rihya/server_kp.keys";
|
|
||||||
|
|
||||||
enum class result { loaded, generated };
|
|
||||||
|
|
||||||
auto load_key = [this](auto const& raw) -> tl::expected<result, std::errc> {
|
|
||||||
msgpack::unpacker unpacker(raw);
|
|
||||||
|
|
||||||
auto pk = unpacker.unpack<std::span<std::byte const, sizeof(kp_.pk)>>();
|
|
||||||
if (!pk) return tl::make_unexpected(std::errc::bad_message);
|
|
||||||
auto sk = unpacker.unpack<std::span<std::byte const, sizeof(kp_.sk)>>();
|
|
||||||
if (!sk) return tl::make_unexpected(std::errc::bad_message);
|
|
||||||
|
|
||||||
std::ranges::transform(pk->begin(), pk->end(), std::begin(kp_.pk),
|
|
||||||
[](auto c) { return std::bit_cast<unsigned char>(c); });
|
|
||||||
|
|
||||||
std::ranges::transform(sk->begin(), sk->end(), std::begin(kp_.sk),
|
|
||||||
[](auto c) { return std::bit_cast<unsigned char>(c); });
|
|
||||||
|
|
||||||
return result::loaded;
|
|
||||||
};
|
|
||||||
|
|
||||||
auto generate_keys =
|
|
||||||
[this](auto const& err) -> tl::expected<result, std::errc> {
|
|
||||||
logger.warning("Could not load server keys, generating a keypair");
|
|
||||||
hydro_kx_keygen(&kp_);
|
|
||||||
return result::generated;
|
|
||||||
};
|
|
||||||
|
|
||||||
auto commit = [this](auto const& handle)
|
|
||||||
-> tl::expected<tl::monostate, std::errc> {
|
|
||||||
std::vector<std::byte> buff(4 + sizeof(kp_.pk) + sizeof(kp_.sk));
|
|
||||||
std::span<std::byte> pk(
|
|
||||||
reinterpret_cast<std::byte*>(kp_.pk), sizeof(kp_.pk));
|
|
||||||
std::span<std::byte> sk(
|
|
||||||
reinterpret_cast<std::byte*>(kp_.sk), sizeof(kp_.pk));
|
|
||||||
msgpack::packer packer(buff);
|
|
||||||
packer.pack(pk);
|
|
||||||
packer.pack(sk);
|
|
||||||
return utility::file::write(handle, packer.subspan()).map([](auto) {
|
|
||||||
logger.info("Wrote new keys to disk");
|
|
||||||
return tl::monostate{};
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
auto load_or_generate_keys = [&load_key, &generate_keys, &commit](
|
|
||||||
auto const& handle)
|
|
||||||
-> tl::expected<tl::monostate, std::errc> {
|
|
||||||
return utility::file::read<std::byte>(handle)
|
|
||||||
.and_then(load_key)
|
|
||||||
.or_else(generate_keys)
|
|
||||||
.and_then([&handle, &commit](auto r)
|
|
||||||
-> tl::expected<tl::monostate, std::errc> {
|
|
||||||
if (r == result::generated) {
|
|
||||||
return commit(handle);
|
|
||||||
}
|
|
||||||
return tl::monostate{};
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return utility::file::open(filename, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR)
|
|
||||||
.and_then(load_or_generate_keys);
|
|
||||||
}
|
|
||||||
|
|
||||||
net::awaitable<tl::expected<proto::session*, proto::error>>
|
net::awaitable<tl::expected<proto::session*, proto::error>>
|
||||||
monolithic_server::create_session(std::shared_ptr<user_connection> const& conn,
|
monolithic_server::create_session(std::shared_ptr<user_connection> const& conn,
|
||||||
proto::connect_message const& info) {
|
proto::connect_info const& info) {
|
||||||
// Move to session strand.
|
// Move to session strand.
|
||||||
co_await net::post(session_strand_,
|
co_await net::post(session_strand_,
|
||||||
net::bind_executor(session_strand_, use_awaitable));
|
net::bind_executor(session_strand_, use_awaitable));
|
||||||
auto session = session_mgr_.create_session(info.user_id);
|
|
||||||
logger.info("Created session: {}", session);
|
proto::session session(info.user_id, {});
|
||||||
co_return session;
|
|
||||||
|
// Now, update the map of sessions
|
||||||
|
co_return tl::make_unexpected(proto::error::unsupported);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<server> parselink::make_server(std::string_view address,
|
std::unique_ptr<server> parselink::make_server(std::string_view address,
|
||||||
|
|||||||
@ -1,20 +0,0 @@
|
|||||||
# parselink
|
|
||||||
|
|
||||||
cc_library(
|
|
||||||
name = "server",
|
|
||||||
srcs = [
|
|
||||||
"memory_session_manager.cpp",
|
|
||||||
],
|
|
||||||
deps = [
|
|
||||||
"//include/parselink:proto",
|
|
||||||
"//include/parselink:msgpack",
|
|
||||||
"//include/parselink:server",
|
|
||||||
"//source/logging",
|
|
||||||
"@hydrogen",
|
|
||||||
"@boost//:asio",
|
|
||||||
],
|
|
||||||
visibility = [
|
|
||||||
# TODO: Fix visibility
|
|
||||||
"//visibility:public",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
@ -1,84 +0,0 @@
|
|||||||
//-----------------------------------------------------------------------------
|
|
||||||
// ___ __ _ _
|
|
||||||
// / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __
|
|
||||||
// / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ /
|
|
||||||
// / ___/ (_| | | \__ \ __/ /__| | | | | <
|
|
||||||
// \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ .
|
|
||||||
//
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Author: Kurt Sassenrath
|
|
||||||
// Module: Server
|
|
||||||
//
|
|
||||||
// Simple in-memory session manager.
|
|
||||||
//
|
|
||||||
// Copyright (c) 2023 Kurt Sassenrath.
|
|
||||||
//
|
|
||||||
// License TBD.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
#include <parselink/logging.h>
|
|
||||||
#include <parselink/server/memory_session_manager.h>
|
|
||||||
|
|
||||||
#include <fmt/chrono.h>
|
|
||||||
|
|
||||||
using namespace parselink;
|
|
||||||
using namespace std::chrono_literals;
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
logging::logger logger("memory_session_manager");
|
|
||||||
|
|
||||||
constexpr std::size_t max_open_sessions = 1000;
|
|
||||||
constexpr auto expires_time = 1min;
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
memory_session_manager::memory_session_manager(boost::asio::io_context& ctx)
|
|
||||||
: ctx_(ctx)
|
|
||||||
, strand_(ctx) {
|
|
||||||
logger.trace("Creating: {}", this);
|
|
||||||
}
|
|
||||||
|
|
||||||
memory_session_manager::~memory_session_manager() {
|
|
||||||
logger.trace("Destroying: {}", this);
|
|
||||||
}
|
|
||||||
|
|
||||||
tl::expected<proto::session*, proto::error>
|
|
||||||
memory_session_manager::create_session(std::string_view user_id) {
|
|
||||||
auto [itr, inserted] = sessions_.try_emplace(std::string{user_id}, user_id);
|
|
||||||
if (!inserted) {
|
|
||||||
logger.debug("Session already found for {}, last activity"
|
|
||||||
" {:%Y-%m-%d %H:%M:%S}",
|
|
||||||
user_id, fmt::gmtime(itr->second.last_activity()));
|
|
||||||
if (itr->second.last_activity() + expires_time
|
|
||||||
< std::chrono::system_clock::now()) {
|
|
||||||
logger.debug("Session expired. Creating new session");
|
|
||||||
itr->second = proto::session{user_id};
|
|
||||||
}
|
|
||||||
return &itr->second;
|
|
||||||
}
|
|
||||||
lookup_by_sid_.emplace(itr->second.id(), &itr->second);
|
|
||||||
return &itr->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
tl::expected<tl::monostate, proto::error>
|
|
||||||
memory_session_manager::destroy_session(proto::session* s) {
|
|
||||||
return tl::make_unexpected(proto::error::unsupported);
|
|
||||||
}
|
|
||||||
|
|
||||||
tl::expected<proto::session*, proto::error> memory_session_manager::find(
|
|
||||||
std::string_view user_id) {
|
|
||||||
auto x = sessions_.find(user_id);
|
|
||||||
if (x != sessions_.end()) {
|
|
||||||
return &x->second;
|
|
||||||
}
|
|
||||||
return tl::make_unexpected(proto::error::unsupported);
|
|
||||||
}
|
|
||||||
|
|
||||||
tl::expected<proto::session*, proto::error> memory_session_manager::find(
|
|
||||||
proto::session_id const& sid) {
|
|
||||||
auto x = lookup_by_sid_.find(sid);
|
|
||||||
if (x != lookup_by_sid_.end()) {
|
|
||||||
return x->second;
|
|
||||||
}
|
|
||||||
return tl::make_unexpected(proto::error::unsupported);
|
|
||||||
}
|
|
||||||
@ -1,11 +1,10 @@
|
|||||||
|
|
||||||
cc_library(
|
cc_library(
|
||||||
name = "common",
|
name = "test_deps",
|
||||||
srcs = [
|
srcs = [
|
||||||
"test_main.cpp",
|
"test_main.cpp",
|
||||||
|
"rng.h",
|
||||||
],
|
],
|
||||||
includes = ["include"],
|
|
||||||
hdrs = glob(["include/*.h"]),
|
|
||||||
deps = [
|
deps = [
|
||||||
"//include/parselink:msgpack",
|
"//include/parselink:msgpack",
|
||||||
"@expected",
|
"@expected",
|
||||||
@ -13,53 +12,47 @@ cc_library(
|
|||||||
"@magic_enum",
|
"@magic_enum",
|
||||||
"@ut",
|
"@ut",
|
||||||
],
|
],
|
||||||
visibility = ["__subpackages__"],
|
|
||||||
)
|
)
|
||||||
|
|
||||||
cc_test(
|
cc_test(
|
||||||
name = "reader",
|
name = "reader",
|
||||||
size = "small",
|
|
||||||
srcs = [
|
srcs = [
|
||||||
"test_reader_relaxed.cpp",
|
"test_reader_relaxed.cpp",
|
||||||
"test_reader_strict.cpp",
|
"test_reader_strict.cpp",
|
||||||
],
|
],
|
||||||
deps = ["common"],
|
deps = ["test_deps"],
|
||||||
)
|
)
|
||||||
|
|
||||||
cc_test(
|
cc_test(
|
||||||
name = "writer",
|
name = "writer",
|
||||||
size = "small",
|
|
||||||
srcs = [
|
srcs = [
|
||||||
"test_writer.cpp",
|
"test_writer.cpp",
|
||||||
],
|
],
|
||||||
deps = ["common"],
|
deps = ["test_deps"],
|
||||||
)
|
)
|
||||||
|
|
||||||
cc_test(
|
cc_test(
|
||||||
name = "token",
|
name = "token",
|
||||||
size = "small",
|
|
||||||
srcs = [
|
srcs = [
|
||||||
"test_token.cpp",
|
"test_token.cpp",
|
||||||
],
|
],
|
||||||
deps = ["common"],
|
deps = ["test_deps"],
|
||||||
)
|
)
|
||||||
|
|
||||||
cc_test(
|
cc_test(
|
||||||
name = "token_reader",
|
name = "token_reader",
|
||||||
size = "small",
|
|
||||||
srcs = [
|
srcs = [
|
||||||
"test_token_reader.cpp",
|
"test_token_reader.cpp",
|
||||||
],
|
],
|
||||||
deps = ["common"],
|
deps = ["test_deps"],
|
||||||
)
|
)
|
||||||
|
|
||||||
cc_test(
|
cc_test(
|
||||||
name = "token_views",
|
name = "token_views",
|
||||||
size = "small",
|
|
||||||
srcs = [
|
srcs = [
|
||||||
"test_token_views.cpp",
|
"test_token_views.cpp",
|
||||||
],
|
],
|
||||||
deps = ["common"],
|
deps = ["test_deps"],
|
||||||
)
|
)
|
||||||
|
|
||||||
cc_binary(
|
cc_binary(
|
||||||
@ -67,13 +60,5 @@ cc_binary(
|
|||||||
srcs = [
|
srcs = [
|
||||||
"test_speed.cpp",
|
"test_speed.cpp",
|
||||||
],
|
],
|
||||||
deps = ["common"],
|
deps = ["test_deps"],
|
||||||
)
|
|
||||||
|
|
||||||
cc_binary(
|
|
||||||
name = "code",
|
|
||||||
srcs = [
|
|
||||||
"test_code.cpp",
|
|
||||||
],
|
|
||||||
deps = ["common"],
|
|
||||||
)
|
)
|
||||||
|
|||||||
@ -1,102 +0,0 @@
|
|||||||
#ifndef msgpack_test_utils_4573e6627d8efe78
|
|
||||||
#define msgpack_test_utils_4573e6627d8efe78
|
|
||||||
|
|
||||||
// clang-format off : Must include fmt/format before extra/formatters.
|
|
||||||
#include <fmt/format.h>
|
|
||||||
|
|
||||||
#include "parselink/msgpack/extra/formatters.h"
|
|
||||||
// clang-format on
|
|
||||||
|
|
||||||
#include <tl/expected.hpp>
|
|
||||||
|
|
||||||
#include <boost/ut.hpp>
|
|
||||||
#include <magic_enum.hpp>
|
|
||||||
#include <magic_enum_format.hpp>
|
|
||||||
|
|
||||||
#include <source_location>
|
|
||||||
|
|
||||||
namespace test {
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
inline constexpr std::array<std::byte, sizeof(T)> as_bytes(T&& t) {
|
|
||||||
return std::bit_cast<std::array<std::byte, sizeof(T)>>(std::forward<T>(t));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename... Bytes>
|
|
||||||
inline constexpr std::array<std::byte, sizeof...(Bytes)> make_bytes(
|
|
||||||
Bytes&&... bytes) noexcept {
|
|
||||||
return {std::byte(std::forward<Bytes>(bytes))...};
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace test
|
|
||||||
|
|
||||||
// This formatter allows expected values to be formatted for both its expected
|
|
||||||
// and unexpected types.
|
|
||||||
template <typename T, typename E>
|
|
||||||
struct fmt::formatter<tl::expected<T, E>> {
|
|
||||||
int width = 0;
|
|
||||||
|
|
||||||
fmt::formatter<T> t_fmtr;
|
|
||||||
fmt::formatter<E> e_fmtr;
|
|
||||||
|
|
||||||
constexpr auto parse(auto& ctx) {
|
|
||||||
using char_type =
|
|
||||||
typename std::remove_reference_t<decltype(ctx)>::char_type;
|
|
||||||
auto delim = char_type{'|'};
|
|
||||||
auto close_brace = char_type{'}'};
|
|
||||||
std::array<char_type, 32> fmtbuf;
|
|
||||||
|
|
||||||
if (ctx.begin() == ctx.end() || *ctx.begin() == '}') {
|
|
||||||
return ctx.begin();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool t_parsed = false;
|
|
||||||
|
|
||||||
auto itr = ctx.begin();
|
|
||||||
auto buf = fmtbuf.begin();
|
|
||||||
|
|
||||||
while (*itr != close_brace) {
|
|
||||||
if (*itr == delim) {
|
|
||||||
if (t_parsed) {
|
|
||||||
fmt::throw_format_error("multiple delims encountered");
|
|
||||||
}
|
|
||||||
t_parsed = true;
|
|
||||||
*buf = close_brace;
|
|
||||||
auto str = basic_string_view<char_type>(
|
|
||||||
fmtbuf.data(), std::distance(fmtbuf.begin(), buf));
|
|
||||||
basic_format_parse_context<char_type> subparser(str, 0);
|
|
||||||
t_fmtr.parse(subparser);
|
|
||||||
buf = fmtbuf.begin();
|
|
||||||
} else {
|
|
||||||
*buf = *itr;
|
|
||||||
++buf;
|
|
||||||
}
|
|
||||||
++itr;
|
|
||||||
}
|
|
||||||
|
|
||||||
*buf = close_brace;
|
|
||||||
auto str = basic_string_view<char_type>(
|
|
||||||
fmtbuf.data(), std::distance(fmtbuf.begin(), buf));
|
|
||||||
basic_format_parse_context<char_type> subparser(str, 0);
|
|
||||||
if (t_parsed) {
|
|
||||||
e_fmtr.parse(subparser);
|
|
||||||
} else {
|
|
||||||
t_fmtr.parse(subparser);
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.advance_to(itr);
|
|
||||||
|
|
||||||
return itr;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto format(tl::expected<T, E> const& v, auto& ctx) const {
|
|
||||||
if (v) {
|
|
||||||
// return fmt::format_to(ctx.out(), "{}", v.value());
|
|
||||||
return t_fmtr.format(v.value(), ctx);
|
|
||||||
} else {
|
|
||||||
return e_fmtr.format(v.error(), ctx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // msgpack_test_utils_4573e6627d8efe78
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
cc_test(
|
|
||||||
name = "packer",
|
|
||||||
size = "small",
|
|
||||||
srcs = glob([
|
|
||||||
"*.cpp",
|
|
||||||
"*.h",
|
|
||||||
]),
|
|
||||||
deps = ["//tests/msgpack:common", "@rapidcheck"],
|
|
||||||
)
|
|
||||||
@ -1,63 +0,0 @@
|
|||||||
//-----------------------------------------------------------------------------
|
|
||||||
// ___ __ _ _
|
|
||||||
// / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __
|
|
||||||
// / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ /
|
|
||||||
// / ___/ (_| | | \__ \ __/ /__| | | | | <
|
|
||||||
// \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ .
|
|
||||||
//
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Author: Kurt Sassenrath
|
|
||||||
// Module: msgpack
|
|
||||||
//
|
|
||||||
// Packer tests, byte types
|
|
||||||
//
|
|
||||||
// Copyright (c) 2023 Kurt Sassenrath.
|
|
||||||
//
|
|
||||||
// License TBD.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
#include "test_packer.h"
|
|
||||||
|
|
||||||
using namespace boost::ut;
|
|
||||||
|
|
||||||
suite pack_bytes = [] {
|
|
||||||
#if 0
|
|
||||||
// Byte ranges -> Binary
|
|
||||||
"packer::pack<std::span<std::byte>>"_test = [] {
|
|
||||||
expect(test_deduced<std::span<std::byte const>>());
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
"packer::pack<std::array<std::byte, N>>"_test = [] {
|
|
||||||
{
|
|
||||||
constexpr auto data = test::make_bytes(0x01, 0x02, 0x03, 0x04);
|
|
||||||
constexpr auto expected =
|
|
||||||
test::make_bytes(0xc4, 0x04, 0x01, 0x02, 0x03, 0x04);
|
|
||||||
std::array<std::byte, std::size(expected)> payload;
|
|
||||||
|
|
||||||
msgpack::packer packer(payload);
|
|
||||||
expect(!!packer.pack(data));
|
|
||||||
expect(std::ranges::equal(payload, expected));
|
|
||||||
}
|
|
||||||
{
|
|
||||||
constexpr std::array<std::byte, 256> data{};
|
|
||||||
constexpr std::array<std::byte, 259> expected{
|
|
||||||
std::byte(0xc5), std::byte(0x01), std::byte(0x00)};
|
|
||||||
std::array<std::byte, std::size(expected)> payload;
|
|
||||||
|
|
||||||
msgpack::packer packer(payload);
|
|
||||||
expect(!!packer.pack(data));
|
|
||||||
expect(std::ranges::equal(payload, expected));
|
|
||||||
}
|
|
||||||
{
|
|
||||||
constexpr std::array<std::byte, 65536> data{};
|
|
||||||
constexpr std::array<std::byte, std::size(data) + 5> expected{
|
|
||||||
std::byte(0xc6), std::byte(0x00), std::byte(0x01),
|
|
||||||
std::byte(0x00), std::byte(0x00)};
|
|
||||||
std::array<std::byte, std::size(expected)> payload;
|
|
||||||
|
|
||||||
msgpack::packer packer(payload);
|
|
||||||
expect(!!packer.pack(data));
|
|
||||||
expect(std::ranges::equal(payload, expected));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@ -1,63 +0,0 @@
|
|||||||
//-----------------------------------------------------------------------------
|
|
||||||
// ___ __ _ _
|
|
||||||
// / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __
|
|
||||||
// / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ /
|
|
||||||
// / ___/ (_| | | \__ \ __/ /__| | | | | <
|
|
||||||
// \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ .
|
|
||||||
//
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Author: Kurt Sassenrath
|
|
||||||
// Module: msgpack
|
|
||||||
//
|
|
||||||
// Packer tests, ranges (for types that serialize to array / map formats)
|
|
||||||
//
|
|
||||||
// Copyright (c) 2023 Kurt Sassenrath.
|
|
||||||
//
|
|
||||||
// License TBD.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
#include "test_packer.h"
|
|
||||||
|
|
||||||
#include <fmt/ranges.h>
|
|
||||||
|
|
||||||
using namespace boost::ut;
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
constexpr bool expect_equal(auto const& expected, auto const& actual) {
|
|
||||||
if (!std::ranges::equal(expected, actual)) {
|
|
||||||
fmt::print("\n\tExpected: ");
|
|
||||||
for (auto x : expected) {
|
|
||||||
fmt::print("{:02x} ", x);
|
|
||||||
}
|
|
||||||
fmt::print("\n\t Actual: ");
|
|
||||||
for (auto x : actual) {
|
|
||||||
fmt::print("{:02x} ", x);
|
|
||||||
}
|
|
||||||
fmt::print("\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
suite pack_ranges = [] {
|
|
||||||
"packer::pack<std::array/std::span<int>>"_test = [] {
|
|
||||||
constexpr auto data_array = std::to_array<int>({5, 10, 15, 20});
|
|
||||||
constexpr auto expected = test::make_bytes(0x94, 0x05, 0xa, 0xf, 0x14);
|
|
||||||
|
|
||||||
{
|
|
||||||
std::array<std::byte, std::size(expected)> payload;
|
|
||||||
msgpack::packer packer(payload);
|
|
||||||
expect(!!packer.pack(data_array));
|
|
||||||
expect(expect_equal(std::span(expected), payload));
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
std::span data_span{data_array};
|
|
||||||
std::array<std::byte, std::size(expected)> payload;
|
|
||||||
msgpack::packer packer(payload);
|
|
||||||
expect(!!packer.pack(data_span));
|
|
||||||
expect(expect_equal(expected, payload));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@ -1,71 +0,0 @@
|
|||||||
//-----------------------------------------------------------------------------
|
|
||||||
// ___ __ _ _
|
|
||||||
// / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __
|
|
||||||
// / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ /
|
|
||||||
// / ___/ (_| | | \__ \ __/ /__| | | | | <
|
|
||||||
// \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ .
|
|
||||||
//
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Author: Kurt Sassenrath
|
|
||||||
// Module: msgpack
|
|
||||||
//
|
|
||||||
// Packer tests, signed ints.
|
|
||||||
//
|
|
||||||
// Copyright (c) 2023 Kurt Sassenrath.
|
|
||||||
//
|
|
||||||
// License TBD.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
#include "test_packer.h"
|
|
||||||
|
|
||||||
#include <rapidcheck.h>
|
|
||||||
|
|
||||||
using namespace boost::ut;
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
auto check_pack() {
|
|
||||||
return rc::check([](T value) {
|
|
||||||
std::array<std::byte, 16> payload;
|
|
||||||
msgpack::packer packer(payload);
|
|
||||||
if (!packer.pack(value)) return false;
|
|
||||||
|
|
||||||
if (value < 128 && value >= -32) {
|
|
||||||
// positive_fixint/negative_fixint
|
|
||||||
return packer.tell() == 1
|
|
||||||
&& payload[0] == static_cast<std::byte>(value);
|
|
||||||
} else if (within<std::int8_t>(value)) {
|
|
||||||
return payload[0] == msgpack::format::int8::marker
|
|
||||||
&& verify_packed<std::int8_t>(packer, value);
|
|
||||||
} else if (within<std::int16_t>(value)) {
|
|
||||||
return payload[0] == msgpack::format::int16::marker
|
|
||||||
&& verify_packed<std::int16_t>(packer, value);
|
|
||||||
} else if (within<std::int32_t>(value)) {
|
|
||||||
return payload[0] == msgpack::format::int32::marker
|
|
||||||
&& verify_packed<std::int32_t>(packer, value);
|
|
||||||
} else {
|
|
||||||
return payload[0] == msgpack::format::int64::marker
|
|
||||||
&& verify_packed<std::int64_t>(packer, value);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
} // anonymous namespace
|
|
||||||
|
|
||||||
suite pack_signed_types = [] {
|
|
||||||
"packer::pack<std::int8_t>"_test = [] {
|
|
||||||
expect(check_pack<std::int8_t>());
|
|
||||||
};
|
|
||||||
|
|
||||||
"packer::pack<std::int16_t>"_test = [] {
|
|
||||||
expect(check_pack<std::int16_t>());
|
|
||||||
};
|
|
||||||
|
|
||||||
"packer::pack<std::int32_t>"_test = [] {
|
|
||||||
expect(check_pack<std::int32_t>());
|
|
||||||
};
|
|
||||||
|
|
||||||
"packer::pack<std::int64_t>"_test = [] {
|
|
||||||
expect(check_pack<std::int64_t>());
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@ -1,50 +0,0 @@
|
|||||||
//-----------------------------------------------------------------------------
|
|
||||||
// ___ __ _ _
|
|
||||||
// / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __
|
|
||||||
// / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ /
|
|
||||||
// / ___/ (_| | | \__ \ __/ /__| | | | | <
|
|
||||||
// \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ .
|
|
||||||
//
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Author: Kurt Sassenrath
|
|
||||||
// Module: msgpack
|
|
||||||
//
|
|
||||||
// Packer tests, simple types
|
|
||||||
//
|
|
||||||
// Copyright (c) 2023 Kurt Sassenrath.
|
|
||||||
//
|
|
||||||
// License TBD.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
#include "test_packer.h"
|
|
||||||
|
|
||||||
using namespace boost::ut;
|
|
||||||
|
|
||||||
suite pack_simple_types = [] {
|
|
||||||
"packer::pack<nil>"_test = [] {
|
|
||||||
std::array<std::byte, 8> payload;
|
|
||||||
msgpack::packer packer(payload);
|
|
||||||
expect(!!packer.pack(msgpack::nil{}));
|
|
||||||
expect(packer.tell() == 1);
|
|
||||||
expect(payload[0] == std::byte{0xc0});
|
|
||||||
};
|
|
||||||
|
|
||||||
"packer::pack<invalid>"_test = [] {
|
|
||||||
std::array<std::byte, 8> payload;
|
|
||||||
msgpack::packer packer(payload);
|
|
||||||
expect(!!packer.pack(msgpack::invalid{}));
|
|
||||||
expect(packer.tell() == 1);
|
|
||||||
expect(payload[0] == std::byte{0xc1});
|
|
||||||
};
|
|
||||||
|
|
||||||
"packer::pack<bool>"_test = [] {
|
|
||||||
std::array<std::byte, 8> payload;
|
|
||||||
msgpack::packer packer(payload);
|
|
||||||
expect(!!packer.pack(false));
|
|
||||||
expect(packer.tell() == 1);
|
|
||||||
expect(payload[0] == std::byte{0xc2});
|
|
||||||
expect(!!packer.pack(true));
|
|
||||||
expect(packer.tell() == 2);
|
|
||||||
expect(payload[1] == std::byte{0xc3});
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@ -1,46 +0,0 @@
|
|||||||
//-----------------------------------------------------------------------------
|
|
||||||
// ___ __ _ _
|
|
||||||
// / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __
|
|
||||||
// / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ /
|
|
||||||
// / ___/ (_| | | \__ \ __/ /__| | | | | <
|
|
||||||
// \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ .
|
|
||||||
//
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Author: Kurt Sassenrath
|
|
||||||
// Module: msgpack
|
|
||||||
//
|
|
||||||
// Packer tests, strings.
|
|
||||||
//
|
|
||||||
// Copyright (c) 2023 Kurt Sassenrath.
|
|
||||||
//
|
|
||||||
// License TBD.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
#include "test_packer.h"
|
|
||||||
|
|
||||||
#include <rapidcheck.h>
|
|
||||||
|
|
||||||
using namespace boost::ut;
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
template <std::unsigned_integral LenType, typename StrType = std::string>
|
|
||||||
auto check_string() {
|
|
||||||
return rc::check([](LenType value) {
|
|
||||||
auto str = *rc::gen::container<std::string>(
|
|
||||||
value, rc::gen::character<char>());
|
|
||||||
std::vector<std::byte> payload;
|
|
||||||
payload.resize(value + 32);
|
|
||||||
msgpack::packer packer(payload);
|
|
||||||
if (!packer.pack(str)) return false;
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
} // anonymous namespace
|
|
||||||
|
|
||||||
suite pack_strings = [] {
|
|
||||||
"packer::pack<std::string>"_test = [] {
|
|
||||||
expect(check_string<std::uint8_t>());
|
|
||||||
expect(check_string<std::uint16_t>());
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@ -1,42 +0,0 @@
|
|||||||
//-----------------------------------------------------------------------------
|
|
||||||
// ___ __ _ _
|
|
||||||
// / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __
|
|
||||||
// / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ /
|
|
||||||
// / ___/ (_| | | \__ \ __/ /__| | | | | <
|
|
||||||
// \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ .
|
|
||||||
//
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Author: Kurt Sassenrath
|
|
||||||
// Module: msgpack
|
|
||||||
//
|
|
||||||
// Packer tests, common code.
|
|
||||||
//
|
|
||||||
// Copyright (c) 2023 Kurt Sassenrath.
|
|
||||||
//
|
|
||||||
// License TBD.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
#ifndef tests_msgpack_packer_6e9a5bf8e14223bc
|
|
||||||
#define tests_msgpack_packer_6e9a5bf8e14223bc
|
|
||||||
|
|
||||||
#include "parselink/msgpack/core/packer.h"
|
|
||||||
|
|
||||||
#include "test_utils.h"
|
|
||||||
|
|
||||||
template <std::integral T>
|
|
||||||
constexpr auto within(auto value) noexcept {
|
|
||||||
return value >= std::numeric_limits<T>::min()
|
|
||||||
&& value <= std::numeric_limits<T>::max();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <std::integral T>
|
|
||||||
constexpr auto verify_packed(auto const& packer, auto value) noexcept {
|
|
||||||
std::array<std::byte, (std::numeric_limits<T>::digits + 7) / 8> raw;
|
|
||||||
if (packer.tell() != 1 + raw.size()) return false;
|
|
||||||
|
|
||||||
auto packed = packer.subspan().subspan(1);
|
|
||||||
be_to_host(packed.begin(), packed.end(), raw.begin());
|
|
||||||
auto actual = std::bit_cast<T>(raw);
|
|
||||||
return actual == value;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // tests_msgpack_packer_6e9a5bf8e14223bc
|
|
||||||
@ -1,71 +0,0 @@
|
|||||||
//-----------------------------------------------------------------------------
|
|
||||||
// ___ __ _ _
|
|
||||||
// / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __
|
|
||||||
// / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ /
|
|
||||||
// / ___/ (_| | | \__ \ __/ /__| | | | | <
|
|
||||||
// \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ .
|
|
||||||
//
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Author: Kurt Sassenrath
|
|
||||||
// Module: msgpack
|
|
||||||
//
|
|
||||||
// Packer tests, unsigned ints.
|
|
||||||
//
|
|
||||||
// Copyright (c) 2023 Kurt Sassenrath.
|
|
||||||
//
|
|
||||||
// License TBD.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
#include "test_packer.h"
|
|
||||||
|
|
||||||
#include <rapidcheck.h>
|
|
||||||
|
|
||||||
using namespace boost::ut;
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
auto check_unsigned() {
|
|
||||||
return rc::check([](T value) {
|
|
||||||
std::array<std::byte, 16> payload;
|
|
||||||
msgpack::packer packer(payload);
|
|
||||||
if (!packer.pack(value)) return false;
|
|
||||||
|
|
||||||
if (value < 128) {
|
|
||||||
// positive_fixint
|
|
||||||
return packer.tell() == 1
|
|
||||||
&& payload[0] == static_cast<std::byte>(value);
|
|
||||||
} else if (within<std::uint8_t>(value)) {
|
|
||||||
return payload[0] == msgpack::format::uint8::marker
|
|
||||||
&& verify_packed<std::uint8_t>(packer, value);
|
|
||||||
} else if (within<std::uint16_t>(value)) {
|
|
||||||
return payload[0] == msgpack::format::uint16::marker
|
|
||||||
&& verify_packed<std::uint16_t>(packer, value);
|
|
||||||
} else if (within<std::uint32_t>(value)) {
|
|
||||||
return payload[0] == msgpack::format::uint32::marker
|
|
||||||
&& verify_packed<std::uint32_t>(packer, value);
|
|
||||||
} else {
|
|
||||||
return payload[0] == msgpack::format::uint64::marker
|
|
||||||
&& verify_packed<std::uint64_t>(packer, value);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
} // anonymous namespace
|
|
||||||
|
|
||||||
suite packer_unsigned_ints = [] {
|
|
||||||
"packer::pack<std::uint8_t>"_test = [] {
|
|
||||||
expect(check_unsigned<std::uint8_t>());
|
|
||||||
};
|
|
||||||
|
|
||||||
"packer::pack<std::uint16_t>"_test = [] {
|
|
||||||
expect(check_unsigned<std::uint16_t>());
|
|
||||||
};
|
|
||||||
|
|
||||||
"packer::pack<std::uint32_t>"_test = [] {
|
|
||||||
expect(check_unsigned<std::uint32_t>());
|
|
||||||
};
|
|
||||||
|
|
||||||
"packer::pack<std::uint64_t>"_test = [] {
|
|
||||||
expect(check_unsigned<std::uint64_t>());
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@ -1,17 +0,0 @@
|
|||||||
#include <array>
|
|
||||||
#include <map>
|
|
||||||
#include <parselink/msgpack/core/packer.h>
|
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
|
||||||
std::map<int, std::string_view> args{};
|
|
||||||
for (int i = 0; i < argc; ++i) {
|
|
||||||
args.emplace(i, argv[i]);
|
|
||||||
}
|
|
||||||
std::array<std::byte, 500> buff;
|
|
||||||
msgpack::packer packer(buff);
|
|
||||||
packer.pack(args);
|
|
||||||
for (auto b : packer.subspan()) {
|
|
||||||
printf("%c", char(b));
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@ -1,412 +0,0 @@
|
|||||||
//-----------------------------------------------------------------------------
|
|
||||||
// ___ __ _ _
|
|
||||||
// / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __
|
|
||||||
// / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ /
|
|
||||||
// / ___/ (_| | | \__ \ __/ /__| | | | | <
|
|
||||||
// \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ .
|
|
||||||
//
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Author: Kurt Sassenrath
|
|
||||||
// Module: msgpack
|
|
||||||
//
|
|
||||||
// Default packer tests.
|
|
||||||
//
|
|
||||||
// Copyright (c) 2023 Kurt Sassenrath.
|
|
||||||
//
|
|
||||||
// License TBD.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
#include "parselink/msgpack/core/packer.h"
|
|
||||||
|
|
||||||
#include <fmt/format.h>
|
|
||||||
#include <fmt/ranges.h>
|
|
||||||
|
|
||||||
#include "rng.h"
|
|
||||||
#include <algorithm>
|
|
||||||
#include <boost/ut.hpp>
|
|
||||||
#include <chrono>
|
|
||||||
|
|
||||||
using namespace boost::ut;
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct fmt::formatter<msgpack::invalid> : fmt::formatter<std::string_view> {
|
|
||||||
template <typename FormatContext>
|
|
||||||
auto format(msgpack::nil const&, FormatContext& ctx) const {
|
|
||||||
return fmt::format_to(ctx.out(), "{{msgpack::invalid}}");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct fmt::formatter<msgpack::nil> : fmt::formatter<std::string_view> {
|
|
||||||
template <typename FormatContext>
|
|
||||||
auto format(msgpack::nil const&, FormatContext& ctx) const {
|
|
||||||
return fmt::format_to(ctx.out(), "{{msgpack::nil}}");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct fmt::formatter<msgpack::map_desc> : fmt::formatter<std::string_view> {
|
|
||||||
template <typename FormatContext>
|
|
||||||
auto format(msgpack::map_desc const& value, FormatContext& ctx) const {
|
|
||||||
return fmt::format_to(
|
|
||||||
ctx.out(), "{{msgpack::map_desc: {}}}", value.count);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct fmt::formatter<msgpack::array_desc> : fmt::formatter<std::string_view> {
|
|
||||||
template <typename FormatContext>
|
|
||||||
auto format(msgpack::array_desc const& value, FormatContext& ctx) const {
|
|
||||||
return fmt::format_to(
|
|
||||||
ctx.out(), "{{msgpack::array_desc: {}}}", value.count);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
template <typename... Bytes>
|
|
||||||
constexpr std::array<std::byte, sizeof...(Bytes)> make_bytes(Bytes&&... bytes) {
|
|
||||||
return {static_cast<std::byte>(std::forward<Bytes>(bytes))...};
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr auto equal(auto a, auto b) {
|
|
||||||
return std::equal(std::begin(a), std::end(a), std::begin(b), std::end(b));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct test_data {};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct test_data<msgpack::nil> {
|
|
||||||
static constexpr msgpack::nil values[] = {{}};
|
|
||||||
static constexpr auto payload = make_bytes(0xc0);
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct test_data<msgpack::invalid> {
|
|
||||||
static constexpr msgpack::invalid values[] = {{}};
|
|
||||||
static constexpr auto payload = make_bytes(0xc1);
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct test_data<bool> {
|
|
||||||
static constexpr auto values = std::to_array<bool>({false, true});
|
|
||||||
static constexpr auto payload = make_bytes(0xc2, 0xc3);
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct test_data<std::uint64_t> {
|
|
||||||
static constexpr auto values = std::to_array<std::uint64_t>({
|
|
||||||
0x00, // positive fixint
|
|
||||||
0x79, // positive fixint
|
|
||||||
0x80, // uint8
|
|
||||||
0xff, // uint8
|
|
||||||
0x100, // uint16
|
|
||||||
0xffff, // uint16
|
|
||||||
0x10000, // uint32
|
|
||||||
0xffffffff, // uint32
|
|
||||||
0x100000000, // uint64
|
|
||||||
0xffffffffffffffff // uint64
|
|
||||||
});
|
|
||||||
|
|
||||||
static constexpr auto payload = make_bytes(0x00, // positive fixint
|
|
||||||
0x79, // positive fixint
|
|
||||||
0xcc, 0x80, // uint8
|
|
||||||
0xcc, 0xff, // uint8
|
|
||||||
0xcd, 0x01, 0x00, // uint16
|
|
||||||
0xcd, 0xff, 0xff, // uint16
|
|
||||||
0xce, 0x00, 0x01, 0x00, 0x00, // uint32
|
|
||||||
0xce, 0xff, 0xff, 0xff, 0xff, // uint32
|
|
||||||
0xcf, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, // uint64
|
|
||||||
0xcf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff // uint64
|
|
||||||
);
|
|
||||||
|
|
||||||
static constexpr auto valid(auto value) noexcept {
|
|
||||||
return value <= std::numeric_limits<std::uint64_t>::max();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <std::unsigned_integral T>
|
|
||||||
struct test_data<T> : test_data<std::uint64_t> {
|
|
||||||
static constexpr auto valid(auto value) noexcept {
|
|
||||||
return value <= std::numeric_limits<T>::max();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct test_data<std::int64_t> {
|
|
||||||
static constexpr auto values = std::to_array<std::int64_t>({
|
|
||||||
-1, // negative fixint
|
|
||||||
-32, // negative fixint
|
|
||||||
-33, // int8
|
|
||||||
-128, // int8
|
|
||||||
0, // int8
|
|
||||||
127, // int8
|
|
||||||
128, // int16
|
|
||||||
-129, // int16
|
|
||||||
-32768, // int16
|
|
||||||
32767, // int16
|
|
||||||
-32769, // int32
|
|
||||||
32768, // int32
|
|
||||||
-2147483648, // int32
|
|
||||||
2147483647, // int32
|
|
||||||
-2147483649, // int64
|
|
||||||
2147483648, // int64
|
|
||||||
std::numeric_limits<std::int64_t>::lowest(), // int64
|
|
||||||
std::numeric_limits<std::int64_t>::max(), // int64
|
|
||||||
});
|
|
||||||
|
|
||||||
static constexpr auto payload = make_bytes(0xff, // negative fixint
|
|
||||||
0xe0, // negative fixint
|
|
||||||
0xd0, 0xdf, // int8
|
|
||||||
0xd0, 0x80, // int8
|
|
||||||
0xd0, 0x0, // int8
|
|
||||||
0xd0, 0x7f, // int8
|
|
||||||
0xd1, 0x00, 0x80, // int16
|
|
||||||
0xd1, 0xff, 0x7f, // int16
|
|
||||||
0xd1, 0x80, 0x00, // int16
|
|
||||||
0xd1, 0x7f, 0xff, // int16
|
|
||||||
0xd2, 0xff, 0xff, 0x7f, 0xff, // int32
|
|
||||||
0xd2, 0x00, 0x00, 0x80, 0x00, // int32
|
|
||||||
0xd2, 0x80, 0x00, 0x00, 0x00, // int32
|
|
||||||
0xd2, 0x7f, 0xff, 0xff, 0xff, // int32
|
|
||||||
0xd3, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, // int64
|
|
||||||
0xd3, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, // int64
|
|
||||||
0xd3, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // int64
|
|
||||||
0xd3, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff // int64
|
|
||||||
);
|
|
||||||
|
|
||||||
static constexpr auto valid(auto value) noexcept {
|
|
||||||
return value <= std::numeric_limits<std::int64_t>::max()
|
|
||||||
&& value >= std::numeric_limits<std::int64_t>::lowest();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct test_data<msgpack::array_desc> {
|
|
||||||
static constexpr auto values = std::to_array<msgpack::array_desc>({
|
|
||||||
1, // fixarray
|
|
||||||
15, // fixarray
|
|
||||||
16, // array16
|
|
||||||
65535, // array16
|
|
||||||
65536 // array32
|
|
||||||
});
|
|
||||||
|
|
||||||
static constexpr auto payload = make_bytes(0x91, // fixarray
|
|
||||||
0x9f, // fixarray
|
|
||||||
0xdc, 0x00, 0x10, // array16
|
|
||||||
0xdc, 0xff, 0xff, // array16
|
|
||||||
0xdd, 0x00, 0x01, 0x00, 0x00 // array32
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct test_data<msgpack::map_desc> {
|
|
||||||
static constexpr auto values = std::to_array<msgpack::map_desc>({
|
|
||||||
1, // fixmap
|
|
||||||
15, // fixmap
|
|
||||||
16, // map16
|
|
||||||
65535, // map16
|
|
||||||
65536 // map32
|
|
||||||
});
|
|
||||||
|
|
||||||
static constexpr auto payload = make_bytes(0x81, // fixmap
|
|
||||||
0x8f, // fixmap
|
|
||||||
0xde, 0x00, 0x10, // map16
|
|
||||||
0xde, 0xff, 0xff, // map16
|
|
||||||
0xdf, 0x00, 0x01, 0x00, 0x00 // map32
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
template <std::convertible_to<std::string_view> T>
|
|
||||||
struct test_data<T> {
|
|
||||||
static constexpr auto values = std::to_array<T>({
|
|
||||||
"", // fixstr
|
|
||||||
"0", // fixstr
|
|
||||||
"0123456789abcdef0123456789abcde", // fixstr
|
|
||||||
"0123456789abcdef0123456789abcdef", // str8
|
|
||||||
});
|
|
||||||
|
|
||||||
static constexpr auto payload = make_bytes(0xa0, // fixstr
|
|
||||||
0xa1, 0x30, // fixstr
|
|
||||||
|
|
||||||
// fixstr
|
|
||||||
0xbf, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
|
|
||||||
0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x30, 0x31, 0x32, 0x33, 0x34,
|
|
||||||
0x35, 0x36, 0x37, 0x38, 0x39, 0x61, 0x62, 0x63, 0x64, 0x65,
|
|
||||||
|
|
||||||
// str8
|
|
||||||
0xd9, 0x20, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
|
|
||||||
0x39, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x30, 0x31, 0x32, 0x33,
|
|
||||||
0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x61, 0x62, 0x63, 0x64, 0x65,
|
|
||||||
0x66);
|
|
||||||
|
|
||||||
static constexpr auto valid(T value) noexcept {
|
|
||||||
return std::string_view(value).size()
|
|
||||||
<= std::numeric_limits<std::uint32_t>::max();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct test_data<std::span<std::byte const>> {
|
|
||||||
static constexpr auto test1 = make_bytes(0x02, 0x03);
|
|
||||||
static constexpr auto values =
|
|
||||||
std::to_array<std::span<std::byte const>>({test1});
|
|
||||||
static constexpr auto payload = make_bytes(0xc4, 0x02, 0x02, 0x03);
|
|
||||||
|
|
||||||
static constexpr auto valid(auto value) noexcept {
|
|
||||||
return value.size() <= std::numeric_limits<std::uint32_t>::max();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <std::signed_integral T>
|
|
||||||
struct test_data<T> : test_data<std::int64_t> {
|
|
||||||
static constexpr auto valid(auto value) noexcept {
|
|
||||||
return value <= std::numeric_limits<T>::max()
|
|
||||||
&& value >= std::numeric_limits<T>::lowest();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
bool test_deduced() noexcept {
|
|
||||||
constexpr auto const& expected_payload = test_data<T>::payload;
|
|
||||||
std::array<std::byte, expected_payload.size()> payload;
|
|
||||||
|
|
||||||
msgpack::packer packer(payload);
|
|
||||||
for (auto const& value : test_data<T>::values) {
|
|
||||||
if constexpr (requires { test_data<T>::valid(value); }) {
|
|
||||||
if (!test_data<T>::valid(value)) break;
|
|
||||||
}
|
|
||||||
expect(!!packer.pack(T(value)));
|
|
||||||
auto expect = std::span(expected_payload.begin(), packer.tell());
|
|
||||||
auto correct = equal(packer.subspan(), expect);
|
|
||||||
if (!correct) {
|
|
||||||
fmt::print("Deduction failed for '{}'\n", T(value));
|
|
||||||
fmt::print("\tActual: {::#04x}\n", packer.subspan());
|
|
||||||
fmt::print("\tExpect: {::#04x}\n", expect);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // anonymous namespace
|
|
||||||
|
|
||||||
suite packer_single_format = [] {
|
|
||||||
"packer::pack<bool>"_test = [] { expect(test_deduced<bool>()); };
|
|
||||||
"packer::pack<nil>"_test = [] { expect(test_deduced<msgpack::nil>()); };
|
|
||||||
"packer::pack<invalid>"_test = [] {
|
|
||||||
expect(test_deduced<msgpack::invalid>());
|
|
||||||
};
|
|
||||||
|
|
||||||
// Unsigned ints
|
|
||||||
"packer::pack<std::uint8_t>"_test = [] {
|
|
||||||
expect(test_deduced<std::uint8_t>());
|
|
||||||
};
|
|
||||||
"packer::pack<std::uint16_t>"_test = [] {
|
|
||||||
expect(test_deduced<std::uint16_t>());
|
|
||||||
};
|
|
||||||
"packer::pack<std::uint32_t>"_test = [] {
|
|
||||||
expect(test_deduced<std::uint32_t>());
|
|
||||||
};
|
|
||||||
"packer::pack<std::uint64_t>"_test = [] {
|
|
||||||
expect(test_deduced<std::uint64_t>());
|
|
||||||
};
|
|
||||||
|
|
||||||
// Signed ints
|
|
||||||
"packer::pack<std::int8_t>"_test = [] {
|
|
||||||
expect(test_deduced<std::int8_t>());
|
|
||||||
};
|
|
||||||
"packer::pack<std::int16_t>"_test = [] {
|
|
||||||
expect(test_deduced<std::int16_t>());
|
|
||||||
};
|
|
||||||
"packer::pack<std::int32_t>"_test = [] {
|
|
||||||
expect(test_deduced<std::int32_t>());
|
|
||||||
};
|
|
||||||
"packer::pack<std::int64_t>"_test = [] {
|
|
||||||
expect(test_deduced<std::int64_t>());
|
|
||||||
};
|
|
||||||
|
|
||||||
// Strings
|
|
||||||
"packer::pack<std::string_view>"_test = [] {
|
|
||||||
expect(test_deduced<std::string_view>());
|
|
||||||
};
|
|
||||||
#if 0
|
|
||||||
"packer::pack<char const*>"_test = [] {
|
|
||||||
expect(test_deduced<char const*>());
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Byte ranges -> Binary
|
|
||||||
|
|
||||||
"packer::pack<std::span<std::byte>>"_test = [] {
|
|
||||||
expect(test_deduced<std::span<std::byte const>>());
|
|
||||||
};
|
|
||||||
|
|
||||||
"packer::pack<std::array<std::byte, N>>"_test = [] {
|
|
||||||
{
|
|
||||||
constexpr auto data = make_bytes(0x01, 0x02, 0x03, 0x04);
|
|
||||||
constexpr auto expected =
|
|
||||||
make_bytes(0xc4, 0x04, 0x01, 0x02, 0x03, 0x04);
|
|
||||||
std::array<std::byte, std::size(expected)> payload;
|
|
||||||
|
|
||||||
msgpack::packer packer(payload);
|
|
||||||
expect(!!packer.pack(data));
|
|
||||||
expect(std::ranges::equal(payload, expected));
|
|
||||||
}
|
|
||||||
{
|
|
||||||
constexpr std::array<std::byte, 256> data{};
|
|
||||||
constexpr std::array<std::byte, 259> expected{
|
|
||||||
std::byte(0xc5), std::byte(0x01), std::byte(0x00)};
|
|
||||||
std::array<std::byte, std::size(expected)> payload;
|
|
||||||
|
|
||||||
msgpack::packer packer(payload);
|
|
||||||
expect(!!packer.pack(data));
|
|
||||||
expect(std::ranges::equal(payload, expected));
|
|
||||||
}
|
|
||||||
{
|
|
||||||
constexpr std::array<std::byte, 65536> data{};
|
|
||||||
constexpr std::array<std::byte, std::size(data) + 5> expected{
|
|
||||||
std::byte(0xc6), std::byte(0x00), std::byte(0x01),
|
|
||||||
std::byte(0x00), std::byte(0x00)};
|
|
||||||
std::array<std::byte, std::size(expected)> payload;
|
|
||||||
|
|
||||||
msgpack::packer packer(payload);
|
|
||||||
expect(!!packer.pack(data));
|
|
||||||
expect(std::ranges::equal(payload, expected));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// array_desc - Just the header of the array.
|
|
||||||
"packer::pack<msgpack::array_desc>"_test = [] {
|
|
||||||
expect(test_deduced<msgpack::array_desc>());
|
|
||||||
};
|
|
||||||
|
|
||||||
// map_desc - Just the header of the map.
|
|
||||||
"packer::pack<msgpack::map_desc>"_test = [] {
|
|
||||||
expect(test_deduced<msgpack::map_desc>());
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
suite packer_ranges = [] {
|
|
||||||
"packer::pack<std::array/std::span<int>>"_test = [] {
|
|
||||||
constexpr auto data_array = std::to_array<int>({5, 10, 15, 20});
|
|
||||||
constexpr auto expected =
|
|
||||||
make_bytes(0x94, 0xd0, 0x05, 0xd0, 0xa, 0xd0, 0xf, 0xd0, 0x14);
|
|
||||||
|
|
||||||
{
|
|
||||||
std::array<std::byte, std::size(expected)> payload;
|
|
||||||
msgpack::packer packer(payload);
|
|
||||||
expect(!!packer.pack(data_array));
|
|
||||||
expect(std::ranges::equal(payload, expected));
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
std::span data_span{data_array};
|
|
||||||
std::array<std::byte, std::size(expected)> payload;
|
|
||||||
msgpack::packer packer(payload);
|
|
||||||
expect(!!packer.pack(data_span));
|
|
||||||
expect(std::ranges::equal(payload, expected));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
100
tests/msgpack/test_utils.h
Normal file
100
tests/msgpack/test_utils.h
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
#ifndef msgpack_test_utils_4573e6627d8efe78
|
||||||
|
#define msgpack_test_utils_4573e6627d8efe78
|
||||||
|
|
||||||
|
#include <boost/ut.hpp>
|
||||||
|
#include <magic_enum.hpp>
|
||||||
|
|
||||||
|
#include <fmt/format.h>
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
constexpr bool operator==(std::span<T const> a, std::span<T const> b) noexcept {
|
||||||
|
return std::equal(a.begin(), a.end(), b.begin(), b.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
constexpr std::array<std::byte, sizeof(T)> as_bytes(T&& t) {
|
||||||
|
return std::bit_cast<std::array<std::byte, sizeof(T)>>(std::forward<T>(t));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Bytes>
|
||||||
|
constexpr std::array<std::byte, sizeof...(Bytes)> make_bytes(Bytes&&... bytes) {
|
||||||
|
return {std::byte(std::forward<Bytes>(bytes))...};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, std::size_t C = 1024 * 1024>
|
||||||
|
struct oversized_array {
|
||||||
|
std::array<T, C> data;
|
||||||
|
std::size_t size;
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr auto to_bytes_array_oversized(auto const& container) {
|
||||||
|
using value_type = std::decay_t<decltype(container[0])>;
|
||||||
|
oversized_array<value_type> arr;
|
||||||
|
std::copy(std::begin(container), std::end(container), std::begin(arr.data));
|
||||||
|
arr.size = std::distance(std::begin(container), std::end(container));
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
consteval auto generate_bytes(auto callable) {
|
||||||
|
constexpr auto oversized = to_bytes_array_oversized(callable());
|
||||||
|
using value_type = std::decay_t<decltype(oversized.data[0])>;
|
||||||
|
std::array<value_type, oversized.size> out;
|
||||||
|
std::copy(std::begin(oversized.data),
|
||||||
|
std::next(std::begin(oversized.data), oversized.size),
|
||||||
|
std::begin(out));
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
consteval auto build_string(auto callable) {
|
||||||
|
constexpr auto string_array = generate_bytes(callable);
|
||||||
|
return string_array;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <std::size_t... Sizes>
|
||||||
|
constexpr auto cat(std::array<std::byte, Sizes> const&... a) noexcept {
|
||||||
|
std::array<std::byte, (Sizes + ...)> out;
|
||||||
|
std::size_t index{};
|
||||||
|
((std::copy_n(a.begin(), Sizes, out.begin() + index), index += Sizes), ...);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr auto repeat(std::span<std::byte const> sv, std::size_t count) {
|
||||||
|
std::vector<std::byte> range;
|
||||||
|
range.reserve(sv.size() * count);
|
||||||
|
for (decltype(count) i = 0; i < count; ++i) {
|
||||||
|
std::copy_n(sv.begin(), sv.size(), std::back_inserter(range));
|
||||||
|
}
|
||||||
|
return range;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr auto repeat(std::string_view sv, std::size_t count) {
|
||||||
|
std::vector<char> range;
|
||||||
|
range.reserve(sv.size() * count);
|
||||||
|
for (decltype(count) i = 0; i < count; ++i) {
|
||||||
|
std::copy_n(sv.begin(), sv.size(), std::back_inserter(range));
|
||||||
|
}
|
||||||
|
return range;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr auto from_string_view(std::string_view sv) {
|
||||||
|
std::vector<std::byte> range;
|
||||||
|
range.resize(sv.size());
|
||||||
|
auto itr = range.begin();
|
||||||
|
for (auto c : sv) {
|
||||||
|
*itr = std::byte(c);
|
||||||
|
++itr;
|
||||||
|
}
|
||||||
|
return range;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename E>
|
||||||
|
std::ostream& operator<<(std::ostream& os, tl::expected<T, E> const& exp) {
|
||||||
|
if (exp.has_value()) {
|
||||||
|
os << "Value: '" << *exp << "'";
|
||||||
|
} else {
|
||||||
|
os << "Error";
|
||||||
|
}
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // msgpack_test_utils_4573e6627d8efe78
|
||||||
@ -2,9 +2,6 @@
|
|||||||
#include <boost/ut.hpp>
|
#include <boost/ut.hpp>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include <fmt/format.h>
|
|
||||||
#include <fmt/ranges.h>
|
|
||||||
|
|
||||||
using namespace boost::ut;
|
using namespace boost::ut;
|
||||||
namespace format = msgpack::format;
|
namespace format = msgpack::format;
|
||||||
|
|
||||||
@ -105,17 +102,17 @@ suite writer = [] {
|
|||||||
std::array<std::byte, 2> payload;
|
std::array<std::byte, 2> payload;
|
||||||
auto constexpr expected = make_bytes(0x32, 0x55);
|
auto constexpr expected = make_bytes(0x32, 0x55);
|
||||||
msgpack::writer writer(payload);
|
msgpack::writer writer(payload);
|
||||||
expect(!!writer.write<fmt>(std::uint8_t{0x32}));
|
auto result = writer.write<fmt>(std::uint8_t{0x32});
|
||||||
|
expect(!!result);
|
||||||
expect(writer.tell() == 1);
|
expect(writer.tell() == 1);
|
||||||
expect(*writer.subspan().begin() == *expected.begin());
|
expect(*writer.subspan().begin() == std::byte{0x32});
|
||||||
expect(writer.write<fmt>(std::uint8_t{0x82})
|
expect(writer.write<fmt>(std::uint8_t{0x82})
|
||||||
== tl::make_unexpected(error::bad_value));
|
== tl::make_unexpected(error::bad_value));
|
||||||
expect(!!writer.write<fmt>(std::uint8_t{0x55}));
|
|
||||||
|
writer.write(std::uint8_t{0x55});
|
||||||
expect(writer.tell() == 2);
|
expect(writer.tell() == 2);
|
||||||
expect(equal(writer.subspan(), expected));
|
expect(equal(writer.subspan(), expected));
|
||||||
expect(writer.write<fmt>(std::uint8_t{0x82})
|
expect(writer.write(std::uint8_t{0x01})
|
||||||
== tl::make_unexpected(error::out_of_space));
|
|
||||||
expect(writer.write<fmt>(std::uint8_t{0x32})
|
|
||||||
== tl::make_unexpected(error::out_of_space));
|
== tl::make_unexpected(error::out_of_space));
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -126,13 +123,12 @@ suite writer = [] {
|
|||||||
std::array<std::byte, 4> payload;
|
std::array<std::byte, 4> payload;
|
||||||
auto constexpr expected = make_bytes(0xcc, 0x32, 0xcc, 0x82);
|
auto constexpr expected = make_bytes(0xcc, 0x32, 0xcc, 0x82);
|
||||||
msgpack::writer writer(payload);
|
msgpack::writer writer(payload);
|
||||||
auto result = writer.write<fmt>(std::uint8_t{0x32});
|
expect(!!writer.write<fmt>(std::uint8_t{0x32}));
|
||||||
expect(!!result);
|
|
||||||
expect(writer.tell() == 2);
|
expect(writer.tell() == 2);
|
||||||
expect(equal(writer.subspan(), std::span{expected.begin(), 2}));
|
expect(equal(writer.subspan(), std::span{expected.begin(), 2}));
|
||||||
expect(!!writer.write<fmt>(std::uint8_t{0x82}));
|
expect(!!writer.write<fmt>(std::uint8_t{0x82}));
|
||||||
expect(equal(writer.subspan(), expected));
|
expect(equal(writer.subspan(), expected));
|
||||||
expect(writer.write<fmt>(std::uint8_t{0x01})
|
expect(writer.write(std::uint8_t{0x01})
|
||||||
== tl::make_unexpected(error::out_of_space));
|
== tl::make_unexpected(error::out_of_space));
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -212,7 +208,7 @@ suite writer = [] {
|
|||||||
expect(equal(writer.subspan(), std::span{expected.begin(), 2}));
|
expect(equal(writer.subspan(), std::span{expected.begin(), 2}));
|
||||||
expect(!!writer.write<fmt>(std::int8_t{-5}));
|
expect(!!writer.write<fmt>(std::int8_t{-5}));
|
||||||
expect(equal(writer.subspan(), expected));
|
expect(equal(writer.subspan(), expected));
|
||||||
expect(writer.write<fmt>(0x01)
|
expect(writer.write(std::uint8_t{0x01})
|
||||||
== tl::make_unexpected(error::out_of_space));
|
== tl::make_unexpected(error::out_of_space));
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -435,179 +431,3 @@ suite writer = [] {
|
|||||||
expect(equal(writer.subspan(), expected));
|
expect(equal(writer.subspan(), expected));
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
// Deduced writer tests
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct test_data {};
|
|
||||||
|
|
||||||
template <std::signed_integral T>
|
|
||||||
struct test_data<T> {
|
|
||||||
static constexpr auto values = std::to_array<std::int64_t>({
|
|
||||||
-1, // negative fixint
|
|
||||||
-32, // negative fixint
|
|
||||||
-33, // int8
|
|
||||||
-128, // int8
|
|
||||||
0, // int8
|
|
||||||
127, // int8
|
|
||||||
128, // int16
|
|
||||||
-129, // int16
|
|
||||||
-32768, // int16
|
|
||||||
32767, // int16
|
|
||||||
-32769, // int32
|
|
||||||
32768, // int32
|
|
||||||
-2147483648, // int32
|
|
||||||
2147483647, // int32
|
|
||||||
-2147483649, // int64
|
|
||||||
2147483648, // int64
|
|
||||||
std::numeric_limits<std::int64_t>::lowest(), // int64
|
|
||||||
std::numeric_limits<std::int64_t>::max(), // int64
|
|
||||||
});
|
|
||||||
|
|
||||||
static constexpr auto payload = make_bytes(0xff, // negative fixint
|
|
||||||
0xe0, // negative fixint
|
|
||||||
0xd0, 0xdf, // int8
|
|
||||||
0xd0, 0x80, // int8
|
|
||||||
0xd0, 0x0, // int8
|
|
||||||
0xd0, 0x7f, // int8
|
|
||||||
0xd1, 0x00, 0x80, // int16
|
|
||||||
0xd1, 0xff, 0x7f, // int16
|
|
||||||
0xd1, 0x80, 0x00, // int16
|
|
||||||
0xd1, 0x7f, 0xff, // int16
|
|
||||||
0xd2, 0xff, 0xff, 0x7f, 0xff, // int32
|
|
||||||
0xd2, 0x00, 0x00, 0x80, 0x00, // int32
|
|
||||||
0xd2, 0x80, 0x00, 0x00, 0x00, // int32
|
|
||||||
0xd2, 0x7f, 0xff, 0xff, 0xff, // int32
|
|
||||||
0xd3, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, // int64
|
|
||||||
0xd3, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, // int64
|
|
||||||
0xd3, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // int64
|
|
||||||
0xd3, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff // int64
|
|
||||||
);
|
|
||||||
|
|
||||||
static constexpr auto valid(auto value) noexcept {
|
|
||||||
return value <= std::numeric_limits<T>::max()
|
|
||||||
&& value >= std::numeric_limits<T>::lowest();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <std::unsigned_integral T>
|
|
||||||
struct test_data<T> {
|
|
||||||
static constexpr auto values = std::to_array<std::uint64_t>({
|
|
||||||
0x00, // positive fixint
|
|
||||||
0x79, // positive fixint
|
|
||||||
0x80, // uint8
|
|
||||||
0xff, // uint8
|
|
||||||
0x100, // uint16
|
|
||||||
0xffff, // uint16
|
|
||||||
0x10000, // uint32
|
|
||||||
0xffffffff, // uint32
|
|
||||||
0x100000000, // uint64
|
|
||||||
0xffffffffffffffff // uint64
|
|
||||||
});
|
|
||||||
|
|
||||||
static constexpr auto payload = make_bytes(0x00, // positive fixint
|
|
||||||
0x79, // positive fixint
|
|
||||||
0xcc, 0x80, // uint8
|
|
||||||
0xcc, 0xff, // uint8
|
|
||||||
0xcd, 0x01, 0x00, // uint16
|
|
||||||
0xcd, 0xff, 0xff, // uint16
|
|
||||||
0xce, 0x00, 0x01, 0x00, 0x00, // uint32
|
|
||||||
0xce, 0xff, 0xff, 0xff, 0xff, // uint32
|
|
||||||
0xcf, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, // uint64
|
|
||||||
0xcf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff // uint64
|
|
||||||
);
|
|
||||||
|
|
||||||
static constexpr auto valid(auto value) noexcept {
|
|
||||||
return value <= std::numeric_limits<T>::max();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct test_data<std::string_view> {
|
|
||||||
static constexpr auto values = std::to_array<std::string_view>({
|
|
||||||
"", // fixstr
|
|
||||||
"0", // fixstr
|
|
||||||
"0123456789abcdef0123456789abcde", // fixstr
|
|
||||||
"0123456789abcdef0123456789abcdef", // str8
|
|
||||||
});
|
|
||||||
|
|
||||||
static constexpr auto payload = make_bytes(0xa0, // fixstr
|
|
||||||
0xa1, 0x30, // fixstr
|
|
||||||
|
|
||||||
// fixstr
|
|
||||||
0xbf, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
|
|
||||||
0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x30, 0x31, 0x32, 0x33, 0x34,
|
|
||||||
0x35, 0x36, 0x37, 0x38, 0x39, 0x61, 0x62, 0x63, 0x64, 0x65,
|
|
||||||
|
|
||||||
// str8
|
|
||||||
0xd9, 0x20, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
|
|
||||||
0x39, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x30, 0x31, 0x32, 0x33,
|
|
||||||
0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x61, 0x62, 0x63, 0x64, 0x65,
|
|
||||||
0x66);
|
|
||||||
|
|
||||||
static constexpr auto valid(auto value) noexcept {
|
|
||||||
return value.size() <= std::numeric_limits<std::uint32_t>::max();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
bool test_deduced() noexcept {
|
|
||||||
constexpr auto const& expected_payload = test_data<T>::payload;
|
|
||||||
std::array<std::byte, expected_payload.size()> payload;
|
|
||||||
|
|
||||||
msgpack::writer writer(payload);
|
|
||||||
for (auto const& value : test_data<T>::values) {
|
|
||||||
if (!test_data<T>::valid(value)) break;
|
|
||||||
expect(!!writer.write2(T(value)));
|
|
||||||
auto expect = std::span(expected_payload.begin(), writer.tell());
|
|
||||||
auto correct = equal(writer.subspan(), expect);
|
|
||||||
if (!correct) {
|
|
||||||
fmt::print("Deduction failed for '{}'\n", T(value));
|
|
||||||
fmt::print("\tActual: {::#04x}\n", writer.subspan());
|
|
||||||
fmt::print("\tExpect: {::#04x}\n", expect);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // anonymous namespace
|
|
||||||
|
|
||||||
suite deduced_writer = [] {
|
|
||||||
"writer::write<std::uint8_t> (deduced + compressed)"_test = [] {
|
|
||||||
expect(test_deduced<std::uint8_t>());
|
|
||||||
};
|
|
||||||
|
|
||||||
"writer::write<std::uint16_t> (deduced + compressed)"_test = [] {
|
|
||||||
expect(test_deduced<std::uint16_t>());
|
|
||||||
};
|
|
||||||
|
|
||||||
"writer::write<std::uint32_t> (deduced + compressed)"_test = [] {
|
|
||||||
expect(test_deduced<std::uint32_t>());
|
|
||||||
};
|
|
||||||
|
|
||||||
"writer::write<std::uint64_t> (deduced + compressed)"_test = [] {
|
|
||||||
expect(test_deduced<std::uint64_t>());
|
|
||||||
};
|
|
||||||
|
|
||||||
"writer::write<std::int8_t> (deduced + compressed)"_test = [] {
|
|
||||||
expect(test_deduced<std::int8_t>());
|
|
||||||
};
|
|
||||||
|
|
||||||
"writer::write<std::int16_t> (deduced + compressed)"_test = [] {
|
|
||||||
expect(test_deduced<std::int16_t>());
|
|
||||||
};
|
|
||||||
|
|
||||||
"writer::write<std::int32_t> (deduced + compressed)"_test = [] {
|
|
||||||
expect(test_deduced<std::int32_t>());
|
|
||||||
};
|
|
||||||
|
|
||||||
"writer::write<std::int64_t> (deduced + compressed)"_test = [] {
|
|
||||||
expect(test_deduced<std::int32_t>());
|
|
||||||
};
|
|
||||||
|
|
||||||
"writer::write<std::string_view> (deduced + compressed)"_test = [] {
|
|
||||||
expect(test_deduced<std::string_view>());
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|||||||
@ -1,9 +0,0 @@
|
|||||||
cc_test(
|
|
||||||
name = "unpacker",
|
|
||||||
size = "small",
|
|
||||||
srcs = glob([
|
|
||||||
"*.cpp",
|
|
||||||
"*.h",
|
|
||||||
]),
|
|
||||||
deps = ["//tests/msgpack:common", "@rapidcheck"],
|
|
||||||
)
|
|
||||||
@ -1,68 +0,0 @@
|
|||||||
//-----------------------------------------------------------------------------
|
|
||||||
// ___ __ _ _
|
|
||||||
// / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __
|
|
||||||
// / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ /
|
|
||||||
// / ___/ (_| | | \__ \ __/ /__| | | | | <
|
|
||||||
// \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ .
|
|
||||||
//
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Author: Kurt Sassenrath
|
|
||||||
// Module: msgpack
|
|
||||||
//
|
|
||||||
// Unpacker tests for bytes.
|
|
||||||
//
|
|
||||||
// Copyright (c) 2023 Kurt Sassenrath.
|
|
||||||
//
|
|
||||||
// License TBD.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
#include "test_unpacker.h"
|
|
||||||
|
|
||||||
using namespace boost::ut;
|
|
||||||
|
|
||||||
suite unpack_byte_types = [] {
|
|
||||||
// TODO: Make non-const version?
|
|
||||||
"unpacker::unpack<std::span<std::byte const>>"_test = [] {
|
|
||||||
constexpr auto payload =
|
|
||||||
test::make_bytes(0xc4, 0x05, 0x01, 0x02, 0x03, 0x04, 0x05);
|
|
||||||
constexpr auto expected =
|
|
||||||
test::make_bytes(0x01, 0x02, 0x03, 0x04, 0x05);
|
|
||||||
{
|
|
||||||
msgpack::unpacker unpacker(payload);
|
|
||||||
auto actual = unpacker.unpack<std::span<std::byte const>>();
|
|
||||||
expect(std::ranges::equal(*actual, expected));
|
|
||||||
}
|
|
||||||
{
|
|
||||||
// Test extent
|
|
||||||
msgpack::unpacker unpacker(payload);
|
|
||||||
auto actual = unpacker.unpack<std::span<std::byte const, 5>>();
|
|
||||||
expect(std::ranges::equal(*actual, expected));
|
|
||||||
}
|
|
||||||
{
|
|
||||||
msgpack::unpacker unpacker(payload);
|
|
||||||
auto actual = unpacker.unpack<std::span<std::byte const, 4>>();
|
|
||||||
expect(actual == tl::make_unexpected(msgpack::error::wrong_length));
|
|
||||||
auto actual2 = unpacker.unpack<std::span<std::byte const>>();
|
|
||||||
expect(std::ranges::equal(*actual2, expected));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: Make non-const version?
|
|
||||||
"unpacker::unpack<std::array<std::byte, N>>"_test = [] {
|
|
||||||
constexpr auto payload =
|
|
||||||
test::make_bytes(0xc4, 0x05, 0x01, 0x02, 0x03, 0x04, 0x06);
|
|
||||||
constexpr auto expected =
|
|
||||||
test::make_bytes(0x01, 0x02, 0x03, 0x04, 0x06);
|
|
||||||
{
|
|
||||||
msgpack::unpacker unpacker(payload);
|
|
||||||
auto actual = unpacker.unpack<std::array<std::byte, 5>>();
|
|
||||||
expect(std::ranges::equal(*actual, expected));
|
|
||||||
}
|
|
||||||
{
|
|
||||||
msgpack::unpacker unpacker(payload);
|
|
||||||
auto actual = unpacker.unpack<std::array<std::byte, 4>>();
|
|
||||||
expect(actual == tl::make_unexpected(msgpack::error::wrong_length));
|
|
||||||
auto actual2 = unpacker.unpack<std::span<std::byte const, 5>>();
|
|
||||||
expect(std::ranges::equal(*actual2, expected));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@ -1,105 +0,0 @@
|
|||||||
//-----------------------------------------------------------------------------
|
|
||||||
// ___ __ _ _
|
|
||||||
// / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __
|
|
||||||
// / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ /
|
|
||||||
// / ___/ (_| | | \__ \ __/ /__| | | | | <
|
|
||||||
// \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ .
|
|
||||||
//
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Author: Kurt Sassenrath
|
|
||||||
// Module: msgpack
|
|
||||||
//
|
|
||||||
// Unpacker tests for signed integer values
|
|
||||||
//
|
|
||||||
// Copyright (c) 2023 Kurt Sassenrath.
|
|
||||||
//
|
|
||||||
// License TBD.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
#include "test_unpacker.h"
|
|
||||||
|
|
||||||
#include <rapidcheck.h>
|
|
||||||
|
|
||||||
using namespace boost::ut;
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct get_marker;
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct get_marker<std::int8_t> {
|
|
||||||
static constexpr std::byte marker = msgpack::format::int8::marker;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct get_marker<std::int16_t> {
|
|
||||||
static constexpr std::byte marker = msgpack::format::int16::marker;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct get_marker<std::int32_t> {
|
|
||||||
static constexpr std::byte marker = msgpack::format::int32::marker;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct get_marker<std::int64_t> {
|
|
||||||
static constexpr std::byte marker = msgpack::format::int64::marker;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Utilize rapidcheck to generate random packed payloads, and unpacking the
|
|
||||||
// correct value
|
|
||||||
template <typename PackType, typename UnpackType>
|
|
||||||
auto check_unpack_integer() noexcept {
|
|
||||||
auto result = rc::check([](PackType value) {
|
|
||||||
std::vector<std::byte> payload = {get_marker<PackType>::marker};
|
|
||||||
auto bytes = host_to_be(
|
|
||||||
std::bit_cast<std::array<std::byte, sizeof(PackType)>>(value));
|
|
||||||
std::copy(bytes.begin(), bytes.end(), std::back_inserter(payload));
|
|
||||||
msgpack::unpacker unpacker(payload);
|
|
||||||
auto unpacked_value = unpacker.unpack<UnpackType>();
|
|
||||||
if (std::numeric_limits<UnpackType>::max() < value
|
|
||||||
|| std::numeric_limits<UnpackType>::min() > value) {
|
|
||||||
return unpacked_value
|
|
||||||
== tl::make_unexpected(msgpack::error::will_truncate);
|
|
||||||
} else {
|
|
||||||
return unpacked_value.has_value() && unpacked_value == value;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (!result) {
|
|
||||||
fmt::print("Failed on {}\n",
|
|
||||||
std::source_location::current().function_name());
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // anonymous namespace
|
|
||||||
|
|
||||||
suite unpack_signed_types = [] {
|
|
||||||
"unpacker::unpack<std::int8_t>"_test = [] {
|
|
||||||
expect(check_unpack_integer<std::int8_t, std::int8_t>());
|
|
||||||
expect(check_unpack_integer<std::int16_t, std::int8_t>());
|
|
||||||
expect(check_unpack_integer<std::int32_t, std::int8_t>());
|
|
||||||
expect(check_unpack_integer<std::int64_t, std::int8_t>());
|
|
||||||
};
|
|
||||||
|
|
||||||
"unpacker::unpack<std::int16_t>"_test = [] {
|
|
||||||
expect(check_unpack_integer<std::int8_t, std::int16_t>());
|
|
||||||
expect(check_unpack_integer<std::int16_t, std::int16_t>());
|
|
||||||
expect(check_unpack_integer<std::int32_t, std::int16_t>());
|
|
||||||
expect(check_unpack_integer<std::int64_t, std::int16_t>());
|
|
||||||
};
|
|
||||||
|
|
||||||
"unpacker::unpack<std::int32_t>"_test = [] {
|
|
||||||
expect(check_unpack_integer<std::int8_t, std::int32_t>());
|
|
||||||
expect(check_unpack_integer<std::int16_t, std::int32_t>());
|
|
||||||
expect(check_unpack_integer<std::int32_t, std::int32_t>());
|
|
||||||
expect(check_unpack_integer<std::int64_t, std::int32_t>());
|
|
||||||
};
|
|
||||||
|
|
||||||
"unpacker::unpack<std::int64_t>"_test = [] {
|
|
||||||
expect(check_unpack_integer<std::int8_t, std::int64_t>());
|
|
||||||
expect(check_unpack_integer<std::int32_t, std::int64_t>());
|
|
||||||
expect(check_unpack_integer<std::int32_t, std::int64_t>());
|
|
||||||
expect(check_unpack_integer<std::int64_t, std::int64_t>());
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@ -1,61 +0,0 @@
|
|||||||
//-----------------------------------------------------------------------------
|
|
||||||
// ___ __ _ _
|
|
||||||
// / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __
|
|
||||||
// / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ /
|
|
||||||
// / ___/ (_| | | \__ \ __/ /__| | | | | <
|
|
||||||
// \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ .
|
|
||||||
//
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Author: Kurt Sassenrath
|
|
||||||
// Module: msgpack
|
|
||||||
//
|
|
||||||
// Default packer tests.
|
|
||||||
//
|
|
||||||
// Copyright (c) 2023 Kurt Sassenrath.
|
|
||||||
//
|
|
||||||
// License TBD.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
#include "test_unpacker.h"
|
|
||||||
|
|
||||||
#include <fmt/format.h>
|
|
||||||
|
|
||||||
using namespace boost::ut;
|
|
||||||
|
|
||||||
namespace {} // anonymous namespace
|
|
||||||
|
|
||||||
suite unpacker_simple_types = [] {
|
|
||||||
"unpacker::unpack<invalid>"_test = [] {
|
|
||||||
tl::expected<int, msgpack::error> x(
|
|
||||||
tl::make_unexpected(msgpack::error::end_of_message));
|
|
||||||
static constexpr auto payload = test::make_bytes(0xc1, 0xc1);
|
|
||||||
msgpack::unpacker unpacker(payload);
|
|
||||||
expect(type_check_unpacker<msgpack::invalid>(unpacker));
|
|
||||||
expect(unpacker.unpack<msgpack::invalid>() == msgpack::invalid{});
|
|
||||||
expect(unpacker.unpack<msgpack::invalid>() == msgpack::invalid{});
|
|
||||||
expect(unpacker.unpack<msgpack::invalid>()
|
|
||||||
== tl::make_unexpected(msgpack::error::end_of_message));
|
|
||||||
};
|
|
||||||
|
|
||||||
"unpacker::unpack<nil>"_test = [] {
|
|
||||||
static constexpr auto payload = test::make_bytes(0xc0, 0xc0);
|
|
||||||
msgpack::unpacker unpacker(payload);
|
|
||||||
expect(type_check_unpacker<msgpack::nil>(unpacker));
|
|
||||||
expect(unpacker.unpack<msgpack::nil>() == msgpack::nil{});
|
|
||||||
expect(unpacker.unpack<msgpack::nil>() == msgpack::nil{});
|
|
||||||
expect(unpacker.unpack<msgpack::nil>()
|
|
||||||
== tl::make_unexpected(msgpack::error::end_of_message));
|
|
||||||
};
|
|
||||||
|
|
||||||
"unpacker::unpack<bool>"_test = [] {
|
|
||||||
constexpr auto payload = test::make_bytes(0xc3, 0xc2);
|
|
||||||
msgpack::unpacker unpacker(payload);
|
|
||||||
expect(type_check_unpacker<bool>(unpacker));
|
|
||||||
auto value = unpacker.unpack<bool>();
|
|
||||||
expect(value && *value == true);
|
|
||||||
value = unpacker.unpack<bool>();
|
|
||||||
expect(value && *value == false);
|
|
||||||
expect(unpacker.unpack<bool>()
|
|
||||||
== tl::make_unexpected(msgpack::error::end_of_message));
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@ -1,31 +0,0 @@
|
|||||||
//-----------------------------------------------------------------------------
|
|
||||||
// ___ __ _ _
|
|
||||||
// / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __
|
|
||||||
// / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ /
|
|
||||||
// / ___/ (_| | | \__ \ __/ /__| | | | | <
|
|
||||||
// \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ .
|
|
||||||
//
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Author: Kurt Sassenrath
|
|
||||||
// Module: msgpack
|
|
||||||
//
|
|
||||||
// Unpacker tests for strings.
|
|
||||||
//
|
|
||||||
// Copyright (c) 2023 Kurt Sassenrath.
|
|
||||||
//
|
|
||||||
// License TBD.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
#include "test_unpacker.h"
|
|
||||||
|
|
||||||
using namespace boost::ut;
|
|
||||||
|
|
||||||
suite unpack_string_types = [] {
|
|
||||||
"unpacker::unpack<std::string_view>"_test = [] {
|
|
||||||
static constexpr auto payload = test::make_bytes(0xa2, 'H', 'i');
|
|
||||||
msgpack::unpacker unpacker(payload);
|
|
||||||
expect(type_check_unpacker<std::string_view>(unpacker));
|
|
||||||
expect(unpacker.unpack<std::string_view>() == "Hi");
|
|
||||||
expect(unpacker.unpack<msgpack::invalid>()
|
|
||||||
== tl::make_unexpected(msgpack::error::end_of_message));
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@ -1,57 +0,0 @@
|
|||||||
//-----------------------------------------------------------------------------
|
|
||||||
// ___ __ _ _
|
|
||||||
// / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __
|
|
||||||
// / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ /
|
|
||||||
// / ___/ (_| | | \__ \ __/ /__| | | | | <
|
|
||||||
// \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ .
|
|
||||||
//
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Author: Kurt Sassenrath
|
|
||||||
// Module: msgpack
|
|
||||||
//
|
|
||||||
// Unpacker tests, common code.
|
|
||||||
//
|
|
||||||
// Copyright (c) 2023 Kurt Sassenrath.
|
|
||||||
//
|
|
||||||
// License TBD.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
#ifndef tests_msgpack_unpacker_25b118dd14e2b1b4
|
|
||||||
#define tests_msgpack_unpacker_25b118dd14e2b1b4
|
|
||||||
|
|
||||||
#include "parselink/msgpack/core/unpacker.h"
|
|
||||||
|
|
||||||
#include "test_utils.h"
|
|
||||||
|
|
||||||
// Helper template to check that a type is either equal to itself, or verify
|
|
||||||
// that it won't unpack correctly.
|
|
||||||
template <typename T, typename U>
|
|
||||||
constexpr bool unpack_type_check(msgpack::unpacker unpacker) noexcept {
|
|
||||||
auto wrong_type = tl::make_unexpected(msgpack::error::wrong_type);
|
|
||||||
auto compatible_type = std::same_as<T, U>
|
|
||||||
|| (std::is_unsigned_v<T> && std::is_unsigned_v<U>)
|
|
||||||
|| (std::is_signed_v<T> && std::is_signed_v<U>);
|
|
||||||
auto result = compatible_type || unpacker.unpack<U>() == wrong_type;
|
|
||||||
if (!result) {
|
|
||||||
fmt::print("Unpack_type_check failed: {}\n",
|
|
||||||
std::source_location::current().function_name());
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate the wrong types do not unpack correctly, or fail.
|
|
||||||
template <typename Expected, typename... OtherTypes>
|
|
||||||
constexpr bool try_unpacking_types(msgpack::unpacker unpacker) noexcept {
|
|
||||||
return ((unpack_type_check<Expected, OtherTypes>(unpacker)) && ...);
|
|
||||||
}
|
|
||||||
|
|
||||||
#define SUPPORTED_TYPES \
|
|
||||||
msgpack::nil, msgpack::invalid, bool, std::uint8_t, std::uint16_t, \
|
|
||||||
std::uint32_t, std::uint64_t, std::int8_t, std::int16_t, \
|
|
||||||
std::int32_t, std::int64_t, std::string_view
|
|
||||||
|
|
||||||
template <typename Expected>
|
|
||||||
constexpr bool type_check_unpacker(msgpack::unpacker unpacker) noexcept {
|
|
||||||
return try_unpacking_types<Expected, SUPPORTED_TYPES>(unpacker);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // tests_msgpack_unpacker_25b118dd14e2b1b4
|
|
||||||
@ -1,131 +0,0 @@
|
|||||||
//-----------------------------------------------------------------------------
|
|
||||||
// ___ __ _ _
|
|
||||||
// / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __
|
|
||||||
// / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ /
|
|
||||||
// / ___/ (_| | | \__ \ __/ /__| | | | | <
|
|
||||||
// \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ .
|
|
||||||
//
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Author: Kurt Sassenrath
|
|
||||||
// Module: msgpack
|
|
||||||
//
|
|
||||||
// Default packer tests.
|
|
||||||
//
|
|
||||||
// Copyright (c) 2023 Kurt Sassenrath.
|
|
||||||
//
|
|
||||||
// License TBD.
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
#include "test_unpacker.h"
|
|
||||||
|
|
||||||
#include <rapidcheck.h>
|
|
||||||
|
|
||||||
using namespace boost::ut;
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
constexpr auto equal(auto a, auto b) {
|
|
||||||
return std::equal(std::begin(a), std::end(a), std::begin(b), std::end(b));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename... Bytes>
|
|
||||||
constexpr std::array<std::byte, sizeof...(Bytes)> make_bytes(Bytes&&... bytes) {
|
|
||||||
return {std::byte(std::forward<Bytes>(bytes))...};
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T, typename U>
|
|
||||||
constexpr bool test_unpack(msgpack::unpacker unpacker) noexcept {
|
|
||||||
auto wrong_type = tl::make_unexpected(msgpack::error::wrong_type);
|
|
||||||
return std::same_as<T, U> || unpacker.unpack<U>() == wrong_type;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Expected, typename... OtherTypes>
|
|
||||||
constexpr bool test_types(msgpack::unpacker unpacker) noexcept {
|
|
||||||
return ((test_unpack<Expected, OtherTypes>(unpacker)) && ...);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Expected>
|
|
||||||
constexpr bool expect_invalid(msgpack::unpacker unpacker) noexcept {
|
|
||||||
return test_types<Expected, msgpack::nil, msgpack::invalid, bool,
|
|
||||||
std::uint8_t, std::string_view>(unpacker);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct get_marker;
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct get_marker<std::uint8_t> {
|
|
||||||
static constexpr std::byte marker = msgpack::format::uint8::marker;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct get_marker<std::uint16_t> {
|
|
||||||
static constexpr std::byte marker = msgpack::format::uint16::marker;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct get_marker<std::uint32_t> {
|
|
||||||
static constexpr std::byte marker = msgpack::format::uint32::marker;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct get_marker<std::uint64_t> {
|
|
||||||
static constexpr std::byte marker = msgpack::format::uint64::marker;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Utilize rapidcheck to generate random packed payloads, and unpacking the
|
|
||||||
// correct value
|
|
||||||
template <typename PackType, typename UnpackType>
|
|
||||||
auto check_unpack_integer() noexcept {
|
|
||||||
auto result = rc::check([](PackType value) {
|
|
||||||
std::vector<std::byte> payload = {get_marker<PackType>::marker};
|
|
||||||
auto bytes = host_to_be(
|
|
||||||
std::bit_cast<std::array<std::byte, sizeof(PackType)>>(value));
|
|
||||||
std::copy(bytes.begin(), bytes.end(), std::back_inserter(payload));
|
|
||||||
msgpack::unpacker unpacker(payload);
|
|
||||||
auto unpacked_value = unpacker.unpack<UnpackType>();
|
|
||||||
if (std::numeric_limits<UnpackType>::max() < value
|
|
||||||
|| std::numeric_limits<UnpackType>::min() > value) {
|
|
||||||
return unpacked_value
|
|
||||||
== tl::make_unexpected(msgpack::error::will_truncate);
|
|
||||||
} else {
|
|
||||||
return unpacked_value.has_value() && unpacked_value == value;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (!result) {
|
|
||||||
fmt::print("Failed on {}\n",
|
|
||||||
std::source_location::current().function_name());
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // anonymous namespace
|
|
||||||
|
|
||||||
suite unpack_unsigned_types = [] {
|
|
||||||
"unpacker::unpack<std::uint8_t>"_test = [] {
|
|
||||||
expect(check_unpack_integer<std::uint8_t, std::uint8_t>());
|
|
||||||
expect(check_unpack_integer<std::uint16_t, std::uint8_t>());
|
|
||||||
expect(check_unpack_integer<std::uint32_t, std::uint8_t>());
|
|
||||||
expect(check_unpack_integer<std::uint64_t, std::uint8_t>());
|
|
||||||
};
|
|
||||||
|
|
||||||
"unpacker::unpack<std::uint16_t>"_test = [] {
|
|
||||||
expect(check_unpack_integer<std::uint8_t, std::uint16_t>());
|
|
||||||
expect(check_unpack_integer<std::uint16_t, std::uint16_t>());
|
|
||||||
expect(check_unpack_integer<std::uint32_t, std::uint16_t>());
|
|
||||||
expect(check_unpack_integer<std::uint64_t, std::uint16_t>());
|
|
||||||
};
|
|
||||||
|
|
||||||
"unpacker::unpack<std::uint32_t>"_test = [] {
|
|
||||||
expect(check_unpack_integer<std::uint8_t, std::uint32_t>());
|
|
||||||
expect(check_unpack_integer<std::uint16_t, std::uint32_t>());
|
|
||||||
expect(check_unpack_integer<std::uint32_t, std::uint32_t>());
|
|
||||||
expect(check_unpack_integer<std::uint64_t, std::uint32_t>());
|
|
||||||
};
|
|
||||||
|
|
||||||
"unpacker::unpack<std::uint64_t>"_test = [] {
|
|
||||||
expect(check_unpack_integer<std::uint8_t, std::uint64_t>());
|
|
||||||
expect(check_unpack_integer<std::uint16_t, std::uint64_t>());
|
|
||||||
expect(check_unpack_integer<std::uint32_t, std::uint64_t>());
|
|
||||||
expect(check_unpack_integer<std::uint64_t, std::uint64_t>());
|
|
||||||
};
|
|
||||||
};
|
|
||||||
Loading…
Reference in New Issue
Block a user