123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144 |
- /*
- * Copyright (c) 2023-2024, Tim Flynn <trflynn89@serenityos.org>
- *
- * SPDX-License-Identifier: BSD-2-Clause
- */
- #define AK_DONT_REPLACE_STD
- #include <AK/String.h>
- #include <AK/StringBuilder.h>
- #include <LibLocale/ICU.h>
- #include <unicode/bytestream.h>
- #include <unicode/casemap.h>
- #include <unicode/stringoptions.h>
- // This file contains definitions of AK::String methods which require UCD data.
- namespace AK {
- struct ResolvedLocale {
- ByteString buffer;
- char const* locale { nullptr };
- };
- static ResolvedLocale resolve_locale(Optional<StringView> const& locale)
- {
- if (!locale.has_value())
- return {};
- ResolvedLocale resolved_locale;
- resolved_locale.buffer = *locale;
- resolved_locale.locale = resolved_locale.buffer.characters();
- return resolved_locale;
- }
- ErrorOr<String> String::to_lowercase(Optional<StringView> const& locale) const
- {
- UErrorCode status = U_ZERO_ERROR;
- StringBuilder builder { bytes_as_string_view().length() };
- icu::StringByteSink sink { &builder };
- auto resolved_locale = resolve_locale(locale);
- icu::CaseMap::utf8ToLower(resolved_locale.locale, 0, Locale::icu_string_piece(*this), sink, nullptr, status);
- if (Locale::icu_failure(status))
- return Error::from_string_literal("Unable to convert string to lowercase");
- return builder.to_string_without_validation();
- }
- ErrorOr<String> String::to_uppercase(Optional<StringView> const& locale) const
- {
- UErrorCode status = U_ZERO_ERROR;
- StringBuilder builder { bytes_as_string_view().length() };
- icu::StringByteSink sink { &builder };
- auto resolved_locale = resolve_locale(locale);
- icu::CaseMap::utf8ToUpper(resolved_locale.locale, 0, Locale::icu_string_piece(*this), sink, nullptr, status);
- if (Locale::icu_failure(status))
- return Error::from_string_literal("Unable to convert string to uppercase");
- return builder.to_string_without_validation();
- }
- ErrorOr<String> String::to_titlecase(Optional<StringView> const& locale, TrailingCodePointTransformation trailing_code_point_transformation) const
- {
- UErrorCode status = U_ZERO_ERROR;
- StringBuilder builder { bytes_as_string_view().length() };
- icu::StringByteSink sink { &builder };
- auto resolved_locale = resolve_locale(locale);
- u32 options = 0;
- if (trailing_code_point_transformation == TrailingCodePointTransformation::PreserveExisting)
- options |= U_TITLECASE_NO_LOWERCASE;
- icu::CaseMap::utf8ToTitle(resolved_locale.locale, options, nullptr, Locale::icu_string_piece(*this), sink, nullptr, status);
- if (Locale::icu_failure(status))
- return Error::from_string_literal("Unable to convert string to titlecase");
- return builder.to_string_without_validation();
- }
- static ErrorOr<void> build_casefold_string(StringView string, StringBuilder& builder)
- {
- UErrorCode status = U_ZERO_ERROR;
- icu::StringByteSink sink { &builder };
- icu::CaseMap::utf8Fold(0, Locale::icu_string_piece(string), sink, nullptr, status);
- if (Locale::icu_failure(status))
- return Error::from_string_literal("Unable to casefold string");
- return {};
- }
- ErrorOr<String> String::to_casefold() const
- {
- StringBuilder builder { bytes_as_string_view().length() };
- TRY(build_casefold_string(*this, builder));
- return builder.to_string_without_validation();
- }
- bool String::equals_ignoring_case(String const& other) const
- {
- StringBuilder lhs_builder { bytes_as_string_view().length() };
- if (build_casefold_string(*this, lhs_builder).is_error())
- return false;
- StringBuilder rhs_builder { other.bytes_as_string_view().length() };
- if (build_casefold_string(other, rhs_builder).is_error())
- return false;
- return lhs_builder.string_view() == rhs_builder.string_view();
- }
- Optional<size_t> String::find_byte_offset_ignoring_case(StringView needle, size_t from_byte_offset) const
- {
- auto haystack = bytes_as_string_view().substring_view(from_byte_offset);
- if (haystack.is_empty())
- return {};
- StringBuilder lhs_builder { haystack.length() };
- if (build_casefold_string(haystack, lhs_builder).is_error())
- return {};
- StringBuilder rhs_builder { needle.length() };
- if (build_casefold_string(needle, rhs_builder).is_error())
- return false;
- if (auto index = lhs_builder.string_view().find(rhs_builder.string_view()); index.has_value())
- return *index + from_byte_offset;
- return {};
- }
- }
|