@@ -17,7 +17,8 @@ import "dart:_internal"
17
17
POWERS_OF_TEN,
18
18
unsafeCast,
19
19
writeIntoOneByteString,
20
- writeIntoTwoByteString;
20
+ writeIntoTwoByteString,
21
+ createOneByteStringFromCharacters;
21
22
22
23
import "dart:typed_data" show Uint8List, Uint16List;
23
24
@@ -114,7 +115,7 @@ class _JsonListener {
114
115
void popContainer () {
115
116
value = currentContainer;
116
117
currentContainer = stack.removeLast ();
117
- if (currentContainer is Map ) key = stack.removeLast () as String ;
118
+ if (currentContainer is Map ) key = unsafeCast < String >( stack.removeLast ()) ;
118
119
}
119
120
120
121
void handleString (String value) {
@@ -139,12 +140,12 @@ class _JsonListener {
139
140
}
140
141
141
142
void propertyName () {
142
- key = value as String ;
143
+ key = unsafeCast < String >(value) ;
143
144
value = null ;
144
145
}
145
146
146
147
void propertyValue () {
147
- var map = currentContainer as Map ;
148
+ var map = unsafeCast < Map >(currentContainer) ;
148
149
var reviver = this .reviver;
149
150
if (reviver != null ) {
150
151
value = reviver (key, value);
@@ -164,7 +165,7 @@ class _JsonListener {
164
165
}
165
166
166
167
void arrayElement () {
167
- var list = currentContainer as List ;
168
+ var list = unsafeCast < List >(currentContainer) ;
168
169
var reviver = this .reviver;
169
170
if (reviver != null ) {
170
171
value = reviver (list.length, value);
@@ -535,6 +536,13 @@ mixin _ChunkedJsonParser<T> on _JsonParserWithListener {
535
536
*/
536
537
int getChar (int index);
537
538
539
+ /**
540
+ * Returns [true] if [getChar] is returning UTF16 code units.
541
+ *
542
+ * Otherwise it is expected that [getChar] is returning UTF8 bytes.
543
+ */
544
+ bool get isUtf16Input;
545
+
538
546
/**
539
547
* Copy ASCII characters from start to end of chunk into a list.
540
548
*
@@ -813,22 +821,35 @@ mixin _ChunkedJsonParser<T> on _JsonParserWithListener {
813
821
* Starts parsing at [position] and continues until [chunkEnd] .
814
822
* Continues parsing where the previous chunk (if any) ended.
815
823
*/
824
+ @pragma ('vm:unsafe:no-interrupts' )
825
+ @pragma ('vm:unsafe:no-bounds-checks' )
816
826
void parse (int position) {
817
827
int length = chunkEnd;
818
828
if (partialState != NO_PARTIAL ) {
819
829
position = parsePartial (position);
820
830
if (position == length) return ;
821
831
}
832
+ final charAttributes = _characterAttributes;
833
+
822
834
int state = this .state;
835
+ outer:
823
836
while (position < length) {
824
- int char = getChar (position);
825
- switch (char) {
826
- case SPACE :
827
- case CARRIAGE_RETURN :
828
- case NEWLINE :
829
- case TAB :
830
- position++ ;
837
+ int char = 0 ;
838
+ do {
839
+ char = getChar (position);
840
+ if (isUtf16Input && char > 0xFF ) {
831
841
break ;
842
+ }
843
+ if ((charAttributes.codeUnitAt (char) & CHAR_WHITESPACE ) == 0 ) {
844
+ break ;
845
+ }
846
+ position++ ;
847
+ if (position >= length) {
848
+ break outer;
849
+ }
850
+ } while (true );
851
+
852
+ switch (char) {
832
853
case QUOTE :
833
854
if ((state & ALLOW_STRING_MASK ) != 0 ) fail (position);
834
855
state | = VALUE_READ_BITS ;
@@ -988,35 +1009,80 @@ mixin _ChunkedJsonParser<T> on _JsonParserWithListener {
988
1009
return length;
989
1010
}
990
1011
1012
+ static const int CHAR_SIMPLE_STRING_END = 1 ;
1013
+ static const int CHAR_WHITESPACE = 2 ;
1014
+
1015
+ /**
1016
+ * [_characterAttributes] string was generated using the following code:
1017
+ *
1018
+ * ```
1019
+ * int $(String ch) => ch.codeUnitAt(0);
1020
+ * final list = Uint8List(256);
1021
+ * for (var i = 0; i < $(' '); i++) {
1022
+ * list[i] |= CHAR_SIMPLE_STRING_END;
1023
+ * }
1024
+ * list[$('"')] |= CHAR_SIMPLE_STRING_END;
1025
+ * list[$('\\')] |= CHAR_SIMPLE_STRING_END;
1026
+ * list[$(' ')] |= CHAR_WHITESPACE;
1027
+ * list[$('\r')] |= CHAR_WHITESPACE;
1028
+ * list[$('\n')] |= CHAR_WHITESPACE;
1029
+ * list[$('\t')] |= CHAR_WHITESPACE;
1030
+ * for (var i = 0; i < 256; i += 64) {
1031
+ * print("'${String.fromCharCodes([
1032
+ * for (var v in list.skip(i).take(64)) v + $(' '),
1033
+ * ])}'");
1034
+ * }
1035
+ * ```
1036
+ */
1037
+ static const String _characterAttributes =
1038
+ '!!!!!!!!!##!!#!!!!!!!!!!!!!!!!!!" ! '
1039
+ ' ! '
1040
+ ' '
1041
+ ' ' ;
1042
+
991
1043
/**
992
1044
* Parses a string value.
993
1045
*
994
1046
* Initial [position] is right after the initial quote.
995
1047
* Returned position right after the final quote.
996
1048
*/
1049
+ @pragma ('vm:unsafe:no-interrupts' )
1050
+ @pragma ('vm:unsafe:no-bounds-checks' )
997
1051
int parseString (int position) {
1052
+ final charAttributes = _characterAttributes;
1053
+
998
1054
// Format: '"'([^\x00-\x1f\\\"]|'\\'[bfnrt/\\"])*'"'
999
1055
// Initial position is right after first '"'.
1000
1056
int start = position;
1001
1057
int end = chunkEnd;
1002
1058
int bits = 0 ;
1003
- while (position < end) {
1004
- int char = getChar (position++ );
1005
- bits | = char; // Includes final '"', but that never matters.
1006
- // BACKSLASH is larger than QUOTE and SPACE.
1007
- if (char > BACKSLASH ) {
1008
- continue ;
1059
+ int char = 0 ;
1060
+ if (position < end) {
1061
+ do {
1062
+ // Caveat: do not combine the following two lines together. It helps
1063
+ // compiler to generate better code (it currently can't reorder operations
1064
+ // to reduce register pressure).
1065
+ char = getChar (position);
1066
+ position++ ;
1067
+ bits | = char; // Includes final '"', but that never matters.
1068
+ if (isUtf16Input && char > 0xFF ) {
1069
+ continue ;
1070
+ }
1071
+ if ((charAttributes.codeUnitAt (char) & CHAR_SIMPLE_STRING_END ) != 0 ) {
1072
+ break ;
1073
+ }
1074
+ } while (position < end);
1075
+ if (char == QUOTE ) {
1076
+ int sliceEnd = position - 1 ;
1077
+ listener.handleString (getString (start, sliceEnd, bits));
1078
+ return sliceEnd + 1 ;
1009
1079
}
1010
1080
if (char == BACKSLASH ) {
1011
- beginString ();
1012
1081
int sliceEnd = position - 1 ;
1082
+ beginString ();
1013
1083
if (start < sliceEnd) addSliceToString (start, sliceEnd);
1014
1084
return parseStringToBuffer (sliceEnd);
1015
1085
}
1016
- if (char == QUOTE ) {
1017
- listener.handleString (getString (start, position - 1 , bits));
1018
- return position;
1019
- }
1020
1086
if (char < SPACE ) {
1021
1087
fail (position - 1 , "Control character in string" );
1022
1088
}
@@ -1065,7 +1131,11 @@ mixin _ChunkedJsonParser<T> on _JsonParserWithListener {
1065
1131
* This function scans through the string literal for escapes, and copies
1066
1132
* slices of non-escape characters using [addSliceToString] .
1067
1133
*/
1134
+ @pragma ('vm:unsafe:no-interrupts' )
1135
+ @pragma ('vm:unsafe:no-bounds-checks' )
1068
1136
int parseStringToBuffer (int position) {
1137
+ final charAttributes = _characterAttributes;
1138
+
1069
1139
int end = chunkEnd;
1070
1140
int start = position;
1071
1141
while (true ) {
@@ -1075,11 +1145,23 @@ mixin _ChunkedJsonParser<T> on _JsonParserWithListener {
1075
1145
}
1076
1146
return chunkString (STR_PLAIN );
1077
1147
}
1078
- int char = getChar (position++ );
1079
- if (char > BACKSLASH ) continue ;
1148
+
1149
+ int char = 0 ;
1150
+ do {
1151
+ char = getChar (position);
1152
+ position++ ;
1153
+ if (isUtf16Input && char > 0xFF ) {
1154
+ continue ;
1155
+ }
1156
+ if ((charAttributes.codeUnitAt (char) & CHAR_SIMPLE_STRING_END ) != 0 ) {
1157
+ break ;
1158
+ }
1159
+ } while (position < end);
1160
+
1080
1161
if (char < SPACE ) {
1081
1162
fail (position - 1 ); // Control character in string.
1082
1163
}
1164
+
1083
1165
if (char == QUOTE ) {
1084
1166
int quotePosition = position - 1 ;
1085
1167
if (quotePosition > start) {
@@ -1088,13 +1170,16 @@ mixin _ChunkedJsonParser<T> on _JsonParserWithListener {
1088
1170
listener.handleString (endString ());
1089
1171
return position;
1090
1172
}
1173
+
1091
1174
if (char != BACKSLASH ) {
1092
1175
continue ;
1093
1176
}
1177
+
1094
1178
// Handle escape.
1095
1179
if (position - 1 > start) {
1096
1180
addSliceToString (start, position - 1 );
1097
1181
}
1182
+
1098
1183
if (position == end) return chunkString (STR_ESCAPE );
1099
1184
position = parseStringEscape (position);
1100
1185
if (position == end) return position;
@@ -1391,6 +1476,10 @@ class _JsonStringParser extends _JsonParserWithListener
1391
1476
1392
1477
_JsonStringParser (_JsonListener listener) : super (listener);
1393
1478
1479
+ @pragma ('vm:prefer-inline' )
1480
+ bool get isUtf16Input => true ;
1481
+
1482
+ @pragma ('vm:prefer-inline' )
1394
1483
int getChar (int position) => chunk.codeUnitAt (position);
1395
1484
1396
1485
String getString (int start, int end, int bits) {
@@ -1512,13 +1601,16 @@ class _JsonUtf8Parser extends _JsonParserWithListener
1512
1601
parse (start);
1513
1602
}
1514
1603
1604
+ @pragma ('vm:prefer-inline' )
1605
+ bool get isUtf16Input => false ;
1606
+
1515
1607
@pragma ('vm:prefer-inline' )
1516
1608
int getChar (int position) => chunk[position];
1517
1609
1518
1610
String getString (int start, int end, int bits) {
1519
1611
const int maxAsciiChar = 0x7f ;
1520
1612
if (bits <= maxAsciiChar) {
1521
- return new String . fromCharCodes (chunk, start, end);
1613
+ return createOneByteStringFromCharacters (chunk, start, end);
1522
1614
}
1523
1615
beginString ();
1524
1616
if (start < end) addSliceToString (start, end);
0 commit comments