From ccae9e8ca41ca939949e70bf6fb97bcb3794a99f Mon Sep 17 00:00:00 2001 From: MistEO Date: Mon, 16 Oct 2023 18:20:49 +0800 Subject: [PATCH] feat: update meojson to v3.1.1 (#6956) --- 3rdparty/include/meojson/bitops.hpp | 88 +++++ 3rdparty/include/meojson/json.hpp | 355 +++++++++++++----- 3rdparty/include/meojson/packed_bytes.hpp | 126 +++++++ 3rdparty/include/meojson/packed_bytes_arm.hpp | 63 ++++ 3rdparty/include/meojson/packed_bytes_x86.hpp | 123 ++++++ 5 files changed, 658 insertions(+), 97 deletions(-) create mode 100644 3rdparty/include/meojson/bitops.hpp create mode 100644 3rdparty/include/meojson/packed_bytes.hpp create mode 100644 3rdparty/include/meojson/packed_bytes_arm.hpp create mode 100644 3rdparty/include/meojson/packed_bytes_x86.hpp diff --git a/3rdparty/include/meojson/bitops.hpp b/3rdparty/include/meojson/bitops.hpp new file mode 100644 index 0000000000..ec6137fd64 --- /dev/null +++ b/3rdparty/include/meojson/bitops.hpp @@ -0,0 +1,88 @@ +#pragma once + +#if __cplusplus >= 202002L || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) +#include +namespace json::__bitops { + using std::countl_one; + using std::countr_one; + using std::countl_zero; + using std::countr_zero; + inline constexpr bool is_little_endian() { + return std::endian::native == std::endian::little; + } +} +#else +#include +namespace json::__bitops { +#if defined(__GNUC__) || defined(__clang__) + inline constexpr int countl_zero(uint32_t x) { + if constexpr (sizeof(uint32_t) == sizeof(unsigned int)) + return x == 0 ? 32 : __builtin_clz(x); + if constexpr (sizeof(uint32_t) == sizeof(unsigned long)) + return x == 0 ? 32 : __builtin_clzl(x); + return x == 0 ? 32 : __builtin_clzll(x); + } + inline constexpr int countr_zero(uint32_t x) { + if constexpr (sizeof(uint32_t) == sizeof(unsigned int)) + return x == 0 ? 32 : __builtin_ctz(x); + if constexpr (sizeof(uint32_t) == sizeof(unsigned long)) + return x == 0 ? 32 : __builtin_ctzl(x); + return x == 0 ? 32 : __builtin_ctzll(x); + } + inline constexpr int countl_zero(uint64_t x) { + return x == 0 ? 64 : __builtin_clzll(x); + } + inline constexpr int countr_zero(uint64_t x) { + return x == 0 ? 64 : __builtin_ctzll(x); + } +#elif defined(_MSC_VER) +#ifdef __AVX2__ + // lzcnt intrinsics is not constexpr + inline int countl_zero(uint32_t x) { + return __lzcnt(x); + } + inline int countr_zero(uint32_t x) { + return _tzcnt_u32(x); + } + inline int countl_zero(uint64_t x) { + return (int)__lzcnt64(x); + } + inline int countr_zero(uint64_t x) { + return (int)_tzcnt_u64(x); + } +#else + inline constexpr int countl_zero(uint32_t x) { + unsigned long index = 0; + return _BitScanReverse(&index, x) ? 31 - index : 32; + } + inline constexpr int countr_zero(uint32_t x) { + unsigned long index = 0; + return _BitScanForward(&index, x) ? index : 32; + } + inline constexpr int countl_zero(uint64_t x) { + unsigned long index = 0; + return _BitScanReverse64(&index, x) ? 63 - index : 64; + } + inline constexpr int countr_zero(uint64_t x) { + unsigned long index = 0; + return _BitScanForward64(&index, x) ? index : 64; + } +#endif // __AVX2__ +#else // compiler +#error "bring your own bit counting implementation" +#endif + inline int countl_one(uint32_t x) { return countl_zero(~x); } + inline int countr_one(uint32_t x) { return countr_zero(~x); } + inline int countl_one(uint64_t x) { return countl_zero(~x); } + inline int countr_one(uint64_t x) { return countr_zero(~x); } + + // no constexpr endian awareness before C++20 + inline bool is_little_endian() { + union { + uint32_t u32; + uint8_t u8; + } u = { 0x01020304 }; + return u.u8 == 4; + } +} +#endif // C++20 diff --git a/3rdparty/include/meojson/json.hpp b/3rdparty/include/meojson/json.hpp index a31d9bf7ff..41546ae4eb 100644 --- a/3rdparty/include/meojson/json.hpp +++ b/3rdparty/include/meojson/json.hpp @@ -5,11 +5,15 @@ #include #include #include +#include +#include #include #include #include #include +#include "packed_bytes.hpp" + #define MEOJSON_INLINE inline namespace json @@ -131,7 +135,7 @@ public: bool as_boolean() const; int as_integer() const; - // unsigned as_unsigned() const; + unsigned as_unsigned() const; long as_long() const; unsigned long as_unsigned_long() const; long long as_long_long() const; @@ -182,16 +186,16 @@ public: basic_value& operator[](const string_t& key); basic_value& operator[](string_t&& key); - basic_value operator|(const basic_object& rhs) &; - basic_value operator|(basic_object&& rhs) &; + basic_value operator|(const basic_object& rhs) const&; + basic_value operator|(basic_object&& rhs) const&; basic_value operator|(const basic_object& rhs) &&; basic_value operator|(basic_object&& rhs) &&; basic_value& operator|=(const basic_object& rhs); basic_value& operator|=(basic_object&& rhs); - basic_value operator+(const basic_array& rhs) &; - basic_value operator+(basic_array&& rhs) &; + basic_value operator+(const basic_array& rhs) const&; + basic_value operator+(basic_array&& rhs) const&; basic_value operator+(const basic_array& rhs) &&; basic_value operator+(basic_array&& rhs) &&; @@ -200,6 +204,7 @@ public: explicit operator bool() const { return as_boolean(); } explicit operator int() const { return as_integer(); } + explicit operator unsigned() const { return as_unsigned(); } explicit operator long() const { return as_long(); } explicit operator unsigned long() const { return as_unsigned_long(); } explicit operator long long() const { return as_long_long(); } @@ -263,8 +268,12 @@ public: explicit basic_array(const basic_value& val); explicit basic_array(basic_value&& val); - template - basic_array(array_t arr); + template ::value_type>>> + basic_array(array_t arr) + { + _array_data.assign(std::make_move_iterator(arr.begin()), std::make_move_iterator(arr.end())); + } ~basic_array() noexcept = default; @@ -297,6 +306,7 @@ public: void clear() noexcept; bool erase(size_t pos); + bool erase(iterator iter); iterator begin() noexcept; iterator end() noexcept; @@ -315,8 +325,8 @@ public: const basic_value& operator[](size_t pos) const; basic_value& operator[](size_t pos); - basic_array operator+(const basic_array& rhs) &; - basic_array operator+(basic_array&& rhs) &; + basic_array operator+(const basic_array& rhs) const&; + basic_array operator+(basic_array&& rhs) const&; basic_array operator+(const basic_array& rhs) &&; basic_array operator+(basic_array&& rhs) &&; @@ -356,6 +366,8 @@ class basic_object public: using raw_object = std::map>; + using key_type = typename raw_object::key_type; + using mapped_type = typename raw_object::mapped_type; using value_type = typename raw_object::value_type; using iterator = typename raw_object::iterator; using const_iterator = typename raw_object::const_iterator; @@ -370,8 +382,12 @@ public: basic_object(std::initializer_list init_list); explicit basic_object(const basic_value& val); explicit basic_object(basic_value&& val); - template - basic_object(map_t map); + template ::value_type>>> + basic_object(map_t map) + { + _object_data.insert(std::make_move_iterator(map.begin()), std::make_move_iterator(map.end())); + } ~basic_object() = default; @@ -404,6 +420,7 @@ public: void clear() noexcept; bool erase(const string_t& key); + bool erase(iterator iter); iterator begin() noexcept; iterator end() noexcept; @@ -415,8 +432,8 @@ public: basic_value& operator[](const string_t& key); basic_value& operator[](string_t&& key); - basic_object operator|(const basic_object& rhs) &; - basic_object operator|(basic_object&& rhs) &; + basic_object operator|(const basic_object& rhs) const&; + basic_object operator|(basic_object&& rhs) const&; basic_object operator|(const basic_object& rhs) &&; basic_object operator|(basic_object&& rhs) &&; @@ -448,7 +465,8 @@ private: // * parser declare * // **************************** -template +template class parser { public: @@ -476,6 +494,7 @@ private: // parse and return a string_t std::optional parse_stdstring(); + void skip_string_literal(); bool skip_whitespace() noexcept; bool skip_digit(); @@ -520,13 +539,6 @@ auto parse(istream_t& istream, bool check_bom); template auto open(const path_t& path, bool check_bom = false); -template -ostream_t& operator<<(ostream_t& out, const basic_value& val); -template -ostream_t& operator<<(ostream_t& out, const basic_array& arr); -template -ostream_t& operator<<(ostream_t& out, const basic_object& obj); - namespace literals { value operator""_json(const char* str, size_t len); @@ -553,6 +565,9 @@ namespace literals template const basic_value invalid_value(); +template +basic_value serialize(any_t&& arg); + // ****************************** // * basic_value impl * // ****************************** @@ -893,6 +908,13 @@ MEOJSON_INLINE int basic_value::as_integer() const } } +template +MEOJSON_INLINE unsigned basic_value::as_unsigned() const +{ + // I don't know why there is no std::stou. + return static_cast(as_unsigned_long()); +} + template MEOJSON_INLINE long basic_value::as_long() const { @@ -1193,13 +1215,13 @@ MEOJSON_INLINE basic_value& basic_value::operator[](string_t } template -MEOJSON_INLINE basic_value basic_value::operator|(const basic_object& rhs) & +MEOJSON_INLINE basic_value basic_value::operator|(const basic_object& rhs) const& { return as_object() | rhs; } template -MEOJSON_INLINE basic_value basic_value::operator|(basic_object&& rhs) & +MEOJSON_INLINE basic_value basic_value::operator|(basic_object&& rhs) const& { return as_object() | std::move(rhs); } @@ -1231,13 +1253,13 @@ MEOJSON_INLINE basic_value& basic_value::operator|=(basic_ob } template -MEOJSON_INLINE basic_value basic_value::operator+(const basic_array& rhs) & +MEOJSON_INLINE basic_value basic_value::operator+(const basic_array& rhs) const& { return as_array() + rhs; } template -MEOJSON_INLINE basic_value basic_value::operator+(basic_array&& rhs) & +MEOJSON_INLINE basic_value basic_value::operator+(basic_array&& rhs) const& { return as_array() + std::move(rhs); } @@ -1338,15 +1360,6 @@ MEOJSON_INLINE basic_array::basic_array(basic_value&& val) ; } -template -template -MEOJSON_INLINE basic_array::basic_array(array_t arr) -{ - static_assert(std::is_constructible_v, - "array_t can not construct a json array"); - _array_data.assign(std::make_move_iterator(arr.begin()), std::make_move_iterator(arr.end())); -} - template MEOJSON_INLINE void basic_array::clear() noexcept { @@ -1356,7 +1369,13 @@ MEOJSON_INLINE void basic_array::clear() noexcept template MEOJSON_INLINE bool basic_array::erase(size_t pos) { - return _array_data.erase(pos) != _array_data.end(); + return erase(_array_data.begin() + pos); +} + +template +MEOJSON_INLINE bool basic_array::erase(iterator iter) +{ + return _array_data.erase(iter) != _array_data.end(); } template @@ -1570,7 +1589,7 @@ MEOJSON_INLINE const basic_value& basic_array::operator[](si } template -MEOJSON_INLINE basic_array basic_array::operator+(const basic_array& rhs) & +MEOJSON_INLINE basic_array basic_array::operator+(const basic_array& rhs) const& { basic_array temp = *this; temp._array_data.insert(_array_data.end(), rhs.begin(), rhs.end()); @@ -1578,7 +1597,7 @@ MEOJSON_INLINE basic_array basic_array::operator+(const basi } template -MEOJSON_INLINE basic_array basic_array::operator+(basic_array&& rhs) & +MEOJSON_INLINE basic_array basic_array::operator+(basic_array&& rhs) const& { basic_array temp = *this; temp._array_data.insert(_array_data.end(), std::make_move_iterator(rhs.begin()), @@ -1682,6 +1701,12 @@ MEOJSON_INLINE bool basic_object::erase(const string_t& key) return _object_data.erase(key) > 0 ? true : false; } +template +MEOJSON_INLINE bool basic_object::erase(iterator iter) +{ + return _object_data.erase(iter) != _object_data.end(); +} + template template MEOJSON_INLINE decltype(auto) basic_object::emplace(args_t&&... args) @@ -1855,7 +1880,7 @@ MEOJSON_INLINE basic_value& basic_object::operator[](string_ } template -MEOJSON_INLINE basic_object basic_object::operator|(const basic_object& rhs) & +MEOJSON_INLINE basic_object basic_object::operator|(const basic_object& rhs) const& { basic_object temp = *this; temp._object_data.insert(rhs.begin(), rhs.end()); @@ -1863,7 +1888,7 @@ MEOJSON_INLINE basic_object basic_object::operator|(const ba } template -MEOJSON_INLINE basic_object basic_object::operator|(basic_object&& rhs) & +MEOJSON_INLINE basic_object basic_object::operator|(basic_object&& rhs) const& { basic_object temp = *this; // temp._object_data.merge(std::move(rhs._object_data)); @@ -1906,27 +1931,19 @@ MEOJSON_INLINE bool basic_object::operator==(const basic_object -template -basic_object::basic_object(map_t map) -{ - static_assert(std::is_constructible_v, - "map_t can not construct a json object"); - _object_data.insert(std::make_move_iterator(map.begin()), std::make_move_iterator(map.end())); -} - // ************************* // * parser impl * // ************************* -template -MEOJSON_INLINE std::optional> parser::parse(const parsing_t& content) +template +MEOJSON_INLINE std::optional> parser::parse( + const parsing_t& content) { - return parser(content.cbegin(), content.cend()).parse(); + return parser(content.cbegin(), content.cend()).parse(); } -template -MEOJSON_INLINE std::optional> parser::parse() +template +MEOJSON_INLINE std::optional> parser::parse() { if (!skip_whitespace()) { return std::nullopt; @@ -1957,8 +1974,8 @@ MEOJSON_INLINE std::optional> parser: return result_value; } -template -MEOJSON_INLINE basic_value parser::parse_value() +template +MEOJSON_INLINE basic_value parser::parse_value() { switch (*_cur) { case 'n': @@ -1989,8 +2006,8 @@ MEOJSON_INLINE basic_value parser::parse_value() } } -template -MEOJSON_INLINE basic_value parser::parse_null() +template +MEOJSON_INLINE basic_value parser::parse_null() { for (const auto& ch : null_string()) { if (*_cur == ch) { @@ -2004,8 +2021,8 @@ MEOJSON_INLINE basic_value parser::parse_null() return basic_value(); } -template -MEOJSON_INLINE basic_value parser::parse_boolean() +template +MEOJSON_INLINE basic_value parser::parse_boolean() { switch (*_cur) { case 't': @@ -2033,8 +2050,8 @@ MEOJSON_INLINE basic_value parser::parse_boolean( } } -template -MEOJSON_INLINE basic_value parser::parse_number() +template +MEOJSON_INLINE basic_value parser::parse_number() { const auto first = _cur; if (*_cur == '-') { @@ -2072,8 +2089,8 @@ MEOJSON_INLINE basic_value parser::parse_number() return basic_value(basic_value::value_type::number, string_t(first, _cur)); } -template -MEOJSON_INLINE basic_value parser::parse_string() +template +MEOJSON_INLINE basic_value parser::parse_string() { auto string_opt = parse_stdstring(); if (!string_opt) { @@ -2082,8 +2099,8 @@ MEOJSON_INLINE basic_value parser::parse_string() return basic_value(basic_value::value_type::string, std::move(string_opt).value()); } -template -MEOJSON_INLINE basic_value parser::parse_array() +template +MEOJSON_INLINE basic_value parser::parse_array() { if (*_cur == '[') { ++_cur; @@ -2133,8 +2150,8 @@ MEOJSON_INLINE basic_value parser::parse_array() return basic_array(std::move(result)); } -template -MEOJSON_INLINE basic_value parser::parse_object() +template +MEOJSON_INLINE basic_value parser::parse_object() { if (*_cur == '{') { ++_cur; @@ -2197,8 +2214,8 @@ MEOJSON_INLINE basic_value parser::parse_object() return basic_object(std::move(result)); } -template -MEOJSON_INLINE std::optional parser::parse_stdstring() +template +MEOJSON_INLINE std::optional parser::parse_stdstring() { if (*_cur == '"') { ++_cur; @@ -2211,6 +2228,11 @@ MEOJSON_INLINE std::optional parser::parse_stdstr auto no_escape_beg = _cur; while (_cur != _end) { + if constexpr (sizeof(*_cur) == 1) { + if constexpr (accel_traits::available) { + skip_string_literal(); + } + } switch (*_cur) { case '\t': case '\r': @@ -2246,9 +2268,9 @@ MEOJSON_INLINE std::optional parser::parse_stdstr case 't': result.push_back('\t'); break; - // case 'u': - // result.push_back('\u'); - // break; + // case 'u': + // result.push_back('\u'); + // break; default: // Illegal backslash escape return std::nullopt; @@ -2268,8 +2290,30 @@ MEOJSON_INLINE std::optional parser::parse_stdstr return std::nullopt; } -template -MEOJSON_INLINE bool parser::skip_whitespace() noexcept +template +MEOJSON_INLINE void parser::skip_string_literal() +{ + if constexpr (sizeof(*_cur) != 1) { + return; + } + while (_end - _cur >= accel_traits::step) { + auto pack = accel_traits::load_unaligned(&(*_cur)); + auto result = accel_traits::less(pack, 32); + result = accel_traits::bitwise_or(result, accel_traits::equal(pack, static_cast('"'))); + result = accel_traits::bitwise_or(result, accel_traits::equal(pack, static_cast('\\'))); + if (accel_traits::is_all_zero(result)) { + _cur += accel_traits::step; + } + else { + auto index = accel_traits::first_nonzero_byte(result); + _cur += index; + break; + } + } +} + +template +MEOJSON_INLINE bool parser::skip_whitespace() noexcept { while (_cur != _end) { switch (*_cur) { @@ -2288,8 +2332,8 @@ MEOJSON_INLINE bool parser::skip_whitespace() noexcept return false; } -template -MEOJSON_INLINE bool parser::skip_digit() +template +MEOJSON_INLINE bool parser::skip_digit() { // At least one digit if (_cur != _end && std::isdigit(*_cur)) { @@ -2366,6 +2410,37 @@ MEOJSON_INLINE auto open(const path_t& filepath, bool check_bom) return opt; } +template >, + typename enable_t = typename std::enable_if_t || + std::is_base_of_v>> +ostream_t& operator<<(ostream_t& out, const basic_value& val) +{ + out << val.format(); + return out; +} +template >, + typename enable_t = + std::enable_if_t || std::is_base_of_v>> +ostream_t& operator<<(ostream_t& out, const basic_array& arr) +{ + out << arr.format(); + return out; +} +template >, + typename enable_t = + std::enable_if_t || std::is_base_of_v>> +ostream_t& operator<<(ostream_t& out, const basic_object& obj) +{ + out << obj.format(); + return out; +} + namespace literals { MEOJSON_INLINE value operator""_json(const char* str, size_t len) @@ -2445,33 +2520,119 @@ namespace literals } } // namespace literals -template -MEOJSON_INLINE ostream_t& operator<<(ostream_t& out, const basic_value& val) -{ - out << val.format(); - return out; -} - -template -MEOJSON_INLINE ostream_t& operator<<(ostream_t& out, const basic_array& arr) -{ - out << arr.format(); - return out; -} - -template -MEOJSON_INLINE ostream_t& operator<<(ostream_t& out, const basic_object& obj) -{ - out << obj.format(); - return out; -} - template MEOJSON_INLINE const basic_value invalid_value() { return basic_value(basic_value::value_type::invalid, typename basic_value::var_t()); } +namespace _serialization_helper +{ + template + class has_output_operator + { + template + static auto test(int) -> decltype(std::declval() << std::declval(), std::true_type()); + + template + static std::false_type test(...); + + public: + static constexpr bool value = decltype(test(0))::value; + }; + + template + using void_t = void; + + template + constexpr bool is_container = false; + template + constexpr bool is_container< + T, void_t().begin()), decltype(std::declval().end())>> = + true; + + // something like a map + template + constexpr bool is_associative_container = false; + template + constexpr bool is_associative_container> = is_container; + + // something like a vector + template + constexpr bool is_sequence_container = false; + template + constexpr bool is_sequence_container = is_container && !is_associative_container; + + template + MEOJSON_INLINE string_t to_stream_string(input_t&& arg) + { + if constexpr (has_output_operator::value) { + using char_t = typename string_t::value_type; + using stringstream_t = std::basic_stringstream, std::allocator>; + + stringstream_t ss; + ss << std::forward(arg); + return std::move(ss).str(); + } + else { + static_assert(!sizeof(input_t), "Unable to convert type to string, there is no operator <<."); + } + } + + template + MEOJSON_INLINE string_t to_string(input_t&& arg) + { + if constexpr (std::is_constructible_v) { + return arg; + } + else if constexpr (loose) { + return to_stream_string(std::forward(arg)); + } + else { + static_assert(!sizeof(input_t), "Unable to convert type to string."); + } + } +} + +template +MEOJSON_INLINE basic_value serialize(any_t&& arg) +{ + using namespace _serialization_helper; + + if constexpr (std::is_constructible_v, any_t>) { + return arg; + } + else if constexpr (std::is_constructible_v, any_t>) { + return basic_array(std::forward(arg)); + } + else if constexpr (std::is_constructible_v, any_t>) { + return basic_object(std::forward(arg)); + } + else if constexpr (is_sequence_container>) { + basic_value result; + for (auto&& val : arg) { + using value_t = decltype(val); + + result.emplace(serialize(std::forward(val))); + } + return result; + } + else if constexpr (is_associative_container>) { + basic_value result; + for (auto&& [key, val] : arg) { + using key_t = decltype(key); + using value_t = decltype(val); + + result.emplace(to_string(std::forward(key)), + serialize(std::forward(val))); + } + return result; + } + else { + return to_string(std::forward(arg)); + } +} + template MEOJSON_INLINE static constexpr string_t unescape_string(const string_t& str) { diff --git a/3rdparty/include/meojson/packed_bytes.hpp b/3rdparty/include/meojson/packed_bytes.hpp new file mode 100644 index 0000000000..c4717776ce --- /dev/null +++ b/3rdparty/include/meojson/packed_bytes.hpp @@ -0,0 +1,126 @@ +#pragma once +#include +#include +#include +#include "bitops.hpp" + +#if defined(__GNUC__) || defined(__clang__) +#define __packed_bytes_strong_inline __attribute__((always_inline)) +#elif defined(_MSC_VER) +#define __packed_bytes_strong_inline __forceinline +#else +#define __packed_bytes_strong_inline inline +#endif + +struct packed_bytes_trait_none { + static constexpr bool available = false; +}; + +template +struct packed_bytes { + using traits = packed_bytes_trait_none; +}; + +#if defined(__SSE2__) || defined(_M_X64) || (defined(_M_IX86_FP) && _M_IX86_FP) +#include "packed_bytes_x86.hpp" +#elif defined(__ARM_NEON) || defined(_M_ARM) || defined(_M_ARM64) +#include "packed_bytes_arm.hpp" +#endif + +struct packed_bytes_trait_uint64 { + static constexpr bool available = sizeof(void*) >= 8; + static constexpr auto step = 8; + using value_type = std::enable_if_t= 8, uint64_t>; + + __packed_bytes_strong_inline static value_type load_unaligned(const void *ptr) { + value_type result; + memcpy((void*)&result, ptr, 8); + return result; + } + + __packed_bytes_strong_inline static value_type less(value_type x, uint8_t n) { + return (((x) - UINT64_C(0x0101010101010101) * (n)) & ~(x) & UINT64_C(0x8080808080808080)); + } + + __packed_bytes_strong_inline static value_type is_zero_memberwise(value_type v) { + return (((v) - UINT64_C(0x0101010101010101)) & ~(v) & UINT64_C(0x8080808080808080)); + } + + __packed_bytes_strong_inline static bool is_all_zero(value_type v) + { + return v == UINT64_C(0); + } + + __packed_bytes_strong_inline static value_type equal(value_type x, uint8_t n) { + return is_zero_memberwise((x) ^ (UINT64_C(0x0101010101010101) * (n))); + } + + __packed_bytes_strong_inline static value_type bitwise_or(value_type a, value_type b) { + return a | b; + } + + __packed_bytes_strong_inline static size_t first_nonzero_byte(value_type x) { + if (json::__bitops::is_little_endian()) + return json::__bitops::countr_zero(x) / 8; + else + return json::__bitops::countl_zero(x) / 8; + } +}; + +struct packed_bytes_trait_uint32 { + static constexpr bool available = true; + static constexpr auto step = 4; + using value_type = uint32_t; + + __packed_bytes_strong_inline static value_type load_unaligned(const void *ptr) { + value_type result; + memcpy((void*)&result, ptr, 4); + return result; + } + + __packed_bytes_strong_inline static value_type less(value_type x, uint8_t n) { + return (((x) - ~UINT32_C(0) / 255 * (n)) & ~(x) & ~UINT32_C(0) / 255 * 128); + } + + __packed_bytes_strong_inline static value_type is_zero_memberwise(value_type v) { + return (((v) - UINT32_C(0x01010101)) & ~(v) & UINT32_C(0x80808080));; + } + + __packed_bytes_strong_inline static bool is_all_zero(value_type v) { + return v == UINT32_C(0); + } + + __packed_bytes_strong_inline static value_type equal(value_type x, uint8_t n) { + return is_zero_memberwise((x) ^ (~UINT32_C(0) / 255 * (n))); + } + + __packed_bytes_strong_inline static value_type bitwise_or(value_type a, value_type b) { + return a | b; + } + + __packed_bytes_strong_inline static size_t first_nonzero_byte(value_type x) { + if (json::__bitops::is_little_endian()) + return json::__bitops::countr_zero(x) / 8; + else + return json::__bitops::countl_zero(x) / 8; + } +}; +template <> +struct packed_bytes<8> { + using traits = std::enable_if_t; +}; + +template <> +struct packed_bytes<4> { + using traits = packed_bytes_trait_uint32; +}; + +template +using packed_bytes_trait = typename packed_bytes::traits; + +using packed_bytes_trait_max = std::conditional_t::available, packed_bytes_trait<32>, + std::conditional_t::available, packed_bytes_trait<16>, + std::conditional_t::available, packed_bytes_trait<8>, + packed_bytes_trait<4> + >>>; + diff --git a/3rdparty/include/meojson/packed_bytes_arm.hpp b/3rdparty/include/meojson/packed_bytes_arm.hpp new file mode 100644 index 0000000000..86fc07fb8c --- /dev/null +++ b/3rdparty/include/meojson/packed_bytes_arm.hpp @@ -0,0 +1,63 @@ +#pragma once + +// current NEON implementation doesn't outperform 64-bit scalar implementation +#ifdef MEOJSON_ENABLE_NEON +#include "packed_bytes.hpp" +#include + +#if defined(__aarch64__) || defined(__arm64__) || defined(_M_ARM64) +#define __packed_bytes_trait_arm64 +#endif + +struct packed_bytes_trait_neon { + static constexpr bool available = true; + static constexpr auto step = 16; + using value_type = uint8x16_t; + + __packed_bytes_strong_inline static value_type load_unaligned(const void *ptr) { + return vld1q_u8((uint8_t*)ptr); + } + + __packed_bytes_strong_inline static value_type less(value_type x, uint8_t n) { + auto bcast = vdupq_n_u8(n); + auto is_less = vcltq_u8(x, bcast); + return is_less; + } + + __packed_bytes_strong_inline static value_type equal(value_type x, uint8_t n) { + return vceqq_u8(x, vdupq_n_u8(n)); + } + + __packed_bytes_strong_inline static value_type equal(value_type x, value_type y) { + return vceqq_u8(x, y); + } + + __packed_bytes_strong_inline static value_type bitwise_or(value_type a, value_type b) { + return vorrq_u8(a, b); + } + + __packed_bytes_strong_inline static bool is_all_zero(value_type x) { +#ifdef __packed_bytes_trait_arm64 + return vmaxvq_u8(x) == 0; +#else + auto fold64 = vorr_u64(vget_high_u64(vreinterpretq_u8_u64(x), 0), vget_low_u64(vreinterpretq_u8_u64(x), 1)); + auto fold32 = vget_lane_u32(vreinterpret_u64_u32(fold64), 0) | vget_lane_u32(vreinterpret_u64_u32(fold64), 1); + return fold32 == 0; +#endif + } + + __packed_bytes_strong_inline static size_t first_nonzero_byte(value_type x) { + // https://community.arm.com/arm-community-blogs/b/infrastructure-solutions-blog/posts/porting-x86-vector-bitmask-optimizations-to-arm-neon + auto cmp = equal(x, 0); + auto res = vshrn_n_u16(cmp, 4); + auto mask64 = vget_lane_u64(vreinterpret_u64_u8(res), 0); + return json::__bitops::countr_one(mask64) >> 2; + } +}; + +template <> +struct packed_bytes<16> { + using traits = packed_bytes_trait_neon; +}; + +#endif diff --git a/3rdparty/include/meojson/packed_bytes_x86.hpp b/3rdparty/include/meojson/packed_bytes_x86.hpp new file mode 100644 index 0000000000..afa6951d86 --- /dev/null +++ b/3rdparty/include/meojson/packed_bytes_x86.hpp @@ -0,0 +1,123 @@ +#pragma once +#include "packed_bytes.hpp" + +#include +#if defined(__SSE4_1__) || defined(__AVX2__) || defined(_MSC_VER) +// MSVC enables all SSE4.1 intrinsics by default +#include +#endif + +struct packed_bytes_trait_sse { + static constexpr bool available = true; + static constexpr auto step = 16; + using value_type = __m128i; + + __packed_bytes_strong_inline static value_type load_unaligned(const void *ptr) { + return _mm_loadu_si128(reinterpret_cast(ptr)); + } + + __packed_bytes_strong_inline static value_type less(value_type x, uint8_t n) { + auto bcast = _mm_set1_epi8(static_cast(n)); + auto all1 = _mm_set1_epi8(-1); + auto max_with_n = _mm_max_epu8(x, bcast); + auto is_greater_or_equal = _mm_cmpeq_epi8(max_with_n, x); + auto is_less = _mm_andnot_si128(is_greater_or_equal, all1); + return is_less; + } + + __packed_bytes_strong_inline static value_type equal(value_type x, uint8_t n) { + return _mm_cmpeq_epi8(x, _mm_set1_epi8(static_cast(n))); + } + + __packed_bytes_strong_inline static value_type equal(value_type x, value_type y) { + return _mm_cmpeq_epi8(x, y); + } + + __packed_bytes_strong_inline static value_type bitwise_or(value_type a, value_type b) { + return _mm_or_si128(a, b); + } + + __packed_bytes_strong_inline static bool is_all_zero(value_type x) { +#if defined(__SSE4_1__) || defined(__AVX2__) || defined(_MSC_VER) + // SSE4.1 path + return !!_mm_testz_si128(x, x); +#else + // SSE2 path + auto cmp = _mm_cmpeq_epi8(x, _mm_set1_epi8(0)); + auto mask = (uint16_t)_mm_movemask_epi8(cmp); + return mask == UINT16_C(0xFFFF); +#endif + } + + __packed_bytes_strong_inline static size_t first_nonzero_byte(value_type x) { + auto cmp = _mm_cmpeq_epi8(x, _mm_set1_epi8(0)); + auto mask = (uint16_t)_mm_movemask_epi8(cmp); + return json::__bitops::countr_one((uint32_t)mask); + } +}; + +template <> +struct packed_bytes<16> { + using traits = packed_bytes_trait_sse; +}; + + +#ifdef __AVX2__ +#include + +struct packed_bytes_trait_avx2 +{ + static constexpr bool available = true; + static constexpr auto step = 32; + using value_type = __m256i; + + __packed_bytes_strong_inline static value_type load_unaligned(const void* ptr) + { + return _mm256_loadu_si256(reinterpret_cast(ptr)); + } + + __packed_bytes_strong_inline static value_type less(value_type x, uint8_t n) + { + auto bcast = _mm256_set1_epi8(static_cast(n)); + auto all1 = _mm256_set1_epi8(-1); + auto max_with_n = _mm256_max_epu8(x, bcast); + auto is_greater_or_equal = _mm256_cmpeq_epi8(max_with_n, x); + auto is_less = _mm256_andnot_si256(is_greater_or_equal, all1); + return is_less; + } + + __packed_bytes_strong_inline static value_type equal(value_type x, uint8_t n) + { + return _mm256_cmpeq_epi8(x, _mm256_set1_epi8(static_cast(n))); + } + + __packed_bytes_strong_inline static value_type equal(value_type x, value_type y) + { + return _mm256_cmpeq_epi8(x, y); + } + + __packed_bytes_strong_inline static value_type bitwise_or(value_type a, value_type b) + { + return _mm256_or_si256(a, b); + } + + __packed_bytes_strong_inline static bool is_all_zero(value_type x) + { + return (bool)_mm256_testz_si256(x, x); + } + + __packed_bytes_strong_inline static size_t first_nonzero_byte(value_type x) + { + auto cmp = _mm256_cmpeq_epi8(x, _mm256_set1_epi8(0)); + auto mask = (uint32_t)_mm256_movemask_epi8(cmp); + // AVX512 alternative: _mm_cmpeq_epi8_mask + return json::__bitops::countr_one(mask); + } +}; + +template <> +struct packed_bytes<32> { + using traits = packed_bytes_trait_avx2; +}; + +#endif