Coverage Report

Created: 2022-05-20 06:13

/src/serenity/AK/StringBuilder.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/ByteBuffer.h>
8
#include <AK/Checked.h>
9
#include <AK/PrintfImplementation.h>
10
#include <AK/StdLibExtras.h>
11
#include <AK/StringBuilder.h>
12
#include <AK/StringView.h>
13
#include <AK/UnicodeUtils.h>
14
#include <AK/Utf32View.h>
15
16
#ifndef KERNEL
17
#    include <AK/String.h>
18
#    include <AK/Utf16View.h>
19
#endif
20
21
namespace AK {
22
23
inline ErrorOr<void> StringBuilder::will_append(size_t size)
24
9.57M
{
25
9.57M
    Checked<size_t> needed_capacity = m_buffer.size();
26
9.57M
    needed_capacity += size;
27
9.57M
    VERIFY(!needed_capacity.has_overflow());
28
    // Prefer to completely use the existing capacity first
29
9.57M
    if (needed_capacity <= m_buffer.capacity())
30
9.57M
        return {};
31
511
    Checked<size_t> expanded_capacity = needed_capacity;
32
511
    expanded_capacity *= 2;
33
511
    VERIFY(!expanded_capacity.has_overflow());
34
511
    TRY(m_buffer.try_ensure_capacity(expanded_capacity.value()));
35
0
    return {};
36
511
}
37
38
StringBuilder::StringBuilder(size_t initial_capacity)
39
2.41k
{
40
2.41k
    m_buffer.ensure_capacity(initial_capacity);
41
2.41k
}
42
43
ErrorOr<void> StringBuilder::try_append(StringView string)
44
0
{
45
0
    if (string.is_empty())
46
0
        return {};
47
0
    TRY(will_append(string.length()));
48
0
    TRY(m_buffer.try_append(string.characters_without_null_termination(), string.length()));
49
0
    return {};
50
0
}
51
52
ErrorOr<void> StringBuilder::try_append(char ch)
53
9.57M
{
54
9.57M
    TRY(will_append(1));
55
9.57M
    TRY(m_buffer.try_append(ch));
56
0
    return {};
57
9.57M
}
58
59
void StringBuilder::append(StringView string)
60
0
{
61
0
    MUST(try_append(string));
62
0
}
63
64
ErrorOr<void> StringBuilder::try_append(char const* characters, size_t length)
65
0
{
66
0
    return try_append(StringView { characters, length });
67
0
}
68
69
void StringBuilder::append(char const* characters, size_t length)
70
0
{
71
0
    MUST(try_append(characters, length));
72
0
}
73
74
void StringBuilder::append(char ch)
75
9.55M
{
76
9.55M
    MUST(try_append(ch));
77
9.55M
}
78
79
void StringBuilder::appendvf(char const* fmt, va_list ap)
80
0
{
81
0
    printf_internal([this](char*&, char ch) {
82
0
        append(ch);
83
0
    },
84
0
        nullptr, fmt, ap);
85
0
}
86
87
ByteBuffer StringBuilder::to_byte_buffer() const
88
0
{
89
    // FIXME: Handle OOM failure.
90
0
    return ByteBuffer::copy(data(), length()).release_value_but_fixme_should_propagate_errors();
91
0
}
92
93
#ifndef KERNEL
94
String StringBuilder::to_string() const
95
1.85k
{
96
1.85k
    if (is_empty())
97
107
        return String::empty();
98
1.74k
    return String((char const*)data(), length());
99
1.85k
}
100
101
String StringBuilder::build() const
102
0
{
103
0
    return to_string();
104
0
}
105
#endif
106
107
StringView StringBuilder::string_view() const
108
559
{
109
559
    return StringView { data(), m_buffer.size() };
110
559
}
111
112
void StringBuilder::clear()
113
0
{
114
0
    m_buffer.clear();
115
0
}
116
117
ErrorOr<void> StringBuilder::try_append_code_point(u32 code_point)
118
0
{
119
0
    auto nwritten = AK::UnicodeUtils::code_point_to_utf8(code_point, [this](char c) { append(c); });
120
0
    if (nwritten < 0) {
121
0
        TRY(try_append(0xef));
122
0
        TRY(try_append(0xbf));
123
0
        TRY(try_append(0xbd));
124
0
    }
125
0
    return {};
126
0
}
127
128
void StringBuilder::append_code_point(u32 code_point)
129
0
{
130
0
    MUST(try_append_code_point(code_point));
131
0
}
132
133
#ifndef KERNEL
134
ErrorOr<void> StringBuilder::try_append(Utf16View const& utf16_view)
135
0
{
136
0
    for (size_t i = 0; i < utf16_view.length_in_code_units();) {
137
0
        auto code_point = utf16_view.code_point_at(i);
138
0
        TRY(try_append_code_point(code_point));
139
140
0
        i += (code_point > 0xffff ? 2 : 1);
141
0
    }
142
0
    return {};
143
0
}
144
145
void StringBuilder::append(Utf16View const& utf16_view)
146
0
{
147
0
    MUST(try_append(utf16_view));
148
0
}
149
#endif
150
151
ErrorOr<void> StringBuilder::try_append(Utf32View const& utf32_view)
152
0
{
153
0
    for (size_t i = 0; i < utf32_view.length(); ++i) {
154
0
        auto code_point = utf32_view.code_points()[i];
155
0
        TRY(try_append_code_point(code_point));
156
0
    }
157
0
    return {};
158
0
}
159
160
void StringBuilder::append(Utf32View const& utf32_view)
161
0
{
162
0
    MUST(try_append(utf32_view));
163
0
}
164
165
void StringBuilder::append_as_lowercase(char ch)
166
0
{
167
0
    if (ch >= 'A' && ch <= 'Z')
168
0
        append(ch + 0x20);
169
0
    else
170
0
        append(ch);
171
0
}
172
173
void StringBuilder::append_escaped_for_json(StringView string)
174
0
{
175
0
    MUST(try_append_escaped_for_json(string));
176
0
}
177
178
ErrorOr<void> StringBuilder::try_append_escaped_for_json(StringView string)
179
0
{
180
0
    for (auto ch : string) {
181
0
        switch (ch) {
182
0
        case '\b':
183
0
            TRY(try_append("\\b"));
184
0
            break;
185
0
        case '\n':
186
0
            TRY(try_append("\\n"));
187
0
            break;
188
0
        case '\t':
189
0
            TRY(try_append("\\t"));
190
0
            break;
191
0
        case '\"':
192
0
            TRY(try_append("\\\""));
193
0
            break;
194
0
        case '\\':
195
0
            TRY(try_append("\\\\"));
196
0
            break;
197
0
        default:
198
0
            if (ch >= 0 && ch <= 0x1f)
199
0
                TRY(try_appendff("\\u{:04x}", ch));
200
0
            else
201
0
                TRY(try_append(ch));
202
0
        }
203
0
    }
204
0
    return {};
205
0
}
206
207
}