Skip to content

Commit 999656a

Browse files
tcl3gmta
authored andcommitted
LibJS: Add a localCompare() fast path for ASCII strings
When comparing ASCII strings we now use our own collation tables rather than invoking ICU.
1 parent f268458 commit 999656a

File tree

1 file changed

+61
-0
lines changed

1 file changed

+61
-0
lines changed

Libraries/LibJS/Runtime/StringPrototype.cpp

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -547,6 +547,62 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::last_index_of)
547547
return *result;
548548
}
549549

550+
// clang-format off
551+
static constexpr AK::Array<u8, 128> ascii_collation_primary_weights {
552+
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 0, 0,
553+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
554+
6, 12, 16, 28, 38, 29, 27, 15, 17, 18, 24, 32, 9, 8, 14, 25,
555+
39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 11, 10, 33, 34, 35, 13,
556+
23, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
557+
64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 19, 26, 20, 31, 7,
558+
30, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
559+
64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 21, 36, 22, 37, 0,
560+
};
561+
562+
static constexpr AK::Array<u8, 128> ascii_collation_tertiary_weights {
563+
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0,
564+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
565+
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
566+
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
567+
1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
568+
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1,
569+
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
570+
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
571+
};
572+
// clang-format on
573+
574+
static Optional<int> try_fast_ascii_string_compare(StringView a, StringView b)
575+
{
576+
auto len = min(a.length(), b.length());
577+
578+
for (size_t i = 0; i < len; i++) {
579+
auto a_character = a[i];
580+
auto b_character = b[i];
581+
582+
if (a_character != b_character) {
583+
auto a_primary_weight = ascii_collation_primary_weights[a_character];
584+
// We bail for control characters so that we don't have to compare secondary weights.
585+
if (a_primary_weight == 0)
586+
return {};
587+
auto b_primary_weight = ascii_collation_primary_weights[b_character];
588+
if (b_primary_weight == 0)
589+
return {};
590+
if (a_primary_weight != b_primary_weight)
591+
return a_primary_weight < b_primary_weight ? -1 : 1;
592+
if (a.length() != b.length())
593+
return a.length() < b.length() ? -1 : 1;
594+
auto a_tertiary_weight = ascii_collation_tertiary_weights[a_character];
595+
auto b_tertiary_weight = ascii_collation_tertiary_weights[b_character];
596+
if (a_tertiary_weight != b_tertiary_weight)
597+
return a_tertiary_weight < b_tertiary_weight ? -1 : 1;
598+
}
599+
}
600+
if (a.length() != b.length())
601+
return a.length() < b.length() ? -1 : 1;
602+
603+
return 0;
604+
}
605+
550606
// 22.1.3.12 String.prototype.localeCompare ( that [ , reserved1 [ , reserved2 ] ] ), https://tc39.es/ecma262/#sec-string.prototype.localecompare
551607
// 20.1.1 String.prototype.localeCompare ( that [ , locales [ , options ] ] ), https://tc39.es/ecma402/#sup-String.prototype.localeCompare
552608
JS_DEFINE_NATIVE_FUNCTION(StringPrototype::locale_compare)
@@ -572,6 +628,11 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::locale_compare)
572628
// OPTIMIZATION: Identical strings are equal with the default options.
573629
if (string == that_value)
574630
return Value(0);
631+
// OPTIMIZATION: If both strings are ASCII, use a comparison that doesn't invoke ICU.
632+
if (string.has_ascii_storage() && that_value.has_ascii_storage()) {
633+
if (auto maybe_result = try_fast_ascii_string_compare(string.ascii_view(), that_value.ascii_view()); maybe_result.has_value())
634+
return Value(*maybe_result);
635+
}
575636
collator = realm.intrinsics().default_collator();
576637
} else {
577638
collator = TRY(construct(vm, realm.intrinsics().intl_collator_constructor(), locales, options));

0 commit comments

Comments
 (0)