Compare commits

..
Author SHA1 Message Date
Niels Lohmann b6bf3e611c Merge branch 'develop' into fix/issue-5993
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-11-03 13:43:49 +01:00
Niels Lohmann e32051125f Fix the build of develop: tests from #5997 and #5991 (#6019)
* Fix the build of develop: tests from #5997 and #5991

- unit-msgpack-cpp17: assign the result of json::parse in
  CHECK_THROWS_WITH_AS (GCC -Werror=unused-result)
- unit-json_view: failing_allocator::allocate is [[noreturn]]
  (clang -Wmissing-noreturn)
- unit-class_lexer: build the quoted string once
  (clang-tidy performance-inefficient-string-concatenation)
- unit-allocator: sweep_allocator provides pointer, reference and size
  typedefs, which GCC 4.8's std::map takes from the allocator
- unit-allocator: skip the "sax_parse into an existing value" sweep with
  iterator debugging, like the parser callback sweep: MSVC's std::vector
  default constructor is noexcept but allocates its debug proxy through
  the allocator, so a failing allocation in create<array_t>() terminates

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Fix the remaining CI failures of the tests from #5997 and #5991

- unit-custom-object-key-type: drop the redundant member initializer and
  return a braced init list (clang-tidy); direct-initialize the converted
  value, as copy-initialization makes GCC 4.8 instantiate the copy
  constructor, which needs a range constructor strict_object lacks
- unit-stream-bulk-scan: pick the claimed get area size by tag dispatch
  instead of a constant condition (MSVC C4127)
- unit-allocator: skip the parser callback and sax_parse sweeps with
  Clang 3.4 and 3.5, which leak memory when an element constructor throws
  inside a libstdc++ std::map copy or std::vector reallocation

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Fix the CI failures that the first failing TU of each job hid

- unit-stream-bulk-scan: compute the claimed size without a condition;
  the tag-dispatched template was unused with a 64-bit std::size_t
  (-Wunused-template)
- unit-json_view: give failing_allocator the typedefs, rebind and
  members that GCC 4.8's std::map takes from the allocator directly
- clang-tidy hits in the tests from #5997: raw string literals
  (unit-json_view), redundant c_str() and a NOLINT for &text[0]
  (unit-locale-cpp), reserve() before a loop (unit-ordered_json), and a
  NOLINT for a return that a braced list would turn into an array
  (unit-regression1)

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Skip the 3 GiB get-area test with a 32-bit std::size_t in the preprocessor

The claimed size was a constant 0 on Win32, so `claimed == 0 || ...` was a
constant condition and MSVC reported C4127. Guard the section with
`#if SIZE_MAX > UINT32_MAX` instead, as unit-msgpack-custom-types.cpp does.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Silence modernize-use-transparent-functors for failing_map in unit-json_view

std::less<> needs C++14, and the test is also built as C++11.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

---------

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-11-03 13:42:46 +01:00
Niels Lohmann 8c1421d493 Merge develop into fix/issue-5993
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-11-02 20:07:52 +01:00
Niels Lohmann 6d8d2df367 Merge develop into fix/issue-5993
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-11-02 18:27:02 +01:00
Niels Lohmann 6aa10fd343 Simplify the decimal point condition in dump_float
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-11-02 17:25:51 +01:00
Niels Lohmann 98cfd55317 Move the fraction only for a multibyte decimal point
For a one-byte decimal point, the std::copy() call had its destination at
the start of its source range, which std::copy() does not allow. The bytes
are now moved only when the decimal point is longer than the '.' it is
replaced with. Also name the right place of get_decimal_point().

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-11-02 09:32:50 +01:00
Claude e96469f783 Use std::char_traits<char>::length instead of strlen in dump_float()
Codacy (flawfinder, CWE-126) flags strlen on the locale's decimal point.
The string is NUL-terminated by localeconv(), so the behavior is the same.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N5NasAgbDGG28MqXjQvLqp
Signed-off-by: Claude <noreply@anthropic.com>
2026-11-02 05:39:29 +00:00
Niels Lohmann 3720e4b3aa Replace a multibyte decimal point in dump() of a long double (#5993)
The snprintf path of dump_float(), used when number_float_t is neither
IEEE single nor double (a long double on x86-64 and aarch64 Linux), read
localeconv()->decimal_point and ->thousands_sep as single chars. Under a
locale with a multibyte decimal point (glibc's ps_AF: U+066B), it removed
the first byte as a "thousands separator" and left the second one in the
output, so 1.5L dumped as "1\xab5.0".

Find the whole decimal point with std::search and replace it with '.',
as the parser already does since #5660. Drop the thousands-separator
removal: %g never groups digits.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-11-01 22:48:42 +01:00
19 changed files with 139 additions and 214 deletions

No files matched your search

@@ -15,9 +15,7 @@ the patch. This function applies a JSON patch in place and returns void.
## Exception safety
No guarantees: if an operation fails, the operations before it stay applied, and a failing `move` operation may already
have removed the value at its "from" location. Use [`patch`](patch.md) if the value must be unchanged after a failed
patch.
No guarantees, value may be corrupted by an unsuccessful patch operation.
## Exceptions
@@ -64,19 +62,8 @@ the JSON value, the complexity can usually be neglected.
## Notes
Unlike [`patch`](patch.md), `patch_inplace` applies the operation "in place" and no copy of the JSON value is created.
That makes it faster for large documents by avoiding the copy.
!!! warning "Not atomic"
`patch_inplace` applies the operations one after the other and does not undo them when one fails. After an
exception, the value holds the result of the operations before the failing one, and a failing `move` operation
(a "remove" followed by an "add") may already have removed the value at its "from" location; see the second example
below. [RFC 6902](https://tools.ietf.org/html/rfc6902#section-5) requires that a patch with a failing operation is
not deemed successful, but leaves open what happens to the document.
If the value must stay unchanged when the patch fails, use [`patch`](patch.md), which applies the patch to a copy:
`#!cpp j = j.patch(json_patch);`. Validating the patch beforehand is not enough, as "test" operations and missing
paths are only detected while the patch is applied.
That makes it faster for large documents by avoiding the copy. However, the JSON value might be corrupted if the
function throws an exception.
Duplicate member names inside an operation object are resolved by the parser before the patch is applied (the last one
is kept, see [`object_t`](object_t.md#behavior)), so `#!json {"op": "add", "op": "remove", "path": "/a"}` is a
-127
View File
@@ -1,127 +0,0 @@
# Reading and writing files
The library has no functions that take a file name. It reads from and writes to streams, so files are handled with
`#!cpp std::ifstream` and `#!cpp std::ofstream` (or a `#!cpp FILE*` when reading). This page collects the recipes for
the common cases and the mistakes that show up again and again.
## Reading a JSON file
Pass the stream to [`parse`](../api/basic_json/parse.md) directly, and check that the file was opened:
```cpp
#include <fstream>
#include <stdexcept>
#include <string>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
json read_json_file(const std::string& path)
{
std::ifstream f(path);
if (!f.is_open())
{
throw std::runtime_error("cannot open " + path);
}
return json::parse(f);
}
```
- **Check that the file is open.** A stream that failed to open looks like an empty input, so `parse` reports
[`parse_error.101`](../home/exceptions.md#jsonexceptionparse_error101) ("unexpected end of input") at line 1,
column 1 instead of saying that the file is missing. A relative path is resolved against the working directory of the
running program, which is often not the directory of the source file (see the
[FAQ](../home/faq.md#parsing-a-file-fails-with-unexpected-end-of-input)).
- **Don't read the file into a string first.** Parsing from a `#!cpp std::ifstream` is only about 10 to 20 % slower
than reading the whole file into a string with one `read()` and parsing that, and it needs no memory for the copy (see
[input sources and performance](parsing/index.md#input-sources-and-performance)). The common way to read a file with
`#!cpp std::istreambuf_iterator<char>` is so slow that it takes about twice as long as parsing from the stream. Read
the file into a string only if you need the text itself, for example for an error message.
- `parse` requires the file to hold exactly one JSON value. Files with one value per line are read with
[JSON Lines](parsing/json_lines.md); files too large for memory are covered in
[Large, streamed, and partial input](parsing/large_and_partial_input.md).
## Writing a JSON file
[`dump`](../api/basic_json/dump.md) the value into a string first, then write the string:
```cpp
void write_json_file(const std::string& path, const json& j)
{
const std::string text = j.dump(4) + '\n'; // may throw: nothing is written yet
std::ofstream f(path, std::ios::binary);
if (!f.is_open())
{
throw std::runtime_error("cannot open " + path);
}
f << text;
f.close();
if (f.fail())
{
throw std::runtime_error("cannot write " + path);
}
}
```
- **Serialize before opening the file.** Opening a `#!cpp std::ofstream` truncates the file. `dump` and
[`operator<<`](../api/operator_ltlt.md) throw [`type_error.316`](../home/exceptions.md#jsonexceptiontype_error316)
when a string is not valid UTF-8 (see [handling invalid UTF-8](serialization.md#handling-invalid-utf-8)), and
`#!cpp f << std::setw(4) << j` may already have written part of the value when it throws. Serializing into a string
first leaves the old file untouched in that case.
- **Check the stream after closing it.** A full disk or a missing permission sets the stream's error state; it does not
throw unless you enabled exceptions on the stream with `exceptions()`.
- `std::ios::binary` writes the text as it is. Without it, Windows writes `\r\n` for every newline of a pretty-printed
value.
## Replacing a file without corrupting it
The library never writes a file itself, so this section is not about the library. It is general C++ advice for a
problem users of the library keep running into.
A program that crashes or is killed while it writes leaves a truncated file behind. To make sure readers see either the
old or the new contents, write to a temporary file in the same directory and rename it over the original
(C++17, `#!cpp <filesystem>`):
```cpp
#include <filesystem>
void replace_json_file(const std::filesystem::path& path, const json& j)
{
std::filesystem::path tmp = path;
tmp += ".tmp"; // use a unique name if several processes may write the file
write_json_file(tmp.string(), j);
std::filesystem::rename(tmp, path); // replaces path
}
```
The temporary file must be in the same directory (more precisely, on the same file system) as the target: a rename
across file systems does not fall back to a copy, it fails and throws `#!cpp std::filesystem::filesystem_error`. On
POSIX systems, the rename atomically replaces the file. Standard C++ has no way to flush a file to the disk (`fsync`),
so this protects against a crashed program, but not necessarily against a power loss.
## Binary formats
The [binary formats](binary_formats/index.md) work the same way, with the streams opened in binary mode:
```cpp
// read
std::ifstream in("data.cbor", std::ios::binary);
json j = json::from_cbor(in);
// write
const std::vector<std::uint8_t> bytes = json::to_cbor(j);
std::ofstream out("data.cbor", std::ios::binary);
out.write(reinterpret_cast<const char*>(bytes.data()), static_cast<std::streamsize>(bytes.size()));
```
Without `std::ios::binary`, Windows turns the bytes `0x0D 0x0A` into `0x0A` and may stop at a byte `0x1A` while reading,
and turns `0x0A` into `0x0D 0x0A` while writing, which corrupts the data.
## See also
- [Parsing](parsing/index.md) - the inputs `parse` accepts
- [Serialization](serialization.md) - the options of `dump`
- [`parse`](../api/basic_json/parse.md), [`dump`](../api/basic_json/dump.md)
- [`operator>>`](../api/operator_gtgt.md), [`operator<<`](../api/operator_ltlt.md)
-2
View File
@@ -46,8 +46,6 @@ C++ types, and finally serialize it again.
[BJData](binary_formats/bjdata.md), [BON8](binary_formats/bon8.md), [BSON](binary_formats/bson.md),
[CBOR](binary_formats/cbor.md), [MessagePack](binary_formats/messagepack.md), or [UBJSON](binary_formats/ubjson.md).
- [Binary values](binary_values.md) — store and exchange raw byte sequences.
- [Reading and writing files](files.md) — read and write files safely, including replacing a file without
corrupting it.
## How values are stored and configured
+1 -2
View File
@@ -29,8 +29,7 @@ other three continue on its result.
[`patch`](../api/basic_json/patch.md) leaves the original value unchanged and returns the patched result as a copy.
If the document is large and the original value is no longer needed,
[`patch_inplace`](../api/basic_json/patch_inplace.md) applies the same operations in place instead. Unlike
`patch`, it is not atomic: if an operation fails, the operations before it stay applied.
[`patch_inplace`](../api/basic_json/patch_inplace.md) applies the same operations in place instead.
!!! note "Duplicate member names"
@@ -35,8 +35,6 @@ std::ifstream f("example.json");
json data = json::parse(f);
```
See [Reading and writing files](../files.md) for checking that the file was opened and for writing files safely.
The input must be encoded in UTF-8; other encodings are not supported. The exception is wide-character input, which
is read as UTF-16 or UTF-32 — depending on the character type — and converted to UTF-8; see the
[`parse`](../../api/basic_json/parse.md) page for an example. A single input may contain only one JSON value.
@@ -17,9 +17,6 @@ To write a value directly to a stream (for example, a file or `#!cpp std::cout`)
std::cout << j << std::endl;
```
To write a file without leaving a truncated file behind when serialization fails, see
[Reading and writing files](files.md#writing-a-json-file).
!!! note "String, not raw value"
`dump` always returns a **JSON text**. Serializing a JSON string therefore includes the surrounding quotes and
+1 -1
View File
@@ -110,7 +110,7 @@ in the source files that need it, instantiate `basic_json` once, and consider pr
The parser did not see any character: the file was most likely not opened. A relative path is resolved against the
working directory of the running program, which is often the build directory of your IDE, not the source directory.
Check `is_open()` and try an absolute path. Other causes are an empty file and a stream that was already read. See
[`parse_error.101`](exceptions.md#jsonexceptionparse_error101) and [Reading and writing files](../features/files.md).
[`parse_error.101`](exceptions.md#jsonexceptionparse_error101).
### Input that arrives in chunks
-1
View File
@@ -130,7 +130,6 @@ nav:
- features/element_access/unchecked_access.md
- features/element_access/checked_access.md
- features/element_access/default_value.md
- features/files.md
- features/iterators.md
- features/merge_patch.md
- features/json_patch.md
+23 -19
View File
@@ -8,7 +8,7 @@
#pragma once
#include <algorithm> // remove, fill, find, none_of, min
#include <algorithm> // copy, none_of, min, search
#include <array> // array
#include <clocale> // localeconv, lconv
#include <cmath> // isfinite
@@ -2080,31 +2080,35 @@ class serializer
// check if the buffer was large enough
JSON_ASSERT(static_cast<std::size_t>(len) < number_buffer.size());
// look up the locale's thousands separator and decimal point now,
// matching what snprintf_float() just used (see lexer::get_decimal_point())
// look up the locale's decimal point now, matching what
// snprintf_float() just used (see get_decimal_point() in
// 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();
JSON_ASSERT(loc != nullptr);
const char thousands_sep = (loc->thousands_sep == nullptr) ? '\0' : *loc->thousands_sep;
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());
}
const char* const decimal_point = (loc->decimal_point == nullptr) ? "" : loc->decimal_point;
const std::size_t decimal_point_len = std::char_traits<char>::length(decimal_point);
// convert decimal point to '.'
if (decimal_point != '\0' && decimal_point != '.')
if ((decimal_point_len > 1) || (decimal_point_len == 1 && decimal_point[0] != '.'))
{
// NOLINTNEXTLINE(readability-qualified-auto,llvm-qualified-auto): std::find returns an iterator, see https://github.com/nlohmann/json/issues/3081
const auto dec_pos = std::find(number_buffer.begin(), number_buffer.end(), decimal_point);
if (dec_pos != number_buffer.end())
const auto first = number_buffer.begin();
const auto last = first + len;
// NOLINTNEXTLINE(readability-qualified-auto,llvm-qualified-auto): std::search returns an iterator, see https://github.com/nlohmann/json/issues/3081
const auto dec_pos = std::search(first, last, decimal_point, decimal_point + decimal_point_len);
if (dec_pos != last)
{
*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;
}
}
}
+23 -19
View File
@@ -33446,7 +33446,7 @@ NLOHMANN_JSON_NAMESPACE_END
#include <algorithm> // remove, fill, find, none_of, min
#include <algorithm> // copy, none_of, min, search
#include <array> // array
#include <clocale> // localeconv, lconv
#include <cmath> // isfinite
@@ -37300,31 +37300,35 @@ class serializer
// check if the buffer was large enough
JSON_ASSERT(static_cast<std::size_t>(len) < number_buffer.size());
// look up the locale's thousands separator and decimal point now,
// matching what snprintf_float() just used (see lexer::get_decimal_point())
// look up the locale's decimal point now, matching what
// snprintf_float() just used (see get_decimal_point() in
// 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();
JSON_ASSERT(loc != nullptr);
const char thousands_sep = (loc->thousands_sep == nullptr) ? '\0' : *loc->thousands_sep;
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());
}
const char* const decimal_point = (loc->decimal_point == nullptr) ? "" : loc->decimal_point;
const std::size_t decimal_point_len = std::char_traits<char>::length(decimal_point);
// convert decimal point to '.'
if (decimal_point != '\0' && decimal_point != '.')
if ((decimal_point_len > 1) || (decimal_point_len == 1 && decimal_point[0] != '.'))
{
// NOLINTNEXTLINE(readability-qualified-auto,llvm-qualified-auto): std::find returns an iterator, see https://github.com/nlohmann/json/issues/3081
const auto dec_pos = std::find(number_buffer.begin(), number_buffer.end(), decimal_point);
if (dec_pos != number_buffer.end())
const auto first = number_buffer.begin();
const auto last = first + len;
// NOLINTNEXTLINE(readability-qualified-auto,llvm-qualified-auto): std::search returns an iterator, see https://github.com/nlohmann/json/issues/3081
const auto dec_pos = std::search(first, last, decimal_point, decimal_point + decimal_point_len);
if (dec_pos != last)
{
*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;
}
}
}
+17 -3
View File
@@ -1038,6 +1038,14 @@ template<class T>
struct sweep_allocator
{
using value_type = T;
// GCC 4.8's std::map takes these from the allocator instead of from
// std::allocator_traits
using pointer = T*;
using const_pointer = const T*;
using reference = T&;
using const_reference = const T&;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
sweep_allocator() = default;
template<class U> sweep_allocator(const sweep_allocator<U>& /*unused*/) noexcept {}
@@ -1155,9 +1163,14 @@ TEST_CASE("parse() survives a failing allocation at every point (#5985)")
// With iterator debugging (MSVC STL debug builds, also used by clang-cl),
// containers allocate a debug proxy through the allocator inside their
// noexcept move constructors, so failing that allocation terminates the
// program instead of throwing std::bad_alloc. Nothing to check there.
// noexcept default and move constructors, so failing that allocation
// terminates the program instead of throwing std::bad_alloc. Nothing to
// check there.
#if !(defined(_ITERATOR_DEBUG_LEVEL) && _ITERATOR_DEBUG_LEVEL > 0)
// Clang 3.4 and 3.5 leak memory when an element constructor throws
// inside libstdc++'s containers (a std::map copy, a std::vector
// reallocation), without any json value involved.
#if !(defined(__clang__) && !defined(__apple_build_version__) && __clang_major__ == 3 && __clang_minor__ < 6)
SECTION("parser callback")
{
const auto keep_all = [](int /*depth*/, sweep_json::parse_event_t /*event*/, sweep_json& /*parsed*/) noexcept
@@ -1178,7 +1191,6 @@ TEST_CASE("parse() survives a failing allocation at every point (#5985)")
return sweep_json::parse(text, drop_d);
});
}
#endif
SECTION("sax_parse into an existing value")
{
@@ -1192,5 +1204,7 @@ TEST_CASE("parse() survives a failing allocation at every point (#5985)")
return value;
});
}
#endif
#endif
}
#endif
+4 -3
View File
@@ -2222,9 +2222,10 @@ TEST_CASE("serialization of short strings")
{
CAPTURE(s)
const json j = s;
CHECK(j.dump() == "\"" + s + "\"");
CHECK(j.dump(-1, ' ', true) == "\"" + s + "\"");
CHECK(json({{s, s}}).dump() == "{\"" + s + "\":\"" + s + "\"}");
const std::string quoted = "\"" + s + "\""; // NOLINT(performance-inefficient-string-concatenation)
CHECK(j.dump() == quoted);
CHECK(j.dump(-1, ' ', true) == quoted);
CHECK(json({{s, s}}).dump() == "{" + quoted + ":" + quoted + "}"); // NOLINT(performance-inefficient-string-concatenation)
}
// a character that needs an escape
+5 -3
View File
@@ -160,12 +160,12 @@ class strict_key
}
private:
std::string m_value{};
std::string m_value;
};
strict_key make_strict_key(const std::string& key)
{
return strict_key(key.c_str());
return {key.c_str()};
}
template<class Key, class Value, class Compare, class Allocator>
@@ -224,7 +224,9 @@ TEST_CASE("converting a value with keys that cannot be made from a std::string n
}
}
const strict_json converted = value;
// direct-initialization: a copy-initialization makes GCC 4.8 instantiate the
// copy constructor, which needs a range constructor strict_object lacks
const strict_json converted(value);
// at() needs to make a std::string of a key for its error message
const auto member = [](const strict_json & object, const char* name) -> const strict_json &
+37 -3
View File
@@ -105,19 +105,53 @@ template<typename T>
struct failing_allocator
{
using value_type = T;
// GCC 4.8's std::map takes these from the allocator instead of from
// std::allocator_traits
using pointer = T*;
using const_pointer = const T*;
using reference = T&;
using const_reference = const T&;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
template<typename U>
struct rebind
{
using other = failing_allocator<U>;
};
failing_allocator() = default;
template<typename U>
explicit failing_allocator(const failing_allocator<U>& /*other*/) noexcept {}
T* allocate(std::size_t /*n*/) // NOLINT(readability-convert-member-functions-to-static)
[[noreturn]] T* allocate(std::size_t /*n*/) // NOLINT(readability-convert-member-functions-to-static)
{
throw std::bad_alloc();
}
void deallocate(T* /*p*/, std::size_t /*n*/) noexcept {} // NOLINT(readability-convert-member-functions-to-static)
// GCC 4.8's std::map calls these on the allocator directly instead of
// going through std::allocator_traits
std::size_t max_size() const noexcept // NOLINT(readability-convert-member-functions-to-static)
{
return (std::numeric_limits<std::size_t>::max)() / sizeof(T);
}
template<typename U, typename... Args>
void construct(U* p, Args&& ... args) // NOLINT(readability-convert-member-functions-to-static)
{
::new (static_cast<void*>(p)) U(std::forward<Args>(args)...);
}
template<typename U>
void destroy(U* p) noexcept // NOLINT(readability-convert-member-functions-to-static)
{
static_cast<void>(p); // unused for trivially destructible types
p->~U();
}
template<typename U>
bool operator==(const failing_allocator<U>& /*other*/) const noexcept
{
@@ -270,7 +304,7 @@ TEST_CASE("json_view")
"\"", "\"abc", "\"\\x\"", "\"\\u12\"", "\"\\ud800\"", "\"\\udc00\"", "\"\x01\"", "\"\xff\"", "\"\xc3\"", "[1]x", "/", "/*", "[\n 1,\n x\n]", // NOLINT(modernize-raw-string-literal)
"1e400", "-1e400", "[1.7976931348623159e308]", "{\"a\":\n{\"b\": [1, 2,\n 3 x]}}",
// (an error after every kind of value: the error is found by replaying the parser)
"[null, true, -1, 1, 1.5, \"s\", {\"a\": 1} 2]", "{\"a\": null, \"b\": -1, \"c\": 1.5, \"d\": \"s\", \"e\": {}, 1}"
R"([null, true, -1, 1, 1.5, "s", {"a": 1} 2])", R"({"a": null, "b": -1, "c": 1.5, "d": "s", "e": {}, 1})"
})
{
CAPTURE(text)
@@ -1556,7 +1590,7 @@ TEST_CASE("json_view values")
CHECK_THROWS_WITH_AS(not_an_object.root().get<view_map>(), json_view_test::message("type_error.302", "", 0, 5, "type must be object, but is array").c_str(), json::type_error&);
#if !defined(JSON_NOEXCEPTION)
// (and one that cannot allocate its nodes)
using failing_map = std::map<std::string, json_view, std::less<std::string>, failing_allocator<std::pair<const std::string, json_view>>>;
using failing_map = std::map<std::string, json_view, std::less<std::string>, failing_allocator<std::pair<const std::string, json_view>>>; // NOLINT(modernize-use-transparent-functors)
const json_document one_member = json_document::parse(R"({"a":1})");
CHECK_THROWS_AS(one_member.root().get<failing_map>(), std::bad_alloc&);
#endif
+12 -4
View File
@@ -397,6 +397,14 @@ TEST_CASE("locale with a multi-byte decimal point")
// a value the locale-independent paths convert is not affected
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)
{
@@ -525,7 +533,7 @@ struct locale_switching_token
{
switched = std::setlocale(LC_NUMERIC, switch_to.c_str()) != nullptr;
}
return &text[0];
return &text[0]; // NOLINT(readability-container-data-pointer): std::string::data() is const before C++17
}
std::string text;
@@ -647,7 +655,7 @@ TEST_CASE("convert_float_locale_aware")
// "C" -> comma locale: strtod stops at the '.', so the conversion is repeated
{
locale_switching_token token("1.5", comma_locale.c_str());
locale_switching_token token("1.5", comma_locale);
double value = 0;
nlohmann::detail::convert_float_locale_aware(token, 1, value);
if (token.switched)
@@ -660,7 +668,7 @@ TEST_CASE("convert_float_locale_aware")
// the same for the other floating-point types
REQUIRE(std::setlocale(LC_NUMERIC, "C") != nullptr);
{
locale_switching_token token("2.25", comma_locale.c_str());
locale_switching_token token("2.25", comma_locale);
float value = 0;
nlohmann::detail::convert_float_locale_aware(token, 1, value);
if (token.switched)
@@ -670,7 +678,7 @@ TEST_CASE("convert_float_locale_aware")
}
REQUIRE(std::setlocale(LC_NUMERIC, "C") != nullptr);
{
locale_switching_token token("2.25", comma_locale.c_str());
locale_switching_token token("2.25", comma_locale);
long double value = 0;
nlohmann::detail::convert_float_locale_aware(token, 1, value);
if (token.switched)
+2 -1
View File
@@ -122,7 +122,8 @@ TEST_CASE("JSON text with std::byte")
SECTION("invalid text")
{
CHECK_FALSE(json::accept(to_bytes("[1,\n2,")));
CHECK_THROWS_WITH_AS(json::parse(to_bytes("[1,\n2,")),
json _;
CHECK_THROWS_WITH_AS(_ = json::parse(to_bytes("[1,\n2,")),
"[json.exception.parse_error.101] parse error at line 2, column 3: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal", json::parse_error&);
}
}
+1
View File
@@ -541,6 +541,7 @@ TEST_CASE("object_key_index")
};
std::vector<std::size_t> hashes;
hashes.reserve(initial);
for (std::size_t i = 0; i < initial; ++i)
{
hashes.push_back(hash_of(i));
+1 -1
View File
@@ -1533,7 +1533,7 @@ TEST_CASE("regression tests 1")
{
my_json::object_t top = orig.get_ref<const my_json::object_t&>();
top.at("x") = my_json::parse(R"({"a":{"b":1},"n":null,"arr":[10,20],"objs":[{"m":5},{"m":6}]})");
return my_json(std::move(top));
return my_json(std::move(top)); // NOLINT(modernize-return-braced-init-list): {top} would be an array
};
SECTION("existing members")
+9 -4
View File
@@ -120,6 +120,7 @@ class oversized_get_area_streambuf : public std::streambuf
private:
std::array<char, 8> m_data{{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'}};
};
#endif
// how a test reads from the stream
@@ -463,15 +464,18 @@ TEST_CASE("parsing from a std::istream scans the get area in bulk")
CHECK(token.empty());
}
#if SIZE_MAX > UINT32_MAX
// a get area larger than an int needs a 64-bit std::size_t (checked by the
// preprocessor, as a constant condition triggers MSVC's C4127)
SECTION("skipping more characters than gbump() takes at once")
{
// gbump() takes an int, but a get area can be larger
const std::size_t claimed = sizeof(std::size_t) < 8 ? 0 : std::size_t{3} << 30u;
const std::size_t claimed = std::size_t{3} << 30u;
oversized_get_area_streambuf buf(claimed);
if (claimed == 0 || static_cast<std::size_t>(buf.in_avail()) != claimed)
if (static_cast<std::size_t>(buf.in_avail()) != claimed)
{
// a 32-bit size_t, or a standard library whose streambuf stores
// the size of the get area in an int (MSVC)
// a standard library whose streambuf stores the size of the get
// area in an int (MSVC)
MESSAGE("a get area larger than an int is not supported");
}
else
@@ -491,6 +495,7 @@ TEST_CASE("parsing from a std::istream scans the get area in bulk")
buf.restore_get_area();
}
}
#endif
#endif
SECTION("SAX parsing from a stream")