#pragma once #include #include #include #include #include #include #include #include #include #include #include namespace msgpackpp { enum class error_code { overflow, empty, lack, invalid, type_error, }; namespace format { constexpr uint8_t POSITIVE_FIXINT_MIN = 0x00; constexpr uint8_t POSITIVE_FIXINT_MAX = 0x7F; constexpr uint8_t FIXMAP_MIN = 0x80; constexpr uint8_t FIXMAP_MAX = 0x8F; constexpr uint8_t FIXARRAY_MIN = 0x90; constexpr uint8_t FIXARRAY_MAX = 0x9F; constexpr uint8_t FIXSTR_MIN = 0xA0; constexpr uint8_t FIXSTR_MAX = 0xBF; constexpr uint8_t NEGATIVE_FIXINT_MIN = 0xE0; constexpr uint8_t NEGATIVE_FIXINT_MAX = 0xFF; constexpr uint8_t NIL = 0xC0; constexpr uint8_t NEVER_USED = 0xC1; constexpr uint8_t FALSE = 0xC2; constexpr uint8_t TRUE = 0xC3; constexpr uint8_t BIN8 = 0xC4; constexpr uint8_t BIN16 = 0xC5; constexpr uint8_t BIN32 = 0xC6; constexpr uint8_t EXT8 = 0xC7; constexpr uint8_t EXT16 = 0xC8; constexpr uint8_t EXT32 = 0xC9; constexpr uint8_t FLOAT32 = 0xCA; constexpr uint8_t FLOAT64 = 0xCB; constexpr uint8_t UINT8 = 0xCC; constexpr uint8_t UINT16 = 0xCD; constexpr uint8_t UINT32 = 0xCE; constexpr uint8_t UINT64 = 0xCF; constexpr uint8_t INT8 = 0xD0; constexpr uint8_t INT16 = 0xD1; constexpr uint8_t INT32 = 0xD2; constexpr uint8_t INT64 = 0xD3; constexpr uint8_t FIXEXT1 = 0xD4; constexpr uint8_t FIXEXT2 = 0xD5; constexpr uint8_t FIXEXT4 = 0xD6; constexpr uint8_t FIXEXT8 = 0xD7; constexpr uint8_t FIXEXT16 = 0xD8; constexpr uint8_t STR8 = 0xD9; constexpr uint8_t STR16 = 0xDA; constexpr uint8_t STR32 = 0xDB; constexpr uint8_t ARRAY16 = 0xDC; constexpr uint8_t ARRAY32 = 0xDD; constexpr uint8_t MAP16 = 0xDE; constexpr uint8_t MAP32 = 0xDF; constexpr bool is_positive_fixint(uint8_t b) { return b <= POSITIVE_FIXINT_MAX; } constexpr bool is_fixmap(uint8_t b) { return b >= FIXMAP_MIN && b <= FIXMAP_MAX; } constexpr bool is_fixarray(uint8_t b) { return b >= FIXARRAY_MIN && b <= FIXARRAY_MAX; } constexpr bool is_fixstr(uint8_t b) { return b >= FIXSTR_MIN && b <= FIXSTR_MAX; } constexpr bool is_negative_fixint(uint8_t b) { return b >= NEGATIVE_FIXINT_MIN; } } // namespace format template using result = std::expected; template result body_number(const uint8_t *p, int size) { if (size < 1 + static_cast(sizeof(T))) { return std::unexpected(error_code::lack); } if constexpr (sizeof(T) == 1) { return static_cast(p[1]); } else if constexpr (sizeof(T) == 2) { return static_cast((p[1] << 8) | p[2]); } else if constexpr (sizeof(T) == 4) { uint8_t buf[] = {p[4], p[3], p[2], p[1]}; T val; __builtin_memcpy(&val, buf, sizeof(T)); return val; } else if constexpr (sizeof(T) == 8) { uint8_t buf[] = {p[8], p[7], p[6], p[5], p[4], p[3], p[2], p[1]}; T val; __builtin_memcpy(&val, buf, sizeof(T)); return val; } else { return std::unexpected(error_code::invalid); } } struct body_info { int header; // bytes before the body (includes format byte + length fields + ext type byte) uint32_t body; // body size in bytes (0 for containers, computed for variable-length) }; inline result get_body_info(const uint8_t *p, int size) { if (size < 1) return std::unexpected(error_code::empty); uint8_t b = p[0]; using namespace format; if (is_positive_fixint(b)) return body_info{1, 0}; if (is_negative_fixint(b)) return body_info{1, 0}; if (is_fixmap(b)) return body_info{1, 0}; // container if (is_fixarray(b)) return body_info{1, 0}; // container if (is_fixstr(b)) return body_info{1, static_cast(b & 0x1F)}; switch (b) { case NIL: case FALSE: case TRUE: return body_info{1, 0}; case NEVER_USED: return std::unexpected(error_code::invalid); case BIN8: { auto n = body_number(p, size); if (!n) return std::unexpected(n.error()); return body_info{1+1, *n}; } case BIN16: { auto n = body_number(p, size); if (!n) return std::unexpected(n.error()); return body_info{1+2, *n}; } case BIN32: { auto n = body_number(p, size); if (!n) return std::unexpected(n.error()); return body_info{1+4, *n}; } case EXT8: { auto n = body_number(p, size); if (!n) return std::unexpected(n.error()); return body_info{1+1+1, *n}; } case EXT16: { auto n = body_number(p, size); if (!n) return std::unexpected(n.error()); return body_info{1+2+1, *n}; } case EXT32: { auto n = body_number(p, size); if (!n) return std::unexpected(n.error()); return body_info{1+4+1, *n}; } case FLOAT32: return body_info{1, 4}; case FLOAT64: return body_info{1, 8}; case UINT8: return body_info{1, 1}; case UINT16: return body_info{1, 2}; case UINT32: return body_info{1, 4}; case UINT64: return body_info{1, 8}; case INT8: return body_info{1, 1}; case INT16: return body_info{1, 2}; case INT32: return body_info{1, 4}; case INT64: return body_info{1, 8}; case FIXEXT1: return body_info{1+1, 1}; case FIXEXT2: return body_info{1+1, 2}; case FIXEXT4: return body_info{1+1, 4}; case FIXEXT8: return body_info{1+1, 8}; case FIXEXT16: return body_info{1+1, 16}; case STR8: { auto n = body_number(p, size); if (!n) return std::unexpected(n.error()); return body_info{1+1, *n}; } case STR16: { auto n = body_number(p, size); if (!n) return std::unexpected(n.error()); return body_info{1+2, *n}; } case STR32: { auto n = body_number(p, size); if (!n) return std::unexpected(n.error()); return body_info{1+4, *n}; } case ARRAY16: case ARRAY32: case MAP16: case MAP32: return body_info{1 + (b == ARRAY16 || b == MAP16 ? 2 : 4), 0}; // container default: return std::unexpected(error_code::invalid); } } class packer { public: using buffer = std::vector; private: std::shared_ptr m_buffer; template void push_big_endian(T n) { auto p = reinterpret_cast(&n) + (sizeof(T) - 1); for (size_t i = 0; i < sizeof(T); ++i, --p) { m_buffer->push_back(*p); } } template void push(const Range &r) { m_buffer->insert(m_buffer->end(), std::begin(r), std::end(r)); } public: packer() : m_buffer(std::make_shared()) {} packer(const std::shared_ptr &buf) : m_buffer(buf) {} packer(const packer &) = delete; packer &operator=(const packer &) = delete; using pack_result = result>; pack_result pack_nil() { m_buffer->push_back(format::NIL); return *this; } pack_result pack_bool(bool v) { m_buffer->push_back(v ? format::TRUE : format::FALSE); return *this; } template pack_result pack_integer(T n) { if constexpr (std::is_signed_v) { if (n >= 0 && n <= 0x7F) { m_buffer->push_back(static_cast(n)); } else if (n >= -32 && n < 0) { m_buffer->push_back(static_cast(n)); // negative fixint } else if (n >= std::numeric_limits::min() && n <= std::numeric_limits::max()) { m_buffer->push_back(format::INT8); m_buffer->push_back(static_cast(n)); } else if (n >= std::numeric_limits::min() && n <= std::numeric_limits::max()) { m_buffer->push_back(format::INT16); push_big_endian(static_cast(n)); } else if (n >= std::numeric_limits::min() && n <= std::numeric_limits::max()) { m_buffer->push_back(format::INT32); push_big_endian(static_cast(n)); } else { m_buffer->push_back(format::INT64); push_big_endian(static_cast(n)); } } else { if (n <= 0x7F) { m_buffer->push_back(static_cast(n)); } else if (n <= std::numeric_limits::max()) { m_buffer->push_back(format::UINT8); m_buffer->push_back(static_cast(n)); } else if (n <= std::numeric_limits::max()) { m_buffer->push_back(format::UINT16); push_big_endian(static_cast(n)); } else if (n <= std::numeric_limits::max()) { m_buffer->push_back(format::UINT32); push_big_endian(static_cast(n)); } else { m_buffer->push_back(format::UINT64); push_big_endian(static_cast(n)); } } return *this; } pack_result pack_float(float n) { m_buffer->push_back(format::FLOAT32); push_big_endian(n); return *this; } pack_result pack_double(double n) { m_buffer->push_back(format::FLOAT64); push_big_endian(n); return *this; } template pack_result pack_str(const Range &r) { auto sz = static_cast(std::distance(std::begin(r), std::end(r))); if (sz < 32) { m_buffer->push_back(format::FIXSTR_MIN | static_cast(sz)); } else if (sz <= std::numeric_limits::max()) { m_buffer->push_back(format::STR8); m_buffer->push_back(static_cast(sz)); } else if (sz <= std::numeric_limits::max()) { m_buffer->push_back(format::STR16); push_big_endian(static_cast(sz)); } else if (sz <= std::numeric_limits::max()) { m_buffer->push_back(format::STR32); push_big_endian(static_cast(sz)); } else { return std::unexpected(error_code::overflow); } push(r); return *this; } pack_result pack_str(const char *s) { return pack_str(std::string_view(s)); } template pack_result pack_bin(const Range &r) { auto sz = static_cast(std::distance(std::begin(r), std::end(r))); if (sz <= std::numeric_limits::max()) { m_buffer->push_back(format::BIN8); m_buffer->push_back(static_cast(sz)); } else if (sz <= std::numeric_limits::max()) { m_buffer->push_back(format::BIN16); push_big_endian(static_cast(sz)); } else if (sz <= std::numeric_limits::max()) { m_buffer->push_back(format::BIN32); push_big_endian(static_cast(sz)); } else { return std::unexpected(error_code::overflow); } push(r); return *this; } pack_result pack_array(size_t n) { if (n <= 15) { m_buffer->push_back(format::FIXARRAY_MIN | static_cast(n)); } else if (n <= std::numeric_limits::max()) { m_buffer->push_back(format::ARRAY16); push_big_endian(static_cast(n)); } else if (n <= std::numeric_limits::max()) { m_buffer->push_back(format::ARRAY32); push_big_endian(static_cast(n)); } else { return std::unexpected(error_code::overflow); } return *this; } pack_result pack_map(size_t n) { if (n <= 15) { m_buffer->push_back(format::FIXMAP_MIN | static_cast(n)); } else if (n <= std::numeric_limits::max()) { m_buffer->push_back(format::MAP16); push_big_endian(static_cast(n)); } else if (n <= std::numeric_limits::max()) { m_buffer->push_back(format::MAP32); push_big_endian(static_cast(n)); } else { return std::unexpected(error_code::overflow); } return *this; } template pack_result pack_ext(char type, const Range &r) { auto sz = static_cast(std::distance(std::begin(r), std::end(r))); switch (sz) { case 1: m_buffer->push_back(format::FIXEXT1); break; case 2: m_buffer->push_back(format::FIXEXT2); break; case 4: m_buffer->push_back(format::FIXEXT4); break; case 8: m_buffer->push_back(format::FIXEXT8); break; case 16: m_buffer->push_back(format::FIXEXT16); break; default: if (sz <= std::numeric_limits::max()) { m_buffer->push_back(format::EXT8); m_buffer->push_back(static_cast(sz)); } else if (sz <= std::numeric_limits::max()) { m_buffer->push_back(format::EXT16); push_big_endian(static_cast(sz)); } else if (sz <= std::numeric_limits::max()) { m_buffer->push_back(format::EXT32); push_big_endian(static_cast(sz)); } else { return std::unexpected(error_code::overflow); } } m_buffer->push_back(static_cast(type)); push(r); return *this; } template requires std::is_integral_v && (!std::is_same_v) pack_result pack(T n) { return pack_integer(n); } pack_result pack(bool v) { return pack_bool(v); } pack_result pack(float v) { return pack_float(v); } pack_result pack(double v) { return pack_double(v); } pack_result pack(const char *v) { return pack_str(v); } pack_result pack(std::string_view v) { return pack_str(v); } pack_result pack(const std::string &v) { return pack_str(v); } pack_result pack(const std::vector &v) { return pack_bin(v); } template pack_result pack(const std::tuple &t) { auto r = pack_array(sizeof...(Ts)); if (!r) return r; return pack_tuple_elements(t, std::index_sequence_for{}); } template requires requires(const T &v) { { T::ext_id } -> std::convertible_to; v.as_tuple(); } pack_result pack(const T &v) { packer inner; auto r = inner.pack(v.as_tuple()); if (!r) return r; return pack_ext(T::ext_id, inner.get_payload()); } template requires (requires(const T &v) { v.as_tuple(); } && !requires { { T::ext_id } -> std::convertible_to; }) pack_result pack(const T &v) { return pack(v.as_tuple()); } private: template pack_result pack_tuple_elements(const Tuple &t, std::index_sequence) { pack_result r = *this; ((r = r ? r->get().pack(std::get(t)) : r), ...); return r; } public: const buffer &get_payload() const { return *m_buffer; } }; class parser { const uint8_t *m_p = nullptr; int m_size = 0; result header_byte() const { if (m_size < 1) return std::unexpected(error_code::empty); return m_p[0]; } public: parser() = default; parser(const std::vector &v) : m_p(v.data()), m_size(static_cast(v.size())) {} parser(const uint8_t *p, int size) : m_p(p), m_size(size < 0 ? 0 : size) {} bool is_empty() const { return m_size == 0; } const uint8_t *data() const { return m_p; } int size() const { return m_size; } result advance(int n) const { if (n > m_size) return std::unexpected(error_code::lack); return parser(m_p + n, m_size - n); } result next() const { auto hdr = header_byte(); if (!hdr) return std::unexpected(hdr.error()); uint8_t b = *hdr; if (is_array()) { auto info = get_body_info(m_p, m_size); if (!info) return std::unexpected(info.error()); auto cnt = count(); if (!cnt) return std::unexpected(cnt.error()); auto cur = advance(info->header); if (!cur) return std::unexpected(cur.error()); for (uint32_t i = 0; i < *cnt; ++i) { auto n = cur->next(); if (!n) return std::unexpected(n.error()); cur = *n; } return *cur; } else if (is_map()) { auto info = get_body_info(m_p, m_size); if (!info) return std::unexpected(info.error()); auto cnt = count(); if (!cnt) return std::unexpected(cnt.error()); auto cur = advance(info->header); if (!cur) return std::unexpected(cur.error()); for (uint32_t i = 0; i < *cnt; ++i) { auto k = cur->next(); if (!k) return std::unexpected(k.error()); cur = *k; auto v = cur->next(); if (!v) return std::unexpected(v.error()); cur = *v; } return *cur; } else { auto info = get_body_info(m_p, m_size); if (!info) return std::unexpected(info.error()); auto total = info->header + static_cast(info->body); return advance(total); } } bool is_nil() const { auto h = header_byte(); return h && *h == format::NIL; } bool is_bool() const { auto h = header_byte(); return h && (*h == format::TRUE || *h == format::FALSE); } bool is_number() const { auto h = header_byte(); if (!h) return false; uint8_t b = *h; if (format::is_positive_fixint(b)) return true; if (format::is_negative_fixint(b)) return true; return b >= format::FLOAT32 && b <= format::INT64; } bool is_string() const { auto h = header_byte(); if (!h) return false; uint8_t b = *h; if (format::is_fixstr(b)) return true; return b == format::STR8 || b == format::STR16 || b == format::STR32; } bool is_binary() const { auto h = header_byte(); if (!h) return false; uint8_t b = *h; return b == format::BIN8 || b == format::BIN16 || b == format::BIN32; } bool is_ext() const { auto h = header_byte(); if (!h) return false; uint8_t b = *h; return (b >= format::FIXEXT1 && b <= format::FIXEXT16) || b == format::EXT8 || b == format::EXT16 || b == format::EXT32; } bool is_array() const { auto h = header_byte(); if (!h) return false; uint8_t b = *h; if (format::is_fixarray(b)) return true; return b == format::ARRAY16 || b == format::ARRAY32; } bool is_map() const { auto h = header_byte(); if (!h) return false; uint8_t b = *h; if (format::is_fixmap(b)) return true; return b == format::MAP16 || b == format::MAP32; } result get_bool() const { auto h = header_byte(); if (!h) return std::unexpected(h.error()); if (*h == format::TRUE) return true; if (*h == format::FALSE) return false; return std::unexpected(error_code::type_error); } result get_string() const { auto h = header_byte(); if (!h) return std::unexpected(h.error()); uint8_t b = *h; size_t offset, len; if (format::is_fixstr(b)) { len = b & 0x1F; offset = 1; } else if (b == format::STR8) { auto n = body_number(m_p, m_size); if (!n) return std::unexpected(n.error()); len = *n; offset = 1 + 1; } else if (b == format::STR16) { auto n = body_number(m_p, m_size); if (!n) return std::unexpected(n.error()); len = *n; offset = 1 + 2; } else if (b == format::STR32) { auto n = body_number(m_p, m_size); if (!n) return std::unexpected(n.error()); len = *n; offset = 1 + 4; } else { return std::unexpected(error_code::type_error); } if (static_cast(offset + len) > m_size) { return std::unexpected(error_code::lack); } return std::string_view(reinterpret_cast(m_p + offset), len); } result get_binary_view() const { auto h = header_byte(); if (!h) return std::unexpected(h.error()); uint8_t b = *h; size_t offset, len; if (b == format::BIN8) { auto n = body_number(m_p, m_size); if (!n) return std::unexpected(n.error()); len = *n; offset = 1 + 1; } else if (b == format::BIN16) { auto n = body_number(m_p, m_size); if (!n) return std::unexpected(n.error()); len = *n; offset = 1 + 2; } else if (b == format::BIN32) { auto n = body_number(m_p, m_size); if (!n) return std::unexpected(n.error()); len = *n; offset = 1 + 4; } else { return std::unexpected(error_code::type_error); } if (static_cast(offset + len) > m_size) { return std::unexpected(error_code::lack); } return std::string_view(reinterpret_cast(m_p + offset), len); } result> get_ext() const { auto h = header_byte(); if (!h) return std::unexpected(h.error()); uint8_t b = *h; int8_t ext_type; size_t data_offset, data_len; switch (b) { case format::FIXEXT1: ext_type = m_p[1]; data_offset = 2; data_len = 1; break; case format::FIXEXT2: ext_type = m_p[1]; data_offset = 2; data_len = 2; break; case format::FIXEXT4: ext_type = m_p[1]; data_offset = 2; data_len = 4; break; case format::FIXEXT8: ext_type = m_p[1]; data_offset = 2; data_len = 8; break; case format::FIXEXT16: ext_type = m_p[1]; data_offset = 2; data_len = 16; break; case format::EXT8: { auto n = body_number(m_p, m_size); if (!n) return std::unexpected(n.error()); ext_type = m_p[2]; data_offset = 3; data_len = *n; break; } case format::EXT16: { auto n = body_number(m_p, m_size); if (!n) return std::unexpected(n.error()); ext_type = m_p[3]; data_offset = 4; data_len = *n; break; } case format::EXT32: { auto n = body_number(m_p, m_size); if (!n) return std::unexpected(n.error()); ext_type = m_p[5]; data_offset = 6; data_len = *n; break; } default: return std::unexpected(error_code::type_error); } if (static_cast(data_offset + data_len) > m_size) { return std::unexpected(error_code::lack); } return std::tuple{ext_type, std::string_view(reinterpret_cast(m_p + data_offset), data_len)}; } template result get_number() const { auto h = header_byte(); if (!h) return std::unexpected(h.error()); uint8_t b = *h; if (format::is_positive_fixint(b)) return static_cast(b); if (format::is_negative_fixint(b)) return static_cast(static_cast(b)); switch (b) { case format::UINT8: { auto n = body_number(m_p, m_size); if (!n) return std::unexpected(n.error()); return static_cast(*n); } case format::UINT16: { auto n = body_number(m_p, m_size); if (!n) return std::unexpected(n.error()); return static_cast(*n); } case format::UINT32: { auto n = body_number(m_p, m_size); if (!n) return std::unexpected(n.error()); return static_cast(*n); } case format::UINT64: { auto n = body_number(m_p, m_size); if (!n) return std::unexpected(n.error()); return static_cast(*n); } case format::INT8: { auto n = body_number(m_p, m_size); if (!n) return std::unexpected(n.error()); return static_cast(*n); } case format::INT16: { auto n = body_number(m_p, m_size); if (!n) return std::unexpected(n.error()); return static_cast(*n); } case format::INT32: { auto n = body_number(m_p, m_size); if (!n) return std::unexpected(n.error()); return static_cast(*n); } case format::INT64: { auto n = body_number(m_p, m_size); if (!n) return std::unexpected(n.error()); return static_cast(*n); } case format::FLOAT32: { auto n = body_number(m_p, m_size); if (!n) return std::unexpected(n.error()); return static_cast(*n); } case format::FLOAT64: { auto n = body_number(m_p, m_size); if (!n) return std::unexpected(n.error()); return static_cast(*n); } default: return std::unexpected(error_code::type_error); } } result count() const { auto h = header_byte(); if (!h) return std::unexpected(h.error()); uint8_t b = *h; if (format::is_fixarray(b)) return static_cast(b & 0x0F); if (format::is_fixmap(b)) return static_cast(b & 0x0F); switch (b) { case format::ARRAY16: { auto n = body_number(m_p, m_size); if (!n) return std::unexpected(n.error()); return static_cast(*n); } case format::ARRAY32: { auto n = body_number(m_p, m_size); if (!n) return std::unexpected(n.error()); return *n; } case format::MAP16: { auto n = body_number(m_p, m_size); if (!n) return std::unexpected(n.error()); return static_cast(*n); } case format::MAP32: { auto n = body_number(m_p, m_size); if (!n) return std::unexpected(n.error()); return *n; } default: return std::unexpected(error_code::type_error); } } result first_item() const { if (!is_array() && !is_map()) return std::unexpected(error_code::type_error); auto info = get_body_info(m_p, m_size); if (!info) return std::unexpected(info.error()); return advance(info->header); } parser operator[](int index) const { auto cur = first_item(); if (!cur) return {}; for (int i = 0; i < index; ++i) { auto n = cur->next(); if (!n) return {}; cur = *n; } return *cur; } }; template requires std::is_integral_v && (!std::is_same_v) result unpack(const parser &p, T &out) { auto v = p.get_number(); if (!v) return std::unexpected(v.error()); out = *v; return p.next(); } inline result unpack(const parser &p, bool &out) { auto v = p.get_bool(); if (!v) return std::unexpected(v.error()); out = *v; return p.next(); } inline result unpack(const parser &p, std::string_view &out) { auto v = p.get_string(); if (!v) return std::unexpected(v.error()); out = *v; return p.next(); } inline result unpack(const parser &p, std::vector &out) { auto v = p.get_binary_view(); if (!v) return std::unexpected(v.error()); out.assign(v->begin(), v->end()); return p.next(); } template result unpack_tuple_elements(const parser &p, std::tuple &t, std::index_sequence) { result cur = p.first_item(); if (!cur) return cur; ((cur = cur ? unpack(*cur, std::get(t)) : cur), ...); return cur; } template result unpack(const parser &p, std::tuple &t) { auto cnt = p.count(); if (!cnt) return std::unexpected(cnt.error()); if (*cnt != sizeof...(Ts)) return std::unexpected(error_code::type_error); auto r = unpack_tuple_elements(p, t, std::index_sequence_for{}); if (!r) return r; return p.next(); } template requires (requires(T &v) { v.as_tuple(); } && !requires { { T::ext_id } -> std::convertible_to; }) result unpack(const parser &p, T &out) { auto tup = out.as_tuple(); auto cnt = p.count(); if (!cnt) return std::unexpected(cnt.error()); if (*cnt != std::tuple_size_v) return std::unexpected(error_code::type_error); auto r = unpack_tuple_elements(p, tup, std::make_index_sequence>{}); if (!r) return r; return p.next(); } template requires requires(T &v) { { T::ext_id } -> std::convertible_to; v.as_tuple(); } result unpack(const parser &p, T &out) { auto ext = p.get_ext(); if (!ext) return std::unexpected(ext.error()); auto [ext_type, ext_data] = *ext; if (ext_type != T::ext_id) return std::unexpected(error_code::type_error); parser inner(reinterpret_cast(ext_data.data()), static_cast(ext_data.size())); auto tup = out.as_tuple(); auto r = unpack_tuple_elements(inner, tup, std::make_index_sequence>{}); if (!r) return r; return p.next(); } } // namespace msgpackpp