WIP unpacking

This commit is contained in:
Kurt Sassenrath 2024-01-08 19:58:12 -08:00
parent 0e6f05dd68
commit db601fb770
5 changed files with 299 additions and 6 deletions

View 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

View File

@ -1,4 +1,3 @@
//-----------------------------------------------------------------------------
// ___ __ _ _
// / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __

View 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

View File

@ -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;
}

View 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