Coverage Report

Created: 2025-03-04 07:22

/src/serenity/AK/StringView.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org>
3
 *
4
 * SPDX-License-Identifier: BSD-2-Clause
5
 */
6
7
#include <AK/AnyOf.h>
8
#include <AK/ByteBuffer.h>
9
#include <AK/Find.h>
10
#include <AK/Function.h>
11
#include <AK/StringBuilder.h>
12
#include <AK/StringView.h>
13
#include <AK/Vector.h>
14
15
#ifndef KERNEL
16
#    include <AK/ByteString.h>
17
#    include <AK/DeprecatedFlyString.h>
18
#    include <AK/FlyString.h>
19
#    include <AK/String.h>
20
#endif
21
22
namespace AK {
23
24
#ifndef KERNEL
25
StringView::StringView(String const& string)
26
97.0M
    : m_characters(reinterpret_cast<char const*>(string.bytes().data()))
27
97.0M
    , m_length(string.bytes().size())
28
97.0M
{
29
97.0M
}
30
31
StringView::StringView(FlyString const& string)
32
0
    : m_characters(reinterpret_cast<char const*>(string.bytes().data()))
33
0
    , m_length(string.bytes().size())
34
0
{
35
0
}
36
37
StringView::StringView(ByteString const& string)
38
247M
    : m_characters(string.characters())
39
247M
    , m_length(string.length())
40
247M
{
41
247M
}
42
43
StringView::StringView(DeprecatedFlyString const& string)
44
2.82k
    : m_characters(string.characters())
45
2.82k
    , m_length(string.length())
46
2.82k
{
47
2.82k
}
48
#endif
49
50
StringView::StringView(ByteBuffer const& buffer)
51
38.6k
    : m_characters((char const*)buffer.data())
52
38.6k
    , m_length(buffer.size())
53
38.6k
{
54
38.6k
}
55
56
Vector<StringView> StringView::split_view(char const separator, SplitBehavior split_behavior) const
57
1.65M
{
58
1.65M
    StringView seperator_view { &separator, 1 };
59
1.65M
    return split_view(seperator_view, split_behavior);
60
1.65M
}
61
62
Vector<StringView> StringView::split_view(StringView separator, SplitBehavior split_behavior) const
63
1.70M
{
64
1.70M
    Vector<StringView> parts;
65
41.6M
    for_each_split_view(separator, split_behavior, [&](StringView view) {
66
41.6M
        parts.append(view);
67
41.6M
    });
68
1.70M
    return parts;
69
1.70M
}
70
71
template<typename Callback>
72
static void for_each_line(StringView string, Callback&& callback)
73
7.28k
{
74
7.28k
    char const* characters = string.characters_without_null_termination();
75
76
7.28k
    size_t substart = 0;
77
7.28k
    bool last_ch_was_cr = false;
78
79
298M
    for (size_t i = 0; i < string.length(); ++i) {
80
298M
        char ch = characters[i];
81
298M
        bool split_view = false;
82
83
298M
        switch (ch) {
84
47.4M
        case '\n':
85
47.4M
            if (last_ch_was_cr)
86
402k
                substart = i + 1;
87
47.0M
            else
88
47.0M
                split_view = true;
89
90
47.4M
            last_ch_was_cr = false;
91
47.4M
            break;
92
93
48.9M
        case '\r':
94
48.9M
            split_view = true;
95
48.9M
            last_ch_was_cr = true;
96
48.9M
            break;
97
98
202M
        default:
99
202M
            last_ch_was_cr = false;
100
202M
            break;
101
298M
        }
102
103
298M
        if (split_view) {
104
95.9M
            callback(string.substring_view(substart, i - substart));
105
95.9M
            substart = i + 1;
106
95.9M
        }
107
298M
    }
108
109
7.28k
    if (size_t taillen = string.length() - substart; taillen != 0)
110
6.41k
        callback(string.substring_view(substart, taillen));
111
7.28k
}
StringView.cpp:void AK::for_each_line<AK::StringView::lines(AK::StringView::ConsiderCarriageReturn) const::$_0>(AK::StringView, AK::StringView::lines(AK::StringView::ConsiderCarriageReturn) const::$_0&&)
Line
Count
Source
73
7.28k
{
74
7.28k
    char const* characters = string.characters_without_null_termination();
75
76
7.28k
    size_t substart = 0;
77
7.28k
    bool last_ch_was_cr = false;
78
79
298M
    for (size_t i = 0; i < string.length(); ++i) {
80
298M
        char ch = characters[i];
81
298M
        bool split_view = false;
82
83
298M
        switch (ch) {
84
47.4M
        case '\n':
85
47.4M
            if (last_ch_was_cr)
86
402k
                substart = i + 1;
87
47.0M
            else
88
47.0M
                split_view = true;
89
90
47.4M
            last_ch_was_cr = false;
91
47.4M
            break;
92
93
48.9M
        case '\r':
94
48.9M
            split_view = true;
95
48.9M
            last_ch_was_cr = true;
96
48.9M
            break;
97
98
202M
        default:
99
202M
            last_ch_was_cr = false;
100
202M
            break;
101
298M
        }
102
103
298M
        if (split_view) {
104
95.9M
            callback(string.substring_view(substart, i - substart));
105
95.9M
            substart = i + 1;
106
95.9M
        }
107
298M
    }
108
109
7.28k
    if (size_t taillen = string.length() - substart; taillen != 0)
110
6.41k
        callback(string.substring_view(substart, taillen));
111
7.28k
}
Unexecuted instantiation: StringView.cpp:void AK::for_each_line<AK::StringView::count_lines(AK::StringView::ConsiderCarriageReturn) const::$_0>(AK::StringView, AK::StringView::count_lines(AK::StringView::ConsiderCarriageReturn) const::$_0&&)
112
113
Vector<StringView> StringView::lines(ConsiderCarriageReturn consider_carriage_return) const
114
7.28k
{
115
7.28k
    if (is_empty())
116
0
        return {};
117
118
7.28k
    if (consider_carriage_return == ConsiderCarriageReturn::No)
119
0
        return split_view('\n', SplitBehavior::KeepEmpty);
120
121
7.28k
    Vector<StringView> lines;
122
95.9M
    for_each_line(*this, [&](auto line) { lines.append(line); });
123
124
7.28k
    return lines;
125
7.28k
}
126
127
size_t StringView::count_lines(ConsiderCarriageReturn consider_carriage_return) const
128
0
{
129
0
    if (is_empty())
130
0
        return 1;
131
132
0
    if (consider_carriage_return == ConsiderCarriageReturn::No)
133
0
        return count('\n') + 1;
134
135
0
    size_t lines = 0;
136
0
    for_each_line(*this, [&](auto) { ++lines; });
137
138
0
    return lines;
139
0
}
140
141
bool StringView::starts_with(char ch) const
142
134M
{
143
134M
    if (is_empty())
144
91.3M
        return false;
145
43.4M
    return ch == characters_without_null_termination()[0];
146
134M
}
147
148
bool StringView::starts_with(StringView str, CaseSensitivity case_sensitivity) const
149
395M
{
150
395M
    return StringUtils::starts_with(*this, str, case_sensitivity);
151
395M
}
152
153
bool StringView::ends_with(char ch) const
154
804k
{
155
804k
    if (is_empty())
156
540k
        return false;
157
264k
    return ch == characters_without_null_termination()[length() - 1];
158
804k
}
159
160
bool StringView::ends_with(StringView str, CaseSensitivity case_sensitivity) const
161
2.26M
{
162
2.26M
    return StringUtils::ends_with(*this, str, case_sensitivity);
163
2.26M
}
164
165
bool StringView::matches(StringView mask, Vector<MaskSpan>& mask_spans, CaseSensitivity case_sensitivity) const
166
0
{
167
0
    return StringUtils::matches(*this, mask, case_sensitivity, &mask_spans);
168
0
}
169
170
bool StringView::matches(StringView mask, CaseSensitivity case_sensitivity) const
171
0
{
172
0
    return StringUtils::matches(*this, mask, case_sensitivity);
173
0
}
174
175
bool StringView::contains(char needle) const
176
1.50G
{
177
3.40G
    for (char current : *this) {
178
3.40G
        if (current == needle)
179
234M
            return true;
180
3.40G
    }
181
1.27G
    return false;
182
1.50G
}
183
184
bool StringView::contains(u32 needle) const
185
38.7M
{
186
    // A code point should be at most four UTF-8 bytes, which easily fits into StringBuilder's inline-buffer.
187
    // Therefore, this will not allocate.
188
38.7M
    StringBuilder needle_builder;
189
38.7M
    auto result = needle_builder.try_append_code_point(needle);
190
38.7M
    if (result.is_error()) {
191
        // The needle is invalid, therefore the string does not contain it.
192
0
        return false;
193
0
    }
194
195
38.7M
    return contains(needle_builder.string_view());
196
38.7M
}
197
198
bool StringView::contains(StringView needle, CaseSensitivity case_sensitivity) const
199
48.5M
{
200
48.5M
    if (needle.length() == 1)
201
29.9M
        return contains(needle.characters_without_null_termination()[0]);
202
18.5M
    return StringUtils::contains(*this, needle, case_sensitivity);
203
48.5M
}
204
205
bool StringView::equals_ignoring_ascii_case(StringView other) const
206
94.2M
{
207
94.2M
    return StringUtils::equals_ignoring_ascii_case(*this, other);
208
94.2M
}
209
210
#ifndef KERNEL
211
ByteString StringView::to_lowercase_string() const
212
1.04k
{
213
1.04k
    return StringImpl::create_lowercased(characters_without_null_termination(), length()).release_nonnull();
214
1.04k
}
215
216
ByteString StringView::to_uppercase_string() const
217
0
{
218
0
    return StringImpl::create_uppercased(characters_without_null_termination(), length()).release_nonnull();
219
0
}
220
221
ByteString StringView::to_titlecase_string() const
222
0
{
223
0
    return StringUtils::to_titlecase(*this);
224
0
}
225
#endif
226
227
StringView StringView::substring_view_starting_from_substring(StringView substring) const
228
0
{
229
0
    char const* remaining_characters = substring.characters_without_null_termination();
230
0
    VERIFY(remaining_characters >= m_characters);
231
0
    VERIFY(remaining_characters <= m_characters + m_length);
232
0
    size_t remaining_length = m_length - (remaining_characters - m_characters);
233
0
    return { remaining_characters, remaining_length };
234
0
}
235
236
StringView StringView::substring_view_starting_after_substring(StringView substring) const
237
51.9M
{
238
51.9M
    char const* remaining_characters = substring.characters_without_null_termination() + substring.length();
239
51.9M
    VERIFY(remaining_characters >= m_characters);
240
51.9M
    VERIFY(remaining_characters <= m_characters + m_length);
241
51.9M
    size_t remaining_length = m_length - (remaining_characters - m_characters);
242
51.9M
    return { remaining_characters, remaining_length };
243
51.9M
}
244
245
bool StringView::copy_characters_to_buffer(char* buffer, size_t buffer_size) const
246
0
{
247
    // We must fit at least the NUL-terminator.
248
0
    VERIFY(buffer_size > 0);
249
250
0
    size_t characters_to_copy = min(m_length, buffer_size - 1);
251
0
    __builtin_memcpy(buffer, m_characters, characters_to_copy);
252
0
    buffer[characters_to_copy] = 0;
253
254
0
    return characters_to_copy == m_length;
255
0
}
256
257
#ifndef KERNEL
258
259
bool StringView::operator==(ByteString const& string) const
260
264M
{
261
264M
    return *this == string.view();
262
264M
}
263
264
426k
ByteString StringView::to_byte_string() const { return ByteString { *this }; }
265
266
ByteString StringView::replace(StringView needle, StringView replacement, ReplaceMode replace_mode) const
267
1
{
268
1
    return StringUtils::replace(*this, needle, replacement, replace_mode);
269
1
}
270
#endif
271
272
Vector<size_t> StringView::find_all(StringView needle) const
273
0
{
274
0
    return StringUtils::find_all(*this, needle);
275
0
}
276
277
Vector<StringView> StringView::split_view_if(Function<bool(char)> const& predicate, SplitBehavior split_behavior) const
278
0
{
279
0
    if (is_empty())
280
0
        return {};
281
282
0
    Vector<StringView> v;
283
0
    size_t substart = 0;
284
0
    bool keep_empty = has_flag(split_behavior, SplitBehavior::KeepEmpty);
285
0
    bool keep_separator = has_flag(split_behavior, SplitBehavior::KeepTrailingSeparator);
286
0
    for (size_t i = 0; i < length(); ++i) {
287
0
        char ch = characters_without_null_termination()[i];
288
0
        if (predicate(ch)) {
289
0
            size_t sublen = i - substart;
290
0
            if (sublen != 0 || keep_empty)
291
0
                v.append(substring_view(substart, keep_separator ? sublen + 1 : sublen));
292
0
            substart = i + 1;
293
0
        }
294
0
    }
295
0
    size_t taillen = length() - substart;
296
0
    if (taillen != 0 || keep_empty)
297
0
        v.append(substring_view(substart, taillen));
298
0
    return v;
299
0
}
300
301
}