summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMaxime Coste <frrrwww@gmail.com>2016-02-05 09:27:22 +0000
committerMaxime Coste <frrrwww@gmail.com>2016-02-10 09:44:42 +0000
commit8d37a716fbf83cd58a026d70a293cb30f972ecaa (patch)
tree050dbb7e18abcfcf711cf64700611353337c2eda /src
parent87769c9b03b516053249f86355a4e503a529c659 (diff)
Use a custom SSO aware string backend
Diffstat (limited to 'src')
-rw-r--r--src/remote.cc2
-rw-r--r--src/string.cc119
-rw-r--r--src/string.hh71
3 files changed, 178 insertions, 14 deletions
diff --git a/src/remote.cc b/src/remote.cc
index ca9e987c..e2756ef1 100644
--- a/src/remote.cc
+++ b/src/remote.cc
@@ -164,7 +164,7 @@ String read<String>(int socket)
String res;
if (length > 0)
{
- res.resize((int)length);
+ res.force_size((int)length);
read(socket, &res[0_byte], (int)length);
}
return res;
diff --git a/src/string.cc b/src/string.cc
index 9395a26e..2734042b 100644
--- a/src/string.cc
+++ b/src/string.cc
@@ -10,6 +10,125 @@
namespace Kakoune
{
+String::Data::Data(const char* data, size_t size, size_t capacity)
+{
+ if (capacity > Short::capacity)
+ {
+ if (capacity & 1)
+ ++capacity;
+
+ l.ptr = Alloc{}.allocate(capacity+1);
+ l.size = size;
+ l.capacity = capacity;
+
+ memcpy(l.ptr, data, size);
+ l.ptr[size] = 0;
+ }
+ else
+ set_short(data, size);
+}
+
+String::Data::Data(Data&& other) noexcept
+{
+ if (other.is_long())
+ {
+ l = other.l;
+ other.s.size = 1;
+ }
+ else
+ s = other.s;
+}
+
+String::Data& String::Data::operator=(const Data& other)
+{
+ const size_t new_size = other.size();
+ reserve<false>(new_size);
+ memcpy(data(), other.data(), new_size+1);
+ set_size(new_size);
+
+ return *this;
+}
+
+String::Data& String::Data::operator=(Data&& other) noexcept
+{
+ if (other.is_long())
+ {
+ l = other.l;
+ other.set_empty();
+ }
+ else
+ s = other.s;
+
+ return *this;
+}
+
+template<bool copy>
+void String::Data::reserve(size_t new_capacity)
+{
+ if (new_capacity <= capacity())
+ return;
+
+ if (is_long())
+ new_capacity = std::max(l.capacity * 2, new_capacity);
+
+ char* new_ptr = Alloc{}.allocate(new_capacity+1);
+ if (copy)
+ {
+ memcpy(new_ptr, data(), size()+1);
+ l.size = size();
+ }
+ release();
+
+ l.ptr = new_ptr;
+ l.capacity = new_capacity;
+}
+
+template void String::Data::reserve<true>(size_t);
+template void String::Data::reserve<false>(size_t);
+
+void String::Data::force_size(size_t new_size)
+{
+ reserve<false>(new_size);
+ set_size(new_size);
+}
+
+void String::Data::append(const char* str, size_t len)
+{
+ const size_t new_size = size() + len;
+ reserve(new_size);
+
+ memcpy(data() + size(), str, len);
+ set_size(new_size);
+ data()[new_size] = 0;
+}
+
+void String::Data::clear()
+{
+ release();
+ set_empty();
+}
+
+void String::Data::release()
+{
+ if (is_long())
+ Alloc{}.deallocate(l.ptr, l.capacity+1);
+}
+
+void String::Data::set_size(size_t size)
+{
+ if (is_long())
+ l.size = size;
+ else
+ s.size = (size << 1) | 1;
+}
+
+void String::Data::set_short(const char* data, size_t size)
+{
+ s.size = (size << 1) | 1;
+ memcpy(s.string, data, size);
+ s.string[size] = 0;
+}
+
const String String::ms_empty;
Vector<String> split(StringView str, char separator, char escape)
diff --git a/src/string.hh b/src/string.hh
index 638f30f9..ab83b56a 100644
--- a/src/string.hh
+++ b/src/string.hh
@@ -8,7 +8,7 @@
#include "utf8.hh"
#include "vector.hh"
-#include <string>
+#include <string.h>
#include <climits>
namespace Kakoune
@@ -94,9 +94,6 @@ constexpr ByteCount strlen(const char* s)
class String : public StringOps<String, char>
{
public:
- using Content = std::basic_string<char, std::char_traits<char>,
- Allocator<char, MemoryDomain::String>>;
-
String() {}
String(const char* content) : m_data(content, (size_t)(int)strlen(content)) {}
String(const char* content, ByteCount len) : m_data(content, (size_t)(int)len) {}
@@ -109,30 +106,78 @@ public:
String(const char* begin, const char* end) : m_data(begin, end-begin) {}
[[gnu::always_inline]]
- char* data() { return &m_data[0]; }
+ char* data() { return m_data.data(); }
[[gnu::always_inline]]
const char* data() const { return m_data.data(); }
[[gnu::always_inline]]
- ByteCount length() const { return m_data.length(); }
+ ByteCount length() const { return m_data.size(); }
[[gnu::always_inline]]
- const char* c_str() const { return m_data.c_str(); }
+ const char* c_str() const { return m_data.data(); }
[[gnu::always_inline]]
void append(const char* data, ByteCount count) { m_data.append(data, (size_t)(int)count); }
void clear() { m_data.clear(); }
- void push_back(char c) { m_data.push_back(c); }
- void resize(ByteCount size) { m_data.resize((size_t)(int)size); }
+ void push_back(char c) { m_data.append(&c, 1); }
+ void force_size(ByteCount size) { m_data.force_size((size_t)(int)size); }
void reserve(ByteCount size) { m_data.reserve((size_t)(int)size); }
static const String ms_empty;
+ union Data
+ {
+ using Alloc = Allocator<char, MemoryDomain::String>;
+
+ struct Long
+ {
+ char* ptr;
+ size_t size;
+ size_t capacity;
+ } l;
+
+ struct Short
+ {
+ static constexpr size_t capacity = sizeof(Long) - 2;
+ char string[capacity+1];
+ unsigned char size;
+ } s;
+
+ Data() { set_empty(); }
+ Data(const char* data, size_t size, size_t capacity);
+ Data(const char* data, size_t size) : Data(data, size, size) {}
+ Data(const Data& other) : Data{other.data(), other.size()} {}
+
+ ~Data() { release(); }
+ Data(Data&& other) noexcept;
+ Data& operator=(const Data& other);
+ Data& operator=(Data&& other) noexcept;
+
+ bool is_long() const { return (s.size & 1) == 0; }
+ size_t size() const { return is_long() ? l.size : (s.size >> 1); }
+ size_t capacity() const { return is_long() ? l.capacity : Short::capacity; }
+
+ const char* data() const { return is_long() ? l.ptr : s.string; }
+ char* data() { return is_long() ? l.ptr : s.string; }
+
+ template<bool copy = true>
+ void reserve(size_t new_capacity);
+ void force_size(size_t new_size);
+ void append(const char* str, size_t len);
+ void clear();
+
+ private:
+ void release();
+ void set_empty() { s.size = 1; }
+ void set_size(size_t size);
+ void set_short(const char* data, size_t size);
+ };
+
private:
- Content m_data;
+ Data m_data;
};
class StringView : public StringOps<StringView, const char>
@@ -162,12 +207,12 @@ public:
if (*end == '\0')
unowned = begin;
else
- owned = String::Content(begin, end);
+ owned = String::Data(begin, end - begin);
}
- operator const char*() const { return unowned ? unowned : owned.c_str(); }
+ operator const char*() const { return unowned ? unowned : owned.data(); }
private:
- String::Content owned;
+ String::Data owned;
const char* unowned = nullptr;
};