blob: 2b1c741678f32be1bc89e48d0f1d0dee4281dc9e [file] [edit]
// 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);
}
}