feat: update meojson to v3.1.1 (#6956)

This commit is contained in:
MistEO
2023-10-16 18:20:49 +08:00
committed by GitHub
parent 8964afb728
commit ccae9e8ca4
5 changed files with 658 additions and 97 deletions

88
3rdparty/include/meojson/bitops.hpp vendored Normal file
View File

@@ -0,0 +1,88 @@
#pragma once
#if __cplusplus >= 202002L || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L)
#include <bit>
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 <cstdint>
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

View File

@@ -5,11 +5,15 @@
#include <map>
#include <memory>
#include <optional>
#include <ostream>
#include <sstream>
#include <string>
#include <tuple>
#include <variant>
#include <vector>
#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<string_t>& operator[](const string_t& key);
basic_value<string_t>& operator[](string_t&& key);
basic_value<string_t> operator|(const basic_object<string_t>& rhs) &;
basic_value<string_t> operator|(basic_object<string_t>&& rhs) &;
basic_value<string_t> operator|(const basic_object<string_t>& rhs) const&;
basic_value<string_t> operator|(basic_object<string_t>&& rhs) const&;
basic_value<string_t> operator|(const basic_object<string_t>& rhs) &&;
basic_value<string_t> operator|(basic_object<string_t>&& rhs) &&;
basic_value<string_t>& operator|=(const basic_object<string_t>& rhs);
basic_value<string_t>& operator|=(basic_object<string_t>&& rhs);
basic_value<string_t> operator+(const basic_array<string_t>& rhs) &;
basic_value<string_t> operator+(basic_array<string_t>&& rhs) &;
basic_value<string_t> operator+(const basic_array<string_t>& rhs) const&;
basic_value<string_t> operator+(basic_array<string_t>&& rhs) const&;
basic_value<string_t> operator+(const basic_array<string_t>& rhs) &&;
basic_value<string_t> operator+(basic_array<string_t>&& 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<string_t>& val);
explicit basic_array(basic_value<string_t>&& val);
template <typename array_t>
basic_array(array_t arr);
template <typename array_t, typename _ = std::enable_if_t<
std::is_constructible_v<value_type, typename std::decay_t<array_t>::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<string_t>& operator[](size_t pos) const;
basic_value<string_t>& operator[](size_t pos);
basic_array<string_t> operator+(const basic_array<string_t>& rhs) &;
basic_array<string_t> operator+(basic_array<string_t>&& rhs) &;
basic_array<string_t> operator+(const basic_array<string_t>& rhs) const&;
basic_array<string_t> operator+(basic_array<string_t>&& rhs) const&;
basic_array<string_t> operator+(const basic_array<string_t>& rhs) &&;
basic_array<string_t> operator+(basic_array<string_t>&& rhs) &&;
@@ -356,6 +366,8 @@ class basic_object
public:
using raw_object = std::map<string_t, basic_value<string_t>>;
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<value_type> init_list);
explicit basic_object(const basic_value<string_t>& val);
explicit basic_object(basic_value<string_t>&& val);
template <typename map_t>
basic_object(map_t map);
template <typename map_t, typename _ = std::enable_if_t<
std::is_constructible_v<value_type, typename std::decay_t<map_t>::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<string_t>& operator[](const string_t& key);
basic_value<string_t>& operator[](string_t&& key);
basic_object<string_t> operator|(const basic_object<string_t>& rhs) &;
basic_object<string_t> operator|(basic_object<string_t>&& rhs) &;
basic_object<string_t> operator|(const basic_object<string_t>& rhs) const&;
basic_object<string_t> operator|(basic_object<string_t>&& rhs) const&;
basic_object<string_t> operator|(const basic_object<string_t>& rhs) &&;
basic_object<string_t> operator|(basic_object<string_t>&& rhs) &&;
@@ -448,7 +465,8 @@ private:
// * parser declare *
// ****************************
template <typename string_t = default_string_t, typename parsing_t = void>
template <typename string_t = default_string_t, typename parsing_t = void,
typename accel_traits = packed_bytes_trait_max>
class parser
{
public:
@@ -476,6 +494,7 @@ private:
// parse and return a string_t
std::optional<string_t> 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 <typename ifstream_t = std::ifstream, typename path_t = void>
auto open(const path_t& path, bool check_bom = false);
template <typename ostream_t, typename string_t>
ostream_t& operator<<(ostream_t& out, const basic_value<string_t>& val);
template <typename ostream_t, typename string_t>
ostream_t& operator<<(ostream_t& out, const basic_array<string_t>& arr);
template <typename ostream_t, typename string_t>
ostream_t& operator<<(ostream_t& out, const basic_object<string_t>& obj);
namespace literals
{
value operator""_json(const char* str, size_t len);
@@ -553,6 +565,9 @@ namespace literals
template <typename string_t = default_string_t>
const basic_value<string_t> invalid_value();
template <bool loose, typename any_t, typename string_t = default_string_t>
basic_value<string_t> serialize(any_t&& arg);
// ******************************
// * basic_value impl *
// ******************************
@@ -893,6 +908,13 @@ MEOJSON_INLINE int basic_value<string_t>::as_integer() const
}
}
template <typename string_t>
MEOJSON_INLINE unsigned basic_value<string_t>::as_unsigned() const
{
// I don't know why there is no std::stou.
return static_cast<unsigned>(as_unsigned_long());
}
template <typename string_t>
MEOJSON_INLINE long basic_value<string_t>::as_long() const
{
@@ -1193,13 +1215,13 @@ MEOJSON_INLINE basic_value<string_t>& basic_value<string_t>::operator[](string_t
}
template <typename string_t>
MEOJSON_INLINE basic_value<string_t> basic_value<string_t>::operator|(const basic_object<string_t>& rhs) &
MEOJSON_INLINE basic_value<string_t> basic_value<string_t>::operator|(const basic_object<string_t>& rhs) const&
{
return as_object() | rhs;
}
template <typename string_t>
MEOJSON_INLINE basic_value<string_t> basic_value<string_t>::operator|(basic_object<string_t>&& rhs) &
MEOJSON_INLINE basic_value<string_t> basic_value<string_t>::operator|(basic_object<string_t>&& rhs) const&
{
return as_object() | std::move(rhs);
}
@@ -1231,13 +1253,13 @@ MEOJSON_INLINE basic_value<string_t>& basic_value<string_t>::operator|=(basic_ob
}
template <typename string_t>
MEOJSON_INLINE basic_value<string_t> basic_value<string_t>::operator+(const basic_array<string_t>& rhs) &
MEOJSON_INLINE basic_value<string_t> basic_value<string_t>::operator+(const basic_array<string_t>& rhs) const&
{
return as_array() + rhs;
}
template <typename string_t>
MEOJSON_INLINE basic_value<string_t> basic_value<string_t>::operator+(basic_array<string_t>&& rhs) &
MEOJSON_INLINE basic_value<string_t> basic_value<string_t>::operator+(basic_array<string_t>&& rhs) const&
{
return as_array() + std::move(rhs);
}
@@ -1338,15 +1360,6 @@ MEOJSON_INLINE basic_array<string_t>::basic_array(basic_value<string_t>&& val)
;
}
template <typename string_t>
template <typename array_t>
MEOJSON_INLINE basic_array<string_t>::basic_array(array_t arr)
{
static_assert(std::is_constructible_v<value_type, typename array_t::value_type>,
"array_t can not construct a json array");
_array_data.assign(std::make_move_iterator(arr.begin()), std::make_move_iterator(arr.end()));
}
template <typename string_t>
MEOJSON_INLINE void basic_array<string_t>::clear() noexcept
{
@@ -1356,7 +1369,13 @@ MEOJSON_INLINE void basic_array<string_t>::clear() noexcept
template <typename string_t>
MEOJSON_INLINE bool basic_array<string_t>::erase(size_t pos)
{
return _array_data.erase(pos) != _array_data.end();
return erase(_array_data.begin() + pos);
}
template <typename string_t>
MEOJSON_INLINE bool basic_array<string_t>::erase(iterator iter)
{
return _array_data.erase(iter) != _array_data.end();
}
template <typename string_t>
@@ -1570,7 +1589,7 @@ MEOJSON_INLINE const basic_value<string_t>& basic_array<string_t>::operator[](si
}
template <typename string_t>
MEOJSON_INLINE basic_array<string_t> basic_array<string_t>::operator+(const basic_array<string_t>& rhs) &
MEOJSON_INLINE basic_array<string_t> basic_array<string_t>::operator+(const basic_array<string_t>& rhs) const&
{
basic_array<string_t> temp = *this;
temp._array_data.insert(_array_data.end(), rhs.begin(), rhs.end());
@@ -1578,7 +1597,7 @@ MEOJSON_INLINE basic_array<string_t> basic_array<string_t>::operator+(const basi
}
template <typename string_t>
MEOJSON_INLINE basic_array<string_t> basic_array<string_t>::operator+(basic_array<string_t>&& rhs) &
MEOJSON_INLINE basic_array<string_t> basic_array<string_t>::operator+(basic_array<string_t>&& rhs) const&
{
basic_array<string_t> temp = *this;
temp._array_data.insert(_array_data.end(), std::make_move_iterator(rhs.begin()),
@@ -1682,6 +1701,12 @@ MEOJSON_INLINE bool basic_object<string_t>::erase(const string_t& key)
return _object_data.erase(key) > 0 ? true : false;
}
template <typename string_t>
MEOJSON_INLINE bool basic_object<string_t>::erase(iterator iter)
{
return _object_data.erase(iter) != _object_data.end();
}
template <typename string_t>
template <typename... args_t>
MEOJSON_INLINE decltype(auto) basic_object<string_t>::emplace(args_t&&... args)
@@ -1855,7 +1880,7 @@ MEOJSON_INLINE basic_value<string_t>& basic_object<string_t>::operator[](string_
}
template <typename string_t>
MEOJSON_INLINE basic_object<string_t> basic_object<string_t>::operator|(const basic_object<string_t>& rhs) &
MEOJSON_INLINE basic_object<string_t> basic_object<string_t>::operator|(const basic_object<string_t>& rhs) const&
{
basic_object<string_t> temp = *this;
temp._object_data.insert(rhs.begin(), rhs.end());
@@ -1863,7 +1888,7 @@ MEOJSON_INLINE basic_object<string_t> basic_object<string_t>::operator|(const ba
}
template <typename string_t>
MEOJSON_INLINE basic_object<string_t> basic_object<string_t>::operator|(basic_object<string_t>&& rhs) &
MEOJSON_INLINE basic_object<string_t> basic_object<string_t>::operator|(basic_object<string_t>&& rhs) const&
{
basic_object<string_t> temp = *this;
// temp._object_data.merge(std::move(rhs._object_data));
@@ -1906,27 +1931,19 @@ MEOJSON_INLINE bool basic_object<string_t>::operator==(const basic_object<string
return _object_data == rhs._object_data;
}
template <typename string_t>
template <typename map_t>
basic_object<string_t>::basic_object(map_t map)
{
static_assert(std::is_constructible_v<value_type, typename map_t::value_type>,
"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 <typename string_t, typename parsing_t>
MEOJSON_INLINE std::optional<basic_value<string_t>> parser<string_t, parsing_t>::parse(const parsing_t& content)
template <typename string_t, typename parsing_t, typename accel_traits>
MEOJSON_INLINE std::optional<basic_value<string_t>> parser<string_t, parsing_t, accel_traits>::parse(
const parsing_t& content)
{
return parser<string_t, parsing_t>(content.cbegin(), content.cend()).parse();
return parser<string_t, parsing_t, accel_traits>(content.cbegin(), content.cend()).parse();
}
template <typename string_t, typename parsing_t>
MEOJSON_INLINE std::optional<basic_value<string_t>> parser<string_t, parsing_t>::parse()
template <typename string_t, typename parsing_t, typename accel_traits>
MEOJSON_INLINE std::optional<basic_value<string_t>> parser<string_t, parsing_t, accel_traits>::parse()
{
if (!skip_whitespace()) {
return std::nullopt;
@@ -1957,8 +1974,8 @@ MEOJSON_INLINE std::optional<basic_value<string_t>> parser<string_t, parsing_t>:
return result_value;
}
template <typename string_t, typename parsing_t>
MEOJSON_INLINE basic_value<string_t> parser<string_t, parsing_t>::parse_value()
template <typename string_t, typename parsing_t, typename accel_traits>
MEOJSON_INLINE basic_value<string_t> parser<string_t, parsing_t, accel_traits>::parse_value()
{
switch (*_cur) {
case 'n':
@@ -1989,8 +2006,8 @@ MEOJSON_INLINE basic_value<string_t> parser<string_t, parsing_t>::parse_value()
}
}
template <typename string_t, typename parsing_t>
MEOJSON_INLINE basic_value<string_t> parser<string_t, parsing_t>::parse_null()
template <typename string_t, typename parsing_t, typename accel_traits>
MEOJSON_INLINE basic_value<string_t> parser<string_t, parsing_t, accel_traits>::parse_null()
{
for (const auto& ch : null_string<string_t>()) {
if (*_cur == ch) {
@@ -2004,8 +2021,8 @@ MEOJSON_INLINE basic_value<string_t> parser<string_t, parsing_t>::parse_null()
return basic_value<string_t>();
}
template <typename string_t, typename parsing_t>
MEOJSON_INLINE basic_value<string_t> parser<string_t, parsing_t>::parse_boolean()
template <typename string_t, typename parsing_t, typename accel_traits>
MEOJSON_INLINE basic_value<string_t> parser<string_t, parsing_t, accel_traits>::parse_boolean()
{
switch (*_cur) {
case 't':
@@ -2033,8 +2050,8 @@ MEOJSON_INLINE basic_value<string_t> parser<string_t, parsing_t>::parse_boolean(
}
}
template <typename string_t, typename parsing_t>
MEOJSON_INLINE basic_value<string_t> parser<string_t, parsing_t>::parse_number()
template <typename string_t, typename parsing_t, typename accel_traits>
MEOJSON_INLINE basic_value<string_t> parser<string_t, parsing_t, accel_traits>::parse_number()
{
const auto first = _cur;
if (*_cur == '-') {
@@ -2072,8 +2089,8 @@ MEOJSON_INLINE basic_value<string_t> parser<string_t, parsing_t>::parse_number()
return basic_value<string_t>(basic_value<string_t>::value_type::number, string_t(first, _cur));
}
template <typename string_t, typename parsing_t>
MEOJSON_INLINE basic_value<string_t> parser<string_t, parsing_t>::parse_string()
template <typename string_t, typename parsing_t, typename accel_traits>
MEOJSON_INLINE basic_value<string_t> parser<string_t, parsing_t, accel_traits>::parse_string()
{
auto string_opt = parse_stdstring();
if (!string_opt) {
@@ -2082,8 +2099,8 @@ MEOJSON_INLINE basic_value<string_t> parser<string_t, parsing_t>::parse_string()
return basic_value<string_t>(basic_value<string_t>::value_type::string, std::move(string_opt).value());
}
template <typename string_t, typename parsing_t>
MEOJSON_INLINE basic_value<string_t> parser<string_t, parsing_t>::parse_array()
template <typename string_t, typename parsing_t, typename accel_traits>
MEOJSON_INLINE basic_value<string_t> parser<string_t, parsing_t, accel_traits>::parse_array()
{
if (*_cur == '[') {
++_cur;
@@ -2133,8 +2150,8 @@ MEOJSON_INLINE basic_value<string_t> parser<string_t, parsing_t>::parse_array()
return basic_array<string_t>(std::move(result));
}
template <typename string_t, typename parsing_t>
MEOJSON_INLINE basic_value<string_t> parser<string_t, parsing_t>::parse_object()
template <typename string_t, typename parsing_t, typename accel_traits>
MEOJSON_INLINE basic_value<string_t> parser<string_t, parsing_t, accel_traits>::parse_object()
{
if (*_cur == '{') {
++_cur;
@@ -2197,8 +2214,8 @@ MEOJSON_INLINE basic_value<string_t> parser<string_t, parsing_t>::parse_object()
return basic_object<string_t>(std::move(result));
}
template <typename string_t, typename parsing_t>
MEOJSON_INLINE std::optional<string_t> parser<string_t, parsing_t>::parse_stdstring()
template <typename string_t, typename parsing_t, typename accel_traits>
MEOJSON_INLINE std::optional<string_t> parser<string_t, parsing_t, accel_traits>::parse_stdstring()
{
if (*_cur == '"') {
++_cur;
@@ -2211,6 +2228,11 @@ MEOJSON_INLINE std::optional<string_t> parser<string_t, parsing_t>::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<string_t> parser<string_t, parsing_t>::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<string_t> parser<string_t, parsing_t>::parse_stdstr
return std::nullopt;
}
template <typename string_t, typename parsing_t>
MEOJSON_INLINE bool parser<string_t, parsing_t>::skip_whitespace() noexcept
template <typename string_t, typename parsing_t, typename accel_traits>
MEOJSON_INLINE void parser<string_t, parsing_t, accel_traits>::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<uint8_t>('"')));
result = accel_traits::bitwise_or(result, accel_traits::equal(pack, static_cast<uint8_t>('\\')));
if (accel_traits::is_all_zero(result)) {
_cur += accel_traits::step;
}
else {
auto index = accel_traits::first_nonzero_byte(result);
_cur += index;
break;
}
}
}
template <typename string_t, typename parsing_t, typename accel_traits>
MEOJSON_INLINE bool parser<string_t, parsing_t, accel_traits>::skip_whitespace() noexcept
{
while (_cur != _end) {
switch (*_cur) {
@@ -2288,8 +2332,8 @@ MEOJSON_INLINE bool parser<string_t, parsing_t>::skip_whitespace() noexcept
return false;
}
template <typename string_t, typename parsing_t>
MEOJSON_INLINE bool parser<string_t, parsing_t>::skip_digit()
template <typename string_t, typename parsing_t, typename accel_traits>
MEOJSON_INLINE bool parser<string_t, parsing_t, accel_traits>::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 ostream_t, typename string_t,
typename std_ostream_t =
std::basic_ostream<typename string_t::value_type, std::char_traits<typename string_t::value_type>>,
typename enable_t = typename std::enable_if_t<std::is_same_v<std_ostream_t, ostream_t> ||
std::is_base_of_v<std_ostream_t, ostream_t>>>
ostream_t& operator<<(ostream_t& out, const basic_value<string_t>& val)
{
out << val.format();
return out;
}
template <typename ostream_t, typename string_t,
typename std_ostream_t =
std::basic_ostream<typename string_t::value_type, std::char_traits<typename string_t::value_type>>,
typename enable_t =
std::enable_if_t<std::is_same_v<std_ostream_t, ostream_t> || std::is_base_of_v<std_ostream_t, ostream_t>>>
ostream_t& operator<<(ostream_t& out, const basic_array<string_t>& arr)
{
out << arr.format();
return out;
}
template <typename ostream_t, typename string_t,
typename std_ostream_t =
std::basic_ostream<typename string_t::value_type, std::char_traits<typename string_t::value_type>>,
typename enable_t =
std::enable_if_t<std::is_same_v<std_ostream_t, ostream_t> || std::is_base_of_v<std_ostream_t, ostream_t>>>
ostream_t& operator<<(ostream_t& out, const basic_object<string_t>& 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 <typename ostream_t, typename string_t>
MEOJSON_INLINE ostream_t& operator<<(ostream_t& out, const basic_value<string_t>& val)
{
out << val.format();
return out;
}
template <typename ostream_t, typename string_t>
MEOJSON_INLINE ostream_t& operator<<(ostream_t& out, const basic_array<string_t>& arr)
{
out << arr.format();
return out;
}
template <typename ostream_t, typename string_t>
MEOJSON_INLINE ostream_t& operator<<(ostream_t& out, const basic_object<string_t>& obj)
{
out << obj.format();
return out;
}
template <typename string_t>
MEOJSON_INLINE const basic_value<string_t> invalid_value()
{
return basic_value<string_t>(basic_value<string_t>::value_type::invalid, typename basic_value<string_t>::var_t());
}
namespace _serialization_helper
{
template <typename T>
class has_output_operator
{
template <typename U>
static auto test(int) -> decltype(std::declval<std::ostream&>() << std::declval<U>(), std::true_type());
template <typename U>
static std::false_type test(...);
public:
static constexpr bool value = decltype(test<T>(0))::value;
};
template <typename...>
using void_t = void;
template <typename T, typename = void>
constexpr bool is_container = false;
template <typename T>
constexpr bool is_container<
T, void_t<typename T::value_type, decltype(std::declval<T>().begin()), decltype(std::declval<T>().end())>> =
true;
// something like a map
template <typename T, typename = void>
constexpr bool is_associative_container = false;
template <typename T>
constexpr bool is_associative_container<T, void_t<typename T::key_type, typename T::mapped_type>> = is_container<T>;
// something like a vector
template <typename T, typename = void>
constexpr bool is_sequence_container = false;
template <typename T>
constexpr bool is_sequence_container<T> = is_container<T> && !is_associative_container<T>;
template <typename input_t, typename string_t>
MEOJSON_INLINE string_t to_stream_string(input_t&& arg)
{
if constexpr (has_output_operator<input_t>::value) {
using char_t = typename string_t::value_type;
using stringstream_t = std::basic_stringstream<char_t, std::char_traits<char_t>, std::allocator<char_t>>;
stringstream_t ss;
ss << std::forward<input_t>(arg);
return std::move(ss).str();
}
else {
static_assert(!sizeof(input_t), "Unable to convert type to string, there is no operator <<.");
}
}
template <bool loose, typename input_t, typename string_t>
MEOJSON_INLINE string_t to_string(input_t&& arg)
{
if constexpr (std::is_constructible_v<string_t, input_t>) {
return arg;
}
else if constexpr (loose) {
return to_stream_string<input_t, string_t>(std::forward<input_t>(arg));
}
else {
static_assert(!sizeof(input_t), "Unable to convert type to string.");
}
}
}
template <bool loose, typename any_t, typename string_t>
MEOJSON_INLINE basic_value<string_t> serialize(any_t&& arg)
{
using namespace _serialization_helper;
if constexpr (std::is_constructible_v<basic_value<string_t>, any_t>) {
return arg;
}
else if constexpr (std::is_constructible_v<basic_array<string_t>, any_t>) {
return basic_array<string_t>(std::forward<any_t>(arg));
}
else if constexpr (std::is_constructible_v<basic_object<string_t>, any_t>) {
return basic_object<string_t>(std::forward<any_t>(arg));
}
else if constexpr (is_sequence_container<std::decay_t<any_t>>) {
basic_value<string_t> result;
for (auto&& val : arg) {
using value_t = decltype(val);
result.emplace(serialize<loose, value_t, string_t>(std::forward<value_t>(val)));
}
return result;
}
else if constexpr (is_associative_container<std::decay_t<any_t>>) {
basic_value<string_t> result;
for (auto&& [key, val] : arg) {
using key_t = decltype(key);
using value_t = decltype(val);
result.emplace(to_string<loose, key_t, string_t>(std::forward<key_t>(key)),
serialize<loose, value_t, string_t>(std::forward<value_t>(val)));
}
return result;
}
else {
return to_string<loose, any_t, string_t>(std::forward<any_t>(arg));
}
}
template <typename string_t>
MEOJSON_INLINE static constexpr string_t unescape_string(const string_t& str)
{

View File

@@ -0,0 +1,126 @@
#pragma once
#include <cstdint>
#include <cstring>
#include <type_traits>
#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 <size_t N>
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<sizeof(void*) >= 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<packed_bytes_trait_uint64::available, packed_bytes_trait_uint64>;
};
template <>
struct packed_bytes<4> {
using traits = packed_bytes_trait_uint32;
};
template <size_t N>
using packed_bytes_trait = typename packed_bytes<N>::traits;
using packed_bytes_trait_max = std::conditional_t<packed_bytes_trait<32>::available, packed_bytes_trait<32>,
std::conditional_t<packed_bytes_trait<16>::available, packed_bytes_trait<16>,
std::conditional_t<packed_bytes_trait<8>::available, packed_bytes_trait<8>,
packed_bytes_trait<4>
>>>;

View File

@@ -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 <arm_neon.h>
#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

View File

@@ -0,0 +1,123 @@
#pragma once
#include "packed_bytes.hpp"
#include <emmintrin.h>
#if defined(__SSE4_1__) || defined(__AVX2__) || defined(_MSC_VER)
// MSVC enables all SSE4.1 intrinsics by default
#include <smmintrin.h>
#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<const __m128i*>(ptr));
}
__packed_bytes_strong_inline static value_type less(value_type x, uint8_t n) {
auto bcast = _mm_set1_epi8(static_cast<char>(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<char>(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 <immintrin.h>
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<const __m256i*>(ptr));
}
__packed_bytes_strong_inline static value_type less(value_type x, uint8_t n)
{
auto bcast = _mm256_set1_epi8(static_cast<char>(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<char>(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