mirror of
https://github.com/nlohmann/json.git
synced 2026-10-05 14:10:31 +00:00
Address the clang-tidy findings of the view's parser
- tables as std::array; the frames of the first 64 levels stay a C array (not initialized on purpose, NOLINT) - \u escapes are decoded with the library's hex_codepoint() instead of a second table - the parse failure is private, with an accessor; the special member functions of the builder are all declared - no nested conditional operators; explicit parentheses; a repeated branch body merged; auto for casts - the test's C arrays, fixed seed, and escaped literals are marked, as in the other tests Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
@@ -10,6 +10,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <algorithm> // find, find_if, max
|
#include <algorithm> // find, find_if, max
|
||||||
|
#include <array> // array
|
||||||
#include <cstddef> // size_t, ptrdiff_t
|
#include <cstddef> // size_t, ptrdiff_t
|
||||||
#include <cstdint> // uint8_t, uint16_t, uint32_t, uint64_t
|
#include <cstdint> // uint8_t, uint16_t, uint32_t, uint64_t
|
||||||
#include <cstring> // memcmp, memcpy
|
#include <cstring> // memcmp, memcpy
|
||||||
@@ -87,8 +88,15 @@ class builder
|
|||||||
|
|
||||||
builder(const builder&) = delete;
|
builder(const builder&) = delete;
|
||||||
builder& operator=(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:
|
private:
|
||||||
struct frame
|
struct frame
|
||||||
@@ -101,16 +109,17 @@ class builder
|
|||||||
document_data& doc;
|
document_data& doc;
|
||||||
const unsigned char* const b;
|
const unsigned char* const b;
|
||||||
const unsigned char* const e;
|
const unsigned char* const e;
|
||||||
|
parse_failure m_failure{};
|
||||||
|
|
||||||
// the open array/object is in the cursor; enclosing ones on a stack that is
|
// the open array/object is in the cursor; enclosing ones on a stack that is
|
||||||
// inline for the first 64 levels
|
// 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<frame> deep{};
|
std::vector<frame> deep{};
|
||||||
|
|
||||||
NLOHMANN_VIEW_NOINLINE bool fail(error_code c, const unsigned char* at) noexcept
|
NLOHMANN_VIEW_NOINLINE bool fail(error_code c, const unsigned char* at) noexcept
|
||||||
{
|
{
|
||||||
failure.code = c;
|
m_failure.code = c;
|
||||||
failure.offset = static_cast<std::size_t>(at - b);
|
m_failure.offset = static_cast<std::size_t>(at - b);
|
||||||
doc.tape_size = 0;
|
doc.tape_size = 0;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -188,34 +197,23 @@ class builder
|
|||||||
const std::uint64_t done = static_cast<std::uint64_t>(at - b) + 1;
|
const std::uint64_t done = static_cast<std::uint64_t>(at - b) + 1;
|
||||||
const std::uint64_t guess = static_cast<std::uint64_t>(n) * static_cast<std::uint64_t>(e - b + 1) / done;
|
const std::uint64_t guess = static_cast<std::uint64_t>(n) * static_cast<std::uint64_t>(e - b + 1) / done;
|
||||||
doc.tape_size = n;
|
doc.tape_size = n;
|
||||||
doc.reserve((std::max)(static_cast<std::size_t>(guess + guess / 4 + 64), n + n / 2 + 64));
|
doc.reserve((std::max)(static_cast<std::size_t>(guess + (guess / 4) + 64), n + (n / 2) + 64));
|
||||||
return doc.tape;
|
return doc.tape;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// four hex digits at p as a code unit (p moves past them), or -1 (p at
|
/// 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,
|
/// 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
|
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))
|
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]];
|
const int cp = hex_codepoint(p);
|
||||||
if (NLOHMANN_VIEW_LIKELY(((d0 | d1 | d2 | d3) & 0xF0u) == 0))
|
if (NLOHMANN_VIEW_LIKELY(cp >= 0))
|
||||||
{
|
{
|
||||||
p += 4;
|
p += 4;
|
||||||
return static_cast<int>((d0 << 12) | (d1 << 8) | (d2 << 4) | d3);
|
return cp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
p = hex4_error(p);
|
p = hex4_error(p);
|
||||||
@@ -240,12 +238,14 @@ class builder
|
|||||||
NLOHMANN_VIEW_NOINLINE decoded slow_string(const unsigned char* s, const unsigned char* p)
|
NLOHMANN_VIEW_NOINLINE decoded slow_string(const unsigned char* s, const unsigned char* p)
|
||||||
{
|
{
|
||||||
// single-character escapes; 0: invalid (and 'u', handled separately)
|
// single-character escapes; 0: invalid (and 'u', handled separately)
|
||||||
static const char simple_escape[128] =
|
static const std::array<char, 128> 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, 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, '\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();
|
const std::size_t start = arena_used();
|
||||||
arena_run(s, static_cast<std::size_t>(p - s));
|
arena_run(s, static_cast<std::size_t>(p - s));
|
||||||
@@ -374,8 +374,8 @@ class builder
|
|||||||
{
|
{
|
||||||
const std::size_t used = arena_used();
|
const std::size_t used = arena_used();
|
||||||
doc.arena.resize((std::max)(doc.arena.size() * 2, used + n + 256));
|
doc.arena.resize((std::max)(doc.arena.size() * 2, used + n + 256));
|
||||||
aw = &doc.arena[0] + used;
|
aw = &doc.arena[0] + used; // NOLINT(readability-container-data-pointer): data() is const before C++17
|
||||||
aend = &doc.arena[0] + doc.arena.size();
|
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
|
/// append the run [r, r + n) to the arena; short runs as one fixed-size
|
||||||
@@ -670,7 +670,11 @@ root_done:
|
|||||||
/// never matches a JSON token, so the error paths tell the end apart.
|
/// never matches a JSON token, so the error paths tell the end apart.
|
||||||
NLOHMANN_VIEW_ALWAYS_INLINE unsigned char cur() const noexcept
|
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
|
/// root scalar
|
||||||
@@ -733,11 +737,7 @@ root_done:
|
|||||||
{
|
{
|
||||||
// (a branch, not an add of the comparison: p must not
|
// (a branch, not an add of the comparison: p must not
|
||||||
// wait for the byte after the line break)
|
// wait for the byte after the line break)
|
||||||
if (NLOHMANN_VIEW_LIKELY(cur() == '\n'))
|
if (NLOHMANN_VIEW_UNLIKELY(cur() == '\r') && (Sentinel || e - p >= 2) && p[1] == '\n')
|
||||||
{
|
|
||||||
++p;
|
|
||||||
}
|
|
||||||
else if ((Sentinel || e - p >= 2) && p[1] == '\n')
|
|
||||||
{
|
{
|
||||||
p += 2;
|
p += 2;
|
||||||
}
|
}
|
||||||
@@ -748,7 +748,7 @@ root_done:
|
|||||||
// indentation: two spaces per step, fixed offsets (after yyjson)
|
// indentation: two spaces per step, fixed offsets (after yyjson)
|
||||||
while (e - p >= 32)
|
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)
|
NLOHMANN_VIEW_REPEAT16(NLOHMANN_VIEW_STEP)
|
||||||
#undef NLOHMANN_VIEW_STEP
|
#undef NLOHMANN_VIEW_STEP
|
||||||
p += 32;
|
p += 32;
|
||||||
@@ -780,7 +780,7 @@ indent_done:
|
|||||||
{
|
{
|
||||||
if (NLOHMANN_VIEW_UNLIKELY(out == cap))
|
if (NLOHMANN_VIEW_UNLIKELY(out == cap))
|
||||||
{
|
{
|
||||||
const std::size_t n = static_cast<std::size_t>(out - base);
|
const auto n = static_cast<std::size_t>(out - base);
|
||||||
base = cold.grow(n, p);
|
base = cold.grow(n, p);
|
||||||
out = base + n;
|
out = base + n;
|
||||||
cap = base + cold.doc.tape_cap;
|
cap = base + cold.doc.tape_cap;
|
||||||
@@ -829,7 +829,7 @@ indent_done:
|
|||||||
n.next = static_cast<std::uint32_t>(out - base) - cur_idx;
|
n.next = static_cast<std::uint32_t>(out - base) - cur_idx;
|
||||||
if (--depth != 0)
|
if (--depth != 0)
|
||||||
{
|
{
|
||||||
frame f;
|
frame f{};
|
||||||
if (NLOHMANN_VIEW_LIKELY(depth <= 64))
|
if (NLOHMANN_VIEW_LIKELY(depth <= 64))
|
||||||
{
|
{
|
||||||
f = cold.shallow[depth - 1];
|
f = cold.shallow[depth - 1];
|
||||||
@@ -879,7 +879,7 @@ indent_done:
|
|||||||
{
|
{
|
||||||
return fail(error_code::number_after_minus);
|
return fail(error_code::number_after_minus);
|
||||||
}
|
}
|
||||||
const std::size_t int_digits = static_cast<std::size_t>(p - int_start);
|
const auto int_digits = static_cast<std::size_t>(p - int_start);
|
||||||
std::size_t frac_digits = 0;
|
std::size_t frac_digits = 0;
|
||||||
bool is_float = false;
|
bool is_float = false;
|
||||||
if (p != e && *p == '.')
|
if (p != e && *p == '.')
|
||||||
@@ -912,7 +912,7 @@ indent_done:
|
|||||||
{
|
{
|
||||||
if (exponent < 100000)
|
if (exponent < 100000)
|
||||||
{
|
{
|
||||||
exponent = exponent * 10 + (*p - '0');
|
exponent = (exponent * 10) + (*p - '0');
|
||||||
}
|
}
|
||||||
++p;
|
++p;
|
||||||
}
|
}
|
||||||
@@ -923,7 +923,11 @@ indent_done:
|
|||||||
is_float = true;
|
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)
|
if (!is_float)
|
||||||
{
|
{
|
||||||
// integers that do not fit become floats, as in parse()
|
// integers that do not fit become floats, as in parse()
|
||||||
@@ -947,19 +951,19 @@ indent_done:
|
|||||||
// float) need the conversion
|
// float) need the conversion
|
||||||
if (NLOHMANN_VIEW_UNLIKELY(static_cast<long>(int_digits) + exponent > std::numeric_limits<FloatType>::max_exponent10 - 8 && kind == value_t::number_float))
|
if (NLOHMANN_VIEW_UNLIKELY(static_cast<long>(int_digits) + exponent > std::numeric_limits<FloatType>::max_exponent10 - 8 && kind == value_t::number_float))
|
||||||
{
|
{
|
||||||
if (cold.float_overflows(s, p))
|
if (builder::float_overflows(s, p))
|
||||||
{
|
{
|
||||||
p = s;
|
p = s;
|
||||||
return fail(error_code::number_overflow);
|
return fail(error_code::number_overflow);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const auto layout = static_cast<std::uint16_t>((int_digits < 255 ? int_digits : 255) | ((frac_digits < 255 ? frac_digits : 255) << 8));
|
const auto layout = static_cast<std::uint16_t>((int_digits < 255 ? int_digits : 255) | ((frac_digits < 255 ? frac_digits : 255) << 8));
|
||||||
std::uint64_t second = static_cast<std::uint64_t>(p - s);
|
auto second = static_cast<std::uint64_t>(p - s);
|
||||||
if (kind != value_t::number_float)
|
if (kind != value_t::number_float)
|
||||||
{
|
{
|
||||||
// integers are converted now, while their digits are in cache
|
// integers are converted now, while their digits are in cache
|
||||||
const std::uint64_t m = int_digits <= 19 ? parse_upto19(int_start, static_cast<unsigned>(int_digits), e)
|
const std::uint64_t m = int_digits <= 19 ? parse_upto19(int_start, static_cast<unsigned>(int_digits), e)
|
||||||
: parse_upto19(int_start, 19, e) * 10 + static_cast<std::uint64_t>(int_start[19] - '0');
|
: (parse_upto19(int_start, 19, e) * 10) + static_cast<std::uint64_t>(int_start[19] - '0');
|
||||||
second = negative ? 0 - m : m;
|
second = negative ? 0 - m : m;
|
||||||
}
|
}
|
||||||
emit(kind, 0, layout, static_cast<std::size_t>(s - b), second);
|
emit(kind, 0, layout, static_cast<std::size_t>(s - b), second);
|
||||||
@@ -998,12 +1002,12 @@ inline bool build_with(document_data& d, const char* src, std::size_t size, bool
|
|||||||
{
|
{
|
||||||
builder<FloatType, Comments, TrailingCommas, NulIsEnd, true> bld(d, src, size);
|
builder<FloatType, Comments, TrailingCommas, NulIsEnd, true> bld(d, src, size);
|
||||||
const bool ok = bld.run();
|
const bool ok = bld.run();
|
||||||
failure = bld.failure;
|
failure = bld.failure();
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
builder<FloatType, Comments, TrailingCommas, NulIsEnd, false> bld(d, src, size);
|
builder<FloatType, Comments, TrailingCommas, NulIsEnd, false> bld(d, src, size);
|
||||||
const bool ok = bld.run();
|
const bool ok = bld.run();
|
||||||
failure = bld.failure;
|
failure = bld.failure();
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <array> // array
|
||||||
#include <cstddef> // size_t
|
#include <cstddef> // size_t
|
||||||
#include <cstring> // memcpy
|
#include <cstring> // memcpy
|
||||||
#include <new> // operator new, placement new
|
#include <new> // operator new, placement new
|
||||||
@@ -34,9 +35,9 @@ struct document_data
|
|||||||
std::size_t tape_cap = 0;
|
std::size_t tape_cap = 0;
|
||||||
node* inline_tape = nullptr; ///< node array allocated together with this header
|
node* inline_tape = nullptr; ///< node array allocated together with this header
|
||||||
std::size_t inline_cap = 0;
|
std::size_t inline_cap = 0;
|
||||||
std::string arena{}; ///< decoded strings that contained escapes
|
std::string arena{}; ///< decoded strings that contained escapes // NOLINT(readability-redundant-member-init)
|
||||||
std::string owned{}; ///< owned copy of the input, if any
|
std::string owned{}; ///< owned copy of the input, if any // NOLINT(readability-redundant-member-init)
|
||||||
const char* base[4] = {nullptr, nullptr, nullptr, nullptr}; ///< string bases: source, arena (indexed by flags & node_flags::storage)
|
std::array<const char*, 4> base = {{nullptr, nullptr, nullptr, nullptr}}; ///< string bases: source, arena (indexed by flags & node_flags::storage)
|
||||||
bool discarded = true;
|
bool discarded = true;
|
||||||
|
|
||||||
/// one allocation for the header and room for `nodes` nodes; large
|
/// one allocation for the header and room for `nodes` nodes; large
|
||||||
@@ -45,8 +46,9 @@ struct document_data
|
|||||||
{
|
{
|
||||||
nodes = nodes <= 256 ? nodes : 0;
|
nodes = nodes <= 256 ? nodes : 0;
|
||||||
void* mem = ::operator new (sizeof(document_data) + (nodes * sizeof(node)));
|
void* mem = ::operator new (sizeof(document_data) + (nodes * sizeof(node)));
|
||||||
auto* d = new (mem) document_data();
|
auto* d = new (mem) document_data(); // NOLINT(cppcoreguidelines-owning-memory): owned by the returned pointer, freed by deleter
|
||||||
d->inline_tape = static_cast<node*>(static_cast<void*>(static_cast<char*>(mem) + sizeof(document_data))); // (aligned: sizeof is a multiple of the alignment)
|
// (aligned: sizeof is a multiple of the alignment; through void*, as GCC's -Wcast-align wants)
|
||||||
|
d->inline_tape = static_cast<node*>(static_cast<void*>(static_cast<char*>(mem) + sizeof(document_data))); // NOLINT(bugprone-casting-through-void)
|
||||||
d->inline_cap = nodes;
|
d->inline_cap = nodes;
|
||||||
d->tape = d->inline_tape;
|
d->tape = d->inline_tape;
|
||||||
d->tape_cap = nodes;
|
d->tape_cap = nodes;
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <array> // array
|
||||||
#include <cstddef> // size_t
|
#include <cstddef> // size_t
|
||||||
#include <cstdint> // uint8_t, uint16_t, uint64_t
|
#include <cstdint> // uint8_t, uint16_t, uint64_t
|
||||||
#include <cstring> // memcpy
|
#include <cstring> // memcpy
|
||||||
@@ -30,18 +31,20 @@ namespace view
|
|||||||
/// 1 for bytes that may appear verbatim in a string: 0x20..0x7F except '"' and '\\'
|
/// 1 for bytes that may appear verbatim in a string: 0x20..0x7F except '"' and '\\'
|
||||||
inline const std::uint8_t* string_plain() noexcept
|
inline const std::uint8_t* string_plain() noexcept
|
||||||
{
|
{
|
||||||
static const std::uint8_t table[256] =
|
static const std::array<std::uint8_t, 256> 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 ('"')
|
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, 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, 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, 1, 1, 1, 1, // 0x60..0x7F
|
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 ('\\')
|
||||||
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
|
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, // 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, // 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, // 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, // 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, // 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, // 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
|
NLOHMANN_VIEW_ALWAYS_INLINE bool is_digit(unsigned char c) noexcept
|
||||||
@@ -133,11 +136,13 @@ NLOHMANN_VIEW_ALWAYS_INLINE const unsigned char* skip_digits(const unsigned char
|
|||||||
/// powers of ten up to 10^19 as integers
|
/// powers of ten up to 10^19 as integers
|
||||||
inline std::uint64_t int_pow10(unsigned k) noexcept
|
inline std::uint64_t int_pow10(unsigned k) noexcept
|
||||||
{
|
{
|
||||||
static const std::uint64_t table[20] =
|
static const std::array<std::uint64_t, 20> table =
|
||||||
{
|
{
|
||||||
1u, 10u, 100u, 1000u, 10000u, 100000u, 1000000u, 10000000u, 100000000u, 1000000000u,
|
{
|
||||||
10000000000u, 100000000000u, 1000000000000u, 10000000000000u, 100000000000000u, 1000000000000000u,
|
1u, 10u, 100u, 1000u, 10000u, 100000u, 1000000u, 10000000u, 100000000u, 1000000000u,
|
||||||
10000000000000000u, 100000000000000000u, 1000000000000000000u, 10000000000000000000u
|
10000000000u, 100000000000u, 1000000000000u, 10000000000000u, 100000000000000u, 1000000000000000u,
|
||||||
|
10000000000000000u, 100000000000000000u, 1000000000000000000u, 10000000000000000000u
|
||||||
|
}
|
||||||
};
|
};
|
||||||
return table[k];
|
return table[k];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -139,7 +139,7 @@ void check_same(const std::string& text)
|
|||||||
// a small deterministic generator of documents
|
// a small deterministic generator of documents
|
||||||
struct generator
|
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)
|
int r(int n)
|
||||||
{
|
{
|
||||||
@@ -156,7 +156,7 @@ struct generator
|
|||||||
|
|
||||||
void str(std::string& o)
|
void str(std::string& o)
|
||||||
{
|
{
|
||||||
static const char* const pieces[] = {"a", "Z", " ", "~", "\\n", "\\\"", "\\\\", "\\/", "\\u00e9", "\\ud83d\\ude00", "\xc3\xa9", "\xe3\x81\x82", "\xf0\x9f\x98\x80", "\x7f", "\\u001f", "long enough text to leave the first 16 bytes"};
|
static const char* const pieces[] = {"a", "Z", " ", "~", "\\n", "\\\"", "\\\\", "\\/", "\\u00e9", "\\ud83d\\ude00", "\xc3\xa9", "\xe3\x81\x82", "\xf0\x9f\x98\x80", "\x7f", "\\u001f", "long enough text to leave the first 16 bytes"}; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
||||||
o += '"';
|
o += '"';
|
||||||
for (int n = r(3) == 0 ? r(20) : r(6); n > 0; --n)
|
for (int n = r(3) == 0 ? r(20) : r(6); n > 0; --n)
|
||||||
{
|
{
|
||||||
@@ -167,7 +167,7 @@ struct generator
|
|||||||
|
|
||||||
void num(std::string& o)
|
void num(std::string& o)
|
||||||
{
|
{
|
||||||
static const char* const numbers[] = {"0", "-0", "1", "-1", "12", "123456789", "1234567890123456789", "9223372036854775807", "-9223372036854775808",
|
static const char* const numbers[] = {"0", "-0", "1", "-1", "12", "123456789", "1234567890123456789", "9223372036854775807", "-9223372036854775808", // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
||||||
"9223372036854775808", "18446744073709551615", "18446744073709551616", "-9223372036854775809",
|
"9223372036854775808", "18446744073709551615", "18446744073709551616", "-9223372036854775809",
|
||||||
"1.5", "-2.25e-3", "1e10", "1E+2", "0.000001", "3.141592653589793238462643", "1e308", "-1e-400", "123.456e7"
|
"1.5", "-2.25e-3", "1e10", "1E+2", "0.000001", "3.141592653589793238462643", "1e308", "-1e-400", "123.456e7"
|
||||||
};
|
};
|
||||||
@@ -207,7 +207,7 @@ struct generator
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
static const char* const literals[] = {"true", "false", "null"};
|
static const char* const literals[] = {"true", "false", "null"}; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
||||||
o += literals[r(3)];
|
o += literals[r(3)];
|
||||||
}
|
}
|
||||||
ws(o);
|
ws(o);
|
||||||
@@ -221,8 +221,8 @@ TEST_CASE("json_view builder")
|
|||||||
{
|
{
|
||||||
for (const char* text :
|
for (const char* text :
|
||||||
{
|
{
|
||||||
"null", "true", "false", "0", "-0", "42", "-42", "1.5", "\"\"", "\"abc\"", "[]", "{}", "[1,2,3]", "{\"a\":1,\"b\":[true,null]}",
|
"null", "true", "false", "0", "-0", "42", "-42", "1.5", "\"\"", "\"abc\"", "[]", "{}", "[1,2,3]", "{\"a\":1,\"b\":[true,null]}", // NOLINT(modernize-raw-string-literal)
|
||||||
" [ 1 , 2 ] ", "{\"a\" : {\"b\" : {}}}", "[[[]]]", "\"\\u00e4\\n\\ud83d\\ude00\"", "{\"a\":1,\"a\":2}", "18446744073709551616",
|
" [ 1 , 2 ] ", "{\"a\" : {\"b\" : {}}}", "[[[]]]", "\"\\u00e4\\n\\ud83d\\ude00\"", "{\"a\":1,\"a\":2}", "18446744073709551616", // NOLINT(modernize-raw-string-literal)
|
||||||
"-9223372036854775809", "123456789012345678901234567890", "1e400", "-1e400", "1.7976931348623157e308"
|
"-9223372036854775809", "123456789012345678901234567890", "1e400", "-1e400", "1.7976931348623157e308"
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
@@ -235,7 +235,7 @@ TEST_CASE("json_view builder")
|
|||||||
for (const char* text :
|
for (const char* text :
|
||||||
{
|
{
|
||||||
"", " ", "[", "]", "{", "}", "[1,]", "{\"a\":1,}", "[1 2]", "{\"a\" 1}", "{1:2}", "tru", "nul", "fals", "truex", "-", "01", "1.", ".5", "1e", "1e+",
|
"", " ", "[", "]", "{", "}", "[1,]", "{\"a\":1,}", "[1 2]", "{\"a\" 1}", "{1:2}", "tru", "nul", "fals", "truex", "-", "01", "1.", ".5", "1e", "1e+",
|
||||||
"\"", "\"abc", "\"\\x\"", "\"\\u12\"", "\"\\u12G4\"", "\"\\ud800\"", "\"\\udc00\"", "\"\\ud800\\u0041\"", "\"\x01\"", "\"\xff\"", "\"\xc3\"",
|
"\"", "\"abc", "\"\\x\"", "\"\\u12\"", "\"\\u12G4\"", "\"\\ud800\"", "\"\\udc00\"", "\"\\ud800\\u0041\"", "\"\x01\"", "\"\xff\"", "\"\xc3\"", // NOLINT(modernize-raw-string-literal)
|
||||||
"\"\xe0\x80\x80\"", "\"\xed\xa0\x80\"", "[1]x", "[1] [2]", "/", "/*", "/* */ 1", "// c\n1", "1 // c", "[1,/*c*/2]", "[1,2,]"
|
"\"\xe0\x80\x80\"", "\"\xed\xa0\x80\"", "[1]x", "[1] [2]", "/", "/*", "/* */ 1", "// c\n1", "1 // c", "[1,/*c*/2]", "[1,2,]"
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
@@ -297,7 +297,7 @@ TEST_CASE("json_view builder")
|
|||||||
// damage: flip one byte, or cut the text
|
// damage: flip one byte, or cut the text
|
||||||
std::string damaged = text;
|
std::string damaged = text;
|
||||||
const auto at = static_cast<std::size_t>(g.r(static_cast<int>(damaged.size())));
|
const auto at = static_cast<std::size_t>(g.r(static_cast<int>(damaged.size())));
|
||||||
static const char replacements[] = {'x', '"', '\\', ',', ':', ']', '}', '[', '{', '1', '-', '.', 'e', '\0', '\n', '/'};
|
static const char replacements[] = {'x', '"', '\\', ',', ':', ']', '}', '[', '{', '1', '-', '.', 'e', '\0', '\n', '/'}; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
||||||
damaged[at] = replacements[g.r(16)];
|
damaged[at] = replacements[g.r(16)];
|
||||||
check_same(damaged);
|
check_same(damaged);
|
||||||
check_same(text.substr(0, at));
|
check_same(text.substr(0, at));
|
||||||
|
|||||||
Reference in New Issue
Block a user