From b4113536ef6b4141c004ab008ca7fe9c8e35ed72 Mon Sep 17 00:00:00 2001 From: Timothy Flynn Date: Sat, 18 Feb 2023 09:32:59 -0500 Subject: LibJS: Use substrings-with-superstrings in Intl.NumberFormat's grouping To add grouping to a number, we take a string such as "123456.123" and break it into integer and fraction parts. Then we take the integer part and break it into locale-specific sized groups to inject the locale's group separator (e.g. a comma in en-US). We currently create new strings for each of these groups. Instead, we can use the shared superstring method to avoid all of that string copying. --- .../Libraries/LibJS/Runtime/Intl/NumberFormat.cpp | 37 ++++++++++++---------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/Userland/Libraries/LibJS/Runtime/Intl/NumberFormat.cpp b/Userland/Libraries/LibJS/Runtime/Intl/NumberFormat.cpp index 883a7cbe36..8ee1e6a472 100644 --- a/Userland/Libraries/LibJS/Runtime/Intl/NumberFormat.cpp +++ b/Userland/Libraries/LibJS/Runtime/Intl/NumberFormat.cpp @@ -672,15 +672,15 @@ ThrowCompletionOr> partition_number_pattern(VM& vm, Num return result; } -static ThrowCompletionOr> separate_integer_into_groups(VM& vm, ::Locale::NumberGroupings const& grouping_sizes, StringView integer, NumberFormat::UseGrouping use_grouping) +static ThrowCompletionOr> separate_integer_into_groups(VM& vm, ::Locale::NumberGroupings const& grouping_sizes, String integer, NumberFormat::UseGrouping use_grouping) { - auto default_group = [&]() -> ThrowCompletionOr> { - Vector groups; - TRY_OR_THROW_OOM(vm, groups.try_append(integer)); + auto default_group = [&]() -> ThrowCompletionOr> { + Vector groups; + TRY_OR_THROW_OOM(vm, groups.try_append(move(integer))); return groups; }; - Utf8View utf8_integer { integer }; + auto utf8_integer = integer.code_points(); if (utf8_integer.length() <= grouping_sizes.primary_grouping_size) return default_group(); @@ -704,10 +704,15 @@ static ThrowCompletionOr> separate_integer_into_groups(VM& vm VERIFY_NOT_REACHED(); } - Vector groups; + Vector groups; auto add_group = [&](size_t index, size_t length) -> ThrowCompletionOr { - TRY_OR_THROW_OOM(vm, groups.try_prepend(utf8_integer.unicode_substring_view(index, length).as_string())); + length = utf8_integer.unicode_substring_view(index, length).byte_length(); + index = utf8_integer.byte_offset_of(index); + + auto group = TRY_OR_THROW_OOM(vm, integer.substring_from_byte_offset_with_shared_superstring(index, length)); + TRY_OR_THROW_OOM(vm, groups.try_prepend(move(group))); + return {}; }; @@ -788,28 +793,28 @@ ThrowCompletionOr> partition_notation_sub_pattern(VM& v // 3. Let decimalSepIndex be StringIndexOf(n, ".", 0). auto decimal_sep_index = formatted_string.find_byte_offset('.'); - StringView integer; - Optional fraction; + String integer; + Optional fraction; // 4. If decimalSepIndex > 0, then if (decimal_sep_index.has_value() && (*decimal_sep_index > 0)) { // a. Let integer be the substring of n from position 0, inclusive, to position decimalSepIndex, exclusive. - integer = formatted_string.bytes_as_string_view().substring_view(0, *decimal_sep_index); + integer = TRY_OR_THROW_OOM(vm, formatted_string.substring_from_byte_offset_with_shared_superstring(0, *decimal_sep_index)); // b. Let fraction be the substring of n from position decimalSepIndex, exclusive, to the end of n. - fraction = formatted_string.bytes_as_string_view().substring_view(*decimal_sep_index + 1); + fraction = TRY_OR_THROW_OOM(vm, formatted_string.substring_from_byte_offset_with_shared_superstring(*decimal_sep_index + 1)); } // 5. Else, else { // a. Let integer be n. - integer = formatted_string.bytes_as_string_view(); + integer = move(formatted_string); // b. Let fraction be undefined. } // 6. If the numberFormat.[[UseGrouping]] is false, then if (number_format.use_grouping() == NumberFormat::UseGrouping::False) { // a. Append a new Record { [[Type]]: "integer", [[Value]]: integer } as the last element of result. - TRY_OR_THROW_OOM(vm, result.try_append({ "integer"sv, TRY_OR_THROW_OOM(vm, String::from_utf8(integer)) })); + TRY_OR_THROW_OOM(vm, result.try_append({ "integer"sv, move(integer) })); } // 7. Else, else { @@ -817,7 +822,7 @@ ThrowCompletionOr> partition_notation_sub_pattern(VM& v auto group_sep_symbol = TRY_OR_THROW_OOM(vm, ::Locale::get_number_system_symbol(number_format.data_locale(), number_format.numbering_system(), ::Locale::NumericSymbol::Group)).value_or(","sv); // b. Let groups be a List whose elements are, in left to right order, the substrings defined by ILND set of locations within the integer, which may depend on the value of numberFormat.[[UseGrouping]]. - auto groups = MUST_OR_THROW_OOM(separate_integer_into_groups(vm, *grouping_sizes, integer, number_format.use_grouping())); + auto groups = MUST_OR_THROW_OOM(separate_integer_into_groups(vm, *grouping_sizes, move(integer), number_format.use_grouping())); // c. Assert: The number of elements in groups List is greater than 0. VERIFY(!groups.is_empty()); @@ -828,7 +833,7 @@ ThrowCompletionOr> partition_notation_sub_pattern(VM& v auto integer_group = groups.take_first(); // ii. Append a new Record { [[Type]]: "integer", [[Value]]: integerGroup } as the last element of result. - TRY_OR_THROW_OOM(vm, result.try_append({ "integer"sv, TRY_OR_THROW_OOM(vm, String::from_utf8(integer_group)) })); + TRY_OR_THROW_OOM(vm, result.try_append({ "integer"sv, move(integer_group) })); // iii. If groups List is not empty, then if (!groups.is_empty()) { @@ -845,7 +850,7 @@ ThrowCompletionOr> partition_notation_sub_pattern(VM& v // b. Append a new Record { [[Type]]: "decimal", [[Value]]: decimalSepSymbol } as the last element of result. TRY_OR_THROW_OOM(vm, result.try_append({ "decimal"sv, TRY_OR_THROW_OOM(vm, String::from_utf8(decimal_sep_symbol)) })); // c. Append a new Record { [[Type]]: "fraction", [[Value]]: fraction } as the last element of result. - TRY_OR_THROW_OOM(vm, result.try_append({ "fraction"sv, TRY_OR_THROW_OOM(vm, String::from_utf8(*fraction)) })); + TRY_OR_THROW_OOM(vm, result.try_append({ "fraction"sv, fraction.release_value() })); } } // iv. Else if p is equal to "compactSymbol", then -- cgit v1.2.3