Compare commits

..
Author SHA1 Message Date
Niels Lohmann 74a96a0bbb Binary writers: zero only a window ahead of the vector sink's position
output_vector_sink grew the vector by resizing it to its whole new
capacity. resize() zeroes the characters it adds, so every doubling
zeroed megabytes of a fresh allocation at once: that faults in all its
pages and evicts them from the cache before the writer reaches them,
and the writer's stores then miss again. The sink now reserves the same
capacity as before but resizes the vector only to 64 KiB
(vector_sink_window) ahead of the position, which is still cached when
the writer's stores come.

to_cbor(), to_msgpack(), to_ubjson(), to_bjdata() and to_bon8() of an
array of one million numbers into a fresh vector: 4-26 % faster on x86
(Skylake laptop with Apple clang 13, Haswell VPS with GCC 16 and
Clang 23), 1-2.5 % on Apple M1 Max; other documents unchanged (within
0.4 % in reruns). The capacity and growth of the vector are as before.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-11-03 16:12:16 +01:00
20 changed files with 99 additions and 81 deletions

No files matched your search

+1 -1
View File
@@ -28,7 +28,7 @@ jobs:
steps: steps:
- name: Harden Runner - name: Harden Runner
uses: step-security/harden-runner@46b06cea07f9e64621fdf066ba77ac6915163d43 # v2.22.3 uses: step-security/harden-runner@dc2769abcc0fcfbadfc1df54081c1608579ef6b2 # v2.22.2
with: with:
egress-policy: audit egress-policy: audit
+2 -2
View File
@@ -22,7 +22,7 @@ jobs:
runs-on: ubuntu-26.04 runs-on: ubuntu-26.04
steps: steps:
- name: Harden Runner - name: Harden Runner
uses: step-security/harden-runner@46b06cea07f9e64621fdf066ba77ac6915163d43 # v2.22.3 uses: step-security/harden-runner@dc2769abcc0fcfbadfc1df54081c1608579ef6b2 # v2.22.2
with: with:
egress-policy: audit egress-policy: audit
@@ -46,7 +46,7 @@ jobs:
steps: steps:
- name: Harden Runner - name: Harden Runner
uses: step-security/harden-runner@46b06cea07f9e64621fdf066ba77ac6915163d43 # v2.22.3 uses: step-security/harden-runner@dc2769abcc0fcfbadfc1df54081c1608579ef6b2 # v2.22.2
with: with:
egress-policy: audit egress-policy: audit
+1 -1
View File
@@ -20,7 +20,7 @@ jobs:
timeout-minutes: 30 timeout-minutes: 30
steps: steps:
- name: Harden Runner - name: Harden Runner
uses: step-security/harden-runner@46b06cea07f9e64621fdf066ba77ac6915163d43 # v2.22.3 uses: step-security/harden-runner@dc2769abcc0fcfbadfc1df54081c1608579ef6b2 # v2.22.2
with: with:
egress-policy: audit egress-policy: audit
+1 -1
View File
@@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
steps: steps:
- name: Harden Runner - name: Harden Runner
uses: step-security/harden-runner@46b06cea07f9e64621fdf066ba77ac6915163d43 # v2.22.3 uses: step-security/harden-runner@dc2769abcc0fcfbadfc1df54081c1608579ef6b2 # v2.22.2
with: with:
egress-policy: audit egress-policy: audit
+1 -1
View File
@@ -27,7 +27,7 @@ jobs:
steps: steps:
- name: Harden Runner - name: Harden Runner
uses: step-security/harden-runner@46b06cea07f9e64621fdf066ba77ac6915163d43 # v2.22.3 uses: step-security/harden-runner@dc2769abcc0fcfbadfc1df54081c1608579ef6b2 # v2.22.2
with: with:
egress-policy: audit egress-policy: audit
@@ -20,7 +20,7 @@ jobs:
pull-requests: write pull-requests: write
steps: steps:
- name: Harden Runner - name: Harden Runner
uses: step-security/harden-runner@46b06cea07f9e64621fdf066ba77ac6915163d43 # v2.22.3 uses: step-security/harden-runner@dc2769abcc0fcfbadfc1df54081c1608579ef6b2 # v2.22.2
with: with:
egress-policy: audit egress-policy: audit
+1 -1
View File
@@ -21,7 +21,7 @@ jobs:
runs-on: ubuntu-26.04 runs-on: ubuntu-26.04
steps: steps:
- name: Harden Runner - name: Harden Runner
uses: step-security/harden-runner@46b06cea07f9e64621fdf066ba77ac6915163d43 # v2.22.3 uses: step-security/harden-runner@dc2769abcc0fcfbadfc1df54081c1608579ef6b2 # v2.22.2
with: with:
egress-policy: audit egress-policy: audit
+1 -1
View File
@@ -31,7 +31,7 @@ jobs:
security-events: write security-events: write
steps: steps:
- name: Harden Runner - name: Harden Runner
uses: step-security/harden-runner@46b06cea07f9e64621fdf066ba77ac6915163d43 # v2.22.3 uses: step-security/harden-runner@dc2769abcc0fcfbadfc1df54081c1608579ef6b2 # v2.22.2
with: with:
egress-policy: audit egress-policy: audit
+1 -1
View File
@@ -54,7 +54,7 @@ jobs:
timeout-minutes: 60 timeout-minutes: 60
steps: steps:
- name: Harden Runner - name: Harden Runner
uses: step-security/harden-runner@46b06cea07f9e64621fdf066ba77ac6915163d43 # v2.22.3 uses: step-security/harden-runner@dc2769abcc0fcfbadfc1df54081c1608579ef6b2 # v2.22.2
with: with:
egress-policy: audit egress-policy: audit
+1 -1
View File
@@ -23,7 +23,7 @@ jobs:
steps: steps:
- name: Harden Runner - name: Harden Runner
uses: step-security/harden-runner@46b06cea07f9e64621fdf066ba77ac6915163d43 # v2.22.3 uses: step-security/harden-runner@dc2769abcc0fcfbadfc1df54081c1608579ef6b2 # v2.22.2
with: with:
egress-policy: audit egress-policy: audit
+1 -1
View File
@@ -37,7 +37,7 @@ jobs:
runs-on: ubuntu-26.04 runs-on: ubuntu-26.04
steps: steps:
- name: Harden Runner - name: Harden Runner
uses: step-security/harden-runner@46b06cea07f9e64621fdf066ba77ac6915163d43 # v2.22.3 uses: step-security/harden-runner@dc2769abcc0fcfbadfc1df54081c1608579ef6b2 # v2.22.2
with: with:
egress-policy: audit egress-policy: audit
+1 -1
View File
@@ -40,7 +40,7 @@ jobs:
steps: steps:
- name: Harden Runner - name: Harden Runner
uses: step-security/harden-runner@46b06cea07f9e64621fdf066ba77ac6915163d43 # v2.22.3 uses: step-security/harden-runner@dc2769abcc0fcfbadfc1df54081c1608579ef6b2 # v2.22.2
with: with:
egress-policy: audit egress-policy: audit
+1 -1
View File
@@ -36,7 +36,7 @@ jobs:
runs-on: ubuntu-26.04 runs-on: ubuntu-26.04
steps: steps:
- name: Harden Runner - name: Harden Runner
uses: step-security/harden-runner@46b06cea07f9e64621fdf066ba77ac6915163d43 # v2.22.3 uses: step-security/harden-runner@dc2769abcc0fcfbadfc1df54081c1608579ef6b2 # v2.22.2
with: with:
egress-policy: audit egress-policy: audit
+1 -1
View File
@@ -16,7 +16,7 @@ jobs:
steps: steps:
- name: Harden Runner - name: Harden Runner
uses: step-security/harden-runner@46b06cea07f9e64621fdf066ba77ac6915163d43 # v2.22.3 uses: step-security/harden-runner@dc2769abcc0fcfbadfc1df54081c1608579ef6b2 # v2.22.2
with: with:
egress-policy: audit egress-policy: audit
+5 -5
View File
@@ -80,7 +80,7 @@ jobs:
runs-on: ubuntu-26.04 runs-on: ubuntu-26.04
steps: steps:
- name: Harden Runner - name: Harden Runner
uses: step-security/harden-runner@46b06cea07f9e64621fdf066ba77ac6915163d43 # v2.22.3 uses: step-security/harden-runner@dc2769abcc0fcfbadfc1df54081c1608579ef6b2 # v2.22.2
with: with:
egress-policy: audit egress-policy: audit
@@ -112,7 +112,7 @@ jobs:
target: [ci_test_single_header, ci_cppcheck, ci_cpplint, ci_reproducible_tests, ci_non_git_tests, ci_offline_testdata, ci_reuse_compliance, ci_test_valgrind] target: [ci_test_single_header, ci_cppcheck, ci_cpplint, ci_reproducible_tests, ci_non_git_tests, ci_offline_testdata, ci_reuse_compliance, ci_test_valgrind]
steps: steps:
- name: Harden Runner - name: Harden Runner
uses: step-security/harden-runner@46b06cea07f9e64621fdf066ba77ac6915163d43 # v2.22.3 uses: step-security/harden-runner@dc2769abcc0fcfbadfc1df54081c1608579ef6b2 # v2.22.2
with: with:
egress-policy: audit egress-policy: audit
@@ -178,7 +178,7 @@ jobs:
runs-on: ubuntu-26.04 runs-on: ubuntu-26.04
steps: steps:
- name: Harden Runner - name: Harden Runner
uses: step-security/harden-runner@46b06cea07f9e64621fdf066ba77ac6915163d43 # v2.22.3 uses: step-security/harden-runner@dc2769abcc0fcfbadfc1df54081c1608579ef6b2 # v2.22.2
with: with:
egress-policy: audit egress-policy: audit
@@ -433,7 +433,7 @@ jobs:
runs-on: ubuntu-26.04 runs-on: ubuntu-26.04
steps: steps:
- name: Harden Runner - name: Harden Runner
uses: step-security/harden-runner@46b06cea07f9e64621fdf066ba77ac6915163d43 # v2.22.3 uses: step-security/harden-runner@dc2769abcc0fcfbadfc1df54081c1608579ef6b2 # v2.22.2
with: with:
egress-policy: audit egress-policy: audit
@@ -456,7 +456,7 @@ jobs:
target: [ci_test_examples, ci_test_build_documentation, ci_test_documentation_mermaid] target: [ci_test_examples, ci_test_build_documentation, ci_test_documentation_mermaid]
steps: steps:
- name: Harden Runner - name: Harden Runner
uses: step-security/harden-runner@46b06cea07f9e64621fdf066ba77ac6915163d43 # v2.22.3 uses: step-security/harden-runner@dc2769abcc0fcfbadfc1df54081c1608579ef6b2 # v2.22.2
with: with:
egress-policy: audit egress-policy: audit
@@ -116,6 +116,10 @@ inline void copy_short(CharType* dst, const CharType* src, const std::size_t len
/// the number of characters an empty vector is grown to by output_vector_sink /// the number of characters an empty vector is grown to by output_vector_sink
constexpr std::size_t min_vector_sink_capacity = 64; constexpr std::size_t min_vector_sink_capacity = 64;
/// the number of characters output_vector_sink makes writable ahead of its
/// position at a time: 64 KiB of the vector's capacity, not all of it
constexpr std::size_t vector_sink_window = 65536;
/// @brief non-virtual output sink writing into a std::vector /// @brief non-virtual output sink writing into a std::vector
/// ///
/// This sink is not part of the virtual output_adapter_protocol hierarchy: it is /// This sink is not part of the virtual output_adapter_protocol hierarchy: it is
@@ -129,7 +133,8 @@ constexpr std::size_t min_vector_sink_capacity = 64;
/// encoding them: insert() is an out-of-line call into a routine that can insert /// encoding them: insert() is an out-of-line call into a routine that can insert
/// anywhere, and resize() is one as well with libstdc++. The sink therefore keeps /// anywhere, and resize() is one as well with libstdc++. The sink therefore keeps
/// its own write position: it grows the vector geometrically ahead of the /// its own write position: it grows the vector geometrically ahead of the
/// position and copies each write to it, so a write is a capacity check and a /// position (making @ref vector_sink_window characters of it writable at a
/// time) and copies each write to it, so a write is a capacity check and a
/// short copy. The destructor shrinks the vector to the bytes actually written, /// short copy. The destructor shrinks the vector to the bytes actually written,
/// also when a write throws; shrinking a vector never allocates. The sink owns /// also when a write throws; shrinking a vector never allocates. The sink owns
/// that trim, so it can be moved but not copied, and the vector must not be used /// that trim, so it can be moved but not copied, and the vector must not be used
@@ -277,9 +282,17 @@ class output_vector_sink
// reallocate for every few bytes // reallocate for every few bytes
const std::size_t written = position(); const std::size_t written = position();
const std::size_t needed = written + length; const std::size_t needed = written + length;
v->resize((std::max)(min_vector_sink_capacity, needed <= v->capacity() const std::size_t capacity = (std::max)(min_vector_sink_capacity, needed <= v->capacity()
? v->capacity() ? v->capacity()
: (std::max)(needed, v->capacity() * 2))); : (std::max)(needed, v->capacity() * 2));
v->reserve(capacity);
// resize() zeroes the characters it adds. Zeroing all of a doubled
// capacity, megabytes of a fresh allocation, at once faults in its
// pages and evicts them from the cache before the writer gets there,
// so its stores miss again; a window ahead of the position is still
// cached when they come (the binary writers on a long array of
// numbers: 4-26 % faster on x86, 1-2.5 % on Apple M1)
v->resize((std::min)(capacity, (std::max)(needed, written + vector_sink_window)));
cursor = v->data() + written; cursor = v->data() + written;
limit = v->data() + v->size(); limit = v->data() + v->size();
} }
+19 -23
View File
@@ -8,7 +8,7 @@
#pragma once #pragma once
#include <algorithm> // copy, none_of, min, search #include <algorithm> // remove, fill, find, none_of, min
#include <array> // array #include <array> // array
#include <clocale> // localeconv, lconv #include <clocale> // localeconv, lconv
#include <cmath> // isfinite #include <cmath> // isfinite
@@ -2080,35 +2080,31 @@ class serializer
// check if the buffer was large enough // check if the buffer was large enough
JSON_ASSERT(static_cast<std::size_t>(len) < number_buffer.size()); JSON_ASSERT(static_cast<std::size_t>(len) < number_buffer.size());
// look up the locale's decimal point now, matching what // look up the locale's thousands separator and decimal point now,
// snprintf_float() just used (see get_decimal_point() in // matching what snprintf_float() just used (see lexer::get_decimal_point())
// number_parse.hpp); it is a string, which can be longer than one
// byte (U+066B in ps_AF, #5993).
// A thousands separator needs no handling: %g never groups digits.
const auto* loc = std::localeconv(); const auto* loc = std::localeconv();
JSON_ASSERT(loc != nullptr); JSON_ASSERT(loc != nullptr);
const char* const decimal_point = (loc->decimal_point == nullptr) ? "" : loc->decimal_point; const char thousands_sep = (loc->thousands_sep == nullptr) ? '\0' : *loc->thousands_sep;
const std::size_t decimal_point_len = std::char_traits<char>::length(decimal_point); const char decimal_point = (loc->decimal_point == nullptr) ? '\0' : *loc->decimal_point;
// erase thousands separators
if (thousands_sep != '\0')
{
// NOLINTNEXTLINE(readability-qualified-auto,llvm-qualified-auto): std::remove returns an iterator, see https://github.com/nlohmann/json/issues/3081
const auto end = std::remove(number_buffer.begin(), number_buffer.begin() + len, thousands_sep);
std::fill(end, number_buffer.end(), '\0');
JSON_ASSERT((end - number_buffer.begin()) <= len);
len = (end - number_buffer.begin());
}
// convert decimal point to '.' // convert decimal point to '.'
if ((decimal_point_len > 1) || (decimal_point_len == 1 && decimal_point[0] != '.')) if (decimal_point != '\0' && decimal_point != '.')
{ {
const auto first = number_buffer.begin(); // NOLINTNEXTLINE(readability-qualified-auto,llvm-qualified-auto): std::find returns an iterator, see https://github.com/nlohmann/json/issues/3081
const auto last = first + len; const auto dec_pos = std::find(number_buffer.begin(), number_buffer.end(), decimal_point);
// NOLINTNEXTLINE(readability-qualified-auto,llvm-qualified-auto): std::search returns an iterator, see https://github.com/nlohmann/json/issues/3081 if (dec_pos != number_buffer.end())
const auto dec_pos = std::search(first, last, decimal_point, decimal_point + decimal_point_len);
if (dec_pos != last)
{ {
*dec_pos = '.'; *dec_pos = '.';
if (decimal_point_len > 1)
{
// move the fraction and exponent behind the '.' (std::copy
// allows the overlap, as the destination is before the source)
// NOLINTNEXTLINE(readability-qualified-auto,llvm-qualified-auto): std::copy returns an iterator, see https://github.com/nlohmann/json/issues/3081
const auto end = std::copy(dec_pos + static_cast<std::ptrdiff_t>(decimal_point_len), last, dec_pos + 1);
*end = '\0';
len = end - first;
}
} }
} }
+35 -26
View File
@@ -27491,6 +27491,10 @@ inline void copy_short(CharType* dst, const CharType* src, const std::size_t len
/// the number of characters an empty vector is grown to by output_vector_sink /// the number of characters an empty vector is grown to by output_vector_sink
constexpr std::size_t min_vector_sink_capacity = 64; constexpr std::size_t min_vector_sink_capacity = 64;
/// the number of characters output_vector_sink makes writable ahead of its
/// position at a time: 64 KiB of the vector's capacity, not all of it
constexpr std::size_t vector_sink_window = 65536;
/// @brief non-virtual output sink writing into a std::vector /// @brief non-virtual output sink writing into a std::vector
/// ///
/// This sink is not part of the virtual output_adapter_protocol hierarchy: it is /// This sink is not part of the virtual output_adapter_protocol hierarchy: it is
@@ -27504,7 +27508,8 @@ constexpr std::size_t min_vector_sink_capacity = 64;
/// encoding them: insert() is an out-of-line call into a routine that can insert /// encoding them: insert() is an out-of-line call into a routine that can insert
/// anywhere, and resize() is one as well with libstdc++. The sink therefore keeps /// anywhere, and resize() is one as well with libstdc++. The sink therefore keeps
/// its own write position: it grows the vector geometrically ahead of the /// its own write position: it grows the vector geometrically ahead of the
/// position and copies each write to it, so a write is a capacity check and a /// position (making @ref vector_sink_window characters of it writable at a
/// time) and copies each write to it, so a write is a capacity check and a
/// short copy. The destructor shrinks the vector to the bytes actually written, /// short copy. The destructor shrinks the vector to the bytes actually written,
/// also when a write throws; shrinking a vector never allocates. The sink owns /// also when a write throws; shrinking a vector never allocates. The sink owns
/// that trim, so it can be moved but not copied, and the vector must not be used /// that trim, so it can be moved but not copied, and the vector must not be used
@@ -27652,9 +27657,17 @@ class output_vector_sink
// reallocate for every few bytes // reallocate for every few bytes
const std::size_t written = position(); const std::size_t written = position();
const std::size_t needed = written + length; const std::size_t needed = written + length;
v->resize((std::max)(min_vector_sink_capacity, needed <= v->capacity() const std::size_t capacity = (std::max)(min_vector_sink_capacity, needed <= v->capacity()
? v->capacity() ? v->capacity()
: (std::max)(needed, v->capacity() * 2))); : (std::max)(needed, v->capacity() * 2));
v->reserve(capacity);
// resize() zeroes the characters it adds. Zeroing all of a doubled
// capacity, megabytes of a fresh allocation, at once faults in its
// pages and evicts them from the cache before the writer gets there,
// so its stores miss again; a window ahead of the position is still
// cached when they come (the binary writers on a long array of
// numbers: 4-26 % faster on x86, 1-2.5 % on Apple M1)
v->resize((std::min)(capacity, (std::max)(needed, written + vector_sink_window)));
cursor = v->data() + written; cursor = v->data() + written;
limit = v->data() + v->size(); limit = v->data() + v->size();
} }
@@ -33446,7 +33459,7 @@ NLOHMANN_JSON_NAMESPACE_END
#include <algorithm> // copy, none_of, min, search #include <algorithm> // remove, fill, find, none_of, min
#include <array> // array #include <array> // array
#include <clocale> // localeconv, lconv #include <clocale> // localeconv, lconv
#include <cmath> // isfinite #include <cmath> // isfinite
@@ -37300,35 +37313,31 @@ class serializer
// check if the buffer was large enough // check if the buffer was large enough
JSON_ASSERT(static_cast<std::size_t>(len) < number_buffer.size()); JSON_ASSERT(static_cast<std::size_t>(len) < number_buffer.size());
// look up the locale's decimal point now, matching what // look up the locale's thousands separator and decimal point now,
// snprintf_float() just used (see get_decimal_point() in // matching what snprintf_float() just used (see lexer::get_decimal_point())
// number_parse.hpp); it is a string, which can be longer than one
// byte (U+066B in ps_AF, #5993).
// A thousands separator needs no handling: %g never groups digits.
const auto* loc = std::localeconv(); const auto* loc = std::localeconv();
JSON_ASSERT(loc != nullptr); JSON_ASSERT(loc != nullptr);
const char* const decimal_point = (loc->decimal_point == nullptr) ? "" : loc->decimal_point; const char thousands_sep = (loc->thousands_sep == nullptr) ? '\0' : *loc->thousands_sep;
const std::size_t decimal_point_len = std::char_traits<char>::length(decimal_point); const char decimal_point = (loc->decimal_point == nullptr) ? '\0' : *loc->decimal_point;
// erase thousands separators
if (thousands_sep != '\0')
{
// NOLINTNEXTLINE(readability-qualified-auto,llvm-qualified-auto): std::remove returns an iterator, see https://github.com/nlohmann/json/issues/3081
const auto end = std::remove(number_buffer.begin(), number_buffer.begin() + len, thousands_sep);
std::fill(end, number_buffer.end(), '\0');
JSON_ASSERT((end - number_buffer.begin()) <= len);
len = (end - number_buffer.begin());
}
// convert decimal point to '.' // convert decimal point to '.'
if ((decimal_point_len > 1) || (decimal_point_len == 1 && decimal_point[0] != '.')) if (decimal_point != '\0' && decimal_point != '.')
{ {
const auto first = number_buffer.begin(); // NOLINTNEXTLINE(readability-qualified-auto,llvm-qualified-auto): std::find returns an iterator, see https://github.com/nlohmann/json/issues/3081
const auto last = first + len; const auto dec_pos = std::find(number_buffer.begin(), number_buffer.end(), decimal_point);
// NOLINTNEXTLINE(readability-qualified-auto,llvm-qualified-auto): std::search returns an iterator, see https://github.com/nlohmann/json/issues/3081 if (dec_pos != number_buffer.end())
const auto dec_pos = std::search(first, last, decimal_point, decimal_point + decimal_point_len);
if (dec_pos != last)
{ {
*dec_pos = '.'; *dec_pos = '.';
if (decimal_point_len > 1)
{
// move the fraction and exponent behind the '.' (std::copy
// allows the overlap, as the destination is before the source)
// NOLINTNEXTLINE(readability-qualified-auto,llvm-qualified-auto): std::copy returns an iterator, see https://github.com/nlohmann/json/issues/3081
const auto end = std::copy(dec_pos + static_cast<std::ptrdiff_t>(decimal_point_len), last, dec_pos + 1);
*end = '\0';
len = end - first;
}
} }
} }
+9 -1
View File
@@ -50,6 +50,14 @@ std::vector<json> test_values()
large_payloads.push_back(json::binary(std::vector<std::uint8_t>(static_cast<std::size_t>(100 * i), static_cast<std::uint8_t>(i)))); large_payloads.push_back(json::binary(std::vector<std::uint8_t>(static_cast<std::size_t>(100 * i), static_cast<std::uint8_t>(i))));
} }
// several times vector_sink_window characters in every format: the vector
// sink makes the capacity it grows to writable a window at a time
json many_windows = json::array();
for (std::uint64_t i = 0; i < 30000; ++i)
{
many_windows.push_back((i * 0x9E3779B97F4A7C15u) >> 1u);
}
return return
{ {
json(nullptr), json(true), json(false), json(nullptr), json(true), json(false),
@@ -61,7 +69,7 @@ std::vector<json> test_values()
json::array(), json::object(), json::array(), json::object(),
json::array({1, 2, 3}), json({{"a", 1}, {"b", nullptr}}), json::array({1, 2, 3}), json({{"a", 1}, {"b", nullptr}}),
json({{"nested", {{"deep", json::array({1, "two", 3.0, nullptr})}}}}), json({{"nested", {{"deep", json::array({1, "two", 3.0, nullptr})}}}}),
big_array, big_object, large_payloads, big_array, big_object, large_payloads, many_windows,
json::binary(std::vector<std::uint8_t>(5000, 0x42)) json::binary(std::vector<std::uint8_t>(5000, 0x42))
}; };
} }
-8
View File
@@ -397,14 +397,6 @@ TEST_CASE("locale with a multi-byte decimal point")
// a value the locale-independent paths convert is not affected // a value the locale-independent paths convert is not affected
CHECK(json::parse("12.5") == 12.5); CHECK(json::parse("12.5") == 12.5);
// dump() of a long double that takes the snprintf path replaces the
// whole decimal point, not only its first byte (#5993)
CHECK(long_double_json(1.5L).dump() == "1.5");
CHECK(long_double_json(2.0L).dump() == "2.0");
CHECK(long_double_json(-0.25L).dump() == "-0.25");
CHECK(long_double_json::parse(long_double_json(1.5e-7L).dump()) == long_double_json(1.5e-7L));
CHECK(long_double_json::parse(ld.dump()) == expected_long_double);
} }
if (!tested) if (!tested)
{ {