@@ -52,6 +52,14 @@ export type JSONValue =
52
52
| { + [ key : string ] : JSONValue }
53
53
| $ReadOnlyArray < JSONValue > ;
54
54
55
+ const ROW_ID = 0 ;
56
+ const ROW_TAG = 1 ;
57
+ const ROW_LENGTH = 2 ;
58
+ const ROW_CHUNK_BY_NEWLINE = 3 ;
59
+ const ROW_CHUNK_BY_LENGTH = 4 ;
60
+
61
+ type RowParserState = 0 | 1 | 2 | 3 | 4 ;
62
+
55
63
const PENDING = 'pending' ;
56
64
const BLOCKED = 'blocked' ;
57
65
const RESOLVED_MODEL = 'resolved_model' ;
@@ -165,9 +173,13 @@ export type Response = {
165
173
_bundlerConfig : SSRManifest ,
166
174
_callServer : CallServerCallback ,
167
175
_chunks : Map < number , SomeChunk< any >> ,
168
- _partialRow : string ,
169
176
_fromJSON : ( key : string , value : JSONValue ) => any ,
170
177
_stringDecoder : StringDecoder ,
178
+ _rowState : RowParserState ,
179
+ _rowID : number , // parts of a row ID parsed so far
180
+ _rowTag : number , // 0 indicates that we're currently parsing the row ID
181
+ _rowLength : number , // remaining bytes in the row. 0 indicates that we're looking for a newline.
182
+ _buffer : Array < Uint8Array > , // chunks received so far as part of this row
171
183
} ;
172
184
173
185
function readChunk < T > (chunk: SomeChunk< T > ): T {
@@ -276,6 +288,14 @@ function createResolvedModuleChunk<T>(
276
288
return new Chunk ( RESOLVED_MODULE , value , null , response ) ;
277
289
}
278
290
291
+ function createInitializedTextChunk(
292
+ response: Response,
293
+ value: string,
294
+ ): InitializedChunk< string > {
295
+ // $FlowFixMe[invalid-constructor] Flow doesn't support functions as constructors
296
+ return new Chunk ( INITIALIZED , value , null , response ) ;
297
+ }
298
+
279
299
function resolveModelChunk< T > (
280
300
chunk: SomeChunk< T > ,
281
301
value: UninitializedModel,
@@ -665,9 +685,13 @@ export function createResponse(
665
685
_bundlerConfig : bundlerConfig ,
666
686
_callServer : callServer !== undefined ? callServer : missingCall ,
667
687
_chunks : chunks ,
668
- _partialRow : '' ,
669
688
_stringDecoder : createStringDecoder ( ) ,
670
689
_fromJSON : ( null : any ) ,
690
+ _rowState : 0 ,
691
+ _rowID : 0 ,
692
+ _rowTag : 0 ,
693
+ _rowLength : 0 ,
694
+ _buffer : [ ] ,
671
695
} ;
672
696
// Don't inline this call because it causes closure to outline the call above.
673
697
response . _fromJSON = createFromJSONCallback ( response ) ;
@@ -688,6 +712,13 @@ function resolveModel(
688
712
}
689
713
}
690
714
715
+ function resolveText ( response : Response , id : number , text : string ) : void {
716
+ const chunks = response . _chunks ;
717
+ // We assume that we always reference large strings after they've been
718
+ // emitted.
719
+ chunks . set ( id , createInitializedTextChunk ( response , text ) ) ;
720
+ }
721
+
691
722
function resolveModule (
692
723
response : Response ,
693
724
id : number ,
@@ -802,33 +833,40 @@ function resolveHint(
802
833
code : string ,
803
834
model : UninitializedModel ,
804
835
) : void {
805
- const hintModel = parseModel < HintModel > ( response , model ) ;
836
+ const hintModel : HintModel = parseModel ( response , model ) ;
806
837
dispatchHint ( code , hintModel ) ;
807
838
}
808
839
809
- function processFullRow ( response : Response , row : string ) : void {
810
- if ( row === '' ) {
811
- return ;
840
+ function processFullRow (
841
+ response : Response ,
842
+ id : number ,
843
+ tag : number ,
844
+ buffer : Array < Uint8Array > ,
845
+ lastChunk : string | Uint8Array ,
846
+ ) : void {
847
+ let row = '';
848
+ const stringDecoder = response . _stringDecoder ;
849
+ for ( let i = 0 ; i < buffer . length ; i ++ ) {
850
+ const chunk = buffer [ i ] ;
851
+ row += readPartialStringChunk ( stringDecoder , chunk ) ;
852
+ }
853
+ if ( typeof lastChunk === 'string' ) {
854
+ row += lastChunk ;
855
+ } else {
856
+ row += readFinalStringChunk ( stringDecoder , lastChunk ) ;
812
857
}
813
- const colon = row . indexOf ( ':' , 0 ) ;
814
- const id = parseInt ( row . slice ( 0 , colon ) , 16 ) ;
815
- const tag = row [ colon + 1 ] ;
816
- // When tags that are not text are added, check them here before
817
- // parsing the row as text.
818
- // switch (tag) {
819
- // }
820
858
switch ( tag ) {
821
- case 'I' : {
822
- resolveModule ( response , id , row . slice ( colon + 2 ) ) ;
859
+ case 73 /* "I" */ : {
860
+ resolveModule ( response , id , row ) ;
823
861
return ;
824
862
}
825
- case 'H' : {
826
- const code = row [ colon + 2 ] ;
827
- resolveHint ( response , code , row . slice ( colon + 3 ) ) ;
863
+ case 72 /* "H" */ : {
864
+ const code = row [ 0 ] ;
865
+ resolveHint ( response , code , row . slice ( 1 ) ) ;
828
866
return ;
829
867
}
830
- case 'E' : {
831
- const errorInfo = JSON . parse ( row . slice ( colon + 2 ) ) ;
868
+ case 69 /* "E" */ : {
869
+ const errorInfo = JSON . parse ( row ) ;
832
870
if ( __DEV__ ) {
833
871
resolveErrorDev (
834
872
response ,
@@ -842,9 +880,13 @@ function processFullRow(response: Response, row: string): void {
842
880
}
843
881
return ;
844
882
}
883
+ case 84 /* "T" */ : {
884
+ resolveText ( response , id , row ) ;
885
+ return ;
886
+ }
845
887
default : {
846
888
// We assume anything else is JSON.
847
- resolveModel ( response , id , row . slice ( colon + 1 ) ) ;
889
+ resolveModel ( response , id , row ) ;
848
890
return ;
849
891
}
850
892
}
@@ -854,18 +896,96 @@ export function processBinaryChunk(
854
896
response : Response ,
855
897
chunk : Uint8Array ,
856
898
) : void {
857
- const stringDecoder = response . _stringDecoder ;
858
- let linebreak = chunk . indexOf ( 10 ) ; // newline
859
- while ( linebreak > - 1 ) {
860
- const fullrow =
861
- response . _partialRow +
862
- readFinalStringChunk ( stringDecoder , chunk . subarray ( 0 , linebreak ) ) ;
863
- processFullRow ( response , fullrow ) ;
864
- response . _partialRow = '' ;
865
- chunk = chunk . subarray ( linebreak + 1 ) ;
866
- linebreak = chunk . indexOf ( 10 ) ; // newline
899
+ let i = 0 ;
900
+ let rowState = response . _rowState ;
901
+ let rowID = response . _rowID ;
902
+ let rowTag = response . _rowTag ;
903
+ let rowLength = response . _rowLength ;
904
+ const buffer = response . _buffer ;
905
+ const chunkLength = chunk . length ;
906
+ while ( i < chunkLength ) {
907
+ let lastIdx = - 1 ;
908
+ switch ( rowState ) {
909
+ case ROW_ID : {
910
+ const byte = chunk [ i ++ ] ;
911
+ if ( byte === 58 /* ":" */ ) {
912
+ // Finished the rowID, next we'll parse the tag.
913
+ rowState = ROW_TAG ;
914
+ } else {
915
+ rowID = ( rowID << 4 ) | ( byte > 96 ? byte - 87 : byte - 48 ) ;
916
+ }
917
+ continue ;
918
+ }
919
+ case ROW_TAG : {
920
+ const resolvedRowTag = chunk [ i ] ;
921
+ if ( resolvedRowTag === 84 /* "T" */ ) {
922
+ rowTag = resolvedRowTag ;
923
+ rowState = ROW_LENGTH ;
924
+ i ++ ;
925
+ } else if ( resolvedRowTag > 64 && resolvedRowTag < 91 /* "A"-"Z" */ ) {
926
+ rowTag = resolvedRowTag ;
927
+ rowState = ROW_CHUNK_BY_NEWLINE ;
928
+ i ++ ;
929
+ } else {
930
+ rowTag = 0 ;
931
+ rowState = ROW_CHUNK_BY_NEWLINE ;
932
+ // This was an unknown tag so it was probably part of the data.
933
+ }
934
+ continue ;
935
+ }
936
+ case ROW_LENGTH : {
937
+ const byte = chunk [ i ++ ] ;
938
+ if ( byte === 44 /* "," */ ) {
939
+ // Finished the rowLength, next we'll buffer up to that length.
940
+ rowState = ROW_CHUNK_BY_LENGTH ;
941
+ } else {
942
+ rowLength = ( rowLength << 4 ) | ( byte > 96 ? byte - 87 : byte - 48 ) ;
943
+ }
944
+ continue ;
945
+ }
946
+ case ROW_CHUNK_BY_NEWLINE : {
947
+ // We're looking for a newline
948
+ lastIdx = chunk . indexOf ( 10 /* "\n" */ , i ) ;
949
+ break ;
950
+ }
951
+ case ROW_CHUNK_BY_LENGTH : {
952
+ // We're looking for the remaining byte length
953
+ if ( i + rowLength <= chunk . length ) {
954
+ lastIdx = i + rowLength ;
955
+ }
956
+ break ;
957
+ }
958
+ }
959
+ if ( lastIdx > - 1 ) {
960
+ // We found the last chunk of the row
961
+ const offset = chunk . byteOffset + i ;
962
+ const length = lastIdx - i ;
963
+ const lastChunk = new Uint8Array ( chunk . buffer , offset , length ) ;
964
+ processFullRow ( response , rowID , rowTag , buffer , lastChunk ) ;
965
+ // Reset state machine for a new row
966
+ rowState = ROW_ID ;
967
+ rowTag = 0 ;
968
+ rowID = 0 ;
969
+ rowLength = 0 ;
970
+ buffer . length = 0 ;
971
+ i = lastIdx + 1 ;
972
+ } else {
973
+ // The rest of this row is in a future chunk. We stash the rest of the
974
+ // current chunk until we can process the full row.
975
+ const offset = chunk . byteOffset + i ;
976
+ const length = chunk . byteLength - i ;
977
+ const remainingSlice = new Uint8Array ( chunk . buffer , offset , length ) ;
978
+ buffer . push ( remainingSlice ) ;
979
+ // Update how many bytes we're still waiting for. If we're looking for
980
+ // a newline, this doesn't hurt since we'll just ignore it.
981
+ rowLength -= remainingSlice . byteLength ;
982
+ break ;
983
+ }
867
984
}
868
- response . _partialRow += readPartialStringChunk ( stringDecoder , chunk ) ;
985
+ response . _rowState = rowState ;
986
+ response . _rowID = rowID ;
987
+ response . _rowTag = rowTag ;
988
+ response . _rowLength = rowLength ;
869
989
}
870
990
871
991
function parseModel < T > (response: Response, json: UninitializedModel): T {
0 commit comments