Use a custom SSO aware string backend

This commit is contained in:
Maxime Coste 2016-02-05 09:27:22 +00:00
parent 87769c9b03
commit 8d37a716fb
3 changed files with 178 additions and 14 deletions

View File

@ -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;

View File

@ -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)

View File

@ -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;
};