Write a line break and the indentation of the next line together

A pretty-printed array or object wrote ",\n", checked the room for the
indentation, and stored it, for every element. The line break and the
usual indentation (up to 32 characters, with room in the write buffer) are
now written with one check and one run of stores, also after the opening
bracket and in front of the closing one. Other indentations take the
unchanged path.

Apple M1, Apple clang, retired instructions per dump(4), on top of the
previous changes:
  citm_catalog 7.28M -> 7.06M (-3 %)   canada 44.4M -> 43.3M (-3 %)
  twitter 3.91M -> 3.78M (-3 %)        jeopardy -1 %
Linux x86-64 (callgrind), gcc 13 / clang 18, dump(4) / dump(1, '\t'):
  citm_catalog dump(4) -3 % / -4 %, tab -4 % / -5 %, with the function
  inlined (without that GCC wrote the combined step out of line and was
  5 % slower than before the change).
Compact output is unchanged.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
Niels Lohmann committed 2026-10-22 09:28:26 +02:00
1 parent ae30ba55c3
commit 47462527b2
2 files changed
+68 -28

No files matched your search

+34 -14
View File
@@ -167,30 +167,27 @@ class serializer
if (pretty_print)
{
put_literal("{\n");
// variable to hold indentation for recursive calls
const auto new_indent = next_indent(current_indent, indent_step);
put_literal_indent("{\n", new_indent);
// first n-1 elements
auto i = val.m_data.m_value.object->cbegin();
for (std::size_t cnt = 0; cnt < val.m_data.m_value.object->size() - 1; ++cnt, ++i)
{
put_indent(new_indent);
put_quoted(i->first, "\": ");
dump_element(i->second, new_indent, depth + 1);
put_literal(",\n");
put_literal_indent(",\n", new_indent);
}
// last element
JSON_ASSERT(i != val.m_data.m_value.object->cend());
JSON_ASSERT(std::next(i) == val.m_data.m_value.object->cend());
put_indent(new_indent);
put_quoted(i->first, "\": ");
dump_element(i->second, new_indent, depth + 1);
put_char('\n');
put_indent(current_indent);
put_literal_indent("\n", current_indent);
put_char('}');
}
else
@@ -234,27 +231,24 @@ class serializer
if (pretty_print)
{
put_literal("[\n");
// variable to hold indentation for recursive calls
const auto new_indent = next_indent(current_indent, indent_step);
put_literal_indent("[\n", new_indent);
// first n-1 elements
for (auto i = val.m_data.m_value.array->cbegin();
i != val.m_data.m_value.array->cend() - 1; ++i)
{
put_indent(new_indent);
dump_element(*i, new_indent, depth + 1);
put_literal(",\n");
put_literal_indent(",\n", new_indent);
}
// last element
JSON_ASSERT(!val.m_data.m_value.array->empty());
put_indent(new_indent);
dump_element(val.m_data.m_value.array->back(), new_indent, depth + 1);
put_char('\n');
put_indent(current_indent);
put_literal_indent("\n", current_indent);
put_char(']');
}
else
@@ -1430,6 +1424,32 @@ class serializer
write_buffer_pos = indent;
}
/*!
@brief append a string literal and then @a indent indentation characters
The usual line break and indentation go into the buffer with one check of
its room and four stores of the indentation, see @ref put_indent.
*/
template<std::size_t N>
JSON_HEDLEY_ALWAYS_INLINE void put_literal_indent(const char (&s)[N], const std::size_t indent) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
{
constexpr std::size_t length = N - 1;
if (JSON_HEDLEY_LIKELY(indent <= 32 && write_buffer_pos + length + 32 <= write_buffer.size()))
{
const std::uint64_t word = 0x0101010101010101ull * static_cast<unsigned char>(indent_char);
char* const d = write_buffer.data() + write_buffer_pos;
std::memcpy(d, s, length);
std::memcpy(d + length, &word, sizeof(word));
std::memcpy(d + length + 8, &word, sizeof(word));
std::memcpy(d + length + 16, &word, sizeof(word));
std::memcpy(d + length + 24, &word, sizeof(word));
write_buffer_pos += length + indent;
return;
}
put_literal(s);
put_indent(indent);
}
/*!
@brief append a string literal to the write buffer
+34 -14
View File
@@ -30311,30 +30311,27 @@ class serializer
if (pretty_print)
{
put_literal("{\n");
// variable to hold indentation for recursive calls
const auto new_indent = next_indent(current_indent, indent_step);
put_literal_indent("{\n", new_indent);
// first n-1 elements
auto i = val.m_data.m_value.object->cbegin();
for (std::size_t cnt = 0; cnt < val.m_data.m_value.object->size() - 1; ++cnt, ++i)
{
put_indent(new_indent);
put_quoted(i->first, "\": ");
dump_element(i->second, new_indent, depth + 1);
put_literal(",\n");
put_literal_indent(",\n", new_indent);
}
// last element
JSON_ASSERT(i != val.m_data.m_value.object->cend());
JSON_ASSERT(std::next(i) == val.m_data.m_value.object->cend());
put_indent(new_indent);
put_quoted(i->first, "\": ");
dump_element(i->second, new_indent, depth + 1);
put_char('\n');
put_indent(current_indent);
put_literal_indent("\n", current_indent);
put_char('}');
}
else
@@ -30378,27 +30375,24 @@ class serializer
if (pretty_print)
{
put_literal("[\n");
// variable to hold indentation for recursive calls
const auto new_indent = next_indent(current_indent, indent_step);
put_literal_indent("[\n", new_indent);
// first n-1 elements
for (auto i = val.m_data.m_value.array->cbegin();
i != val.m_data.m_value.array->cend() - 1; ++i)
{
put_indent(new_indent);
dump_element(*i, new_indent, depth + 1);
put_literal(",\n");
put_literal_indent(",\n", new_indent);
}
// last element
JSON_ASSERT(!val.m_data.m_value.array->empty());
put_indent(new_indent);
dump_element(val.m_data.m_value.array->back(), new_indent, depth + 1);
put_char('\n');
put_indent(current_indent);
put_literal_indent("\n", current_indent);
put_char(']');
}
else
@@ -31574,6 +31568,32 @@ class serializer
write_buffer_pos = indent;
}
/*!
@brief append a string literal and then @a indent indentation characters
The usual line break and indentation go into the buffer with one check of
its room and four stores of the indentation, see @ref put_indent.
*/
template<std::size_t N>
JSON_HEDLEY_ALWAYS_INLINE void put_literal_indent(const char (&s)[N], const std::size_t indent) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
{
constexpr std::size_t length = N - 1;
if (JSON_HEDLEY_LIKELY(indent <= 32 && write_buffer_pos + length + 32 <= write_buffer.size()))
{
const std::uint64_t word = 0x0101010101010101ull * static_cast<unsigned char>(indent_char);
char* const d = write_buffer.data() + write_buffer_pos;
std::memcpy(d, s, length);
std::memcpy(d + length, &word, sizeof(word));
std::memcpy(d + length + 8, &word, sizeof(word));
std::memcpy(d + length + 16, &word, sizeof(word));
std::memcpy(d + length + 24, &word, sizeof(word));
write_buffer_pos += length + indent;
return;
}
put_literal(s);
put_indent(indent);
}
/*!
@brief append a string literal to the write buffer