1// Copyright 2019 the V8 project authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include 'src/objects/js-objects.h' 6#include 'src/objects/intl-objects.h' 7 8extern macro IntlAsciiCollationWeightsL1(): RawPtr<uint8>; 9extern macro IntlAsciiCollationWeightsL3(): RawPtr<uint8>; 10const kIntlAsciiCollationWeightsLength: 11 constexpr int31 generates 'Intl::kAsciiCollationWeightsLength'; 12 13macro IntlAsciiCollationWeightL1(c: char8): uint8 labels _Bailout { 14 static_assert(kIntlAsciiCollationWeightsLength == 256); 15 return IntlAsciiCollationWeightsL1()[Convert<intptr>(c)]; 16} 17macro IntlAsciiCollationWeightL1(c: char16): uint8 labels Bailout { 18 if (Convert<uint32>(c) >= kIntlAsciiCollationWeightsLength) goto Bailout; 19 return IntlAsciiCollationWeightsL1()[Convert<intptr>(c)]; 20} 21 22macro IntlAsciiCollationWeightL3(c: char8): uint8 labels _Bailout { 23 static_assert(kIntlAsciiCollationWeightsLength == 256); 24 return IntlAsciiCollationWeightsL3()[Convert<intptr>(c)]; 25} 26macro IntlAsciiCollationWeightL3(c: char16): uint8 labels Bailout { 27 if (Convert<uint32>(c) >= kIntlAsciiCollationWeightsLength) goto Bailout; 28 return IntlAsciiCollationWeightsL3()[Convert<intptr>(c)]; 29} 30 31macro CheckEmptyOr1Byte( 32 _it: torque_internal::SliceIterator<char8, const &char8>): 33 void labels _Bailout { 34 // char8 is always within 0xFF. 35} 36macro CheckEmptyOr1Byte( 37 it: torque_internal::SliceIterator<char16, const &char16>): 38 void labels Bailout { 39 let it = it; 40 if ((it.Next() otherwise return ) > 0xFF) goto Bailout; 41} 42 43// This fast path works for ASCII-only strings and is based on the assumption 44// that most strings are either bytewise equal or differ on L1 (i.e., not just 45// in capitalization). So we first compare the strings on L1 and only afterwards 46// consider L3. This makes use of the 256-entry L1 and L3 tables defined in 47// src/objects/intl-objects.cc. 48macro LocaleCompareFastPath<T1: type, T2: type>( 49 left: ConstSlice<T1>, right: ConstSlice<T2>): Number labels Bailout { 50 if (EqualContent(left, right)) return 0; 51 let leftIt = left.Iterator(); 52 let rightIt = right.Iterator(); 53 while (true) { 54 try { 55 const lChar = leftIt.Next() otherwise goto LeftExhausted; 56 const leftWeight = IntlAsciiCollationWeightL1(lChar) otherwise Bailout; 57 if (leftWeight == 0) goto Bailout; 58 // If rightIt is exhausted, we already checked that the next char of the 59 // left string has non-zero weight, so it cannot be ignorable or a 60 // combining character. 61 // Return 1 because right string is shorter and L1 is equal. 62 const rChar = rightIt.Next() otherwise return 1; 63 const rightWeight = IntlAsciiCollationWeightL1(rChar) otherwise Bailout; 64 if (rightWeight == 0) goto Bailout; 65 if (leftWeight == rightWeight) continue; 66 // The result is only valid if the last processed character is not 67 // followed by a unicode combining character (we are overly strict and 68 // restrict to code points up to 0xFF). 69 CheckEmptyOr1Byte(leftIt) otherwise Bailout; 70 CheckEmptyOr1Byte(rightIt) otherwise Bailout; 71 if (leftWeight < rightWeight) return -1; 72 return 1; 73 } label LeftExhausted { 74 const rChar = rightIt.Next() otherwise break; 75 const rightWeight = IntlAsciiCollationWeightL1(rChar) otherwise Bailout; 76 // If the following character might be ignorable or a combining character, 77 // we bail out because the strings might still be considered equal. 78 if (rightWeight == 0) goto Bailout; 79 // Return -1 because left string is shorter and L1 is equal. 80 return -1; 81 } 82 } 83 leftIt = left.Iterator(); 84 rightIt = right.Iterator(); 85 while (true) { 86 const lChar = leftIt.Next() otherwise unreachable; 87 const leftWeight = IntlAsciiCollationWeightL3(lChar) otherwise unreachable; 88 dcheck(leftWeight != 0); 89 const rChar = rightIt.Next() otherwise unreachable; 90 const rightWeight = IntlAsciiCollationWeightL3(rChar) otherwise unreachable; 91 dcheck(rightWeight != 0); 92 dcheck( 93 IntlAsciiCollationWeightL1(lChar) otherwise unreachable == 94 IntlAsciiCollationWeightL1(rChar) otherwise unreachable); 95 if (leftWeight == rightWeight) continue; 96 if (leftWeight < rightWeight) return -1; 97 return 1; 98 } 99 VerifiedUnreachable(); 100} 101 102transitioning builtin StringFastLocaleCompare(implicit context: Context)( 103 localeCompareFn: JSFunction, left: JSAny, right: JSAny, 104 locales: JSAny): JSAny { 105 try { 106 const left = Cast<String>(left) otherwise Bailout; 107 if (TaggedEqual(left, right)) return SmiConstant(0); 108 StringToSlice(left) otherwise LeftOneByte, LeftTwoByte; 109 } label LeftOneByte(leftSlice: ConstSlice<char8>) { 110 try { 111 const right = Cast<String>(right) otherwise Bailout; 112 StringToSlice(right) otherwise RightOneByte, RightTwoByte; 113 } label RightOneByte(rightSlice: ConstSlice<char8>) { 114 return LocaleCompareFastPath(leftSlice, rightSlice) otherwise Bailout; 115 } label RightTwoByte(rightSlice: ConstSlice<char16>) { 116 return LocaleCompareFastPath(leftSlice, rightSlice) otherwise Bailout; 117 } 118 } label LeftTwoByte(leftSlice: ConstSlice<char16>) { 119 try { 120 const right = Cast<String>(right) otherwise Bailout; 121 StringToSlice(right) otherwise RightOneByte, RightTwoByte; 122 } label RightOneByte(rightSlice: ConstSlice<char8>) { 123 return LocaleCompareFastPath(leftSlice, rightSlice) otherwise Bailout; 124 } label RightTwoByte(rightSlice: ConstSlice<char16>) { 125 return LocaleCompareFastPath(leftSlice, rightSlice) otherwise Bailout; 126 } 127 } label Bailout deferred { 128 return Call(context, localeCompareFn, left, right, locales); 129 } 130} 131