parselink-old/source/include/parselink/msgpack/core/writer.h

275 lines
7.6 KiB
C++

//-----------------------------------------------------------------------------
// ___ __ _ _
// / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __
// / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ /
// / ___/ (_| | | \__ \ __/ /__| | | | | <
// \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ .
//
//-----------------------------------------------------------------------------
// Author: Kurt Sassenrath
// Module: msgpack
//
// Core writing implementation, upon which writers are built.
//
// Copyright (c) 2023 Kurt Sassenrath.
//
// License TBD.
//-----------------------------------------------------------------------------
#ifndef msgpack_core_writer_ce48a51aa6ed0858
#define msgpack_core_writer_ce48a51aa6ed0858
#include "format.h"
#include "../util/endianness.h"
#include <limits>
#include <type_traits>
#include <tl/expected.hpp>
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, writer_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, writer_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(writer_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(writer_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(writer_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:
using error_code = writer_error;
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_code::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