Skip to content

Commit 8f0582c

Browse files
Explain resizable buffers for BYOB readers
1 parent dd4e180 commit 8f0582c

File tree

1 file changed

+92
-0
lines changed

1 file changed

+92
-0
lines changed
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
# Resizable buffers for BYOB readers
2+
3+
4+
## Introduction
5+
6+
The streams APIs provide ubiquitous, interoperable primitives for creating, composing, and consuming streams of data.
7+
For streams representing bytes, readable byte streams are an extended version of readable streams which are provided to
8+
handle bytes efficiently. These readable byte streams allow for BYOB (bring-your-own-buffer) readers to be acquired,
9+
where a buffer can be reused for multiple reads to reduce garbage collection and to minimize copies.
10+
11+
This change extends BYOB readers to accept resizable `ArrayBuffer`s, allowing the consumer to adjust the buffer's size
12+
without copying to a new buffer.
13+
14+
## API Proposed
15+
16+
* [`ReadableStreamBYOBReader.read(view, opts)`](https://streams.spec.whatwg.org/#byob-reader-read)
17+
and [`ReadableStreamBYOBRequest.respondWithNewView(view)`](https://streams.spec.whatwg.org/#rs-byob-request-respond-with-new-view)
18+
will also accept an `ArrayBufferView` backed by a resizable `ArrayBuffer`.
19+
* The backing buffer will still become detached as usual,
20+
but now the resizability of that buffer will be preserved when it is returned by the reader.
21+
* [`ReadableByteStreamController.enqueue(chunk)`](https://streams.spec.whatwg.org/#rbs-controller-enqueue)
22+
will be left unchanged.
23+
24+
## Examples
25+
26+
### Grow buffer for large read
27+
28+
The code starts out reading into a small buffer of 1024 bytes.
29+
If that is not large enough to hold the entire response, we grow the buffer
30+
so it can hold the additional bytes from subsequent reads.
31+
32+
```javascript
33+
const reader = readableStream.getReader({ mode: "byob" });
34+
const buffer = new ArrayBuffer(1024, { maxByteLength: 8192 });
35+
let offset = 0;
36+
while (true) {
37+
const { value: view, done } =
38+
await reader.read(new Uint8Array(buffer, offset, buffer.byteLength - offset));
39+
buffer = view.buffer;
40+
offset += view.byteLength;
41+
if (done) {
42+
return new Uint8Array(buffer, 0, offset);
43+
}
44+
if (offset === buffer.byteLength) {
45+
// Buffer is full, resize if possible.
46+
if (buffer.byteLength < buffer.maxByteLength) {
47+
buffer.resize(buffer.byteLength * 2);
48+
} else {
49+
throw new RangeError("Response is too large!");
50+
}
51+
}
52+
}
53+
```
54+
55+
### Shrink buffer after reading
56+
57+
The code reads a response that can be up to 1024 bytes long into a single `ArrayBuffer`.
58+
If the response ends up being smaller than 1024 bytes, we resize the buffer to match the exact response size
59+
and free up the unused bytes of that buffer.
60+
61+
```javascript
62+
const MAX_SIZE = 1024;
63+
const reader = readableStream.getReader({ mode: "byob" });
64+
// Create a buffer that can fit a complete response (at most 1024 bytes).
65+
const buffer = new ArrayBuffer(1024, { maxByteLength: 1024 });
66+
// Read the whole response.
67+
const { value: view, done } =
68+
await reader.read(new Uint8Array(buffer, 0, buffer.byteLength), { min: buffer.byteLength });
69+
buffer = view.buffer;
70+
if (done) {
71+
// If the response was smaller, shrink the backing buffer *without copying*.
72+
buffer.resize(view.byteLength);
73+
} else {
74+
throw new RangeError("Response is too large!");
75+
}
76+
```
77+
78+
## Goals
79+
80+
* Allow buffers for BYOB to grow or shrink between reads without copying.
81+
82+
## Non-Goals
83+
84+
* Growable shared array buffers are not part of this proposal.
85+
86+
## End-user Benefits
87+
88+
* ...
89+
90+
## Alternatives
91+
92+
* ...

0 commit comments

Comments
 (0)