WIP unpacking
This commit is contained in:
parent
0e6f05dd68
commit
db601fb770
122
include/parselink/msgpack/core/detail/builtin_unpackable_types.h
Normal file
122
include/parselink/msgpack/core/detail/builtin_unpackable_types.h
Normal file
@ -0,0 +1,122 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// ___ __ _ _
|
||||
// / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __
|
||||
// / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ /
|
||||
// / ___/ (_| | | \__ \ __/ /__| | | | | <
|
||||
// \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ .
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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 {
|
||||
|
||||
// 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 byte_range = std::ranges::contiguous_range<T>
|
||||
&& std::same_as<std::ranges::range_value_t<T>, std::byte>;
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <typename>
|
||||
struct builtin_unpacker;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Built-in unpacker specializations
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
template <>
|
||||
struct builtin_unpacker<invalid> {
|
||||
static constexpr bool accept(std::byte marker) noexcept {
|
||||
return marker == format::nil::marker;
|
||||
}
|
||||
|
||||
static constexpr auto unpack(auto& unpacker) noexcept {
|
||||
++unpacker.in;
|
||||
return invalid{};
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct builtin_unpacker<nil> {
|
||||
static constexpr bool accept(std::byte marker) noexcept {
|
||||
return marker == format::nil::marker;
|
||||
}
|
||||
|
||||
static constexpr auto unpack(auto& unpacker) noexcept {
|
||||
++unpacker.in;
|
||||
return nil{};
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct builtin_unpacker<bool> {
|
||||
static constexpr bool accept(std::byte marker) noexcept {
|
||||
return (marker & ~format::boolean::mask) == format::boolean::marker;
|
||||
}
|
||||
|
||||
static constexpr auto unpack(auto& unpacker) noexcept {
|
||||
// In this case, we will not run out of room
|
||||
return static_cast<bool>((*(unpacker.in)++) & ~format::boolean::mask);
|
||||
}
|
||||
};
|
||||
|
||||
// This unpacker will accept any uintX, positive_fixint types.
|
||||
struct unsigned_int_unpacker {
|
||||
static constexpr bool accept(std::byte marker) noexcept {
|
||||
if (!(marker & 0x80)) {
|
||||
return true;
|
||||
}
|
||||
switch (marker) {
|
||||
case format::uint8::marker:
|
||||
case format::uint16::marker:
|
||||
case format::uint32::marker:
|
||||
case format::uint64::marker:
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
template <std::signed_integral T>
|
||||
struct builtin_unpacker<T> {
|
||||
static constexpr bool accept(std::byte marker) noexcept {
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace msgpack
|
||||
|
||||
#endif // msgpack_core_detail_builtin_unpackable_types_bf5de632f088a7d8
|
||||
@ -1,4 +1,3 @@
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// ___ __ _ _
|
||||
// / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __
|
||||
|
||||
86
include/parselink/msgpack/core/detail/unpackable_concepts.h
Normal file
86
include/parselink/msgpack/core/detail/unpackable_concepts.h
Normal file
@ -0,0 +1,86 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// ___ __ _ _
|
||||
// / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __
|
||||
// / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ /
|
||||
// / ___/ (_| | | \__ \ __/ /__| | | | | <
|
||||
// \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ .
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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 <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;
|
||||
};
|
||||
|
||||
} // 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, template <typename> typename Unpacker,
|
||||
typename Context = detail::unpacker_context_model>
|
||||
concept unpackable_with = requires(T&& value, Context& dest) {
|
||||
//{
|
||||
//Unpacker<std::remove_cvref_t<T>>::total_size(value)
|
||||
//} -> std::same_as<std::size_t>;
|
||||
{
|
||||
Unpacker<std::remove_cvref_t<T>>::unpack(dest)
|
||||
} -> std::same_as<T>;
|
||||
};
|
||||
|
||||
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
|
||||
@ -53,9 +53,6 @@
|
||||
|
||||
namespace msgpack {
|
||||
|
||||
template <typename T>
|
||||
struct pack_adapter {};
|
||||
|
||||
class packer {
|
||||
// Replace with output_range?
|
||||
using BufferType = std::span<std::byte>;
|
||||
@ -93,9 +90,8 @@ public:
|
||||
context ctx{curr_, end_};
|
||||
// If packing is successful, it updates iterators.
|
||||
auto ret = ctx.pack(std::forward<T>(v));
|
||||
if (!!ret) {
|
||||
if (ret) {
|
||||
curr_ = ctx.out;
|
||||
end_ = ctx.end;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
90
include/parselink/msgpack/core/unpacker.h
Normal file
90
include/parselink/msgpack/core/unpacker.h
Normal file
@ -0,0 +1,90 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// ___ __ _ _
|
||||
// / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __
|
||||
// / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ /
|
||||
// / ___/ (_| | | \__ \ __/ /__| | | | | <
|
||||
// \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ .
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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;
|
||||
}
|
||||
|
||||
private:
|
||||
BufferType buff_;
|
||||
decltype(std::begin(buff_)) curr_;
|
||||
decltype(std::end(buff_)) end_;
|
||||
};
|
||||
|
||||
} // namespace msgpack
|
||||
|
||||
#endif // msgpack_core_unpacker_910ec357d397ac7e
|
||||
Loading…
Reference in New Issue
Block a user