perf(ext/fetch): use content-length in InnerBody.consume (#15925)

This fast path prevents repeated allocations when receiving a fetch body with a known size.

Co-authored-by: Luca Casonato <hello@lcas.dev>
This commit is contained in:
Marcos Casagrande 2022-09-26 20:27:50 +02:00 committed by GitHub
parent 1628dba6db
commit c7dd842f84
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 164 additions and 4 deletions

View file

@ -64,10 +64,12 @@
}
class InnerBody {
#knownExactLength = null;
/**
* @param {ReadableStream<Uint8Array> | { body: Uint8Array | string, consumed: boolean }} stream
*/
constructor(stream) {
constructor(stream, knownExactLength) {
/** @type {ReadableStream<Uint8Array> | { body: Uint8Array | string, consumed: boolean }} */
this.streamOrStatic = stream ??
{ body: new Uint8Array(), consumed: false };
@ -75,6 +77,8 @@
this.source = null;
/** @type {null | number} */
this.length = null;
this.#knownExactLength = knownExactLength;
}
get stream() {
@ -147,14 +151,31 @@
const reader = this.stream.getReader();
/** @type {Uint8Array[]} */
const chunks = [];
let finalBuffer = this.#knownExactLength
? new Uint8Array(this.#knownExactLength)
: null;
let totalLength = 0;
while (true) {
const { value: chunk, done } = await reader.read();
if (done) break;
ArrayPrototypePush(chunks, chunk);
if (finalBuffer) {
// fast path, content-length is present
TypedArrayPrototypeSet(finalBuffer, chunk, totalLength);
} else {
// slow path, content-length is not present
ArrayPrototypePush(chunks, chunk);
}
totalLength += chunk.byteLength;
}
const finalBuffer = new Uint8Array(totalLength);
if (finalBuffer) {
return finalBuffer;
}
finalBuffer = new Uint8Array(totalLength);
let i = 0;
for (const chunk of chunks) {
TypedArrayPrototypeSet(finalBuffer, chunk, i);
@ -199,7 +220,7 @@
clone() {
const [out1, out2] = this.stream.tee();
this.streamOrStatic = out1;
const second = new InnerBody(out2);
const second = new InnerBody(out2, this.#knownExactLength);
second.source = core.deserialize(core.serialize(this.source));
second.length = this.length;
return second;