/src/serenity/AK/String.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> |
3 | | * |
4 | | * SPDX-License-Identifier: BSD-2-Clause |
5 | | */ |
6 | | |
7 | | #include <AK/ByteBuffer.h> |
8 | | #include <AK/FlyString.h> |
9 | | #include <AK/Format.h> |
10 | | #include <AK/Function.h> |
11 | | #include <AK/Memory.h> |
12 | | #include <AK/StdLibExtras.h> |
13 | | #include <AK/String.h> |
14 | | #include <AK/StringView.h> |
15 | | #include <AK/Vector.h> |
16 | | |
17 | | namespace AK { |
18 | | |
19 | | bool String::operator==(FlyString const& fly_string) const |
20 | 0 | { |
21 | 0 | return m_impl == fly_string.impl() || view() == fly_string.view(); |
22 | 0 | } |
23 | | |
24 | | bool String::operator==(String const& other) const |
25 | 0 | { |
26 | 0 | return m_impl == other.impl() || view() == other.view(); |
27 | 0 | } |
28 | | |
29 | | bool String::operator==(StringView other) const |
30 | 0 | { |
31 | 0 | return view() == other; |
32 | 0 | } |
33 | | |
34 | | bool String::operator<(String const& other) const |
35 | 0 | { |
36 | 0 | return view() < other.view(); |
37 | 0 | } |
38 | | |
39 | | bool String::operator>(String const& other) const |
40 | 0 | { |
41 | 0 | return view() > other.view(); |
42 | 0 | } |
43 | | |
44 | | bool String::copy_characters_to_buffer(char* buffer, size_t buffer_size) const |
45 | 0 | { |
46 | | // We must fit at least the NUL-terminator. |
47 | 0 | VERIFY(buffer_size > 0); |
48 | | |
49 | 0 | size_t characters_to_copy = min(length(), buffer_size - 1); |
50 | 0 | __builtin_memcpy(buffer, characters(), characters_to_copy); |
51 | 0 | buffer[characters_to_copy] = 0; |
52 | |
|
53 | 0 | return characters_to_copy == length(); |
54 | 0 | } |
55 | | |
56 | | String String::isolated_copy() const |
57 | 0 | { |
58 | 0 | if (!m_impl) |
59 | 0 | return {}; |
60 | 0 | if (!m_impl->length()) |
61 | 0 | return empty(); |
62 | 0 | char* buffer; |
63 | 0 | auto impl = StringImpl::create_uninitialized(length(), buffer); |
64 | 0 | memcpy(buffer, m_impl->characters(), m_impl->length()); |
65 | 0 | return String(move(*impl)); |
66 | 0 | } |
67 | | |
68 | | String String::substring(size_t start, size_t length) const |
69 | 0 | { |
70 | 0 | if (!length) |
71 | 0 | return String::empty(); |
72 | 0 | VERIFY(m_impl); |
73 | 0 | VERIFY(!Checked<size_t>::addition_would_overflow(start, length)); |
74 | 0 | VERIFY(start + length <= m_impl->length()); |
75 | 0 | return { characters() + start, length }; |
76 | 0 | } |
77 | | |
78 | | String String::substring(size_t start) const |
79 | 0 | { |
80 | 0 | VERIFY(m_impl); |
81 | 0 | VERIFY(start <= length()); |
82 | 0 | return { characters() + start, length() - start }; |
83 | 0 | } |
84 | | |
85 | | StringView String::substring_view(size_t start, size_t length) const |
86 | 0 | { |
87 | 0 | VERIFY(m_impl); |
88 | 0 | VERIFY(!Checked<size_t>::addition_would_overflow(start, length)); |
89 | 0 | VERIFY(start + length <= m_impl->length()); |
90 | 0 | return { characters() + start, length }; |
91 | 0 | } |
92 | | |
93 | | StringView String::substring_view(size_t start) const |
94 | 0 | { |
95 | 0 | VERIFY(m_impl); |
96 | 0 | VERIFY(start <= length()); |
97 | 0 | return { characters() + start, length() - start }; |
98 | 0 | } |
99 | | |
100 | | Vector<String> String::split(char separator, bool keep_empty) const |
101 | 0 | { |
102 | 0 | return split_limit(separator, 0, keep_empty); |
103 | 0 | } |
104 | | |
105 | | Vector<String> String::split_limit(char separator, size_t limit, bool keep_empty) const |
106 | 0 | { |
107 | 0 | if (is_empty()) |
108 | 0 | return {}; |
109 | | |
110 | 0 | Vector<String> v; |
111 | 0 | size_t substart = 0; |
112 | 0 | for (size_t i = 0; i < length() && (v.size() + 1) != limit; ++i) { |
113 | 0 | char ch = characters()[i]; |
114 | 0 | if (ch == separator) { |
115 | 0 | size_t sublen = i - substart; |
116 | 0 | if (sublen != 0 || keep_empty) |
117 | 0 | v.append(substring(substart, sublen)); |
118 | 0 | substart = i + 1; |
119 | 0 | } |
120 | 0 | } |
121 | 0 | size_t taillen = length() - substart; |
122 | 0 | if (taillen != 0 || keep_empty) |
123 | 0 | v.append(substring(substart, taillen)); |
124 | 0 | return v; |
125 | 0 | } |
126 | | |
127 | | Vector<StringView> String::split_view(Function<bool(char)> separator, bool keep_empty) const |
128 | 0 | { |
129 | 0 | if (is_empty()) |
130 | 0 | return {}; |
131 | | |
132 | 0 | Vector<StringView> v; |
133 | 0 | size_t substart = 0; |
134 | 0 | for (size_t i = 0; i < length(); ++i) { |
135 | 0 | char ch = characters()[i]; |
136 | 0 | if (separator(ch)) { |
137 | 0 | size_t sublen = i - substart; |
138 | 0 | if (sublen != 0 || keep_empty) |
139 | 0 | v.append(substring_view(substart, sublen)); |
140 | 0 | substart = i + 1; |
141 | 0 | } |
142 | 0 | } |
143 | 0 | size_t taillen = length() - substart; |
144 | 0 | if (taillen != 0 || keep_empty) |
145 | 0 | v.append(substring_view(substart, taillen)); |
146 | 0 | return v; |
147 | 0 | } |
148 | | |
149 | | Vector<StringView> String::split_view(char const separator, bool keep_empty) const |
150 | 0 | { |
151 | 0 | return split_view([separator](char ch) { return ch == separator; }, keep_empty); |
152 | 0 | } |
153 | | |
154 | | ByteBuffer String::to_byte_buffer() const |
155 | 0 | { |
156 | 0 | if (!m_impl) |
157 | 0 | return {}; |
158 | | // FIXME: Handle OOM failure. |
159 | 0 | return ByteBuffer::copy(bytes()).release_value_but_fixme_should_propagate_errors(); |
160 | 0 | } |
161 | | |
162 | | template<typename T> |
163 | | Optional<T> String::to_int(TrimWhitespace trim_whitespace) const |
164 | 0 | { |
165 | 0 | return StringUtils::convert_to_int<T>(view(), trim_whitespace); |
166 | 0 | } Unexecuted instantiation: AK::Optional<signed char> AK::String::to_int<signed char>(AK::TrimWhitespace) const Unexecuted instantiation: AK::Optional<short> AK::String::to_int<short>(AK::TrimWhitespace) const Unexecuted instantiation: AK::Optional<int> AK::String::to_int<int>(AK::TrimWhitespace) const Unexecuted instantiation: AK::Optional<long> AK::String::to_int<long>(AK::TrimWhitespace) const |
167 | | |
168 | | template Optional<i8> String::to_int(TrimWhitespace) const; |
169 | | template Optional<i16> String::to_int(TrimWhitespace) const; |
170 | | template Optional<i32> String::to_int(TrimWhitespace) const; |
171 | | template Optional<i64> String::to_int(TrimWhitespace) const; |
172 | | |
173 | | template<typename T> |
174 | | Optional<T> String::to_uint(TrimWhitespace trim_whitespace) const |
175 | 0 | { |
176 | 0 | return StringUtils::convert_to_uint<T>(view(), trim_whitespace); |
177 | 0 | } Unexecuted instantiation: AK::Optional<unsigned char> AK::String::to_uint<unsigned char>(AK::TrimWhitespace) const Unexecuted instantiation: AK::Optional<unsigned short> AK::String::to_uint<unsigned short>(AK::TrimWhitespace) const Unexecuted instantiation: AK::Optional<unsigned int> AK::String::to_uint<unsigned int>(AK::TrimWhitespace) const Unexecuted instantiation: AK::Optional<unsigned long> AK::String::to_uint<unsigned long>(AK::TrimWhitespace) const Unexecuted instantiation: AK::Optional<unsigned long long> AK::String::to_uint<unsigned long long>(AK::TrimWhitespace) const |
178 | | |
179 | | template Optional<u8> String::to_uint(TrimWhitespace) const; |
180 | | template Optional<u16> String::to_uint(TrimWhitespace) const; |
181 | | template Optional<u32> String::to_uint(TrimWhitespace) const; |
182 | | template Optional<unsigned long> String::to_uint(TrimWhitespace) const; |
183 | | template Optional<unsigned long long> String::to_uint(TrimWhitespace) const; |
184 | | |
185 | | bool String::starts_with(StringView str, CaseSensitivity case_sensitivity) const |
186 | 0 | { |
187 | 0 | return StringUtils::starts_with(*this, str, case_sensitivity); |
188 | 0 | } |
189 | | |
190 | | bool String::starts_with(char ch) const |
191 | 0 | { |
192 | 0 | if (is_empty()) |
193 | 0 | return false; |
194 | 0 | return characters()[0] == ch; |
195 | 0 | } |
196 | | |
197 | | bool String::ends_with(StringView str, CaseSensitivity case_sensitivity) const |
198 | 0 | { |
199 | 0 | return StringUtils::ends_with(*this, str, case_sensitivity); |
200 | 0 | } |
201 | | |
202 | | bool String::ends_with(char ch) const |
203 | 0 | { |
204 | 0 | if (is_empty()) |
205 | 0 | return false; |
206 | 0 | return characters()[length() - 1] == ch; |
207 | 0 | } |
208 | | |
209 | | String String::repeated(char ch, size_t count) |
210 | 0 | { |
211 | 0 | if (!count) |
212 | 0 | return empty(); |
213 | 0 | char* buffer; |
214 | 0 | auto impl = StringImpl::create_uninitialized(count, buffer); |
215 | 0 | memset(buffer, ch, count); |
216 | 0 | return *impl; |
217 | 0 | } |
218 | | |
219 | | String String::repeated(StringView string, size_t count) |
220 | 0 | { |
221 | 0 | if (!count || string.is_empty()) |
222 | 0 | return empty(); |
223 | 0 | char* buffer; |
224 | 0 | auto impl = StringImpl::create_uninitialized(count * string.length(), buffer); |
225 | 0 | for (size_t i = 0; i < count; i++) |
226 | 0 | __builtin_memcpy(buffer + i * string.length(), string.characters_without_null_termination(), string.length()); |
227 | 0 | return *impl; |
228 | 0 | } |
229 | | |
230 | | String String::bijective_base_from(size_t value, unsigned base, StringView map) |
231 | 0 | { |
232 | 0 | if (map.is_null()) |
233 | 0 | map = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"sv; |
234 | |
|
235 | 0 | VERIFY(base >= 2 && base <= map.length()); |
236 | | |
237 | | // The '8 bits per byte' assumption may need to go? |
238 | 0 | Array<char, round_up_to_power_of_two(sizeof(size_t) * 8 + 1, 2)> buffer; |
239 | 0 | size_t i = 0; |
240 | 0 | do { |
241 | 0 | buffer[i++] = map[value % base]; |
242 | 0 | value /= base; |
243 | 0 | } while (value > 0); |
244 | | |
245 | | // NOTE: Weird as this may seem, the thing that comes after 'Z' is 'AA', which as a number would be '00' |
246 | | // to make this work, only the most significant digit has to be in a range of (1..25) as opposed to (0..25), |
247 | | // but only if it's not the only digit in the string. |
248 | 0 | if (i > 1) |
249 | 0 | --buffer[i - 1]; |
250 | |
|
251 | 0 | for (size_t j = 0; j < i / 2; ++j) |
252 | 0 | swap(buffer[j], buffer[i - j - 1]); |
253 | |
|
254 | 0 | return String { ReadonlyBytes(buffer.data(), i) }; |
255 | 0 | } |
256 | | |
257 | | String String::roman_number_from(size_t value) |
258 | 0 | { |
259 | 0 | if (value > 3999) |
260 | 0 | return String::number(value); |
261 | | |
262 | 0 | StringBuilder builder; |
263 | |
|
264 | 0 | while (value > 0) { |
265 | 0 | if (value >= 1000) { |
266 | 0 | builder.append('M'); |
267 | 0 | value -= 1000; |
268 | 0 | } else if (value >= 900) { |
269 | 0 | builder.append("CM"sv); |
270 | 0 | value -= 900; |
271 | 0 | } else if (value >= 500) { |
272 | 0 | builder.append('D'); |
273 | 0 | value -= 500; |
274 | 0 | } else if (value >= 400) { |
275 | 0 | builder.append("CD"sv); |
276 | 0 | value -= 400; |
277 | 0 | } else if (value >= 100) { |
278 | 0 | builder.append('C'); |
279 | 0 | value -= 100; |
280 | 0 | } else if (value >= 90) { |
281 | 0 | builder.append("XC"sv); |
282 | 0 | value -= 90; |
283 | 0 | } else if (value >= 50) { |
284 | 0 | builder.append('L'); |
285 | 0 | value -= 50; |
286 | 0 | } else if (value >= 40) { |
287 | 0 | builder.append("XL"sv); |
288 | 0 | value -= 40; |
289 | 0 | } else if (value >= 10) { |
290 | 0 | builder.append('X'); |
291 | 0 | value -= 10; |
292 | 0 | } else if (value == 9) { |
293 | 0 | builder.append("IX"sv); |
294 | 0 | value -= 9; |
295 | 0 | } else if (value >= 5 && value <= 8) { |
296 | 0 | builder.append('V'); |
297 | 0 | value -= 5; |
298 | 0 | } else if (value == 4) { |
299 | 0 | builder.append("IV"sv); |
300 | 0 | value -= 4; |
301 | 0 | } else if (value <= 3) { |
302 | 0 | builder.append('I'); |
303 | 0 | value -= 1; |
304 | 0 | } |
305 | 0 | } |
306 | |
|
307 | 0 | return builder.to_string(); |
308 | 0 | } |
309 | | |
310 | | bool String::matches(StringView mask, Vector<MaskSpan>& mask_spans, CaseSensitivity case_sensitivity) const |
311 | 0 | { |
312 | 0 | return StringUtils::matches(*this, mask, case_sensitivity, &mask_spans); |
313 | 0 | } |
314 | | |
315 | | bool String::matches(StringView mask, CaseSensitivity case_sensitivity) const |
316 | 0 | { |
317 | 0 | return StringUtils::matches(*this, mask, case_sensitivity); |
318 | 0 | } |
319 | | |
320 | | bool String::contains(StringView needle, CaseSensitivity case_sensitivity) const |
321 | 1.61k | { |
322 | 1.61k | return StringUtils::contains(*this, needle, case_sensitivity); |
323 | 1.61k | } |
324 | | |
325 | | bool String::contains(char needle, CaseSensitivity case_sensitivity) const |
326 | 0 | { |
327 | 0 | return StringUtils::contains(*this, StringView(&needle, 1), case_sensitivity); |
328 | 0 | } |
329 | | |
330 | | bool String::equals_ignoring_case(StringView other) const |
331 | 0 | { |
332 | 0 | return StringUtils::equals_ignoring_case(view(), other); |
333 | 0 | } |
334 | | |
335 | | String String::reverse() const |
336 | 0 | { |
337 | 0 | StringBuilder reversed_string(length()); |
338 | 0 | for (size_t i = length(); i-- > 0;) { |
339 | 0 | reversed_string.append(characters()[i]); |
340 | 0 | } |
341 | 0 | return reversed_string.to_string(); |
342 | 0 | } |
343 | | |
344 | | String escape_html_entities(StringView html) |
345 | 0 | { |
346 | 0 | StringBuilder builder; |
347 | 0 | for (size_t i = 0; i < html.length(); ++i) { |
348 | 0 | if (html[i] == '<') |
349 | 0 | builder.append("<"); |
350 | 0 | else if (html[i] == '>') |
351 | 0 | builder.append(">"); |
352 | 0 | else if (html[i] == '&') |
353 | 0 | builder.append("&"); |
354 | 0 | else if (html[i] == '"') |
355 | 0 | builder.append("""); |
356 | 0 | else |
357 | 0 | builder.append(html[i]); |
358 | 0 | } |
359 | 0 | return builder.to_string(); |
360 | 0 | } |
361 | | |
362 | | String::String(FlyString const& string) |
363 | | : m_impl(string.impl()) |
364 | 0 | { |
365 | 0 | } |
366 | | |
367 | | String String::to_lowercase() const |
368 | 0 | { |
369 | 0 | if (!m_impl) |
370 | 0 | return {}; |
371 | 0 | return m_impl->to_lowercase(); |
372 | 0 | } |
373 | | |
374 | | String String::to_uppercase() const |
375 | 0 | { |
376 | 0 | if (!m_impl) |
377 | 0 | return {}; |
378 | 0 | return m_impl->to_uppercase(); |
379 | 0 | } |
380 | | |
381 | | String String::to_snakecase() const |
382 | 0 | { |
383 | 0 | return StringUtils::to_snakecase(*this); |
384 | 0 | } |
385 | | |
386 | | String String::to_titlecase() const |
387 | 0 | { |
388 | 0 | return StringUtils::to_titlecase(*this); |
389 | 0 | } |
390 | | |
391 | | bool operator<(char const* characters, String const& string) |
392 | 0 | { |
393 | 0 | return string.view() > characters; |
394 | 0 | } |
395 | | |
396 | | bool operator>=(char const* characters, String const& string) |
397 | 0 | { |
398 | 0 | return string.view() <= characters; |
399 | 0 | } |
400 | | |
401 | | bool operator>(char const* characters, String const& string) |
402 | 0 | { |
403 | 0 | return string.view() < characters; |
404 | 0 | } |
405 | | |
406 | | bool operator<=(char const* characters, String const& string) |
407 | 0 | { |
408 | 0 | return string.view() >= characters; |
409 | 0 | } |
410 | | |
411 | | bool String::operator==(char const* cstring) const |
412 | 22.5M | { |
413 | 22.5M | return view() == cstring; |
414 | 22.5M | } |
415 | | |
416 | | InputStream& operator>>(InputStream& stream, String& string) |
417 | 0 | { |
418 | 0 | StringBuilder builder; |
419 | |
|
420 | 0 | for (;;) { |
421 | 0 | char next_char; |
422 | 0 | stream >> next_char; |
423 | |
|
424 | 0 | if (stream.has_any_error()) { |
425 | 0 | stream.set_fatal_error(); |
426 | 0 | string = nullptr; |
427 | 0 | return stream; |
428 | 0 | } |
429 | | |
430 | 0 | if (next_char) { |
431 | 0 | builder.append(next_char); |
432 | 0 | } else { |
433 | 0 | string = builder.to_string(); |
434 | 0 | return stream; |
435 | 0 | } |
436 | 0 | } |
437 | 0 | } |
438 | | |
439 | | String String::vformatted(StringView fmtstr, TypeErasedFormatParams& params) |
440 | 0 | { |
441 | 0 | StringBuilder builder; |
442 | 0 | MUST(vformat(builder, fmtstr, params)); |
443 | 0 | return builder.to_string(); |
444 | 0 | } |
445 | | |
446 | | Vector<size_t> String::find_all(StringView needle) const |
447 | 0 | { |
448 | 0 | return StringUtils::find_all(*this, needle); |
449 | 0 | } |
450 | | |
451 | | } |