From 703ef54bd9ce56cfebbd7bf1bcd4338d41df7132 Mon Sep 17 00:00:00 2001 From: Daniel Lemire Date: Thu, 18 Sep 2025 15:16:38 -0600 Subject: [PATCH] allow string reuse (#2454) * allow string reuse * portability fix * using data and not begin --- doc/builder.md | 23 ++++++++++++++++++- .../simdjson/generic/ondemand/json_builder.h | 12 ++++++---- .../generic/ondemand/json_string_builder.h | 16 +++++++++++-- .../builder/builder_string_builder_tests.cpp | 15 ++++++++++++ 4 files changed, 59 insertions(+), 7 deletions(-) diff --git a/doc/builder.md b/doc/builder.md index 324a7d9da..aff539b91 100644 --- a/doc/builder.md +++ b/doc/builder.md @@ -176,6 +176,17 @@ std::vector> c = {{1.0, 2.0}, {3.0, 4.0}}; std::string json = simdjson::to_json(c); ``` +We also have an overload for when you want to reuse the same `std::string` instance: + + +```cpp +std::vector> c = {{1.0, 2.0}, {3.0, 4.0}}; +std::string json; +auto error = simdjson::to_json(c, json); +if(error) { /* there was an error */ } +``` + + We do recommend that you create and reuse the `string_builder` instance for performance reasons. @@ -240,7 +251,17 @@ with the `simdjson::to_json` template function. If you know the output size, in bytes, of your JSON string, you may pass it as a second parameter (e.g., `simdjson::to_json(c, 31123)`). +Sometimes you may want to reuse the same `std::string` instance. We +have an overload for this purpose: +```cpp +Car c = {"Toyota", "Corolla", 2017, {30.0,30.2,30.513,30.79}}; +std::string s; +auto error = simdjson::to_json(c, s); +if(error) { /* there was an error */ } +``` + +You can then also add a third parameter for the expected output size in bytes. ### Without `string_buffer` instance but with explicit error handling @@ -249,7 +270,7 @@ pattern: ```cpp std::string json; - if(simdjson::to(c).get(json)) { + if(simdjson::to_json(c).get(json)) { // there was an error } else { // json contain the serialized JSON diff --git a/include/simdjson/generic/ondemand/json_builder.h b/include/simdjson/generic/ondemand/json_builder.h index 7005dfc79..cf73ef6db 100644 --- a/include/simdjson/generic/ondemand/json_builder.h +++ b/include/simdjson/generic/ondemand/json_builder.h @@ -263,7 +263,7 @@ void append(string_builder &b, const Z &z) { } template -simdjson_result to_json_string(const Z &z, size_t initial_capacity = 1024) { +simdjson_warn_unused simdjson_result to_json_string(const Z &z, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) { string_builder b(initial_capacity); append(b, z); std::string_view s; @@ -272,8 +272,8 @@ simdjson_result to_json_string(const Z &z, size_t initial_capacity } template -simdjson_error to_json(const Z &z, std::string &s) { - string_builder b; +simdjson_warn_unused simdjson_error to_json(const Z &z, std::string &s, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) { + string_builder b(initial_capacity); append(b, z); std::string_view view; if(auto e = b.view().get(view); e) { return e; } @@ -290,9 +290,13 @@ string_builder& operator<<(string_builder& b, const Z& z) { } // namespace SIMDJSON_IMPLEMENTATION // Alias the function template to 'to' in the global namespace template -simdjson_result to_json(const Z &z, size_t initial_capacity = 1024) { +simdjson_warn_unused simdjson_result to_json(const Z &z, size_t initial_capacity = SIMDJSON_IMPLEMENTATION::builder::string_builder::DEFAULT_INITIAL_CAPACITY) { return SIMDJSON_IMPLEMENTATION::builder::to_json_string(z, initial_capacity); } +template +simdjson_warn_unused simdjson_error to_json(const Z &z, std::string &s, size_t initial_capacity = SIMDJSON_IMPLEMENTATION::builder::string_builder::DEFAULT_INITIAL_CAPACITY) { + return SIMDJSON_IMPLEMENTATION::builder::to_json(z, s, initial_capacity); +} } // namespace simdjson #endif // SIMDJSON_STATIC_REFLECTION diff --git a/include/simdjson/generic/ondemand/json_string_builder.h b/include/simdjson/generic/ondemand/json_string_builder.h index f4a5665ae..28238f4d9 100644 --- a/include/simdjson/generic/ondemand/json_string_builder.h +++ b/include/simdjson/generic/ondemand/json_string_builder.h @@ -43,7 +43,9 @@ struct string_constant { */ class string_builder { public: - simdjson_inline string_builder(size_t initial_capacity = 1024); + simdjson_inline string_builder(size_t initial_capacity = DEFAULT_INITIAL_CAPACITY); + + static constexpr size_t DEFAULT_INITIAL_CAPACITY = 1024; /** * Append number (includes Booleans). Booleans are mapped to the strings @@ -257,7 +259,7 @@ private: #if !SIMDJSON_STATIC_REFLECTION // fallback implementation until we have static reflection template -simdjson_result to_json(const Z &z, size_t initial_capacity = 1024) { +simdjson_warn_unused simdjson_result to_json(const Z &z, size_t initial_capacity = simdjson::SIMDJSON_IMPLEMENTATION::builder::string_builder::DEFAULT_INITIAL_CAPACITY) { simdjson::SIMDJSON_IMPLEMENTATION::builder::string_builder b(initial_capacity); b.append(z); std::string_view s; @@ -265,6 +267,16 @@ simdjson_result to_json(const Z &z, size_t initial_capacity = 1024) if(e) { return e; } return std::string(s); } +template +simdjson_warn_unused simdjson_error to_json(const Z &z, std::string &s, size_t initial_capacity = simdjson::SIMDJSON_IMPLEMENTATION::builder::string_builder::DEFAULT_INITIAL_CAPACITY) { + simdjson::SIMDJSON_IMPLEMENTATION::builder::string_builder b(initial_capacity); + b.append(z); + std::string_view sv; + auto e = b.view().get(sv); + if(e) { return e; } + s.assign(sv.data(), sv.size()); + return simdjson::SUCCESS; +} #endif diff --git a/tests/builder/builder_string_builder_tests.cpp b/tests/builder/builder_string_builder_tests.cpp index 8c5d67da3..f866f1e9e 100644 --- a/tests/builder/builder_string_builder_tests.cpp +++ b/tests/builder/builder_string_builder_tests.cpp @@ -519,6 +519,20 @@ namespace builder_tests { ASSERT_EQUAL(s, "[[1.0,2.0],[3.0,4.0]]"); TEST_SUCCEED(); } + bool double_double_test_to_string() { + TEST_START(); + std::vector> c = {{1.0, 2.0}, {3.0, 4.0}}; + simdjson::builder::string_builder sb; + sb.append(c); + std::string_view p; + auto result = sb.view().get(p); + ASSERT_SUCCESS(result); + ASSERT_EQUAL(p, "[[1.0,2.0],[3.0,4.0]]"); + std::string s; + simdjson::simdjson_error ec = simdjson::to_json(c,s); + ASSERT_EQUAL(s, "[[1.0,2.0],[3.0,4.0]]"); + TEST_SUCCEED(); + } #if SIMDJSON_EXCEPTIONS bool car_test_simple_complete_exceptions() { TEST_START(); @@ -567,6 +581,7 @@ namespace builder_tests { #if SIMDJSON_SUPPORTS_RANGES && SIMDJSON_SUPPORTS_CONCEPTS map_test() && double_double_test() && + double_double_test_to_string() && car_test_simple() && car_test_simple_complete() && #if SIMDJSON_EXCEPTIONS