|
| 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