Serenity Operating System
at portability 423 lines 11 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#include <AK/StdLibExtras.h> 28#include <AK/String.h> 29#include <AK/StringBuilder.h> 30#include <AK/Vector.h> 31 32#ifndef KERNEL 33# include <inttypes.h> 34#endif 35 36#ifdef KERNEL 37extern "C" char* strstr(const char* haystack, const char* needle); 38#endif 39 40static inline char to_lowercase(char c) 41{ 42 if (c >= 'A' && c <= 'Z') 43 return c | 0x20; 44 return c; 45} 46 47namespace AK { 48 49bool String::operator==(const String& other) const 50{ 51 if (!m_impl) 52 return !other.m_impl; 53 54 if (!other.m_impl) 55 return false; 56 57 if (length() != other.length()) 58 return false; 59 60 return !memcmp(characters(), other.characters(), length()); 61} 62 63bool String::operator==(const StringView& other) const 64{ 65 if (!m_impl) 66 return !other.m_characters; 67 68 if (!other.m_characters) 69 return false; 70 71 if (length() != other.length()) 72 return false; 73 74 return !memcmp(characters(), other.characters_without_null_termination(), length()); 75} 76 77bool String::operator<(const String& other) const 78{ 79 if (!m_impl) 80 return other.m_impl; 81 82 if (!other.m_impl) 83 return false; 84 85 return strcmp(characters(), other.characters()) < 0; 86} 87 88bool String::operator>(const String& other) const 89{ 90 if (!m_impl) 91 return other.m_impl; 92 93 if (!other.m_impl) 94 return false; 95 96 return strcmp(characters(), other.characters()) > 0; 97} 98 99String String::empty() 100{ 101 return StringImpl::the_empty_stringimpl(); 102} 103 104String String::isolated_copy() const 105{ 106 if (!m_impl) 107 return {}; 108 if (!m_impl->length()) 109 return empty(); 110 char* buffer; 111 auto impl = StringImpl::create_uninitialized(length(), buffer); 112 memcpy(buffer, m_impl->characters(), m_impl->length()); 113 return String(move(*impl)); 114} 115 116String String::substring(size_t start, size_t length) const 117{ 118 if (!length) 119 return {}; 120 ASSERT(m_impl); 121 ASSERT(start + length <= m_impl->length()); 122 // FIXME: This needs some input bounds checking. 123 return { characters() + start, length }; 124} 125 126StringView String::substring_view(size_t start, size_t length) const 127{ 128 ASSERT(m_impl); 129 ASSERT(start + length <= m_impl->length()); 130 // FIXME: This needs some input bounds checking. 131 return { characters() + start, length }; 132} 133 134Vector<String> String::split(char separator, bool keep_empty) const 135{ 136 return split_limit(separator, 0, keep_empty); 137} 138 139Vector<String> String::split_limit(char separator, size_t limit, bool keep_empty) const 140{ 141 if (is_empty()) 142 return {}; 143 144 Vector<String> v; 145 size_t substart = 0; 146 for (size_t i = 0; i < length() && ((size_t)v.size() + 1) != limit; ++i) { 147 char ch = characters()[i]; 148 if (ch == separator) { 149 size_t sublen = i - substart; 150 if (sublen != 0 || keep_empty) 151 v.append(substring(substart, sublen)); 152 substart = i + 1; 153 } 154 } 155 size_t taillen = length() - substart; 156 if (taillen != 0 || keep_empty) 157 v.append(substring(substart, taillen)); 158 return v; 159} 160 161Vector<StringView> String::split_view(const char separator, bool keep_empty) const 162{ 163 if (is_empty()) 164 return {}; 165 166 Vector<StringView> v; 167 size_t substart = 0; 168 for (size_t i = 0; i < length(); ++i) { 169 char ch = characters()[i]; 170 if (ch == separator) { 171 size_t sublen = i - substart; 172 if (sublen != 0 || keep_empty) 173 v.append(substring_view(substart, sublen)); 174 substart = i + 1; 175 } 176 } 177 size_t taillen = length() - substart; 178 if (taillen != 0 || keep_empty) 179 v.append(substring_view(substart, taillen)); 180 return v; 181} 182 183ByteBuffer String::to_byte_buffer() const 184{ 185 if (!m_impl) 186 return nullptr; 187 return ByteBuffer::copy(reinterpret_cast<const u8*>(characters()), length()); 188} 189 190int String::to_int(bool& ok) const 191{ 192 bool negative = false; 193 int value = 0; 194 size_t i = 0; 195 196 if (is_empty()) { 197 ok = false; 198 return 0; 199 } 200 201 if (characters()[0] == '-') { 202 i++; 203 negative = true; 204 } 205 for (; i < length(); i++) { 206 if (characters()[i] < '0' || characters()[i] > '9') { 207 ok = false; 208 return 0; 209 } 210 value = value * 10; 211 value += characters()[i] - '0'; 212 } 213 ok = true; 214 215 return negative ? -value : value; 216} 217 218unsigned String::to_uint(bool& ok) const 219{ 220 unsigned value = 0; 221 for (size_t i = 0; i < length(); ++i) { 222 if (characters()[i] < '0' || characters()[i] > '9') { 223 ok = false; 224 return 0; 225 } 226 value = value * 10; 227 value += characters()[i] - '0'; 228 } 229 ok = true; 230 return value; 231} 232 233String String::number(unsigned long long value) 234{ 235 int size; 236 char buffer[32]; 237 size = sprintf(buffer, "%llu", value); 238 return String(buffer, size); 239} 240 241String String::number(unsigned long value) 242{ 243 int size; 244 char buffer[32]; 245 size = sprintf(buffer, "%lu", value); 246 return String(buffer, size); 247} 248 249String String::number(unsigned value) 250{ 251 char buffer[32]; 252 int size = sprintf(buffer, "%u", value); 253 return String(buffer, size); 254} 255 256String String::number(long long value) 257{ 258 char buffer[32]; 259 int size = sprintf(buffer, "%lld", value); 260 return String(buffer, size); 261} 262 263String String::number(long value) 264{ 265 char buffer[32]; 266 int size = sprintf(buffer, "%ld", value); 267 return String(buffer, size); 268} 269 270String String::number(int value) 271{ 272 char buffer[32]; 273 int size = sprintf(buffer, "%d", value); 274 return String(buffer, size); 275} 276 277String String::format(const char* fmt, ...) 278{ 279 StringBuilder builder; 280 va_list ap; 281 va_start(ap, fmt); 282 builder.appendvf(fmt, ap); 283 va_end(ap); 284 return builder.to_string(); 285} 286 287bool String::starts_with(const StringView& str) const 288{ 289 if (str.is_empty()) 290 return true; 291 if (is_empty()) 292 return false; 293 if (str.length() > length()) 294 return false; 295 return !memcmp(characters(), str.characters_without_null_termination(), str.length()); 296} 297 298bool String::starts_with(char ch) const 299{ 300 if (is_empty()) 301 return false; 302 return characters()[0] == ch; 303} 304 305bool String::ends_with(const StringView& str) const 306{ 307 if (str.is_empty()) 308 return true; 309 if (is_empty()) 310 return false; 311 if (str.length() > length()) 312 return false; 313 return !memcmp(characters() + (length() - str.length()), str.characters_without_null_termination(), str.length()); 314} 315 316bool String::ends_with(char ch) const 317{ 318 if (is_empty()) 319 return false; 320 return characters()[length() - 1] == ch; 321} 322String String::repeated(char ch, size_t count) 323{ 324 if (!count) 325 return empty(); 326 char* buffer; 327 auto impl = StringImpl::create_uninitialized(count, buffer); 328 memset(buffer, ch, count); 329 return *impl; 330} 331 332bool String::matches(const StringView& mask, CaseSensitivity case_sensitivity) const 333{ 334 if (case_sensitivity == CaseSensitivity::CaseInsensitive) { 335 String this_lower = this->to_lowercase(); 336 String mask_lower = String(mask).to_lowercase(); 337 return this_lower.match_helper(mask_lower); 338 } 339 340 return match_helper(mask); 341} 342 343bool String::match_helper(const StringView& mask) const 344{ 345 if (is_null()) 346 return false; 347 348 const char* string_ptr = characters(); 349 const char* mask_ptr = mask.characters_without_null_termination(); 350 const char* mask_end = mask_ptr + mask.length(); 351 352 // Match string against mask directly unless we hit a * 353 while ((*string_ptr) && (mask_ptr < mask_end) && (*mask_ptr != '*')) { 354 if ((*mask_ptr != *string_ptr) && (*mask_ptr != '?')) 355 return false; 356 mask_ptr++; 357 string_ptr++; 358 } 359 360 const char* cp = nullptr; 361 const char* mp = nullptr; 362 363 while (*string_ptr) { 364 if ((mask_ptr < mask_end) && (*mask_ptr == '*')) { 365 // If we have only a * left, there is no way to not match. 366 if (++mask_ptr == mask_end) 367 return true; 368 mp = mask_ptr; 369 cp = string_ptr + 1; 370 } else if ((mask_ptr < mask_end) && ((*mask_ptr == *string_ptr) || (*mask_ptr == '?'))) { 371 mask_ptr++; 372 string_ptr++; 373 } else if ((cp != nullptr) && (mp != nullptr)) { 374 mask_ptr = mp; 375 string_ptr = cp++; 376 } else { 377 break; 378 } 379 } 380 381 // Handle any trailing mask 382 while ((mask_ptr < mask_end) && (*mask_ptr == '*')) 383 mask_ptr++; 384 385 // If we 'ate' all of the mask and the string then we match. 386 return (mask_ptr == mask_end) && !*string_ptr; 387} 388 389bool String::contains(const String& needle) const 390{ 391 return strstr(characters(), needle.characters()); 392} 393 394bool String::equals_ignoring_case(const StringView& other) const 395{ 396 if (other.m_impl == impl()) 397 return true; 398 if (length() != other.length()) 399 return false; 400 for (size_t i = 0; i < length(); ++i) { 401 if (::to_lowercase(characters()[i]) != ::to_lowercase(other.characters_without_null_termination()[i])) 402 return false; 403 } 404 return true; 405} 406 407String escape_html_entities(const StringView& html) 408{ 409 StringBuilder builder; 410 for (size_t i = 0; i < html.length(); ++i) { 411 if (html[i] == '<') 412 builder.append("&lt;"); 413 else if (html[i] == '>') 414 builder.append("&gt;"); 415 else if (html[i] == '&') 416 builder.append("&amp;"); 417 else 418 builder.append(html[i]); 419 } 420 return builder.to_string(); 421} 422 423}