1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-21 15:35:07 +00:00

AK: Add some inline capacity to StringBuilder

This patch adds a 128-byte inline buffer that we use before switching
to using a dynamically growing ByteBuffer.

This allows us to avoid heap allocations in many cases, and totally
incidentally also speeds up @nico's favorite test, "disasm /bin/id"
more than 2x. :^)
This commit is contained in:
Andreas Kling 2020-11-24 22:04:22 +01:00
parent a43aa82d69
commit 54ade31d84
2 changed files with 29 additions and 11 deletions

View file

@ -25,6 +25,7 @@
*/ */
#include <AK/ByteBuffer.h> #include <AK/ByteBuffer.h>
#include <AK/Checked.h>
#include <AK/Memory.h> #include <AK/Memory.h>
#include <AK/PrintfImplementation.h> #include <AK/PrintfImplementation.h>
#include <AK/StdLibExtras.h> #include <AK/StdLibExtras.h>
@ -37,13 +38,26 @@ namespace AK {
inline void StringBuilder::will_append(size_t size) inline void StringBuilder::will_append(size_t size)
{ {
if ((m_length + size) > m_buffer.size()) Checked<size_t> needed_capacity = m_length;
m_buffer.grow(max(static_cast<size_t>(16), m_buffer.size() * 2 + size)); needed_capacity += size;
ASSERT(!needed_capacity.has_overflow());
if (needed_capacity < inline_capacity)
return;
Checked<size_t> expanded_capacity = needed_capacity;
expanded_capacity *= 2;
ASSERT(!expanded_capacity.has_overflow());
if (m_buffer.is_null()) {
m_buffer.grow(expanded_capacity.value());
memcpy(m_buffer.data(), m_inline_buffer, m_length);
} else if (needed_capacity.value() > m_buffer.size()) {
m_buffer.grow(expanded_capacity.value());
}
} }
StringBuilder::StringBuilder(size_t initial_capacity) StringBuilder::StringBuilder(size_t initial_capacity)
{ {
m_buffer.grow((int)initial_capacity); if (initial_capacity > inline_capacity)
m_buffer.grow(initial_capacity);
} }
void StringBuilder::append(const StringView& str) void StringBuilder::append(const StringView& str)
@ -51,7 +65,7 @@ void StringBuilder::append(const StringView& str)
if (str.is_empty()) if (str.is_empty())
return; return;
will_append(str.length()); will_append(str.length());
memcpy(m_buffer.data() + m_length, str.characters_without_null_termination(), str.length()); memcpy(data() + m_length, str.characters_without_null_termination(), str.length());
m_length += str.length(); m_length += str.length();
} }
@ -63,7 +77,7 @@ void StringBuilder::append(const char* characters, size_t length)
void StringBuilder::append(char ch) void StringBuilder::append(char ch)
{ {
will_append(1); will_append(1);
m_buffer.data()[m_length] = ch; data()[m_length] = ch;
m_length += 1; m_length += 1;
} }
@ -86,16 +100,14 @@ void StringBuilder::appendf(const char* fmt, ...)
ByteBuffer StringBuilder::to_byte_buffer() const ByteBuffer StringBuilder::to_byte_buffer() const
{ {
ByteBuffer buffer_copy = m_buffer.isolated_copy(); return ByteBuffer::copy(data(), length());
buffer_copy.trim(m_length);
return buffer_copy;
} }
String StringBuilder::to_string() const String StringBuilder::to_string() const
{ {
if (is_empty()) if (is_empty())
return String::empty(); return String::empty();
return String((const char*)m_buffer.data(), m_length); return String((const char*)data(), length());
} }
String StringBuilder::build() const String StringBuilder::build() const
@ -105,12 +117,13 @@ String StringBuilder::build() const
StringView StringBuilder::string_view() const StringView StringBuilder::string_view() const
{ {
return StringView { (const char*)m_buffer.data(), m_length }; return StringView { data(), m_length };
} }
void StringBuilder::clear() void StringBuilder::clear()
{ {
m_buffer.clear(); m_buffer.clear();
m_inline_buffer[0] = '\0';
m_length = 0; m_length = 0;
} }

View file

@ -38,7 +38,7 @@ class StringBuilder {
public: public:
using OutputType = String; using OutputType = String;
explicit StringBuilder(size_t initial_capacity = 16); explicit StringBuilder(size_t initial_capacity = inline_capacity);
~StringBuilder() { } ~StringBuilder() { }
void append(const StringView&); void append(const StringView&);
@ -83,7 +83,12 @@ public:
private: private:
void will_append(size_t); void will_append(size_t);
u8* data() { return m_buffer.is_null() ? m_inline_buffer : m_buffer.data(); }
const u8* data() const { return m_buffer.is_null() ? m_inline_buffer : m_buffer.data(); }
bool using_inline_buffer() const { return m_buffer.is_null(); }
static constexpr size_t inline_capacity = 128;
u8 m_inline_buffer[inline_capacity];
ByteBuffer m_buffer; ByteBuffer m_buffer;
size_t m_length { 0 }; size_t m_length { 0 };
}; };