Skip to content

Commit

Permalink
lib: allow byob reader for 'blob.stream()'
Browse files Browse the repository at this point in the history
Fixes: nodejs#47993
PR-URL: nodejs#49713
Reviewed-By: Benjamin Gruenbaum <[email protected]>
Reviewed-By: Matteo Collina <[email protected]>
  • Loading branch information
debadree25 authored and alexfernandez committed Nov 1, 2023
1 parent 8e610d7 commit cefe957
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 11 deletions.
10 changes: 8 additions & 2 deletions lib/internal/blob.js
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,7 @@ class Blob {

const reader = this[kHandle].getReader();
return new lazyReadableStream({
type: 'bytes',
start(c) {
// There really should only be one read at a time so using an
// array here is purely defensive.
Expand All @@ -339,6 +340,9 @@ class Blob {
if (status === 0) {
// EOS
c.close();
// This is to signal the end for byob readers
// see https://streams.spec.whatwg.org/#example-rbs-pull
c.byobRequest?.respond(0);
const pending = this.pendingPulls.shift();
pending.resolve();
return;
Expand All @@ -352,13 +356,15 @@ class Blob {
pending.reject(error);
return;
}
if (buffer !== undefined) {
// ReadableByteStreamController.enqueue errors if we submit a 0-length
// buffer. We need to check for that here.
if (buffer !== undefined && buffer.byteLength !== 0) {
c.enqueue(new Uint8Array(buffer));
}
// We keep reading until we either reach EOS, some error, or we
// hit the flow rate of the stream (c.desiredSize).
queueMicrotask(() => {
if (c.desiredSize <= 0) {
if (c.desiredSize < 0) {
// A manual backpressure check.
if (this.pendingPulls.length !== 0) {
// A case of waiting pull finished (= not yet canceled)
Expand Down
46 changes: 44 additions & 2 deletions test/parallel/test-blob.js
Original file line number Diff line number Diff line change
Expand Up @@ -331,12 +331,54 @@ assert.throws(() => new Blob({}), {
const b = new Blob(Array(10).fill('hello'));
const stream = b.stream();
const reader = stream.getReader();
assert.strictEqual(stream[kState].controller.desiredSize, 1);
assert.strictEqual(stream[kState].controller.desiredSize, 0);
const { value, done } = await reader.read();
assert.strictEqual(value.byteLength, 5);
assert(!done);
setTimeout(() => {
assert.strictEqual(stream[kState].controller.desiredSize, 0);
// The blob stream is now a byte stream hence after the first read,
// it should pull in the next 'hello' which is 5 bytes hence -5.
assert.strictEqual(stream[kState].controller.desiredSize, -5);
}, 0);
})().then(common.mustCall());

(async () => {
const blob = new Blob(['hello', 'world']);
const stream = blob.stream();
const reader = stream.getReader({ mode: 'byob' });
const decoder = new TextDecoder();
const chunks = [];
while (true) {
const { value, done } = await reader.read(new Uint8Array(100));
if (done) break;
chunks.push(decoder.decode(value, { stream: true }));
}
assert.strictEqual(chunks.join(''), 'helloworld');
})().then(common.mustCall());

(async () => {
const b = new Blob(Array(10).fill('hello'));
const stream = b.stream();
const reader = stream.getReader({ mode: 'byob' });
assert.strictEqual(stream[kState].controller.desiredSize, 0);
const { value, done } = await reader.read(new Uint8Array(100));
assert.strictEqual(value.byteLength, 5);
assert(!done);
setTimeout(() => {
assert.strictEqual(stream[kState].controller.desiredSize, -5);
}, 0);
})().then(common.mustCall());

(async () => {
const b = new Blob(Array(10).fill('hello'));
const stream = b.stream();
const reader = stream.getReader({ mode: 'byob' });
assert.strictEqual(stream[kState].controller.desiredSize, 0);
const { value, done } = await reader.read(new Uint8Array(2));
assert.strictEqual(value.byteLength, 2);
assert(!done);
setTimeout(() => {
assert.strictEqual(stream[kState].controller.desiredSize, -3);
}, 0);
})().then(common.mustCall());

Expand Down
7 changes: 0 additions & 7 deletions test/wpt/status/FileAPI/blob.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,5 @@
},
"Blob-slice.any.js": {
"skip": "Depends on File API"
},
"Blob-stream.any.js": {
"fail": {
"expected": [
"Reading Blob.stream() with BYOB reader"
]
}
}
}

0 comments on commit cefe957

Please sign in to comment.