@@ -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
552608JS_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