[js_runtime] Utf8 - reuse buffer to avoid creating many small buffers

Allocating typed data on Chrome is surprisingly expensive
(e.g. https://bugs.chromium.org/p/v8/issues/detail?id=9199).  This
makes it worthwhile to keep a buffer around for reuse rather than
allocating a new one for each conversion that requires copying.

Change-Id: I8b2823f487cd8b869fc8b22f309490475c7c010d
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/337462
Reviewed-by: Nicholas Shahan <nshahan@google.com>
Commit-Queue: Stephen Adams <sra@google.com>
This commit is contained in:
Stephen Adams 2023-11-21 22:40:10 +00:00 committed by Commit Queue
parent 69666dc283
commit 3f2432f36c

View file

@ -55,10 +55,8 @@ class _Utf8Decoder {
bytes = casted;
errorOffset = 0;
} else {
// TODO(sra): Use a persistent buffer to avoid allocations for shorter
// inputs, perhaps up to several kB.
bytes = JS<NativeUint8List>(
'NativeUint8List', '#', _makeUint8List(codeUnits, start, end));
'NativeUint8List', '#', _makeNativeUint8List(codeUnits, start, end));
errorOffset = start;
end -= start;
start = 0;
@ -113,6 +111,29 @@ class _Utf8Decoder {
return decodeGeneral(bytes, start, end, single);
}
static Uint8List _makeNativeUint8List(
List<int> codeUnits, int start, int end) {
final int length = end - start;
// Re-use a dedicated buffer to avoid allocating small buffers as this is
// unreasonably expensive on some JavaScript engines.
final Uint8List bytes =
length <= _reusableBufferSize ? _reusableBuffer : Uint8List(length);
for (int i = 0; i < length; i++) {
int b = codeUnits[start + i];
if ((b & 0xFF) != b) {
// Replace invalid byte values by 0xFF, which is a valid byte value that
// is an invalid UTF8 sequence.
b = 0xFF;
}
JS('', '#[#] = #', bytes, i, b); // bytes[i] = b;
}
return bytes;
}
static const _reusableBufferSize = 4096;
static final Uint8List _reusableBuffer = Uint8List(_reusableBufferSize);
static String? _convertInterceptedUint8List(
bool allowMalformed, NativeUint8List codeUnits, int start, int end) {
final decoder = allowMalformed ? _decoderNonfatal : _decoder;