Grow the decode arena through a function and store its position back on every return
slow_string() kept its write position in locals and grew the arena through the macro NLOHMANN_VIEW_ENSURE. The macro is now the always-inline member arena_ensure() (a lambda is not inlined at every call site by clang, which then keeps the position in memory: +11 % instructions on escape-heavy strings), and a guard stores the position back into m_arena_write on every return instead of by hand on the successful one. Instruction counts are unchanged (clang) or 1 % lower (GCC). Also word the complexity of back() as that of at(). Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
3 files changed
+109
-35
No files matched your search
@@ -28,7 +28,7 @@ This exception does not carry a [`JSON_DIAGNOSTICS`](../macros/json_diagnostics.
|
|||||||
|
|
||||||
Linear in the size of the array or object: unlike [`front()`](front.md), which only ever looks at the first
|
Linear in the size of the array or object: unlike [`front()`](front.md), which only ever looks at the first
|
||||||
element, `back()` has to walk every element to find where they end, since elements are not a fixed size in the
|
element, `back()` has to walk every element to find where they end, since elements are not a fixed size in the
|
||||||
index. Constant for an array none of whose elements is an array or object, since the elements are then adjacent
|
index. Constant if no element of the array is an array or object, since the elements are then adjacent
|
||||||
in the index.
|
in the index.
|
||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
|
|||||||
@@ -277,26 +277,25 @@ class builder
|
|||||||
};
|
};
|
||||||
// The write position is kept in locals: the stores of characters into
|
// The write position is kept in locals: the stores of characters into
|
||||||
// the arena may alias the members of the builder, which would then be
|
// the arena may alias the members of the builder, which would then be
|
||||||
// reloaded after every one. It is stored back where the arena grows
|
// reloaded after every one. It is stored back where the arena grows,
|
||||||
// and at the end.
|
// and by `write_back` on every return.
|
||||||
const unsigned char* const end = m_end;
|
const unsigned char* const end = m_end;
|
||||||
const std::size_t start = arena_used();
|
const std::size_t start = arena_used();
|
||||||
char* w = m_arena_write;
|
char* w = m_arena_write;
|
||||||
const char* w_end = m_arena_end;
|
const char* w_end = m_arena_end;
|
||||||
#define NLOHMANN_VIEW_ENSURE(n) \
|
const arena_write_back write_back(*this, w);
|
||||||
if (NLOHMANN_VIEW_UNLIKELY(static_cast<std::size_t>(w_end - w) < (n))) { m_arena_write = w; arena_grow(n); w = m_arena_write; w_end = m_arena_end; }
|
|
||||||
{
|
{
|
||||||
const auto n = static_cast<std::size_t>(p - s);
|
const auto n = static_cast<std::size_t>(p - s);
|
||||||
if (n <= 16 && end - s >= 16)
|
if (n <= 16 && end - s >= 16)
|
||||||
{
|
{
|
||||||
NLOHMANN_VIEW_ENSURE(16)
|
arena_ensure(w, w_end, 16);
|
||||||
std::memcpy(w, s, 16);
|
std::memcpy(w, s, 16);
|
||||||
}
|
}
|
||||||
else if (n != 0)
|
else if (n != 0)
|
||||||
{
|
{
|
||||||
// (an empty run leaves an empty arena unallocated, and memcpy()
|
// (an empty run leaves an empty arena unallocated, and memcpy()
|
||||||
// must not be passed its null pointer even for zero bytes)
|
// must not be passed its null pointer even for zero bytes)
|
||||||
NLOHMANN_VIEW_ENSURE(n)
|
arena_ensure(w, w_end, n);
|
||||||
std::memcpy(w, s, n);
|
std::memcpy(w, s, n);
|
||||||
}
|
}
|
||||||
w += n;
|
w += n;
|
||||||
@@ -311,8 +310,9 @@ class builder
|
|||||||
if (c == '"')
|
if (c == '"')
|
||||||
{
|
{
|
||||||
++p;
|
++p;
|
||||||
m_arena_write = w;
|
// (from w: `write_back` stores it only after the return value
|
||||||
return decoded{p, start, arena_used() - start};
|
// is made; the arena is allocated, since an escape was written)
|
||||||
|
return decoded{p, start, static_cast<std::size_t>(w - m_doc.arena.data()) - start};
|
||||||
}
|
}
|
||||||
if (c != '\\')
|
if (c != '\\')
|
||||||
{
|
{
|
||||||
@@ -356,12 +356,12 @@ class builder
|
|||||||
}
|
}
|
||||||
cp = 0x10000 + ((cp - 0xD800) << 10) + (lo - 0xDC00);
|
cp = 0x10000 + ((cp - 0xD800) << 10) + (lo - 0xDC00);
|
||||||
}
|
}
|
||||||
NLOHMANN_VIEW_ENSURE(4)
|
arena_ensure(w, w_end, 4);
|
||||||
w = put_utf8(w, cp);
|
w = put_utf8(w, cp);
|
||||||
}
|
}
|
||||||
else if (NLOHMANN_VIEW_LIKELY(d < 128 && simple_escape[d] != 0))
|
else if (NLOHMANN_VIEW_LIKELY(d < 128 && simple_escape[d] != 0))
|
||||||
{
|
{
|
||||||
NLOHMANN_VIEW_ENSURE(1)
|
arena_ensure(w, w_end, 1);
|
||||||
*w++ = simple_escape[d];
|
*w++ = simple_escape[d];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -387,7 +387,7 @@ class builder
|
|||||||
NLOHMANN_VIEW_REPEAT16(NLOHMANN_VIEW_STEP)
|
NLOHMANN_VIEW_REPEAT16(NLOHMANN_VIEW_STEP)
|
||||||
#undef NLOHMANN_VIEW_STEP
|
#undef NLOHMANN_VIEW_STEP
|
||||||
run_found:
|
run_found:
|
||||||
NLOHMANN_VIEW_ENSURE(16)
|
arena_ensure(w, w_end, 16);
|
||||||
std::memcpy(w, p, 16); // (only the first `steps` bytes are kept)
|
std::memcpy(w, p, 16); // (only the first `steps` bytes are kept)
|
||||||
w += steps;
|
w += steps;
|
||||||
p += steps;
|
p += steps;
|
||||||
@@ -399,7 +399,7 @@ run_found:
|
|||||||
const unsigned char* const r = p;
|
const unsigned char* const r = p;
|
||||||
p = scan_string_run(p, end);
|
p = scan_string_run(p, end);
|
||||||
const auto n = static_cast<std::size_t>(p - r);
|
const auto n = static_cast<std::size_t>(p - r);
|
||||||
NLOHMANN_VIEW_ENSURE(n)
|
arena_ensure(w, w_end, n);
|
||||||
std::memcpy(w, r, n);
|
std::memcpy(w, r, n);
|
||||||
w += n;
|
w += n;
|
||||||
}
|
}
|
||||||
@@ -423,12 +423,12 @@ run_found:
|
|||||||
const unsigned char* const r = p;
|
const unsigned char* const r = p;
|
||||||
p = scan_string_run(p, end);
|
p = scan_string_run(p, end);
|
||||||
const auto n = static_cast<std::size_t>(p - r);
|
const auto n = static_cast<std::size_t>(p - r);
|
||||||
NLOHMANN_VIEW_ENSURE(n)
|
arena_ensure(w, w_end, n);
|
||||||
std::memcpy(w, r, n);
|
std::memcpy(w, r, n);
|
||||||
w += n;
|
w += n;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
NLOHMANN_VIEW_ENSURE(16)
|
arena_ensure(w, w_end, 16);
|
||||||
const unsigned k = copy_plain_run16(p, w);
|
const unsigned k = copy_plain_run16(p, w);
|
||||||
w += k;
|
w += k;
|
||||||
p += k;
|
p += k;
|
||||||
@@ -442,7 +442,7 @@ run_found:
|
|||||||
const unsigned char* const r = p;
|
const unsigned char* const r = p;
|
||||||
p = scan_string_run(p, end);
|
p = scan_string_run(p, end);
|
||||||
const auto n = static_cast<std::size_t>(p - r);
|
const auto n = static_cast<std::size_t>(p - r);
|
||||||
NLOHMANN_VIEW_ENSURE(n)
|
arena_ensure(w, w_end, n);
|
||||||
std::memcpy(w, r, n);
|
std::memcpy(w, r, n);
|
||||||
w += n;
|
w += n;
|
||||||
}
|
}
|
||||||
@@ -452,12 +452,11 @@ run_found:
|
|||||||
const unsigned char* const r = p;
|
const unsigned char* const r = p;
|
||||||
p = scan_string_run(p, end);
|
p = scan_string_run(p, end);
|
||||||
const auto n = static_cast<std::size_t>(p - r);
|
const auto n = static_cast<std::size_t>(p - r);
|
||||||
NLOHMANN_VIEW_ENSURE(n)
|
arena_ensure(w, w_end, n);
|
||||||
std::memcpy(w, r, n);
|
std::memcpy(w, r, n);
|
||||||
w += n;
|
w += n;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
#undef NLOHMANN_VIEW_ENSURE
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// does the float token [s, p) overflow FloatType? (parse() rejects it)
|
/// does the float token [s, p) overflow FloatType? (parse() rejects it)
|
||||||
@@ -498,6 +497,44 @@ run_found:
|
|||||||
char* m_arena_write = nullptr;
|
char* m_arena_write = nullptr;
|
||||||
char* m_arena_end = nullptr;
|
char* m_arena_end = nullptr;
|
||||||
|
|
||||||
|
/// room for n more bytes at w, the write position kept in a local, with
|
||||||
|
/// w_end the end of the arena. (A lambda instead is not always inlined,
|
||||||
|
/// and then w would be kept in memory, as m_arena_write is.)
|
||||||
|
NLOHMANN_VIEW_ALWAYS_INLINE void arena_ensure(char*& w, const char*& w_end, std::size_t n)
|
||||||
|
{
|
||||||
|
if (NLOHMANN_VIEW_UNLIKELY(static_cast<std::size_t>(w_end - w) < n))
|
||||||
|
{
|
||||||
|
m_arena_write = w;
|
||||||
|
arena_grow(n);
|
||||||
|
w = m_arena_write;
|
||||||
|
w_end = m_arena_end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// stores a write position kept in a local back into m_arena_write when
|
||||||
|
/// the function that keeps it returns, on every path
|
||||||
|
class arena_write_back
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
arena_write_back(builder& b, char*& w) noexcept
|
||||||
|
: m_builder(b)
|
||||||
|
, m_w(w)
|
||||||
|
{}
|
||||||
|
~arena_write_back()
|
||||||
|
{
|
||||||
|
m_builder.m_arena_write = m_w;
|
||||||
|
}
|
||||||
|
|
||||||
|
arena_write_back(const arena_write_back&) = delete;
|
||||||
|
arena_write_back& operator=(const arena_write_back&) = delete;
|
||||||
|
arena_write_back(arena_write_back&&) = delete;
|
||||||
|
arena_write_back& operator=(arena_write_back&&) = delete;
|
||||||
|
|
||||||
|
private:
|
||||||
|
builder& m_builder;
|
||||||
|
char*& m_w;
|
||||||
|
};
|
||||||
|
|
||||||
NLOHMANN_VIEW_ALWAYS_INLINE std::size_t arena_used() const noexcept
|
NLOHMANN_VIEW_ALWAYS_INLINE std::size_t arena_used() const noexcept
|
||||||
{
|
{
|
||||||
return m_arena_write != nullptr ? static_cast<std::size_t>(m_arena_write - m_doc.arena.data()) : 0;
|
return m_arena_write != nullptr ? static_cast<std::size_t>(m_arena_write - m_doc.arena.data()) : 0;
|
||||||
|
|||||||
Generated
+54
-17
@@ -1468,26 +1468,25 @@ class builder
|
|||||||
};
|
};
|
||||||
// The write position is kept in locals: the stores of characters into
|
// The write position is kept in locals: the stores of characters into
|
||||||
// the arena may alias the members of the builder, which would then be
|
// the arena may alias the members of the builder, which would then be
|
||||||
// reloaded after every one. It is stored back where the arena grows
|
// reloaded after every one. It is stored back where the arena grows,
|
||||||
// and at the end.
|
// and by `write_back` on every return.
|
||||||
const unsigned char* const end = m_end;
|
const unsigned char* const end = m_end;
|
||||||
const std::size_t start = arena_used();
|
const std::size_t start = arena_used();
|
||||||
char* w = m_arena_write;
|
char* w = m_arena_write;
|
||||||
const char* w_end = m_arena_end;
|
const char* w_end = m_arena_end;
|
||||||
#define NLOHMANN_VIEW_ENSURE(n) \
|
const arena_write_back write_back(*this, w);
|
||||||
if (NLOHMANN_VIEW_UNLIKELY(static_cast<std::size_t>(w_end - w) < (n))) { m_arena_write = w; arena_grow(n); w = m_arena_write; w_end = m_arena_end; }
|
|
||||||
{
|
{
|
||||||
const auto n = static_cast<std::size_t>(p - s);
|
const auto n = static_cast<std::size_t>(p - s);
|
||||||
if (n <= 16 && end - s >= 16)
|
if (n <= 16 && end - s >= 16)
|
||||||
{
|
{
|
||||||
NLOHMANN_VIEW_ENSURE(16)
|
arena_ensure(w, w_end, 16);
|
||||||
std::memcpy(w, s, 16);
|
std::memcpy(w, s, 16);
|
||||||
}
|
}
|
||||||
else if (n != 0)
|
else if (n != 0)
|
||||||
{
|
{
|
||||||
// (an empty run leaves an empty arena unallocated, and memcpy()
|
// (an empty run leaves an empty arena unallocated, and memcpy()
|
||||||
// must not be passed its null pointer even for zero bytes)
|
// must not be passed its null pointer even for zero bytes)
|
||||||
NLOHMANN_VIEW_ENSURE(n)
|
arena_ensure(w, w_end, n);
|
||||||
std::memcpy(w, s, n);
|
std::memcpy(w, s, n);
|
||||||
}
|
}
|
||||||
w += n;
|
w += n;
|
||||||
@@ -1502,8 +1501,9 @@ class builder
|
|||||||
if (c == '"')
|
if (c == '"')
|
||||||
{
|
{
|
||||||
++p;
|
++p;
|
||||||
m_arena_write = w;
|
// (from w: `write_back` stores it only after the return value
|
||||||
return decoded{p, start, arena_used() - start};
|
// is made; the arena is allocated, since an escape was written)
|
||||||
|
return decoded{p, start, static_cast<std::size_t>(w - m_doc.arena.data()) - start};
|
||||||
}
|
}
|
||||||
if (c != '\\')
|
if (c != '\\')
|
||||||
{
|
{
|
||||||
@@ -1547,12 +1547,12 @@ class builder
|
|||||||
}
|
}
|
||||||
cp = 0x10000 + ((cp - 0xD800) << 10) + (lo - 0xDC00);
|
cp = 0x10000 + ((cp - 0xD800) << 10) + (lo - 0xDC00);
|
||||||
}
|
}
|
||||||
NLOHMANN_VIEW_ENSURE(4)
|
arena_ensure(w, w_end, 4);
|
||||||
w = put_utf8(w, cp);
|
w = put_utf8(w, cp);
|
||||||
}
|
}
|
||||||
else if (NLOHMANN_VIEW_LIKELY(d < 128 && simple_escape[d] != 0))
|
else if (NLOHMANN_VIEW_LIKELY(d < 128 && simple_escape[d] != 0))
|
||||||
{
|
{
|
||||||
NLOHMANN_VIEW_ENSURE(1)
|
arena_ensure(w, w_end, 1);
|
||||||
*w++ = simple_escape[d];
|
*w++ = simple_escape[d];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -1578,7 +1578,7 @@ class builder
|
|||||||
NLOHMANN_VIEW_REPEAT16(NLOHMANN_VIEW_STEP)
|
NLOHMANN_VIEW_REPEAT16(NLOHMANN_VIEW_STEP)
|
||||||
#undef NLOHMANN_VIEW_STEP
|
#undef NLOHMANN_VIEW_STEP
|
||||||
run_found:
|
run_found:
|
||||||
NLOHMANN_VIEW_ENSURE(16)
|
arena_ensure(w, w_end, 16);
|
||||||
std::memcpy(w, p, 16); // (only the first `steps` bytes are kept)
|
std::memcpy(w, p, 16); // (only the first `steps` bytes are kept)
|
||||||
w += steps;
|
w += steps;
|
||||||
p += steps;
|
p += steps;
|
||||||
@@ -1590,7 +1590,7 @@ run_found:
|
|||||||
const unsigned char* const r = p;
|
const unsigned char* const r = p;
|
||||||
p = scan_string_run(p, end);
|
p = scan_string_run(p, end);
|
||||||
const auto n = static_cast<std::size_t>(p - r);
|
const auto n = static_cast<std::size_t>(p - r);
|
||||||
NLOHMANN_VIEW_ENSURE(n)
|
arena_ensure(w, w_end, n);
|
||||||
std::memcpy(w, r, n);
|
std::memcpy(w, r, n);
|
||||||
w += n;
|
w += n;
|
||||||
}
|
}
|
||||||
@@ -1614,12 +1614,12 @@ run_found:
|
|||||||
const unsigned char* const r = p;
|
const unsigned char* const r = p;
|
||||||
p = scan_string_run(p, end);
|
p = scan_string_run(p, end);
|
||||||
const auto n = static_cast<std::size_t>(p - r);
|
const auto n = static_cast<std::size_t>(p - r);
|
||||||
NLOHMANN_VIEW_ENSURE(n)
|
arena_ensure(w, w_end, n);
|
||||||
std::memcpy(w, r, n);
|
std::memcpy(w, r, n);
|
||||||
w += n;
|
w += n;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
NLOHMANN_VIEW_ENSURE(16)
|
arena_ensure(w, w_end, 16);
|
||||||
const unsigned k = copy_plain_run16(p, w);
|
const unsigned k = copy_plain_run16(p, w);
|
||||||
w += k;
|
w += k;
|
||||||
p += k;
|
p += k;
|
||||||
@@ -1633,7 +1633,7 @@ run_found:
|
|||||||
const unsigned char* const r = p;
|
const unsigned char* const r = p;
|
||||||
p = scan_string_run(p, end);
|
p = scan_string_run(p, end);
|
||||||
const auto n = static_cast<std::size_t>(p - r);
|
const auto n = static_cast<std::size_t>(p - r);
|
||||||
NLOHMANN_VIEW_ENSURE(n)
|
arena_ensure(w, w_end, n);
|
||||||
std::memcpy(w, r, n);
|
std::memcpy(w, r, n);
|
||||||
w += n;
|
w += n;
|
||||||
}
|
}
|
||||||
@@ -1643,12 +1643,11 @@ run_found:
|
|||||||
const unsigned char* const r = p;
|
const unsigned char* const r = p;
|
||||||
p = scan_string_run(p, end);
|
p = scan_string_run(p, end);
|
||||||
const auto n = static_cast<std::size_t>(p - r);
|
const auto n = static_cast<std::size_t>(p - r);
|
||||||
NLOHMANN_VIEW_ENSURE(n)
|
arena_ensure(w, w_end, n);
|
||||||
std::memcpy(w, r, n);
|
std::memcpy(w, r, n);
|
||||||
w += n;
|
w += n;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
#undef NLOHMANN_VIEW_ENSURE
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// does the float token [s, p) overflow FloatType? (parse() rejects it)
|
/// does the float token [s, p) overflow FloatType? (parse() rejects it)
|
||||||
@@ -1689,6 +1688,44 @@ run_found:
|
|||||||
char* m_arena_write = nullptr;
|
char* m_arena_write = nullptr;
|
||||||
char* m_arena_end = nullptr;
|
char* m_arena_end = nullptr;
|
||||||
|
|
||||||
|
/// room for n more bytes at w, the write position kept in a local, with
|
||||||
|
/// w_end the end of the arena. (A lambda instead is not always inlined,
|
||||||
|
/// and then w would be kept in memory, as m_arena_write is.)
|
||||||
|
NLOHMANN_VIEW_ALWAYS_INLINE void arena_ensure(char*& w, const char*& w_end, std::size_t n)
|
||||||
|
{
|
||||||
|
if (NLOHMANN_VIEW_UNLIKELY(static_cast<std::size_t>(w_end - w) < n))
|
||||||
|
{
|
||||||
|
m_arena_write = w;
|
||||||
|
arena_grow(n);
|
||||||
|
w = m_arena_write;
|
||||||
|
w_end = m_arena_end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// stores a write position kept in a local back into m_arena_write when
|
||||||
|
/// the function that keeps it returns, on every path
|
||||||
|
class arena_write_back
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
arena_write_back(builder& b, char*& w) noexcept
|
||||||
|
: m_builder(b)
|
||||||
|
, m_w(w)
|
||||||
|
{}
|
||||||
|
~arena_write_back()
|
||||||
|
{
|
||||||
|
m_builder.m_arena_write = m_w;
|
||||||
|
}
|
||||||
|
|
||||||
|
arena_write_back(const arena_write_back&) = delete;
|
||||||
|
arena_write_back& operator=(const arena_write_back&) = delete;
|
||||||
|
arena_write_back(arena_write_back&&) = delete;
|
||||||
|
arena_write_back& operator=(arena_write_back&&) = delete;
|
||||||
|
|
||||||
|
private:
|
||||||
|
builder& m_builder;
|
||||||
|
char*& m_w;
|
||||||
|
};
|
||||||
|
|
||||||
NLOHMANN_VIEW_ALWAYS_INLINE std::size_t arena_used() const noexcept
|
NLOHMANN_VIEW_ALWAYS_INLINE std::size_t arena_used() const noexcept
|
||||||
{
|
{
|
||||||
return m_arena_write != nullptr ? static_cast<std::size_t>(m_arena_write - m_doc.arena.data()) : 0;
|
return m_arena_write != nullptr ? static_cast<std::size_t>(m_arena_write - m_doc.arena.data()) : 0;
|
||||||
|
|||||||
Reference in new issue
Block a user