123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304 |
- /*
- * Copyright (c) 2018-2020, Andreas Kling <awesomekling@gmail.com>
- * Copyright (c) 2020, Fei Wu <f.eiwu@yahoo.com>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- #include <AK/Memory.h>
- #include <AK/Optional.h>
- #include <AK/String.h>
- #include <AK/StringUtils.h>
- #include <AK/StringView.h>
- #include <AK/Vector.h>
- namespace AK {
- namespace StringUtils {
- bool matches(const StringView& str, const StringView& mask, CaseSensitivity case_sensitivity, Vector<MaskSpan>* match_spans)
- {
- auto record_span = [&match_spans](size_t start, size_t length) {
- if (match_spans)
- match_spans->append({ start, length });
- };
- if (str.is_null() || mask.is_null())
- return str.is_null() && mask.is_null();
- if (mask == "*") {
- record_span(0, str.length());
- return true;
- }
- if (case_sensitivity == CaseSensitivity::CaseInsensitive) {
- const String str_lower = String(str).to_lowercase();
- const String mask_lower = String(mask).to_lowercase();
- return matches(str_lower, mask_lower, CaseSensitivity::CaseSensitive, match_spans);
- }
- const char* string_ptr = str.characters_without_null_termination();
- const char* string_start = str.characters_without_null_termination();
- const char* string_end = string_ptr + str.length();
- const char* mask_ptr = mask.characters_without_null_termination();
- const char* mask_end = mask_ptr + mask.length();
- auto matches_one = [](char ch, char p) {
- if (p == '?')
- return true;
- return p == ch && ch != 0;
- };
- while (string_ptr < string_end && mask_ptr < mask_end) {
- auto string_start_ptr = string_ptr;
- switch (*mask_ptr) {
- case '*':
- if (mask_ptr[1] == 0) {
- record_span(string_ptr - string_start, string_end - string_ptr);
- return true;
- }
- while (string_ptr < string_end && !matches(string_ptr, mask_ptr + 1))
- ++string_ptr;
- record_span(string_start_ptr - string_start, string_ptr - string_start_ptr);
- --string_ptr;
- break;
- case '?':
- record_span(string_ptr - string_start, 1);
- break;
- default:
- if (!matches_one(*string_ptr, *mask_ptr))
- return false;
- break;
- }
- ++string_ptr;
- ++mask_ptr;
- }
- return string_ptr == string_end && mask_ptr == mask_end;
- }
- Optional<int> convert_to_int(const StringView& str)
- {
- if (str.is_empty())
- return {};
- bool negative = false;
- size_t i = 0;
- const auto characters = str.characters_without_null_termination();
- if (characters[0] == '-' || characters[0] == '+') {
- if (str.length() == 1)
- return {};
- i++;
- negative = (characters[0] == '-');
- }
- int value = 0;
- for (; i < str.length(); i++) {
- if (characters[i] < '0' || characters[i] > '9')
- return {};
- value = value * 10;
- value += characters[i] - '0';
- }
- return negative ? -value : value;
- }
- Optional<unsigned> convert_to_uint(const StringView& str)
- {
- if (str.is_empty())
- return {};
- unsigned value = 0;
- const auto characters = str.characters_without_null_termination();
- for (size_t i = 0; i < str.length(); i++) {
- if (characters[i] < '0' || characters[i] > '9')
- return {};
- value = value * 10;
- value += characters[i] - '0';
- }
- return value;
- }
- Optional<unsigned> convert_to_uint_from_hex(const StringView& str)
- {
- if (str.is_empty())
- return {};
- unsigned value = 0;
- const auto count = str.length();
- for (size_t i = 0; i < count; i++) {
- char digit = str[i];
- u8 digit_val;
- if (digit >= '0' && digit <= '9') {
- digit_val = digit - '0';
- } else if (digit >= 'a' && digit <= 'f') {
- digit_val = 10 + (digit - 'a');
- } else if (digit >= 'A' && digit <= 'F') {
- digit_val = 10 + (digit - 'A');
- } else {
- return {};
- }
- value = (value << 4) + digit_val;
- }
- return value;
- }
- static inline char to_lowercase(char c)
- {
- if (c >= 'A' && c <= 'Z')
- return c | 0x20;
- return c;
- }
- bool equals_ignoring_case(const StringView& a, const StringView& b)
- {
- if (a.impl() && a.impl() == b.impl())
- return true;
- if (a.length() != b.length())
- return false;
- for (size_t i = 0; i < a.length(); ++i) {
- if (to_lowercase(a.characters_without_null_termination()[i]) != to_lowercase(b.characters_without_null_termination()[i]))
- return false;
- }
- return true;
- }
- bool ends_with(const StringView& str, const StringView& end, CaseSensitivity case_sensitivity)
- {
- if (end.is_empty())
- return true;
- if (str.is_empty())
- return false;
- if (end.length() > str.length())
- return false;
- if (case_sensitivity == CaseSensitivity::CaseSensitive)
- return !memcmp(str.characters_without_null_termination() + (str.length() - end.length()), end.characters_without_null_termination(), end.length());
- auto str_chars = str.characters_without_null_termination();
- auto end_chars = end.characters_without_null_termination();
- size_t si = str.length() - end.length();
- for (size_t ei = 0; ei < end.length(); ++si, ++ei) {
- if (to_lowercase(str_chars[si]) != to_lowercase(end_chars[ei]))
- return false;
- }
- return true;
- }
- bool starts_with(const StringView& str, const StringView& start, CaseSensitivity case_sensitivity)
- {
- if (start.is_empty())
- return true;
- if (str.is_empty())
- return false;
- if (start.length() > str.length())
- return false;
- if (str.characters_without_null_termination() == start.characters_without_null_termination())
- return true;
- if (case_sensitivity == CaseSensitivity::CaseSensitive)
- return !memcmp(str.characters_without_null_termination(), start.characters_without_null_termination(), start.length());
- auto str_chars = str.characters_without_null_termination();
- auto start_chars = start.characters_without_null_termination();
- size_t si = 0;
- for (size_t starti = 0; starti < start.length(); ++si, ++starti) {
- if (to_lowercase(str_chars[si]) != to_lowercase(start_chars[starti]))
- return false;
- }
- return true;
- }
- bool contains(const StringView& str, const StringView& needle, CaseSensitivity case_sensitivity)
- {
- if (str.is_null() || needle.is_null() || str.is_empty() || needle.length() > str.length())
- return false;
- if (needle.is_empty())
- return true;
- auto str_chars = str.characters_without_null_termination();
- auto needle_chars = needle.characters_without_null_termination();
- if (case_sensitivity == CaseSensitivity::CaseSensitive)
- return memmem(str_chars, str.length(), needle_chars, needle.length()) != nullptr;
- auto needle_first = to_lowercase(needle_chars[0]);
- for (size_t si = 0; si < str.length(); si++) {
- if (to_lowercase(str_chars[si]) != needle_first)
- continue;
- for (size_t ni = 0; si + ni < str.length(); ni++) {
- if (to_lowercase(str_chars[si + ni]) != to_lowercase(needle_chars[ni])) {
- si += ni;
- break;
- }
- if (ni + 1 == needle.length())
- return true;
- }
- }
- return false;
- }
- StringView trim_whitespace(const StringView& str, TrimMode mode)
- {
- auto is_whitespace_character = [](char ch) -> bool {
- return ch == '\t'
- || ch == '\n'
- || ch == '\v'
- || ch == '\f'
- || ch == '\r'
- || ch == ' ';
- };
- size_t substring_start = 0;
- size_t substring_length = str.length();
- if (mode == TrimMode::Left || mode == TrimMode::Both) {
- for (size_t i = 0; i < str.length(); ++i) {
- if (substring_length == 0)
- return "";
- if (!is_whitespace_character(str[i]))
- break;
- ++substring_start;
- --substring_length;
- }
- }
- if (mode == TrimMode::Right || mode == TrimMode::Both) {
- for (size_t i = str.length() - 1; i > 0; --i) {
- if (substring_length == 0)
- return "";
- if (!is_whitespace_character(str[i]))
- break;
- --substring_length;
- }
- }
- return str.substring_view(substring_start, substring_length);
- }
- }
- }
|