diff options
author | Andreas Kling <kling@serenityos.org> | 2020-11-24 22:04:22 +0100 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2020-11-24 22:06:51 +0100 |
commit | 54ade31d84c8078fd0113dd9b0d188c04a4dea0c (patch) | |
tree | 0762cf084a3041680c27d08b4cd5a144ffe3901a | |
parent | a43aa82d69d67cce76ba8938ee441e977e37f7ef (diff) | |
download | serenity-54ade31d84c8078fd0113dd9b0d188c04a4dea0c.zip |
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. :^)
-rw-r--r-- | AK/StringBuilder.cpp | 33 | ||||
-rw-r--r-- | AK/StringBuilder.h | 7 |
2 files changed, 29 insertions, 11 deletions
diff --git a/AK/StringBuilder.cpp b/AK/StringBuilder.cpp index 9f2b8f8be0..7acfd9b93c 100644 --- a/AK/StringBuilder.cpp +++ b/AK/StringBuilder.cpp @@ -25,6 +25,7 @@ */ #include <AK/ByteBuffer.h> +#include <AK/Checked.h> #include <AK/Memory.h> #include <AK/PrintfImplementation.h> #include <AK/StdLibExtras.h> @@ -37,13 +38,26 @@ namespace AK { inline void StringBuilder::will_append(size_t size) { - if ((m_length + size) > m_buffer.size()) - m_buffer.grow(max(static_cast<size_t>(16), m_buffer.size() * 2 + size)); + Checked<size_t> needed_capacity = m_length; + 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) { - m_buffer.grow((int)initial_capacity); + if (initial_capacity > inline_capacity) + m_buffer.grow(initial_capacity); } void StringBuilder::append(const StringView& str) @@ -51,7 +65,7 @@ void StringBuilder::append(const StringView& str) if (str.is_empty()) return; 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(); } @@ -63,7 +77,7 @@ void StringBuilder::append(const char* characters, size_t length) void StringBuilder::append(char ch) { will_append(1); - m_buffer.data()[m_length] = ch; + data()[m_length] = ch; m_length += 1; } @@ -86,16 +100,14 @@ void StringBuilder::appendf(const char* fmt, ...) ByteBuffer StringBuilder::to_byte_buffer() const { - ByteBuffer buffer_copy = m_buffer.isolated_copy(); - buffer_copy.trim(m_length); - return buffer_copy; + return ByteBuffer::copy(data(), length()); } String StringBuilder::to_string() const { if (is_empty()) return String::empty(); - return String((const char*)m_buffer.data(), m_length); + return String((const char*)data(), length()); } String StringBuilder::build() const @@ -105,12 +117,13 @@ String StringBuilder::build() const StringView StringBuilder::string_view() const { - return StringView { (const char*)m_buffer.data(), m_length }; + return StringView { data(), m_length }; } void StringBuilder::clear() { m_buffer.clear(); + m_inline_buffer[0] = '\0'; m_length = 0; } diff --git a/AK/StringBuilder.h b/AK/StringBuilder.h index cc042e60de..d313c9147d 100644 --- a/AK/StringBuilder.h +++ b/AK/StringBuilder.h @@ -38,7 +38,7 @@ class StringBuilder { public: using OutputType = String; - explicit StringBuilder(size_t initial_capacity = 16); + explicit StringBuilder(size_t initial_capacity = inline_capacity); ~StringBuilder() { } void append(const StringView&); @@ -83,7 +83,12 @@ public: private: 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; size_t m_length { 0 }; }; |