From 1cc3fb0e230dda14a226295b8122951ca4b26b9e Mon Sep 17 00:00:00 2001 From: bbgan <2893129936@qq.com> Date: Sat, 25 May 2024 14:48:09 +0800 Subject: [PATCH 1/3] support without pb_base_impl[struct_pb] --- iguana/pb_util.hpp | 190 ++++++++++++++++++++++++------------------- iguana/pb_writer.hpp | 52 ++++++------ test/test_pb.cpp | 8 -- 3 files changed, 134 insertions(+), 116 deletions(-) diff --git a/iguana/pb_util.hpp b/iguana/pb_util.hpp index a8014110..d1e0fb27 100644 --- a/iguana/pb_util.hpp +++ b/iguana/pb_util.hpp @@ -361,48 +361,66 @@ IGUANA_INLINE constexpr void for_each_n(F&& f, std::index_sequence) { (std::forward(f)(std::integral_constant{}), ...); } -template -IGUANA_INLINE size_t numeric_size(T&& t) { - using value_type = std::remove_const_t>; - if constexpr (omit_default_val) { - if constexpr (is_fixed_v || is_signed_varint_v) { - if (t.val == 0) +template +IGUANA_INLINE size_t str_numeric_size(Type&& t) { + using T = std::remove_const_t>; + if constexpr (std::is_same_v || + std::is_same_v) { + // string + if constexpr (omit_default_val) { + if (t.size() == 0) IGUANA_UNLIKELY { return 0; } } + if constexpr (key_size == 0) { + return t.size(); + } else { - if (t == static_cast(0)) - IGUANA_UNLIKELY { return 0; } + return key_size + variant_uint32_size(static_cast(t.size())) + + t.size(); } } - if constexpr (std::is_integral_v) { - if constexpr (std::is_same_v) { - return 1 + key_size; + else { + // numeric + if constexpr (omit_default_val) { + if constexpr (is_fixed_v || is_signed_varint_v) { + if (t.val == 0) + IGUANA_UNLIKELY { return 0; } + } + else { + if (t == static_cast(0)) + IGUANA_UNLIKELY { return 0; } + } + } + if constexpr (std::is_integral_v) { + if constexpr (std::is_same_v) { + return 1 + key_size; + } + else { + return key_size + variant_intergal_size(t); + } + } + else if constexpr (detail::is_signed_varint_v) { + return key_size + variant_intergal_size(encode_zigzag(t.val)); + } + else if constexpr (detail::is_fixed_v) { + return key_size + sizeof(typename T::value_type); + } + else if constexpr (std::is_same_v || std::is_same_v) { + return key_size + sizeof(T); + } + else if constexpr (std::is_enum_v) { + using U = std::underlying_type_t; + return key_size + variant_intergal_size(static_cast(t)); } else { - return key_size + variant_intergal_size(t); + static_assert(!sizeof(T), "err"); } } - else if constexpr (detail::is_signed_varint_v) { - return key_size + variant_intergal_size(encode_zigzag(t.val)); - } - else if constexpr (detail::is_fixed_v) { - return key_size + sizeof(typename value_type::value_type); - } - else if constexpr (std::is_same_v || - std::is_same_v) { - return key_size + sizeof(value_type); - } - else if constexpr (std::is_enum_v) { - using U = std::underlying_type_t; - return key_size + variant_intergal_size(static_cast(t)); - } - else { - static_assert(!sizeof(value_type), "err"); - } } -template -IGUANA_INLINE size_t pb_key_value_size(T&& t); +template +IGUANA_INLINE size_t pb_key_value_size(Type&& t, Arr* size_arr); template constexpr inline size_t get_variant_index() { @@ -420,12 +438,12 @@ constexpr inline size_t get_variant_index() { } } -template -IGUANA_INLINE size_t pb_oneof_size(Type&& t) { +template +IGUANA_INLINE size_t pb_oneof_size(Type&& t, Arr* size_arr) { using T = std::decay_t; int len = 0; std::visit( - [&len](auto&& value) IGUANA__INLINE_LAMBDA { + [&len, size_arr](auto&& value) IGUANA__INLINE_LAMBDA { using value_type = std::remove_const_t>; constexpr auto offset = @@ -434,7 +452,7 @@ IGUANA_INLINE size_t pb_oneof_size(Type&& t) { ((field_no + offset) << 3) | static_cast(get_wire_type()); len = pb_key_value_size( - std::forward(value)); + std::forward(value), size_arr); }, std::forward(t)); return len; @@ -442,15 +460,20 @@ IGUANA_INLINE size_t pb_oneof_size(Type&& t) { // returns size = key_size + optional(len_size) + len // when key_size == 0, return len -template -IGUANA_INLINE size_t pb_key_value_size(Type&& t) { +template +IGUANA_INLINE size_t pb_key_value_size(Type&& t, Arr* size_arr) { using T = std::remove_const_t>; if constexpr (is_reflection_v || is_custom_reflection_v) { size_t len = 0; static constexpr auto tuple = get_members_tuple(); constexpr size_t SIZE = std::tuple_size_v>; + size_t pre_index = -1; + if constexpr (!inherits_from_pb_base_v && key_size != 0) { + pre_index = (*size_arr).size(); + (*size_arr).push_back(0); // placeholder + } for_each_n( - [&len, &t](auto i) IGUANA__INLINE_LAMBDA { + [&len, &t, size_arr](auto i) IGUANA__INLINE_LAMBDA { using field_type = std::tuple_element_t>; @@ -462,7 +485,7 @@ IGUANA_INLINE size_t pb_key_value_size(Type&& t) { get_variant_index - 1>(); if constexpr (offset == 0) { - len += pb_oneof_size(val); + len += pb_oneof_size(val, size_arr); } } else { @@ -470,14 +493,16 @@ IGUANA_INLINE size_t pb_key_value_size(Type&& t) { (value.field_no << 3) | static_cast(get_wire_type()); constexpr auto sub_keysize = variant_uint32_size_constexpr(sub_key); - len += pb_key_value_size(val); + len += pb_key_value_size(val, size_arr); } }, std::make_index_sequence{}); - static_assert(inherits_from_pb_base_v, - "must be inherited from iguana::pb_base_impl"); - t.cache_size = len; - + if constexpr (inherits_from_pb_base_v) { + t.cache_size = len; + } + else if constexpr (key_size != 0) { + (*size_arr)[pre_index] = len; + } if constexpr (key_size == 0) { // for top level return len; @@ -497,19 +522,20 @@ IGUANA_INLINE size_t pb_key_value_size(Type&& t) { size_t len = 0; if constexpr (is_lenprefix_v) { for (auto& item : t) { - len += pb_key_value_size(item); + len += pb_key_value_size(item, size_arr); } return len; } else { for (auto& item : t) { - // here 0 to get pakced size - len += numeric_size<0, false>(item); + // here 0 to get pakced size, and item must be numeric + len += str_numeric_size<0, false>(item); } if (len == 0) { return 0; } else { + (*size_arr).push_back(len); return key_size + variant_uint32_size(static_cast(len)) + len; } } @@ -518,8 +544,8 @@ IGUANA_INLINE size_t pb_key_value_size(Type&& t) { size_t len = 0; for (auto& [k, v] : t) { // the key_size of k and v is constant 1 - size_t kv_len = - pb_key_value_size<1, false>(k) + pb_key_value_size<1, false>(v); + auto kv_len = pb_key_value_size<1, false>(k, size_arr) + + pb_key_value_size<1, false>(v, size_arr); len += key_size + variant_uint32_size(static_cast(kv_len)) + kv_len; } @@ -529,60 +555,56 @@ IGUANA_INLINE size_t pb_key_value_size(Type&& t) { if (!t.has_value()) { return 0; } - return pb_key_value_size(*t); - } - else if constexpr (std::is_same_v || - std::is_same_v) { - if constexpr (omit_default_val) { - if (t.size() == 0) - IGUANA_UNLIKELY { return 0; } - } - if constexpr (key_size == 0) { - return t.size(); - } - else { - return key_size + variant_uint32_size(static_cast(t.size())) + - t.size(); - } + return pb_key_value_size(*t, size_arr); } else { - return numeric_size(t); + return str_numeric_size(t); } } // return the payload size -template -IGUANA_INLINE size_t pb_value_size(T&& t) { - using value_type = std::remove_const_t>; - if constexpr (is_reflection_v) { - static_assert(inherits_from_pb_base_v>, - "must be inherited from iguana::pb_base_impl"); - return t.cache_size; - } - else if constexpr (is_sequence_container::value) { - using item_type = typename value_type::value_type; - size_t len = 0; +template +IGUANA_INLINE size_t pb_value_size(Type&& t, uint32_t*& sz_ptr) { + using T = std::remove_const_t>; + if constexpr (is_reflection_v || is_custom_reflection_v) { + if constexpr (inherits_from_pb_base_v) { + return t.cache_size; + } + else { + // *sz_ptr is secure and logically guaranteed + if constexpr (skip_next) { + return *(sz_ptr++); + } + else { + return *sz_ptr; + } + } + } + else if constexpr (is_sequence_container::value) { + using item_type = typename T::value_type; if constexpr (!is_lenprefix_v) { - for (auto& item : t) { - len += numeric_size<0, false>(item); + if constexpr (skip_next) { + return *(sz_ptr++); + } + else { + return *sz_ptr; } - return len; } else { static_assert(!sizeof(item_type), "the size of this type is meaningless"); } } - else if constexpr (is_map_container::value) { - static_assert(!sizeof(value_type), "the size of this type is meaningless"); + else if constexpr (is_map_container::value) { + static_assert(!sizeof(T), "the size of this type is meaningless"); } - else if constexpr (optional_v) { + else if constexpr (optional_v) { if (!t.has_value()) { return 0; } - return pb_value_size(*t); + return pb_value_size(*t, sz_ptr); } else { - return pb_key_value_size<0>(t); + return str_numeric_size<0, false>(t); } } diff --git a/iguana/pb_writer.hpp b/iguana/pb_writer.hpp index 957d0e97..cdd1a4aa 100644 --- a/iguana/pb_writer.hpp +++ b/iguana/pb_writer.hpp @@ -27,10 +27,11 @@ IGUANA_INLINE void encode_fixed_field(V val, It&& it) { template -IGUANA_INLINE void to_pb_impl(Type&& t, It&& it); +IGUANA_INLINE void to_pb_impl(Type&& t, It&& it, uint32_t*& sz_ptr); template -IGUANA_INLINE void encode_pair_value(V&& val, It&& it, size_t size) { +IGUANA_INLINE void encode_pair_value(V&& val, It&& it, size_t size, + uint32_t*& sz_ptr) { if (size == 0) IGUANA_UNLIKELY { // map keys can't be omitted even if values are empty @@ -39,7 +40,7 @@ IGUANA_INLINE void encode_pair_value(V&& val, It&& it, size_t size) { serialize_varint(0, it); } else { - to_pb_impl(val, it); + to_pb_impl(val, it, sz_ptr); } } @@ -78,10 +79,10 @@ IGUANA_INLINE void encode_numeric_field(T t, It&& it) { } template -IGUANA_INLINE void to_pb_oneof(Type&& t, It&& it) { +IGUANA_INLINE void to_pb_oneof(Type&& t, It&& it, uint32_t*& sz_ptr) { using T = std::decay_t; std::visit( - [&it](auto&& value) IGUANA__INLINE_LAMBDA { + [&it, &sz_ptr](auto&& value) IGUANA__INLINE_LAMBDA { using value_type = std::remove_const_t>; constexpr auto offset = @@ -89,20 +90,19 @@ IGUANA_INLINE void to_pb_oneof(Type&& t, It&& it) { constexpr uint32_t key = ((field_no + offset) << 3) | static_cast(get_wire_type()); - to_pb_impl(std::forward(value), it); + to_pb_impl(std::forward(value), it, sz_ptr); }, std::forward(t)); } // omit_default_val = true indicates to omit the default value in searlization template -IGUANA_INLINE void to_pb_impl(Type&& t, It&& it) { +IGUANA_INLINE void to_pb_impl(Type&& t, It&& it, uint32_t*& sz_ptr) { using T = std::remove_const_t>; if constexpr (is_reflection_v || is_custom_reflection_v) { - // TODO: improve the key serialize - auto len = pb_value_size(t); // can't be omitted even if values are empty if constexpr (key != 0) { + auto len = pb_value_size(t, sz_ptr); serialize_varint_u32_constexpr(it); serialize_varint(len, it); if (len == 0) @@ -111,7 +111,7 @@ IGUANA_INLINE void to_pb_impl(Type&& t, It&& it) { static constexpr auto tuple = get_members_tuple(); constexpr size_t SIZE = std::tuple_size_v>; for_each_n( - [&t, &it](auto i) IGUANA__INLINE_LAMBDA { + [&t, &it, &sz_ptr](auto i) IGUANA__INLINE_LAMBDA { using field_type = std::tuple_element_t>; @@ -124,14 +124,14 @@ IGUANA_INLINE void to_pb_impl(Type&& t, It&& it) { get_variant_index - 1>(); if constexpr (offset == 0) { - to_pb_oneof(val, it); + to_pb_oneof(val, it, sz_ptr); } } else { constexpr uint32_t sub_key = (value.field_no << 3) | static_cast(get_wire_type()); - to_pb_impl(val, it); + to_pb_impl(val, it, sz_ptr); } }, std::make_index_sequence{}); @@ -143,14 +143,14 @@ IGUANA_INLINE void to_pb_impl(Type&& t, It&& it) { if constexpr (is_lenprefix_v) { // non-packed for (auto& item : t) { - to_pb_impl(item, it); + to_pb_impl(item, it, sz_ptr); } } else { if (t.empty()) IGUANA_UNLIKELY { return; } serialize_varint_u32_constexpr(it); - serialize_varint(pb_value_size(t), it); + serialize_varint(pb_value_size(t, sz_ptr), it); for (auto& item : t) { encode_numeric_field(item, it); } @@ -168,26 +168,27 @@ IGUANA_INLINE void to_pb_impl(Type&& t, It&& it) { for (auto& [k, v] : t) { serialize_varint_u32_constexpr(it); - auto k_len = pb_value_size(k); - auto v_len = pb_value_size(v); - auto pair_len = key1_size + key2_size + k_len + v_len; + // k must be string or numeric + auto k_val_len = str_numeric_size<0, false>(k); + auto v_val_len = pb_value_size(v, sz_ptr); + auto pair_len = key1_size + key2_size + k_val_len + v_val_len; if constexpr (is_lenprefix_v) { - pair_len += variant_uint32_size(k_len); + pair_len += variant_uint32_size(k_val_len); } if constexpr (is_lenprefix_v) { - pair_len += variant_uint32_size(v_len); + pair_len += variant_uint32_size(v_val_len); } serialize_varint(pair_len, it); // map k and v can't be omitted even if values are empty - encode_pair_value(k, it, k_len); - encode_pair_value(v, it, v_len); + encode_pair_value(k, it, k_val_len, sz_ptr); + encode_pair_value(v, it, v_val_len, sz_ptr); } } else if constexpr (optional_v) { if (!t.has_value()) { return; } - to_pb_impl(*t, it); + to_pb_impl(*t, it, sz_ptr); } else if constexpr (std::is_same_v || std::is_same_v) { @@ -208,8 +209,11 @@ IGUANA_INLINE void to_pb_impl(Type&& t, It&& it) { template IGUANA_INLINE void to_pb(T& t, Stream& out) { - auto byte_len = detail::pb_key_value_size<0>(t); + std::vector size_arr; + auto byte_len = detail::pb_key_value_size<0>(t, &size_arr); detail::resize(out, byte_len); - detail::to_pb_impl<0>(t, &out[0]); + auto sz_ptr = size_arr.empty() ? nullptr : &size_arr[0]; + detail::to_pb_impl<0>(t, &out[0], sz_ptr); } + } // namespace iguana diff --git a/test/test_pb.cpp b/test/test_pb.cpp index c0ea2ff5..abc02f4c 100644 --- a/test/test_pb.cpp +++ b/test/test_pb.cpp @@ -242,7 +242,6 @@ TEST_CASE("test struct_pb") { std::string str; iguana::to_pb(inner, str); - CHECK(str.size() == iguana::detail::pb_key_value_size<0>(inner)); my_space::inner_struct inner1; iguana::from_pb(inner1, str); @@ -266,7 +265,6 @@ TEST_CASE("test struct_pb") { test_pb_st1 st1{41, {42}, {43}}; std::string str; iguana::to_pb(st1, str); - CHECK(str.size() == iguana::detail::pb_key_value_size<0>(st1)); test_pb_st1 st2; iguana::from_pb(st2, str); @@ -279,7 +277,6 @@ TEST_CASE("test struct_pb") { test_pb_st2 st1{41, {42}, {43}}; std::string str; iguana::to_pb(st1, str); - CHECK(str.size() == iguana::detail::pb_key_value_size<0>(st1)); test_pb_st2 st2; iguana::from_pb(st2, str); @@ -289,7 +286,6 @@ TEST_CASE("test struct_pb") { test_pb_st3 st1{41, {42}, {43}}; std::string str; iguana::to_pb(st1, str); - CHECK(str.size() == iguana::detail::pb_key_value_size<0>(st1)); test_pb_st3 st2; iguana::from_pb(st2, str); @@ -299,7 +295,6 @@ TEST_CASE("test struct_pb") { test_pb_st4 st1{41, "it is a test"}; std::string str; iguana::to_pb(st1, str); - CHECK(str.size() == iguana::detail::pb_key_value_size<0>(st1)); test_pb_st4 st2; iguana::from_pb(st2, str); @@ -310,7 +305,6 @@ TEST_CASE("test struct_pb") { test_pb_st5 st1{41, "it is a test"}; std::string str; iguana::to_pb(st1, str); - CHECK(str.size() == iguana::detail::pb_key_value_size<0>(st1)); test_pb_st5 st2; iguana::from_pb(st2, str); @@ -321,7 +315,6 @@ TEST_CASE("test struct_pb") { test_pb_st6 st1{41, "it is a test"}; std::string str; iguana::to_pb(st1, str); - CHECK(str.size() == iguana::detail::pb_key_value_size<0>(st1)); test_pb_st6 st2; iguana::from_pb(st2, str); @@ -351,7 +344,6 @@ TEST_CASE("test struct_pb") { test_pb_st9 st1{1, {2, 4, 6}, "test"}; std::string str; iguana::to_pb(st1, str); - CHECK(str.size() == iguana::detail::pb_key_value_size<0>(st1)); test_pb_st9 st2; iguana::from_pb(st2, str); From b3eb293069006e0702b2330246ffe5f64a149fa4 Mon Sep 17 00:00:00 2001 From: bbgan <2893129936@qq.com> Date: Sat, 25 May 2024 15:01:31 +0800 Subject: [PATCH 2/3] add test --- test/test_pb.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/test/test_pb.cpp b/test/test_pb.cpp index abc02f4c..9c792725 100644 --- a/test/test_pb.cpp +++ b/test/test_pb.cpp @@ -15,7 +15,8 @@ void print_hex_str(const std::string &str) { std::cout << oss.str() << std::endl; } -#define BASE(T) : public iguana::pb_base_impl +#define BASE(T) +#define PUBLIC(T) : public iguana::pb_base_impl struct point_t BASE(point_t) { point_t() = default; @@ -51,7 +52,7 @@ struct test_pb_st1 BASE(test_pb_st1) { }; REFLECTION(test_pb_st1, x, y, z); -struct test_pb_sts BASE(test_pb_sts) { +struct test_pb_sts PUBLIC(test_pb_sts) { test_pb_sts() = default; test_pb_sts(std::vector l) : list(std::move(l)) {} std::vector list; @@ -111,7 +112,7 @@ struct pair_t BASE(pair_t) { }; REFLECTION(pair_t, x, y); -struct message_t BASE(message_t) { +struct message_t PUBLIC(message_t) { message_t() = default; message_t(int a, pair_t b) : id(a), t(b) {} int id; @@ -149,7 +150,7 @@ struct test_pb_st10 BASE(test_pb_st10) { }; REFLECTION(test_pb_st10, x, y, z); -struct test_pb_st11 BASE(test_pb_st11) { +struct test_pb_st11 PUBLIC(test_pb_st11) { test_pb_st11() = default; test_pb_st11(int a, std::vector> b, std::vector c) From 0b3eb6e216d1b2a469fbe5e161332ac7dd2d2b94 Mon Sep 17 00:00:00 2001 From: bbgan <2893129936@qq.com> Date: Sat, 25 May 2024 16:40:19 +0800 Subject: [PATCH 3/3] arr record only reflection sizes[struct_pb] --- iguana/pb_util.hpp | 26 ++++++++++++-------------- iguana/pb_writer.hpp | 2 +- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/iguana/pb_util.hpp b/iguana/pb_util.hpp index d1e0fb27..71c5ae1a 100644 --- a/iguana/pb_util.hpp +++ b/iguana/pb_util.hpp @@ -420,7 +420,7 @@ IGUANA_INLINE size_t str_numeric_size(Type&& t) { template -IGUANA_INLINE size_t pb_key_value_size(Type&& t, Arr* size_arr); +IGUANA_INLINE size_t pb_key_value_size(Type&& t, Arr& size_arr); template constexpr inline size_t get_variant_index() { @@ -439,11 +439,11 @@ constexpr inline size_t get_variant_index() { } template -IGUANA_INLINE size_t pb_oneof_size(Type&& t, Arr* size_arr) { +IGUANA_INLINE size_t pb_oneof_size(Type&& t, Arr& size_arr) { using T = std::decay_t; int len = 0; std::visit( - [&len, size_arr](auto&& value) IGUANA__INLINE_LAMBDA { + [&len, &size_arr](auto&& value) IGUANA__INLINE_LAMBDA { using value_type = std::remove_const_t>; constexpr auto offset = @@ -461,7 +461,7 @@ IGUANA_INLINE size_t pb_oneof_size(Type&& t, Arr* size_arr) { // returns size = key_size + optional(len_size) + len // when key_size == 0, return len template -IGUANA_INLINE size_t pb_key_value_size(Type&& t, Arr* size_arr) { +IGUANA_INLINE size_t pb_key_value_size(Type&& t, Arr& size_arr) { using T = std::remove_const_t>; if constexpr (is_reflection_v || is_custom_reflection_v) { size_t len = 0; @@ -469,11 +469,11 @@ IGUANA_INLINE size_t pb_key_value_size(Type&& t, Arr* size_arr) { constexpr size_t SIZE = std::tuple_size_v>; size_t pre_index = -1; if constexpr (!inherits_from_pb_base_v && key_size != 0) { - pre_index = (*size_arr).size(); - (*size_arr).push_back(0); // placeholder + pre_index = size_arr.size(); + size_arr.push_back(0); // placeholder } for_each_n( - [&len, &t, size_arr](auto i) IGUANA__INLINE_LAMBDA { + [&len, &t, &size_arr](auto i) IGUANA__INLINE_LAMBDA { using field_type = std::tuple_element_t>; @@ -501,7 +501,7 @@ IGUANA_INLINE size_t pb_key_value_size(Type&& t, Arr* size_arr) { t.cache_size = len; } else if constexpr (key_size != 0) { - (*size_arr)[pre_index] = len; + size_arr[pre_index] = len; } if constexpr (key_size == 0) { // for top level @@ -535,7 +535,6 @@ IGUANA_INLINE size_t pb_key_value_size(Type&& t, Arr* size_arr) { return 0; } else { - (*size_arr).push_back(len); return key_size + variant_uint32_size(static_cast(len)) + len; } } @@ -582,13 +581,12 @@ IGUANA_INLINE size_t pb_value_size(Type&& t, uint32_t*& sz_ptr) { } else if constexpr (is_sequence_container::value) { using item_type = typename T::value_type; + size_t len = 0; if constexpr (!is_lenprefix_v) { - if constexpr (skip_next) { - return *(sz_ptr++); - } - else { - return *sz_ptr; + for (auto& item : t) { + len += str_numeric_size<0, false>(item); } + return len; } else { static_assert(!sizeof(item_type), "the size of this type is meaningless"); diff --git a/iguana/pb_writer.hpp b/iguana/pb_writer.hpp index cdd1a4aa..db2662ab 100644 --- a/iguana/pb_writer.hpp +++ b/iguana/pb_writer.hpp @@ -210,7 +210,7 @@ IGUANA_INLINE void to_pb_impl(Type&& t, It&& it, uint32_t*& sz_ptr) { template IGUANA_INLINE void to_pb(T& t, Stream& out) { std::vector size_arr; - auto byte_len = detail::pb_key_value_size<0>(t, &size_arr); + auto byte_len = detail::pb_key_value_size<0>(t, size_arr); detail::resize(out, byte_len); auto sz_ptr = size_arr.empty() ? nullptr : &size_arr[0]; detail::to_pb_impl<0>(t, &out[0], sz_ptr);