Serenity Operating System
1/*
2 * Copyright (c) 2018-2022, Andreas Kling <kling@serenityos.org>
3 * Copyright (c) 2023, Tim Flynn <trflynn89@serenityos.org>
4 *
5 * SPDX-License-Identifier: BSD-2-Clause
6 */
7
8#pragma once
9
10#include <AK/CharacterTypes.h>
11#include <AK/Concepts.h>
12#include <AK/Format.h>
13#include <AK/Forward.h>
14#include <AK/Optional.h>
15#include <AK/RefCounted.h>
16#include <AK/Span.h>
17#include <AK/StringBuilder.h>
18#include <AK/StringUtils.h>
19#include <AK/StringView.h>
20#include <AK/Traits.h>
21#include <AK/Types.h>
22#include <AK/UnicodeUtils.h>
23#include <AK/Utf8View.h>
24#include <AK/Vector.h>
25
26namespace AK {
27
28namespace Detail {
29class StringData;
30}
31
32// FIXME: Remove this when Apple Clang and OpenBSD Clang fully supports consteval.
33#if defined(AK_OS_MACOS) || defined(AK_OS_OPENBSD)
34# define AK_SHORT_STRING_CONSTEVAL constexpr
35#else
36# define AK_SHORT_STRING_CONSTEVAL consteval
37#endif
38
39// String is a strongly owned sequence of Unicode code points encoded as UTF-8.
40// The data may or may not be heap-allocated, and may or may not be reference counted.
41// There is no guarantee that the underlying bytes are null-terminated.
42class String {
43public:
44 // NOTE: For short strings, we avoid heap allocations by storing them in the data pointer slot.
45 static constexpr size_t MAX_SHORT_STRING_BYTE_COUNT = sizeof(Detail::StringData*) - 1;
46
47 String(String const&);
48 String(String&&);
49
50 String& operator=(String&&);
51 String& operator=(String const&);
52
53 constexpr ~String()
54 {
55 if (!is_constant_evaluated())
56 destroy_string();
57 }
58
59 // Creates an empty (zero-length) String.
60 constexpr String()
61 : String(ShortString { SHORT_STRING_FLAG, {} })
62 {
63 }
64
65 // Creates a new String from a sequence of UTF-8 encoded code points.
66 static ErrorOr<String> from_utf8(StringView);
67
68 // Creates a new String by reading byte_count bytes from a UTF-8 encoded Stream.
69 static ErrorOr<String> from_stream(Stream&, size_t byte_count);
70
71 // Creates a new String from a short sequence of UTF-8 encoded code points. If the provided string
72 // does not fit in the short string storage, a compilation error will be emitted.
73 static AK_SHORT_STRING_CONSTEVAL String from_utf8_short_string(StringView string)
74 {
75 VERIFY(string.length() <= MAX_SHORT_STRING_BYTE_COUNT);
76 VERIFY(Utf8View { string }.validate());
77
78 ShortString short_string;
79 for (size_t i = 0; i < string.length(); ++i)
80 short_string.storage[i] = string.characters_without_null_termination()[i];
81 short_string.byte_count_and_short_string_flag = (string.length() << 1) | SHORT_STRING_FLAG;
82
83 return String { short_string };
84 }
85
86 // Creates a new String from a single code point.
87 static constexpr String from_code_point(u32 code_point)
88 {
89 VERIFY(is_unicode(code_point));
90
91 ShortString short_string;
92 size_t i = 0;
93
94 auto length = UnicodeUtils::code_point_to_utf8(code_point, [&](auto byte) {
95 short_string.storage[i++] = static_cast<u8>(byte);
96 });
97 short_string.byte_count_and_short_string_flag = (length << 1) | SHORT_STRING_FLAG;
98
99 return String { short_string };
100 }
101
102 // Creates a new String with a single code point repeated N times.
103 static ErrorOr<String> repeated(u32 code_point, size_t count);
104
105 // Creates a new String by case-transforming this String. Using these methods require linking LibUnicode into your application.
106 ErrorOr<String> to_lowercase(Optional<StringView> const& locale = {}) const;
107 ErrorOr<String> to_uppercase(Optional<StringView> const& locale = {}) const;
108 ErrorOr<String> to_titlecase(Optional<StringView> const& locale = {}) const;
109 ErrorOr<String> to_casefold() const;
110
111 // Compare this String against another string with caseless matching. Using this method requires linking LibUnicode into your application.
112 [[nodiscard]] bool equals_ignoring_case(String const&) const;
113
114 [[nodiscard]] bool starts_with(u32 code_point) const;
115 [[nodiscard]] bool starts_with_bytes(StringView) const;
116
117 [[nodiscard]] bool ends_with(u32 code_point) const;
118 [[nodiscard]] bool ends_with_bytes(StringView) const;
119
120 // Creates a substring with a deep copy of the specified data window.
121 ErrorOr<String> substring_from_byte_offset(size_t start, size_t byte_count) const;
122 ErrorOr<String> substring_from_byte_offset(size_t start) const;
123
124 // Creates a substring that strongly references the origin superstring instead of making a deep copy of the data.
125 ErrorOr<String> substring_from_byte_offset_with_shared_superstring(size_t start, size_t byte_count) const;
126 ErrorOr<String> substring_from_byte_offset_with_shared_superstring(size_t start) const;
127
128 // Returns an iterable view over the Unicode code points.
129 [[nodiscard]] Utf8View code_points() const;
130
131 // Returns the underlying UTF-8 encoded bytes.
132 // NOTE: There is no guarantee about null-termination.
133 [[nodiscard]] ReadonlyBytes bytes() const;
134
135 // Returns true if the String is zero-length.
136 [[nodiscard]] bool is_empty() const;
137
138 // Returns a StringView covering the full length of the string. Note that iterating this will go byte-at-a-time, not code-point-at-a-time.
139 [[nodiscard]] StringView bytes_as_string_view() const;
140
141 ErrorOr<String> replace(StringView needle, StringView replacement, ReplaceMode replace_mode) const;
142 ErrorOr<String> reverse() const;
143
144 ErrorOr<String> trim(Utf8View const& code_points_to_trim, TrimMode mode = TrimMode::Both) const;
145 ErrorOr<String> trim(StringView code_points_to_trim, TrimMode mode = TrimMode::Both) const;
146
147 ErrorOr<Vector<String>> split_limit(u32 separator, size_t limit, SplitBehavior = SplitBehavior::Nothing) const;
148 ErrorOr<Vector<String>> split(u32 separator, SplitBehavior = SplitBehavior::Nothing) const;
149
150 Optional<size_t> find_byte_offset(u32 code_point, size_t from_byte_offset = 0) const;
151 Optional<size_t> find_byte_offset(StringView substring, size_t from_byte_offset = 0) const;
152
153 [[nodiscard]] bool operator==(String const&) const;
154 [[nodiscard]] bool operator!=(String const& other) const { return !(*this == other); }
155
156 [[nodiscard]] bool operator==(FlyString const&) const;
157 [[nodiscard]] bool operator!=(FlyString const& other) const { return !(*this == other); }
158
159 [[nodiscard]] bool operator==(StringView) const;
160 [[nodiscard]] bool operator!=(StringView other) const { return !(*this == other); }
161
162 [[nodiscard]] bool operator==(char const* cstring) const;
163 [[nodiscard]] bool operator!=(char const* cstring) const { return !(*this == cstring); }
164
165 // NOTE: UTF-8 is defined in a way that lexicographic ordering of code points is equivalent to lexicographic ordering of bytes.
166 [[nodiscard]] int operator<=>(String const& other) const { return this->bytes_as_string_view().compare(other.bytes_as_string_view()); }
167
168 template<typename... Ts>
169 [[nodiscard]] ALWAYS_INLINE constexpr bool is_one_of(Ts&&... strings) const
170 {
171 return (this->operator==(forward<Ts>(strings)) || ...);
172 }
173
174 [[nodiscard]] bool contains(StringView, CaseSensitivity = CaseSensitivity::CaseSensitive) const;
175 [[nodiscard]] bool contains(u32, CaseSensitivity = CaseSensitivity::CaseSensitive) const;
176
177 [[nodiscard]] u32 hash() const;
178
179 template<Arithmetic T>
180 static ErrorOr<String> number(T value)
181 {
182 return formatted("{}", value);
183 }
184
185 template<Arithmetic T>
186 Optional<T> to_number(TrimWhitespace trim_whitespace = TrimWhitespace::Yes) const
187 {
188 if constexpr (IsSigned<T>)
189 return StringUtils::convert_to_int<T>(bytes_as_string_view(), trim_whitespace);
190 else
191 return StringUtils::convert_to_uint<T>(bytes_as_string_view(), trim_whitespace);
192 }
193
194 static ErrorOr<String> vformatted(StringView fmtstr, TypeErasedFormatParams&);
195
196 template<typename... Parameters>
197 static ErrorOr<String> formatted(CheckedFormatString<Parameters...>&& fmtstr, Parameters const&... parameters)
198 {
199 VariadicFormatParams<AllowDebugOnlyFormatters::No, Parameters...> variadic_format_parameters { parameters... };
200 return vformatted(fmtstr.view(), variadic_format_parameters);
201 }
202
203 template<class SeparatorType, class CollectionType>
204 static ErrorOr<String> join(SeparatorType const& separator, CollectionType const& collection, StringView fmtstr = "{}"sv)
205 {
206 StringBuilder builder;
207 TRY(builder.try_join(separator, collection, fmtstr));
208 return builder.to_string();
209 }
210
211 // NOTE: This is primarily interesting to unit tests.
212 [[nodiscard]] bool is_short_string() const;
213
214 [[nodiscard]] static String fly_string_data_to_string(Badge<FlyString>, uintptr_t const&);
215 [[nodiscard]] static StringView fly_string_data_to_string_view(Badge<FlyString>, uintptr_t const&);
216 [[nodiscard]] static u32 fly_string_data_to_hash(Badge<FlyString>, uintptr_t const&);
217 [[nodiscard]] uintptr_t to_fly_string_data(Badge<FlyString>) const;
218
219 static void ref_fly_string_data(Badge<FlyString>, uintptr_t);
220 static void unref_fly_string_data(Badge<FlyString>, uintptr_t);
221 void did_create_fly_string(Badge<FlyString>) const;
222
223 // FIXME: Remove these once all code has been ported to String
224 [[nodiscard]] DeprecatedString to_deprecated_string() const;
225 static ErrorOr<String> from_deprecated_string(DeprecatedString const&);
226
227private:
228 // NOTE: If the least significant bit of the pointer is set, this is a short string.
229 static constexpr uintptr_t SHORT_STRING_FLAG = 1;
230
231 static constexpr bool has_short_string_bit(uintptr_t data)
232 {
233 return (data & SHORT_STRING_FLAG) != 0;
234 }
235
236 struct ShortString {
237 ReadonlyBytes bytes() const;
238 size_t byte_count() const;
239
240 // NOTE: This is the byte count shifted left 1 step and or'ed with a 1 (the SHORT_STRING_FLAG)
241 u8 byte_count_and_short_string_flag { 0 };
242 u8 storage[MAX_SHORT_STRING_BYTE_COUNT] = { 0 };
243 };
244
245 explicit String(NonnullRefPtr<Detail::StringData const>);
246
247 explicit constexpr String(ShortString short_string)
248 : m_short_string(short_string)
249 {
250 }
251
252 void destroy_string();
253
254 union {
255 ShortString m_short_string;
256 Detail::StringData const* m_data { nullptr };
257 };
258};
259
260template<>
261struct Traits<String> : public GenericTraits<String> {
262 static unsigned hash(String const&);
263};
264
265template<>
266struct Formatter<String> : Formatter<StringView> {
267 ErrorOr<void> format(FormatBuilder&, String const&);
268};
269
270}
271
272[[nodiscard]] ALWAYS_INLINE AK::ErrorOr<AK::String> operator""_string(char const* cstring, size_t length)
273{
274 return AK::String::from_utf8(AK::StringView(cstring, length));
275}
276
277[[nodiscard]] ALWAYS_INLINE AK_SHORT_STRING_CONSTEVAL AK::String operator""_short_string(char const* cstring, size_t length)
278{
279 return AK::String::from_utf8_short_string(AK::StringView(cstring, length));
280}