/* * Copyright (c) 2018-2021, Andreas Kling * * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include #include #include #include #include namespace AK { StringView::StringView(const String& string) : m_characters(string.characters()) , m_length(string.length()) { } StringView::StringView(const FlyString& string) : m_characters(string.characters()) , m_length(string.length()) { } StringView::StringView(const ByteBuffer& buffer) : m_characters((const char*)buffer.data()) , m_length(buffer.size()) { } Vector StringView::split_view(const char separator, bool keep_empty) const { if (is_empty()) return {}; Vector v; size_t substart = 0; for (size_t i = 0; i < length(); ++i) { char ch = characters_without_null_termination()[i]; if (ch == separator) { size_t sublen = i - substart; if (sublen != 0 || keep_empty) v.append(substring_view(substart, sublen)); substart = i + 1; } } size_t taillen = length() - substart; if (taillen != 0 || keep_empty) v.append(substring_view(substart, taillen)); return v; } Vector StringView::split_view(const StringView& separator, bool keep_empty) const { VERIFY(!separator.is_empty()); if (is_empty()) return {}; StringView view { *this }; Vector parts; auto maybe_separator_index = find(separator); while (maybe_separator_index.has_value()) { auto separator_index = maybe_separator_index.value(); auto part_with_separator = view.substring_view(0, separator_index + separator.length()); if (keep_empty || separator_index > 0) parts.append(part_with_separator.substring_view(0, separator_index)); view = view.substring_view_starting_after_substring(part_with_separator); maybe_separator_index = view.find(separator); } if (keep_empty || !view.is_empty()) parts.append(view); return parts; } Vector StringView::lines(bool consider_cr) const { if (is_empty()) return {}; if (!consider_cr) return split_view('\n', true); Vector v; size_t substart = 0; bool last_ch_was_cr = false; bool split_view = false; for (size_t i = 0; i < length(); ++i) { char ch = characters_without_null_termination()[i]; if (ch == '\n') { split_view = true; if (last_ch_was_cr) { substart = i + 1; split_view = false; last_ch_was_cr = false; } } if (ch == '\r') { split_view = true; last_ch_was_cr = true; } if (split_view) { size_t sublen = i - substart; v.append(substring_view(substart, sublen)); substart = i + 1; } split_view = false; } size_t taillen = length() - substart; if (taillen != 0) v.append(substring_view(substart, taillen)); return v; } bool StringView::starts_with(char ch) const { if (is_empty()) return false; return ch == characters_without_null_termination()[0]; } bool StringView::starts_with(const StringView& str, CaseSensitivity case_sensitivity) const { return StringUtils::starts_with(*this, str, case_sensitivity); } bool StringView::ends_with(char ch) const { if (is_empty()) return false; return ch == characters_without_null_termination()[length() - 1]; } bool StringView::ends_with(const StringView& str, CaseSensitivity case_sensitivity) const { return StringUtils::ends_with(*this, str, case_sensitivity); } bool StringView::matches(const StringView& mask, Vector& mask_spans, CaseSensitivity case_sensitivity) const { return StringUtils::matches(*this, mask, case_sensitivity, &mask_spans); } bool StringView::matches(const StringView& mask, CaseSensitivity case_sensitivity) const { return StringUtils::matches(*this, mask, case_sensitivity); } bool StringView::contains(char needle) const { for (char current : *this) { if (current == needle) return true; } return false; } bool StringView::contains(const StringView& needle, CaseSensitivity case_sensitivity) const { return StringUtils::contains(*this, needle, case_sensitivity); } bool StringView::equals_ignoring_case(const StringView& other) const { return StringUtils::equals_ignoring_case(*this, other); } StringView StringView::substring_view_starting_from_substring(const StringView& substring) const { const char* remaining_characters = substring.characters_without_null_termination(); VERIFY(remaining_characters >= m_characters); VERIFY(remaining_characters <= m_characters + m_length); size_t remaining_length = m_length - (remaining_characters - m_characters); return { remaining_characters, remaining_length }; } StringView StringView::substring_view_starting_after_substring(const StringView& substring) const { const char* remaining_characters = substring.characters_without_null_termination() + substring.length(); VERIFY(remaining_characters >= m_characters); VERIFY(remaining_characters <= m_characters + m_length); size_t remaining_length = m_length - (remaining_characters - m_characters); return { remaining_characters, remaining_length }; } template Optional StringView::to_int() const { return StringUtils::convert_to_int(*this); } template Optional StringView::to_int() const; template Optional StringView::to_int() const; template Optional StringView::to_int() const; template Optional StringView::to_int() const; template Optional StringView::to_uint() const { return StringUtils::convert_to_uint(*this); } template Optional StringView::to_uint() const; template Optional StringView::to_uint() const; template Optional StringView::to_uint() const; template Optional StringView::to_uint() const; template Optional StringView::to_uint() const; template Optional StringView::to_uint() const; unsigned StringView::hash() const { if (is_empty()) return 0; return string_hash(characters_without_null_termination(), length()); } bool StringView::operator==(const String& string) const { if (string.is_null()) return !m_characters; if (!m_characters) return false; if (m_length != string.length()) return false; if (m_characters == string.characters()) return true; return !__builtin_memcmp(m_characters, string.characters(), m_length); } Optional StringView::find_first_of(char c) const { if (const auto location = AK::find(begin(), end(), c); location != end()) { return location.index(); } return {}; } Optional StringView::find_first_of(const StringView& view) const { if (const auto location = AK::find_if(begin(), end(), [&](const auto c) { return any_of(view.begin(), view.end(), [&](const auto view_char) { return c == view_char; }); }); location != end()) { return location.index(); } return {}; } Optional StringView::find_last_of(char c) const { for (size_t pos = m_length; pos != 0; --pos) { if (m_characters[pos - 1] == c) return pos - 1; } return {}; } Optional StringView::find_last_of(const StringView& view) const { for (size_t pos = m_length; pos != 0; --pos) { char c = m_characters[pos - 1]; for (char view_char : view) { if (c == view_char) return pos - 1; } } return {}; } Optional StringView::find(char c) const { return find(StringView { &c, 1 }); } Optional StringView::find(const StringView& view) const { return StringUtils::find(*this, view); } String StringView::to_string() const { return String { *this }; } }