From cf464aa666be9c62f5b2e8ca9ed292ed3d4ba419 Mon Sep 17 00:00:00 2001 From: Ramya Eliger Date: Wed, 7 Oct 2026 13:17:56 +0530 Subject: [PATCH 1/2] create the new value before the walk in set_at_pointer --- include/boost/json/impl/pointer.ipp | 7 ++++- test/pointer.cpp | 44 +++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/include/boost/json/impl/pointer.ipp b/include/boost/json/impl/pointer.ipp index a6a05d84d..897c896ef 100644 --- a/include/boost/json/impl/pointer.ipp +++ b/include/boost/json/impl/pointer.ipp @@ -426,6 +426,11 @@ value::set_at_pointer( system::error_code& ec, set_pointer_options const& opts ) { + // ref can refer to an element of this document, which the walk below + // can replace or relocate while it creates missing elements, so the new + // value is created before the document is modified + value new_value = ref.make_value( storage() ); + value* result = detail::walk_pointer( *this, sv, @@ -495,7 +500,7 @@ value::set_at_pointer( }); if( result ) - *result = ref.make_value( storage() ); + *result = std::move(new_value); return result; } diff --git a/test/pointer.cpp b/test/pointer.cpp index 7a2ffcbb8..f57beddfd 100644 --- a/test/pointer.cpp +++ b/test/pointer.cpp @@ -340,6 +340,50 @@ class pointer_test BOOST_TEST(( jv == object{ {"x", 1} } )); BOOST_TEST( *result == 1 ); BOOST_TEST( result == &jv.at_pointer("/x") ); + + // the new value has to be created before the walk, because the walk + // replaces and reallocates elements the value_ref can refer to + opts = {}; + jv = object{ {"c", nullptr} }; + result = &jv.set_at_pointer( "/c/x", jv.at("c"), opts ); + BOOST_TEST(( jv == object{ {"c", object{ {"x", nullptr} }} } )); + BOOST_TEST( result == &jv.at_pointer("/c/x") ); + + jv = object{ {"c", nullptr} }; + result = &jv.set_at_pointer( "/c/0", jv.at("c"), opts ); + BOOST_TEST(( jv == object{ {"c", array{nullptr}} } )); + BOOST_TEST( result == &jv.at_pointer("/c/0") ); + + jv = value(); + result = &jv.set_at_pointer( "/x", jv, opts ); + BOOST_TEST(( jv == object{ {"x", nullptr} } )); + BOOST_TEST( result == &jv.at_pointer("/x") ); + + opts.replace_any_scalar = true; + jv = object{ {"c", "a string that does not fit in the SBO buffer"} }; + result = &jv.set_at_pointer( "/c/x", jv.at("c"), opts ); + BOOST_TEST(( + jv == object{ {"c", object{ + {"x", "a string that does not fit in the SBO buffer"} }} } )); + BOOST_TEST( result == &jv.at_pointer("/c/x") ); + + // growing the object frees the table the value_ref points into + opts = {}; + jv = object{ {"b", array{1, 2, 3}} }; + BOOST_TEST( jv.get_object().capacity() == jv.get_object().size() ); + result = &jv.set_at_pointer( "/a", jv.at("b"), opts ); + BOOST_TEST(( jv == object{ + {"b", array{1, 2, 3}}, {"a", array{1, 2, 3}} } )); + BOOST_TEST( result == &jv.at_pointer("/a") ); + + // growing the array frees the table the value_ref points into + opts.max_created_elements = 100; + jv = array{ array{9, 8}, 1 }; + BOOST_TEST( jv.get_array().capacity() == jv.get_array().size() ); + result = &jv.set_at_pointer( "/5", jv.at_pointer("/0"), opts ); + BOOST_TEST(( jv == array{ + array{9, 8}, 1, nullptr, nullptr, nullptr, array{9, 8} } )); + BOOST_TEST( result == &jv.at_pointer("/5") ); } template From fa04fc46297bdb0a4ab5c2f4a683107761372d84 Mon Sep 17 00:00:00 2001 From: Ramya Eliger Date: Thu, 15 Oct 2026 13:25:57 +0530 Subject: [PATCH 2/2] keep only the in-place replacement case in the set_at_pointer test --- test/pointer.cpp | 36 ------------------------------------ 1 file changed, 36 deletions(-) diff --git a/test/pointer.cpp b/test/pointer.cpp index f57beddfd..75f56dcda 100644 --- a/test/pointer.cpp +++ b/test/pointer.cpp @@ -348,42 +348,6 @@ class pointer_test result = &jv.set_at_pointer( "/c/x", jv.at("c"), opts ); BOOST_TEST(( jv == object{ {"c", object{ {"x", nullptr} }} } )); BOOST_TEST( result == &jv.at_pointer("/c/x") ); - - jv = object{ {"c", nullptr} }; - result = &jv.set_at_pointer( "/c/0", jv.at("c"), opts ); - BOOST_TEST(( jv == object{ {"c", array{nullptr}} } )); - BOOST_TEST( result == &jv.at_pointer("/c/0") ); - - jv = value(); - result = &jv.set_at_pointer( "/x", jv, opts ); - BOOST_TEST(( jv == object{ {"x", nullptr} } )); - BOOST_TEST( result == &jv.at_pointer("/x") ); - - opts.replace_any_scalar = true; - jv = object{ {"c", "a string that does not fit in the SBO buffer"} }; - result = &jv.set_at_pointer( "/c/x", jv.at("c"), opts ); - BOOST_TEST(( - jv == object{ {"c", object{ - {"x", "a string that does not fit in the SBO buffer"} }} } )); - BOOST_TEST( result == &jv.at_pointer("/c/x") ); - - // growing the object frees the table the value_ref points into - opts = {}; - jv = object{ {"b", array{1, 2, 3}} }; - BOOST_TEST( jv.get_object().capacity() == jv.get_object().size() ); - result = &jv.set_at_pointer( "/a", jv.at("b"), opts ); - BOOST_TEST(( jv == object{ - {"b", array{1, 2, 3}}, {"a", array{1, 2, 3}} } )); - BOOST_TEST( result == &jv.at_pointer("/a") ); - - // growing the array frees the table the value_ref points into - opts.max_created_elements = 100; - jv = array{ array{9, 8}, 1 }; - BOOST_TEST( jv.get_array().capacity() == jv.get_array().size() ); - result = &jv.set_at_pointer( "/5", jv.at_pointer("/0"), opts ); - BOOST_TEST(( jv == array{ - array{9, 8}, 1, nullptr, nullptr, nullptr, array{9, 8} } )); - BOOST_TEST( result == &jv.at_pointer("/5") ); } template