Skip to content

feat(streams): BYOB readers + real ByteLengthQueuingStrategy accounting - #4966

Merged
proggeramlug merged 1 commit into
mainfrom
feat/streams-byob-4915
Jun 11, 2026
Merged

proggeramlug merged 1 commit into
mainfrom
feat/streams-byob-4915

Conversation

@proggeramlug

Copy link
Copy Markdown
Contributor

Fixes #4915 (stub-elimination epic #4919).

What

Implements the Web Streams residuals left after #237/#1545 — all three acceptance items:

1. ReadableStreamBYOBReader — stream.getReader({ mode: "byob" }) and new ReadableStreamBYOBReader(stream) mint readers whose read(view) fills the caller-supplied TypedArray/DataView/Buffer in place, draining queued byte chunks across chunk boundaries (remainder requeued at the head). Reads at EOF resolve { value: <empty view>, done: true }. The byte-stream controller exposes byobRequest — { view, respond(bytesWritten), respondWithNewView(view) } — non-null while a read(view) is parked, so pull sources write straight into the caller's buffer. New crates/perry-stdlib/src/streams/byob.rs carries the implementation (with its own GC root scanning for parked promises/views).

2. ByteLengthQueuingStrategy as a real strategy — per-chunk strategy.size(chunk) results (computed once at enqueue per spec; byteLength on byte streams, 1 otherwise) are summed into desiredSize for ReadableStream, WritableStream, and TransformStream. The WritableStream/TransformStream constructors now accept whole strategy objects (streams/strategy.rs::parse_strategy_value handles number-or-object-or-undefined); TransformStream takes both writableStrategy and readableStrategy. Byte streams default to highWaterMark: 0 per spec — no eager pull at construction; ShouldCallPull fires once a read request is parked.

3. Spec-shaped errors — TypeError for BYOB mode on non-byte streams, invalid/zero-length views, released readers, and respond-with-no-request; RangeError (ERR_OUT_OF_RANGE) for out-of-range bytesWritten. The dead js_streams_throw_byob_not_implemented / js_streams_throw_byte_length_not_implemented stubs (cited in the issue at perry-ext-streams/src/lib.rs and stdlib/streams/transform.rs) are removed — nothing routed to them. The three #4915 manifest stub notes are dropped and API docs regenerated.

streams.rs stayed under the 2000-line CI cap by extracting the #1545 queuing-strategy block into streams/strategy.rs.

Verification

  • New parity test test-files/test_parity_streams_byob.ts is byte-for-byte identical to node --experimental-strip-types (BYOB multi-chunk reads, mode TypeError, byobRequest.respond, desiredSize accounting on all three constructors, strategy object shape).
  • New e2e guard crates/perry/tests/issue_4915_streams_byob.rs (green).
  • Regression sweep: test_issue_237_streams_{pipe,user_source,blob}, test_issue_320_readable_stream MATCH Node; test_parity_stream_web, test_issue_562_stream_subclass, test_parity_stream_consumers, test_parity_stream are byte-identical to a fresh origin/main baseline build (their failures are pre-existing Roadmap: Node.js + TypeScript compatibility & stability #793 module-inventory gaps, including the from-iterable dynamic-read loop — verified unchanged).
  • perry-stdlib lib tests (96), stub_inventory, fmt, and the file-size gate all pass.

No version bump / changelog — maintainer folds metadata at merge per repo convention.

…ng (#4915)

Implements the Web Streams residuals left after #237/#1545:

- ReadableStreamBYOBReader: getReader({ mode: "byob" }) and
  new ReadableStreamBYOBReader(stream) mint readers whose read(view)
  fills the caller-supplied TypedArray/DataView/Buffer (draining queued
  byte chunks across chunk boundaries, remainder requeued); done
  resolves with an empty view per spec.
- ReadableByteStreamController.byobRequest: { view, respond(bytesWritten),
  respondWithNewView(view) } while a read(view) is parked, null otherwise;
  pull sources write straight into the caller's buffer.
- Real QueuingStrategy accounting: per-chunk size() results (or
  byteLength on byte streams) are summed into desiredSize for
  ReadableStream, WritableStream, and TransformStream; the
  WritableStream/TransformStream constructors now accept strategy
  objects (number-or-object parsing in streams/strategy.rs).
- Byte streams default to highWaterMark 0 per spec (no eager pull at
  construction; ShouldCallPull fires on parked read requests).
- Spec-shaped errors: TypeError for BYOB mode on non-byte streams /
  invalid views / released readers, RangeError for out-of-range
  bytesWritten; removed the dead js_streams_throw_*_not_implemented
  stubs (perry-ext-streams + stdlib) and the three #4915 manifest
  stub notes; regenerated API docs.

Verified byte-for-byte against node --experimental-strip-types
(test-files/test_parity_streams_byob.ts) and guarded by
crates/perry/tests/issue_4915_streams_byob.rs. Streams regression
sweep (pipe/user-source/blob/readable/subclass/consumers/stream_web)
is byte-identical to origin/main.

Fixes #4915
@proggeramlug
proggeramlug force-pushed the feat/streams-byob-4915 branch from 5d9a47e to 2c8b2df Compare June 11, 2026 05:59
@proggeramlug
proggeramlug merged commit 486a734 into main Jun 11, 2026
12 of 13 checks passed
@proggeramlug
proggeramlug deleted the feat/streams-byob-4915 branch June 11, 2026 06:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Web Streams residuals: BYOB readers + ByteLengthQueuingStrategy still throw (post-#1545)

1 participant