From aa1a0c75618ab7edfd33a2f36c9a155efba486a9 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Tue, 29 Sep 2026 00:36:57 +0200 Subject: [PATCH] Address the clang-tidy findings of json_document and json_view - the input dispatch takes byte ranges by const reference and reads the size once (which also settles a finding of the static analyzer); input adapters are taken by value - the classification of inputs keeps its nested conditional operators, a constant expression of C++11 (NOLINT) - the test's C arrays, fixed seed, and escaped literals are marked, as in the other tests Signed-off-by: Niels Lohmann --- include/nlohmann/detail/view/input.hpp | 4 +- include/nlohmann/json_view.hpp | 17 +-- single_include/nlohmann/json_view.hpp | 164 ++++++++++++++----------- tests/src/unit-json_view.cpp | 18 +-- 4 files changed, 111 insertions(+), 92 deletions(-) diff --git a/include/nlohmann/detail/view/input.hpp b/include/nlohmann/detail/view/input.hpp index c43979f1f..e8a5e3148 100644 --- a/include/nlohmann/detail/view/input.hpp +++ b/include/nlohmann/detail/view/input.hpp @@ -48,6 +48,7 @@ struct classify_input #else static constexpr bool is_string_view = false; #endif + // NOLINTBEGIN(readability-avoid-nested-conditional-operator): a constant expression of C++11 static constexpr input_kind value = std::is_array::value ? input_kind::char_array : std::is_pointer::value ? input_kind::c_string @@ -55,6 +56,7 @@ struct classify_input : (is_bytes && (!is_rvalue || is_string_view)) ? input_kind::borrow_range : is_bytes ? input_kind::copy_range : input_kind::adapter; + // NOLINTEND(readability-avoid-nested-conditional-operator) }; /// std::basic_string guarantees a NUL at data()[size()] (the parser's sentinel) @@ -66,7 +68,7 @@ struct is_std_string> : std::true_type {} /// drain a json input adapter (UTF-16/32 inputs arrive as UTF-8) template -std::string collect_adapter(Adapter&& ia) +std::string collect_adapter(Adapter ia) { std::string buf; for (;;) diff --git a/include/nlohmann/json_view.hpp b/include/nlohmann/json_view.hpp index 114f2cf70..614f093ec 100644 --- a/include/nlohmann/json_view.hpp +++ b/include/nlohmann/json_view.hpp @@ -396,7 +396,7 @@ class basic_json_document { return; } - using node = detail::view::node; + using detail::view::node; document_data& d = *m_data; // allocate everything first, so that an exception leaves the document @@ -511,15 +511,16 @@ class basic_json_document } template - void read_kind(T&& s, bool ae, bool c, bool tc, std::integral_constant /*unused*/) + void read_kind(const T& s, bool ae, bool c, bool tc, std::integral_constant /*unused*/) { // std::basic_string guarantees data()[size()] == 0: use it as sentinel - build(s.size() == 0 ? "" : reinterpret_cast(s.data()), static_cast(s.size()), ae, c, tc, false, // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) - detail::view::is_std_string::type>::value || s.size() == 0); + const auto size = static_cast(s.size()); + build(size == 0 ? "" : reinterpret_cast(s.data()), size, ae, c, tc, false, // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) + detail::view::is_std_string::value || size == 0); } template - void read_kind(T&& s, bool ae, bool c, bool tc, std::integral_constant /*unused*/) + void read_kind(const T& s, bool ae, bool c, bool tc, std::integral_constant /*unused*/) { build_owned(std::string(reinterpret_cast(s.data()), static_cast(s.size())), ae, c, tc); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) } @@ -561,9 +562,9 @@ class basic_json_document } template - static std::string collect_impl(T&& s, std::true_type /*contiguous*/) + static std::string collect_impl(const T& s, std::true_type /*contiguous*/) { - return std::string(reinterpret_cast(s.data()), static_cast(s.size())); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) + return {reinterpret_cast(s.data()), static_cast(s.size())}; // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) } template @@ -572,7 +573,7 @@ class basic_json_document return detail::view::collect_adapter(detail::input_adapter(std::forward(s))); } - std::unique_ptr m_data{}; + std::unique_ptr m_data{}; // NOLINT(readability-redundant-member-init) }; /// a parsed JSON text for json diff --git a/single_include/nlohmann/json_view.hpp b/single_include/nlohmann/json_view.hpp index 0d91d9a59..fd87291d9 100644 --- a/single_include/nlohmann/json_view.hpp +++ b/single_include/nlohmann/json_view.hpp @@ -52,6 +52,7 @@ #include // max +#include // array #include // size_t, ptrdiff_t #include // uint8_t, uint16_t, uint32_t, uint64_t #include // memcmp, memcpy @@ -71,6 +72,7 @@ +#include // array #include // size_t #include // memcpy #include // operator new, placement new @@ -130,7 +132,9 @@ #define NLOHMANN_VIEW_THROW(exception) throw exception #else #include - #define NLOHMANN_VIEW_THROW(exception) std::abort() + // (the exception is built first, so that the arguments of the throwing + // helpers count as used; the program ends anyway) + #define NLOHMANN_VIEW_THROW(exception) (static_cast(exception), std::abort()) #endif #if defined(JSON_THROW_USER) #undef NLOHMANN_VIEW_THROW @@ -266,9 +270,9 @@ struct document_data std::size_t tape_cap = 0; node* inline_tape = nullptr; ///< node array allocated together with this header std::size_t inline_cap = 0; - std::string arena{}; ///< decoded strings that contained escapes - std::string owned{}; ///< owned copy of the input, if any - const char* base[4] = {nullptr, nullptr, nullptr, nullptr}; ///< string bases: source, arena (indexed by flags & node_flags::storage) + std::string arena{}; ///< decoded strings that contained escapes // NOLINT(readability-redundant-member-init) + std::string owned{}; ///< owned copy of the input, if any // NOLINT(readability-redundant-member-init) + std::array base = {{nullptr, nullptr, nullptr, nullptr}}; ///< string bases: source, arena (indexed by flags & node_flags::storage) bool discarded = true; /// one allocation for the header and room for `nodes` nodes; large @@ -277,8 +281,9 @@ struct document_data { nodes = nodes <= 256 ? nodes : 0; void* mem = ::operator new (sizeof(document_data) + (nodes * sizeof(node))); - auto* d = new (mem) document_data(); - d->inline_tape = static_cast(static_cast(static_cast(mem) + sizeof(document_data))); // (aligned: sizeof is a multiple of the alignment) + auto* d = new (mem) document_data(); // NOLINT(cppcoreguidelines-owning-memory): owned by the returned pointer, freed by deleter + // (aligned: sizeof is a multiple of the alignment; through void*, as GCC's -Wcast-align wants) + d->inline_tape = static_cast(static_cast(static_cast(mem) + sizeof(document_data))); // NOLINT(bugprone-casting-through-void) d->inline_cap = nodes; d->tape = d->inline_tape; d->tape_cap = nodes; @@ -375,6 +380,7 @@ NLOHMANN_JSON_NAMESPACE_END +#include // array #include // size_t #include // uint8_t, uint16_t, uint64_t #include // memcpy @@ -397,18 +403,20 @@ namespace view /// 1 for bytes that may appear verbatim in a string: 0x20..0x7F except '"' and '\\' inline const std::uint8_t* string_plain() noexcept { - static const std::uint8_t table[256] = + static const std::array table = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x00..0x1F - 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x20..0x3F ('"') - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, // 0x40..0x5F ('\\') - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x60..0x7F - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x80..0x9F - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xA0..0xBF - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xC0..0xDF - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xE0..0xFF + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x00..0x1F + 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x20..0x3F ('"') + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, // 0x40..0x5F ('\\') + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x60..0x7F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x80..0x9F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xA0..0xBF + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xC0..0xDF + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xE0..0xFF + } }; - return table; + return table.data(); } NLOHMANN_VIEW_ALWAYS_INLINE bool is_digit(unsigned char c) noexcept @@ -500,11 +508,13 @@ NLOHMANN_VIEW_ALWAYS_INLINE const unsigned char* skip_digits(const unsigned char /// powers of ten up to 10^19 as integers inline std::uint64_t int_pow10(unsigned k) noexcept { - static const std::uint64_t table[20] = + static const std::array table = { - 1u, 10u, 100u, 1000u, 10000u, 100000u, 1000000u, 10000000u, 100000000u, 1000000000u, - 10000000000u, 100000000000u, 1000000000000u, 10000000000000u, 100000000000000u, 1000000000000000u, - 10000000000000000u, 100000000000000000u, 1000000000000000000u, 10000000000000000000u + { + 1u, 10u, 100u, 1000u, 10000u, 100000u, 1000000u, 10000000u, 100000000u, 1000000000u, + 10000000000u, 100000000000u, 1000000000000u, 10000000000000u, 100000000000000u, 1000000000000000u, + 10000000000000000u, 100000000000000000u, 1000000000000000000u, 10000000000000000000u + } }; return table[k]; } @@ -616,8 +626,15 @@ class builder builder(const builder&) = delete; builder& operator=(const builder&) = delete; + builder(builder&&) = delete; + builder& operator=(builder&&) = delete; + ~builder() = default; - parse_failure failure{}; + /// where and why the parse failed (after run() returned false) + const parse_failure& failure() const noexcept + { + return m_failure; + } private: struct frame @@ -630,16 +647,17 @@ class builder document_data& doc; const unsigned char* const b; const unsigned char* const e; + parse_failure m_failure{}; // the open array/object is in the cursor; enclosing ones on a stack that is // inline for the first 64 levels - frame shallow[64]; + frame shallow[64]; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays): not initialized on purpose; filled as containers open std::vector deep{}; NLOHMANN_VIEW_NOINLINE bool fail(error_code c, const unsigned char* at) noexcept { - failure.code = c; - failure.offset = static_cast(at - b); + m_failure.code = c; + m_failure.offset = static_cast(at - b); doc.tape_size = 0; return false; } @@ -717,34 +735,23 @@ class builder const std::uint64_t done = static_cast(at - b) + 1; const std::uint64_t guess = static_cast(n) * static_cast(e - b + 1) / done; doc.tape_size = n; - doc.reserve((std::max)(static_cast(guess + guess / 4 + 64), n + n / 2 + 64)); + doc.reserve((std::max)(static_cast(guess + (guess / 4) + 64), n + (n / 2) + 64)); return doc.tape; } /// four hex digits at p as a code unit (p moves past them), or -1 (p at /// the first bad digit); one table lookup per digit and a single check, - /// as in yyjson's read_hex_u16 + /// the four hex digits of a unicode escape (the library's table, after + /// yyjson's read_hex_u16), or -1 NLOHMANN_VIEW_ALWAYS_INLINE int hex4(const unsigned char*& p) noexcept { - // digit values; 0xF0: not a hex digit - static const std::uint8_t hex[256] = - { - 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, - 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, - 0xF0, 10, 11, 12, 13, 14, 15, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, - 0xF0, 10, 11, 12, 13, 14, 15, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, - 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, - 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, - 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, - 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, - }; if (NLOHMANN_VIEW_LIKELY(e - p >= 4)) { - const unsigned d0 = hex[p[0]], d1 = hex[p[1]], d2 = hex[p[2]], d3 = hex[p[3]]; - if (NLOHMANN_VIEW_LIKELY(((d0 | d1 | d2 | d3) & 0xF0u) == 0)) + const int cp = hex_codepoint(p); + if (NLOHMANN_VIEW_LIKELY(cp >= 0)) { p += 4; - return static_cast((d0 << 12) | (d1 << 8) | (d2 << 4) | d3); + return cp; } } p = hex4_error(p); @@ -769,12 +776,14 @@ class builder NLOHMANN_VIEW_NOINLINE decoded slow_string(const unsigned char* s, const unsigned char* p) { // single-character escapes; 0: invalid (and 'u', handled separately) - static const char simple_escape[128] = + static const std::array simple_escape = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, '"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '/', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '\\', 0, 0, 0, - 0, 0, '\b', 0, 0, 0, '\f', 0, 0, 0, 0, 0, 0, 0, '\n', 0, 0, 0, '\r', 0, '\t', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, '"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '/', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '\\', 0, 0, 0, + 0, 0, '\b', 0, 0, 0, '\f', 0, 0, 0, 0, 0, 0, 0, '\n', 0, 0, 0, '\r', 0, '\t', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + } }; const std::size_t start = arena_used(); arena_run(s, static_cast(p - s)); @@ -902,8 +911,8 @@ class builder { const std::size_t used = arena_used(); doc.arena.resize((std::max)(doc.arena.size() * 2, used + n + 256)); - aw = &doc.arena[0] + used; - aend = &doc.arena[0] + doc.arena.size(); + aw = &doc.arena[0] + used; // NOLINT(readability-container-data-pointer): data() is const before C++17 + aend = &doc.arena[0] + doc.arena.size(); // NOLINT(readability-container-data-pointer) } /// append the run [r, r + n) to the arena; short runs as one fixed-size @@ -1198,7 +1207,11 @@ root_done: /// never matches a JSON token, so the error paths tell the end apart. NLOHMANN_VIEW_ALWAYS_INLINE unsigned char cur() const noexcept { - return Sentinel ? *p : (p != e ? *p : 0); + if (Sentinel) + { + return *p; + } + return p != e ? *p : 0; } /// root scalar @@ -1261,11 +1274,7 @@ root_done: { // (a branch, not an add of the comparison: p must not // wait for the byte after the line break) - if (NLOHMANN_VIEW_LIKELY(cur() == '\n')) - { - ++p; - } - else if ((Sentinel || e - p >= 2) && p[1] == '\n') + if (NLOHMANN_VIEW_UNLIKELY(cur() == '\r') && (Sentinel || e - p >= 2) && p[1] == '\n') { p += 2; } @@ -1276,7 +1285,7 @@ root_done: // indentation: two spaces per step, fixed offsets (after yyjson) while (e - p >= 32) { -#define NLOHMANN_VIEW_STEP(i) if (NLOHMANN_VIEW_LIKELY(load16(p + 2 * (i)) == 0x2020)) {} else { p += 2 * (i); goto indent_done; } +#define NLOHMANN_VIEW_STEP(i) if (NLOHMANN_VIEW_LIKELY(load16(p + (std::ptrdiff_t{2} * (i))) == 0x2020)) {} else { p += std::ptrdiff_t{2} * (i); goto indent_done; } NLOHMANN_VIEW_REPEAT16(NLOHMANN_VIEW_STEP) #undef NLOHMANN_VIEW_STEP p += 32; @@ -1308,7 +1317,7 @@ indent_done: { if (NLOHMANN_VIEW_UNLIKELY(out == cap)) { - const std::size_t n = static_cast(out - base); + const auto n = static_cast(out - base); base = cold.grow(n, p); out = base + n; cap = base + cold.doc.tape_cap; @@ -1357,7 +1366,7 @@ indent_done: n.next = static_cast(out - base) - cur_idx; if (--depth != 0) { - frame f; + frame f{}; if (NLOHMANN_VIEW_LIKELY(depth <= 64)) { f = cold.shallow[depth - 1]; @@ -1407,7 +1416,7 @@ indent_done: { return fail(error_code::number_after_minus); } - const std::size_t int_digits = static_cast(p - int_start); + const auto int_digits = static_cast(p - int_start); std::size_t frac_digits = 0; bool is_float = false; if (p != e && *p == '.') @@ -1440,7 +1449,7 @@ indent_done: { if (exponent < 100000) { - exponent = exponent * 10 + (*p - '0'); + exponent = (exponent * 10) + (*p - '0'); } ++p; } @@ -1451,7 +1460,11 @@ indent_done: is_float = true; } - value_t kind = is_float ? value_t::number_float : (negative ? value_t::number_integer : value_t::number_unsigned); + value_t kind = value_t::number_float; + if (!is_float) + { + kind = negative ? value_t::number_integer : value_t::number_unsigned; + } if (!is_float) { // integers that do not fit become floats, as in parse() @@ -1474,19 +1487,19 @@ indent_done: // could reach 1e308 need the conversion if (NLOHMANN_VIEW_UNLIKELY(static_cast(int_digits) + exponent > 300 && kind == value_t::number_float)) { - if (cold.float_overflows(s, p)) + if (builder::float_overflows(s, p)) { p = s; return fail(error_code::number_overflow); } } const auto layout = static_cast((int_digits < 255 ? int_digits : 255) | ((frac_digits < 255 ? frac_digits : 255) << 8)); - std::uint64_t second = static_cast(p - s); + auto second = static_cast(p - s); if (kind != value_t::number_float) { // integers are converted now, while their digits are in cache const std::uint64_t m = int_digits <= 19 ? parse_upto19(int_start, static_cast(int_digits), e) - : parse_upto19(int_start, 19, e) * 10 + static_cast(int_start[19] - '0'); + : (parse_upto19(int_start, 19, e) * 10) + static_cast(int_start[19] - '0'); second = negative ? 0 - m : m; } emit(kind, 0, layout, static_cast(s - b), second); @@ -1525,12 +1538,12 @@ inline bool build_with(document_data& d, const char* src, std::size_t size, bool { builder bld(d, src, size); const bool ok = bld.run(); - failure = bld.failure; + failure = bld.failure(); return ok; } builder bld(d, src, size); const bool ok = bld.run(); - failure = bld.failure; + failure = bld.failure(); return ok; } @@ -1690,6 +1703,7 @@ struct classify_input #else static constexpr bool is_string_view = false; #endif + // NOLINTBEGIN(readability-avoid-nested-conditional-operator): a constant expression of C++11 static constexpr input_kind value = std::is_array::value ? input_kind::char_array : std::is_pointer::value ? input_kind::c_string @@ -1697,6 +1711,7 @@ struct classify_input : (is_bytes && (!is_rvalue || is_string_view)) ? input_kind::borrow_range : is_bytes ? input_kind::copy_range : input_kind::adapter; + // NOLINTEND(readability-avoid-nested-conditional-operator) }; /// std::basic_string guarantees a NUL at data()[size()] (the parser's sentinel) @@ -1708,7 +1723,7 @@ struct is_std_string> : std::true_type {} /// drain a json input adapter (UTF-16/32 inputs arrive as UTF-8) template -std::string collect_adapter(Adapter&& ia) +std::string collect_adapter(Adapter ia) { std::string buf; for (;;) @@ -2405,7 +2420,7 @@ class basic_json_document { return; } - using node = detail::view::node; + using detail::view::node; document_data& d = *m_data; // allocate everything first, so that an exception leaves the document @@ -2520,15 +2535,16 @@ class basic_json_document } template - void read_kind(T&& s, bool ae, bool c, bool tc, std::integral_constant /*unused*/) + void read_kind(const T& s, bool ae, bool c, bool tc, std::integral_constant /*unused*/) { // std::basic_string guarantees data()[size()] == 0: use it as sentinel - build(s.size() == 0 ? "" : reinterpret_cast(s.data()), static_cast(s.size()), ae, c, tc, false, // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) - detail::view::is_std_string::type>::value || s.size() == 0); + const auto size = static_cast(s.size()); + build(size == 0 ? "" : reinterpret_cast(s.data()), size, ae, c, tc, false, // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) + detail::view::is_std_string::value || size == 0); } template - void read_kind(T&& s, bool ae, bool c, bool tc, std::integral_constant /*unused*/) + void read_kind(const T& s, bool ae, bool c, bool tc, std::integral_constant /*unused*/) { build_owned(std::string(reinterpret_cast(s.data()), static_cast(s.size())), ae, c, tc); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) } @@ -2570,9 +2586,9 @@ class basic_json_document } template - static std::string collect_impl(T&& s, std::true_type /*contiguous*/) + static std::string collect_impl(const T& s, std::true_type /*contiguous*/) { - return std::string(reinterpret_cast(s.data()), static_cast(s.size())); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) + return {reinterpret_cast(s.data()), static_cast(s.size())}; // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) } template @@ -2581,7 +2597,7 @@ class basic_json_document return detail::view::collect_adapter(detail::input_adapter(std::forward(s))); } - std::unique_ptr m_data{}; + std::unique_ptr m_data{}; // NOLINT(readability-redundant-member-init) }; /// a parsed JSON text for json diff --git a/tests/src/unit-json_view.cpp b/tests/src/unit-json_view.cpp index 36bc7c57e..eda6db9fe 100644 --- a/tests/src/unit-json_view.cpp +++ b/tests/src/unit-json_view.cpp @@ -61,7 +61,7 @@ std::string view_exception(const std::string& text, bool comments = false, bool // a small deterministic generator of documents struct generator { - std::mt19937 rng{5295}; + std::mt19937 rng{5295}; // NOLINT(cert-msc32-c,cert-msc51-cpp,bugprone-random-generator-seed) int r(int n) { @@ -70,7 +70,7 @@ struct generator void str(std::string& o) { - static const char* const pieces[] = {"a", "Z", " ", "\\n", "\\\"", "\\u00e9", "\\ud83d\\ude00", "\xc3\xa9", "\xe3\x81\x82", "long text beyond the first sixteen bytes"}; + static const char* const pieces[] = {"a", "Z", " ", "\\n", "\\\"", "\\u00e9", "\\ud83d\\ude00", "\xc3\xa9", "\xe3\x81\x82", "long text beyond the first sixteen bytes"}; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) o += '"'; for (int n = r(5); n > 0; --n) { @@ -81,7 +81,7 @@ struct generator void value(std::string& o, int depth) { - static const char* const scalars[] = {"0", "-1", "123456789012", "18446744073709551615", "18446744073709551616", "-9223372036854775809", + static const char* const scalars[] = {"0", "-1", "123456789012", "18446744073709551615", "18446744073709551616", "-9223372036854775809", // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) "1.5", "-2.25e-3", "1E2", "0.1", "true", "false", "null" }; const int k = depth > 5 ? 2 + r(4) : r(6); @@ -119,7 +119,7 @@ TEST_CASE("json_view") { for (const char* text : {"null", "true", "false", "0", "-1", "18446744073709551615", "-9223372036854775808", "18446744073709551616", "1.5", - "\"\"", "\"text\"", "[]", "[1,2,3]", "{}", "{\"a\":1,\"b\":2}" + "\"\"", "\"text\"", "[]", "[1,2,3]", "{}", "{\"a\":1,\"b\":2}" // NOLINT(modernize-raw-string-literal) }) { CAPTURE(text); @@ -186,7 +186,7 @@ TEST_CASE("json_view") for (const char* text : { "", " ", "[", "]", "{", "[1,]", "{\"a\":1,}", "[1 2]", "{\"a\" 1}", "{1:2}", "tru", "nul", "fals", "truex", "-", "01", "1.", ".5", "1e", - "\"", "\"abc", "\"\\x\"", "\"\\u12\"", "\"\\ud800\"", "\"\\udc00\"", "\"\x01\"", "\"\xff\"", "\"\xc3\"", "[1]x", "/", "/*", "[\n 1,\n x\n]", + "\"", "\"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]}}" }) { @@ -324,7 +324,7 @@ TEST_CASE("json_view") { big += (i != 0 ? ",\"" : "\"") + std::to_string(i) + "\""; } - big += "]"; + big += ']'; json_document d = json_document::parse(big); CHECK(d.node_count() == 10001); const std::size_t before = d.memory_usage(); @@ -342,9 +342,9 @@ TEST_CASE("json_view") { escaped += (i != 0 ? ",\"a\\n" : "\"a\\n") + std::to_string(i) + "\""; } - escaped += "]"; + escaped += ']'; json_document reused = json_document::parse(escaped); - const std::string smaller = "[\"x\\ty\", [true, \"\\u00e4\"]]"; + const std::string smaller = "[\"x\\ty\", [true, \"\\u00e4\"]]"; // NOLINT(modernize-raw-string-literal) reused.read(smaller); const std::size_t grown = reused.memory_usage(); reused.shrink_to_fit(); @@ -352,7 +352,7 @@ TEST_CASE("json_view") CHECK(reused.root().materialize() == json::parse(smaller)); // a small document stays in the storage block of the header - json_document small = json_document::parse("[1,[2,3],{\"a\":\"b\\n\"}]"); + json_document small = json_document::parse("[1,[2,3],{\"a\":\"b\\n\"}]"); // NOLINT(modernize-raw-string-literal) small.shrink_to_fit(); CHECK(small.root().materialize() == json::parse("[1,[2,3],{\"a\":\"b\\n\"}]")); }