Use a custom SSO aware string backend
This commit is contained in:
parent
87769c9b03
commit
8d37a716fb
|
@ -164,7 +164,7 @@ String read<String>(int socket)
|
||||||
String res;
|
String res;
|
||||||
if (length > 0)
|
if (length > 0)
|
||||||
{
|
{
|
||||||
res.resize((int)length);
|
res.force_size((int)length);
|
||||||
read(socket, &res[0_byte], (int)length);
|
read(socket, &res[0_byte], (int)length);
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
|
|
119
src/string.cc
119
src/string.cc
|
@ -10,6 +10,125 @@
|
||||||
namespace Kakoune
|
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;
|
const String String::ms_empty;
|
||||||
|
|
||||||
Vector<String> split(StringView str, char separator, char escape)
|
Vector<String> split(StringView str, char separator, char escape)
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
#include "utf8.hh"
|
#include "utf8.hh"
|
||||||
#include "vector.hh"
|
#include "vector.hh"
|
||||||
|
|
||||||
#include <string>
|
#include <string.h>
|
||||||
#include <climits>
|
#include <climits>
|
||||||
|
|
||||||
namespace Kakoune
|
namespace Kakoune
|
||||||
|
@ -94,9 +94,6 @@ constexpr ByteCount strlen(const char* s)
|
||||||
class String : public StringOps<String, char>
|
class String : public StringOps<String, char>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using Content = std::basic_string<char, std::char_traits<char>,
|
|
||||||
Allocator<char, MemoryDomain::String>>;
|
|
||||||
|
|
||||||
String() {}
|
String() {}
|
||||||
String(const char* content) : m_data(content, (size_t)(int)strlen(content)) {}
|
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) {}
|
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) {}
|
String(const char* begin, const char* end) : m_data(begin, end-begin) {}
|
||||||
|
|
||||||
[[gnu::always_inline]]
|
[[gnu::always_inline]]
|
||||||
char* data() { return &m_data[0]; }
|
char* data() { return m_data.data(); }
|
||||||
|
|
||||||
[[gnu::always_inline]]
|
[[gnu::always_inline]]
|
||||||
const char* data() const { return m_data.data(); }
|
const char* data() const { return m_data.data(); }
|
||||||
|
|
||||||
[[gnu::always_inline]]
|
[[gnu::always_inline]]
|
||||||
ByteCount length() const { return m_data.length(); }
|
ByteCount length() const { return m_data.size(); }
|
||||||
|
|
||||||
[[gnu::always_inline]]
|
[[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]]
|
[[gnu::always_inline]]
|
||||||
void append(const char* data, ByteCount count) { m_data.append(data, (size_t)(int)count); }
|
void append(const char* data, ByteCount count) { m_data.append(data, (size_t)(int)count); }
|
||||||
|
|
||||||
void clear() { m_data.clear(); }
|
void clear() { m_data.clear(); }
|
||||||
|
|
||||||
void push_back(char c) { m_data.push_back(c); }
|
void push_back(char c) { m_data.append(&c, 1); }
|
||||||
void resize(ByteCount size) { m_data.resize((size_t)(int)size); }
|
void force_size(ByteCount size) { m_data.force_size((size_t)(int)size); }
|
||||||
void reserve(ByteCount size) { m_data.reserve((size_t)(int)size); }
|
void reserve(ByteCount size) { m_data.reserve((size_t)(int)size); }
|
||||||
|
|
||||||
static const String ms_empty;
|
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:
|
private:
|
||||||
Content m_data;
|
void release();
|
||||||
|
void set_empty() { s.size = 1; }
|
||||||
|
void set_size(size_t size);
|
||||||
|
void set_short(const char* data, size_t size);
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
Data m_data;
|
||||||
};
|
};
|
||||||
|
|
||||||
class StringView : public StringOps<StringView, const char>
|
class StringView : public StringOps<StringView, const char>
|
||||||
|
@ -162,12 +207,12 @@ public:
|
||||||
if (*end == '\0')
|
if (*end == '\0')
|
||||||
unowned = begin;
|
unowned = begin;
|
||||||
else
|
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:
|
private:
|
||||||
String::Content owned;
|
String::Data owned;
|
||||||
const char* unowned = nullptr;
|
const char* unowned = nullptr;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue
Block a user