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