Make nesting_depth_guard usable regardless of JSON_NO_THREAD_LOCAL

nesting_depth_limit() and nesting_depth() stay behind #ifndef
JSON_NO_THREAD_LOCAL, since a descent cannot be bounded without a
per-thread count. But the guard itself now always exists, becoming a
no-op that is never okay() under that macro - the same way the bound
is already reached on every call without one. copy_structured() no
longer needs to know which case it is in.

This is what lets #5390 reuse the guard for comparison, which cannot
test JSON_NO_THREAD_LOCAL where the macro-based operators use it: the
guard now carries that distinction itself instead of requiring every
caller to.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
Niels Lohmann committed 2026-09-02 19:08:36 +02:00
1 parent 4aebfc8d4d
commit fd927b0cae
2 files changed
+28 -12

No files matched your search

+14 -6
View File
@@ -824,9 +824,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
#ifndef JSON_NO_THREAD_LOCAL #ifndef JSON_NO_THREAD_LOCAL
/// the number of levels an operation descends into before it finishes the /// the number of levels an operation descends into before it finishes the
/// value below it without the call stack /// value below it without the call stack
///
/// only meaningful together with @ref nesting_depth_guard, which is why
/// both live inside this very guard's own #ifndef
static constexpr std::uint8_t nesting_depth_limit() static constexpr std::uint8_t nesting_depth_limit()
{ {
return 128; return 128;
@@ -849,6 +846,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
static thread_local std::uint8_t depth = 0; // NOLINT(misc-use-internal-linkage) static thread_local std::uint8_t depth = 0; // NOLINT(misc-use-internal-linkage)
return depth; return depth;
} }
#endif
/*! /*!
@brief counts one level of a bounded descent for as long as it runs, and @brief counts one level of a bounded descent for as long as it runs, and
@@ -857,19 +855,32 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
Looks the count up and tests it against the limit itself, rather than Looks the count up and tests it against the limit itself, rather than
leaving that to the caller: either way it is reached exactly once, so leaving that to the caller: either way it is reached exactly once, so
there is nothing to be gained by making the caller do it. there is nothing to be gained by making the caller do it.
Does nothing and is never @ref okay without thread-local storage, where no
descent can be bounded at all: a caller that only descends while this says
it may always ends up finishing without the call stack, exactly as if every
value were nested past the limit.
*/ */
class nesting_depth_guard class nesting_depth_guard
{ {
public: public:
nesting_depth_guard() noexcept nesting_depth_guard() noexcept
#ifdef JSON_NO_THREAD_LOCAL
: m_okay(false)
#else
: m_okay(nesting_depth() < nesting_depth_limit()) : m_okay(nesting_depth() < nesting_depth_limit())
#endif
{ {
#ifndef JSON_NO_THREAD_LOCAL
++nesting_depth(); ++nesting_depth();
#endif
} }
~nesting_depth_guard() ~nesting_depth_guard()
{ {
#ifndef JSON_NO_THREAD_LOCAL
--nesting_depth(); --nesting_depth();
#endif
} }
nesting_depth_guard(const nesting_depth_guard&) = delete; nesting_depth_guard(const nesting_depth_guard&) = delete;
@@ -885,7 +896,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
private: private:
bool m_okay; bool m_okay;
}; };
#endif
/// an entry of the iterative deep copy's worklist: a structured value and /// an entry of the iterative deep copy's worklist: a structured value and
/// the value that is to become its copy /// the value that is to become its copy
@@ -1140,7 +1150,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
*/ */
void copy_structured(const basic_json& src) void copy_structured(const basic_json& src)
{ {
#ifndef JSON_NO_THREAD_LOCAL
const nesting_depth_guard guard; const nesting_depth_guard guard;
if (JSON_HEDLEY_LIKELY(guard.okay())) if (JSON_HEDLEY_LIKELY(guard.okay()))
@@ -1148,7 +1157,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
copy_level(src); copy_level(src);
return; return;
} }
#endif
// Finish this value without descending any further. It is completed // Finish this value without descending any further. It is completed
// before this returns, so a copy made by a custom base class - or by // before this returns, so a copy made by a custom base class - or by
+14 -6
View File
@@ -22187,9 +22187,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
#ifndef JSON_NO_THREAD_LOCAL #ifndef JSON_NO_THREAD_LOCAL
/// the number of levels an operation descends into before it finishes the /// the number of levels an operation descends into before it finishes the
/// value below it without the call stack /// value below it without the call stack
///
/// only meaningful together with @ref nesting_depth_guard, which is why
/// both live inside this very guard's own #ifndef
static constexpr std::uint8_t nesting_depth_limit() static constexpr std::uint8_t nesting_depth_limit()
{ {
return 128; return 128;
@@ -22212,6 +22209,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
static thread_local std::uint8_t depth = 0; // NOLINT(misc-use-internal-linkage) static thread_local std::uint8_t depth = 0; // NOLINT(misc-use-internal-linkage)
return depth; return depth;
} }
#endif
/*! /*!
@brief counts one level of a bounded descent for as long as it runs, and @brief counts one level of a bounded descent for as long as it runs, and
@@ -22220,19 +22218,32 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
Looks the count up and tests it against the limit itself, rather than Looks the count up and tests it against the limit itself, rather than
leaving that to the caller: either way it is reached exactly once, so leaving that to the caller: either way it is reached exactly once, so
there is nothing to be gained by making the caller do it. there is nothing to be gained by making the caller do it.
Does nothing and is never @ref okay without thread-local storage, where no
descent can be bounded at all: a caller that only descends while this says
it may always ends up finishing without the call stack, exactly as if every
value were nested past the limit.
*/ */
class nesting_depth_guard class nesting_depth_guard
{ {
public: public:
nesting_depth_guard() noexcept nesting_depth_guard() noexcept
#ifdef JSON_NO_THREAD_LOCAL
: m_okay(false)
#else
: m_okay(nesting_depth() < nesting_depth_limit()) : m_okay(nesting_depth() < nesting_depth_limit())
#endif
{ {
#ifndef JSON_NO_THREAD_LOCAL
++nesting_depth(); ++nesting_depth();
#endif
} }
~nesting_depth_guard() ~nesting_depth_guard()
{ {
#ifndef JSON_NO_THREAD_LOCAL
--nesting_depth(); --nesting_depth();
#endif
} }
nesting_depth_guard(const nesting_depth_guard&) = delete; nesting_depth_guard(const nesting_depth_guard&) = delete;
@@ -22248,7 +22259,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
private: private:
bool m_okay; bool m_okay;
}; };
#endif
/// an entry of the iterative deep copy's worklist: a structured value and /// an entry of the iterative deep copy's worklist: a structured value and
/// the value that is to become its copy /// the value that is to become its copy
@@ -22503,7 +22513,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
*/ */
void copy_structured(const basic_json& src) void copy_structured(const basic_json& src)
{ {
#ifndef JSON_NO_THREAD_LOCAL
const nesting_depth_guard guard; const nesting_depth_guard guard;
if (JSON_HEDLEY_LIKELY(guard.okay())) if (JSON_HEDLEY_LIKELY(guard.okay()))
@@ -22511,7 +22520,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
copy_level(src); copy_level(src);
return; return;
} }
#endif
// Finish this value without descending any further. It is completed // Finish this value without descending any further. It is completed
// before this returns, so a copy made by a custom base class - or by // before this returns, so a copy made by a custom base class - or by