mirror of
https://github.com/nlohmann/json.git
synced 2026-07-08 19:45:09 +00:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 366f3d26e5 | |||
| 7c9208bfb3 | |||
| bb60941f0e |
@@ -4,6 +4,8 @@ updates:
|
||||
directory: /
|
||||
schedule:
|
||||
interval: daily
|
||||
cooldown:
|
||||
default-days: 7
|
||||
groups:
|
||||
codeql-action:
|
||||
patterns:
|
||||
@@ -13,23 +15,33 @@ updates:
|
||||
directory: /docs/mkdocs
|
||||
schedule:
|
||||
interval: daily
|
||||
cooldown:
|
||||
default-days: 7
|
||||
|
||||
- package-ecosystem: pip
|
||||
directory: /tools/astyle
|
||||
schedule:
|
||||
interval: daily
|
||||
cooldown:
|
||||
default-days: 7
|
||||
|
||||
- package-ecosystem: pip
|
||||
directory: /tools/generate_natvis
|
||||
schedule:
|
||||
interval: daily
|
||||
cooldown:
|
||||
default-days: 7
|
||||
|
||||
- package-ecosystem: pip
|
||||
directory: /tools/serve_header
|
||||
schedule:
|
||||
interval: daily
|
||||
cooldown:
|
||||
default-days: 7
|
||||
|
||||
- package-ecosystem: pip
|
||||
directory: /cmake/requirements
|
||||
schedule:
|
||||
interval: daily
|
||||
cooldown:
|
||||
default-days: 7
|
||||
|
||||
@@ -49,7 +49,7 @@ jobs:
|
||||
# SEMGREP_APP_TOKEN is still passed through so registry auth works if a
|
||||
# token is ever added.
|
||||
- name: Install Semgrep
|
||||
run: python3 -m pip install --user semgrep
|
||||
run: python3 -m pip install --user semgrep==1.168.0
|
||||
|
||||
# `semgrep scan --sarif` always exits 0 even with findings; continue-on-error
|
||||
# is a safety net so the SARIF upload still runs if the scan itself errors.
|
||||
|
||||
@@ -5,11 +5,8 @@
|
||||
# -Wno-extra-semi-stmt The library uses assert which triggers this warning.
|
||||
# -Wno-padded We do not care about padding warnings.
|
||||
# -Wno-covered-switch-default All switches list all cases and a default case.
|
||||
# -Wno-unsafe-buffer-usage Pervasive: the library's own low-level numeric/buffer code
|
||||
# (to_chars, serializer, lexer, binary reader/writer, input
|
||||
# adapters, json_pointer) plus vendored Doctest itself (~208
|
||||
# distinct sites measured 2026-07-08 on clang trunk) all use
|
||||
# raw pointer arithmetic / libc string calls by necessity.
|
||||
# -Wno-unsafe-buffer-usage Otherwise Doctest would not compile.
|
||||
# -Wno-missing-noreturn We found no way to silence this warning otherwise, see PR #4871
|
||||
|
||||
set(CLANG_CXXFLAGS
|
||||
-Werror
|
||||
@@ -21,4 +18,5 @@ set(CLANG_CXXFLAGS
|
||||
-Wno-padded
|
||||
-Wno-covered-switch-default
|
||||
-Wno-unsafe-buffer-usage
|
||||
-Wno-missing-noreturn
|
||||
)
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
User-agent: *
|
||||
Allow: /
|
||||
|
||||
Sitemap: https://json.nlohmann.me/sitemap.xml
|
||||
@@ -0,0 +1,25 @@
|
||||
"""Copy each documentation page's Markdown source into the built site."""
|
||||
|
||||
# Creates a `<path>.md` sibling of each HTML output (for example,
|
||||
# `features/comments/` becomes `features/comments.md`) so agents and tools can
|
||||
# fetch the raw Markdown directly instead of parsing rendered HTML.
|
||||
|
||||
import os
|
||||
import shutil
|
||||
|
||||
_pages = []
|
||||
|
||||
|
||||
def on_files(files, config):
|
||||
global _pages
|
||||
_pages = [f for f in files if f.is_documentation_page()]
|
||||
return files
|
||||
|
||||
|
||||
def on_post_build(config):
|
||||
site_dir = config["site_dir"]
|
||||
for file in _pages:
|
||||
url = file.url.rstrip("/")
|
||||
target = os.path.join(site_dir, (url or "index") + ".md")
|
||||
os.makedirs(os.path.dirname(target), exist_ok=True)
|
||||
shutil.copyfile(file.abs_src_path, target)
|
||||
@@ -367,6 +367,9 @@ markdown_extensions:
|
||||
auto_append:
|
||||
- ../includes/glossary.md
|
||||
|
||||
hooks:
|
||||
- hooks/copy_markdown_source.py
|
||||
|
||||
plugins:
|
||||
- search:
|
||||
separator: '[\s\-\.]'
|
||||
@@ -389,6 +392,33 @@ plugins:
|
||||
- https://nlohmann.github.io/json/*
|
||||
- mailto:*
|
||||
- privacy
|
||||
- llmstxt:
|
||||
markdown_description: >
|
||||
JSON for Modern C++ is a C++11 header-only library implementing a JSON
|
||||
value type with an STL-like API, JSON Pointer/Patch, CBOR/MessagePack/
|
||||
BSON/UBJSON/BJData binary format support, and a SAX-style parser interface.
|
||||
sections:
|
||||
Home:
|
||||
- index.md
|
||||
- home/*.md
|
||||
Features:
|
||||
- features/*.md
|
||||
- features/binary_formats/*.md
|
||||
- features/element_access/*.md
|
||||
- features/parsing/*.md
|
||||
- features/types/*.md
|
||||
Integration:
|
||||
- integration/*.md
|
||||
API Documentation:
|
||||
- api/*.md
|
||||
- api/basic_json/*.md
|
||||
- api/adl_serializer/*.md
|
||||
- api/byte_container_with_subtype/*.md
|
||||
- api/json_pointer/*.md
|
||||
- api/json_sax/*.md
|
||||
- api/macros/*.md
|
||||
Community:
|
||||
- community/*.md
|
||||
|
||||
extra_css:
|
||||
- css/custom.css
|
||||
|
||||
@@ -7,5 +7,6 @@ mkdocs-material-extensions==1.3.1 # extensions
|
||||
mkdocs-minify-plugin==0.8.0 # plugin "minify"
|
||||
mkdocs-redirects==1.2.3 # plugin "redirects"
|
||||
mkdocs-htmlproofer-plugin==1.5.0 # plugin "htmlproofer"
|
||||
mkdocs-llmstxt==0.5.0 # plugin "llmstxt"
|
||||
|
||||
PyYAML==6.0.3 # linter
|
||||
|
||||
@@ -430,7 +430,7 @@ class wide_string_input_adapter
|
||||
|
||||
// parsing binary with wchar doesn't make sense, but since the parsing mode can be runtime, we need something here
|
||||
template<class T>
|
||||
JSON_HEDLEY_NO_RETURN std::size_t get_elements(T* /*dest*/, std::size_t /*count*/ = 1)
|
||||
std::size_t get_elements(T* /*dest*/, std::size_t /*count*/ = 1)
|
||||
{
|
||||
JSON_THROW(parse_error::create(112, 1, "wide string type cannot be interpreted as binary data", nullptr));
|
||||
}
|
||||
|
||||
@@ -465,18 +465,12 @@ class serializer
|
||||
{
|
||||
if (codepoint <= 0xFFFF)
|
||||
{
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
|
||||
static_cast<void>((std::snprintf)(string_buffer.data() + bytes, 7, "\\u%04x",
|
||||
static_cast<std::uint16_t>(codepoint)));
|
||||
bytes += 6;
|
||||
write_u_escape(bytes, static_cast<std::uint16_t>(codepoint));
|
||||
}
|
||||
else
|
||||
{
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
|
||||
static_cast<void>((std::snprintf)(string_buffer.data() + bytes, 13, "\\u%04x\\u%04x",
|
||||
static_cast<std::uint16_t>(0xD7C0u + (codepoint >> 10u)),
|
||||
static_cast<std::uint16_t>(0xDC00u + (codepoint & 0x3FFu))));
|
||||
bytes += 12;
|
||||
write_u_escape(bytes, static_cast<std::uint16_t>(0xD7C0u + (codepoint >> 10u)));
|
||||
write_u_escape(bytes, static_cast<std::uint16_t>(0xDC00u + (codepoint & 0x3FFu)));
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -683,6 +677,32 @@ class serializer
|
||||
return result;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief write a lowercase "\uXXXX" escape sequence into @a string_buffer
|
||||
*
|
||||
* Branch-free replacement for `snprintf(buf, 7, "\\u%04x", codeunit)` in the
|
||||
* string escaping hot path. It writes exactly six characters ('\\', 'u' and
|
||||
* four hex digits) at position @a pos of @a string_buffer via a nibble
|
||||
* lookup table, avoiding the format-string parsing and locale machinery of
|
||||
* `snprintf`. Advances @a pos by the number of bytes written (6).
|
||||
*
|
||||
* @param[in] pos position in @a string_buffer to write at; there must
|
||||
* be at least 6 bytes of headroom
|
||||
* @param[in] codeunit 16-bit value to encode
|
||||
*/
|
||||
void write_u_escape(std::size_t& pos, std::uint16_t codeunit) noexcept
|
||||
{
|
||||
JSON_ASSERT(string_buffer.size() - pos >= 6);
|
||||
constexpr const char* nibble_to_hex = "0123456789abcdef";
|
||||
string_buffer[pos + 0] = '\\';
|
||||
string_buffer[pos + 1] = 'u';
|
||||
string_buffer[pos + 2] = nibble_to_hex[(codeunit >> 12u) & 0x0Fu];
|
||||
string_buffer[pos + 3] = nibble_to_hex[(codeunit >> 8u) & 0x0Fu];
|
||||
string_buffer[pos + 4] = nibble_to_hex[(codeunit >> 4u) & 0x0Fu];
|
||||
string_buffer[pos + 5] = nibble_to_hex[codeunit & 0x0Fu];
|
||||
pos += 6;
|
||||
}
|
||||
|
||||
// templates to avoid warnings about useless casts
|
||||
template <typename NumberType, enable_if_t<std::is_signed<NumberType>::value, int> = 0>
|
||||
bool is_negative_number(NumberType x)
|
||||
|
||||
@@ -7262,7 +7262,7 @@ class wide_string_input_adapter
|
||||
|
||||
// parsing binary with wchar doesn't make sense, but since the parsing mode can be runtime, we need something here
|
||||
template<class T>
|
||||
JSON_HEDLEY_NO_RETURN std::size_t get_elements(T* /*dest*/, std::size_t /*count*/ = 1)
|
||||
std::size_t get_elements(T* /*dest*/, std::size_t /*count*/ = 1)
|
||||
{
|
||||
JSON_THROW(parse_error::create(112, 1, "wide string type cannot be interpreted as binary data", nullptr));
|
||||
}
|
||||
@@ -19962,18 +19962,12 @@ class serializer
|
||||
{
|
||||
if (codepoint <= 0xFFFF)
|
||||
{
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
|
||||
static_cast<void>((std::snprintf)(string_buffer.data() + bytes, 7, "\\u%04x",
|
||||
static_cast<std::uint16_t>(codepoint)));
|
||||
bytes += 6;
|
||||
write_u_escape(bytes, static_cast<std::uint16_t>(codepoint));
|
||||
}
|
||||
else
|
||||
{
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
|
||||
static_cast<void>((std::snprintf)(string_buffer.data() + bytes, 13, "\\u%04x\\u%04x",
|
||||
static_cast<std::uint16_t>(0xD7C0u + (codepoint >> 10u)),
|
||||
static_cast<std::uint16_t>(0xDC00u + (codepoint & 0x3FFu))));
|
||||
bytes += 12;
|
||||
write_u_escape(bytes, static_cast<std::uint16_t>(0xD7C0u + (codepoint >> 10u)));
|
||||
write_u_escape(bytes, static_cast<std::uint16_t>(0xDC00u + (codepoint & 0x3FFu)));
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -20180,6 +20174,32 @@ class serializer
|
||||
return result;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief write a lowercase "\uXXXX" escape sequence into @a string_buffer
|
||||
*
|
||||
* Branch-free replacement for `snprintf(buf, 7, "\\u%04x", codeunit)` in the
|
||||
* string escaping hot path. It writes exactly six characters ('\\', 'u' and
|
||||
* four hex digits) at position @a pos of @a string_buffer via a nibble
|
||||
* lookup table, avoiding the format-string parsing and locale machinery of
|
||||
* `snprintf`. Advances @a pos by the number of bytes written (6).
|
||||
*
|
||||
* @param[in] pos position in @a string_buffer to write at; there must
|
||||
* be at least 6 bytes of headroom
|
||||
* @param[in] codeunit 16-bit value to encode
|
||||
*/
|
||||
void write_u_escape(std::size_t& pos, std::uint16_t codeunit) noexcept
|
||||
{
|
||||
JSON_ASSERT(string_buffer.size() - pos >= 6);
|
||||
constexpr const char* nibble_to_hex = "0123456789abcdef";
|
||||
string_buffer[pos + 0] = '\\';
|
||||
string_buffer[pos + 1] = 'u';
|
||||
string_buffer[pos + 2] = nibble_to_hex[(codeunit >> 12u) & 0x0Fu];
|
||||
string_buffer[pos + 3] = nibble_to_hex[(codeunit >> 8u) & 0x0Fu];
|
||||
string_buffer[pos + 4] = nibble_to_hex[(codeunit >> 4u) & 0x0Fu];
|
||||
string_buffer[pos + 5] = nibble_to_hex[codeunit & 0x0Fu];
|
||||
pos += 6;
|
||||
}
|
||||
|
||||
// templates to avoid warnings about useless casts
|
||||
template <typename NumberType, enable_if_t<std::is_signed<NumberType>::value, int> = 0>
|
||||
bool is_negative_number(NumberType x)
|
||||
|
||||
@@ -232,6 +232,9 @@ TEST_CASE("algorithms")
|
||||
// only the first four elements are expected to be sorted, the rest are
|
||||
// unspecified by the standard
|
||||
const json expected({nullptr, false, true, 3});
|
||||
// std::equal below only bounds-checks the first range; assert the
|
||||
// second range is at least as long to rule out an over-read (CWE-126)
|
||||
CHECK(std::distance(begin(expected), end(expected)) >= 4);
|
||||
CHECK(std::equal(j.begin(), j.begin() + 4, begin(expected)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ struct bad_allocator : std::allocator<T>
|
||||
template<class U> bad_allocator(const bad_allocator<U>& /*unused*/) { }
|
||||
|
||||
template<class... Args>
|
||||
[[noreturn]] void construct(T* /*unused*/, Args&& ... /*unused*/) // NOLINT(cppcoreguidelines-missing-std-forward)
|
||||
void construct(T* /*unused*/, Args&& ... /*unused*/) // NOLINT(cppcoreguidelines-missing-std-forward)
|
||||
{
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
|
||||
@@ -168,6 +168,32 @@ TEST_CASE("convenience functions")
|
||||
CHECK_THROWS_WITH_AS(check_escaped("\xC2"), "[json.exception.type_error.316] incomplete UTF-8 string; last byte: 0xC2", json::type_error&);
|
||||
}
|
||||
|
||||
SECTION("string escape with ensure_ascii")
|
||||
{
|
||||
// control characters are escaped regardless of ensure_ascii
|
||||
check_escaped("\x01", "\\u0001", true);
|
||||
check_escaped("\x1f", "\\u001f", true);
|
||||
|
||||
// non-ASCII code points in the Basic Multilingual Plane are emitted as
|
||||
// a single lowercase \uXXXX escape (exercises every nibble position)
|
||||
check_escaped("\xC2\x80", "\\u0080", true); // U+0080
|
||||
check_escaped("\xC3\xBF", "\\u00ff", true); // U+00FF (ÿ)
|
||||
check_escaped("\xDF\xBF", "\\u07ff", true); // U+07FF
|
||||
check_escaped("\xE4\xBD\xA0", "\\u4f60", true); // U+4F60 (你)
|
||||
check_escaped("\xEA\xAF\x8D", "\\uabcd", true); // U+ABCD
|
||||
check_escaped("\xEF\xBF\xBD", "\\ufffd", true); // U+FFFD (replacement char, all-f nibbles)
|
||||
|
||||
// code points outside the BMP are emitted as a UTF-16 surrogate pair
|
||||
// of two lowercase \uXXXX escapes
|
||||
check_escaped("\xF0\x90\x80\x80", "\\ud800\\udc00", true); // U+10000 (lowest astral)
|
||||
check_escaped("\xF0\x9F\x98\x80", "\\ud83d\\ude00", true); // U+1F600 (😀)
|
||||
check_escaped("\xF4\x8F\xBF\xBF", "\\udbff\\udfff", true); // U+10FFFF (highest code point)
|
||||
|
||||
// with ensure_ascii disabled, non-ASCII input is passed through verbatim
|
||||
check_escaped("\xE4\xBD\xA0", "\xE4\xBD\xA0", false);
|
||||
check_escaped("\xF0\x9F\x98\x80", "\xF0\x9F\x98\x80", false);
|
||||
}
|
||||
|
||||
SECTION("string concat")
|
||||
{
|
||||
using nlohmann::detail::concat;
|
||||
|
||||
Reference in New Issue
Block a user