diff --git a/AK/StringBase.h b/AK/StringBase.h index 493a9a98612..df468afd4c5 100644 --- a/AK/StringBase.h +++ b/AK/StringBase.h @@ -73,6 +73,12 @@ public: [[nodiscard]] ALWAYS_INLINE FlatPtr raw(Badge) const { return bit_cast(m_impl); } [[nodiscard]] ALWAYS_INLINE FlatPtr raw(Badge) const { return bit_cast(m_impl); } + template + ALWAYS_INLINE ErrorOr replace_with_new_string(Badge, size_t byte_count, Func&& callback) + { + return replace_with_new_string(byte_count, forward(callback)); + } + protected: template ErrorOr replace_with_new_string(size_t byte_count, Func&& callback) diff --git a/AK/StringImpl.cpp b/AK/StringImpl.cpp index 146786e970b..d7af5a11757 100644 --- a/AK/StringImpl.cpp +++ b/AK/StringImpl.cpp @@ -75,28 +75,6 @@ NonnullRefPtr StringImpl::create(ReadonlyBytes bytes, ShouldCh return StringImpl::create(reinterpret_cast(bytes.data()), bytes.size(), shouldChomp); } -NonnullRefPtr StringImpl::create_lowercased(char const* cstring, size_t length) -{ - if (!length) - return the_empty_stringimpl(); - char* buffer; - auto impl = create_uninitialized(length, buffer); - for (size_t i = 0; i < length; ++i) - buffer[i] = (char)to_ascii_lowercase(cstring[i]); - return impl; -} - -NonnullRefPtr StringImpl::create_uppercased(char const* cstring, size_t length) -{ - if (!length) - return the_empty_stringimpl(); - char* buffer; - auto impl = create_uninitialized(length, buffer); - for (size_t i = 0; i < length; ++i) - buffer[i] = (char)to_ascii_uppercase(cstring[i]); - return impl; -} - unsigned StringImpl::case_insensitive_hash() const { return case_insensitive_string_hash(characters(), length()); diff --git a/AK/StringImpl.h b/AK/StringImpl.h index a7ea6be8192..95dcc5148cb 100644 --- a/AK/StringImpl.h +++ b/AK/StringImpl.h @@ -28,8 +28,6 @@ public: static NonnullRefPtr create(char const* cstring, ShouldChomp = NoChomp); static NonnullRefPtr create(char const* cstring, size_t length, ShouldChomp = NoChomp); static NonnullRefPtr create(ReadonlyBytes, ShouldChomp = NoChomp); - static NonnullRefPtr create_lowercased(char const* cstring, size_t length); - static NonnullRefPtr create_uppercased(char const* cstring, size_t length); void operator delete(void* ptr) { diff --git a/AK/StringUtils.cpp b/AK/StringUtils.cpp index c10f969d133..343b008b154 100644 --- a/AK/StringUtils.cpp +++ b/AK/StringUtils.cpp @@ -488,7 +488,7 @@ ByteString to_snakecase(StringView str) return builder.to_byte_string(); } -ByteString to_titlecase(StringView str) +String to_titlecase(StringView str) { StringBuilder builder; bool next_is_upper = true; @@ -501,7 +501,7 @@ ByteString to_titlecase(StringView str) next_is_upper = ch == ' '; } - return builder.to_byte_string(); + return MUST(builder.to_string()); } // Finishes the replacing algorithm once it is known that ita least one diff --git a/AK/StringUtils.h b/AK/StringUtils.h index 0e0a2ef54f5..31130df24da 100644 --- a/AK/StringUtils.h +++ b/AK/StringUtils.h @@ -105,7 +105,7 @@ enum class SearchDirection { Optional find_any_of(StringView haystack, StringView needles, SearchDirection); ByteString to_snakecase(StringView); -ByteString to_titlecase(StringView); +String to_titlecase(StringView); ByteString replace(StringView, StringView needle, StringView replacement, ReplaceMode); ErrorOr replace(String const&, StringView needle, StringView replacement, ReplaceMode); diff --git a/AK/StringView.cpp b/AK/StringView.cpp index 92b1cd30655..b5519a3fe1b 100644 --- a/AK/StringView.cpp +++ b/AK/StringView.cpp @@ -7,12 +7,14 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include #include @@ -204,17 +206,37 @@ bool StringView::is_ascii() const return simdutf::validate_ascii(characters_without_null_termination(), length()); } -ByteString StringView::to_lowercase_string() const +String StringView::to_ascii_lowercase_string() const { - return StringImpl::create_lowercased(characters_without_null_termination(), length()).release_nonnull(); + VERIFY(Utf8View { *this }.validate()); + + String result; + + MUST(result.replace_with_new_string({}, length(), [&](Bytes buffer) -> ErrorOr { + for (auto [i, character] : enumerate(bytes())) + buffer[i] = static_cast(AK::to_ascii_lowercase(character)); + return {}; + })); + + return result; } -ByteString StringView::to_uppercase_string() const +String StringView::to_ascii_uppercase_string() const { - return StringImpl::create_uppercased(characters_without_null_termination(), length()).release_nonnull(); + VERIFY(Utf8View { *this }.validate()); + + String result; + + MUST(result.replace_with_new_string({}, length(), [&](Bytes buffer) -> ErrorOr { + for (auto [i, character] : enumerate(bytes())) + buffer[i] = static_cast(AK::to_ascii_uppercase(character)); + return {}; + })); + + return result; } -ByteString StringView::to_titlecase_string() const +String StringView::to_ascii_titlecase_string() const { return StringUtils::to_titlecase(*this); } diff --git a/AK/StringView.h b/AK/StringView.h index 7338acdba65..81b1cf87f57 100644 --- a/AK/StringView.h +++ b/AK/StringView.h @@ -105,9 +105,9 @@ public: [[nodiscard]] StringView trim(StringView characters, TrimMode mode = TrimMode::Both) const { return StringUtils::trim(*this, characters, mode); } [[nodiscard]] StringView trim_whitespace(TrimMode mode = TrimMode::Both) const { return StringUtils::trim_whitespace(*this, mode); } - [[nodiscard]] ByteString to_lowercase_string() const; - [[nodiscard]] ByteString to_uppercase_string() const; - [[nodiscard]] ByteString to_titlecase_string() const; + [[nodiscard]] String to_ascii_lowercase_string() const; + [[nodiscard]] String to_ascii_uppercase_string() const; + [[nodiscard]] String to_ascii_titlecase_string() const; [[nodiscard]] Optional find(char needle, size_t start = 0) const { diff --git a/Libraries/LibIDL/IDLParser.cpp b/Libraries/LibIDL/IDLParser.cpp index 6be0c76f022..b3383751a11 100644 --- a/Libraries/LibIDL/IDLParser.cpp +++ b/Libraries/LibIDL/IDLParser.cpp @@ -89,7 +89,7 @@ static ByteString convert_enumeration_value_to_cpp_enum_member(ByteString const& auto word = lexer.consume_while([](auto c) { return is_ascii_alphanumeric(c); }); if (!word.is_empty()) { - builder.append(word.to_titlecase_string()); + builder.append(word.to_ascii_titlecase_string()); } else { auto non_alnum_string = lexer.consume_while([](auto c) { return !is_ascii_alphanumeric(c); }); if (!non_alnum_string.is_empty()) diff --git a/Libraries/LibJS/Runtime/Intl/DisplayNames.cpp b/Libraries/LibJS/Runtime/Intl/DisplayNames.cpp index 8ed78f4ee2f..b82ed1ff515 100644 --- a/Libraries/LibJS/Runtime/Intl/DisplayNames.cpp +++ b/Libraries/LibJS/Runtime/Intl/DisplayNames.cpp @@ -103,7 +103,7 @@ ThrowCompletionOr canonical_code_for_display_names(VM& vm, DisplayNames:: return vm.throw_completion(ErrorType::OptionIsNotValidValue, code, "region"sv); // b. Return the ASCII-uppercase of code. - return PrimitiveString::create(vm, code.to_uppercase_string()); + return PrimitiveString::create(vm, code.to_ascii_uppercase_string()); } // 3. If type is "script", then @@ -119,7 +119,7 @@ ThrowCompletionOr canonical_code_for_display_names(VM& vm, DisplayNames:: // c. Let first be the ASCII-uppercase of the substring of code from 0 to 1. // d. Let rest be the ASCII-lowercase of the substring of code from 1. // e. Return the string-concatenation of first and rest. - return PrimitiveString::create(vm, code.to_titlecase_string()); + return PrimitiveString::create(vm, code.to_ascii_titlecase_string()); } // 4. If type is "calendar", then @@ -133,7 +133,7 @@ ThrowCompletionOr canonical_code_for_display_names(VM& vm, DisplayNames:: return vm.throw_completion(ErrorType::OptionIsNotValidValue, code, "calendar"sv); // c. Return the ASCII-lowercase of code. - return PrimitiveString::create(vm, code.to_lowercase_string()); + return PrimitiveString::create(vm, code.to_ascii_lowercase_string()); } // 5. If type is "dateTimeField", then @@ -154,7 +154,7 @@ ThrowCompletionOr canonical_code_for_display_names(VM& vm, DisplayNames:: return vm.throw_completion(ErrorType::OptionIsNotValidValue, code, "currency"sv); // 8. Return the ASCII-uppercase of code. - return PrimitiveString::create(vm, code.to_uppercase_string()); + return PrimitiveString::create(vm, code.to_ascii_uppercase_string()); } // 12.5.2 IsValidDateTimeFieldCode ( field ), https://tc39.es/ecma402/#sec-isvaliddatetimefieldcode diff --git a/Libraries/LibURL/Parser.cpp b/Libraries/LibURL/Parser.cpp index c72249cad33..5a78a06f596 100644 --- a/Libraries/LibURL/Parser.cpp +++ b/Libraries/LibURL/Parser.cpp @@ -527,10 +527,8 @@ static ErrorOr domain_to_ascii(StringView domain, bool be_strict) } } - if (!slow_path) { - auto lowercase_domain = domain.to_lowercase_string(); - return String::from_utf8_without_validation(lowercase_domain.bytes()); - } + if (!slow_path) + return domain.to_ascii_lowercase_string(); } Unicode::IDNA::ToAsciiOptions const options { diff --git a/Libraries/LibWeb/DOM/DOMTokenList.cpp b/Libraries/LibWeb/DOM/DOMTokenList.cpp index 92afc12a754..f53cd956338 100644 --- a/Libraries/LibWeb/DOM/DOMTokenList.cpp +++ b/Libraries/LibWeb/DOM/DOMTokenList.cpp @@ -227,7 +227,7 @@ WebIDL::ExceptionOr DOMTokenList::supports(StringView token) return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, MUST(String::formatted("Attribute {} does not define any supported tokens", m_associated_attribute)) }; // 2. Let lowercase token be a copy of token, in ASCII lowercase. - auto lowercase_token = token.to_lowercase_string(); + auto lowercase_token = token.to_ascii_lowercase_string(); // 3. If lowercase token is present in supported tokens, return true. if (supported_tokens->contains_slow(lowercase_token)) diff --git a/Libraries/LibWeb/DOM/Document.cpp b/Libraries/LibWeb/DOM/Document.cpp index cd253ed61a3..e8a3d0b437e 100644 --- a/Libraries/LibWeb/DOM/Document.cpp +++ b/Libraries/LibWeb/DOM/Document.cpp @@ -2072,17 +2072,16 @@ HTML::EnvironmentSettingsObject& Document::relevant_settings_object() const } // https://dom.spec.whatwg.org/#dom-document-createelement -WebIDL::ExceptionOr> Document::create_element(String const& a_local_name, Variant const& options) +WebIDL::ExceptionOr> Document::create_element(String const& local_name, Variant const& options) { - auto local_name = a_local_name.to_byte_string(); - // 1. If localName does not match the Name production, then throw an "InvalidCharacterError" DOMException. - if (!is_valid_name(a_local_name)) + if (!is_valid_name(local_name)) return WebIDL::InvalidCharacterError::create(realm(), "Invalid character in tag name."_string); // 2. If this is an HTML document, then set localName to localName in ASCII lowercase. - if (document_type() == Type::HTML) - local_name = local_name.to_lowercase(); + auto local_name_lower = document_type() == Type::HTML + ? local_name.to_ascii_lowercase() + : local_name; // 3. Let is be null. Optional is_value; @@ -2100,7 +2099,7 @@ WebIDL::ExceptionOr> Document::create_element(String const& a_l namespace_ = Namespace::HTML; // 6. Return the result of creating an element given this, localName, namespace, null, is, and with the synchronous custom elements flag set. - return TRY(DOM::create_element(*this, FlyString::from_utf8_without_validation(local_name.bytes()), move(namespace_), {}, move(is_value), true)); + return TRY(DOM::create_element(*this, FlyString::from_utf8_without_validation(local_name_lower.bytes()), move(namespace_), {}, move(is_value), true)); } // https://dom.spec.whatwg.org/#dom-document-createelementns diff --git a/Libraries/LibWeb/Encoding/TextDecoder.cpp b/Libraries/LibWeb/Encoding/TextDecoder.cpp index 9b86efb5b11..1130f5dcd54 100644 --- a/Libraries/LibWeb/Encoding/TextDecoder.cpp +++ b/Libraries/LibWeb/Encoding/TextDecoder.cpp @@ -32,7 +32,7 @@ WebIDL::ExceptionOr> TextDecoder::construct_impl(JS::Realm& // 3. Set this’s encoding to encoding. // https://encoding.spec.whatwg.org/#dom-textdecoder-encoding // The encoding getter steps are to return this’s encoding’s name, ASCII lowercased. - auto lowercase_encoding_name = MUST(String::from_byte_string(encoding.value().to_lowercase_string())); + auto lowercase_encoding_name = encoding.value().to_ascii_lowercase_string(); // 4. If options["fatal"] is true, then set this’s error mode to "fatal". auto fatal = options.value_or({}).fatal; diff --git a/Libraries/LibWeb/HTML/TokenizedFeatures.cpp b/Libraries/LibWeb/HTML/TokenizedFeatures.cpp index 287de6c7d30..9b170bc26fb 100644 --- a/Libraries/LibWeb/HTML/TokenizedFeatures.cpp +++ b/Libraries/LibWeb/HTML/TokenizedFeatures.cpp @@ -68,7 +68,7 @@ TokenizedFeature::Map tokenize_open_features(StringView features) lexer.ignore_while(is_feature_separator); // 4. Collect a sequence of code points that are not feature separators from features given position. Set name to the collected characters, converted to ASCII lowercase. - name = MUST(String::from_byte_string(lexer.consume_until(is_feature_separator).to_lowercase_string())); + name = lexer.consume_until(is_feature_separator).to_ascii_lowercase_string(); // 5. Set name to the result of normalizing the feature name name. name = normalize_feature_name(name); @@ -85,7 +85,7 @@ TokenizedFeature::Map tokenize_open_features(StringView features) lexer.ignore_while([](auto character) { return Infra::is_ascii_whitespace(character) || character == '='; }); // 2. Collect a sequence of code points that are not feature separators code points from features given position. Set value to the collected code points, converted to ASCII lowercase. - value = MUST(String::from_byte_string(lexer.consume_until(is_feature_separator).to_lowercase_string())); + value = lexer.consume_until(is_feature_separator).to_ascii_lowercase_string(); // 8. If name is not the empty string, then set tokenizedFeatures[name] to value. if (!name.is_empty()) diff --git a/Meta/Lagom/Tools/CodeGenerators/LibWeb/GenerateWebGLRenderingContext.cpp b/Meta/Lagom/Tools/CodeGenerators/LibWeb/GenerateWebGLRenderingContext.cpp index bfa93bee0b2..53e27ac0fc0 100644 --- a/Meta/Lagom/Tools/CodeGenerators/LibWeb/GenerateWebGLRenderingContext.cpp +++ b/Meta/Lagom/Tools/CodeGenerators/LibWeb/GenerateWebGLRenderingContext.cpp @@ -288,7 +288,7 @@ static void generate_get_parameter(SourceGenerator& generator, int webgl_version return JS::@type_name@::create(m_realm, @element_count@, array_buffer); )~~~"); } else if (type_name == "WebGLProgram"sv || type_name == "WebGLBuffer"sv || type_name == "WebGLTexture"sv || type_name == "WebGLFramebuffer"sv || type_name == "WebGLRenderbuffer"sv) { - impl_generator.set("stored_name", name_and_type.name.to_lowercase_string()); + impl_generator.set("stored_name", name_and_type.name.to_ascii_lowercase_string()); impl_generator.append(R"~~~( if (!m_@stored_name@) return JS::js_null();