-
Notifications
You must be signed in to change notification settings - Fork 47.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Fizz] write chunks to a buffer with no re-use #24034
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,24 +21,64 @@ export function flushBuffered(destination: Destination) { | |
// transform streams. https://github.com/whatwg/streams/issues/960 | ||
} | ||
|
||
let currentView = null; | ||
let writtenBytes = 0; | ||
|
||
export function beginWriting(destination: Destination) {} | ||
|
||
export function writeChunk( | ||
destination: Destination, | ||
chunk: PrecomputedChunk | Chunk, | ||
): void { | ||
destination.enqueue(chunk); | ||
if (currentView === null) { | ||
currentView = new Uint8Array(512); | ||
writtenBytes = 0; | ||
} | ||
|
||
if (chunk.length > currentView.length) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can hard code this to 512. This gets important if we start using byobRequest since the current view could be smaller in that case. However, what really matters is whether it'll overflow the next view which you allocate below. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah yup that makes sense |
||
// this chunk is larger than our view which implies it was not | ||
// one that is cached by the streaming renderer. We will enqueu | ||
// it directly and expect it is not re-used | ||
if (writtenBytes > 0) { | ||
destination.enqueue(new Uint8Array(currentView.buffer, 0, writtenBytes)); | ||
currentView = null; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, maybe because of this case. It is very likely that we're going to have at least one more call after this. Like we have to write There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. One case where this might not hold is if we start using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I’m confused, why would using byobRequest make it so we wouldn’t need to allocate a new buffer? What about byob is distinct from our self created buffers that would change this logic? |
||
writtenBytes = 0; | ||
} | ||
destination.enqueue(chunk); | ||
return; | ||
} | ||
|
||
const allowableBytes = currentView.length - writtenBytes; | ||
if (allowableBytes < chunk.length) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I’m curious about the performance of subarrays given they are fixed address ranges with no copying. Is there still significant overhead to subarray(0) that is worth avoiding with special cases? |
||
// this chunk would overflow the current view. We enqueu a full view | ||
// and start a new view with the remaining chunk | ||
currentView.set(chunk.subarray(0, allowableBytes), writtenBytes); | ||
destination.enqueue(currentView); | ||
currentView = new Uint8Array(512); | ||
currentView.set(chunk.subarray(allowableBytes)); | ||
writtenBytes = chunk.length - allowableBytes; | ||
} else { | ||
currentView.set(chunk, writtenBytes); | ||
writtenBytes += chunk.length; | ||
} | ||
} | ||
|
||
export function writeChunkAndReturn( | ||
destination: Destination, | ||
chunk: PrecomputedChunk | Chunk, | ||
): boolean { | ||
destination.enqueue(chunk); | ||
return destination.desiredSize > 0; | ||
writeChunk(destination, chunk); | ||
// in web streams there is no backpressure so we can alwas write more | ||
return true; | ||
} | ||
|
||
export function completeWriting(destination: Destination) {} | ||
export function completeWriting(destination: Destination) { | ||
if (currentView && writtenBytes > 0) { | ||
destination.enqueue(new Uint8Array(currentView.buffer, 0, writtenBytes)); | ||
currentView = null; | ||
writtenBytes = 0; | ||
} | ||
} | ||
|
||
export function close(destination: Destination) { | ||
destination.close(); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do this lazily here rather than eagerly in
beginWriting
? You can cheat flow and assume that it has already been initialized at this point since we know it has. That way you don't need a runtime check for every call.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I forget the original intent but I changed it to match your suggestion and also figured it was more defensive in case endWriting was ever not called (may be impossible but figured a wayward throw might leave this as a possibility)