Serenity Operating System
at master 346 lines 13 kB view raw
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