From e3a1a9aef2e88fb7a140ce23f5f066d20cdd91ea Mon Sep 17 00:00:00 2001 From: Andrea Cocito <39852324+puffetto@users.noreply.github.com> Date: Mon, 28 Jul 2025 10:03:59 +0200 Subject: [PATCH 1/9] Added iterators in alt_string class Signed-off-by: Andrea Cocito <39852324+puffetto@users.noreply.github.com> --- tests/src/unit-alt-string.cpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tests/src/unit-alt-string.cpp b/tests/src/unit-alt-string.cpp index fd6047c374..8d72522432 100644 --- a/tests/src/unit-alt-string.cpp +++ b/tests/src/unit-alt-string.cpp @@ -58,6 +58,26 @@ class alt_string str_impl.push_back(c); } + void shrink_to_fit() { + str_impl.shrink_to_fit(); + } + + std::string::iterator begin() { + return str_impl.begin(); + } + + std::string::const_iterator begin() const { + return str_impl.begin(); + } + + std::string::iterator end() { + return str_impl.end(); + } + + std::string::const_iterator end() const { + return str_impl.end(); + } + template bool operator==(const op_type& op) const { From 1f55457b9c29f13a12125f232ccebc3079964f66 Mon Sep 17 00:00:00 2001 From: Andrea Cocito <39852324+puffetto@users.noreply.github.com> Date: Mon, 28 Jul 2025 10:05:54 +0200 Subject: [PATCH 2/9] Make string_escape linear in execution time Signed-off-by: Andrea Cocito <39852324+puffetto@users.noreply.github.com> --- include/nlohmann/detail/string_escape.hpp | 105 +++++++++++++--------- 1 file changed, 63 insertions(+), 42 deletions(-) diff --git a/include/nlohmann/detail/string_escape.hpp b/include/nlohmann/detail/string_escape.hpp index 63715cde13..02aff6628a 100644 --- a/include/nlohmann/detail/string_escape.hpp +++ b/include/nlohmann/detail/string_escape.hpp @@ -15,58 +15,79 @@ namespace detail { /*! -@brief replace all occurrences of a substring by another string - -@param[in,out] s the string to manipulate; changed so that all - occurrences of @a f are replaced with @a t -@param[in] f the substring to replace with @a t -@param[in] t the string to replace @a f - -@pre The search string @a f must not be empty. **This precondition is -enforced with an assertion.** + * @brief Out Of Place string escaping as described in RFC 6901 (Sect. 4) + * @param[in] s string to escape + * @return escaped string + * + */ +template // [[nodiscard]] +inline StringType escape(StringType const & s) + { + using CharT = typename StringType::value_type; + StringType res; -@since version 2.0.0 -*/ -template -inline void replace_substring(StringType& s, const StringType& f, - const StringType& t) -{ - JSON_ASSERT(!f.empty()); - for (auto pos = s.find(f); // find the first occurrence of f - pos != StringType::npos; // make sure f was found - s.replace(pos, f.size(), t), // replace with t, and - pos = s.find(f, pos + t.size())) // find the next occurrence of f - {} -} + int esz=s.size(); + for (auto const ch : s) + if(ch == CharT('~') || ch == CharT('/')) + ++esz; + if(esz == s.size()) + res = s; + else { + res.reserve(esz); + for (auto const ch : s) // Yes, this is UTF8-safe + if(ch == CharT('~')) + res.append(StringType{"~0"}); + else if (ch == CharT('/')) + res.append(StringType{"~1"}); + else + res.push_back(ch); + } + return res; + } /*! - * @brief string escaping as described in RFC 6901 (Sect. 4) - * @param[in] s string to escape - * @return escaped string + * @brief In Place string unescaping as described in RFC 6901 (Sect. 4) + * @param[in] s string to unescape * - * Note the order of escaping "~" to "~0" and "/" to "~1" is important. */ -template -inline StringType escape(StringType s) -{ - replace_substring(s, StringType{"~"}, StringType{"~0"}); - replace_substring(s, StringType{"/"}, StringType{"~1"}); - return s; -} + template + inline void unescape(StringType& s) + { + using CharT = typename StringType::value_type; + auto j = s.begin(); + while(j != s.end() && *j != CharT('~')) + ++j; + auto i = j; + while(i != s.end()) { + if (*i == CharT('~') && (i + 1) != s.end()) { + if (*(i + 1) == CharT('0')) { + *j++ = CharT('~'); + ++i; + } else if (*(i + 1) == CharT('1')) { + *j++ = CharT('/'); + ++i; + } // ... else shouldn't we throw parse_error.108 here? + } else { + *j++ = *i; + } + ++i; + } + s.resize(j-s.begin()); + s.shrink_to_fit(); + } /*! - * @brief string unescaping as described in RFC 6901 (Sect. 4) + * @brief Out Of Place string unescaping as described in RFC 6901 (Sect. 4) * @param[in] s string to unescape - * @return unescaped string * - * Note the order of escaping "~1" to "/" and "~0" to "~" is important. */ -template -inline void unescape(StringType& s) -{ - replace_substring(s, StringType{"~1"}, StringType{"/"}); - replace_substring(s, StringType{"~0"}, StringType{"~"}); -} + template // [[nodiscard]] + inline StringType unescape(StringType const& s) + { + StringType res = s; + unescape(res); + return res; + } } // namespace detail NLOHMANN_JSON_NAMESPACE_END From 04c412bac5174b15903d6fa68fdbfee9ae743beb Mon Sep 17 00:00:00 2001 From: Andrea Cocito Date: Mon, 28 Jul 2025 10:28:07 +0000 Subject: [PATCH 3/9] Amalgamate changes --- include/nlohmann/detail/string_escape.hpp | 114 ++++++++++++---------- single_include/nlohmann/json.hpp | 109 ++++++++++++++------- tests/src/unit-alt-string.cpp | 15 ++- 3 files changed, 148 insertions(+), 90 deletions(-) diff --git a/include/nlohmann/detail/string_escape.hpp b/include/nlohmann/detail/string_escape.hpp index 02aff6628a..110e3eb3e8 100644 --- a/include/nlohmann/detail/string_escape.hpp +++ b/include/nlohmann/detail/string_escape.hpp @@ -21,73 +21,89 @@ namespace detail * */ template // [[nodiscard]] -inline StringType escape(StringType const & s) - { - using CharT = typename StringType::value_type; - StringType res; +inline StringType escape(StringType const& s) +{ + using CharT = typename StringType::value_type; + StringType res; - int esz=s.size(); - for (auto const ch : s) - if(ch == CharT('~') || ch == CharT('/')) - ++esz; - if(esz == s.size()) - res = s; - else { - res.reserve(esz); - for (auto const ch : s) // Yes, this is UTF8-safe - if(ch == CharT('~')) - res.append(StringType{"~0"}); - else if (ch == CharT('/')) - res.append(StringType{"~1"}); - else - res.push_back(ch); + int esz = s.size(); + for (auto const ch : s) + if (ch == CharT('~') || ch == CharT('/')) + { + ++esz; } - return res; + if (esz == s.size()) + { + res = s; } + else + { + res.reserve(esz); + for (auto const ch : s) // Yes, this is UTF8-safe + if (ch == CharT('~')) + res.append(StringType{"~0"}); + else if (ch == CharT('/')) + res.append(StringType{"~1"}); + else + { + res.push_back(ch); + } + } + return res; +} /*! * @brief In Place string unescaping as described in RFC 6901 (Sect. 4) * @param[in] s string to unescape * */ - template - inline void unescape(StringType& s) +template +inline void unescape(StringType& s) +{ + using CharT = typename StringType::value_type; + auto j = s.begin(); + while (j != s.end() && *j != CharT('~')) { - using CharT = typename StringType::value_type; - auto j = s.begin(); - while(j != s.end() && *j != CharT('~')) - ++j; - auto i = j; - while(i != s.end()) { - if (*i == CharT('~') && (i + 1) != s.end()) { - if (*(i + 1) == CharT('0')) { - *j++ = CharT('~'); - ++i; - } else if (*(i + 1) == CharT('1')) { - *j++ = CharT('/'); - ++i; - } // ... else shouldn't we throw parse_error.108 here? - } else { - *j++ = *i; - } - ++i; + ++j; + } + auto i = j; + while (i != s.end()) + { + if (*i == CharT('~') && (i + 1) != s.end()) + { + if (*(i + 1) == CharT('0')) + { + *j++ = CharT('~'); + ++i; + } + else if (*(i + 1) == CharT('1')) + { + *j++ = CharT('/'); + ++i; + } // ... else shouldn't we throw parse_error.108 here? + } + else + { + *j++ = *i; } - s.resize(j-s.begin()); - s.shrink_to_fit(); + ++i; } + s.resize(j - s.begin()); + s.shrink_to_fit(); +} /*! * @brief Out Of Place string unescaping as described in RFC 6901 (Sect. 4) * @param[in] s string to unescape * */ - template // [[nodiscard]] - inline StringType unescape(StringType const& s) - { - StringType res = s; - unescape(res); - return res; - } +template // [[nodiscard]] +inline StringType unescape(StringType const& s) +{ + StringType res = s; + unescape(res); + return res; +} } // namespace detail NLOHMANN_JSON_NAMESPACE_END diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index aafeaf5ae5..85cedba7b0 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -3085,57 +3085,94 @@ namespace detail { /*! -@brief replace all occurrences of a substring by another string - -@param[in,out] s the string to manipulate; changed so that all - occurrences of @a f are replaced with @a t -@param[in] f the substring to replace with @a t -@param[in] t the string to replace @a f - -@pre The search string @a f must not be empty. **This precondition is -enforced with an assertion.** + * @brief Out Of Place string escaping as described in RFC 6901 (Sect. 4) + * @param[in] s string to escape + * @return escaped string + * + */ +template // [[nodiscard]] +inline StringType escape(StringType const& s) +{ + using CharT = typename StringType::value_type; + StringType res; -@since version 2.0.0 -*/ -template -inline void replace_substring(StringType& s, const StringType& f, - const StringType& t) -{ - JSON_ASSERT(!f.empty()); - for (auto pos = s.find(f); // find the first occurrence of f - pos != StringType::npos; // make sure f was found - s.replace(pos, f.size(), t), // replace with t, and - pos = s.find(f, pos + t.size())) // find the next occurrence of f - {} + int esz = s.size(); + for (auto const ch : s) + if (ch == CharT('~') || ch == CharT('/')) + { + ++esz; + } + if (esz == s.size()) + { + res = s; + } + else + { + res.reserve(esz); + for (auto const ch : s) // Yes, this is UTF8-safe + if (ch == CharT('~')) + res.append(StringType{"~0"}); + else if (ch == CharT('/')) + res.append(StringType{"~1"}); + else + { + res.push_back(ch); + } + } + return res; } /*! - * @brief string escaping as described in RFC 6901 (Sect. 4) - * @param[in] s string to escape - * @return escaped string + * @brief In Place string unescaping as described in RFC 6901 (Sect. 4) + * @param[in] s string to unescape * - * Note the order of escaping "~" to "~0" and "/" to "~1" is important. */ template -inline StringType escape(StringType s) +inline void unescape(StringType& s) { - replace_substring(s, StringType{"~"}, StringType{"~0"}); - replace_substring(s, StringType{"/"}, StringType{"~1"}); - return s; + using CharT = typename StringType::value_type; + auto j = s.begin(); + while (j != s.end() && *j != CharT('~')) + { + ++j; + } + auto i = j; + while (i != s.end()) + { + if (*i == CharT('~') && (i + 1) != s.end()) + { + if (*(i + 1) == CharT('0')) + { + *j++ = CharT('~'); + ++i; + } + else if (*(i + 1) == CharT('1')) + { + *j++ = CharT('/'); + ++i; + } // ... else shouldn't we throw parse_error.108 here? + } + else + { + *j++ = *i; + } + ++i; + } + s.resize(j - s.begin()); + s.shrink_to_fit(); } /*! - * @brief string unescaping as described in RFC 6901 (Sect. 4) + * @brief Out Of Place string unescaping as described in RFC 6901 (Sect. 4) * @param[in] s string to unescape - * @return unescaped string * - * Note the order of escaping "~1" to "/" and "~0" to "~" is important. */ -template -inline void unescape(StringType& s) +template // [[nodiscard]] +inline StringType unescape(StringType const& s) { - replace_substring(s, StringType{"~1"}, StringType{"/"}); - replace_substring(s, StringType{"~0"}, StringType{"~"}); + StringType res = s; + unescape(res); + return res; } } // namespace detail diff --git a/tests/src/unit-alt-string.cpp b/tests/src/unit-alt-string.cpp index 8d72522432..8fb2934132 100644 --- a/tests/src/unit-alt-string.cpp +++ b/tests/src/unit-alt-string.cpp @@ -58,23 +58,28 @@ class alt_string str_impl.push_back(c); } - void shrink_to_fit() { + void shrink_to_fit() + { str_impl.shrink_to_fit(); } - std::string::iterator begin() { + std::string::iterator begin() + { return str_impl.begin(); } - std::string::const_iterator begin() const { + std::string::const_iterator begin() const + { return str_impl.begin(); } - std::string::iterator end() { + std::string::iterator end() + { return str_impl.end(); } - std::string::const_iterator end() const { + std::string::const_iterator end() const + { return str_impl.end(); } From 66a71d21e6ce6aeb3874475e0535c65a00599959 Mon Sep 17 00:00:00 2001 From: Andrea Cocito Date: Mon, 28 Jul 2025 12:54:13 +0000 Subject: [PATCH 4/9] Fixed (hopefully) an issue with string size sign --- include/nlohmann/detail/string_escape.hpp | 2 +- single_include/nlohmann/json.hpp | 2 +- tests/src/unit-alt-string.cpp | 5 +++++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/include/nlohmann/detail/string_escape.hpp b/include/nlohmann/detail/string_escape.hpp index 110e3eb3e8..e4feb88a60 100644 --- a/include/nlohmann/detail/string_escape.hpp +++ b/include/nlohmann/detail/string_escape.hpp @@ -88,7 +88,7 @@ inline void unescape(StringType& s) } ++i; } - s.resize(j - s.begin()); + s.erase(j, s.end()); s.shrink_to_fit(); } diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 85cedba7b0..97b6843bfa 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -3158,7 +3158,7 @@ inline void unescape(StringType& s) } ++i; } - s.resize(j - s.begin()); + s.erase(j, s.end()); s.shrink_to_fit(); } diff --git a/tests/src/unit-alt-string.cpp b/tests/src/unit-alt-string.cpp index 8fb2934132..e342734ca1 100644 --- a/tests/src/unit-alt-string.cpp +++ b/tests/src/unit-alt-string.cpp @@ -83,6 +83,11 @@ class alt_string return str_impl.end(); } + std::string::iterator erase(std::string::const_iterator first, std::string::const_iterator last ) + { + return str_impl.erase(first, last); + } + template bool operator==(const op_type& op) const { From ee410a695dd30d5eb556a6f188bf31b106e0e29e Mon Sep 17 00:00:00 2001 From: Andrea Cocito Date: Mon, 28 Jul 2025 14:29:53 +0000 Subject: [PATCH 5/9] Silenced various clang-tidy warnings --- include/nlohmann/detail/string_escape.hpp | 35 ++++++++++++++--------- single_include/nlohmann/json.hpp | 35 ++++++++++++++--------- 2 files changed, 44 insertions(+), 26 deletions(-) diff --git a/include/nlohmann/detail/string_escape.hpp b/include/nlohmann/detail/string_escape.hpp index e4feb88a60..0b4361be4a 100644 --- a/include/nlohmann/detail/string_escape.hpp +++ b/include/nlohmann/detail/string_escape.hpp @@ -28,10 +28,12 @@ inline StringType escape(StringType const& s) int esz = s.size(); for (auto const ch : s) + { if (ch == CharT('~') || ch == CharT('/')) { ++esz; } + } if (esz == s.size()) { res = s; @@ -39,15 +41,21 @@ inline StringType escape(StringType const& s) else { res.reserve(esz); - for (auto const ch : s) // Yes, this is UTF8-safe + for (auto const ch : s) // Yes, this is UTF8-safe + { if (ch == CharT('~')) + { res.append(StringType{"~0"}); + } else if (ch == CharT('/')) + { res.append(StringType{"~1"}); + } else { res.push_back(ch); } + } } return res; } @@ -92,18 +100,19 @@ inline void unescape(StringType& s) s.shrink_to_fit(); } -/*! - * @brief Out Of Place string unescaping as described in RFC 6901 (Sect. 4) - * @param[in] s string to unescape - * - */ -template // [[nodiscard]] -inline StringType unescape(StringType const& s) -{ - StringType res = s; - unescape(res); - return res; -} +// Left out, so far we don't use it, so it just lowers test coverage +// /*! +// * @brief Out Of Place string unescaping as described in RFC 6901 (Sect. 4) +// * @param[in] s string to unescape +// * +// */ +// template // [[nodiscard]] +// inline StringType unescape(StringType const& s) +// { +// StringType res = s; +// unescape(res); +// return res; +// } } // namespace detail NLOHMANN_JSON_NAMESPACE_END diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 97b6843bfa..5aadf03ab5 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -3098,10 +3098,12 @@ inline StringType escape(StringType const& s) int esz = s.size(); for (auto const ch : s) + { if (ch == CharT('~') || ch == CharT('/')) { ++esz; } + } if (esz == s.size()) { res = s; @@ -3109,15 +3111,21 @@ inline StringType escape(StringType const& s) else { res.reserve(esz); - for (auto const ch : s) // Yes, this is UTF8-safe + for (auto const ch : s) // Yes, this is UTF8-safe + { if (ch == CharT('~')) + { res.append(StringType{"~0"}); + } else if (ch == CharT('/')) + { res.append(StringType{"~1"}); + } else { res.push_back(ch); } + } } return res; } @@ -3162,18 +3170,19 @@ inline void unescape(StringType& s) s.shrink_to_fit(); } -/*! - * @brief Out Of Place string unescaping as described in RFC 6901 (Sect. 4) - * @param[in] s string to unescape - * - */ -template // [[nodiscard]] -inline StringType unescape(StringType const& s) -{ - StringType res = s; - unescape(res); - return res; -} +// Left out, so far we don't use it, so it just lowers test coverage +// /*! +// * @brief Out Of Place string unescaping as described in RFC 6901 (Sect. 4) +// * @param[in] s string to unescape +// * +// */ +// template // [[nodiscard]] +// inline StringType unescape(StringType const& s) +// { +// StringType res = s; +// unescape(res); +// return res; +// } } // namespace detail NLOHMANN_JSON_NAMESPACE_END From 7d079153f2806f985abffd7de32958fa7b942c0e Mon Sep 17 00:00:00 2001 From: Andrea Cocito Date: Mon, 28 Jul 2025 14:44:56 +0000 Subject: [PATCH 6/9] One more warning --- include/nlohmann/detail/string_escape.hpp | 2 +- single_include/nlohmann/json.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/nlohmann/detail/string_escape.hpp b/include/nlohmann/detail/string_escape.hpp index 0b4361be4a..565ee84725 100644 --- a/include/nlohmann/detail/string_escape.hpp +++ b/include/nlohmann/detail/string_escape.hpp @@ -26,7 +26,7 @@ inline StringType escape(StringType const& s) using CharT = typename StringType::value_type; StringType res; - int esz = s.size(); + auto esz = s.size(); for (auto const ch : s) { if (ch == CharT('~') || ch == CharT('/')) diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 5aadf03ab5..902c0e5e4a 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -3096,7 +3096,7 @@ inline StringType escape(StringType const& s) using CharT = typename StringType::value_type; StringType res; - int esz = s.size(); + auto esz = s.size(); for (auto const ch : s) { if (ch == CharT('~') || ch == CharT('/')) From 18ce6f7bf6eaf65b01413b588ea78f803d48df41 Mon Sep 17 00:00:00 2001 From: Andrea Cocito Date: Mon, 28 Jul 2025 14:57:03 +0000 Subject: [PATCH 7/9] And yet one more warning --- tests/src/unit-alt-string.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/src/unit-alt-string.cpp b/tests/src/unit-alt-string.cpp index e342734ca1..61d74a8984 100644 --- a/tests/src/unit-alt-string.cpp +++ b/tests/src/unit-alt-string.cpp @@ -73,12 +73,12 @@ class alt_string return str_impl.begin(); } - std::string::iterator end() + std::string::iterator end() noexcept { return str_impl.end(); } - std::string::const_iterator end() const + std::string::const_iterator end() const noexcept { return str_impl.end(); } From dc0619e504b0700b41757fcb885fd38cea5d146a Mon Sep 17 00:00:00 2001 From: Andrea Cocito Date: Tue, 29 Jul 2025 08:33:06 +0000 Subject: [PATCH 8/9] Better comments, expanded two-chars insertion --- include/nlohmann/detail/string_escape.hpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/include/nlohmann/detail/string_escape.hpp b/include/nlohmann/detail/string_escape.hpp index 565ee84725..f79d1a3b0b 100644 --- a/include/nlohmann/detail/string_escape.hpp +++ b/include/nlohmann/detail/string_escape.hpp @@ -15,7 +15,7 @@ namespace detail { /*! - * @brief Out Of Place string escaping as described in RFC 6901 (Sect. 4) + * @brief Returns a copy of a string escaped as described in RFC 6901 (Sect. 4) * @param[in] s string to escape * @return escaped string * @@ -45,11 +45,13 @@ inline StringType escape(StringType const& s) { if (ch == CharT('~')) { - res.append(StringType{"~0"}); + res.push_back(CharT('~')); + res.push_back(CharT{'0'}); } else if (ch == CharT('/')) { - res.append(StringType{"~1"}); + res.push_back(CharT{'~'}); + res.push_back(CharT{'1'}); } else { @@ -61,7 +63,7 @@ inline StringType escape(StringType const& s) } /*! - * @brief In Place string unescaping as described in RFC 6901 (Sect. 4) + * @brief Unescapes a string as described in RFC 6901 (Sect. 4), in-place * @param[in] s string to unescape * */ From f81406f9d9c8200fa229feb2f4915c989192cf8a Mon Sep 17 00:00:00 2001 From: Andrea Cocito Date: Tue, 29 Jul 2025 08:34:20 +0000 Subject: [PATCH 9/9] make amalgamate --- include/nlohmann/detail/string_escape.hpp | 2 +- single_include/nlohmann/json.hpp | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/include/nlohmann/detail/string_escape.hpp b/include/nlohmann/detail/string_escape.hpp index f79d1a3b0b..36e76bd0fd 100644 --- a/include/nlohmann/detail/string_escape.hpp +++ b/include/nlohmann/detail/string_escape.hpp @@ -46,7 +46,7 @@ inline StringType escape(StringType const& s) if (ch == CharT('~')) { res.push_back(CharT('~')); - res.push_back(CharT{'0'}); + res.push_back(CharT{'0'}); } else if (ch == CharT('/')) { diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 902c0e5e4a..aceb21bc4c 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -3085,7 +3085,7 @@ namespace detail { /*! - * @brief Out Of Place string escaping as described in RFC 6901 (Sect. 4) + * @brief Returns a copy of a string escaped as described in RFC 6901 (Sect. 4) * @param[in] s string to escape * @return escaped string * @@ -3115,11 +3115,13 @@ inline StringType escape(StringType const& s) { if (ch == CharT('~')) { - res.append(StringType{"~0"}); + res.push_back(CharT('~')); + res.push_back(CharT{'0'}); } else if (ch == CharT('/')) { - res.append(StringType{"~1"}); + res.push_back(CharT{'~'}); + res.push_back(CharT{'1'}); } else { @@ -3131,7 +3133,7 @@ inline StringType escape(StringType const& s) } /*! - * @brief In Place string unescaping as described in RFC 6901 (Sect. 4) + * @brief Unescapes a string as described in RFC 6901 (Sect. 4), in-place * @param[in] s string to unescape * */