mirror of
https://github.com/nlohmann/json.git
synced 2026-10-28 00:35:43 +00:00
Detect a patch stored inside the patched value with JSON_DIAGNOSTICS
patch_inplace applied a copy only when the patch was the value itself. With JSON_DIAGNOSTICS the parent pointers make it cheap to also detect a patch nested inside the value, so that case is handled the same way. Without diagnostics it stays documented undefined behavior. Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
4 files changed
+41
-8
No files matched your search
@@ -73,11 +73,14 @@ The JSON patch may be the JSON value itself (`#!cpp j.patch_inplace(j)`): the op
|
|||||||
the patch made before the first operation is applied, so the result is the same as that of
|
the patch made before the first operation is applied, so the result is the same as that of
|
||||||
`#!cpp j = j.patch(j)`, and the exception safety is the same as above.
|
`#!cpp j = j.patch(j)`, and the exception safety is the same as above.
|
||||||
|
|
||||||
|
If [`JSON_DIAGNOSTICS`](../macros/json_diagnostics.md) is enabled, the same holds for a JSON patch stored inside the
|
||||||
|
JSON value (e.g., `#!cpp j.patch_inplace(j["patch"])`): it is detected via the parent pointers.
|
||||||
|
|
||||||
!!! danger "Undefined behavior"
|
!!! danger "Undefined behavior"
|
||||||
|
|
||||||
The JSON patch must not be a value stored inside the JSON value (e.g., `#!cpp j.patch_inplace(j["patch"])`), as the
|
Without [`JSON_DIAGNOSTICS`](../macros/json_diagnostics.md), the JSON patch must not be a value stored inside the
|
||||||
operations may change or destroy it while it is read. Pass a copy (`#!cpp j.patch_inplace(json(j["patch"]))`) or use
|
JSON value (e.g., `#!cpp j.patch_inplace(j["patch"])`), as the operations may change or destroy it while it is
|
||||||
[`patch`](patch.md) instead.
|
read. Pass a copy (`#!cpp j.patch_inplace(json(j["patch"]))`) or use [`patch`](patch.md) instead.
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
@@ -132,4 +135,5 @@ the patch made before the first operation is applied, so the result is the same
|
|||||||
- Throws [`parse_error.109`](../../home/exceptions.md#jsonexceptionparse_error109) instead of
|
- Throws [`parse_error.109`](../../home/exceptions.md#jsonexceptionparse_error109) instead of
|
||||||
[`out_of_range.404`](../../home/exceptions.md#jsonexceptionout_of_range404) for a one-character array index that is
|
[`out_of_range.404`](../../home/exceptions.md#jsonexceptionout_of_range404) for a one-character array index that is
|
||||||
not a digit (e.g., `/x`) in version 3.13.0, as it already did for longer ones.
|
not a digit (e.g., `/x`) in version 3.13.0, as it already did for longer ones.
|
||||||
- Applying a JSON value as patch to itself no longer results in undefined behavior in version 3.13.0.
|
- Applying a JSON value as patch to itself (or, with `JSON_DIAGNOSTICS`, to a value stored inside it) no longer results
|
||||||
|
in undefined behavior in version 3.13.0.
|
||||||
@@ -6951,8 +6951,16 @@ public:
|
|||||||
void patch_inplace(const basic_json& json_patch)
|
void patch_inplace(const basic_json& json_patch)
|
||||||
{
|
{
|
||||||
// the operations below change *this while iterating json_patch: apply
|
// the operations below change *this while iterating json_patch: apply
|
||||||
// a copy when a value is applied as patch to itself
|
// a copy when a value is applied as patch to itself or, with
|
||||||
if (JSON_HEDLEY_UNLIKELY(&json_patch == this))
|
// JSON_DIAGNOSTICS, to a value stored inside it
|
||||||
|
bool patch_aliases_this = (&json_patch == this);
|
||||||
|
#if JSON_DIAGNOSTICS
|
||||||
|
for (const basic_json* p = json_patch.m_parent; !patch_aliases_this && p != nullptr; p = p->m_parent)
|
||||||
|
{
|
||||||
|
patch_aliases_this = (p == this);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(patch_aliases_this))
|
||||||
{
|
{
|
||||||
// NOLINTNEXTLINE(performance-unnecessary-copy-initialization): *this is modified below
|
// NOLINTNEXTLINE(performance-unnecessary-copy-initialization): *this is modified below
|
||||||
const basic_json json_patch_copy = json_patch;
|
const basic_json json_patch_copy = json_patch;
|
||||||
|
|||||||
Generated
+10
-2
@@ -43253,8 +43253,16 @@ public:
|
|||||||
void patch_inplace(const basic_json& json_patch)
|
void patch_inplace(const basic_json& json_patch)
|
||||||
{
|
{
|
||||||
// the operations below change *this while iterating json_patch: apply
|
// the operations below change *this while iterating json_patch: apply
|
||||||
// a copy when a value is applied as patch to itself
|
// a copy when a value is applied as patch to itself or, with
|
||||||
if (JSON_HEDLEY_UNLIKELY(&json_patch == this))
|
// JSON_DIAGNOSTICS, to a value stored inside it
|
||||||
|
bool patch_aliases_this = (&json_patch == this);
|
||||||
|
#if JSON_DIAGNOSTICS
|
||||||
|
for (const basic_json* p = json_patch.m_parent; !patch_aliases_this && p != nullptr; p = p->m_parent)
|
||||||
|
{
|
||||||
|
patch_aliases_this = (p == this);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(patch_aliases_this))
|
||||||
{
|
{
|
||||||
// NOLINTNEXTLINE(performance-unnecessary-copy-initialization): *this is modified below
|
// NOLINTNEXTLINE(performance-unnecessary-copy-initialization): *this is modified below
|
||||||
const basic_json json_patch_copy = json_patch;
|
const basic_json json_patch_copy = json_patch;
|
||||||
|
|||||||
@@ -855,6 +855,19 @@ TEST_CASE("JSON patch")
|
|||||||
CHECK(doc == expected_copies);
|
CHECK(doc == expected_copies);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if JSON_DIAGNOSTICS
|
||||||
|
SECTION("patch stored inside the patched value")
|
||||||
|
{
|
||||||
|
// the parent pointers of JSON_DIAGNOSTICS make the aliasing detectable
|
||||||
|
const json op = {{"op", "replace"}, {"path", "/patch"}, {"value", 1}};
|
||||||
|
json doc = {{"a", 1}, {"patch", json::array({op})}};
|
||||||
|
const json expected = doc.patch(doc["patch"]);
|
||||||
|
|
||||||
|
doc.patch_inplace(doc["patch"]);
|
||||||
|
CHECK(doc == expected);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
SECTION("copy operations with spare capacity")
|
SECTION("copy operations with spare capacity")
|
||||||
{
|
{
|
||||||
// reserve while the array is empty: reallocating non-empty
|
// reserve while the array is empty: reallocating non-empty
|
||||||
|
|||||||
Reference in new issue
Block a user