Serenity Operating System
at master 280 lines 11 kB view raw
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}