@@ -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 ,
@@ -297,6 +299,14 @@ function createInitializedTextChunk(
297
299
return new Chunk ( INITIALIZED , value , null , response ) ;
298
300
}
299
301
302
+ function createInitializedBufferChunk(
303
+ response: Response,
304
+ value: $ArrayBufferView | ArrayBuffer,
305
+ ): InitializedChunk< Uint8Array > {
306
+ // $FlowFixMe[invalid-constructor] Flow doesn't support functions as constructors
307
+ return new Chunk ( INITIALIZED , value , null , response ) ;
308
+ }
309
+
300
310
function resolveModelChunk< T > (
301
311
chunk: SomeChunk< T > ,
302
312
value: UninitializedModel,
@@ -738,6 +748,16 @@ function resolveText(response: Response, id: number, text: string): void {
738
748
chunks . set ( id , createInitializedTextChunk ( response , text ) ) ;
739
749
}
740
750
751
+ function resolveBuffer (
752
+ response : Response ,
753
+ id : number ,
754
+ buffer : $ArrayBufferView | ArrayBuffer ,
755
+ ) : void {
756
+ const chunks = response . _chunks ;
757
+ // We assume that we always reference buffers after they've been emitted.
758
+ chunks . set ( id , createInitializedBufferChunk ( response , buffer ) ) ;
759
+ }
760
+
741
761
function resolveModule (
742
762
response : Response ,
743
763
id : number ,
@@ -856,24 +876,120 @@ function resolveHint(
856
876
dispatchHint ( code , hintModel ) ;
857
877
}
858
878
879
+ function mergeBuffer (
880
+ buffer : Array < Uint8Array > ,
881
+ lastChunk : Uint8Array ,
882
+ ) : Uint8Array {
883
+ const l = buffer . length ;
884
+ // Count the bytes we'll need
885
+ let byteLength = lastChunk . length ;
886
+ for ( let i = 0 ; i < l ; i ++ ) {
887
+ byteLength += buffer [ i ] . byteLength ;
888
+ }
889
+ // Allocate enough contiguous space
890
+ const result = new Uint8Array ( byteLength ) ;
891
+ let offset = 0 ;
892
+ // Copy all the buffers into it.
893
+ for ( let i = 0 ; i < l ; i ++ ) {
894
+ const chunk = buffer [ i ] ;
895
+ result . set ( chunk , offset ) ;
896
+ offset += chunk . byteLength ;
897
+ }
898
+ result . set ( lastChunk , offset ) ;
899
+ return result ;
900
+ }
901
+
902
+ function resolveTypedArray (
903
+ response : Response ,
904
+ id : number ,
905
+ buffer : Array < Uint8Array > ,
906
+ lastChunk : Uint8Array ,
907
+ constructor : any ,
908
+ bytesPerElement : number ,
909
+ ) : void {
910
+ // If the view fits into one original buffer, we just reuse that buffer instead of
911
+ // copying it out to a separate copy. This means that it's not always possible to
912
+ // transfer these values to other threads without copying first since they may
913
+ // share array buffer. For this to work, it must also have bytes aligned to a
914
+ // multiple of a size of the type.
915
+ const chunk =
916
+ buffer . length === 0 && lastChunk . byteOffset % bytesPerElement === 0
917
+ ? lastChunk
918
+ : mergeBuffer ( buffer , lastChunk ) ;
919
+ // TODO: The transfer protocol of RSC is little-endian. If the client isn't little-endian
920
+ // we should convert it instead. In practice big endian isn't really Web compatible so it's
921
+ // somewhat safe to assume that browsers aren't going to run it, but maybe there's some SSR
922
+ // server that's affected.
923
+ const view : $ArrayBufferView = new constructor (
924
+ chunk . buffer ,
925
+ chunk . byteOffset ,
926
+ chunk . byteLength / bytesPerElement ,
927
+ ) ;
928
+ resolveBuffer ( response , id , view ) ;
929
+ }
930
+
859
931
function processFullRow (
860
932
response : Response ,
861
933
id : number ,
862
934
tag : number ,
863
935
buffer : Array < Uint8Array > ,
864
- lastChunk : string | Uint8Array ,
936
+ chunk : Uint8Array ,
865
937
) : void {
866
- let row = '';
938
+ if ( enableBinaryFlight ) {
939
+ switch ( tag ) {
940
+ case 65 /* "A" */ :
941
+ // We must always clone to extract it into a separate buffer instead of just a view.
942
+ resolveBuffer ( response , id , mergeBuffer ( buffer , chunk ) . buffer ) ;
943
+ return ;
944
+ case 67 /* "C" */ :
945
+ resolveTypedArray ( response , id , buffer , chunk , Int8Array , 1 ) ;
946
+ return ;
947
+ case 99 /* "c" */ :
948
+ resolveBuffer (
949
+ response ,
950
+ id ,
951
+ buffer . length === 0 ? chunk : mergeBuffer ( buffer , chunk ) ,
952
+ ) ;
953
+ return ;
954
+ case 85 /* "U" */ :
955
+ resolveTypedArray ( response , id , buffer , chunk , Uint8ClampedArray , 1 ) ;
956
+ return ;
957
+ case 83 /* "S" */ :
958
+ resolveTypedArray ( response , id , buffer , chunk , Int16Array , 2 ) ;
959
+ return ;
960
+ case 115 /* "s" */ :
961
+ resolveTypedArray ( response , id , buffer , chunk , Uint16Array , 2 ) ;
962
+ return ;
963
+ case 76 /* "L" */ :
964
+ resolveTypedArray ( response , id , buffer , chunk , Int32Array , 4 ) ;
965
+ return ;
966
+ case 108 /* "l" */ :
967
+ resolveTypedArray ( response , id , buffer , chunk , Uint32Array , 4 ) ;
968
+ return ;
969
+ case 70 /* "F" */ :
970
+ resolveTypedArray ( response , id , buffer , chunk , Float32Array , 4 ) ;
971
+ return ;
972
+ case 68 /* "D" */ :
973
+ resolveTypedArray ( response , id , buffer , chunk , Float64Array , 8 ) ;
974
+ return ;
975
+ case 78 /* "N" */ :
976
+ resolveTypedArray ( response , id , buffer , chunk , BigInt64Array , 8 ) ;
977
+ return ;
978
+ case 109 /* "m" */ :
979
+ resolveTypedArray ( response , id , buffer , chunk , BigUint64Array , 8 ) ;
980
+ return ;
981
+ case 86 /* "V" */ :
982
+ resolveTypedArray ( response , id , buffer , chunk , DataView , 1 ) ;
983
+ return ;
984
+ }
985
+ }
986
+
867
987
const stringDecoder = response . _stringDecoder ;
988
+ let row = '' ;
868
989
for ( let i = 0 ; i < buffer . length ; i ++ ) {
869
- const chunk = buffer [ i ] ;
870
- row += readPartialStringChunk ( stringDecoder , chunk ) ;
871
- }
872
- if ( typeof lastChunk === 'string' ) {
873
- row += lastChunk ;
874
- } else {
875
- row += readFinalStringChunk ( stringDecoder , lastChunk ) ;
990
+ row += readPartialStringChunk ( stringDecoder , buffer [ i ] ) ;
876
991
}
992
+ row += readFinalStringChunk ( stringDecoder , chunk ) ;
877
993
switch ( tag ) {
878
994
case 73 /* "I" */ : {
879
995
resolveModule ( response , id , row ) ;
@@ -903,7 +1019,7 @@ function processFullRow(
903
1019
resolveText ( response , id , row ) ;
904
1020
return ;
905
1021
}
906
- default : {
1022
+ default : /* """ "{" "[" "t" "f" "n" "0" - "9" */ {
907
1023
// We assume anything else is JSON.
908
1024
resolveModel ( response , id , row ) ;
909
1025
return ;
@@ -937,7 +1053,23 @@ export function processBinaryChunk(
937
1053
}
938
1054
case ROW_TAG : {
939
1055
const resolvedRowTag = chunk [ i ] ;
940
- if ( resolvedRowTag === 84 /* "T" */ ) {
1056
+ if (
1057
+ resolvedRowTag === 84 /* "T" */ ||
1058
+ ( enableBinaryFlight &&
1059
+ ( resolvedRowTag === 65 /* "A" */ ||
1060
+ resolvedRowTag === 67 /* "C" */ ||
1061
+ resolvedRowTag === 99 /* "c" */ ||
1062
+ resolvedRowTag === 85 /* "U" */ ||
1063
+ resolvedRowTag === 83 /* "S" */ ||
1064
+ resolvedRowTag === 115 /* "s" */ ||
1065
+ resolvedRowTag === 76 /* "L" */ ||
1066
+ resolvedRowTag === 108 /* "l" */ ||
1067
+ resolvedRowTag === 70 /* "F" */ ||
1068
+ resolvedRowTag === 68 /* "D" */ ||
1069
+ resolvedRowTag === 78 /* "N" */ ||
1070
+ resolvedRowTag === 109 /* "m" */ ||
1071
+ resolvedRowTag === 86 ) ) /* "V" */
1072
+ ) {
941
1073
rowTag = resolvedRowTag ;
942
1074
rowState = ROW_LENGTH ;
943
1075
i ++ ;
0 commit comments