summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndreas Kling <kling@serenityos.org>2020-11-24 22:04:22 +0100
committerAndreas Kling <kling@serenityos.org>2020-11-24 22:06:51 +0100
commit54ade31d84c8078fd0113dd9b0d188c04a4dea0c (patch)
tree0762cf084a3041680c27d08b4cd5a144ffe3901a
parenta43aa82d69d67cce76ba8938ee441e977e37f7ef (diff)
downloadserenity-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.cpp33
-rw-r--r--AK/StringBuilder.h7
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 };
};