From 5f790cc27ead44aa78c584d5c6ea65423b1595ef Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 27 Oct 2026 21:12:45 +0000 Subject: [PATCH] Share the comment scanners of the view builder with materialize() skip_line_comment() and find_block_comment_end() in scan.hpp are used by the builder's comment() and by position_source::container_end(), which had its own copy. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01N5NasF5pZxP6g8W3fQfyyN --- include/nlohmann/detail/view/builder.hpp | 25 ++----- include/nlohmann/detail/view/materialize.hpp | 17 ++--- include/nlohmann/detail/view/scan.hpp | 26 ++++++++ single_include/nlohmann/json_view.hpp | 69 +++++++++++--------- 4 files changed, 75 insertions(+), 62 deletions(-) diff --git a/include/nlohmann/detail/view/builder.hpp b/include/nlohmann/detail/view/builder.hpp index aa8605966..89b5af2ab 100644 --- a/include/nlohmann/detail/view/builder.hpp +++ b/include/nlohmann/detail/view/builder.hpp @@ -173,31 +173,16 @@ class builder } if (p[1] == '/') { - p += 2; - // (as in parse(), a null byte is the end of the input, so it is - // left for the caller to see) - while (p != m_end && *p != '\n' && *p != '\r' && !(NulIsEnd && *p == 0)) - { - ++p; - } - return p; + return skip_line_comment(p + 2, m_end); } if (p[1] == '*') { - p += 2; - for (;;) + p = find_block_comment_end(p + 2, m_end); + if (p == m_end || *p != '*') { - if (p == m_end || (NulIsEnd && *p == 0)) - { - return fail_at(error_code::comment_unterminated, p); - } - if (*p == '*' && p + 1 != m_end && p[1] == '/') - { - p += 2; - return p; - } - ++p; + return fail_at(error_code::comment_unterminated, p); } + return p + 2; } ++p; return fail_at(error_code::comment_start, p); diff --git a/include/nlohmann/detail/view/materialize.hpp b/include/nlohmann/detail/view/materialize.hpp index 0b7d91d71..7bc9674f0 100644 --- a/include/nlohmann/detail/view/materialize.hpp +++ b/include/nlohmann/detail/view/materialize.hpp @@ -19,6 +19,7 @@ #include #include #include +#include NLOHMANN_JSON_NAMESPACE_BEGIN namespace detail @@ -108,6 +109,8 @@ struct position_source /// (the source was validated when it was parsed) static std::size_t container_end(const char* src, std::size_t size, std::size_t pos) noexcept { + const auto* const b = reinterpret_cast(src); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast): the text as bytes, as the builder reads it + const unsigned char* const e = b + size; while (pos < size) { switch (src[pos]) @@ -125,21 +128,13 @@ struct position_source case '/': if (pos + 1 < size && src[pos + 1] == '/') { - pos += 2; - while (pos < size && src[pos] != '\n' && src[pos] != '\r') - { - ++pos; - } + pos = static_cast(skip_line_comment(b + pos + 2, e) - b); break; } if (pos + 1 < size && src[pos + 1] == '*') { - pos += 2; - while (pos < size && !(src[pos] == '*' && pos + 1 < size && src[pos + 1] == '/')) - { - ++pos; - } - pos += 2; + // (unterminated: pos is size, which ends the loop) + pos = static_cast(find_block_comment_end(b + pos + 2, e) - b) + 2; break; } return std::string::npos; diff --git a/include/nlohmann/detail/view/scan.hpp b/include/nlohmann/detail/view/scan.hpp index e230afbdb..b8d862203 100644 --- a/include/nlohmann/detail/view/scan.hpp +++ b/include/nlohmann/detail/view/scan.hpp @@ -172,6 +172,32 @@ NLOHMANN_VIEW_ALWAYS_INLINE const unsigned char* skip_digits(const unsigned char return p; } +/// advance over the text of a line comment (after its "//") to the line +/// break or the end (with NulIsEnd also a null byte, which is left for the +/// caller to see, as in parse()) +template +inline const unsigned char* skip_line_comment(const unsigned char* p, const unsigned char* e) noexcept +{ + while (p != e && *p != '\n' && *p != '\r' && !(NulIsEnd && *p == 0)) + { + ++p; + } + return p; +} + +/// the "*/" that ends a block comment whose text starts at p (after its "/*"); +/// if it is unterminated, the end (with NulIsEnd also a null byte), which is +/// where the caller reports the error +template +inline const unsigned char* find_block_comment_end(const unsigned char* p, const unsigned char* e) noexcept +{ + while (p != e && !(NulIsEnd && *p == 0) && !(*p == '*' && p + 1 != e && p[1] == '/')) + { + ++p; + } + return p; +} + /// powers of ten up to 10^19 as integers inline std::uint64_t int_pow10(unsigned k) noexcept { diff --git a/single_include/nlohmann/json_view.hpp b/single_include/nlohmann/json_view.hpp index 41eb15940..33f2dbd07 100644 --- a/single_include/nlohmann/json_view.hpp +++ b/single_include/nlohmann/json_view.hpp @@ -1185,6 +1185,32 @@ NLOHMANN_VIEW_ALWAYS_INLINE const unsigned char* skip_digits(const unsigned char return p; } +/// advance over the text of a line comment (after its "//") to the line +/// break or the end (with NulIsEnd also a null byte, which is left for the +/// caller to see, as in parse()) +template +inline const unsigned char* skip_line_comment(const unsigned char* p, const unsigned char* e) noexcept +{ + while (p != e && *p != '\n' && *p != '\r' && !(NulIsEnd && *p == 0)) + { + ++p; + } + return p; +} + +/// the "*/" that ends a block comment whose text starts at p (after its "/*"); +/// if it is unterminated, the end (with NulIsEnd also a null byte), which is +/// where the caller reports the error +template +inline const unsigned char* find_block_comment_end(const unsigned char* p, const unsigned char* e) noexcept +{ + while (p != e && !(NulIsEnd && *p == 0) && !(*p == '*' && p + 1 != e && p[1] == '/')) + { + ++p; + } + return p; +} + /// powers of ten up to 10^19 as integers inline std::uint64_t int_pow10(unsigned k) noexcept { @@ -1382,31 +1408,16 @@ class builder } if (p[1] == '/') { - p += 2; - // (as in parse(), a null byte is the end of the input, so it is - // left for the caller to see) - while (p != m_end && *p != '\n' && *p != '\r' && !(NulIsEnd && *p == 0)) - { - ++p; - } - return p; + return skip_line_comment(p + 2, m_end); } if (p[1] == '*') { - p += 2; - for (;;) + p = find_block_comment_end(p + 2, m_end); + if (p == m_end || *p != '*') { - if (p == m_end || (NulIsEnd && *p == 0)) - { - return fail_at(error_code::comment_unterminated, p); - } - if (*p == '*' && p + 1 != m_end && p[1] == '/') - { - p += 2; - return p; - } - ++p; + return fail_at(error_code::comment_unterminated, p); } + return p + 2; } ++p; return fail_at(error_code::comment_start, p); @@ -6114,6 +6125,8 @@ NLOHMANN_JSON_NAMESPACE_END // #include +// #include + NLOHMANN_JSON_NAMESPACE_BEGIN namespace detail @@ -6203,6 +6216,8 @@ struct position_source /// (the source was validated when it was parsed) static std::size_t container_end(const char* src, std::size_t size, std::size_t pos) noexcept { + const auto* const b = reinterpret_cast(src); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast): the text as bytes, as the builder reads it + const unsigned char* const e = b + size; while (pos < size) { switch (src[pos]) @@ -6220,21 +6235,13 @@ struct position_source case '/': if (pos + 1 < size && src[pos + 1] == '/') { - pos += 2; - while (pos < size && src[pos] != '\n' && src[pos] != '\r') - { - ++pos; - } + pos = static_cast(skip_line_comment(b + pos + 2, e) - b); break; } if (pos + 1 < size && src[pos + 1] == '*') { - pos += 2; - while (pos < size && !(src[pos] == '*' && pos + 1 < size && src[pos + 1] == '/')) - { - ++pos; - } - pos += 2; + // (unterminated: pos is size, which ends the loop) + pos = static_cast(find_block_comment_end(b + pos + 2, e) - b) + 2; break; } return std::string::npos;