diff --git a/README.md b/README.md index ffb451ae..3eecd68d 100644 --- a/README.md +++ b/README.md @@ -127,7 +127,11 @@ int main() { mysql.replace(p); mysql.replace(v); - auto result = mysql.query(); // vector + // 更新指定字段 + mysql.update_some<&person::name, &person::age>(p); + mysql.update_some<&person::name, &person::age>(v); + + auto result = mysql.query_s(); for (auto &person : result) { std::cout << person.id << " " << person.name << " " << person.age << std::endl; diff --git a/iguana/detail/fast_float.h b/iguana/detail/fast_float.h index 7505428a..ddc7f9e4 100644 --- a/iguana/detail/fast_float.h +++ b/iguana/detail/fast_float.h @@ -135,7 +135,6 @@ from_chars_result from_chars_advanced(const char* first, const char* last, #include #include - #if (defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || \ defined(__amd64) || defined(__aarch64__) || defined(_M_ARM64) || \ defined(__MINGW64__) || defined(__s390x__) || \ @@ -2709,7 +2708,7 @@ fastfloat_really_inline void round(adjusted_mantissa& am, if (-am.power2 >= mantissa_shift) { // have a denormal float int32_t shift = -am.power2 + 1; - cb(am, std::min(shift, 64)); + cb(am, (std::min)(shift, 64)); // check for round-up: if rounding-nearest carried us to the hidden bit. am.power2 = (am.mantissa < (uint64_t(1) << binary_format::mantissa_explicit_bits())) diff --git a/iguana/detail/pb_type.hpp b/iguana/detail/pb_type.hpp new file mode 100644 index 00000000..2ce5f60b --- /dev/null +++ b/iguana/detail/pb_type.hpp @@ -0,0 +1,135 @@ +#pragma once + +namespace iguana { + +struct sint32_t { + using value_type = int32_t; + int32_t val; + bool operator==(const sint32_t& other) const { return val == other.val; } +}; + +inline bool operator==(sint32_t value1, int32_t value2) { + return value1.val == value2; +} + +// for key in std::map +inline bool operator<(const sint32_t& lhs, const sint32_t& rhs) { + return lhs.val < rhs.val; +} + +struct sint64_t { + using value_type = int64_t; + int64_t val; + bool operator==(const sint64_t& other) const { return val == other.val; } +}; + +inline bool operator==(sint64_t value1, int64_t value2) { + return value1.val == value2; +} + +inline bool operator<(const sint64_t& lhs, const sint64_t& rhs) { + return lhs.val < rhs.val; +} + +struct fixed32_t { + using value_type = uint32_t; + uint32_t val; + bool operator==(const fixed32_t& other) const { return val == other.val; } +}; + +inline bool operator==(fixed32_t value1, uint32_t value2) { + return value1.val == value2; +} + +inline bool operator<(const fixed32_t& lhs, const fixed32_t& rhs) { + return lhs.val < rhs.val; +} + +struct fixed64_t { + using value_type = uint64_t; + uint64_t val; + bool operator==(const fixed64_t& other) const { return val == other.val; } +}; + +inline bool operator==(fixed64_t value1, uint64_t value2) { + return value1.val == value2; +} + +inline bool operator<(const fixed64_t& lhs, const fixed64_t& rhs) { + return lhs.val < rhs.val; +} + +struct sfixed32_t { + using value_type = int32_t; + int32_t val; + bool operator==(const sfixed32_t& other) const { return val == other.val; } +}; + +inline bool operator==(sfixed32_t value1, int32_t value2) { + return value1.val == value2; +} + +inline bool operator<(const sfixed32_t& lhs, const sfixed32_t& rhs) { + return lhs.val < rhs.val; +} + +struct sfixed64_t { + using value_type = int64_t; + int64_t val; + bool operator==(const sfixed64_t& other) const { return val == other.val; } +}; + +inline bool operator==(sfixed64_t value1, int64_t value2) { + return value1.val == value2; +} + +inline bool operator<(const sfixed64_t& lhs, const sfixed64_t& rhs) { + return lhs.val < rhs.val; +} + +} // namespace iguana + +// for key in std::unordered_map +namespace std { +template <> +struct hash { + size_t operator()(const iguana::sint32_t& x) const noexcept { + return std::hash()(x.val); + } +}; + +template <> +struct hash { + size_t operator()(const iguana::sint64_t& x) const noexcept { + return std::hash()(x.val); + } +}; + +template <> +struct hash { + size_t operator()(const iguana::fixed32_t& x) const noexcept { + return std::hash()(x.val); + } +}; + +template <> +struct hash { + size_t operator()(const iguana::fixed64_t& x) const noexcept { + return std::hash()(x.val); + } +}; + +template <> +struct hash { + size_t operator()(const iguana::sfixed32_t& x) const noexcept { + return std::hash()(x.val); + } +}; + +template <> +struct hash { + size_t operator()(const iguana::sfixed64_t& x) const noexcept { + return std::hash()(x.val); + } +}; +} // namespace std diff --git a/iguana/detail/string_resize.hpp b/iguana/detail/string_resize.hpp new file mode 100644 index 00000000..81d4605e --- /dev/null +++ b/iguana/detail/string_resize.hpp @@ -0,0 +1,131 @@ +#pragma once +#include +#include +#include +#include + +namespace iguana::detail { + +#if __cpp_lib_string_resize_and_overwrite >= 202110L +template +inline void resize(std::basic_string &str, std::size_t sz) { + str.resize_and_overwrite(sz, [sz](ch *, std::size_t) { + return sz; + }); +} +#elif (defined(_MSC_VER) && _MSC_VER <= 1920) +// old msvc don't support visit private, discard it. + +#else + +template +class string_thief { + public: + friend void string_set_length_hacker(std::string &self, std::size_t sz) { +#if defined(_MSVC_STL_VERSION) + (self.*func_ptr)._Myval2._Mysize = sz; +#else +#if defined(_LIBCPP_VERSION) + (self.*func_ptr)(sz); +#else +#if (_GLIBCXX_USE_CXX11_ABI == 0) && defined(__GLIBCXX__) + (self.*func_ptr)()->_M_set_length_and_sharable(sz); +#else +#if defined(__GLIBCXX__) + (self.*func_ptr)(sz); +#endif +#endif +#endif +#endif + } +}; + +#if defined(__GLIBCXX__) // libstdc++ +#if (_GLIBCXX_USE_CXX11_ABI == 0) +template class string_thief; +#else +template class string_thief; +#endif +#elif defined(_LIBCPP_VERSION) +template class string_thief; +#elif defined(_MSVC_STL_VERSION) +template class string_thief; +#endif + +void string_set_length_hacker(std::string &, std::size_t); + +template +inline void resize(std::basic_string &raw_str, std::size_t sz) { + std::string &str = *reinterpret_cast(&raw_str); +#if defined(__GLIBCXX__) || defined(_LIBCPP_VERSION) || \ + defined(_MSVC_STL_VERSION) + if (sz > str.capacity()) { + str.reserve(sz); + } + string_set_length_hacker(str, sz); + str[sz] = '\0'; +#else + raw_str.resize(sz); +#endif +} + +#endif + +#if (defined(_MSC_VER) && _MSC_VER <= 1920) +#else +void vector_set_length_hacker(std::vector &self, std::size_t sz); + +template +class vector_thief { + public: + friend void vector_set_length_hacker(std::vector &self, + std::size_t sz) { +#if defined(_MSVC_STL_VERSION) + (self.*func_ptr)._Myval2._Mylast = self.data() + sz; +#else +#if defined(_LIBCPP_VERSION) +#if _LIBCPP_VERSION < 14000 + ((*(std::__vector_base > *)(&self)).*func_ptr) = + self.data() + sz; +#else + (self.*func_ptr) = self.data() + sz; +#endif +#else +#if defined(__GLIBCXX__) + ((*(std::_Vector_base > *)(&self)).*func_ptr) + ._M_finish = self.data() + sz; +#endif +#endif +#endif + } +}; + +#if defined(__GLIBCXX__) // libstdc++ +template class vector_thief::_M_impl), + &std::vector::_M_impl>; +#elif defined(_LIBCPP_VERSION) +template class vector_thief::__end_), + &std::vector::__end_>; +#elif defined(_MSVC_STL_VERSION) +template class vector_thief::_Mypair), + &std::vector::_Mypair>; +#endif + +template +inline void resize(std::vector &raw_vec, std::size_t sz) { +#if defined(__GLIBCXX__) || \ + (defined(_LIBCPP_VERSION) && defined(_LIBCPP_HAS_NO_ASAN)) || \ + defined(_MSVC_STL_VERSION) + std::vector &vec = *reinterpret_cast *>(&raw_vec); + vec.reserve(sz); + vector_set_length_hacker(vec, sz); +#else + raw_vec.resize(sz); +#endif +} +#endif +}; // namespace iguana::detail \ No newline at end of file diff --git a/iguana/detail/traits.hpp b/iguana/detail/traits.hpp index 64f082e0..57a9fa45 100644 --- a/iguana/detail/traits.hpp +++ b/iguana/detail/traits.hpp @@ -79,5 +79,39 @@ struct member_tratis { template inline constexpr bool is_int64_v = std::is_same_v || std::is_same_v; + +template +struct is_variant : std::false_type {}; + +template +struct is_variant> : std::true_type {}; + +template +struct member_traits { + using value_type = T; +}; + +template +struct member_traits { + using owner_type = Owner; + using value_type = T; +}; + +template +using member_value_type_t = typename member_traits::value_type; + +template +struct variant_type_at { + using type = T; +}; + +template +struct variant_type_at::value>> { + using type = std::variant_alternative_t; +}; +template +using variant_type_at_t = + typename variant_type_at::value_type, I>::type; + } // namespace iguana #endif // SERIALIZE_TRAITS_HPP diff --git a/iguana/pb_reader.hpp b/iguana/pb_reader.hpp new file mode 100644 index 00000000..8d10fe08 --- /dev/null +++ b/iguana/pb_reader.hpp @@ -0,0 +1,260 @@ +#pragma once +#include "detail/string_resize.hpp" +#include "pb_util.hpp" + +namespace iguana { + +template +IGUANA_INLINE void from_pb(T& t, std::string_view pb_str); + +namespace detail { + +template +IGUANA_INLINE void from_pb_impl(T& val, std::string_view& pb_str, + uint32_t field_no = 0); + +template +IGUANA_INLINE void decode_pair_value(T& val, std::string_view& pb_str) { + size_t pos; + uint32_t key = detail::decode_varint(pb_str, pos); + pb_str = pb_str.substr(pos); + WireType wire_type = static_cast(key & 0b0111); + if (wire_type != detail::get_wire_type>()) { + return; + } + from_pb_impl(val, pb_str); +} + +template +IGUANA_INLINE void from_pb_impl(T& val, std::string_view& pb_str, + uint32_t field_no) { + size_t pos = 0; + if constexpr (is_reflection_v) { + size_t pos; + uint32_t size = detail::decode_varint(pb_str, pos); + pb_str = pb_str.substr(pos); + if (pb_str.size() < size) + IGUANA_UNLIKELY { + throw std::invalid_argument("Invalid fixed int value: too few bytes."); + } + if (size == 0) { + return; + } + from_pb(val, pb_str.substr(0, size)); + pb_str = pb_str.substr(size); + } + else if constexpr (is_sequence_container::value) { + using item_type = typename T::value_type; + if constexpr (is_lenprefix_v) { + // item_type non-packed + while (!pb_str.empty()) { + item_type item{}; + from_pb_impl(item, pb_str); + val.push_back(std::move(item)); + if (pb_str.empty()) { + break; + } + uint32_t key = detail::decode_varint(pb_str, pos); + uint32_t field_number = key >> 3; + if (field_number != field_no) { + break; + } + else { + pb_str = pb_str.substr(pos); + } + } + } + else { + // item_type packed + size_t pos; + uint32_t size = detail::decode_varint(pb_str, pos); + pb_str = pb_str.substr(pos); + if (pb_str.size() < size) + IGUANA_UNLIKELY { + throw std::invalid_argument( + "Invalid fixed int value: too few bytes."); + } + using item_type = typename T::value_type; + size_t start = pb_str.size(); + + while (!pb_str.empty()) { + item_type item; + from_pb_impl(item, pb_str); + val.push_back(std::move(item)); + if (start - pb_str.size() == size) { + break; + } + } + } + } + else if constexpr (is_map_container::value) { + using item_type = std::pair; + while (!pb_str.empty()) { + size_t pos; + uint32_t size = detail::decode_varint(pb_str, pos); + pb_str = pb_str.substr(pos); + if (pb_str.size() < size) + IGUANA_UNLIKELY { + throw std::invalid_argument( + "Invalid fixed int value: too few bytes."); + } + item_type item = {}; + decode_pair_value(item.first, pb_str); + decode_pair_value(item.second, pb_str); + val.emplace(std::move(item)); + + if (pb_str.empty()) { + break; + } + uint32_t key = detail::decode_varint(pb_str, pos); + uint32_t field_number = key >> 3; + if (field_number != field_no) { + break; + } + pb_str = pb_str.substr(pos); + } + } + else if constexpr (std::is_integral_v) { + val = static_cast(detail::decode_varint(pb_str, pos)); + pb_str = pb_str.substr(pos); + } + else if constexpr (detail::is_signed_varint_v) { + constexpr size_t len = sizeof(typename T::value_type); + uint64_t temp = detail::decode_varint(pb_str, pos); + if constexpr (len == 8) { + val.val = detail::decode_zigzag(temp); + } + else { + val.val = detail::decode_zigzag(static_cast(temp)); + } + pb_str = pb_str.substr(pos); + } + else if constexpr (detail::is_fixed_v) { + constexpr size_t size = sizeof(typename T::value_type); + if (pb_str.size() < size) + IGUANA_UNLIKELY { + throw std::invalid_argument("Invalid fixed int value: too few bytes."); + } + memcpy(&(val.val), pb_str.data(), size); + pb_str = pb_str.substr(size); + } + else if constexpr (std::is_same_v || std::is_same_v) { + constexpr size_t size = sizeof(T); + if (pb_str.size() < size) + IGUANA_UNLIKELY { + throw std::invalid_argument("Invalid fixed int value: too few bytes."); + } + memcpy(&(val), pb_str.data(), size); + pb_str = pb_str.substr(size); + } + else if constexpr (std::is_same_v || + std::is_same_v) { + size_t size = detail::decode_varint(pb_str, pos); + if (pb_str.size() < pos + size) + IGUANA_UNLIKELY { + throw std::invalid_argument("Invalid string value: too few bytes."); + } + if constexpr (std::is_same_v) { + val = std::string_view(pb_str.data() + pos, size); + } + else { + detail::resize(val, size); + memcpy(val.data(), pb_str.data() + pos, size); + } + pb_str = pb_str.substr(size + pos); + } + else if constexpr (std::is_enum_v) { + using U = std::underlying_type_t; + U value{}; + from_pb_impl(value, pb_str); + val = static_cast(value); + } + else if constexpr (optional_v) { + from_pb_impl(val.emplace(), pb_str); + } + else { + static_assert(!sizeof(T), "err"); + } +} + +template +IGUANA_INLINE void parse_oneof(T& t, const Field& f, std::string_view& pb_str) { + using item_type = typename std::decay_t::sub_type; + from_pb_impl(t.template emplace(), pb_str, f.field_no); +} +} // namespace detail + +template +IGUANA_INLINE void from_pb(T& t, std::string_view pb_str) { + if (pb_str.empty()) + IGUANA_UNLIKELY { return; } + size_t pos = 0; + uint32_t key = detail::decode_varint(pb_str, pos); + WireType wire_type = static_cast(key & 0b0111); + uint32_t field_number = key >> 3; +#ifdef SEQUENTIAL_PARSE + constexpr static auto tp = get_members_tuple(); + constexpr size_t SIZE = std::tuple_size_v>; + bool parse_done = false; + detail::for_each_n( + [&](auto i) IGUANA__INLINE_LAMBDA { + constexpr auto val = std::get(tp); + using sub_type = typename std::decay_t::sub_type; + using value_type = typename std::decay_t::value_type; + // sub_type is the element type when value_type is the variant type; + // otherwise, they are the same. + if (parse_done || field_number != val.field_no) { + return; + } + pb_str = pb_str.substr(pos); + if (wire_type != detail::get_wire_type()) + IGUANA_UNLIKELY { throw std::runtime_error("unmatched wire_type"); } + if constexpr (variant_v) { + detail::parse_oneof(val.value(t), val, pb_str); + } + else { + detail::from_pb_impl(val.value(t), pb_str, val.field_no); + } + if (pb_str.empty()) { + parse_done = true; + return; + } + key = detail::decode_varint(pb_str, pos); + wire_type = static_cast(key & 0b0111); + field_number = key >> 3; + }, + std::make_index_sequence{}); + if (parse_done) + IGUANA_LIKELY { return; } +#endif + while (true) { + pb_str = pb_str.substr(pos); + constexpr static auto map = get_members(); + auto& member = map.at(field_number); + std::visit( + [&t, &pb_str, wire_type](auto& val) { + using sub_type = typename std::decay_t::sub_type; + using value_type = typename std::decay_t::value_type; + if (wire_type != detail::get_wire_type()) { + throw std::runtime_error("unmatched wire_type"); + } + if constexpr (variant_v) { + detail::parse_oneof(val.value(t), val, pb_str); + } + else { + detail::from_pb_impl(val.value(t), pb_str, val.field_no); + } + }, + member); + if (!pb_str.empty()) + IGUANA_LIKELY { + key = detail::decode_varint(pb_str, pos); + wire_type = static_cast(key & 0b0111); + field_number = key >> 3; + } + else { + return; + } + } +} +} // namespace iguana diff --git a/iguana/pb_util.hpp b/iguana/pb_util.hpp new file mode 100644 index 00000000..44c26af5 --- /dev/null +++ b/iguana/pb_util.hpp @@ -0,0 +1,539 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "detail/pb_type.hpp" +#include "reflection.hpp" +#include "util.hpp" + +namespace iguana { + +enum class WireType : uint32_t { + Varint = 0, + Fixed64 = 1, + LengthDelimeted = 2, + StartGroup = 3, + EndGroup = 4, + Fixed32 = 5, + Unknown +}; + +struct pb_base { + size_t cache_size; +}; + +template +constexpr bool inherits_from_pb_base_v = std::is_base_of_v; + +namespace detail { +template +constexpr bool is_fixed_v = + std::is_same_v || std::is_same_v || + std::is_same_v || std::is_same_v; + +template +constexpr bool is_signed_varint_v = + std::is_same_v || std::is_same_v; + +template +constexpr inline WireType get_wire_type() { + if constexpr (std::is_integral_v || is_signed_varint_v || + std::is_enum_v || std::is_same_v) { + return WireType::Varint; + } + else if constexpr (std::is_same_v || + std::is_same_v || + std::is_same_v) { + return WireType::Fixed32; + } + else if constexpr (std::is_same_v || + std::is_same_v || + std::is_same_v) { + return WireType::Fixed64; + } + else if constexpr (std::is_same_v || + std::is_same_v || + is_reflection_v || is_sequence_container::value || + is_map_container::value) { + return WireType::LengthDelimeted; + } + else if constexpr (optional_v) { + return get_wire_type(); + } + else { + throw std::runtime_error("unknown type"); + } +} + +template +constexpr bool is_lenprefix_v = (get_wire_type() == + WireType::LengthDelimeted); + +[[nodiscard]] IGUANA_INLINE uint32_t encode_zigzag(int32_t v) { + return (static_cast(v) << 1U) ^ + static_cast( + -static_cast(static_cast(v) >> 31U)); +} +[[nodiscard]] IGUANA_INLINE uint64_t encode_zigzag(int64_t v) { + return (static_cast(v) << 1U) ^ + static_cast( + -static_cast(static_cast(v) >> 63U)); +} + +[[nodiscard]] IGUANA_INLINE int64_t decode_zigzag(uint64_t u) { + return static_cast((u >> 1U)) ^ + static_cast(-static_cast(u & 1U)); +} +[[nodiscard]] IGUANA_INLINE int64_t decode_zigzag(uint32_t u) { + return static_cast((u >> 1U)) ^ + static_cast(-static_cast(u & 1U)); +} + +template +IGUANA_INLINE uint64_t decode_varint(T& data, size_t& pos) { + const int8_t* begin = reinterpret_cast(data.data()); + const int8_t* end = begin + data.size(); + const int8_t* p = begin; + uint64_t val = 0; + + if ((static_cast(*p) & 0x80) == 0) { + pos = 1; + return static_cast(*p); + } + + // end is always greater than or equal to begin, so this subtraction is safe + if (size_t(end - begin) >= 10) + IGUANA_LIKELY { // fast path + int64_t b; + do { + b = *p++; + val = (b & 0x7f); + if (b >= 0) { + break; + } + b = *p++; + val |= (b & 0x7f) << 7; + if (b >= 0) { + break; + } + b = *p++; + val |= (b & 0x7f) << 14; + if (b >= 0) { + break; + } + b = *p++; + val |= (b & 0x7f) << 21; + if (b >= 0) { + break; + } + b = *p++; + val |= (b & 0x7f) << 28; + if (b >= 0) { + break; + } + b = *p++; + val |= (b & 0x7f) << 35; + if (b >= 0) { + break; + } + b = *p++; + val |= (b & 0x7f) << 42; + if (b >= 0) { + break; + } + b = *p++; + val |= (b & 0x7f) << 49; + if (b >= 0) { + break; + } + b = *p++; + val |= (b & 0x7f) << 56; + if (b >= 0) { + break; + } + b = *p++; + val |= (b & 0x01) << 63; + if (b >= 0) { + break; + } + throw std::invalid_argument("Invalid varint value: too many bytes."); + } while (false); + } + else { + int shift = 0; + while (p != end && *p < 0) { + val |= static_cast(*p++ & 0x7f) << shift; + shift += 7; + } + if (p == end) + IGUANA_UNLIKELY { + throw std::invalid_argument("Invalid varint value: too few bytes."); + } + val |= static_cast(*p++) << shift; + } + + pos = (p - begin); + return val; +} + +// value == 0 ? 1 : floor(log2(value)) / 7 + 1 +constexpr size_t variant_uint32_size_constexpr(uint32_t value) { + if (value == 0) { + return 1; + } + int log = 0; + while (value >>= 1) ++log; + return log / 7 + 1; +} + +template +IGUANA_INLINE void append_varint_u32_constexpr_help(It&& it) { + *(it++) = static_cast((v >> (7 * I)) | 0x80); +} + +template +IGUANA_INLINE void append_varint_u32_constexpr(It&& it, + std::index_sequence) { + (append_varint_u32_constexpr_help(it), ...); +} + +template +IGUANA_INLINE void serialize_varint_u32_constexpr(It&& it) { + constexpr auto size = variant_uint32_size_constexpr(v); + append_varint_u32_constexpr(it, std::make_index_sequence{}); + *(it++) = static_cast(v >> (7 * (size - 1))); +} + +template +IGUANA_INLINE void serialize_varint(uint64_t v, It&& it) { + if (v < 0x80) { + *(it++) = static_cast(v); + return; + } + *(it++) = static_cast(v | 0x80); + v >>= 7; + if (v < 0x80) { + *(it++) = static_cast(v); + return; + } + do { + *(it++) = static_cast(v | 0x80); + v >>= 7; + } while (v >= 0x80); + *(it++) = static_cast(v); +} + +IGUANA_INLINE uint32_t log2_floor_uint32(uint32_t n) { +#if defined(__GNUC__) + return 31 ^ static_cast(__builtin_clz(n)); +#else + unsigned long where; + _BitScanReverse(&where, n); + return where; +#endif +} + +IGUANA_INLINE size_t variant_uint32_size(uint32_t value) { + // This computes value == 0 ? 1 : floor(log2(value)) / 7 + 1 + // Use an explicit multiplication to implement the divide of + // a number in the 1..31 range. + // Explicit OR 0x1 to avoid calling Bits::Log2FloorNonZero(0), which is + // undefined. + uint32_t log2value = log2_floor_uint32(value | 0x1); + return static_cast((log2value * 9 + 73) / 64); +} + +IGUANA_INLINE uint32_t log2_floor_uint64(uint64_t n) { +#if defined(__GNUC__) + return 63 ^ static_cast(__builtin_clzll(n)); +#else + unsigned long where; + _BitScanReverse64(&where, n); + return where; +#endif +} + +IGUANA_INLINE size_t variant_uint64_size(uint64_t value) { + // This computes value == 0 ? 1 : floor(log2(value)) / 7 + 1 + // Use an explicit multiplication to implement the divide of + // a number in the 1..63 range. + // Explicit OR 0x1 to avoid calling Bits::Log2FloorNonZero(0), which is + // undefined. + uint32_t log2value = log2_floor_uint64(value | 0x1); + return static_cast((log2value * 9 + 73) / 64); +} + +template +constexpr IGUANA_INLINE size_t variant_intergal_size(U value) { + using T = std::remove_reference_t; + if constexpr (sizeof(T) == 8) { + return variant_uint64_size(static_cast(value)); + } + else if constexpr (sizeof(T) <= 4) { + if constexpr (std::is_signed_v) { + if (value < 0) { + return 10; + } + } + return variant_uint32_size(static_cast(value)); + } + else { + static_assert(!sizeof(T), "intergal in not supported"); + } +} + +template +IGUANA_INLINE constexpr void for_each_n(F&& f, std::index_sequence) { + (std::forward(f)(std::integral_constant{}), ...); +} + +// cache the size of reflection type +template +IGUANA_INLINE auto& get_set_size_cache(T& t) { + static std::map cache; + return cache[reinterpret_cast(&t)]; +} + +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) + 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 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 +constexpr inline size_t get_variant_index() { + if constexpr (I == 0) { + static_assert(std::is_same_v, T>, + "Type T is not found in Variant"); + return 0; + } + else if constexpr (std::is_same_v, + T>) { + return I; + } + else { + return get_variant_index(); + } +} + +template +IGUANA_INLINE size_t pb_oneof_size(Type&& t) { + using T = std::decay_t; + int len = 0; + std::visit( + [&len](auto&& value) IGUANA__INLINE_LAMBDA { + using value_type = + std::remove_const_t>; + constexpr auto offset = + get_variant_index - 1>(); + constexpr uint32_t key = + ((field_no + offset) << 3) | + static_cast(get_wire_type()); + len = pb_key_value_size( + std::forward(value)); + }, + std::forward(t)); + return len; +} + +// 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) { + using T = std::remove_const_t>; + if constexpr (is_reflection_v || is_custom_reflection_v) { + size_t len = 0; + constexpr auto tuple = get_members_tuple(); + constexpr size_t SIZE = std::tuple_size_v>; + for_each_n( + [&len, &t](auto i) IGUANA__INLINE_LAMBDA { + constexpr auto tuple = get_members_tuple(); + using field_type = + std::tuple_element_t>; + constexpr auto value = std::get(tuple); + using U = typename field_type::value_type; + auto& val = value.value(t); + if constexpr (variant_v) { + constexpr auto offset = + get_variant_index - 1>(); + if constexpr (offset == 0) { + len += pb_oneof_size(val); + } + } + else { + constexpr uint32_t sub_key = + (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); + } + }, + std::make_index_sequence{}); + if constexpr (inherits_from_pb_base_v) { + t.cache_size = len; + } + else { + get_set_size_cache(t) = len; + } + if constexpr (key_size == 0) { + // for top level + return len; + } + else { + if (len == 0) { + // equals key_size + variant_uint32_size(len) + return key_size + 1; + } + else { + return key_size + variant_uint32_size(static_cast(len)) + len; + } + } + } + else if constexpr (is_sequence_container::value) { + using item_type = typename T::value_type; + size_t len = 0; + if constexpr (is_lenprefix_v) { + for (auto& item : t) { + len += pb_key_value_size(item); + } + return len; + } + else { + for (auto& item : t) { + // here 0 to get pakced size + len += numeric_size<0, false>(item); + } + if (len == 0) { + return 0; + } + else { + return key_size + variant_uint32_size(static_cast(len)) + len; + } + } + } + else if constexpr (is_map_container::value) { + 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); + len += key_size + variant_uint32_size(static_cast(kv_len)) + + kv_len; + } + return len; + } + else if constexpr (optional_v) { + 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(); + } + } + else { + return 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) { + if constexpr (inherits_from_pb_base_v) { + return t.cache_size; + } + else { + return get_set_size_cache(t); + } + } + else if constexpr (is_sequence_container::value) { + using item_type = typename value_type::value_type; + size_t len = 0; + if constexpr (!is_lenprefix_v) { + for (auto& item : t) { + len += numeric_size<0, false>(item); + } + 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 (optional_v) { + if (!t.has_value()) { + return 0; + } + return pb_value_size(*t); + } + else { + return pb_key_value_size<0>(t); + } +} + +} // namespace detail +} // namespace iguana \ No newline at end of file diff --git a/iguana/pb_writer.hpp b/iguana/pb_writer.hpp new file mode 100644 index 00000000..957d0e97 --- /dev/null +++ b/iguana/pb_writer.hpp @@ -0,0 +1,215 @@ +#pragma once +#include "detail/string_resize.hpp" +#include "pb_util.hpp" + +namespace iguana { +namespace detail { + +template +IGUANA_INLINE void encode_varint_field(V val, It&& it) { + static_assert(std::is_integral_v, "must be integral"); + if constexpr (key != 0) { + serialize_varint_u32_constexpr(it); + } + serialize_varint(val, it); +} + +template +IGUANA_INLINE void encode_fixed_field(V val, It&& it) { + if constexpr (key != 0) { + serialize_varint_u32_constexpr(it); + } + constexpr size_t size = sizeof(V); + // TODO: check Stream continuous + memcpy(it, &val, size); + it += size; +} + +template +IGUANA_INLINE void to_pb_impl(Type&& t, It&& it); + +template +IGUANA_INLINE void encode_pair_value(V&& val, It&& it, size_t size) { + if (size == 0) + IGUANA_UNLIKELY { + // map keys can't be omitted even if values are empty + // TODO: repeated ? + serialize_varint_u32_constexpr(it); + serialize_varint(0, it); + } + else { + to_pb_impl(val, it); + } +} + +template +IGUANA_INLINE void encode_numeric_field(T t, It&& it) { + if constexpr (omit_default_val) { + if constexpr (is_fixed_v || is_signed_varint_v) { + if (t.val == 0) { + return; + } + } + else { + if (t == static_cast(0)) + IGUANA_UNLIKELY { return; } + } + } + if constexpr (std::is_integral_v) { + detail::encode_varint_field(t, it); + } + else if constexpr (detail::is_signed_varint_v) { + detail::encode_varint_field(encode_zigzag(t.val), it); + } + else if constexpr (detail::is_fixed_v) { + detail::encode_fixed_field(t.val, it); + } + else if constexpr (std::is_same_v || std::is_same_v) { + detail::encode_fixed_field(t, it); + } + else if constexpr (std::is_enum_v) { + using U = std::underlying_type_t; + detail::encode_varint_field(static_cast(t), it); + } + else { + static_assert(!sizeof(T), "unsupported type"); + } +} + +template +IGUANA_INLINE void to_pb_oneof(Type&& t, It&& it) { + using T = std::decay_t; + std::visit( + [&it](auto&& value) IGUANA__INLINE_LAMBDA { + using value_type = + std::remove_const_t>; + constexpr auto offset = + get_variant_index - 1>(); + constexpr uint32_t key = + ((field_no + offset) << 3) | + static_cast(get_wire_type()); + to_pb_impl(std::forward(value), it); + }, + 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) { + 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) { + serialize_varint_u32_constexpr(it); + serialize_varint(len, it); + if (len == 0) + IGUANA_UNLIKELY { return; } + } + 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 { + using field_type = + std::tuple_element_t>; + constexpr auto value = std::get(tuple); + auto& val = value.value(t); + + using U = typename field_type::value_type; + if constexpr (variant_v) { + constexpr auto offset = + get_variant_index - 1>(); + if constexpr (offset == 0) { + to_pb_oneof(val, it); + } + } + else { + constexpr uint32_t sub_key = + (value.field_no << 3) | + static_cast(get_wire_type()); + to_pb_impl(val, it); + } + }, + std::make_index_sequence{}); + } + else if constexpr (is_sequence_container::value) { + // TODO support std::array + // repeated values can't be omitted even if values are empty + using item_type = typename T::value_type; + if constexpr (is_lenprefix_v) { + // non-packed + for (auto& item : t) { + to_pb_impl(item, it); + } + } + else { + if (t.empty()) + IGUANA_UNLIKELY { return; } + serialize_varint_u32_constexpr(it); + serialize_varint(pb_value_size(t), it); + for (auto& item : t) { + encode_numeric_field(item, it); + } + } + } + else if constexpr (is_map_container::value) { + using first_type = typename T::key_type; + using second_type = typename T::mapped_type; + constexpr uint32_t key1 = + (1 << 3) | static_cast(get_wire_type()); + constexpr auto key1_size = variant_uint32_size_constexpr(key1); + constexpr uint32_t key2 = + (2 << 3) | static_cast(get_wire_type()); + constexpr auto key2_size = variant_uint32_size_constexpr(key2); + + 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; + if constexpr (is_lenprefix_v) { + pair_len += variant_uint32_size(k_len); + } + if constexpr (is_lenprefix_v) { + pair_len += variant_uint32_size(v_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); + } + } + else if constexpr (optional_v) { + if (!t.has_value()) { + return; + } + to_pb_impl(*t, it); + } + else if constexpr (std::is_same_v || + std::is_same_v) { + if constexpr (omit_default_val) { + if (t.size() == 0) + IGUANA_UNLIKELY { return; } + } + serialize_varint_u32_constexpr(it); + serialize_varint(t.size(), it); + memcpy(it, t.data(), t.size()); + it += t.size(); + } + else { + encode_numeric_field(t, it); + } +} +} // namespace detail + +template +IGUANA_INLINE void to_pb(T& t, Stream& out) { + auto byte_len = detail::pb_key_value_size<0>(t); + detail::resize(out, byte_len); + detail::to_pb_impl<0>(t, &out[0]); +} +} // namespace iguana diff --git a/iguana/reflection.hpp b/iguana/reflection.hpp index 234da95d..c2ff73bd 100644 --- a/iguana/reflection.hpp +++ b/iguana/reflection.hpp @@ -692,6 +692,143 @@ inline constexpr auto get_iguana_struct_map() { } } +template ::value_type> +struct field_t { + using member_type = T; + using owner_type = typename member_traits::owner_type; + using value_type = typename member_traits::value_type; + using sub_type = ElementType; + constexpr field_t() = default; + constexpr field_t(T member, uint32_t number, frozen::string name = "") + : member_ptr(member), field_name(name), field_no(number) {} + + T member_ptr; + frozen::string field_name; + uint32_t field_no; + + auto &value(owner_type &value) const { return value.*member_ptr; } +}; + +template +struct field_type_t; + +template +struct field_type_t> { + using value_type = std::variant; +}; + +template +struct is_custom_reflection : std::false_type {}; + +template +struct is_custom_reflection< + T, std::void_t()))>> + : std::true_type {}; + +template +struct is_reflection : std::false_type {}; + +template +inline constexpr bool is_reflection_v = is_reflection::value; + +template +inline constexpr bool is_custom_reflection_v = is_custom_reflection::value; + +template +constexpr inline auto build_variant_fields(T t, S &s, uint32_t base_idx, + std::index_sequence) { + using value_type = typename member_traits::value_type; + return std::tuple(field_t>{ + t, (base_idx + uint32_t(I)), s}...); +} + +template +constexpr inline auto build_fields(T t, S &s, uint32_t &index) { + using value_type = typename member_traits::value_type; + if constexpr (is_variant::value) { + constexpr uint32_t Size = std::variant_size_v; + index += (Size - 1); + return build_variant_fields(t, s, I + 1, std::make_index_sequence{}); + } + else { + uint32_t field_no = (I == index) ? (I + 1) : (I + index); + index++; + return std::tuple(field_t{t, field_no, s}); + } +} + +template +constexpr inline auto get_members_tuple_impl(T &&tp, U &&arr, + std::index_sequence &&) { + uint32_t index = 0; + return std::tuple_cat(build_fields(std::get(tp), arr[I], index)...); +} + +template +constexpr inline auto get_members_tuple() { + if constexpr (is_reflection::value) { + using reflect_members = decltype(iguana_reflect_type(std::declval())); + using Tuple = decltype(reflect_members::apply_impl()); + constexpr size_t Size = std::tuple_size_v; + return get_members_tuple_impl(reflect_members::apply_impl(), + reflect_members::arr(), + std::make_index_sequence{}); + } + else if constexpr (is_custom_reflection_v) { + using U = std::remove_const_t>; + return get_members_impl((U *)nullptr); + } + else { + static_assert(!sizeof(T), "expected reflection or custom reflection"); + } +} + +template +constexpr auto inline get_members_impl(Tuple &&tp, std::index_sequence) { + return frozen::unordered_map{ + {std::get(tp).field_no, + T{std::in_place_index, std::move(std::get(tp))}}...}; +} + +template +constexpr size_t count_variant_size() { + if constexpr (is_variant::value) { + return std::variant_size_v; + } + else { + return 1; + } +} + +template +constexpr size_t tuple_type_count_impl(std::index_sequence) { + return ( + (count_variant_size>>() + + ...)); +} + +template +constexpr size_t tuple_type_count() { + return tuple_type_count_impl( + std::make_index_sequence>{}); +} + +template +constexpr inline auto get_members() { + if constexpr (is_reflection_v || is_custom_reflection_v) { + constexpr auto tp = get_members_tuple(); + using Tuple = std::decay_t; + using value_type = typename field_type_t::value_type; + constexpr auto Size = tuple_type_count(); + return get_members_impl(tp, + std::make_index_sequence{}); + } + else { + static_assert(!sizeof(T), "expected reflection or custom reflection"); + } +} + #define REFLECTION(STRUCT_NAME, ...) \ MAKE_META_DATA(STRUCT_NAME, #STRUCT_NAME, GET_ARG_COUNT(__VA_ARGS__), \ __VA_ARGS__) @@ -800,9 +937,6 @@ struct is_private_reflection< template constexpr bool is_private_reflection_v = is_private_reflection::value; -template -struct is_reflection : std::false_type {}; - template struct is_reflection>> : std::true_type {}; @@ -821,9 +955,6 @@ inline auto iguana_reflect_type(const T &t) { } } -template -inline constexpr bool is_reflection_v = is_reflection::value; - template typename Condition, typename Tuple, typename Owner> constexpr int element_index_helper() { diff --git a/iguana/util.hpp b/iguana/util.hpp index 47959618..3f6768a9 100644 --- a/iguana/util.hpp +++ b/iguana/util.hpp @@ -130,12 +130,6 @@ constexpr inline bool shared_ptr_v = template constexpr inline bool smart_ptr_v = shared_ptr_v || unique_ptr_v; -template -struct is_variant : std::false_type {}; - -template -struct is_variant> : std::true_type {}; - template constexpr inline bool variant_v = is_variant>::value; diff --git a/iguana/version.hpp b/iguana/version.hpp index 202ea4a3..bdb1121b 100644 --- a/iguana/version.hpp +++ b/iguana/version.hpp @@ -5,4 +5,4 @@ // IGUANA_VERSION % 100 is the sub-minor version // IGUANA_VERSION / 100 % 1000 is the minor version // IGUANA_VERSION / 100000 is the major version -#define IGUANA_VERSION 100004 // 1.0.4 \ No newline at end of file +#define IGUANA_VERSION 100005 // 1.0.5 \ No newline at end of file diff --git a/ormpp/dbng.hpp b/ormpp/dbng.hpp index 6b711a91..94bdf785 100644 --- a/ormpp/dbng.hpp +++ b/ormpp/dbng.hpp @@ -64,12 +64,12 @@ class dbng { } template - int update_s(const T &t, Args &&...args) { + int update_some(const T &t, Args &&...args) { return db_.template update(t, std::forward(args)...); } template - int update_s(const std::vector &v, Args &&...args) { + int update_some(const std::vector &v, Args &&...args) { return db_.template update(v, std::forward(args)...); } diff --git a/tests/test_ormpp.cpp b/tests/test_ormpp.cpp index c679d115..54a1aab3 100644 --- a/tests/test_ormpp.cpp +++ b/tests/test_ormpp.cpp @@ -1781,16 +1781,17 @@ TEST_CASE("update section filed") { mysql.create_datatable(ormpp_auto_key{"id"}); mysql.insert({"person_a", 1}); mysql.insert({"person_b", 2}); - mysql.update_s<&person::name>(person{"purecpp_a", 0, 1}); - mysql.update_s<&person::name>(person{"purecpp_b"}, "id=2"); + mysql.update_some<&person::name>(person{"purecpp_a", 0, 1}); + mysql.update_some<&person::name>(person{"purecpp_b"}, "id=2"); auto vec1 = mysql.query_s("id=?", 1); auto vec2 = mysql.query_s("id=?", 2); CHECK(vec1.size() == 1); CHECK(vec2.size() == 1); CHECK(vec1.front().name == "purecpp_a"); CHECK(vec2.front().name == "purecpp_b"); - mysql.update_s<&person::name, &person::age>(person{"purecpp", 100, 1}); - mysql.update_s<&person::name, &person::age>(person{"purecpp", 200}, "id=2"); + mysql.update_some<&person::name, &person::age>(person{"purecpp", 100, 1}); + mysql.update_some<&person::name, &person::age>(person{"purecpp", 200}, + "id=2"); auto vec = mysql.query_s(); vec1 = mysql.query_s("id=?", 1); vec2 = mysql.query_s("id=?", 2); @@ -1800,7 +1801,7 @@ TEST_CASE("update section filed") { CHECK(vec2.front().age == 200); CHECK(vec1.front().name == "purecpp"); CHECK(vec2.front().name == "purecpp"); - mysql.update_s<&person::name, &person::age>( + mysql.update_some<&person::name, &person::age>( std::vector{{"purecpp_aa", 111, 1}, {"purecpp_bb", 222, 2}}); vec1 = mysql.query_s("id=?", 1); vec2 = mysql.query_s("id=?", 2); @@ -1819,17 +1820,18 @@ TEST_CASE("update section filed") { postgres.create_datatable(ormpp_auto_key{"id"}); postgres.insert({"person_a", 1}); postgres.insert({"person_b", 2}); - postgres.update_s<&person::name>(person{"purecpp_a", 0, 1}); - postgres.update_s<&person::name>(person{"purecpp_b"}, "id=2"); + postgres.update_some<&person::name>(person{"purecpp_a", 0, 1}); + postgres.update_some<&person::name>(person{"purecpp_b"}, "id=2"); auto vec1 = postgres.query_s("id=$1", 1); auto vec2 = postgres.query_s("id=$1", 2); CHECK(vec1.size() == 1); CHECK(vec2.size() == 1); CHECK(vec1.front().name == "purecpp_a"); CHECK(vec2.front().name == "purecpp_b"); - postgres.update_s<&person::name, &person::age>(person{"purecpp", 100, 1}); - postgres.update_s<&person::name, &person::age>(person{"purecpp", 200}, - "id=2"); + postgres.update_some<&person::name, &person::age>( + person{"purecpp", 100, 1}); + postgres.update_some<&person::name, &person::age>(person{"purecpp", 200}, + "id=2"); auto vec = postgres.query_s(); vec1 = postgres.query_s("id=$1", 1); vec2 = postgres.query_s("id=$1", 2); @@ -1839,7 +1841,7 @@ TEST_CASE("update section filed") { CHECK(vec2.front().age == 200); CHECK(vec1.front().name == "purecpp"); CHECK(vec2.front().name == "purecpp"); - postgres.update_s<&person::name, &person::age>( + postgres.update_some<&person::name, &person::age>( std::vector{{"purecpp_aa", 111, 1}, {"purecpp_bb", 222, 2}}); vec1 = postgres.query_s("id=$1", 1); vec2 = postgres.query_s("id=$1", 2); @@ -1858,17 +1860,17 @@ TEST_CASE("update section filed") { sqlite.create_datatable(ormpp_auto_key{"id"}); sqlite.insert({"person_a", 1}); sqlite.insert({"person_b", 2}); - sqlite.update_s<&person::name>(person{"purecpp_a", 0, 1}); - sqlite.update_s<&person::name>(person{"purecpp_b"}, "id=2"); + sqlite.update_some<&person::name>(person{"purecpp_a", 0, 1}); + sqlite.update_some<&person::name>(person{"purecpp_b"}, "id=2"); auto vec1 = sqlite.query_s("id=?", 1); auto vec2 = sqlite.query_s("id=?", 2); CHECK(vec1.size() == 1); CHECK(vec2.size() == 1); CHECK(vec1.front().name == "purecpp_a"); CHECK(vec2.front().name == "purecpp_b"); - sqlite.update_s<&person::name, &person::age>(person{"purecpp", 100, 1}); - sqlite.update_s<&person::name, &person::age>(person{"purecpp", 200}, - "id=2"); + sqlite.update_some<&person::name, &person::age>(person{"purecpp", 100, 1}); + sqlite.update_some<&person::name, &person::age>(person{"purecpp", 200}, + "id=2"); auto vec = sqlite.query_s(); vec1 = sqlite.query_s("id=?", 1); vec2 = sqlite.query_s("id=?", 2); @@ -1878,7 +1880,7 @@ TEST_CASE("update section filed") { CHECK(vec2.front().age == 200); CHECK(vec1.front().name == "purecpp"); CHECK(vec2.front().name == "purecpp"); - sqlite.update_s<&person::name, &person::age>( + sqlite.update_some<&person::name, &person::age>( std::vector{{"purecpp_aa", 111, 1}, {"purecpp_bb", 222, 2}}); vec1 = sqlite.query_s("id=?", 1); vec2 = sqlite.query_s("id=?", 2);