summaryrefslogtreecommitdiff
path: root/deps/v8/src/objects/intl-objects.tq
blob: bd89c2cebd71338355e6536062f692c8b0016803 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include 'src/objects/js-objects.h'
#include 'src/objects/intl-objects.h'

extern macro IntlAsciiCollationWeightsL1(): RawPtr<uint8>;
extern macro IntlAsciiCollationWeightsL3(): RawPtr<uint8>;
const kIntlAsciiCollationWeightsLength:
    constexpr int31 generates 'Intl::kAsciiCollationWeightsLength';

macro IntlAsciiCollationWeightL1(c: char8): uint8 labels _Bailout {
  static_assert(kIntlAsciiCollationWeightsLength == 256);
  return IntlAsciiCollationWeightsL1()[Convert<intptr>(c)];
}
macro IntlAsciiCollationWeightL1(c: char16): uint8 labels Bailout {
  if (Convert<uint32>(c) >= kIntlAsciiCollationWeightsLength) goto Bailout;
  return IntlAsciiCollationWeightsL1()[Convert<intptr>(c)];
}

macro IntlAsciiCollationWeightL3(c: char8): uint8 labels _Bailout {
  static_assert(kIntlAsciiCollationWeightsLength == 256);
  return IntlAsciiCollationWeightsL3()[Convert<intptr>(c)];
}
macro IntlAsciiCollationWeightL3(c: char16): uint8 labels Bailout {
  if (Convert<uint32>(c) >= kIntlAsciiCollationWeightsLength) goto Bailout;
  return IntlAsciiCollationWeightsL3()[Convert<intptr>(c)];
}

macro CheckEmptyOr1Byte(
    _it: torque_internal::SliceIterator<char8, const &char8>):
    void labels _Bailout {
  // char8 is always within 0xFF.
}
macro CheckEmptyOr1Byte(
    it: torque_internal::SliceIterator<char16, const &char16>):
    void labels Bailout {
  let it = it;
  if ((it.Next() otherwise return) > 0xFF) goto Bailout;
}

// This fast path works for ASCII-only strings and is based on the assumption
// that most strings are either bytewise equal or differ on L1 (i.e., not just
// in capitalization). So we first compare the strings on L1 and only afterwards
// consider L3. This makes use of the 256-entry L1 and L3 tables defined in
// src/objects/intl-objects.cc.
macro LocaleCompareFastPath<T1: type, T2: type>(
    left: ConstSlice<T1>, right: ConstSlice<T2>): Number labels Bailout {
  if (EqualContent(left, right)) return 0;
  let leftIt = left.Iterator();
  let rightIt = right.Iterator();
  while (true) {
    try {
      const lChar = leftIt.Next() otherwise goto LeftExhausted;
      const leftWeight = IntlAsciiCollationWeightL1(lChar) otherwise Bailout;
      if (leftWeight == 0) goto Bailout;
      // If rightIt is exhausted, we already checked that the next char of the
      // left string has non-zero weight, so it cannot be ignorable or a
      // combining character.
      // Return 1 because right string is shorter and L1 is equal.
      const rChar = rightIt.Next() otherwise return 1;
      const rightWeight = IntlAsciiCollationWeightL1(rChar) otherwise Bailout;
      if (rightWeight == 0) goto Bailout;
      if (leftWeight == rightWeight) continue;
      // The result is only valid if the last processed character is not
      // followed by a unicode combining character (we are overly strict and
      // restrict to code points up to 0xFF).
      CheckEmptyOr1Byte(leftIt) otherwise Bailout;
      CheckEmptyOr1Byte(rightIt) otherwise Bailout;
      if (leftWeight < rightWeight) return -1;
      return 1;
    } label LeftExhausted {
      const rChar = rightIt.Next() otherwise break;
      const rightWeight = IntlAsciiCollationWeightL1(rChar) otherwise Bailout;
      // If the following character might be ignorable or a combining character,
      // we bail out because the strings might still be considered equal.
      if (rightWeight == 0) goto Bailout;
      // Return -1 because left string is shorter and L1 is equal.
      return -1;
    }
  }
  leftIt = left.Iterator();
  rightIt = right.Iterator();
  while (true) {
    const lChar = leftIt.Next() otherwise unreachable;
    const leftWeight = IntlAsciiCollationWeightL3(lChar) otherwise unreachable;
    dcheck(leftWeight != 0);
    const rChar = rightIt.Next() otherwise unreachable;
    const rightWeight = IntlAsciiCollationWeightL3(rChar) otherwise unreachable;
    dcheck(rightWeight != 0);
    dcheck(
        IntlAsciiCollationWeightL1(lChar) otherwise unreachable ==
        IntlAsciiCollationWeightL1(rChar) otherwise unreachable);
    if (leftWeight == rightWeight) continue;
    if (leftWeight < rightWeight) return -1;
    return 1;
  }
  VerifiedUnreachable();
}

transitioning builtin StringFastLocaleCompare(implicit context: Context)(
    localeCompareFn: JSFunction, left: JSAny, right: JSAny,
    locales: JSAny): JSAny {
  try {
    const left = Cast<String>(left) otherwise Bailout;
    if (TaggedEqual(left, right)) return SmiConstant(0);
    StringToSlice(left) otherwise LeftOneByte, LeftTwoByte;
  } label LeftOneByte(leftSlice: ConstSlice<char8>) {
    try {
      const right = Cast<String>(right) otherwise Bailout;
      StringToSlice(right) otherwise RightOneByte, RightTwoByte;
    } label RightOneByte(rightSlice: ConstSlice<char8>) {
      return LocaleCompareFastPath(leftSlice, rightSlice) otherwise Bailout;
    } label RightTwoByte(rightSlice: ConstSlice<char16>) {
      return LocaleCompareFastPath(leftSlice, rightSlice) otherwise Bailout;
    }
  } label LeftTwoByte(leftSlice: ConstSlice<char16>) {
    try {
      const right = Cast<String>(right) otherwise Bailout;
      StringToSlice(right) otherwise RightOneByte, RightTwoByte;
    } label RightOneByte(rightSlice: ConstSlice<char8>) {
      return LocaleCompareFastPath(leftSlice, rightSlice) otherwise Bailout;
    } label RightTwoByte(rightSlice: ConstSlice<char16>) {
      return LocaleCompareFastPath(leftSlice, rightSlice) otherwise Bailout;
    }
  } label Bailout deferred {
    return Call(context, localeCompareFn, left, right, locales);
  }
}