// Glaze Library // For the license information refer to glaze.hpp #pragma once #include "glaze/beve/header.hpp" #include "glaze/beve/key_traits.hpp" #include "glaze/core/opts.hpp" #include "glaze/core/reflect.hpp" #include "glaze/util/expected.hpp" #include "glaze/util/for_each.hpp" #include "glaze/util/variant.hpp" namespace glz { // Calculate the number of bytes needed to store a compressed integer [[nodiscard]] GLZ_ALWAYS_INLINE constexpr size_t compressed_int_size(uint64_t i) noexcept { if (i < 64) return 1; if (i < 16384) return 2; if (i < 1073741824) return 4; return 8; } // Compile-time version for known values template [[nodiscard]] consteval size_t compressed_int_size() noexcept { if constexpr (i < 64) return 1; else if constexpr (i < 16384) return 2; else if constexpr (i < 1073741824) return 4; else return 8; } // Forward declaration for the size calculation template template struct calculate_size; // Primary template for BEVE size calculation dispatch // The offset parameter tracks the absolute byte offset from the message start, // used for computing exact alignment padding for aligned typed arrays. template <> struct calculate_size { template [[nodiscard]] GLZ_ALWAYS_INLINE static size_t op(T&& value, size_t offset = 0) { return calculate_size>::template op(std::forward(value), offset); } template [[nodiscard]] GLZ_ALWAYS_INLINE static size_t no_header(T&& value, size_t offset = 0) { return calculate_size>::template no_header(std::forward(value), offset); } }; template requires(glaze_value_t && !custom_write) struct calculate_size { template [[nodiscard]] GLZ_ALWAYS_INLINE static size_t op(auto&& value, size_t offset = 0) { using V = std::remove_cvref_t(), meta_wrapper_v))>; return calculate_size::template op(get_member(value, meta_wrapper_v), offset); } template [[nodiscard]] GLZ_ALWAYS_INLINE static size_t no_header(auto&& value, size_t offset = 0) { using V = std::remove_cvref_t(), meta_wrapper_v))>; return calculate_size::template no_header(get_member(value, meta_wrapper_v), offset); } }; template struct calculate_size { template [[nodiscard]] GLZ_ALWAYS_INLINE static size_t op(auto&&, size_t = 0) noexcept { return 1; // null tag } }; template struct calculate_size { template [[nodiscard]] static size_t op(auto&& value, size_t = 0) noexcept { const auto num_bytes = (value.size() + 7) / 8; return 1 + compressed_int_size(value.size()) + num_bytes; // tag + size + data } }; template struct calculate_size { template [[nodiscard]] static size_t op(auto&&, size_t = 0) noexcept { static constexpr auto data_size = byte_length(); return data_size; // flags are written directly without header } }; template struct calculate_size { template [[nodiscard]] GLZ_ALWAYS_INLINE static size_t op(auto&&, size_t = 0) noexcept { return 0; // member function pointers are not serialized } }; template struct calculate_size { template [[nodiscard]] GLZ_ALWAYS_INLINE static size_t op(auto&&, size_t = 0) noexcept { return 1 + 1; // tag + compressed_int(0) for empty string } }; template struct calculate_size { template [[nodiscard]] GLZ_ALWAYS_INLINE static size_t op(auto&&, size_t = 0) noexcept { return 1; // bool tag contains the value } }; template struct calculate_size { template [[nodiscard]] GLZ_ALWAYS_INLINE static size_t op(auto&& value, size_t offset = 0) { return calculate_size::template op(name_v>, offset); } }; template struct calculate_size> { template [[nodiscard]] GLZ_ALWAYS_INLINE static size_t op(auto&& value, size_t offset = 0) { return calculate_size::template op(value.str, offset); } }; template struct calculate_size> { template [[nodiscard]] GLZ_ALWAYS_INLINE static size_t op(auto&& value, size_t offset = 0) { return calculate_size::template op(value.str, offset); } }; template struct calculate_size { template [[nodiscard]] static size_t op(auto&& value, size_t offset = 0) { return std::visit( [&](auto&& v) -> size_t { using V = std::decay_t; using Variant = std::decay_t; static constexpr uint64_t index = variant_index_v; // 1 byte tag + compressed index + value size const size_t header = 1 + compressed_int_size(); return header + calculate_size::template op(v, offset + header); }, value); } }; template requires num_t || char_t || glaze_enum_t struct calculate_size { template [[nodiscard]] GLZ_ALWAYS_INLINE static size_t op(auto&&, size_t = 0) noexcept { return 1 + sizeof(T); // tag + value } template [[nodiscard]] GLZ_ALWAYS_INLINE static size_t no_header(auto&&, size_t = 0) noexcept { return sizeof(T); // value only } }; template requires(std::is_enum_v && !glaze_enum_t) struct calculate_size { template [[nodiscard]] GLZ_ALWAYS_INLINE static size_t op(auto&&, size_t = 0) noexcept { using V = std::underlying_type_t>; return 1 + sizeof(V); // tag + value } template [[nodiscard]] GLZ_ALWAYS_INLINE static size_t no_header(auto&&, size_t = 0) noexcept { using V = std::underlying_type_t>; return sizeof(V); // value only } }; template struct calculate_size { template [[nodiscard]] GLZ_ALWAYS_INLINE static size_t op(auto&&, size_t = 0) noexcept { using V = typename T::value_type; return 2 + 2 * sizeof(V); // extension tag + complex header + real + imag } template [[nodiscard]] GLZ_ALWAYS_INLINE static size_t no_header(auto&&, size_t = 0) noexcept { using V = typename T::value_type; return 2 * sizeof(V); // real + imag } }; template struct calculate_size { template [[nodiscard]] GLZ_ALWAYS_INLINE static size_t op(auto&& value, size_t = 0) { const sv str = [&]() -> const sv { if constexpr (!char_array_t && std::is_pointer_v>) { return value ? value : ""; } else { return sv{value}; } }(); const auto n = str.size(); return 1 + compressed_int_size(n) + n; // tag + length + data } template [[nodiscard]] GLZ_ALWAYS_INLINE static size_t no_header(auto&& value, size_t = 0) { const auto n = value.size(); return compressed_int_size(n) + n; // length + data } template [[nodiscard]] GLZ_ALWAYS_INLINE static constexpr size_t no_header_cx() noexcept { return compressed_int_size() + N; // length + data } }; template struct calculate_size { static constexpr bool map_like_array = pair_t>; template requires(map_like_array ? check_concatenate(Opts) == false : true) [[nodiscard]] static size_t op(auto&& value, size_t offset = 0) { using V = range_value_t>; size_t result = 1; // tag byte result += compressed_int_size(value.size()); // element count if constexpr (boolean_like) { const auto num_bytes = (value.size() + 7) / 8; result += num_bytes; } else if constexpr (num_t) { if constexpr (check_aligned_arrays(Opts) && sizeof(V) > 1) { result += 1; // extra numeric header byte result += 1; // padding length byte // Compute exact padding from absolute offset constexpr size_t alignment = sizeof(V); const size_t abs_offset = offset + result; const size_t padding = (alignment - (abs_offset % alignment)) % alignment; result += padding; } result += value.size() * sizeof(V); } else if constexpr (str_t) { for (auto& x : value) { result += compressed_int_size(x.size()) + x.size(); } } else if constexpr (complex_t) { // extension tag + complex header + count + data using X = typename V::value_type; result += 1; // complex_header byte result += value.size() * 2 * sizeof(X); } else { // generic array - need to sum element sizes for (auto&& x : value) { result += calculate_size::template op(x, offset + result); } } return result; } template requires(map_like_array && check_concatenate(Opts) == true) [[nodiscard]] static size_t op(auto&& value, size_t offset = 0) { size_t result = 1; // tag byte result += compressed_int_size(value.size()); // element count for (auto&& [k, v] : value) { result += calculate_size::template no_header(k, offset + result); result += calculate_size::template op(v, offset + result); } return result; } }; template struct calculate_size { template [[nodiscard]] GLZ_ALWAYS_INLINE static size_t op(auto&& value, size_t offset = 0) { size_t result = 1; // tag byte result += compressed_int_size<1>(); // count = 1 const auto& [k, v] = value; result += calculate_size::template no_header(k, offset + result); result += calculate_size::template op(v, offset + result); return result; } }; template struct calculate_size { template [[nodiscard]] static size_t op(auto&& value, size_t offset = 0) { size_t result = 1; // tag byte result += compressed_int_size(value.size()); // element count for (auto&& [k, v] : value) { result += calculate_size::template no_header(k, offset + result); result += calculate_size::template op(v, offset + result); } return result; } }; template requires(std::is_array_v) struct calculate_size { template [[nodiscard]] GLZ_ALWAYS_INLINE static size_t op(const V (&value)[N], size_t offset = 0) { return calculate_size::template op(std::span{value, N}, offset); } }; template struct calculate_size { template [[nodiscard]] GLZ_ALWAYS_INLINE static size_t op(auto&& value, size_t offset = 0) { if (value) { return calculate_size::template op(*value, offset); } else { return 1; // null tag } } }; template requires(nullable_value_t && not nullable_like && not is_expected) struct calculate_size { template [[nodiscard]] GLZ_ALWAYS_INLINE static size_t op(auto&& value, size_t offset = 0) { if (value.has_value()) { return calculate_size::template op(value.value(), offset); } else { return 1; // null tag } } }; template requires is_specialization_v || is_specialization_v struct calculate_size { template [[nodiscard]] static size_t op(auto&& value, size_t offset = 0) { using V = std::decay_t; static constexpr auto N = glz::tuple_size_v / 2; size_t result = 0; if constexpr (!check_opening_handled(Options)) { result += 1; // object tag result += compressed_int_size(); // member count } for_each([&]() { constexpr auto Opts = opening_handled_off(); result += calculate_size::template no_header(get<2 * I>(value.value), offset + result); result += calculate_size::template op(get<2 * I + 1>(value.value), offset + result); }); return result; } }; template requires is_specialization_v struct calculate_size { template static consteval bool should_skip_field() { using V = field_t; if constexpr (always_skipped) { return true; } else if constexpr (is_any_function_ptr) { return !check_write_function_pointers(Opts); } else { return false; } } template static consteval size_t count_fields_for_type() { constexpr auto N = reflect::size; return [](std::index_sequence) { return (size_t{} + ... + (should_skip_field() ? size_t{} : size_t{1})); }(std::make_index_sequence{}); } template static consteval size_t merge_element_count() { size_t count{}; using Tuple = std::decay_t().value)>; for_each>([&]() constexpr { using Value = std::decay_t>; if constexpr (is_specialization_v || is_specialization_v) { count += glz::tuple_size_v().value)> / 2; } else { count += count_fields_for_type(); } }); return count; } template [[nodiscard]] static size_t op(auto&& value, size_t offset = 0) { using V = std::decay_t; static constexpr auto N = glz::tuple_size_v; size_t result = 1; // object tag result += compressed_int_size()>(); // member count [&](std::index_sequence) { ((result += calculate_size::template op()>(glz::get(value.value), offset + result)), ...); }(std::make_index_sequence{}); return result; } }; template requires((glaze_object_t || reflectable) && not custom_write) struct calculate_size { static constexpr auto N = reflect::size; template static consteval bool should_skip_field() { using V = field_t; if constexpr (always_skipped) { return true; } else if constexpr (is_any_function_ptr) { return !check_write_function_pointers(Opts); } else { return false; } } template static consteval size_t count_to_write() { return [](std::index_sequence) { return (size_t{} + ... + (should_skip_field() ? size_t{} : size_t{1})); }(std::make_index_sequence{}); } template requires(check_structs_as_arrays(Opts) == true) [[nodiscard]] static size_t op(auto&& value, size_t offset = 0) { size_t result = 1; // generic_array tag result += compressed_int_size()>(); // element count [[maybe_unused]] decltype(auto) t = [&]() -> decltype(auto) { if constexpr (reflectable) { return to_tie(value); } else { return nullptr; } }(); for_each([&]() { if constexpr (should_skip_field()) { return; } else { if constexpr (reflectable) { result += calculate_size::template op(get_member(value, get(t)), offset + result); } else { result += calculate_size::template op(get_member(value, get(reflect::values)), offset + result); } } }); return result; } template requires(check_structs_as_arrays(Options) == false) [[nodiscard]] static size_t op(auto&& value, size_t offset = 0) { constexpr auto Opts = opening_handled_off(); [[maybe_unused]] decltype(auto) t = [&]() -> decltype(auto) { if constexpr (reflectable) { return to_tie(value); } else { return nullptr; } }(); size_t result = 0; if constexpr (maybe_skipped) { // Dynamic path: count members at runtime to handle skip_null_members size_t member_count = 0; // First pass: count members that will be written for_each([&]() { if constexpr (should_skip_field()) { return; } else { using val_t = field_t; if constexpr (null_t && Options.skip_null_members) { if constexpr (always_null_t) { return; } else { const auto is_null = [&]() { decltype(auto) element = [&]() -> decltype(auto) { if constexpr (reflectable) { return get(t); } else { return get(reflect::values); } }; if constexpr (nullable_wrapper) { return !bool(element()(value).val); } else if constexpr (nullable_value_t) { return !get_member(value, element()).has_value(); } else { return !bool(get_member(value, element())); } }(); if (!is_null) { ++member_count; } } } else if constexpr (check_skip_default_members(Options) && has_skippable_default) { decltype(auto) member = [&]() -> decltype(auto) { if constexpr (reflectable) { return get_member(value, get(t)); } else { return get_member(value, get(reflect::values)); } }(); if (!is_default_value(member)) { ++member_count; } } else { ++member_count; } } }); // Write header with dynamic count if constexpr (!check_opening_handled(Options)) { result += 1; // object tag result += compressed_int_size(member_count); // member count } // Second pass: calculate member sizes for_each([&]() { if constexpr (should_skip_field()) { return; } else { using val_t = field_t; if constexpr (null_t && Options.skip_null_members) { if constexpr (always_null_t) { return; } else { const auto is_null = [&]() { decltype(auto) element = [&]() -> decltype(auto) { if constexpr (reflectable) { return get(t); } else { return get(reflect::values); } }; if constexpr (nullable_wrapper) { return !bool(element()(value).val); } else if constexpr (nullable_value_t) { return !get_member(value, element()).has_value(); } else { return !bool(get_member(value, element())); } }(); if (is_null) { return; } } } if constexpr (check_skip_default_members(Options) && has_skippable_default) { decltype(auto) member_val = [&]() -> decltype(auto) { if constexpr (reflectable) { return get_member(value, get(t)); } else { return get_member(value, get(reflect::values)); } }(); if (is_default_value(member_val)) { return; } } static constexpr sv key = reflect::keys[I]; result += calculate_size>::template no_header_cx(); decltype(auto) member = [&]() -> decltype(auto) { if constexpr (reflectable) { return get(t); } else { return get(reflect::values); } }(); result += calculate_size::template op(get_member(value, member), offset + result); } }); } else { // Static path: use compile-time count for better performance if constexpr (!check_opening_handled(Options)) { result += 1; // object tag result += compressed_int_size()>(); // member count } for_each([&]() { if constexpr (should_skip_field()) { return; } else { static constexpr sv key = reflect::keys[I]; result += calculate_size>::template no_header_cx(); decltype(auto) member = [&]() -> decltype(auto) { if constexpr (reflectable) { return get(t); } else { return get(reflect::values); } }(); result += calculate_size::template op(get_member(value, member), offset + result); } }); } return result; } }; template requires glaze_array_t struct calculate_size { template [[nodiscard]] static size_t op(auto&& value, size_t offset = 0) { static constexpr auto N = reflect::size; size_t result = 1; // generic_array tag result += compressed_int_size(); // element count for_each([&]() { result += calculate_size::template op(get_member(value, get(reflect::values)), offset + result); }); return result; } }; template requires(tuple_t || is_std_tuple) struct calculate_size { template [[nodiscard]] static size_t op(auto&& value, size_t offset = 0) { static constexpr auto N = glz::tuple_size_v; size_t result = 1; // generic_array tag result += compressed_int_size(); // element count if constexpr (is_std_tuple) { [&](std::index_sequence) { ((result += calculate_size::template op(std::get(value), offset + result)), ...); }(std::make_index_sequence{}); } else { [&](std::index_sequence) { ((result += calculate_size::template op(glz::get(value), offset + result)), ...); }(std::make_index_sequence{}); } return result; } }; // Filesystem path support template struct calculate_size { template [[nodiscard]] static size_t op(auto&& value, size_t offset = 0) { return calculate_size::template op(value.string(), offset); } }; // ============ Public API ============ // Calculate the size in bytes needed to serialize a value to BEVE format template [[nodiscard]] size_t beve_size(T&& value) { return calculate_size>::template op()>(std::forward(value)); } // Calculate size for untagged BEVE (structs_as_arrays = true) template [[nodiscard]] size_t beve_size_untagged(T&& value) { return calculate_size>::template op< opt_true(), structs_as_arrays_opt_tag{}>>(std::forward(value)); } }