Merge branch 'develop' into claude/fix-issue-3989-db7e45

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
Niels Lohmann committed 2026-10-23 06:35:52 +02:00
commit 5c2aba70f9
60 files changed
+7977 -595357

No files matched your search

+1449 -375
View File
File diff suppressed because it is too large. Load diff
+10 -3
View File
@@ -139,6 +139,12 @@
#define NLOHMANN_JSON_ABI_TAG_DISABLE_UNLOCKED_FILE_IO
#endif
#ifdef JSON_NO_SIMD
#define NLOHMANN_JSON_ABI_TAG_NO_SIMD _ns
#else
#define NLOHMANN_JSON_ABI_TAG_NO_SIMD
#endif
#ifdef JSON_VIEW_NO_SIMD
#define NLOHMANN_JSON_ABI_TAG_VIEW_NO_SIMD _vns
#else
@@ -156,9 +162,9 @@
#endif
// Construct the namespace ABI tags component
#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c, d, e, f, g, h, i, j, k, l) json_abi ## a ## b ## c ## d ## e ## f ## g ## h ## i ## j ## k ## l
#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b, c, d, e, f, g, h, i, j, k, l) \
NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c, d, e, f, g, h, i, j, k, l)
#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c, d, e, f, g, h, i, j, k, l, m) json_abi ## a ## b ## c ## d ## e ## f ## g ## h ## i ## j ## k ## l ## m
#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b, c, d, e, f, g, h, i, j, k, l, m) \
NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c, d, e, f, g, h, i, j, k, l, m)
#define NLOHMANN_JSON_ABI_TAGS \
NLOHMANN_JSON_ABI_TAGS_CONCAT( \
@@ -172,6 +178,7 @@
NLOHMANN_JSON_ABI_TAG_OBJECTS_FOR_ENUM_KEYED_MAPS, \
NLOHMANN_JSON_ABI_TAG_DISABLE_STREAM_BULK_SCAN, \
NLOHMANN_JSON_ABI_TAG_DISABLE_UNLOCKED_FILE_IO, \
NLOHMANN_JSON_ABI_TAG_NO_SIMD, \
NLOHMANN_JSON_ABI_TAG_VIEW_NO_SIMD, \
NLOHMANN_JSON_ABI_TAG_VIEW_USE_SSSE3)
+102 -24
View File
@@ -690,7 +690,12 @@ NLOHMANN_JSON_NAMESPACE_END
#endif
#if NLOHMANN_VIEW_SSE2 && !NLOHMANN_VIEW_SSSE3 && !defined(NLOHMANN_VIEW_NO_TARGET_ATTRIBUTE) && ((defined(__clang__) && __clang_major__ >= 4) || (defined(__GNUC__) && !defined(__clang__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9))))
// (GCC before 4.9 has no SSSE3 intrinsics without -mssse3)
#include <cpuid.h>
// <cpuid.h> of GCC before 11 has no include guard, and json.hpp and
// json_view.hpp both include it
#ifndef NLOHMANN_JSON_CPUID_INCLUDED
#define NLOHMANN_JSON_CPUID_INCLUDED
#include <cpuid.h>
#endif
#include <tmmintrin.h>
#define NLOHMANN_VIEW_SSSE3_DISPATCH 1 // NOLINT(cppcoreguidelines-macro-to-enum,modernize-macro-to-enum)
#define NLOHMANN_VIEW_SSSE3_TARGET __attribute__((target("ssse3")))
@@ -6176,17 +6181,13 @@ class output_buffer
}
private:
/// the size of a growth step (a function: std::min() takes a reference,
/// which a static constexpr member does not have before C++17)
static constexpr std::size_t step() noexcept
{
return 0x10000;
}
/// the size of a growth step
static constexpr std::size_t step = 0x10000;
static StringType& sized(StringType& out, std::size_t estimate)
{
out.reserve(estimate);
out.resize((std::min)((std::max)(estimate, static_cast<std::size_t>(64)), step()));
out.resize(detail::clamp(estimate, std::size_t{64}, step));
return out;
}
@@ -6194,8 +6195,9 @@ class output_buffer
{
const auto used = static_cast<std::size_t>(m_pos - m_out.data());
// (a step does not go beyond the reserved estimate, so that a good
// estimate is never copied to a larger allocation)
const std::size_t size = (std::max)((std::min)(m_out.size() + step(), m_out.capacity()), used + n + 256);
// estimate is never copied to a larger allocation; not a clamp: the
// lower bound used + n + 256 can exceed capacity(), and then it must win)
const std::size_t size = (std::max)((std::min)(m_out.size() + step, m_out.capacity()), used + n + 256);
if (size > m_out.capacity())
{
m_out.reserve((std::max)(m_out.capacity() * 2, size));
@@ -6242,6 +6244,34 @@ inline std::size_t plain_output_run(const unsigned char* s, std::size_t n) noexc
return n;
}
/// Whether dump() with ensure_ascii writes the string at s unchanged, for a
/// string of the source without escape sequences (which has no quote,
/// backslash, or control character): no byte is DEL (0x7F) or above. Adding 1
/// to each byte sets its high bit exactly for 0x7F, and a carry into the next
/// byte only comes from 0xFF, whose own high bit is set anyway.
inline bool is_ascii_without_delete(const char* s, std::size_t n) noexcept
{
constexpr std::uint64_t ones = 0x0101010101010101ull;
constexpr std::uint64_t high = 0x8080808080808080ull;
std::size_t i = 0;
for (; i + 8 <= n; i += 8)
{
const std::uint64_t v = read_eight_bytes(s + i);
if (((v | (v + ones)) & high) != 0)
{
return false;
}
}
for (; i < n; ++i)
{
if (static_cast<unsigned char>(s[i]) >= 0x7F)
{
return false;
}
}
return true;
}
/// A stack that starts in a buffer of the caller (a local array) and moves to
/// the heap (a vector of the caller) only when that is full, so that dumps of
/// shallow documents need no allocation. The top is a pointer, as in
@@ -6357,15 +6387,19 @@ class view_serializer
void dump(const node* root)
{
if (!m_style.pretty && !m_style.ensure_ascii)
if (!m_style.pretty)
{
if (m_style.source_numbers)
if (m_style.ensure_ascii)
{
dump_compact<true>(root);
dump_compact_ascii(root);
}
else if (m_style.source_numbers)
{
dump_compact<true, false>(root);
}
else
{
dump_compact<false>(root);
dump_compact<false, false>(root);
}
return;
}
@@ -6471,6 +6505,41 @@ class view_serializer
m_out.put('"');
}
/// dump_compact() with ensure_ascii, out of line: the walk without it is
/// faster when it is not merged with this one into dump()
NLOHMANN_VIEW_NOINLINE void dump_compact_ascii(const node* root)
{
if (m_style.source_numbers)
{
dump_compact<true, true>(root);
}
else
{
dump_compact<false, true>(root);
}
}
/// whether dump_compact() copies a string or key from the source: one of
/// the source, and with ensure_ascii one without bytes >= 0x7F (an overload
/// each, so that the walk without ensure_ascii is compiled exactly as
/// before: with GCC, a condition that only folds away later changed its
/// block layout and made it 4-6% slower)
static bool copied(const node& n, const char* /*src*/, std::false_type /*ensure_ascii*/) noexcept
{
return (n.flags & node_flags::storage) == 0;
}
static bool copied(const node& n, const char* src, std::true_type /*ensure_ascii*/) noexcept
{
return (n.flags & node_flags::storage) == 0 && is_ascii_without_delete(src + n.off, n.len);
}
/// a string with ensure_ascii (dump_compact()), out of line
NLOHMANN_VIEW_NOINLINE void write_ascii_string(const node& n)
{
write_string(n);
}
/// the copies of dump_compact() that are not fixed-size moves (long
/// strings, or near the end of the source); out of line, so that the
/// compiler does not merge the fixed-size moves into this call
@@ -6488,16 +6557,18 @@ class view_serializer
}
/*!
@brief the compact output without ensure_ascii (the default dump())
@brief the compact output (the default dump(), or with ensure_ascii)
The same walk as dump(), with the write position in a local variable
(stores through char pointers would otherwise force a reload of the
buffer's members after each one), and with strings and number tokens of
the source copied by fixed-size moves of 32 bytes where the source has
that many bytes left, instead of a library call per token. The buffer
keeps 64 bytes of slack for the overshoot.
keeps 64 bytes of slack for the overshoot. With ensure_ascii (Ascii), a
string of the source is copied only if it has no byte >= 0x7F (see
is_ascii_without_delete()); any other string is written by write_string().
*/
template<bool SourceNumbers>
template<bool SourceNumbers, bool Ascii>
void dump_compact(const node* root)
{
struct frame
@@ -6549,7 +6620,14 @@ class view_serializer
const auto escaped = [&](const node & n)
{
m_out.set_cursor(w);
write_decoded(n);
if (enabled(Ascii))
{
write_ascii_string(n);
}
else
{
write_decoded(n);
}
w = m_out.cursor();
lim = m_out.limit();
};
@@ -6572,7 +6650,7 @@ class view_serializer
switch (static_cast<value_t>(n->kind))
{
case value_t::string:
if ((n->flags & node_flags::storage) == 0)
if (copied(*n, src, std::integral_constant<bool, Ascii>()))
{
room(n->len + 2);
*w++ = '"';
@@ -6703,7 +6781,7 @@ class view_serializer
if (cur.object)
{
const node& key = *at;
if ((key.flags & node_flags::storage) == 0)
if (copied(key, src, std::integral_constant<bool, Ascii>()))
{
room(key.len + 3);
*w++ = '"';
@@ -6865,8 +6943,7 @@ class view_serializer
static char* write_double_value_at(char* w, double x)
{
// (from the bits: without the checks of to_chars())
std::uint64_t bits = 0;
std::memcpy(&bits, &x, sizeof(bits));
auto bits = bit_cast<std::uint64_t>(x);
if (NLOHMANN_VIEW_UNLIKELY((bits & 0x7FF0000000000000u) == 0x7FF0000000000000u))
{
return write_text_at(w, "null", 4);
@@ -6914,9 +6991,10 @@ class view_serializer
{
const char* const s = m_doc.str(n);
m_out.put('"');
if ((n.flags & node_flags::storage) == 0 && !m_style.ensure_ascii)
if ((n.flags & node_flags::storage) == 0 && (!m_style.ensure_ascii || is_ascii_without_delete(s, n.len)))
{
// a string of the source without escape sequences has nothing to escape
// a string of the source without escape sequences has nothing to
// escape (with ensure_ascii, if it is printable ASCII)
m_out.put(s, n.len);
}
else if (m_style.ensure_ascii)