// __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ // | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // // SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-FileCopyrightText: 2018-2025 The simdjson authors // SPDX-License-Identifier: MIT #pragma once #include // size_t #include // uint8_t, uint64_t #include #include // Vector code for long runs of string bytes. NEON (AArch64) and SSE2 (x86-64) // belong to the baseline instruction sets and are used by default. The vector // UTF-8 check needs NEON, or SSSE3 if JSON_VIEW_USE_SSSE3 is defined: SSSE3 is // not part of x86-64, so it must not depend on the flags of a translation unit // (two translation units with different flags would have different // definitions of the same inline functions). JSON_VIEW_NO_SIMD selects the // portable code. #if !defined(JSON_VIEW_NO_SIMD) && defined(__aarch64__) && (defined(__GNUC__) || defined(__clang__)) && NLOHMANN_VIEW_LITTLE_ENDIAN #include #define NLOHMANN_VIEW_NEON 1 #else #define NLOHMANN_VIEW_NEON 0 #endif #if !defined(JSON_VIEW_NO_SIMD) && !NLOHMANN_VIEW_NEON && (defined(__SSE2__) || defined(_M_X64) || (defined(_M_IX86_FP) && _M_IX86_FP >= 2)) #include #define NLOHMANN_VIEW_SSE2 1 #else #define NLOHMANN_VIEW_SSE2 0 #endif #if NLOHMANN_VIEW_SSE2 && defined(JSON_VIEW_USE_SSSE3) #include #define NLOHMANN_VIEW_SSSE3 1 #else #define NLOHMANN_VIEW_SSSE3 0 #endif #define NLOHMANN_VIEW_VECTOR (NLOHMANN_VIEW_NEON || NLOHMANN_VIEW_SSE2) #define NLOHMANN_VIEW_VECTOR_UTF8 (NLOHMANN_VIEW_NEON || NLOHMANN_VIEW_SSSE3) NLOHMANN_JSON_NAMESPACE_BEGIN namespace detail { namespace view { #if NLOHMANN_VIEW_VECTOR /*! @brief the first byte of a string run that is a quote, a backslash, a control character, or not ASCII, 16 bytes per step Stops at such a byte, or where fewer than 16 bytes are left (the caller tells the two apart). A signed compare with 0x20 finds control characters and non-ASCII bytes at once. */ NLOHMANN_VIEW_ALWAYS_INLINE const unsigned char* vector_plain_run(const unsigned char* p, const unsigned char* e) noexcept { while (e - p >= 16) { #if NLOHMANN_VIEW_NEON const uint8x16_t in = vld1q_u8(p); const uint8x16_t special = vorrq_u8(vorrq_u8(vceqq_u8(in, vdupq_n_u8('"')), vceqq_u8(in, vdupq_n_u8('\\'))), vcltq_s8(vreinterpretq_s8_u8(in), vdupq_n_s8(0x20))); // one nibble per byte (the usual NEON replacement of x86's movemask, see // D. Kutenin, "Porting x86 vector bitmask optimizations to Arm NEON", 2022) const std::uint64_t bits = vget_lane_u64(vreinterpret_u64_u8(vshrn_n_u16(vreinterpretq_u16_u8(special), 4)), 0); if (bits != 0) { return p + (count_trailing_zeros(bits) >> 2u); } #else const __m128i in = _mm_loadu_si128(static_cast(static_cast(p))); const __m128i special = _mm_or_si128(_mm_or_si128(_mm_cmpeq_epi8(in, _mm_set1_epi8('"')), _mm_cmpeq_epi8(in, _mm_set1_epi8('\\'))), _mm_cmplt_epi8(in, _mm_set1_epi8(0x20))); const auto bits = static_cast(static_cast(_mm_movemask_epi8(special))); if (bits != 0) { return p + count_trailing_zeros(bits); } #endif p += 16; } return p; } #endif #if NLOHMANN_VIEW_VECTOR_UTF8 /// Tables of the UTF-8 check of J. Keiser and D. Lemire, "Validating UTF-8 In /// Less Than One Instruction Per Byte" (2021), as in simdjson ("lookup4"): each /// maps a nibble (high and low nibble of the previous byte, high nibble of the /// current byte) to the errors it allows; a byte pair is ill-formed if all /// three have an error bit in common. template struct utf8_lookup4 { static constexpr std::uint8_t too_short = 1u << 0u, too_long = 1u << 1u, overlong_3 = 1u << 2u, too_large = 1u << 3u; static constexpr std::uint8_t surrogate = 1u << 4u, overlong_2 = 1u << 5u, too_large_1000 = 1u << 6u, overlong_4 = 1u << 6u; static constexpr std::uint8_t two_conts = 1u << 7u, carry = too_short | too_long | two_conts; static const std::uint8_t byte_1_high[16]; static const std::uint8_t byte_1_low[16]; static const std::uint8_t byte_2_high[16]; }; template const std::uint8_t utf8_lookup4::byte_1_high[16] = { too_long, too_long, too_long, too_long, too_long, too_long, too_long, too_long, two_conts, two_conts, two_conts, two_conts, too_short | overlong_2, too_short, too_short | overlong_3 | surrogate, too_short | too_large | too_large_1000 | overlong_4 }; template const std::uint8_t utf8_lookup4::byte_1_low[16] = { carry | overlong_3 | overlong_2 | overlong_4, carry | overlong_2, carry, carry, carry | too_large, carry | too_large | too_large_1000, carry | too_large | too_large_1000, carry | too_large | too_large_1000, carry | too_large | too_large_1000, carry | too_large | too_large_1000, carry | too_large | too_large_1000, carry | too_large | too_large_1000, carry | too_large | too_large_1000, carry | too_large | too_large_1000 | surrogate, carry | too_large | too_large_1000, carry | too_large | too_large_1000 }; template const std::uint8_t utf8_lookup4::byte_2_high[16] = { too_short, too_short, too_short, too_short, too_short, too_short, too_short, too_short, static_cast(too_long | overlong_2 | two_conts | overlong_3 | too_large_1000 | overlong_4), static_cast(too_long | overlong_2 | two_conts | overlong_3 | too_large), static_cast(too_long | overlong_2 | two_conts | surrogate | too_large), static_cast(too_long | overlong_2 | two_conts | surrogate | too_large), too_short, too_short, too_short, too_short }; /// the end of scan_string_vector from block, where the vector loop stopped /// (ill-formed UTF-8, or fewer than 16 bytes left): one byte or sequence at a /// time, from the start of a sequence that crosses into the block inline const unsigned char* scan_string_finish(const unsigned char* p, const unsigned char* block, const unsigned char* e, const std::uint8_t* plain) noexcept { for (int i = 1; i <= 3 && block - i >= p; ++i) { const unsigned char c = block[-i]; if (c < 0x80) { break; } if (c >= 0xC0) { const int len = c >= 0xF0 ? 4 : (c >= 0xE0 ? 3 : 2); if (len > i) { block -= i; } break; } } for (p = block; p != e;) { if (*p < 0x80) { if (plain[*p] == 0) { return p; } ++p; continue; } const std::size_t n = validate_one_utf8(p, static_cast(e - p)); if (n == 0) { return p; } p += n; } return p; } /*! @brief the rest of a string from p (a character boundary), 16 bytes per step The first quote, backslash, or control character is found with vector compares, and the UTF-8 check covers the bytes up to it. Returns where the string scan stops, like scan_string_run: before ill-formed UTF-8 and for the last bytes of the input, the bytes are checked one sequence at a time. Out of line, so that no constants of the check occupy registers in the parse loop. */ NLOHMANN_VIEW_NOINLINE inline const unsigned char* scan_string_vector(const unsigned char* p, const unsigned char* e, const std::uint8_t* plain) noexcept { using lookup = utf8_lookup4<>; const unsigned char* block = p; #if NLOHMANN_VIEW_NEON const uint8x16_t t1h = vld1q_u8(lookup::byte_1_high); const uint8x16_t t1l = vld1q_u8(lookup::byte_1_low); const uint8x16_t t2h = vld1q_u8(lookup::byte_2_high); uint8x16_t prev = vdupq_n_u8(0); while (e - block >= 16) { const uint8x16_t in = vld1q_u8(block); const uint8x16_t special = vorrq_u8(vorrq_u8(vceqq_u8(in, vdupq_n_u8('"')), vceqq_u8(in, vdupq_n_u8('\\'))), vcltq_u8(in, vdupq_n_u8(0x20))); const uint8x16_t prev1 = vextq_u8(prev, in, 15); const uint8x16_t sc = vandq_u8(vandq_u8(vqtbl1q_u8(t1h, vshrq_n_u8(prev1, 4)), vqtbl1q_u8(t1l, vandq_u8(prev1, vdupq_n_u8(0x0F)))), vqtbl1q_u8(t2h, vshrq_n_u8(in, 4))); const uint8x16_t must23 = vorrq_u8(vqsubq_u8(vextq_u8(prev, in, 14), vdupq_n_u8(0xE0 - 0x80)), vqsubq_u8(vextq_u8(prev, in, 13), vdupq_n_u8(0xF0 - 0x80))); const uint8x16_t err = veorq_u8(vandq_u8(must23, vdupq_n_u8(0x80)), sc); const std::uint64_t special_bits = vget_lane_u64(vreinterpret_u64_u8(vshrn_n_u16(vreinterpretq_u16_u8(special), 4)), 0); const std::uint64_t err_bits = vget_lane_u64(vreinterpret_u64_u8(vshrn_n_u16(vreinterpretq_u16_u8(vtstq_u8(err, err)), 4)), 0); if (special_bits != 0) { // errors up to the special byte count (an incomplete sequence // before a quote shows at the quote); the bytes after it do not const unsigned k = static_cast(count_trailing_zeros(special_bits)) >> 2u; const std::uint64_t upto = k == 15 ? ~std::uint64_t{0} : (std::uint64_t{1} << (4u * (k + 1u))) - 1u; if ((err_bits & upto) == 0) { return block + k; } break; } if (err_bits != 0) { break; } prev = in; block += 16; } #else // the same with SSSE3 (pshufb for the table lookups; nibbles from 16-bit // shifts, as there are no byte shifts) const __m128i t1h = _mm_loadu_si128(static_cast(static_cast(lookup::byte_1_high))); const __m128i t1l = _mm_loadu_si128(static_cast(static_cast(lookup::byte_1_low))); const __m128i t2h = _mm_loadu_si128(static_cast(static_cast(lookup::byte_2_high))); const __m128i nibble = _mm_set1_epi8(0x0F); const __m128i zero = _mm_setzero_si128(); __m128i prev = zero; while (e - block >= 16) { const __m128i in = _mm_loadu_si128(static_cast(static_cast(block))); const __m128i special = _mm_or_si128(_mm_or_si128(_mm_cmpeq_epi8(in, _mm_set1_epi8('"')), _mm_cmpeq_epi8(in, _mm_set1_epi8('\\'))), _mm_cmpeq_epi8(_mm_subs_epu8(in, _mm_set1_epi8(0x1F)), zero)); // in < 0x20 const __m128i prev1 = _mm_alignr_epi8(in, prev, 15); const __m128i sc = _mm_and_si128(_mm_and_si128(_mm_shuffle_epi8(t1h, _mm_and_si128(_mm_srli_epi16(prev1, 4), nibble)), _mm_shuffle_epi8(t1l, _mm_and_si128(prev1, nibble))), _mm_shuffle_epi8(t2h, _mm_and_si128(_mm_srli_epi16(in, 4), nibble))); const __m128i must23 = _mm_or_si128(_mm_subs_epu8(_mm_alignr_epi8(in, prev, 14), _mm_set1_epi8(0xE0 - 0x80)), _mm_subs_epu8(_mm_alignr_epi8(in, prev, 13), _mm_set1_epi8(0xF0 - 0x80))); const __m128i err = _mm_xor_si128(_mm_and_si128(must23, _mm_set1_epi8(static_cast(-128))), sc); const auto special_bits = static_cast(_mm_movemask_epi8(special)); const auto err_bits = ~static_cast(_mm_movemask_epi8(_mm_cmpeq_epi8(err, zero))) & 0xFFFFu; if (special_bits != 0) { const unsigned k = static_cast(count_trailing_zeros(static_cast(special_bits))); if ((err_bits & ((2u << k) - 1u)) == 0) { return block + k; } break; } if (err_bits != 0) { break; } prev = in; block += 16; } #endif return scan_string_finish(p, block, e, plain); } #endif } // namespace view } // namespace detail NLOHMANN_JSON_NAMESPACE_END