mirror of
https://github.com/nlohmann/json.git
synced 2026-07-07 11:05:09 +00:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| feeab08982 | |||
| ce576f6316 | |||
| 4cad4e08a4 |
@@ -465,18 +465,12 @@ class serializer
|
|||||||
{
|
{
|
||||||
if (codepoint <= 0xFFFF)
|
if (codepoint <= 0xFFFF)
|
||||||
{
|
{
|
||||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
|
write_u_escape(bytes, static_cast<std::uint16_t>(codepoint));
|
||||||
static_cast<void>((std::snprintf)(string_buffer.data() + bytes, 7, "\\u%04x",
|
|
||||||
static_cast<std::uint16_t>(codepoint)));
|
|
||||||
bytes += 6;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
|
write_u_escape(bytes, static_cast<std::uint16_t>(0xD7C0u + (codepoint >> 10u)));
|
||||||
static_cast<void>((std::snprintf)(string_buffer.data() + bytes, 13, "\\u%04x\\u%04x",
|
write_u_escape(bytes, static_cast<std::uint16_t>(0xDC00u + (codepoint & 0x3FFu)));
|
||||||
static_cast<std::uint16_t>(0xD7C0u + (codepoint >> 10u)),
|
|
||||||
static_cast<std::uint16_t>(0xDC00u + (codepoint & 0x3FFu))));
|
|
||||||
bytes += 12;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -683,6 +677,32 @@ class serializer
|
|||||||
return result;
|
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
|
// templates to avoid warnings about useless casts
|
||||||
template <typename NumberType, enable_if_t<std::is_signed<NumberType>::value, int> = 0>
|
template <typename NumberType, enable_if_t<std::is_signed<NumberType>::value, int> = 0>
|
||||||
bool is_negative_number(NumberType x)
|
bool is_negative_number(NumberType x)
|
||||||
|
|||||||
@@ -19752,18 +19752,12 @@ class serializer
|
|||||||
{
|
{
|
||||||
if (codepoint <= 0xFFFF)
|
if (codepoint <= 0xFFFF)
|
||||||
{
|
{
|
||||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
|
write_u_escape(bytes, static_cast<std::uint16_t>(codepoint));
|
||||||
static_cast<void>((std::snprintf)(string_buffer.data() + bytes, 7, "\\u%04x",
|
|
||||||
static_cast<std::uint16_t>(codepoint)));
|
|
||||||
bytes += 6;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
|
write_u_escape(bytes, static_cast<std::uint16_t>(0xD7C0u + (codepoint >> 10u)));
|
||||||
static_cast<void>((std::snprintf)(string_buffer.data() + bytes, 13, "\\u%04x\\u%04x",
|
write_u_escape(bytes, static_cast<std::uint16_t>(0xDC00u + (codepoint & 0x3FFu)));
|
||||||
static_cast<std::uint16_t>(0xD7C0u + (codepoint >> 10u)),
|
|
||||||
static_cast<std::uint16_t>(0xDC00u + (codepoint & 0x3FFu))));
|
|
||||||
bytes += 12;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -19970,6 +19964,32 @@ class serializer
|
|||||||
return result;
|
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
|
// templates to avoid warnings about useless casts
|
||||||
template <typename NumberType, enable_if_t<std::is_signed<NumberType>::value, int> = 0>
|
template <typename NumberType, enable_if_t<std::is_signed<NumberType>::value, int> = 0>
|
||||||
bool is_negative_number(NumberType x)
|
bool is_negative_number(NumberType x)
|
||||||
|
|||||||
@@ -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&);
|
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")
|
SECTION("string concat")
|
||||||
{
|
{
|
||||||
using nlohmann::detail::concat;
|
using nlohmann::detail::concat;
|
||||||
|
|||||||
Reference in New Issue
Block a user