• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include <algorithm>
17 #include <cstdint>
18 #include <regex>
19 #include "include/mem/panda_string.h"
20 #include "intrinsics.h"
21 #include "libpandabase/utils/logger.h"
22 #include "macros.h"
23 #include "napi/ets_napi.h"
24 #include "runtime/handle_scope-inl.h"
25 #include "runtime/entrypoints/string_index_of.h"
26 #include "runtime/arch/memory_helpers.h"
27 #include "plugins/ets/runtime/types/ets_string.h"
28 #include "plugins/ets/runtime/ets_exceptions.h"
29 #include "plugins/ets/runtime/ets_language_context.h"
30 #include "plugins/ets/runtime/ets_panda_file_items.h"
31 
32 #include "unicode/locid.h"
33 #include "unicode/coll.h"
34 #include "unicode/unistr.h"
35 #include "unicode/normalizer2.h"
36 #include "utils/span.h"
37 
38 using icu::Normalizer2;
39 
40 namespace ark::ets::intrinsics {
41 
42 constexpr const uint32_t CHAR0X1FFC00 = 0x1ffc00;
43 constexpr const uint16_t CHAR0XD800 = 0xd800;
44 constexpr const uint16_t CHAR0XDC00 = 0xdc00;
45 
StdCoreStringGetDataAsArray(EtsString * s,ets_int begin,ets_int end,bool isUtf16)46 static ObjectHeader *StdCoreStringGetDataAsArray(EtsString *s, ets_int begin, ets_int end, bool isUtf16)
47 {
48     ASSERT(s != nullptr);
49     ets_int length = s->GetLength();
50     if (UNLIKELY(begin > end)) {
51         ark::ThrowStringIndexOutOfBoundsException(begin, length);
52         return nullptr;
53     }
54     if (UNLIKELY(begin > length || begin < 0)) {
55         ark::ThrowStringIndexOutOfBoundsException(begin, length);
56         return nullptr;
57     }
58     if (UNLIKELY(end > length)) {
59         ark::ThrowStringIndexOutOfBoundsException(end, length);
60         return nullptr;
61     }
62 
63     auto thread = ManagedThread::GetCurrent();
64     [[maybe_unused]] HandleScope<ObjectHeader *> scope(thread);
65     VMHandle<coretypes::String> sHandle(thread, s->GetCoreType());
66     ets_int n = end - begin;
67     void *array = nullptr;
68     if (isUtf16) {
69         array = EtsCharArray::Create(n);
70     } else {
71         array = EtsByteArray::Create(n);
72     }
73     if (array == nullptr || n == 0) {
74         return reinterpret_cast<ObjectHeader *>(array);
75     }
76     if (isUtf16) {
77         auto charArray = reinterpret_cast<EtsCharArray *>(array);
78         Span<ets_char> out(charArray->GetData<ets_char>(), charArray->GetLength());
79         sHandle.GetPtr()->CopyDataRegionUtf16(&out[0], begin, charArray->GetLength(), sHandle.GetPtr()->GetLength());
80     } else {
81         auto byteArray = reinterpret_cast<EtsByteArray *>(array);
82         Span<uint8_t> out(byteArray->GetData<uint8_t>(), byteArray->GetLength());
83 
84         /* as we need only one LSB no sophisticated conversion is needed */
85         if (sHandle.GetPtr()->IsUtf16()) {
86             auto in = sHandle.GetPtr()->GetDataUtf16();
87             for (int i = 0; i < n; ++i) {
88                 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
89                 out[i] = in[i + begin];
90             }
91         } else {
92             auto in = sHandle.GetPtr()->GetDataMUtf8();
93             for (int i = 0; i < n; ++i) {
94                 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
95                 out[i] = in[i + begin];
96             }
97         }
98     }
99     return reinterpret_cast<ObjectHeader *>(array);
100 }
101 
StdCoreStringGetChars(EtsString * s,ets_int begin,ets_int end)102 ObjectHeader *StdCoreStringGetChars(EtsString *s, ets_int begin, ets_int end)
103 {
104     return StdCoreStringGetDataAsArray(s, begin, end, true);
105 }
106 
StdCoreStringGetBytes(EtsString * s,ets_int begin,ets_int end)107 ObjectHeader *StdCoreStringGetBytes(EtsString *s, ets_int begin, ets_int end)
108 {
109     return StdCoreStringGetDataAsArray(s, begin, end, false);
110 }
111 
StdCoreStringSubstring(EtsString * str,ets_int begin,ets_int end)112 EtsString *StdCoreStringSubstring(EtsString *str, ets_int begin, ets_int end)
113 {
114     ASSERT(str != nullptr);
115     auto indexes = coretypes::String::NormalizeSubStringIndexes(begin, end, str->GetCoreType());
116     if (UNLIKELY(indexes.first == 0 && indexes.second == str->GetLength())) {
117         return str;
118     }
119     ets_int substrLength = indexes.second - indexes.first;
120     return EtsString::FastSubString(str, static_cast<uint32_t>(indexes.first), static_cast<uint32_t>(substrLength));
121 }
122 
StdCoreStringCharAt(EtsString * s,int32_t index)123 uint16_t StdCoreStringCharAt(EtsString *s, int32_t index)
124 {
125     ASSERT(s != nullptr);
126 
127     int32_t length = s->GetLength();
128     if (UNLIKELY(index >= length || index < 0)) {
129         ark::ThrowStringIndexOutOfBoundsException(index, length);
130         return 0;
131     }
132 
133     if (s->IsUtf16()) {
134         Span<uint16_t> sp(s->GetDataUtf16(), length);
135         return sp[index];
136     }
137 
138     Span<uint8_t> sp(s->GetDataMUtf8(), length);
139     return sp[index];
140 }
141 
StdCoreStringGetLength(EtsString * s)142 int32_t StdCoreStringGetLength(EtsString *s)
143 {
144     ASSERT(s != nullptr);
145     return s->GetLength();
146 }
147 
StdCoreStringLength(EtsString * s)148 double StdCoreStringLength(EtsString *s)
149 {
150     ASSERT(s != nullptr);
151     return static_cast<double>(s->GetLength());
152 }
153 
StdCoreStringIsEmpty(EtsString * s)154 EtsBoolean StdCoreStringIsEmpty(EtsString *s)
155 {
156     ASSERT(s != nullptr);
157     return ToEtsBoolean(s->IsEmpty());
158 }
159 
StdCoreStringEquals(EtsString * owner,EtsObject * s)160 uint8_t StdCoreStringEquals(EtsString *owner, EtsObject *s)
161 {
162     if ((owner->AsObject()) == s) {
163         return UINT8_C(1);
164     }
165     if (s == nullptr || !(s->GetClass()->IsStringClass())) {
166         return UINT8_C(0);
167     }
168     return static_cast<uint8_t>(owner->StringsAreEqual(s));
169 }
170 
StdCoreStringMatch(EtsString * thisStr,EtsString * reg)171 EtsString *StdCoreStringMatch(EtsString *thisStr, EtsString *reg)
172 {
173     PandaVector<uint8_t> buf;
174     auto thisS = std::string(thisStr->ConvertToStringView(&buf));
175     auto regex = std::string(reg->ConvertToStringView(&buf));
176 
177     std::regex e(regex);
178     return EtsString::CreateFromMUtf8(std::sregex_iterator(thisS.begin(), thisS.end(), e)->str().c_str());
179 }
180 
StringNormalize(EtsString * str,const Normalizer2 * normalizer)181 EtsString *StringNormalize(EtsString *str, const Normalizer2 *normalizer)
182 {
183     auto coroutine = EtsCoroutine::GetCurrent();
184     [[maybe_unused]] HandleScope<ObjectHeader *> scope(coroutine);
185 
186     icu::UnicodeString utf16Str;
187     if (str->IsUtf16()) {
188         utf16Str = icu::UnicodeString {str->GetDataUtf16(), static_cast<int32_t>(str->GetUtf16Length())};
189     } else {
190         utf16Str =
191             icu::UnicodeString {utf::Mutf8AsCString(str->GetDataMUtf8()), static_cast<int32_t>(str->GetLength())};
192     }
193 
194     UErrorCode errorCode = U_ZERO_ERROR;
195     utf16Str = normalizer->normalize(utf16Str, errorCode);
196 
197     if (UNLIKELY(U_FAILURE(errorCode))) {
198         std::string message = "Got error in process of normalization: '" + std::string(u_errorName(errorCode)) + "'";
199         ThrowEtsException(coroutine, panda_file_items::class_descriptors::RANGE_ERROR, message);
200         return nullptr;
201     }
202 
203     return EtsString::CreateFromUtf16(reinterpret_cast<const uint16_t *>(utf16Str.getTerminatedBuffer()),
204                                       utf16Str.length());
205 }
206 
StdCoreStringNormalizeNFC(EtsString * thisStr)207 EtsString *StdCoreStringNormalizeNFC(EtsString *thisStr)
208 {
209     UErrorCode errorCode = U_ZERO_ERROR;
210     auto normalizer = Normalizer2::getNFCInstance(errorCode);
211     if (UNLIKELY(U_FAILURE(errorCode))) {
212         std::string message = "Cannot get NFC normalizer: '" + std::string(u_errorName(errorCode)) + "'";
213         ThrowEtsException(EtsCoroutine::GetCurrent(), panda_file_items::class_descriptors::RANGE_ERROR, message);
214         return nullptr;
215     }
216     return StringNormalize(thisStr, normalizer);
217 }
218 
StdCoreStringNormalizeNFD(EtsString * thisStr)219 EtsString *StdCoreStringNormalizeNFD(EtsString *thisStr)
220 {
221     UErrorCode errorCode = U_ZERO_ERROR;
222     auto normalizer = Normalizer2::getNFDInstance(errorCode);
223     if (UNLIKELY(U_FAILURE(errorCode))) {
224         std::string message = "Cannot get NFD normalizer: '" + std::string(u_errorName(errorCode)) + "'";
225         ThrowEtsException(EtsCoroutine::GetCurrent(), panda_file_items::class_descriptors::RANGE_ERROR, message);
226         return nullptr;
227     }
228     return StringNormalize(thisStr, normalizer);
229 }
230 
StdCoreStringNormalizeNFKC(EtsString * thisStr)231 EtsString *StdCoreStringNormalizeNFKC(EtsString *thisStr)
232 {
233     UErrorCode errorCode = U_ZERO_ERROR;
234     auto normalizer = Normalizer2::getNFKCInstance(errorCode);
235     if (UNLIKELY(U_FAILURE(errorCode))) {
236         std::string message = "Cannot get NFKC normalizer: '" + std::string(u_errorName(errorCode)) + "'";
237         ThrowEtsException(EtsCoroutine::GetCurrent(), panda_file_items::class_descriptors::RANGE_ERROR, message);
238         return nullptr;
239     }
240     return StringNormalize(thisStr, normalizer);
241 }
242 
StdCoreStringNormalizeNFKD(EtsString * thisStr)243 EtsString *StdCoreStringNormalizeNFKD(EtsString *thisStr)
244 {
245     UErrorCode errorCode = U_ZERO_ERROR;
246     auto normalizer = Normalizer2::getNFKDInstance(errorCode);
247     if (UNLIKELY(U_FAILURE(errorCode))) {
248         std::string message = "Cannot get NFKD normalizer: '" + std::string(u_errorName(errorCode)) + "'";
249         ThrowEtsException(EtsCoroutine::GetCurrent(), panda_file_items::class_descriptors::RANGE_ERROR, message);
250         return nullptr;
251     }
252     return StringNormalize(thisStr, normalizer);
253 }
254 
StdCoreStringIsWellFormed(EtsString * thisStr)255 uint8_t StdCoreStringIsWellFormed(EtsString *thisStr)
256 {
257     if (!thisStr->IsUtf16()) {
258         return UINT8_C(1);
259     }
260     auto length = thisStr->GetUtf16Length();
261     auto codeUnits = Span<uint16_t>(thisStr->GetDataUtf16(), length);
262     for (size_t i = 0; i < length; ++i) {
263         uint16_t codeUnit = codeUnits[i];
264         if ((codeUnit & CHAR0X1FFC00) == CHAR0XD800) {
265             // Code unit is a leading surrogate
266             if (i == length - 1) {
267                 return UINT8_C(0);
268             }
269             // Is not trail surrogate
270             if ((codeUnits[i + 1] & CHAR0X1FFC00) != CHAR0XDC00) {
271                 return UINT8_C(0);
272             }
273             // Skip the paired trailing surrogate
274             ++i;
275             // Is trail surrogate
276         } else if ((codeUnit & CHAR0X1FFC00) == CHAR0XDC00) {
277             return UINT8_C(0);
278         }
279     }
280     return UINT8_C(1);
281 }
282 
ToLowerCase(EtsString * thisStr,const icu::Locale & locale)283 EtsString *ToLowerCase(EtsString *thisStr, const icu::Locale &locale)
284 {
285     auto coroutine = EtsCoroutine::GetCurrent();
286     [[maybe_unused]] HandleScope<ObjectHeader *> scope(coroutine);
287 
288     icu::UnicodeString utf16Str;
289     if (thisStr->IsUtf16()) {
290         utf16Str = icu::UnicodeString {thisStr->GetDataUtf16(), static_cast<int32_t>(thisStr->GetUtf16Length())};
291     } else {
292         utf16Str = icu::UnicodeString {utf::Mutf8AsCString(thisStr->GetDataMUtf8()),
293                                        static_cast<int32_t>(thisStr->GetLength())};
294     }
295     auto res = utf16Str.toLower(locale);
296     return EtsString::CreateFromUtf16(reinterpret_cast<const uint16_t *>(res.getTerminatedBuffer()), res.length());
297 }
298 
ToUpperCase(EtsString * thisStr,const icu::Locale & locale)299 EtsString *ToUpperCase(EtsString *thisStr, const icu::Locale &locale)
300 {
301     auto coroutine = EtsCoroutine::GetCurrent();
302     [[maybe_unused]] HandleScope<ObjectHeader *> scope(coroutine);
303 
304     icu::UnicodeString utf16Str;
305     if (thisStr->IsUtf16()) {
306         utf16Str = icu::UnicodeString {thisStr->GetDataUtf16(), static_cast<int32_t>(thisStr->GetUtf16Length())};
307     } else {
308         utf16Str = icu::UnicodeString {utf::Mutf8AsCString(thisStr->GetDataMUtf8()),
309                                        static_cast<int32_t>(thisStr->GetLength())};
310     }
311     auto res = utf16Str.toUpper(locale);
312     return EtsString::CreateFromUtf16(reinterpret_cast<const uint16_t *>(res.getTerminatedBuffer()), res.length());
313 }
314 
ParseSingleBCP47LanguageTag(EtsString * langTag,icu::Locale & locale)315 UErrorCode ParseSingleBCP47LanguageTag(EtsString *langTag, icu::Locale &locale)
316 {
317     if (langTag == nullptr) {
318         locale = icu::Locale::getDefault();
319         return U_ZERO_ERROR;
320     }
321 
322     PandaVector<uint8_t> buf;
323     std::string_view locTag = langTag->ConvertToStringView(&buf);
324     icu::StringPiece sp {locTag.data(), static_cast<int32_t>(locTag.size())};
325     UErrorCode status = U_ZERO_ERROR;
326     locale = icu::Locale::forLanguageTag(sp, status);
327     return status;
328 }
329 
StdCoreStringToUpperCase(EtsString * thisStr)330 EtsString *StdCoreStringToUpperCase(EtsString *thisStr)
331 {
332     return ToUpperCase(thisStr, icu::Locale::getDefault());
333 }
334 
StdCoreStringToLowerCase(EtsString * thisStr)335 EtsString *StdCoreStringToLowerCase(EtsString *thisStr)
336 {
337     return ToLowerCase(thisStr, icu::Locale::getDefault());
338 }
339 
StdCoreStringToLocaleUpperCase(EtsString * thisStr,EtsString * langTag)340 EtsString *StdCoreStringToLocaleUpperCase(EtsString *thisStr, EtsString *langTag)
341 {
342     ASSERT(langTag != nullptr);
343 
344     icu::Locale locale;
345     auto localeParseStatus = ParseSingleBCP47LanguageTag(langTag, locale);
346     if (UNLIKELY(U_FAILURE(localeParseStatus))) {
347         auto message = "Language tag '" + ConvertToString(langTag->GetCoreType()) + "' is invalid or not supported";
348         ThrowEtsException(EtsCoroutine::GetCurrent(), panda_file_items::class_descriptors::RANGE_ERROR, message);
349         return nullptr;
350     }
351     return ToUpperCase(thisStr, locale);
352 }
353 
StdCoreStringToLocaleLowerCase(EtsString * thisStr,EtsString * langTag)354 EtsString *StdCoreStringToLocaleLowerCase(EtsString *thisStr, EtsString *langTag)
355 {
356     ASSERT(langTag != nullptr);
357 
358     icu::Locale locale;
359     auto localeParseStatus = ParseSingleBCP47LanguageTag(langTag, locale);
360     if (UNLIKELY(U_FAILURE(localeParseStatus))) {
361         auto message = "Language tag '" + ConvertToString(langTag->GetCoreType()) + "' is invalid or not supported";
362         ThrowEtsException(EtsCoroutine::GetCurrent(), panda_file_items::class_descriptors::RANGE_ERROR, message);
363         return nullptr;
364     }
365     return ToLowerCase(thisStr, locale);
366 }
367 
StdCoreStringLocaleCmp(EtsString * thisStr,EtsString * cmpStr,EtsString * langTag)368 ets_double StdCoreStringLocaleCmp(EtsString *thisStr, EtsString *cmpStr, EtsString *langTag)
369 {
370     ASSERT(thisStr != nullptr && cmpStr != nullptr);
371 
372     icu::Locale locale;
373     auto status = ParseSingleBCP47LanguageTag(langTag, locale);
374     if (UNLIKELY(U_FAILURE(status))) {
375         auto message = "Language tag '" + ConvertToString(langTag->GetCoreType()) + "' is invalid or not supported";
376         ThrowEtsException(EtsCoroutine::GetCurrent(), panda_file_items::class_descriptors::RANGE_ERROR, message);
377         return 0;
378     }
379 
380     icu::UnicodeString source;
381     if (thisStr->IsUtf16()) {
382         source = icu::UnicodeString {thisStr->GetDataUtf16(), static_cast<int32_t>(thisStr->GetUtf16Length())};
383     } else {
384         source = icu::UnicodeString {utf::Mutf8AsCString(thisStr->GetDataMUtf8()),
385                                      static_cast<int32_t>(thisStr->GetLength())};
386     }
387     icu::UnicodeString target;
388     if (cmpStr->IsUtf16()) {
389         target = icu::UnicodeString {cmpStr->GetDataUtf16(), static_cast<int32_t>(cmpStr->GetUtf16Length())};
390     } else {
391         target =
392             icu::UnicodeString {utf::Mutf8AsCString(cmpStr->GetDataMUtf8()), static_cast<int32_t>(cmpStr->GetLength())};
393     }
394     status = U_ZERO_ERROR;
395     std::unique_ptr<icu::Collator> myCollator(icu::Collator::createInstance(locale, status));
396     if (UNLIKELY(U_FAILURE(status))) {
397         icu::UnicodeString dispName;
398         locale.getDisplayName(dispName);
399         std::string localeName;
400         dispName.toUTF8String(localeName);
401         LOG(FATAL, ETS) << "Failed to create the collator for " << localeName;
402     }
403     return myCollator->compare(source, target);
404 }
405 
StdCoreStringIndexOfAfter(EtsString * s,uint16_t ch,ets_int fromIndex)406 ets_int StdCoreStringIndexOfAfter(EtsString *s, uint16_t ch, ets_int fromIndex)
407 {
408     return ark::intrinsics::StringIndexOfU16(s, ch, fromIndex);
409 }
410 
StdCoreStringIndexOf(EtsString * s,uint16_t ch)411 ets_int StdCoreStringIndexOf(EtsString *s, uint16_t ch)
412 {
413     return StdCoreStringIndexOfAfter(s, ch, 0);
414 }
415 
StdCoreStringIndexOfString(EtsString * thisStr,EtsString * patternStr,ets_int fromIndex)416 ets_int StdCoreStringIndexOfString(EtsString *thisStr, EtsString *patternStr, ets_int fromIndex)
417 {
418     ASSERT(thisStr != nullptr && patternStr != nullptr);
419     return thisStr->GetCoreType()->IndexOf(patternStr->GetCoreType(), fromIndex);
420 }
421 
StdCoreStringLastIndexOfString(EtsString * thisStr,EtsString * patternStr,ets_int fromIndex)422 ets_int StdCoreStringLastIndexOfString(EtsString *thisStr, EtsString *patternStr, ets_int fromIndex)
423 {
424     ASSERT(thisStr != nullptr && patternStr != nullptr);
425     // "abc".lastIndexOf("ab", -10) will return 0
426     return thisStr->GetCoreType()->LastIndexOf(patternStr->GetCoreType(), std::max(fromIndex, 0));
427 }
428 
StdCoreStringCodePointToChar(ets_int codePoint)429 ets_int StdCoreStringCodePointToChar(ets_int codePoint)
430 {
431     icu::UnicodeString uniStr((UChar32)codePoint);
432     uint32_t ret = bit_cast<uint16_t>(uniStr.charAt(0));
433     // if codepoint contains a surrogate pair
434     // encode it into int with higher bits being second char
435     if (uniStr.length() > 1) {
436         constexpr uint32_t BITS_IN_CHAR = 16;
437         ret |= static_cast<uint32_t>(bit_cast<uint16_t>(uniStr.charAt(1))) << BITS_IN_CHAR;
438     }
439     return bit_cast<ets_int>(ret);
440 }
441 
StdCoreStringHashCode(EtsString * thisStr)442 int32_t StdCoreStringHashCode(EtsString *thisStr)
443 {
444     ASSERT(thisStr != nullptr);
445     return thisStr->GetCoreType()->GetHashcode();
446 }
447 
StdCoreStringIsCompressed(EtsString * thisStr)448 EtsBoolean StdCoreStringIsCompressed(EtsString *thisStr)
449 {
450     ASSERT(thisStr != nullptr);
451     return ToEtsBoolean(thisStr->GetCoreType()->IsMUtf8());
452 }
453 
StdCoreStringConcat2(EtsString * str1,EtsString * str2)454 EtsString *StdCoreStringConcat2(EtsString *str1, EtsString *str2)
455 {
456     auto s1 = reinterpret_cast<coretypes::String *>(str1);
457     auto s2 = reinterpret_cast<coretypes::String *>(str2);
458     return reinterpret_cast<EtsString *>(CoreStringConcat2(s1, s2));
459 }
460 
StdCoreStringConcat3(EtsString * str1,EtsString * str2,EtsString * str3)461 EtsString *StdCoreStringConcat3(EtsString *str1, EtsString *str2, EtsString *str3)
462 {
463     auto s1 = reinterpret_cast<coretypes::String *>(str1);
464     auto s2 = reinterpret_cast<coretypes::String *>(str2);
465     auto s3 = reinterpret_cast<coretypes::String *>(str3);
466     return reinterpret_cast<EtsString *>(CoreStringConcat3(s1, s2, s3));
467 }
468 
StdCoreStringConcat4(EtsString * str1,EtsString * str2,EtsString * str3,EtsString * str4)469 EtsString *StdCoreStringConcat4(EtsString *str1, EtsString *str2, EtsString *str3, EtsString *str4)
470 {
471     auto s1 = reinterpret_cast<coretypes::String *>(str1);
472     auto s2 = reinterpret_cast<coretypes::String *>(str2);
473     auto s3 = reinterpret_cast<coretypes::String *>(str3);
474     auto s4 = reinterpret_cast<coretypes::String *>(str4);
475     return reinterpret_cast<EtsString *>(CoreStringConcat4(s1, s2, s3, s4));
476 }
477 
StdCoreStringCompareTo(EtsString * str1,EtsString * str2)478 ets_int StdCoreStringCompareTo(EtsString *str1, EtsString *str2)
479 {
480     /* corner cases */
481     if (str1->GetLength() == 0) {
482         return -str2->GetLength();
483     }
484     if (str2->GetLength() == 0) {
485         return str1->GetLength();
486     }
487 
488     /* use the default implementation otherwise */
489     return str1->GetCoreType()->Compare(str2->GetCoreType());
490 }
491 
StdCoreStringTrimLeft(EtsString * thisStr)492 EtsString *StdCoreStringTrimLeft(EtsString *thisStr)
493 {
494     return thisStr->TrimLeft();
495 }
496 
StdCoreStringTrimRight(EtsString * thisStr)497 EtsString *StdCoreStringTrimRight(EtsString *thisStr)
498 {
499     return thisStr->TrimRight();
500 }
501 
StdCoreStringTrim(EtsString * thisStr)502 EtsString *StdCoreStringTrim(EtsString *thisStr)
503 {
504     return thisStr->Trim();
505 }
506 
StdCoreStringStartsWith(EtsString * thisStr,EtsString * prefix,EtsInt fromIndex)507 EtsBoolean StdCoreStringStartsWith(EtsString *thisStr, EtsString *prefix, EtsInt fromIndex)
508 {
509     ASSERT(thisStr != nullptr);
510     return thisStr->StartsWith(prefix, fromIndex);
511 }
512 
StdCoreStringEndsWith(EtsString * thisStr,EtsString * suffix,EtsInt endIndex)513 EtsBoolean StdCoreStringEndsWith(EtsString *thisStr, EtsString *suffix, EtsInt endIndex)
514 {
515     ASSERT(thisStr != nullptr);
516     return thisStr->EndsWith(suffix, endIndex);
517 }
518 
519 /* the allocation routine to create an unitialized string of the given size */
AllocateStringObject(size_t length,bool compressed)520 extern "C" EtsString *AllocateStringObject(size_t length, bool compressed)
521 {
522     auto ctx = Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::ETS);
523     auto vm = Runtime::GetCurrent()->GetPandaVM();
524     ASSERT(vm != nullptr);
525     auto *stringClass = Runtime::GetCurrent()->GetClassLinker()->GetExtension(ctx)->GetClassRoot(ClassRoot::STRING);
526     size_t size =
527         compressed ? coretypes::String::ComputeSizeMUtf8(length) : coretypes::String::ComputeSizeUtf16(length);
528     auto string = reinterpret_cast<EtsString *>(vm->GetHeapManager()->AllocateObject(
529         stringClass, size, DEFAULT_ALIGNMENT, nullptr, mem::ObjectAllocatorBase::ObjMemInitPolicy::NO_INIT));
530     if (string != nullptr) {
531         // After setting length we should have a full barrier, so this write should happens-before barrier
532         TSAN_ANNOTATE_IGNORE_WRITES_BEGIN();
533         auto len = ToNativePtr<uint32_t>(ToUintPtr(string) + coretypes::String::GetLengthOffset());
534         auto hashcode = ToNativePtr<uint32_t>(ToUintPtr(string) + coretypes::String::GetHashcodeOffset());
535         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
536         len[0] = compressed ? (length << 1U) : (length << 1U) | 1U;
537         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
538         hashcode[0] = 0U;
539         TSAN_ANNOTATE_IGNORE_WRITES_END();
540         // Witout full memory barrier it is possible that architectures with
541         // weak memory order can try fetching string legth before it's set
542         arch::FullMemoryBarrier();
543     }
544     return string;
545 }
546 
StdCoreStringRepeat(EtsString * str,EtsInt count)547 EtsString *StdCoreStringRepeat(EtsString *str, EtsInt count)
548 {
549     auto length = str->GetLength();
550 
551     if (UNLIKELY(count < 0)) {
552         PandaString message = "repeat: count is negative";
553         auto coroutine = EtsCoroutine::GetCurrent();
554         ThrowEtsException(coroutine, panda_file_items::class_descriptors::RANGE_ERROR, message);
555         return nullptr;
556     }
557 
558     if (length == 0 || count == 0) {
559         return EtsString::CreateFromUtf8(nullptr, 0);
560     }
561 
562     auto thread = ManagedThread::GetCurrent();
563     [[maybe_unused]] HandleScope<ObjectHeader *> scope(thread);
564     VMHandle<coretypes::String> sHandle(thread, str->GetCoreType());
565 
566     int size = length * count;
567     auto compressed = str->GetCoreType()->IsMUtf8();
568     auto rep = AllocateStringObject(size, compressed);
569     if (UNLIKELY(rep == nullptr)) {
570         PandaString message = "repeat: memory allocation failed";
571         auto coroutine = EtsCoroutine::GetCurrent();
572         ThrowEtsException(coroutine, panda_file_items::class_descriptors::OUT_OF_MEMORY_ERROR, message);
573         return nullptr;
574     }
575 
576     if (compressed) {
577         auto strData = sHandle.GetPtr()->GetDataMUtf8();
578         auto repData = rep->GetDataMUtf8();
579         for (int i = 0; i < count; ++i) {
580             // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
581             std::copy_n(strData, length, repData + i * length);
582         }
583     } else {
584         auto strData = sHandle.GetPtr()->GetDataUtf16();
585         auto repData = rep->GetDataUtf16();
586         for (int i = 0; i < count; ++i) {
587             // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
588             std::copy_n(strData, length, repData + i * length);
589         }
590     }
591     return rep;
592 }
593 
594 }  // namespace ark::ets::intrinsics
595