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
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
|
#include "cru/common/StringUtil.hpp"
#include "cru/common/Base.hpp"
#include "cru/common/Exception.hpp"
namespace cru {
using details::ExtractBits;
CodePoint Utf8NextCodePoint(const char* ptr, Index size, Index current,
Index* next_position) {
CodePoint result;
if (current >= size) {
result = k_invalid_code_point;
} else {
const auto cu0 = static_cast<std::uint8_t>(ptr[current++]);
auto read_next_folowing_code = [ptr, size, ¤t]() -> CodePoint {
if (current == size)
throw TextEncodeException(
u"Unexpected end when read continuing byte of multi-byte code "
"point.");
const auto u = static_cast<std::uint8_t>(ptr[current]);
if (!(u & (1u << 7)) || (u & (1u << 6))) {
throw TextEncodeException(
u"Unexpected bad-format (not 0b10xxxxxx) continuing byte of "
"multi-byte code point.");
}
return ExtractBits<std::uint8_t, 6, CodePoint>(ptr[current++]);
};
if ((1u << 7) & cu0) {
if ((1u << 6) & cu0) { // 2~4-length code point
if ((1u << 5) & cu0) { // 3~4-length code point
if ((1u << 4) & cu0) { // 4-length code point
if (cu0 & (1u << 3)) {
throw TextEncodeException(
u"Unexpected bad-format begin byte (not 0b11110xxx) of 4-byte"
"code point.");
}
const CodePoint s0 = ExtractBits<std::uint8_t, 3, CodePoint>(cu0)
<< (6 * 3);
const CodePoint s1 = read_next_folowing_code() << (6 * 2);
const CodePoint s2 = read_next_folowing_code() << 6;
const CodePoint s3 = read_next_folowing_code();
result = s0 + s1 + s2 + s3;
} else { // 3-length code point
const CodePoint s0 = ExtractBits<std::uint8_t, 4, CodePoint>(cu0)
<< (6 * 2);
const CodePoint s1 = read_next_folowing_code() << 6;
const CodePoint s2 = read_next_folowing_code();
result = s0 + s1 + s2;
}
} else { // 2-length code point
const CodePoint s0 = ExtractBits<std::uint8_t, 5, CodePoint>(cu0)
<< 6;
const CodePoint s1 = read_next_folowing_code();
result = s0 + s1;
}
} else {
throw TextEncodeException(
u"Unexpected bad-format (0b10xxxxxx) begin byte of a code point.");
}
} else {
result = static_cast<CodePoint>(cu0);
}
}
if (next_position != nullptr) *next_position = current;
return result;
}
CodePoint Utf16NextCodePoint(const char16_t* ptr, Index size, Index current,
Index* next_position) {
CodePoint result;
if (current >= size) {
result = k_invalid_code_point;
} else {
const auto cu0 = ptr[current++];
if (!IsUtf16SurrogatePairCodeUnit(cu0)) { // 1-length code point
result = static_cast<CodePoint>(cu0);
} else if (IsUtf16SurrogatePairLeading(cu0)) { // 2-length code point
if (current >= size) {
throw TextEncodeException(
u"Unexpected end when reading second code unit of surrogate pair.");
}
const auto cu1 = ptr[current++];
if (!IsUtf16SurrogatePairTrailing(cu1)) {
throw TextEncodeException(
u"Unexpected bad-range second code unit of surrogate pair.");
}
const auto s0 = ExtractBits<std::uint16_t, 10, CodePoint>(cu0) << 10;
const auto s1 = ExtractBits<std::uint16_t, 10, CodePoint>(cu1);
result = s0 + s1 + 0x10000;
} else {
throw TextEncodeException(
u"Unexpected bad-range first code unit of surrogate pair.");
}
}
if (next_position != nullptr) *next_position = current;
return result;
}
CodePoint Utf16PreviousCodePoint(const char16_t* ptr, Index size, Index current,
Index* previous_position) {
CRU_UNUSED(size)
CodePoint result;
if (current <= 0) {
result = k_invalid_code_point;
} else {
const auto cu0 = ptr[--current];
if (!IsUtf16SurrogatePairCodeUnit(cu0)) { // 1-length code point
result = static_cast<CodePoint>(cu0);
} else if (IsUtf16SurrogatePairTrailing(cu0)) { // 2-length code point
if (current <= 0) {
throw TextEncodeException(
u"Unexpected end when reading first code unit of surrogate pair.");
}
const auto cu1 = ptr[--current];
if (!IsUtf16SurrogatePairLeading(cu1)) {
throw TextEncodeException(
u"Unexpected bad-range first code unit of surrogate pair.");
}
const auto s0 = ExtractBits<std::uint16_t, 10, CodePoint>(cu1) << 10;
const auto s1 = ExtractBits<std::uint16_t, 10, CodePoint>(cu0);
result = s0 + s1 + 0x10000;
} else {
throw TextEncodeException(
u"Unexpected bad-range second code unit of surrogate pair.");
}
}
if (previous_position != nullptr) *previous_position = current;
return result;
}
void Utf8EncodeCodePointAppend(CodePoint code_point, std::string& str) {
if (!Utf8EncodeCodePointAppendWithFunc(code_point,
[&str](char c) { str.push_back(c); }))
throw TextEncodeException(u"Code point out of range.");
}
void Utf16EncodeCodePointAppend(CodePoint code_point, std::u16string& str) {
if (!Utf16EncodeCodePointAppendWithFunc(
code_point, [&str](char16_t c) { str.push_back(c); }))
throw TextEncodeException(u"Code point out of range.");
}
std::string ToUtf8(const char16_t* ptr, Index size) {
std::string result;
for (CodePoint cp : Utf16CodePointIterator(ptr, size)) {
Utf8EncodeCodePointAppend(cp, result);
}
return result;
}
std::u16string ToUtf16(const char* ptr, Index size) {
std::u16string result;
for (CodePoint cp : Utf8CodePointIterator(ptr, size)) {
Utf16EncodeCodePointAppend(cp, result);
}
return result;
}
bool Utf16IsValidInsertPosition(const char16_t* ptr, Index size,
Index position) {
if (position < 0) return false;
if (position > size) return false;
if (position == 0) return true;
if (position == size) return true;
return !IsUtf16SurrogatePairTrailing(ptr[position]);
}
Index Utf16BackwardUntil(const char16_t* ptr, Index size, Index position,
const std::function<bool(CodePoint)>& predicate) {
if (position <= 0) return position;
while (true) {
Index p = position;
auto c = Utf16PreviousCodePoint(ptr, size, p, &position);
if (predicate(c)) return p;
if (c == k_invalid_code_point) return p;
}
UnreachableCode();
}
Index Utf16ForwardUntil(const char16_t* ptr, Index size, Index position,
const std::function<bool(CodePoint)>& predicate) {
if (position >= size) return position;
while (true) {
Index p = position;
auto c = Utf16NextCodePoint(ptr, size, p, &position);
if (predicate(c)) return p;
if (c == k_invalid_code_point) return p;
}
UnreachableCode();
}
inline bool IsSpace(CodePoint c) { return c == 0x20 || c == 0xA; }
Index Utf16PreviousWord(const char16_t* ptr, Index size, Index position,
bool* is_space) {
if (position <= 0) return position;
auto c = Utf16PreviousCodePoint(ptr, size, position, nullptr);
if (IsSpace(c)) { // TODO: Currently only test against 0x20(space).
if (is_space) *is_space = true;
return Utf16BackwardUntil(ptr, size, position,
[](CodePoint c) { return !IsSpace(c); });
} else {
if (is_space) *is_space = false;
return Utf16BackwardUntil(ptr, size, position, IsSpace);
}
}
Index Utf16NextWord(const char16_t* ptr, Index size, Index position,
bool* is_space) {
if (position >= size) return position;
auto c = Utf16NextCodePoint(ptr, size, position, nullptr);
if (IsSpace(c)) { // TODO: Currently only test against 0x20(space).
if (is_space) *is_space = true;
return Utf16ForwardUntil(ptr, size, position,
[](CodePoint c) { return !IsSpace(c); });
} else {
if (is_space) *is_space = false;
return Utf16ForwardUntil(ptr, size, position, IsSpace);
}
}
char16_t ToLower(char16_t c) {
if (c >= u'A' && c <= u'Z') {
return c - u'A' + u'a';
}
return c;
}
char16_t ToUpper(char16_t c) {
if (c >= u'a' && c <= u'z') {
return c - u'a' + u'A';
}
return c;
}
bool IsWhitespace(char16_t c) {
return c == u' ' || c == u'\t' || c == u'\n' || c == u'\r';
}
Utf8CodePointIterator CreateUtf8Iterator(const std::byte* buffer, Index size) {
return Utf8CodePointIterator(reinterpret_cast<const char*>(buffer), size);
}
Utf8CodePointIterator CreateUtf8Iterator(const std::vector<std::byte>& buffer) {
return CreateUtf8Iterator(buffer.data(), buffer.size());
}
} // namespace cru
|