Skip to content

Commit b8993db

Browse files
mkustermannCommit Queue
authored andcommitted
[dart2wasm] Take advantage of slices of a json string being ASCII
Sometimes json strings contain escape characters. If they do, then a slower path function is triggered. This CL makes that slower part also take advantage of ASCII property (in a similar way as we do if the entire json string is ASCII). CoreLibraryReviewExempt: Changes only non user visible internals. Change-Id: Ib055a579778b5ba58a9680da1b34624b095af22f Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/410501 Reviewed-by: Lasse Nielsen <[email protected]> Commit-Queue: Martin Kustermann <[email protected]>
1 parent aab7621 commit b8993db

File tree

2 files changed

+73
-34
lines changed

2 files changed

+73
-34
lines changed

sdk/lib/_internal/wasm/lib/convert_patch.dart

Lines changed: 69 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -673,7 +673,7 @@ mixin _ChunkedJsonParser<T> on _ChunkedJsonParserState {
673673
*
674674
* The [start] positions is inclusive, [end] is exclusive.
675675
*/
676-
void addSliceToString(int start, int end);
676+
void addSliceToString(int start, int end, int bits);
677677

678678
/**
679679
* Adds a string slice to the string being built.
@@ -1251,7 +1251,7 @@ mixin _ChunkedJsonParser<T> on _ChunkedJsonParserState {
12511251
}
12521252
}
12531253
beginString();
1254-
if (start < end) addSliceToString(start, end);
1254+
if (start < end) addSliceToString(start, end, bits);
12551255
return chunkString(STR_PLAIN);
12561256
}
12571257

@@ -1298,17 +1298,19 @@ mixin _ChunkedJsonParser<T> on _ChunkedJsonParserState {
12981298
int parseStringToBuffer(int position) {
12991299
int end = chunkEnd;
13001300
int start = position;
1301+
int bits = 0;
13011302
while (true) {
13021303
if (position == end) {
13031304
if (position > start) {
1304-
addSliceToString(start, position);
1305+
addSliceToString(start, position, bits);
13051306
}
13061307
return chunkString(STR_PLAIN);
13071308
}
13081309

13091310
int char = 0;
13101311
do {
13111312
char = getChar(position);
1313+
bits |= char; // Includes final '"', but that never matters.
13121314
position++;
13131315
if (isUtf16Input && char > 0xFF) {
13141316
continue;
@@ -1327,7 +1329,7 @@ mixin _ChunkedJsonParser<T> on _ChunkedJsonParserState {
13271329
if (char == QUOTE) {
13281330
int quotePosition = position - 1;
13291331
if (quotePosition > start) {
1330-
addSliceToString(start, quotePosition);
1332+
addSliceToString(start, quotePosition, bits);
13311333
}
13321334
listener.handleString(endString());
13331335
return position;
@@ -1339,13 +1341,14 @@ mixin _ChunkedJsonParser<T> on _ChunkedJsonParserState {
13391341

13401342
// Handle escape.
13411343
if (position - 1 > start) {
1342-
addSliceToString(start, position - 1);
1344+
addSliceToString(start, position - 1, bits);
13431345
}
13441346

13451347
if (position == end) return chunkString(STR_ESCAPE);
13461348
position = parseStringEscape(position);
13471349
if (position == end) return position;
13481350
start = position;
1351+
bits = 0;
13491352
}
13501353
}
13511354

@@ -1656,7 +1659,7 @@ class _JsonOneByteStringParser extends _ChunkedJsonParserState
16561659
assert(stringBuffer.isEmpty);
16571660
}
16581661

1659-
void addSliceToString(int start, int end) {
1662+
void addSliceToString(int start, int end, int bits) {
16601663
addStringSliceToString(chunk.substringUnchecked(start, end));
16611664
}
16621665

@@ -1710,26 +1713,18 @@ class _JsonTwoByteStringParser extends _ChunkedJsonParserState
17101713
int length = end - start;
17111714
if (length == 0) return '';
17121715

1713-
final WasmArray<WasmI16> sourceArray = chunk.array;
1714-
const int asciiBits = 0x7f;
1715-
if (bits <= asciiBits) {
1716-
final result = OneByteString.withLength(length);
1717-
for (int i = 0; i < length; ++i) {
1718-
result.array.write(i, sourceArray.readUnsigned(start++));
1719-
}
1720-
return result;
1716+
final sourceArray = chunk.array;
1717+
if (bits <= 0xff) {
1718+
return _oneByteStringFromI16(sourceArray, start, length);
17211719
}
1722-
final result = TwoByteString.withLength(length);
1723-
result.array.copy(0, sourceArray, start, length);
1724-
return result;
1720+
return _twoByteStringFromI16(sourceArray, start, length);
17251721
}
17261722

17271723
String getStringWithHash(int start, int end, int bits, int stringHash) {
17281724
final sourceArray = chunk.array;
17291725
final length = end - start;
17301726

1731-
const asciiBits = 0x7f;
1732-
if (bits <= asciiBits) {
1727+
if (bits <= 0xff) {
17331728
return _internOneByteStringFromI16(
17341729
sourceArray,
17351730
stringHash,
@@ -1738,8 +1733,7 @@ class _JsonTwoByteStringParser extends _ChunkedJsonParserState
17381733
);
17391734
}
17401735

1741-
final result = TwoByteString.withLength(length);
1742-
result.array.copy(0, sourceArray, start, length);
1736+
final result = _twoByteStringFromI16(sourceArray, start, length);
17431737
assert(result.hashCode.toWasmI32() == stringHash.toWasmI32());
17441738
setIdentityHashField(result, stringHash);
17451739
return result;
@@ -1749,8 +1743,14 @@ class _JsonTwoByteStringParser extends _ChunkedJsonParserState
17491743
assert(stringBuffer.isEmpty);
17501744
}
17511745

1752-
void addSliceToString(int start, int end) {
1753-
addStringSliceToString(chunk.substringUnchecked(start, end));
1746+
void addSliceToString(int start, int end, int bits) {
1747+
final sourceArray = chunk.array;
1748+
final length = end - start;
1749+
addStringSliceToString(
1750+
bits <= 0xff
1751+
? _oneByteStringFromI16(sourceArray, start, length)
1752+
: _twoByteStringFromI16(sourceArray, start, length),
1753+
);
17541754
}
17551755

17561756
void addStringSliceToString(String string) {
@@ -1812,14 +1812,24 @@ OneByteString _internOneByteStringFromI8(
18121812
}
18131813
}
18141814

1815-
final result = OneByteString.withLength(length);
1816-
result.array.copy(0, array, offset, length);
1815+
final result = _oneByteStringFromI8(array, offset, length);
18171816
assert(result.hashCode.toWasmI32() == stringHash.toWasmI32());
18181817
setIdentityHashField(result, stringHash);
18191818
_oneByteStringInternCache[index] = result;
18201819
return result;
18211820
}
18221821

1822+
@pragma('wasm:prefer-inline')
1823+
OneByteString _oneByteStringFromI8(
1824+
WasmArray<WasmI8> array,
1825+
int start,
1826+
int length,
1827+
) {
1828+
final result = OneByteString.withLength(length);
1829+
result.array.copy(0, array, start, length);
1830+
return result;
1831+
}
1832+
18231833
OneByteString _internOneByteStringFromI16(
18241834
WasmArray<WasmI16> array,
18251835
int stringHash,
@@ -1844,16 +1854,35 @@ OneByteString _internOneByteStringFromI16(
18441854
}
18451855
}
18461856

1847-
final result = OneByteString.withLength(length);
1848-
for (int start = offset, i = 0; i < length; ++i, start++) {
1849-
result.array.write(i, array.readUnsigned(start));
1850-
}
1857+
final result = _oneByteStringFromI16(array, offset, length);
18511858
assert(result.hashCode.toWasmI32() == stringHash.toWasmI32());
18521859
setIdentityHashField(result, stringHash);
18531860
_oneByteStringInternCache[index] = result;
18541861
return result;
18551862
}
18561863

1864+
OneByteString _oneByteStringFromI16(
1865+
WasmArray<WasmI16> array,
1866+
int start,
1867+
int length,
1868+
) {
1869+
final result = OneByteString.withLength(length);
1870+
for (int i = 0; i < length; ++i) {
1871+
result.array.write(i, array.readUnsigned(start++));
1872+
}
1873+
return result;
1874+
}
1875+
1876+
TwoByteString _twoByteStringFromI16(
1877+
WasmArray<WasmI16> array,
1878+
int start,
1879+
int length,
1880+
) {
1881+
final result = TwoByteString.withLength(length);
1882+
result.array.copy(0, array, start, length);
1883+
return result;
1884+
}
1885+
18571886
@patch
18581887
class JsonDecoder {
18591888
@patch
@@ -1999,14 +2028,16 @@ class _JsonUtf8Parser extends _ChunkedJsonParserState
19992028
int getChar(int position) => chunk[position];
20002029

20012030
String getString(int start, int end, int bits) {
2031+
int length = end - start;
2032+
if (length == 0) return '';
2033+
20022034
const int maxAsciiChar = 0x7f;
20032035
if (bits <= maxAsciiChar) {
20042036
return createOneByteStringFromCharacters(chunk, start, end);
20052037
}
20062038
beginString();
2007-
if (start < end) addSliceToString(start, end);
2008-
String result = endString();
2009-
return result;
2039+
addSliceToString(start, end, bits);
2040+
return endString();
20102041
}
20112042

20122043
String getStringWithHash(int start, int end, int bits, int stringHash) {
@@ -2029,8 +2060,12 @@ class _JsonUtf8Parser extends _ChunkedJsonParserState
20292060
stringBuffer.clear();
20302061
}
20312062

2032-
void addSliceToString(int start, int end) {
2033-
addStringSliceToString(decoder.convertChunked(chunk, start, end));
2063+
void addSliceToString(int start, int end, int bits) {
2064+
addStringSliceToString(
2065+
!decoder.expectsContinuation && bits <= 0x7f
2066+
? _oneByteStringFromI8(chunk.data, start, end - start)
2067+
: decoder.convertChunked(chunk, start, end),
2068+
);
20342069
}
20352070

20362071
void addStringSliceToString(String string) {

sdk/lib/convert/utf.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -553,6 +553,10 @@ class _Utf8Decoder {
553553

554554
external String convertChunked(List<int> codeUnits, int start, int? maybeEnd);
555555

556+
/// Whether the decoder expects the continuation of unfinished multi-byte
557+
/// UTF8-encoding.
558+
bool get expectsContinuation => _state > afterBom;
559+
556560
/// Flushes this decoder as if closed.
557561
///
558562
/// This method throws if the input was partial and the decoder was

0 commit comments

Comments
 (0)