Serenity Operating System
1/*
2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#pragma once
8
9#include <AK/Format.h>
10#include <AK/Forward.h>
11#include <AK/RefPtr.h>
12#include <AK/StringBuilder.h>
13#include <AK/StringImpl.h>
14#include <AK/StringUtils.h>
15#include <AK/Traits.h>
16
17namespace AK {
18
19// DeprecatedString is a convenience wrapper around StringImpl, suitable for passing
20// around as a value type. It's basically the same as passing around a
21// RefPtr<StringImpl const>, with a bit of syntactic sugar.
22//
23// Note that StringImpl is an immutable object that cannot shrink or grow.
24// Its allocation size is snugly tailored to the specific string it contains.
25// Copying a DeprecatedString is very efficient, since the internal StringImpl is
26// retainable and so copying only requires modifying the ref count.
27//
28// There are three main ways to construct a new DeprecatedString:
29//
30// s = DeprecatedString("some literal");
31//
32// s = DeprecatedString::formatted("{} little piggies", m_piggies);
33//
34// StringBuilder builder;
35// builder.append("abc");
36// builder.append("123");
37// s = builder.to_deprecated_string();
38
39class DeprecatedString {
40public:
41 ~DeprecatedString() = default;
42
43 DeprecatedString() = default;
44
45 DeprecatedString(StringView view)
46 : m_impl(StringImpl::create(view.characters_without_null_termination(), view.length()))
47 {
48 }
49
50 DeprecatedString(DeprecatedString const& other)
51 : m_impl(other.m_impl)
52 {
53 }
54
55 DeprecatedString(DeprecatedString&& other)
56 : m_impl(move(other.m_impl))
57 {
58 }
59
60 DeprecatedString(char const* cstring, ShouldChomp shouldChomp = NoChomp)
61 : m_impl(StringImpl::create(cstring, shouldChomp))
62 {
63 }
64
65 DeprecatedString(char const* cstring, size_t length, ShouldChomp shouldChomp = NoChomp)
66 : m_impl(StringImpl::create(cstring, length, shouldChomp))
67 {
68 }
69
70 explicit DeprecatedString(ReadonlyBytes bytes, ShouldChomp shouldChomp = NoChomp)
71 : m_impl(StringImpl::create(bytes, shouldChomp))
72 {
73 }
74
75 DeprecatedString(StringImpl const& impl)
76 : m_impl(impl)
77 {
78 }
79
80 DeprecatedString(StringImpl const* impl)
81 : m_impl(impl)
82 {
83 }
84
85 DeprecatedString(RefPtr<StringImpl const>&& impl)
86 : m_impl(move(impl))
87 {
88 }
89
90 DeprecatedString(NonnullRefPtr<StringImpl const>&& impl)
91 : m_impl(move(impl))
92 {
93 }
94
95 DeprecatedString(DeprecatedFlyString const&);
96
97 static ErrorOr<DeprecatedString> from_utf8(ReadonlyBytes);
98
99 [[nodiscard]] static DeprecatedString repeated(char, size_t count);
100 [[nodiscard]] static DeprecatedString repeated(StringView, size_t count);
101
102 [[nodiscard]] static DeprecatedString bijective_base_from(size_t value, unsigned base = 26, StringView map = {});
103 [[nodiscard]] static DeprecatedString roman_number_from(size_t value);
104
105 template<class SeparatorType, class CollectionType>
106 [[nodiscard]] static DeprecatedString join(SeparatorType const& separator, CollectionType const& collection, StringView fmtstr = "{}"sv)
107 {
108 StringBuilder builder;
109 builder.join(separator, collection, fmtstr);
110 return builder.to_deprecated_string();
111 }
112
113 [[nodiscard]] bool matches(StringView mask, CaseSensitivity = CaseSensitivity::CaseInsensitive) const;
114 [[nodiscard]] bool matches(StringView mask, Vector<MaskSpan>&, CaseSensitivity = CaseSensitivity::CaseInsensitive) const;
115
116 template<typename T = int>
117 [[nodiscard]] Optional<T> to_int(TrimWhitespace = TrimWhitespace::Yes) const;
118 template<typename T = unsigned>
119 [[nodiscard]] Optional<T> to_uint(TrimWhitespace = TrimWhitespace::Yes) const;
120#ifndef KERNEL
121 [[nodiscard]] Optional<double> to_double(TrimWhitespace = TrimWhitespace::Yes) const;
122 [[nodiscard]] Optional<float> to_float(TrimWhitespace = TrimWhitespace::Yes) const;
123#endif
124
125 [[nodiscard]] DeprecatedString to_lowercase() const;
126 [[nodiscard]] DeprecatedString to_uppercase() const;
127 [[nodiscard]] DeprecatedString to_snakecase() const;
128 [[nodiscard]] DeprecatedString to_titlecase() const;
129 [[nodiscard]] DeprecatedString invert_case() const;
130
131 [[nodiscard]] bool is_whitespace() const { return StringUtils::is_whitespace(*this); }
132
133 [[nodiscard]] DeprecatedStringCodePointIterator code_points() const;
134
135 [[nodiscard]] DeprecatedString trim(StringView characters, TrimMode mode = TrimMode::Both) const
136 {
137 auto trimmed_view = StringUtils::trim(view(), characters, mode);
138 if (view() == trimmed_view)
139 return *this;
140 return trimmed_view;
141 }
142
143 [[nodiscard]] DeprecatedString trim_whitespace(TrimMode mode = TrimMode::Both) const
144 {
145 auto trimmed_view = StringUtils::trim_whitespace(view(), mode);
146 if (view() == trimmed_view)
147 return *this;
148 return trimmed_view;
149 }
150
151 [[nodiscard]] bool equals_ignoring_ascii_case(StringView) const;
152
153 [[nodiscard]] bool contains(StringView, CaseSensitivity = CaseSensitivity::CaseSensitive) const;
154 [[nodiscard]] bool contains(char, CaseSensitivity = CaseSensitivity::CaseSensitive) const;
155
156 [[nodiscard]] Vector<DeprecatedString> split_limit(char separator, size_t limit, SplitBehavior = SplitBehavior::Nothing) const;
157 [[nodiscard]] Vector<DeprecatedString> split(char separator, SplitBehavior = SplitBehavior::Nothing) const;
158 [[nodiscard]] Vector<StringView> split_view(char separator, SplitBehavior = SplitBehavior::Nothing) const;
159 [[nodiscard]] Vector<StringView> split_view(Function<bool(char)> separator, SplitBehavior = SplitBehavior::Nothing) const;
160
161 [[nodiscard]] Optional<size_t> find(char needle, size_t start = 0) const { return StringUtils::find(*this, needle, start); }
162 [[nodiscard]] Optional<size_t> find(StringView needle, size_t start = 0) const { return StringUtils::find(*this, needle, start); }
163 [[nodiscard]] Optional<size_t> find_last(char needle) const { return StringUtils::find_last(*this, needle); }
164 [[nodiscard]] Optional<size_t> find_last(StringView needle) const { return StringUtils::find_last(*this, needle); }
165 Vector<size_t> find_all(StringView needle) const;
166 using SearchDirection = StringUtils::SearchDirection;
167 [[nodiscard]] Optional<size_t> find_any_of(StringView needles, SearchDirection direction) const { return StringUtils::find_any_of(*this, needles, direction); }
168
169 [[nodiscard]] StringView find_last_split_view(char separator) const { return view().find_last_split_view(separator); }
170
171 [[nodiscard]] DeprecatedString substring(size_t start, size_t length) const;
172 [[nodiscard]] DeprecatedString substring(size_t start) const;
173 [[nodiscard]] StringView substring_view(size_t start, size_t length) const;
174 [[nodiscard]] StringView substring_view(size_t start) const;
175
176 [[nodiscard]] bool is_null() const { return !m_impl; }
177 [[nodiscard]] ALWAYS_INLINE bool is_empty() const { return length() == 0; }
178 [[nodiscard]] ALWAYS_INLINE size_t length() const { return m_impl ? m_impl->length() : 0; }
179 // Includes NUL-terminator, if non-nullptr.
180 [[nodiscard]] ALWAYS_INLINE char const* characters() const { return m_impl ? m_impl->characters() : nullptr; }
181
182 [[nodiscard]] bool copy_characters_to_buffer(char* buffer, size_t buffer_size) const;
183
184 [[nodiscard]] ALWAYS_INLINE ReadonlyBytes bytes() const
185 {
186 if (m_impl) {
187 return m_impl->bytes();
188 }
189 return {};
190 }
191
192 [[nodiscard]] ALWAYS_INLINE char const& operator[](size_t i) const
193 {
194 VERIFY(!is_null());
195 return (*m_impl)[i];
196 }
197
198 [[nodiscard]] ALWAYS_INLINE u8 byte_at(size_t i) const
199 {
200 VERIFY(!is_null());
201 return bit_cast<u8>((*m_impl)[i]);
202 }
203
204 using ConstIterator = SimpleIterator<const DeprecatedString, char const>;
205
206 [[nodiscard]] constexpr ConstIterator begin() const { return ConstIterator::begin(*this); }
207 [[nodiscard]] constexpr ConstIterator end() const { return ConstIterator::end(*this); }
208
209 [[nodiscard]] bool starts_with(StringView, CaseSensitivity = CaseSensitivity::CaseSensitive) const;
210 [[nodiscard]] bool ends_with(StringView, CaseSensitivity = CaseSensitivity::CaseSensitive) const;
211 [[nodiscard]] bool starts_with(char) const;
212 [[nodiscard]] bool ends_with(char) const;
213
214 bool operator==(DeprecatedString const&) const;
215
216 bool operator==(StringView) const;
217
218 bool operator==(DeprecatedFlyString const&) const;
219
220 bool operator<(DeprecatedString const&) const;
221 bool operator>=(DeprecatedString const& other) const { return !(*this < other); }
222 bool operator>=(char const* other) const { return !(*this < other); }
223
224 bool operator>(DeprecatedString const&) const;
225 bool operator<=(DeprecatedString const& other) const { return !(*this > other); }
226 bool operator<=(char const* other) const { return !(*this > other); }
227
228 bool operator==(char const* cstring) const;
229
230 [[nodiscard]] DeprecatedString isolated_copy() const;
231
232 [[nodiscard]] static DeprecatedString empty()
233 {
234 return StringImpl::the_empty_stringimpl();
235 }
236
237 [[nodiscard]] StringImpl const* impl() const { return m_impl.ptr(); }
238
239 DeprecatedString& operator=(DeprecatedString&& other)
240 {
241 if (this != &other)
242 m_impl = move(other.m_impl);
243 return *this;
244 }
245
246 DeprecatedString& operator=(DeprecatedString const& other)
247 {
248 if (this != &other)
249 m_impl = const_cast<DeprecatedString&>(other).m_impl;
250 return *this;
251 }
252
253 DeprecatedString& operator=(nullptr_t)
254 {
255 m_impl = nullptr;
256 return *this;
257 }
258
259 DeprecatedString& operator=(ReadonlyBytes bytes)
260 {
261 m_impl = StringImpl::create(bytes);
262 return *this;
263 }
264
265 [[nodiscard]] u32 hash() const
266 {
267 if (!m_impl)
268 return 0;
269 return m_impl->hash();
270 }
271
272 [[nodiscard]] ByteBuffer to_byte_buffer() const;
273
274 template<typename BufferType>
275 [[nodiscard]] static DeprecatedString copy(BufferType const& buffer, ShouldChomp should_chomp = NoChomp)
276 {
277 if (buffer.is_empty())
278 return empty();
279 return DeprecatedString(reinterpret_cast<char const*>(buffer.data()), buffer.size(), should_chomp);
280 }
281
282 [[nodiscard]] static DeprecatedString vformatted(StringView fmtstr, TypeErasedFormatParams&);
283
284 template<typename... Parameters>
285 [[nodiscard]] static DeprecatedString formatted(CheckedFormatString<Parameters...>&& fmtstr, Parameters const&... parameters)
286 {
287 VariadicFormatParams<AllowDebugOnlyFormatters::No, Parameters...> variadic_format_parameters { parameters... };
288 return vformatted(fmtstr.view(), variadic_format_parameters);
289 }
290
291 template<Arithmetic T>
292 [[nodiscard]] static DeprecatedString number(T value)
293 {
294 return formatted("{}", value);
295 }
296
297 [[nodiscard]] StringView view() const
298 {
299 return { characters(), length() };
300 }
301
302 [[nodiscard]] DeprecatedString replace(StringView needle, StringView replacement, ReplaceMode replace_mode = ReplaceMode::All) const { return StringUtils::replace(*this, needle, replacement, replace_mode); }
303 [[nodiscard]] size_t count(StringView needle) const { return StringUtils::count(*this, needle); }
304 [[nodiscard]] DeprecatedString reverse() const;
305
306 template<typename... Ts>
307 [[nodiscard]] ALWAYS_INLINE constexpr bool is_one_of(Ts&&... strings) const
308 {
309 return (... || this->operator==(forward<Ts>(strings)));
310 }
311
312 template<typename... Ts>
313 [[nodiscard]] ALWAYS_INLINE constexpr bool is_one_of_ignoring_ascii_case(Ts&&... strings) const
314 {
315 return (... ||
316 [this, &strings]() -> bool {
317 if constexpr (requires(Ts a) { a.view()->StringView; })
318 return this->equals_ignoring_ascii_case(forward<Ts>(strings.view()));
319 else
320 return this->equals_ignoring_ascii_case(forward<Ts>(strings));
321 }());
322 }
323
324private:
325 RefPtr<StringImpl const> m_impl;
326};
327
328template<>
329struct Traits<DeprecatedString> : public GenericTraits<DeprecatedString> {
330 static unsigned hash(DeprecatedString const& s) { return s.impl() ? s.impl()->hash() : 0; }
331};
332
333// FIXME: Rename this to indicate that it's about ASCII-only case insensitivity.
334struct CaseInsensitiveStringTraits : public Traits<DeprecatedString> {
335 static unsigned hash(DeprecatedString const& s) { return s.impl() ? s.impl()->case_insensitive_hash() : 0; }
336 static bool equals(DeprecatedString const& a, DeprecatedString const& b) { return a.equals_ignoring_ascii_case(b); }
337};
338
339DeprecatedString escape_html_entities(StringView html);
340
341}
342
343#if USING_AK_GLOBALLY
344using AK::CaseInsensitiveStringTraits;
345using AK::escape_html_entities;
346#endif