Skip to content

Commit 3ae6dea

Browse files
committed
refactor writeChunk to be more defensive and efficient
We now defend against overflows using the next views length instead of the current one. this protects us against a future where we use byobRequest and we get longer initial views than we might create after overflowing the first time. Additionally we add in an optimization when we have completely filled up the currentView where we avoid creating subarrays of the chunk to write since it lands exactly on a view boundary. Finally we move the view creation to beginWriting to avoid a runtime check on each write and because we want to reset the view on each beginWriting call in case a throw elsewhere in the program leaves the currentView in an unfinished state
1 parent 2e302f7 commit 3ae6dea

File tree

1 file changed

+39
-19
lines changed

1 file changed

+39
-19
lines changed

packages/react-server/src/ReactServerStreamConfigBrowser.js

Lines changed: 39 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -21,46 +21,66 @@ export function flushBuffered(destination: Destination) {
2121
// transform streams. https://github.com/whatwg/streams/issues/960
2222
}
2323

24+
const VIEW_SIZE = 512;
2425
let currentView = null;
2526
let writtenBytes = 0;
2627

27-
export function beginWriting(destination: Destination) {}
28+
export function beginWriting(destination: Destination) {
29+
currentView = new Uint8Array(VIEW_SIZE);
30+
writtenBytes = 0;
31+
}
2832

2933
export function writeChunk(
3034
destination: Destination,
3135
chunk: PrecomputedChunk | Chunk,
3236
): void {
33-
if (currentView === null) {
34-
currentView = new Uint8Array(512);
35-
writtenBytes = 0;
37+
if (chunk.length === 0) {
38+
return;
3639
}
3740

38-
if (chunk.length > currentView.length) {
39-
// this chunk is larger than our view which implies it was not
41+
if (chunk.length > VIEW_SIZE) {
42+
// this chunk may overflow a single view which implies it was not
4043
// one that is cached by the streaming renderer. We will enqueu
4144
// it directly and expect it is not re-used
4245
if (writtenBytes > 0) {
43-
destination.enqueue(new Uint8Array(currentView.buffer, 0, writtenBytes));
44-
currentView = null;
46+
destination.enqueue(
47+
new Uint8Array(
48+
((currentView: any): Uint8Array).buffer,
49+
0,
50+
writtenBytes,
51+
),
52+
);
53+
currentView = new Uint8Array(VIEW_SIZE);
4554
writtenBytes = 0;
4655
}
4756
destination.enqueue(chunk);
4857
return;
4958
}
5059

51-
const allowableBytes = currentView.length - writtenBytes;
52-
if (allowableBytes < chunk.length) {
53-
// this chunk would overflow the current view. We enqueu a full view
60+
let bytesToWrite = chunk;
61+
const allowableBytes = ((currentView: any): Uint8Array).length - writtenBytes;
62+
if (allowableBytes < bytesToWrite.length) {
63+
// this chunk would overflow the current view. We enqueue a full view
5464
// and start a new view with the remaining chunk
55-
currentView.set(chunk.subarray(0, allowableBytes), writtenBytes);
56-
destination.enqueue(currentView);
57-
currentView = new Uint8Array(512);
58-
currentView.set(chunk.subarray(allowableBytes));
59-
writtenBytes = chunk.length - allowableBytes;
60-
} else {
61-
currentView.set(chunk, writtenBytes);
62-
writtenBytes += chunk.length;
65+
if (allowableBytes === 0) {
66+
// the current view is already full, send it
67+
destination.enqueue(currentView);
68+
} else {
69+
// fill up the current view and apply the remaining chunk bytes
70+
// to a new view.
71+
((currentView: any): Uint8Array).set(
72+
bytesToWrite.subarray(0, allowableBytes),
73+
writtenBytes,
74+
);
75+
// writtenBytes += allowableBytes; // this can be skipped because we are going to immediately reset the view
76+
destination.enqueue(currentView);
77+
bytesToWrite = bytesToWrite.subarray(allowableBytes);
78+
}
79+
currentView = new Uint8Array(VIEW_SIZE);
80+
writtenBytes = 0;
6381
}
82+
((currentView: any): Uint8Array).set(bytesToWrite, writtenBytes);
83+
writtenBytes += bytesToWrite.length;
6484
}
6585

6686
export function writeChunkAndReturn(

0 commit comments

Comments
 (0)