// Glaze Library // For the license information refer to glaze.hpp #pragma once #include #include #include #include #include "glaze/core/buffer_traits.hpp" #include "glaze/core/opts.hpp" #include "glaze/core/reflect.hpp" #include "glaze/core/seek.hpp" #include "glaze/core/to.hpp" #include "glaze/core/write.hpp" #include "glaze/msgpack/common.hpp" #include "glaze/util/dump.hpp" #include "glaze/util/for_each.hpp" #include "glaze/util/string_literal.hpp" #include "glaze/util/variant.hpp" namespace glz::msgpack::detail { template GLZ_ALWAYS_INLINE void write_nil(B& b, size_t& ix) noexcept(not vector_like) { dump(std::byte{nil}, b, ix); } template GLZ_ALWAYS_INLINE bool write_nil(is_context auto& ctx, B& b, size_t& ix) { if (!ensure_space(ctx, b, ix + 1 + write_padding_bytes)) [[unlikely]] { return false; } dump(std::byte{nil}, b, ix); return true; } template GLZ_ALWAYS_INLINE void write_bool(const bool value, B& b, size_t& ix) noexcept(not vector_like) { dump(std::byte{static_cast(value ? bool_true : bool_false)}, b, ix); } template GLZ_ALWAYS_INLINE bool write_bool(is_context auto& ctx, const bool value, B& b, size_t& ix) { if (!ensure_space(ctx, b, ix + 1 + write_padding_bytes)) [[unlikely]] { return false; } dump(std::byte{static_cast(value ? bool_true : bool_false)}, b, ix); return true; } template GLZ_ALWAYS_INLINE void write_unsigned(uint64_t value, B& b, size_t& ix) noexcept(not vector_like) { if (value <= 0x7F) { dump(std::byte{static_cast(value)}, b, ix); } else if (value <= std::numeric_limits::max()) { dump(std::byte{uint8}, b, ix); dump_uint8(static_cast(value), b, ix); } else if (value <= std::numeric_limits::max()) { dump(std::byte{uint16}, b, ix); dump_uint16(static_cast(value), b, ix); } else if (value <= std::numeric_limits::max()) { dump(std::byte{uint32}, b, ix); dump_uint32(static_cast(value), b, ix); } else { dump(std::byte{uint64}, b, ix); dump_uint64(value, b, ix); } } template GLZ_ALWAYS_INLINE bool write_unsigned(is_context auto& ctx, uint64_t value, B& b, size_t& ix) { // Max size is 9 bytes (1 byte type + 8 bytes value) if (!ensure_space(ctx, b, ix + 9 + write_padding_bytes)) [[unlikely]] { return false; } if (value <= 0x7F) { dump(std::byte{static_cast(value)}, b, ix); } else if (value <= std::numeric_limits::max()) { dump(std::byte{uint8}, b, ix); dump_uint8(static_cast(value), b, ix); } else if (value <= std::numeric_limits::max()) { dump(std::byte{uint16}, b, ix); dump_uint16(static_cast(value), b, ix); } else if (value <= std::numeric_limits::max()) { dump(std::byte{uint32}, b, ix); dump_uint32(static_cast(value), b, ix); } else { dump(std::byte{uint64}, b, ix); dump_uint64(value, b, ix); } return true; } template GLZ_ALWAYS_INLINE void write_signed(int64_t value, B& b, size_t& ix) noexcept(not vector_like) { if (value >= -32 && value <= 127) { dump(std::byte{static_cast(value)}, b, ix); } else if (value >= std::numeric_limits::min() && value <= std::numeric_limits::max()) { dump(std::byte{int8}, b, ix); dump_uint8(static_cast(value), b, ix); } else if (value >= std::numeric_limits::min() && value <= std::numeric_limits::max()) { dump(std::byte{int16}, b, ix); dump_uint16(static_cast(value), b, ix); } else if (value >= std::numeric_limits::min() && value <= std::numeric_limits::max()) { dump(std::byte{int32}, b, ix); dump_uint32(static_cast(value), b, ix); } else { dump(std::byte{int64}, b, ix); dump_uint64(static_cast(value), b, ix); } } template GLZ_ALWAYS_INLINE bool write_signed(is_context auto& ctx, int64_t value, B& b, size_t& ix) { // Max size is 9 bytes (1 byte type + 8 bytes value) if (!ensure_space(ctx, b, ix + 9 + write_padding_bytes)) [[unlikely]] { return false; } if (value >= -32 && value <= 127) { dump(std::byte{static_cast(value)}, b, ix); } else if (value >= std::numeric_limits::min() && value <= std::numeric_limits::max()) { dump(std::byte{int8}, b, ix); dump_uint8(static_cast(value), b, ix); } else if (value >= std::numeric_limits::min() && value <= std::numeric_limits::max()) { dump(std::byte{int16}, b, ix); dump_uint16(static_cast(value), b, ix); } else if (value >= std::numeric_limits::min() && value <= std::numeric_limits::max()) { dump(std::byte{int32}, b, ix); dump_uint32(static_cast(value), b, ix); } else { dump(std::byte{int64}, b, ix); dump_uint64(static_cast(value), b, ix); } return true; } template GLZ_ALWAYS_INLINE void write_floating(const auto value, B& b, size_t& ix) noexcept(not vector_like) { using T = std::decay_t; if constexpr (sizeof(T) <= sizeof(float)) { dump(std::byte{float32}, b, ix); dump_float32(static_cast(value), b, ix); } else { dump(std::byte{float64}, b, ix); dump_float64(static_cast(value), b, ix); } } template GLZ_ALWAYS_INLINE bool write_floating(is_context auto& ctx, const auto value, B& b, size_t& ix) { using T = std::decay_t; // Max size is 9 bytes (1 byte type + 8 bytes value) if (!ensure_space(ctx, b, ix + 9 + write_padding_bytes)) [[unlikely]] { return false; } if constexpr (sizeof(T) <= sizeof(float)) { dump(std::byte{float32}, b, ix); dump_float32(static_cast(value), b, ix); } else { dump(std::byte{float64}, b, ix); dump_float64(static_cast(value), b, ix); } return true; } template GLZ_ALWAYS_INLINE void write_str_header(size_t size, B& b, size_t& ix) noexcept(not vector_like) { if (size <= 31) { dump(std::byte{static_cast(fixstr_bits | size)}, b, ix); } else if (size <= std::numeric_limits::max()) { dump(std::byte{str8}, b, ix); dump_uint8(static_cast(size), b, ix); } else if (size <= std::numeric_limits::max()) { dump(std::byte{str16}, b, ix); dump_uint16(static_cast(size), b, ix); } else { dump(std::byte{str32}, b, ix); dump_uint32(static_cast(size), b, ix); } } template GLZ_ALWAYS_INLINE bool write_str_header(is_context auto& ctx, size_t size, B& b, size_t& ix) { // Max header size is 5 bytes (1 byte type + 4 bytes length) if (!ensure_space(ctx, b, ix + 5 + write_padding_bytes)) [[unlikely]] { return false; } if (size <= 31) { dump(std::byte{static_cast(fixstr_bits | size)}, b, ix); } else if (size <= std::numeric_limits::max()) { dump(std::byte{str8}, b, ix); dump_uint8(static_cast(size), b, ix); } else if (size <= std::numeric_limits::max()) { dump(std::byte{str16}, b, ix); dump_uint16(static_cast(size), b, ix); } else { dump(std::byte{str32}, b, ix); dump_uint32(static_cast(size), b, ix); } return true; } template GLZ_ALWAYS_INLINE void write_array_header(size_t size, B& b, size_t& ix) noexcept(not vector_like) { if (size <= 15) { dump(std::byte{static_cast(fixarray_bits | size)}, b, ix); } else if (size <= std::numeric_limits::max()) { dump(std::byte{array16}, b, ix); dump_uint16(static_cast(size), b, ix); } else { dump(std::byte{array32}, b, ix); dump_uint32(static_cast(size), b, ix); } } template GLZ_ALWAYS_INLINE bool write_array_header(is_context auto& ctx, size_t size, B& b, size_t& ix) { // Max header size is 5 bytes (1 byte type + 4 bytes length) if (!ensure_space(ctx, b, ix + 5 + write_padding_bytes)) [[unlikely]] { return false; } if (size <= 15) { dump(std::byte{static_cast(fixarray_bits | size)}, b, ix); } else if (size <= std::numeric_limits::max()) { dump(std::byte{array16}, b, ix); dump_uint16(static_cast(size), b, ix); } else { dump(std::byte{array32}, b, ix); dump_uint32(static_cast(size), b, ix); } return true; } template GLZ_ALWAYS_INLINE void write_map_header(size_t size, B& b, size_t& ix) noexcept(not vector_like) { if (size <= 15) { dump(std::byte{static_cast(fixmap_bits | size)}, b, ix); } else if (size <= std::numeric_limits::max()) { dump(std::byte{map16}, b, ix); dump_uint16(static_cast(size), b, ix); } else { dump(std::byte{map32}, b, ix); dump_uint32(static_cast(size), b, ix); } } template GLZ_ALWAYS_INLINE bool write_map_header(is_context auto& ctx, size_t size, B& b, size_t& ix) { // Max header size is 5 bytes (1 byte type + 4 bytes length) if (!ensure_space(ctx, b, ix + 5 + write_padding_bytes)) [[unlikely]] { return false; } if (size <= 15) { dump(std::byte{static_cast(fixmap_bits | size)}, b, ix); } else if (size <= std::numeric_limits::max()) { dump(std::byte{map16}, b, ix); dump_uint16(static_cast(size), b, ix); } else { dump(std::byte{map32}, b, ix); dump_uint32(static_cast(size), b, ix); } return true; } template GLZ_ALWAYS_INLINE void write_binary_header(size_t size, B& b, size_t& ix) noexcept(not vector_like) { if (size <= std::numeric_limits::max()) { dump(std::byte{bin8}, b, ix); dump_uint8(static_cast(size), b, ix); } else if (size <= std::numeric_limits::max()) { dump(std::byte{bin16}, b, ix); dump_uint16(static_cast(size), b, ix); } else { dump(std::byte{bin32}, b, ix); dump_uint32(static_cast(size), b, ix); } } template GLZ_ALWAYS_INLINE bool write_binary_header(is_context auto& ctx, size_t size, B& b, size_t& ix) { // Max header size is 5 bytes (1 byte type + 4 bytes length) if (!ensure_space(ctx, b, ix + 5 + write_padding_bytes)) [[unlikely]] { return false; } if (size <= std::numeric_limits::max()) { dump(std::byte{bin8}, b, ix); dump_uint8(static_cast(size), b, ix); } else if (size <= std::numeric_limits::max()) { dump(std::byte{bin16}, b, ix); dump_uint16(static_cast(size), b, ix); } else { dump(std::byte{bin32}, b, ix); dump_uint32(static_cast(size), b, ix); } return true; } template GLZ_ALWAYS_INLINE bool dump_raw_bytes(is_context auto& ctx, const char* data, size_t size, B& b, size_t& ix) noexcept(not vector_like) { if (size == 0) { return true; } if (!ensure_space(ctx, b, ix + size + write_padding_bytes)) [[unlikely]] { return false; } std::memcpy(&b[ix], data, size); ix += size; return true; } } namespace glz { template <> struct serialize { template GLZ_ALWAYS_INLINE static void op(T&& value, Ctx&& ctx, B&& b, IX&& ix) { to>::template op(std::forward(value), std::forward(ctx), std::forward(b), std::forward(ix)); } }; template concept write_msgpack_partial_invocable = requires(T&& value, Ctx&& ctx, B&& b, IX&& ix) { to_partial>::template op( std::forward(value), std::forward(ctx), std::forward(b), std::forward(ix)); }; template <> struct serialize_partial { template static void op(T&& value, Ctx&& ctx, B&& b, IX&& ix) { if constexpr (std::count(Partial.begin(), Partial.end(), "") > 0) { serialize::op(std::forward(value), std::forward(ctx), std::forward(b), std::forward(ix)); } else if constexpr (write_msgpack_partial_invocable) { to_partial>::template op( std::forward(value), std::forward(ctx), std::forward(b), std::forward(ix)); } else { static_assert(false_v, "Glaze metadata is probably needed for your type"); } } }; template requires(glaze_value_t && !custom_write) struct to { template GLZ_ALWAYS_INLINE static void op(Value&& value, Ctx&& ctx, B&& b, IX&& ix) { using V = std::remove_cvref_t(), meta_wrapper_v))>; to::template op(get_member(std::forward(value), meta_wrapper_v), std::forward(ctx), std::forward(b), std::forward(ix)); } }; template struct to { template GLZ_ALWAYS_INLINE static void op(auto&&, is_context auto&& ctx, B&& b, IX&& ix) { if (!msgpack::detail::write_nil(ctx, b, ix)) [[unlikely]] { return; } } }; template struct to { template GLZ_ALWAYS_INLINE static void op(Value&& value, Ctx&& ctx, B&& b, IX&& ix) { if (!value) { if (!msgpack::detail::write_nil(ctx, b, ix)) [[unlikely]] { return; } return; } serialize::op(*value, ctx, b, ix); } }; template requires(nullable_value_t && !nullable_like) struct to { template GLZ_ALWAYS_INLINE static void op(Value&& value, Ctx&& ctx, B&& b, IX&& ix) { if (!value.has_value()) { if (!msgpack::detail::write_nil(ctx, b, ix)) [[unlikely]] { return; } return; } serialize::op(value.value(), ctx, b, ix); } }; template struct to { template GLZ_ALWAYS_INLINE static void op(Value&& value, Ctx&& ctx, B&& b, IX&& ix) { if (!value.val) { if (!msgpack::detail::write_nil(ctx, b, ix)) [[unlikely]] { return; } return; } serialize::op(*value.val, ctx, b, ix); } }; template struct to { template GLZ_ALWAYS_INLINE static void op(const bool value, is_context auto&& ctx, B&& b, IX&& ix) { if (!msgpack::detail::write_bool(ctx, value, b, ix)) [[unlikely]] { return; } } }; template struct to { template static void op(auto&& value, is_context auto&& ctx, B&& b, IX&& ix) { const auto num_bytes = (value.size() + 7) / 8; std::vector bytes(num_bytes); for (size_t byte_i{}, i{}; byte_i < num_bytes; ++byte_i) { for (size_t bit_i = 0; bit_i < 8 && i < value.size(); ++bit_i, ++i) { bytes[byte_i] |= uint8_t(value[i]) << uint8_t(bit_i); } } if (!msgpack::detail::write_binary_header(ctx, bytes.size(), b, ix)) [[unlikely]] { return; } msgpack::detail::dump_raw_bytes(ctx, reinterpret_cast(bytes.data()), bytes.size(), b, ix); } }; template requires(std::is_enum_v && !glaze_enum_t && !custom_write) struct to { template GLZ_ALWAYS_INLINE static void op(Value&& value, Ctx&& ctx, B&& b, IX&& ix) { using U = std::underlying_type_t>; to::template op(static_cast(value), ctx, b, ix); } }; template requires(is_named_enum) struct to { template GLZ_ALWAYS_INLINE static void op(Value&& value, Ctx&& ctx, B&& b, IX&& ix) { const sv str = get_enum_name(value); if (!str.empty()) { if (!msgpack::detail::write_str_header(ctx, str.size(), b, ix)) [[unlikely]] { return; } if (!msgpack::detail::dump_raw_bytes(ctx, str.data(), str.size(), b, ix)) [[unlikely]] { return; } } else { // fallback to numeric representation serialize::op(static_cast>(value), ctx, b, ix); } } }; template requires(num_t || char_t) struct to { template GLZ_ALWAYS_INLINE static void op(auto&& value, is_context auto&& ctx, B&& b, IX&& ix) { if constexpr (std::floating_point>) { if (!msgpack::detail::write_floating(ctx, value, b, ix)) [[unlikely]] { return; } } else if constexpr (std::is_signed_v>) { if (!msgpack::detail::write_signed(ctx, static_cast(value), b, ix)) [[unlikely]] { return; } } else { if (!msgpack::detail::write_unsigned(ctx, static_cast(value), b, ix)) [[unlikely]] { return; } } } }; template struct to { template GLZ_ALWAYS_INLINE static void op(Value&& value, Ctx&& ctx, B&& b, IX&& ix) { const std::string_view str{value.data(), value.size()}; if (!msgpack::detail::write_str_header(ctx, str.size(), b, ix)) [[unlikely]] { return; } msgpack::detail::dump_raw_bytes(ctx, str.data(), str.size(), b, ix); } }; template struct to { template GLZ_ALWAYS_INLINE static void op(Value&& value, Ctx&& ctx, B&& b, IX&& ix) { const std::string_view str{value.data(), value.size()}; if (!msgpack::detail::write_str_header(ctx, str.size(), b, ix)) [[unlikely]] { return; } msgpack::detail::dump_raw_bytes(ctx, str.data(), str.size(), b, ix); } }; template struct to { template GLZ_ALWAYS_INLINE static void op(Value&& value, Ctx&& ctx, B&& b, IX&& ix) { const std::string_view str{value}; if (!msgpack::detail::write_str_header(ctx, str.size(), b, ix)) [[unlikely]] { return; } msgpack::detail::dump_raw_bytes(ctx, str.data(), str.size(), b, ix); } }; template requires(!custom_write) struct to { static constexpr auto N = reflect::size; template static consteval size_t count_members() { return [](std::index_sequence) consteval { return (size_t{} + ... + (always_skipped> ? size_t{} : size_t{1})); }(std::make_index_sequence{}); } template GLZ_ALWAYS_INLINE static void op(Value&& value, Ctx&& ctx, B&& b, IX&& ix) { if constexpr (check_structs_as_arrays(Opts)) { if (!msgpack::detail::write_array_header(ctx, count_members(), b, ix)) [[unlikely]] { return; } for_each([&]() { if (bool(ctx.error)) [[unlikely]] { return; } if constexpr (!always_skipped>) { serialize::op(get_member(value, get(reflect::values)), ctx, b, ix); if constexpr (is_output_streaming) { flush_buffer(b, ix); } } }); } else { if (!msgpack::detail::write_map_header(ctx, count_members(), b, ix)) [[unlikely]] { return; } for_each([&]() { if (bool(ctx.error)) [[unlikely]] { return; } if constexpr (!always_skipped>) { static constexpr sv key = reflect::keys[I]; if (!msgpack::detail::write_str_header(ctx, key.size(), b, ix)) [[unlikely]] { return; } if (!msgpack::detail::dump_raw_bytes(ctx, key.data(), key.size(), b, ix)) [[unlikely]] { return; } serialize::op(get_member(value, get(reflect::values)), ctx, b, ix); if constexpr (is_output_streaming) { flush_buffer(b, ix); } } }); } } }; template requires(!custom_write) struct to { template GLZ_ALWAYS_INLINE static void op(Value&& value, Ctx&& ctx, B&& b, IX&& ix) { to::template op(to_tie(value), ctx, b, ix); } }; template struct to { template GLZ_ALWAYS_INLINE static void op(Value&& value, Ctx&& ctx, B&& b, IX&& ix) { using map_t = std::remove_cvref_t; using val_t = std::remove_cvref_t>; constexpr bool may_skip = null_t && Opts.skip_null_members; size_t count = value.size(); if constexpr (may_skip) { count = 0; for (auto&& item : value) { if (!skip_member(item.second)) ++count; } } if (!msgpack::detail::write_map_header(ctx, count, b, ix)) [[unlikely]] { return; } for (auto&& item : value) { if (bool(ctx.error)) [[unlikely]] { return; } if constexpr (may_skip) { if (skip_member(item.second)) continue; } serialize::op(item.first, ctx, b, ix); serialize::op(item.second, ctx, b, ix); if constexpr (is_output_streaming) { flush_buffer(b, ix); } } } }; #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable : 4702) // unreachable code from if constexpr #endif template struct to { template GLZ_ALWAYS_INLINE static void op(Value&& value, Ctx&& ctx, B&& b, IX&& ix) { if constexpr (msgpack::binary_range_v) { const size_t size = value.size(); if (!msgpack::detail::write_binary_header(ctx, size, b, ix)) [[unlikely]] { return; } if (size == 0) { return; } const auto* data = reinterpret_cast(value.data()); msgpack::detail::dump_raw_bytes(ctx, data, size, b, ix); return; } if (!msgpack::detail::write_array_header(ctx, value.size(), b, ix)) [[unlikely]] { return; } for (auto&& element : value) { if (bool(ctx.error)) [[unlikely]] { return; } serialize::op(element, ctx, b, ix); if constexpr (is_output_streaming) { flush_buffer(b, ix); } } } #ifdef _MSC_VER #pragma warning(pop) #endif }; template <> struct to { template GLZ_ALWAYS_INLINE static void op(Value&& value, Ctx&& ctx, B&& b, IX&& ix) { const size_t len = value.data.size(); const auto type_byte = static_cast(value.type); // Calculate max header + data size and check upfront // Max header is 6 bytes (ext32: 1 byte type + 4 bytes len + 1 byte ext type) if (!ensure_space(ctx, b, ix + 6 + len + write_padding_bytes)) [[unlikely]] { return; } auto dump_payload = [&](auto&& emit_header) { emit_header(); dump(std::byte{type_byte}, b, ix); if (len > 0) { const auto* data = reinterpret_cast(value.data.data()); std::memcpy(&b[ix], data, len); ix += len; } }; switch (len) { case 1: dump_payload([&] { dump(std::byte{msgpack::fixext1}, b, ix); }); return; case 2: dump_payload([&] { dump(std::byte{msgpack::fixext2}, b, ix); }); return; case 4: dump_payload([&] { dump(std::byte{msgpack::fixext4}, b, ix); }); return; case 8: dump_payload([&] { dump(std::byte{msgpack::fixext8}, b, ix); }); return; case 16: dump_payload([&] { dump(std::byte{msgpack::fixext16}, b, ix); }); return; default: break; } if (len <= std::numeric_limits::max()) { dump_payload([&] { dump(std::byte{msgpack::ext8}, b, ix); msgpack::dump_uint8(static_cast(len), b, ix); }); } else if (len <= std::numeric_limits::max()) { dump_payload([&] { dump(std::byte{msgpack::ext16}, b, ix); msgpack::dump_uint16(static_cast(len), b, ix); }); } else if (len <= std::numeric_limits::max()) { dump_payload([&] { dump(std::byte{msgpack::ext32}, b, ix); msgpack::dump_uint32(static_cast(len), b, ix); }); } else { ctx.error = error_code::exceeded_static_array_size; } } }; // MessagePack timestamp extension (type -1) // Chooses the most compact format: // - Timestamp 32: when nanoseconds == 0 and seconds fits in uint32 // - Timestamp 64: when seconds fits in 34 bits (0 to 17179869183) // - Timestamp 96: for all other cases (including negative seconds) template <> struct to { template GLZ_ALWAYS_INLINE static void op(Value&& value, Ctx&& ctx, B&& b, IX&& ix) { // Max size is 15 bytes (timestamp 96: 3 header + 12 payload) if (!ensure_space(ctx, b, ix + 15 + write_padding_bytes)) [[unlikely]] { return; } const auto type_byte = static_cast(msgpack::timestamp_type); // Timestamp 32: seconds only, fits in uint32, no nanoseconds if (value.nanoseconds == 0 && value.seconds >= 0 && value.seconds <= static_cast(std::numeric_limits::max())) { dump(std::byte{msgpack::fixext4}, b, ix); dump(std::byte{type_byte}, b, ix); msgpack::dump_uint32(static_cast(value.seconds), b, ix); } // Timestamp 64: 30-bit nanoseconds + 34-bit seconds else if (value.seconds >= 0 && value.seconds <= 0x3FFFFFFFF) { dump(std::byte{msgpack::fixext8}, b, ix); dump(std::byte{type_byte}, b, ix); // Upper 30 bits: nanoseconds, lower 34 bits: seconds const uint64_t val64 = (static_cast(value.nanoseconds) << 34) | static_cast(value.seconds); msgpack::dump_uint64(val64, b, ix); } // Timestamp 96: full range with signed seconds else { dump(std::byte{msgpack::ext8}, b, ix); msgpack::dump_uint8(12, b, ix); // 12 bytes payload dump(std::byte{type_byte}, b, ix); msgpack::dump_uint32(value.nanoseconds, b, ix); msgpack::dump_uint64(static_cast(value.seconds), b, ix); } } }; // std::chrono::system_clock::time_point support // Converts to msgpack::timestamp for serialization template requires std::same_as, std::chrono::system_clock::time_point> struct to { template GLZ_ALWAYS_INLINE static void op(Value&& value, Ctx&& ctx, B&& b, IX&& ix) { using namespace std::chrono; const auto epoch = value.time_since_epoch(); const auto secs = duration_cast(epoch); const auto nsecs = duration_cast(epoch - secs); msgpack::timestamp ts; ts.seconds = secs.count(); ts.nanoseconds = static_cast(nsecs.count()); to::template op(ts, std::forward(ctx), std::forward(b), std::forward(ix)); } }; template requires(glaze_object_t || writable_map_t || reflectable) struct to_partial { template GLZ_ALWAYS_INLINE static void op(Value&& value, Ctx&& ctx, B&& b, IX&& ix) { static constexpr auto sorted = sort_json_ptrs(Partial); static constexpr auto groups = glz::group_json_ptrs(); static constexpr auto N = glz::tuple_size_v>; if (!msgpack::detail::write_map_header(ctx, N, b, ix)) [[unlikely]] { return; } if constexpr (glaze_object_t) { for_each([&]() { if (bool(ctx.error)) [[unlikely]] { return; } static constexpr auto group = glz::get(groups); static constexpr auto key = get<0>(group); static constexpr auto sub_partial = get<1>(group); static constexpr auto index = key_index(key); static_assert(index < reflect::size, "Invalid key passed to partial write"); if (!msgpack::detail::write_str_header(ctx, key.size(), b, ix)) [[unlikely]] { return; } if (!msgpack::detail::dump_raw_bytes(ctx, key.data(), key.size(), b, ix)) [[unlikely]] { return; } if constexpr (glaze_object_t) { static constexpr auto member = get(reflect::values); serialize_partial::op(get_member(value, member), ctx, b, ix); } else { serialize_partial::op(get_member(value, get(to_tie(value))), ctx, b, ix); } }); } else if constexpr (writable_map_t) { for_each([&]() { if (bool(ctx.error)) [[unlikely]] { return; } static constexpr auto group = glz::get(groups); static constexpr auto key_value = get<0>(group); static constexpr auto sub_partial = get<1>(group); if constexpr (findable, decltype(key_value)>) { serialize::op(key_value, ctx, b, ix); auto it = value.find(key_value); if (it != value.end()) { serialize_partial::op(it->second, ctx, b, ix); } else { ctx.error = error_code::invalid_partial_key; return; } } else { auto k = typename std::decay_t::key_type(key_value); // TODO handle numeric pointer segments serialize::op(k, ctx, b, ix); auto it = value.find(k); if (it != value.end()) { serialize_partial::op(it->second, ctx, b, ix); } else { ctx.error = error_code::invalid_partial_key; return; } } }); } } }; template requires(!custom_write) struct to { template GLZ_ALWAYS_INLINE static void op(Value&& value, Ctx&& ctx, B&& b, IX&& ix) { static constexpr auto N = reflect::size; if (!msgpack::detail::write_array_header(ctx, N, b, ix)) [[unlikely]] { return; } for_each([&]() { if (bool(ctx.error)) [[unlikely]] { return; } serialize::op(get_member(value, get(reflect::values)), ctx, b, ix); }); } }; template requires(tuple_t || is_std_tuple) struct to { template GLZ_ALWAYS_INLINE static void op(Value&& value, Ctx&& ctx, B&& b, IX&& ix) { static constexpr auto N = glz::tuple_size_v; if (!msgpack::detail::write_array_header(ctx, N, b, ix)) [[unlikely]] { return; } if constexpr (is_std_tuple) { [&](std::index_sequence) { ((void)(bool(ctx.error) ? void() : (serialize::op(std::get(value), ctx, b, ix), void())), ...); }(std::make_index_sequence{}); } else { [&](std::index_sequence) { ((void)(bool(ctx.error) ? void() : (serialize::op(glz::get(value), ctx, b, ix), void())), ...); }(std::make_index_sequence{}); } } }; template struct to { template GLZ_ALWAYS_INLINE static void op(Value&& value, Ctx&& ctx, B&& b, IX&& ix) { if (!msgpack::detail::write_array_header(ctx, 2, b, ix)) [[unlikely]] { return; } static constexpr auto ids = ids_v; if (!msgpack::detail::write_str_header(ctx, ids[value.index()].size(), b, ix)) [[unlikely]] { return; } if (!msgpack::detail::dump_raw_bytes(ctx, ids[value.index()].data(), ids[value.index()].size(), b, ix)) [[unlikely]] { return; } std::visit([&](auto&& v) { serialize::op(v, ctx, b, ix); }, value); } }; template requires is_specialization_v struct to { template GLZ_ALWAYS_INLINE static void op(Value&& value, Ctx&& ctx, B&& b, IX&& ix) { using V = std::decay_t; static constexpr auto N = glz::tuple_size_v; if (!msgpack::detail::write_array_header(ctx, N, b, ix)) [[unlikely]] { return; } for_each([&]() { if (bool(ctx.error)) [[unlikely]] { return; } serialize::op(glz::get(value.value), ctx, b, ix); }); } }; template requires is_specialization_v struct to { template GLZ_ALWAYS_INLINE static void op(Value&& value, Ctx&& ctx, B&& b, IX&& ix) { using V = std::decay_t; static constexpr auto N = glz::tuple_size_v; if (!msgpack::detail::write_array_header(ctx, N, b, ix)) [[unlikely]] { return; } for_each([&]() { if (bool(ctx.error)) [[unlikely]] { return; } serialize::op(glz::get(value.value), ctx, b, ix); }); } }; template requires is_specialization_v struct to { template GLZ_ALWAYS_INLINE static void op(Value&& value, Ctx&& ctx, B&& b, IX&& ix) { using V = std::decay_t; static constexpr auto N = glz::tuple_size_v / 2; if (!msgpack::detail::write_map_header(ctx, N, b, ix)) [[unlikely]] { return; } for_each([&]() { if (bool(ctx.error)) [[unlikely]] { return; } serialize::op(glz::get<2 * I>(value.value), ctx, b, ix); serialize::op(glz::get<2 * I + 1>(value.value), ctx, b, ix); }); } }; template requires is_specialization_v struct to { template GLZ_ALWAYS_INLINE static void op(Value&& value, Ctx&& ctx, B&& b, IX&& ix) { using V = std::decay_t; static constexpr auto N = glz::tuple_size_v / 2; if (!msgpack::detail::write_map_header(ctx, N, b, ix)) [[unlikely]] { return; } for_each([&]() { if (bool(ctx.error)) [[unlikely]] { return; } serialize::op(glz::get<2 * I>(value.value), ctx, b, ix); serialize::op(glz::get<2 * I + 1>(value.value), ctx, b, ix); }); } }; template requires is_includer struct to { template GLZ_ALWAYS_INLINE static void op(auto&&, is_context auto&& ctx, B&& b, IX&& ix) { if (!msgpack::detail::write_str_header(ctx, 0, b, ix)) [[unlikely]] { return; } } }; template requires func_t struct to { template GLZ_ALWAYS_INLINE static void op(Value&& value, Ctx&& ctx, B&& b, IX&& ix) { constexpr auto name = name_v>; if (!msgpack::detail::write_str_header(ctx, name.size(), b, ix)) [[unlikely]] { return; } msgpack::detail::dump_raw_bytes(ctx, name.data(), name.size(), b, ix); } }; template T, output_buffer Buffer> [[nodiscard]] error_ctx write_msgpack(T&& value, Buffer&& buffer) { return write(std::forward(value), std::forward(buffer)); } template T, raw_buffer Buffer> [[nodiscard]] error_ctx write_msgpack(T&& value, Buffer&& buffer) { return write(std::forward(value), std::forward(buffer)); } template T> [[nodiscard]] glz::expected write_msgpack(T&& value) { return write(std::forward(value)); } template T, output_buffer Buffer> [[nodiscard]] error_ctx write_msgpack(T&& value, Buffer&& buffer) { return write(std::forward(value), std::forward(buffer)); } template T, raw_buffer Buffer> [[nodiscard]] error_ctx write_msgpack(T&& value, Buffer&& buffer) { return write(std::forward(value), std::forward(buffer)); } template T> [[nodiscard]] glz::expected write_msgpack(T&& value) { std::string buffer{}; const auto result = write(std::forward(value), buffer); if (result) [[unlikely]] { return glz::unexpected(static_cast(result)); } return {buffer}; } template T> [[nodiscard]] error_ctx write_file_msgpack(T&& value, const sv file_name, auto&& buffer) { const auto ec = write()>(std::forward(value), buffer); if (ec) { return ec; } if (auto file_ec = buffer_to_file(buffer, file_name); bool(file_ec)) { return {0, file_ec}; } return {}; } }