24#include <glaze/glaze.hpp>
33namespace morph::model {
61inline constexpr int kPayloadShapeMaxDepth = 8;
78inline constexpr std::string_view kOpaqueShapeTag =
"x";
81[[nodiscard]] std::string payloadShape(
int depth);
86struct IsStdOptional : std::false_type {};
91struct IsStdOptional<std::optional<T>> : std::true_type {};
107[[nodiscard]] std::string payloadShapeObject(
int depth) {
108 using V = std::remove_cvref_t<T>;
109 constexpr std::size_t kMemberCount = glz::reflect<V>::size;
110 std::vector<std::string> members;
111 members.reserve(kMemberCount);
112 [&]<std::size_t... I>(std::index_sequence<I...>) {
113 (members.emplace_back(std::string{glz::reflect<V>::keys[I]} +
':' +
114 payloadShape<glz::refl_t<V, I>>(depth + 1)),
116 }(std::make_index_sequence<kMemberCount>{});
117 std::ranges::sort(members);
118 std::string out{
"("};
120 for (
const auto& member : members) {
169[[nodiscard]] std::string payloadShape(
int depth) {
170 using U = std::remove_cvref_t<T>;
171 if (depth > kPayloadShapeMaxDepth) {
172 return std::string{kOpaqueShapeTag};
176 payloadShape<typename PayloadShapeTag<U>::Inner>(depth + 1) +
'}';
179 }
else if constexpr (std::is_same_v<U, bool>) {
181 }
else if constexpr (std::is_same_v<U, char>) {
183 }
else if constexpr (std::is_enum_v<U>) {
184 return "e" + std::to_string(
sizeof(std::underlying_type_t<U>));
185 }
else if constexpr (std::is_integral_v<U>) {
186 return (std::is_signed_v<U> ? std::string{
"i"} : std::string{
"u"}) + std::to_string(
sizeof(U));
187 }
else if constexpr (std::is_floating_point_v<U>) {
188 return "f" + std::to_string(
sizeof(U));
189 }
else if constexpr (glz::str_t<U> || glz::string_view_t<U>) {
191 }
else if constexpr (IsStdOptional<U>::value) {
192 return "?" + payloadShape<typename U::value_type>(depth + 1);
193 }
else if constexpr (glz::readable_map_t<U>) {
194 return "{" + payloadShape<typename U::key_type>(depth + 1) +
">" +
195 payloadShape<typename U::mapped_type>(depth + 1) +
"}";
196 }
else if constexpr (glz::range<U>) {
197 return "[" + payloadShape<glz::range_value_t<U>>(depth + 1) +
"]";
198 }
else if constexpr (glz::reflectable<U> || glz::glaze_object_t<U>) {
199 return payloadShapeObject<U>(depth);
201 return std::string{kOpaqueShapeTag};
213[[nodiscard]]
inline std::uint64_t fnv1a64(std::string_view bytes)
noexcept {
214 constexpr std::uint64_t kOffsetBasis = 14695981039346656037ULL;
215 constexpr std::uint64_t kPrime = 1099511628211ULL;
216 std::uint64_t hash = kOffsetBasis;
217 for (
const char byte : bytes) {
218 hash ^=
static_cast<std::uint64_t
>(
static_cast<unsigned char>(byte));
227[[nodiscard]]
inline std::string hex64(std::uint64_t value) {
228 constexpr std::string_view kDigits =
"0123456789abcdef";
229 std::string out(16,
'0');
230 for (std::size_t i = 0; i < 16; ++i) {
232 out[15 - i] = kDigits[(value >> (4 * i)) & 0xFULL];
250 static const std::string kShape = detail::payloadShape<A>(0);
267 static const std::string kFingerprint = [] {
Satisfied when T's tag also names an inner type to render inside it (a transparent wrapper).
Satisfied when T declares a stable shape tag.
const std::string & payloadShapeString()
Returns the human-readable shape rendering behind payloadFingerprint<A>().
constexpr std::uint32_t kPayloadFingerprintScheme
Version of the fingerprint algorithm, not of any one payload.
const std::string & payloadFingerprint()
Returns A's payload fingerprint: "<scheme>:<16 hex digits>".
Opt-in, compiler-independent shape tag for a type with a custom Glaze codec.
Declares a stable shape tag for a type T whose JSON shape payloadShape cannot decompose.