1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
|
#pragma once
#include <AK/ByteBuffer.h>
#include <AK/RetainPtr.h>
#include <AK/StringImpl.h>
#include <AK/StringView.h>
#include <AK/Traits.h>
#include <AK/Vector.h>
#include <AK/kstdio.h>
namespace AK {
// String is a convenience wrapper around StringImpl, suitable for passing
// around as a value type. It's basically the same as passing around a
// RetainPtr<StringImpl>, with a bit of syntactic sugar.
//
// Note that StringImpl is an immutable object that cannot shrink or grow.
// Its allocation size is snugly tailored to the specific string it contains.
// Copying a String is very efficient, since the internal StringImpl is
// retainable and so copying only requires modifying the retain count.
//
// There are three main ways to construct a new String:
//
// s = String("some literal");
//
// s = String::format("%d little piggies", m_piggies);
//
// StringBuilder builder;
// builder.append("abc");
// builder.append("123");
// s = builder.to_string();
class String {
public:
~String() {}
String() {}
String(const StringView& view)
{
if (view.m_impl)
m_impl = *view.m_impl;
else
m_impl = StringImpl::create(view.characters(), view.length());
}
String(const String& other)
: m_impl(const_cast<String&>(other).m_impl.copy_ref())
{
}
String(String&& other)
: m_impl(move(other.m_impl))
{
}
String(const char* cstring, ShouldChomp shouldChomp = NoChomp)
: m_impl(StringImpl::create(cstring, shouldChomp))
{
}
String(const char* cstring, int length, ShouldChomp shouldChomp = NoChomp)
: m_impl(StringImpl::create(cstring, length, shouldChomp))
{
}
String(const StringImpl& impl)
: m_impl(const_cast<StringImpl&>(impl))
{
}
String(const StringImpl* impl)
: m_impl(const_cast<StringImpl*>(impl))
{
}
String(RetainPtr<StringImpl>&& impl)
: m_impl(move(impl))
{
}
String(Retained<StringImpl>&& impl)
: m_impl(move(impl))
{
}
enum class CaseSensitivity {
CaseInsensitive,
CaseSensitive,
};
static String repeated(char, int count);
bool matches(const StringView& pattern, CaseSensitivity = CaseSensitivity::CaseInsensitive) const;
int to_int(bool& ok) const;
unsigned to_uint(bool& ok) const;
String to_lowercase() const
{
if (!m_impl)
return String();
return m_impl->to_lowercase();
}
String to_uppercase() const
{
if (!m_impl)
return String();
return m_impl->to_uppercase();
}
Vector<String> split_limit(char separator, int limit) const;
Vector<String> split(char separator) const;
String substring(int start, int length) const;
Vector<StringView> split_view(char separator) const;
StringView substring_view(int start, int length) const;
bool is_null() const { return !m_impl; }
bool is_empty() const { return length() == 0; }
int length() const { return m_impl ? m_impl->length() : 0; }
const char* characters() const { return m_impl ? m_impl->characters() : nullptr; }
char operator[](int i) const
{
ASSERT(m_impl);
return (*m_impl)[i];
}
bool starts_with(const StringView&) const;
bool ends_with(const StringView&) const;
bool operator==(const String&) const;
bool operator!=(const String& other) const { return !(*this == other); }
bool operator<(const String&) const;
bool operator==(const char* cstring) const
{
if (is_null())
return !cstring;
if (!cstring)
return false;
return !strcmp(characters(), cstring);
}
bool operator!=(const char* cstring) const
{
return !(*this == cstring);
}
String isolated_copy() const;
static String empty();
StringImpl* impl() { return m_impl.ptr(); }
const StringImpl* impl() const { return m_impl.ptr(); }
String& operator=(String&& other)
{
if (this != &other)
m_impl = move(other.m_impl);
return *this;
}
String& operator=(const String& other)
{
if (this != &other)
m_impl = const_cast<String&>(other).m_impl.copy_ref();
return *this;
}
ByteBuffer to_byte_buffer() const;
template<typename BufferType>
static String copy(const BufferType& buffer, ShouldChomp should_chomp = NoChomp)
{
if (buffer.is_null())
return {};
if (buffer.is_empty())
return empty();
return String((const char*)buffer.data(), buffer.size(), should_chomp);
}
static String format(const char*, ...);
StringView view() const { return { characters(), length() }; }
private:
bool match_helper(const StringView& mask) const;
RetainPtr<StringImpl> m_impl;
};
inline bool StringView::operator==(const String& string) const
{
if (string.is_null())
return !m_characters;
if (!m_characters)
return false;
if (m_length != string.length())
return false;
if (m_characters == string.characters())
return true;
return !memcmp(m_characters, string.characters(), m_length);
}
template<>
struct Traits<String> {
static unsigned hash(const String& s) { return s.impl() ? s.impl()->hash() : 0; }
static void dump(const String& s) { kprintf("%s", s.characters()); }
};
}
using AK::String;
|