@@ -21,6 +21,8 @@ import type {HintModel} from 'react-server/src/ReactFlightServerConfig';
21
21
22
22
import type { CallServerCallback } from './ReactFlightReplyClient' ;
23
23
24
+ import { enableBinaryFlight } from 'shared/ReactFeatureFlags' ;
25
+
24
26
import {
25
27
resolveClientReference ,
26
28
preloadModule ,
@@ -296,6 +298,14 @@ function createInitializedTextChunk(
296
298
return new Chunk ( INITIALIZED , value , null , response ) ;
297
299
}
298
300
301
+ function createInitializedBufferChunk(
302
+ response: Response,
303
+ value: $ArrayBufferView | ArrayBuffer,
304
+ ): InitializedChunk< Uint8Array > {
305
+ // $FlowFixMe[invalid-constructor] Flow doesn't support functions as constructors
306
+ return new Chunk ( INITIALIZED , value , null , response ) ;
307
+ }
308
+
299
309
function resolveModelChunk< T > (
300
310
chunk: SomeChunk< T > ,
301
311
value: UninitializedModel,
@@ -719,6 +729,16 @@ function resolveText(response: Response, id: number, text: string): void {
719
729
chunks . set ( id , createInitializedTextChunk ( response , text ) ) ;
720
730
}
721
731
732
+ function resolveBuffer (
733
+ response : Response ,
734
+ id : number ,
735
+ buffer : $ArrayBufferView | ArrayBuffer ,
736
+ ) : void {
737
+ const chunks = response . _chunks ;
738
+ // We assume that we always reference buffers after they've been emitted.
739
+ chunks . set ( id , createInitializedBufferChunk ( response , buffer ) ) ;
740
+ }
741
+
722
742
function resolveModule (
723
743
response : Response ,
724
744
id : number ,
@@ -837,24 +857,120 @@ function resolveHint(
837
857
dispatchHint ( code , hintModel ) ;
838
858
}
839
859
860
+ function mergeBuffer (
861
+ buffer : Array < Uint8Array > ,
862
+ lastChunk : Uint8Array ,
863
+ ) : Uint8Array {
864
+ const l = buffer . length ;
865
+ // Count the bytes we'll need
866
+ let byteLength = lastChunk . length ;
867
+ for ( let i = 0 ; i < l ; i ++ ) {
868
+ byteLength += buffer [ i ] . byteLength ;
869
+ }
870
+ // Allocate enough contiguous space
871
+ const result = new Uint8Array ( byteLength ) ;
872
+ let offset = 0 ;
873
+ // Copy all the buffers into it.
874
+ for ( let i = 0 ; i < l ; i ++ ) {
875
+ const chunk = buffer [ i ] ;
876
+ result . set ( chunk , offset ) ;
877
+ offset += chunk . byteLength ;
878
+ }
879
+ result . set ( lastChunk , offset ) ;
880
+ return result ;
881
+ }
882
+
883
+ function resolveTypedArray (
884
+ response : Response ,
885
+ id : number ,
886
+ buffer : Array < Uint8Array > ,
887
+ lastChunk : Uint8Array ,
888
+ constructor : any ,
889
+ bytesPerElement : number ,
890
+ ) : void {
891
+ // If the view fits into one original buffer, we just reuse that buffer instead of
892
+ // copying it out to a separate copy. This means that it's not always possible to
893
+ // transfer these values to other threads without copying first since they may
894
+ // share array buffer. For this to work, it must also have bytes aligned to a
895
+ // multiple of a size of the type.
896
+ const chunk =
897
+ buffer . length === 0 && lastChunk . byteOffset % bytesPerElement === 0
898
+ ? lastChunk
899
+ : mergeBuffer ( buffer , lastChunk ) ;
900
+ // TODO: The transfer protocol of RSC is little-endian. If the client isn't little-endian
901
+ // we should convert it instead. In practice big endian isn't really Web compatible so it's
902
+ // somewhat safe to assume that browsers aren't going to run it, but maybe there's some SSR
903
+ // server that's affected.
904
+ const view : $ArrayBufferView = new constructor (
905
+ chunk . buffer ,
906
+ chunk . byteOffset ,
907
+ chunk . byteLength / bytesPerElement ,
908
+ ) ;
909
+ resolveBuffer ( response , id , view ) ;
910
+ }
911
+
840
912
function processFullRow (
841
913
response : Response ,
842
914
id : number ,
843
915
tag : number ,
844
916
buffer : Array < Uint8Array > ,
845
- lastChunk : string | Uint8Array ,
917
+ chunk : Uint8Array ,
846
918
) : void {
847
- let row = '';
919
+ if ( enableBinaryFlight ) {
920
+ switch ( tag ) {
921
+ case 65 /* "A" */ :
922
+ // We must always clone to extract it into a separate buffer instead of just a view.
923
+ resolveBuffer ( response , id , mergeBuffer ( buffer , chunk ) . buffer ) ;
924
+ return ;
925
+ case 67 /* "C" */ :
926
+ resolveTypedArray ( response , id , buffer , chunk , Int8Array , 1 ) ;
927
+ return ;
928
+ case 99 /* "c" */ :
929
+ resolveBuffer (
930
+ response ,
931
+ id ,
932
+ buffer . length === 0 ? chunk : mergeBuffer ( buffer , chunk ) ,
933
+ ) ;
934
+ return ;
935
+ case 85 /* "U" */ :
936
+ resolveTypedArray ( response , id , buffer , chunk , Uint8ClampedArray , 1 ) ;
937
+ return ;
938
+ case 83 /* "S" */ :
939
+ resolveTypedArray ( response , id , buffer , chunk , Int16Array , 2 ) ;
940
+ return ;
941
+ case 115 /* "s" */ :
942
+ resolveTypedArray ( response , id , buffer , chunk , Uint16Array , 2 ) ;
943
+ return ;
944
+ case 76 /* "L" */ :
945
+ resolveTypedArray ( response , id , buffer , chunk , Int32Array , 4 ) ;
946
+ return ;
947
+ case 108 /* "l" */ :
948
+ resolveTypedArray ( response , id , buffer , chunk , Uint32Array , 4 ) ;
949
+ return ;
950
+ case 70 /* "F" */ :
951
+ resolveTypedArray ( response , id , buffer , chunk , Float32Array , 4 ) ;
952
+ return ;
953
+ case 68 /* "D" */ :
954
+ resolveTypedArray ( response , id , buffer , chunk , Float64Array , 8 ) ;
955
+ return ;
956
+ case 78 /* "N" */ :
957
+ resolveTypedArray ( response , id , buffer , chunk , BigInt64Array , 8 ) ;
958
+ return ;
959
+ case 109 /* "m" */ :
960
+ resolveTypedArray ( response , id , buffer , chunk , BigUint64Array , 8 ) ;
961
+ return ;
962
+ case 86 /* "V" */ :
963
+ resolveTypedArray ( response , id , buffer , chunk , DataView , 1 ) ;
964
+ return ;
965
+ }
966
+ }
967
+
848
968
const stringDecoder = response . _stringDecoder ;
969
+ let row = '' ;
849
970
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 ) ;
971
+ row += readPartialStringChunk ( stringDecoder , buffer [ i ] ) ;
857
972
}
973
+ row += readFinalStringChunk ( stringDecoder , chunk ) ;
858
974
switch ( tag ) {
859
975
case 73 /* "I" */ : {
860
976
resolveModule ( response , id , row ) ;
@@ -884,7 +1000,7 @@ function processFullRow(
884
1000
resolveText ( response , id , row ) ;
885
1001
return ;
886
1002
}
887
- default : {
1003
+ default : /* """ "{" "[" "t" "f" "n" "0" - "9" */ {
888
1004
// We assume anything else is JSON.
889
1005
resolveModel ( response , id , row ) ;
890
1006
return ;
@@ -918,7 +1034,23 @@ export function processBinaryChunk(
918
1034
}
919
1035
case ROW_TAG : {
920
1036
const resolvedRowTag = chunk [ i ] ;
921
- if ( resolvedRowTag === 84 /* "T" */ ) {
1037
+ if (
1038
+ resolvedRowTag === 84 /* "T" */ ||
1039
+ ( enableBinaryFlight &&
1040
+ ( resolvedRowTag === 65 /* "A" */ ||
1041
+ resolvedRowTag === 67 /* "C" */ ||
1042
+ resolvedRowTag === 99 /* "c" */ ||
1043
+ resolvedRowTag === 85 /* "U" */ ||
1044
+ resolvedRowTag === 83 /* "S" */ ||
1045
+ resolvedRowTag === 115 /* "s" */ ||
1046
+ resolvedRowTag === 76 /* "L" */ ||
1047
+ resolvedRowTag === 108 /* "l" */ ||
1048
+ resolvedRowTag === 70 /* "F" */ ||
1049
+ resolvedRowTag === 68 /* "D" */ ||
1050
+ resolvedRowTag === 78 /* "N" */ ||
1051
+ resolvedRowTag === 109 /* "m" */ ||
1052
+ resolvedRowTag === 86 ) ) /* "V" */
1053
+ ) {
922
1054
rowTag = resolvedRowTag ;
923
1055
rowState = ROW_LENGTH ;
924
1056
i ++ ;
0 commit comments