From df80cf91404e8e3b0f0a4eb271467448d126199e Mon Sep 17 00:00:00 2001 From: Vyacheslav Egorov Date: Thu, 21 Mar 2024 11:10:10 +0000 Subject: [PATCH] [core] Improve JSON decoding performance Avoid polymorphic character access by turning _ChunkedJsonParser into a mixin instead of the base class. TEST=ci Change-Id: Id2080724e07d16e96734a80629c8bd8906dc590b Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/358445 Reviewed-by: Daco Harkes Commit-Queue: Slava Egorov --- sdk/lib/_internal/vm/lib/convert_patch.dart | 47 +++++++++++++++------ 1 file changed, 33 insertions(+), 14 deletions(-) diff --git a/sdk/lib/_internal/vm/lib/convert_patch.dart b/sdk/lib/_internal/vm/lib/convert_patch.dart index 9a9ee1ed6bd..a5e0186b41f 100644 --- a/sdk/lib/_internal/vm/lib/convert_patch.dart +++ b/sdk/lib/_internal/vm/lib/convert_patch.dart @@ -59,9 +59,7 @@ class _JsonUtf8Decoder extends Converter, Object?> { Object? convert(List input) { var parser = _JsonUtf8DecoderSink._createParser(_reviver, _allowMalformed); - parser.chunk = input; - parser.chunkEnd = input.length; - parser.parse(0); + parser.parseChunk(input, 0, input.length); parser.close(); return parser.result; } @@ -249,6 +247,11 @@ class _NumberBuffer { double parseDouble() => double.parse(getString()); } +abstract class _JsonParserWithListener { + final _JsonListener listener; + _JsonParserWithListener(this.listener); +} + /** * Chunked JSON parser. * @@ -256,8 +259,13 @@ class _NumberBuffer { * and stores input state between chunks. * * Implementations include [String] and UTF-8 parsers. + * + * Note: this is a mixin instead of the base class to allow compilers + * to specialize applications otherwise accessing chunk characters becomes + * polymorphic. + * */ -abstract class _ChunkedJsonParser { +mixin _ChunkedJsonParser on _JsonParserWithListener { // A simple non-recursive state-based parser for JSON. // // Literal values accepted in states ARRAY_EMPTY, ARRAY_COMMA, OBJECT_COLON @@ -388,8 +396,6 @@ abstract class _ChunkedJsonParser { // Mask used to mask off two lower bits. static const int TWO_BIT_MASK = 3; - final _JsonListener listener; - // The current parsing state. int state = STATE_INITIAL; List states = []; @@ -440,8 +446,6 @@ abstract class _ChunkedJsonParser { */ dynamic buffer = null; - _ChunkedJsonParser(this.listener); - /** * Push the current parse [state] on a stack. * @@ -1380,7 +1384,8 @@ abstract class _ChunkedJsonParser { /** * Chunked JSON parser that parses [String] chunks. */ -class _JsonStringParser extends _ChunkedJsonParser { +class _JsonStringParser extends _JsonParserWithListener + with _ChunkedJsonParser { String chunk = ''; int chunkEnd = 0; @@ -1477,11 +1482,12 @@ class _JsonStringDecoderSink extends StringConversionSinkBase { /** * Chunked JSON parser that parses UTF-8 chunks. */ -class _JsonUtf8Parser extends _ChunkedJsonParser> { +class _JsonUtf8Parser extends _JsonParserWithListener + with _ChunkedJsonParser { static final Uint8List emptyChunk = Uint8List(0); final _Utf8Decoder decoder; - List chunk = emptyChunk; + Uint8List chunk = emptyChunk; int chunkEnd = 0; _JsonUtf8Parser(_JsonListener listener, bool allowMalformed) @@ -1492,6 +1498,21 @@ class _JsonUtf8Parser extends _ChunkedJsonParser> { _ChunkedJsonParser.PARTIAL_KEYWORD | _ChunkedJsonParser.KWD_BOM; } + void parseChunk(List value, int start, int end) { + if (value is Uint8List) { + chunk = value; + } else { + final bytes = Uint8List(end - start); + bytes.setRange(0, bytes.length, value, start); + end = bytes.length; + start = 0; + chunk = bytes; + } + chunkEnd = end; + parse(start); + } + + @pragma('vm:prefer-inline') int getChar(int position) => chunk[position]; String getString(int start, int end, int bits) { @@ -1569,9 +1590,7 @@ class _JsonUtf8DecoderSink extends ByteConversionSink { } void _addChunk(List chunk, int start, int end) { - _parser.chunk = chunk; - _parser.chunkEnd = end; - _parser.parse(start); + _parser.parseChunk(chunk, start, end); } void close() {