Serenity Operating System
at portability 312 lines 9.1 kB view raw
1/* 2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, this 9 * list of conditions and the following disclaimer. 10 * 11 * 2. Redistributions in binary form must reproduce the above copyright notice, 12 * this list of conditions and the following disclaimer in the documentation 13 * and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#pragma once 28 29#include <AK/Forward.h> 30#include <AK/RefPtr.h> 31#include <AK/StringImpl.h> 32#include <AK/StringView.h> 33#include <AK/Traits.h> 34 35namespace AK { 36 37// String is a convenience wrapper around StringImpl, suitable for passing 38// around as a value type. It's basically the same as passing around a 39// RefPtr<StringImpl>, with a bit of syntactic sugar. 40// 41// Note that StringImpl is an immutable object that cannot shrink or grow. 42// Its allocation size is snugly tailored to the specific string it contains. 43// Copying a String is very efficient, since the internal StringImpl is 44// retainable and so copying only requires modifying the ref count. 45// 46// There are three main ways to construct a new String: 47// 48// s = String("some literal"); 49// 50// s = String::format("%d little piggies", m_piggies); 51// 52// StringBuilder builder; 53// builder.append("abc"); 54// builder.append("123"); 55// s = builder.to_string(); 56 57class String { 58public: 59 ~String() {} 60 61 String() {} 62 63 String(const StringView& view) 64 { 65 if (view.m_impl) 66 m_impl = *view.m_impl; 67 else 68 m_impl = StringImpl::create(view.characters_without_null_termination(), view.length()); 69 } 70 71 String(const String& other) 72 : m_impl(const_cast<String&>(other).m_impl) 73 { 74 } 75 76 String(String&& other) 77 : m_impl(move(other.m_impl)) 78 { 79 } 80 81 String(const char* cstring, ShouldChomp shouldChomp = NoChomp) 82 : m_impl(StringImpl::create(cstring, shouldChomp)) 83 { 84 } 85 86 String(const char* cstring, size_t length, ShouldChomp shouldChomp = NoChomp) 87 : m_impl(StringImpl::create(cstring, length, shouldChomp)) 88 { 89 } 90 91 String(const StringImpl& impl) 92 : m_impl(const_cast<StringImpl&>(impl)) 93 { 94 } 95 96 String(const StringImpl* impl) 97 : m_impl(const_cast<StringImpl*>(impl)) 98 { 99 } 100 101 String(RefPtr<StringImpl>&& impl) 102 : m_impl(move(impl)) 103 { 104 } 105 106 String(NonnullRefPtr<StringImpl>&& impl) 107 : m_impl(move(impl)) 108 { 109 } 110 111 enum class CaseSensitivity { 112 CaseInsensitive, 113 CaseSensitive, 114 }; 115 116 static String repeated(char, size_t count); 117 bool matches(const StringView& pattern, CaseSensitivity = CaseSensitivity::CaseInsensitive) const; 118 119 // FIXME: These should be shared between String and StringView somehow! 120 int to_int(bool& ok) const; 121 unsigned to_uint(bool& ok) const; 122 123 String to_lowercase() const 124 { 125 if (!m_impl) 126 return String(); 127 return m_impl->to_lowercase(); 128 } 129 130 String to_uppercase() const 131 { 132 if (!m_impl) 133 return String(); 134 return m_impl->to_uppercase(); 135 } 136 137 bool equals_ignoring_case(const StringView&) const; 138 139 bool contains(const String&) const; 140 141 Vector<String> split_limit(char separator, size_t limit, bool keep_empty = false) const; 142 Vector<String> split(char separator, bool keep_empty = false) const; 143 String substring(size_t start, size_t length) const; 144 145 Vector<StringView> split_view(char separator, bool keep_empty = false) const; 146 StringView substring_view(size_t start, size_t length) const; 147 148 bool is_null() const { return !m_impl; } 149 bool is_empty() const { return length() == 0; } 150 size_t length() const { return m_impl ? m_impl->length() : 0; } 151 const char* characters() const { return m_impl ? m_impl->characters() : nullptr; } 152 char operator[](size_t i) const 153 { 154 ASSERT(m_impl); 155 return (*m_impl)[i]; 156 } 157 158 bool starts_with(const StringView&) const; 159 bool ends_with(const StringView&) const; 160 bool starts_with(char) const; 161 bool ends_with(char) const; 162 163 bool operator==(const String&) const; 164 bool operator!=(const String& other) const { return !(*this == other); } 165 166 bool operator==(const StringView&) const; 167 bool operator!=(const StringView& other) const { return !(*this == other); } 168 169 bool operator<(const String&) const; 170 bool operator<(const char*) const; 171 bool operator>=(const String& other) const { return !(*this < other); } 172 bool operator>=(const char* other) const { return !(*this < other); } 173 174 bool operator>(const String&) const; 175 bool operator>(const char*) const; 176 bool operator<=(const String& other) const { return !(*this > other); } 177 bool operator<=(const char* other) const { return !(*this > other); } 178 179 bool operator==(const char* cstring) const 180 { 181 if (is_null()) 182 return !cstring; 183 if (!cstring) 184 return false; 185 return !strcmp(characters(), cstring); 186 } 187 188 bool operator!=(const char* cstring) const 189 { 190 return !(*this == cstring); 191 } 192 193 String isolated_copy() const; 194 195 static String empty(); 196 197 StringImpl* impl() { return m_impl.ptr(); } 198 const StringImpl* impl() const { return m_impl.ptr(); } 199 200 String& operator=(String&& other) 201 { 202 if (this != &other) 203 m_impl = move(other.m_impl); 204 return *this; 205 } 206 207 String& operator=(const String& other) 208 { 209 if (this != &other) 210 m_impl = const_cast<String&>(other).m_impl; 211 return *this; 212 } 213 214 u32 hash() const 215 { 216 if (!m_impl) 217 return 0; 218 return m_impl->hash(); 219 } 220 221 ByteBuffer to_byte_buffer() const; 222 223 template<typename BufferType> 224 static String copy(const BufferType& buffer, ShouldChomp should_chomp = NoChomp) 225 { 226 if (buffer.is_null()) 227 return {}; 228 if (buffer.is_empty()) 229 return empty(); 230 return String((const char*)buffer.data(), buffer.size(), should_chomp); 231 } 232 233 static String format(const char*, ...); 234 static String number(unsigned); 235 static String number(unsigned long); 236 static String number(unsigned long long); 237 static String number(int); 238 static String number(long); 239 static String number(long long); 240 241 StringView view() const 242 { 243 return { characters(), length() }; 244 } 245 246private: 247 bool match_helper(const StringView& mask) const; 248 RefPtr<StringImpl> m_impl; 249}; 250 251inline bool StringView::operator==(const String& string) const 252{ 253 if (string.is_null()) 254 return !m_characters; 255 if (!m_characters) 256 return false; 257 if (m_length != string.length()) 258 return false; 259 if (m_characters == string.characters()) 260 return true; 261 return !memcmp(m_characters, string.characters(), m_length); 262} 263 264template<> 265struct Traits<String> : public GenericTraits<String> { 266 static unsigned hash(const String& s) { return s.impl() ? s.impl()->hash() : 0; } 267}; 268 269struct CaseInsensitiveStringTraits : public AK::Traits<String> { 270 static unsigned hash(const String& s) { return s.impl() ? s.to_lowercase().impl()->hash() : 0; } 271 static bool equals(const String& a, const String& b) { return a.to_lowercase() == b.to_lowercase(); } 272}; 273 274inline bool operator<(const char* characters, const String& string) 275{ 276 if (!characters) 277 return !string.is_null(); 278 279 if (string.is_null()) 280 return false; 281 282 return strcmp(characters, string.characters()) < 0; 283} 284 285inline bool operator>=(const char* characters, const String& string) 286{ 287 return !(characters < string); 288} 289 290inline bool operator>(const char* characters, const String& string) 291{ 292 if (!characters) 293 return !string.is_null(); 294 295 if (string.is_null()) 296 return false; 297 298 return strcmp(characters, string.characters()) > 0; 299} 300 301inline bool operator<=(const char* characters, const String& string) 302{ 303 return !(characters > string); 304} 305 306String escape_html_entities(const StringView& html); 307 308} 309 310using AK::CaseInsensitiveStringTraits; 311using AK::String; 312using AK::escape_html_entities;