| // Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| import 'dart:_boxed_int'; |
| import 'dart:_error_utils'; |
| import 'dart:_internal' show EfficientLengthIterable, patch, unsafeCast; |
| import 'dart:_js_helper' as js; |
| import 'dart:_typed_data'; |
| import 'dart:_js_types'; |
| import 'dart:_list'; |
| import 'dart:_string'; |
| import 'dart:_wasm'; |
| import 'dart:js_interop'; |
| import 'dart:typed_data'; |
| |
| @pragma('wasm:initialize-at-startup') |
| const int _stringFromCharCodesSize = 512; |
| final _stringFromCharCodes = WasmArray<WasmI16>(_stringFromCharCodesSize); |
| |
| @patch |
| class String { |
| @patch |
| factory String.fromCharCodes( |
| Iterable<int> charCodes, [ |
| int start = 0, |
| int? end, |
| ]) { |
| RangeError.checkNotNegative(start, "start"); |
| if (end != null && end < start) { |
| throw RangeError.range(end, start, null, "end"); |
| } |
| if (charCodes is U8List) { |
| return _fromU8ListCharCodes(charCodes, start, end); |
| } |
| if (charCodes is Uint8List) { |
| return _fromUint8ListCharCodes(charCodes, start, end); |
| } |
| if (charCodes is U16List) { |
| return _fromU16ListCharCodes(charCodes, start, end); |
| } |
| if (charCodes is WasmListBase) { |
| final result = _fromWasmListBaseCharCodes( |
| unsafeCast<WasmListBase<int>>(charCodes), |
| start, |
| end, |
| ); |
| if (result != null) return result; |
| } |
| return _fromIterableCharCodes(charCodes, start, end); |
| } |
| |
| static String _fromU8ListCharCodes( |
| U8List charCodes, |
| int start, |
| int? optionalEnd, |
| ) { |
| final length = charCodes.length; |
| int end = optionalEnd != null |
| ? (optionalEnd < length ? optionalEnd : length) |
| : length; |
| if (end <= start) return ''; |
| final count = end - start; |
| |
| final int offset = charCodes.offsetInElements; |
| start += offset; |
| end += offset; |
| |
| final src = charCodes.data; |
| final dst = count < _stringFromCharCodesSize |
| ? _stringFromCharCodes |
| : WasmArray<WasmI16>(count); |
| for (int i = 0; i < count; ++i) { |
| dst.write(i, src.readUnsigned(start + i)); |
| } |
| return JSStringImpl.fromRefUnchecked( |
| jsStringFromCharCodeArray(dst, 0.toWasmI32(), count.toWasmI32()), |
| ); |
| } |
| |
| static String _fromUint8ListCharCodes( |
| Uint8List charCodes, |
| int start, |
| int? optionalEnd, |
| ) { |
| final length = charCodes.length; |
| final int end = optionalEnd != null |
| ? (optionalEnd < length ? optionalEnd : length) |
| : length; |
| if (end <= start) return ''; |
| final count = end - start; |
| |
| final dst = count < _stringFromCharCodesSize |
| ? _stringFromCharCodes |
| : WasmArray<WasmI16>(count); |
| for (int i = 0; i < count; ++i) { |
| dst.write(i, charCodes[start + i]); |
| } |
| return JSStringImpl.fromRefUnchecked( |
| jsStringFromCharCodeArray(dst, 0.toWasmI32(), count.toWasmI32()), |
| ); |
| } |
| |
| static String _fromU16ListCharCodes( |
| U16List charCodes, |
| int start, |
| int? optionalEnd, |
| ) { |
| final length = charCodes.length; |
| int end = optionalEnd != null |
| ? (optionalEnd < length ? optionalEnd : length) |
| : length; |
| if (end <= start) return ''; |
| final count = end - start; |
| |
| final int offset = charCodes.offsetInElements; |
| start += offset; |
| end += offset; |
| |
| final data = charCodes.data; |
| return JSStringImpl.fromRefUnchecked( |
| jsStringFromCharCodeArray(data, start.toWasmI32(), end.toWasmI32()), |
| ); |
| } |
| |
| static String? _fromWasmListBaseCharCodes( |
| WasmListBase<int> charCodes, |
| int start, |
| int? optionalEnd, |
| ) { |
| final length = charCodes.length; |
| final int end = optionalEnd != null |
| ? (optionalEnd < length ? optionalEnd : length) |
| : length; |
| if (end <= start) return ''; |
| final count = end - start; |
| |
| final src = charCodes.data; |
| final dst = count < _stringFromCharCodesSize |
| ? _stringFromCharCodes |
| : WasmArray<WasmI16>(count); |
| for (int i = 0; i < count; ++i) { |
| final charCode = unsafeCast<BoxedInt>(src[start + i]); |
| if (charCode.gtU(0xffff)) { |
| return null; // fall back to general case. |
| } |
| dst.write(i, charCode); |
| } |
| return JSStringImpl.fromRefUnchecked( |
| jsStringFromCharCodeArray(dst, 0.toWasmI32(), count.toWasmI32()), |
| ); |
| } |
| |
| static String _fromIterableCharCodes( |
| Iterable<int> charCodes, |
| int start, |
| int? end, |
| ) { |
| RangeError.checkNotNegative(start, "start"); |
| if (end != null) { |
| if (end < start) { |
| throw RangeError.range(end, start, null, "end"); |
| } |
| if (end == start) return ""; |
| } |
| |
| final length = charCodes.length; |
| |
| // Skip until `start`. |
| final it = charCodes.iterator; |
| for (int i = 0; i < start; i++) { |
| it.moveNext(); |
| } |
| |
| // The part of the iterable converted to string is collected in a JS typed |
| // array, to be able to effciently get subarrays, to pass to |
| // `String.fromCharCode.apply`. |
| final charCodesLength = (end ?? length) - start; |
| if (charCodesLength <= 0) return ""; |
| final typedArrayLength = charCodesLength * 2; |
| final WasmArray<WasmI16> list = WasmArray(typedArrayLength); |
| int index = 0; // index in `list`. |
| end ??= start + charCodesLength; |
| for (int i = start; i < end; i++) { |
| if (!it.moveNext()) { |
| break; |
| } |
| final charCode = it.current; |
| if (charCode.leU(0xffff)) { |
| list.write(index++, charCode); |
| } else if (charCode.leU(0x10ffff)) { |
| list.write(index++, 0xd800 + ((((charCode - 0x10000) >> 10) & 0x3ff))); |
| list.write(index++, 0xdc00 + (charCode & 0x3ff)); |
| } else { |
| throw RangeError.range(charCode, 0, 0x10ffff); |
| } |
| } |
| |
| return JSStringImpl.fromRefUnchecked( |
| jsStringFromCharCodeArray(list, const WasmI32(0), WasmI32.fromInt(index)), |
| ); |
| } |
| |
| @patch |
| @pragma("wasm:prefer-inline") |
| factory String.fromCharCode(int charCode) { |
| RangeErrorUtils.checkValueBetweenZeroAndPositiveMax(charCode, 0x10ffff); |
| return JSStringImpl.fromCodePoint(charCode); |
| } |
| } |