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/builtins/builtins-string-gen.h'
6
7namespace string {
8
9extern enum TrimMode extends uint31 constexpr 'String::TrimMode' {
10  kTrim,
11  kTrimStart,
12  kTrimEnd
13}
14
15@export
16macro IsWhiteSpaceOrLineTerminator(charCode: char16|char8): bool {
17  // 0x0020 - SPACE (Intentionally out of order to fast path a commmon case)
18  if (charCode == 0x0020) {
19    return true;
20  }
21
22  // Common Non-whitespace characters from (0x000E, 0x00A0)
23  if (Unsigned(Convert<int32>(charCode) - 0x000E) < 0x0092) {
24    return false;
25  }
26
27  // 0x0009 - HORIZONTAL TAB
28  if (charCode < 0x0009) {
29    return false;
30  }
31  // 0x000A - LINE FEED OR NEW LINE
32  // 0x000B - VERTICAL TAB
33  // 0x000C - FORMFEED
34  // 0x000D - HORIZONTAL TAB
35  if (charCode <= 0x000D) {
36    return true;
37  }
38
39  // 0x00A0 - NO-BREAK SPACE
40  if (charCode == 0x00A0) {
41    return true;
42  }
43
44  // 0x1680 - Ogham Space Mark
45  if (charCode == 0x1680) {
46    return true;
47  }
48
49  // 0x2000 - EN QUAD
50  if (charCode < 0x2000) {
51    return false;
52  }
53  // 0x2001 - EM QUAD
54  // 0x2002 - EN SPACE
55  // 0x2003 - EM SPACE
56  // 0x2004 - THREE-PER-EM SPACE
57  // 0x2005 - FOUR-PER-EM SPACE
58  // 0x2006 - SIX-PER-EM SPACE
59  // 0x2007 - FIGURE SPACE
60  // 0x2008 - PUNCTUATION SPACE
61  // 0x2009 - THIN SPACE
62  // 0x200A - HAIR SPACE
63  if (charCode <= 0x200A) {
64    return true;
65  }
66
67  // 0x2028 - LINE SEPARATOR
68  if (charCode == 0x2028) {
69    return true;
70  }
71  // 0x2029 - PARAGRAPH SEPARATOR
72  if (charCode == 0x2029) {
73    return true;
74  }
75  // 0x202F - NARROW NO-BREAK SPACE
76  if (charCode == 0x202F) {
77    return true;
78  }
79  // 0x205F - MEDIUM MATHEMATICAL SPACE
80  if (charCode == 0x205F) {
81    return true;
82  }
83  // 0xFEFF - BYTE ORDER MARK
84  if (charCode == 0xFEFF) {
85    return true;
86  }
87  // 0x3000 - IDEOGRAPHIC SPACE
88  if (charCode == 0x3000) {
89    return true;
90  }
91
92  return false;
93}
94
95transitioning macro StringTrimLoop<T: type>(implicit context: Context)(
96    stringSlice: ConstSlice<T>, startIndex: intptr, endIndex: intptr,
97    increment: intptr): intptr {
98  let index = startIndex;
99  while (true) {
100    if (index == endIndex) {
101      return index;
102    }
103
104    const char: T = *stringSlice.AtIndex(index);
105    if (!IsWhiteSpaceOrLineTerminator(char)) {
106      return index;
107    }
108    index = index + increment;
109  }
110  unreachable;
111}
112
113transitioning macro StringTrimBody<T: type>(implicit context: Context)(
114    string: String, slice: ConstSlice<T>, variant: constexpr TrimMode): String {
115  const stringLength: intptr = string.length_intptr;
116
117  let startIndex: intptr = 0;
118  let endIndex: intptr = stringLength - 1;
119  if (variant == TrimMode::kTrim || variant == TrimMode::kTrimStart) {
120    startIndex = StringTrimLoop(slice, startIndex, stringLength, 1);
121    if (startIndex == stringLength) {
122      return kEmptyString;
123    }
124  }
125
126  if (variant == TrimMode::kTrim || variant == TrimMode::kTrimEnd) {
127    endIndex = StringTrimLoop(slice, endIndex, -1, -1);
128    if (endIndex == -1) {
129      return kEmptyString;
130    }
131  }
132
133  return SubString(string, Unsigned(startIndex), Unsigned(endIndex + 1));
134}
135
136transitioning macro StringTrim(implicit context: Context)(
137    receiver: JSAny, _arguments: Arguments, methodName: constexpr string,
138    variant: constexpr TrimMode): String {
139  const receiverString: String = ToThisString(receiver, methodName);
140
141  try {
142    StringToSlice(receiverString) otherwise OneByte, TwoByte;
143  } label OneByte(slice: ConstSlice<char8>) {
144    return StringTrimBody(receiverString, slice, variant);
145  } label TwoByte(slice: ConstSlice<char16>) {
146    return StringTrimBody(receiverString, slice, variant);
147  }
148}
149
150// ES6 #sec-string.prototype.trim
151transitioning javascript builtin
152StringPrototypeTrim(
153    js-implicit context: NativeContext, receiver: JSAny)(...arguments): String {
154  const methodName: constexpr string = 'String.prototype.trim';
155  return StringTrim(receiver, arguments, methodName, TrimMode::kTrim);
156}
157
158// https://github.com/tc39/proposal-string-left-right-trim
159transitioning javascript builtin
160StringPrototypeTrimStart(
161    js-implicit context: NativeContext, receiver: JSAny)(...arguments): String {
162  const methodName: constexpr string = 'String.prototype.trimLeft';
163  return StringTrim(receiver, arguments, methodName, TrimMode::kTrimStart);
164}
165
166// https://github.com/tc39/proposal-string-left-right-trim
167transitioning javascript builtin
168StringPrototypeTrimEnd(
169    js-implicit context: NativeContext, receiver: JSAny)(...arguments): String {
170  const methodName: constexpr string = 'String.prototype.trimRight';
171  return StringTrim(receiver, arguments, methodName, TrimMode::kTrimEnd);
172}
173}
174