Skip to content

Commit 8d37a71

Browse files
committed
Use a custom SSO aware string backend
1 parent 87769c9 commit 8d37a71

File tree

3 files changed

+178
-14
lines changed

3 files changed

+178
-14
lines changed

src/remote.cc

+1-1
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ String read<String>(int socket)
164164
String res;
165165
if (length > 0)
166166
{
167-
res.resize((int)length);
167+
res.force_size((int)length);
168168
read(socket, &res[0_byte], (int)length);
169169
}
170170
return res;

src/string.cc

+119
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,125 @@
1010
namespace Kakoune
1111
{
1212

13+
String::Data::Data(const char* data, size_t size, size_t capacity)
14+
{
15+
if (capacity > Short::capacity)
16+
{
17+
if (capacity & 1)
18+
++capacity;
19+
20+
l.ptr = Alloc{}.allocate(capacity+1);
21+
l.size = size;
22+
l.capacity = capacity;
23+
24+
memcpy(l.ptr, data, size);
25+
l.ptr[size] = 0;
26+
}
27+
else
28+
set_short(data, size);
29+
}
30+
31+
String::Data::Data(Data&& other) noexcept
32+
{
33+
if (other.is_long())
34+
{
35+
l = other.l;
36+
other.s.size = 1;
37+
}
38+
else
39+
s = other.s;
40+
}
41+
42+
String::Data& String::Data::operator=(const Data& other)
43+
{
44+
const size_t new_size = other.size();
45+
reserve<false>(new_size);
46+
memcpy(data(), other.data(), new_size+1);
47+
set_size(new_size);
48+
49+
return *this;
50+
}
51+
52+
String::Data& String::Data::operator=(Data&& other) noexcept
53+
{
54+
if (other.is_long())
55+
{
56+
l = other.l;
57+
other.set_empty();
58+
}
59+
else
60+
s = other.s;
61+
62+
return *this;
63+
}
64+
65+
template<bool copy>
66+
void String::Data::reserve(size_t new_capacity)
67+
{
68+
if (new_capacity <= capacity())
69+
return;
70+
71+
if (is_long())
72+
new_capacity = std::max(l.capacity * 2, new_capacity);
73+
74+
char* new_ptr = Alloc{}.allocate(new_capacity+1);
75+
if (copy)
76+
{
77+
memcpy(new_ptr, data(), size()+1);
78+
l.size = size();
79+
}
80+
release();
81+
82+
l.ptr = new_ptr;
83+
l.capacity = new_capacity;
84+
}
85+
86+
template void String::Data::reserve<true>(size_t);
87+
template void String::Data::reserve<false>(size_t);
88+
89+
void String::Data::force_size(size_t new_size)
90+
{
91+
reserve<false>(new_size);
92+
set_size(new_size);
93+
}
94+
95+
void String::Data::append(const char* str, size_t len)
96+
{
97+
const size_t new_size = size() + len;
98+
reserve(new_size);
99+
100+
memcpy(data() + size(), str, len);
101+
set_size(new_size);
102+
data()[new_size] = 0;
103+
}
104+
105+
void String::Data::clear()
106+
{
107+
release();
108+
set_empty();
109+
}
110+
111+
void String::Data::release()
112+
{
113+
if (is_long())
114+
Alloc{}.deallocate(l.ptr, l.capacity+1);
115+
}
116+
117+
void String::Data::set_size(size_t size)
118+
{
119+
if (is_long())
120+
l.size = size;
121+
else
122+
s.size = (size << 1) | 1;
123+
}
124+
125+
void String::Data::set_short(const char* data, size_t size)
126+
{
127+
s.size = (size << 1) | 1;
128+
memcpy(s.string, data, size);
129+
s.string[size] = 0;
130+
}
131+
13132
const String String::ms_empty;
14133

15134
Vector<String> split(StringView str, char separator, char escape)

src/string.hh

+58-13
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
#include "utf8.hh"
99
#include "vector.hh"
1010

11-
#include <string>
11+
#include <string.h>
1212
#include <climits>
1313

1414
namespace Kakoune
@@ -94,9 +94,6 @@ constexpr ByteCount strlen(const char* s)
9494
class String : public StringOps<String, char>
9595
{
9696
public:
97-
using Content = std::basic_string<char, std::char_traits<char>,
98-
Allocator<char, MemoryDomain::String>>;
99-
10097
String() {}
10198
String(const char* content) : m_data(content, (size_t)(int)strlen(content)) {}
10299
String(const char* content, ByteCount len) : m_data(content, (size_t)(int)len) {}
@@ -109,30 +106,78 @@ public:
109106
String(const char* begin, const char* end) : m_data(begin, end-begin) {}
110107

111108
[[gnu::always_inline]]
112-
char* data() { return &m_data[0]; }
109+
char* data() { return m_data.data(); }
113110

114111
[[gnu::always_inline]]
115112
const char* data() const { return m_data.data(); }
116113

117114
[[gnu::always_inline]]
118-
ByteCount length() const { return m_data.length(); }
115+
ByteCount length() const { return m_data.size(); }
119116

120117
[[gnu::always_inline]]
121-
const char* c_str() const { return m_data.c_str(); }
118+
const char* c_str() const { return m_data.data(); }
122119

123120
[[gnu::always_inline]]
124121
void append(const char* data, ByteCount count) { m_data.append(data, (size_t)(int)count); }
125122

126123
void clear() { m_data.clear(); }
127124

128-
void push_back(char c) { m_data.push_back(c); }
129-
void resize(ByteCount size) { m_data.resize((size_t)(int)size); }
125+
void push_back(char c) { m_data.append(&c, 1); }
126+
void force_size(ByteCount size) { m_data.force_size((size_t)(int)size); }
130127
void reserve(ByteCount size) { m_data.reserve((size_t)(int)size); }
131128

132129
static const String ms_empty;
133130

131+
union Data
132+
{
133+
using Alloc = Allocator<char, MemoryDomain::String>;
134+
135+
struct Long
136+
{
137+
char* ptr;
138+
size_t size;
139+
size_t capacity;
140+
} l;
141+
142+
struct Short
143+
{
144+
static constexpr size_t capacity = sizeof(Long) - 2;
145+
char string[capacity+1];
146+
unsigned char size;
147+
} s;
148+
149+
Data() { set_empty(); }
150+
Data(const char* data, size_t size, size_t capacity);
151+
Data(const char* data, size_t size) : Data(data, size, size) {}
152+
Data(const Data& other) : Data{other.data(), other.size()} {}
153+
154+
~Data() { release(); }
155+
Data(Data&& other) noexcept;
156+
Data& operator=(const Data& other);
157+
Data& operator=(Data&& other) noexcept;
158+
159+
bool is_long() const { return (s.size & 1) == 0; }
160+
size_t size() const { return is_long() ? l.size : (s.size >> 1); }
161+
size_t capacity() const { return is_long() ? l.capacity : Short::capacity; }
162+
163+
const char* data() const { return is_long() ? l.ptr : s.string; }
164+
char* data() { return is_long() ? l.ptr : s.string; }
165+
166+
template<bool copy = true>
167+
void reserve(size_t new_capacity);
168+
void force_size(size_t new_size);
169+
void append(const char* str, size_t len);
170+
void clear();
171+
172+
private:
173+
void release();
174+
void set_empty() { s.size = 1; }
175+
void set_size(size_t size);
176+
void set_short(const char* data, size_t size);
177+
};
178+
134179
private:
135-
Content m_data;
180+
Data m_data;
136181
};
137182

138183
class StringView : public StringOps<StringView, const char>
@@ -162,12 +207,12 @@ public:
162207
if (*end == '\0')
163208
unowned = begin;
164209
else
165-
owned = String::Content(begin, end);
210+
owned = String::Data(begin, end - begin);
166211
}
167-
operator const char*() const { return unowned ? unowned : owned.c_str(); }
212+
operator const char*() const { return unowned ? unowned : owned.data(); }
168213

169214
private:
170-
String::Content owned;
215+
String::Data owned;
171216
const char* unowned = nullptr;
172217

173218
};

0 commit comments

Comments
 (0)