Skip to content

feat: support zero-copy byte views and transferable ArrayBuffers - #292

Draft
teamchong wants to merge 2 commits into
cloudflare:mainfrom
teamchong:feat/zero-copy-bytes
Draft

teamchong wants to merge 2 commits into
cloudflare:mainfrom
teamchong:feat/zero-copy-bytes

Conversation

@teamchong

@teamchong teamchong commented Nov 9, 2026 •

Copy link
Copy Markdown
Collaborator

Avoid unnecessary ArrayBuffer copies across jsonCompatibleWithBytes and MessagePort (structuredClonable) transports:

  1. Zero-copy jsonCompatibleWithBytes serialization (Devaluator.devaluateImpl):

    • In fix: send only a byte view's own bytes over structured-clone transports #287, partial Uint8Array views (bytes.byteOffset !== 0 || bytes.byteLength !== bytes.buffer.byteLength) were copied with new Uint8Array(bytes) at both structuredClonable and jsonCompatibleWithBytes levels so MessagePort.postMessage wouldn't clone an entire shared backing buffer (such as Node's Buffer pool or WebAssembly.Memory).
    • Custom jsonCompatibleWithBytes transports inspect [bytes.buffer, bytes.byteOffset, bytes.byteLength] directly rather than structured-cloning bytes.buffer, so Devaluator now keeps the exact view at jsonCompatibleWithBytes level and only copies un-transferred partial views at structuredClonable level.
  2. Zero-copy ArrayBufferView deserialization (Evaluator.evaluateImpl):

    • Previously, Evaluator always called bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength) before constructing typed arrays or DataView, even when bytes was already a raw Uint8Array at jsonCompatibleWithBytes or structuredClonable level.
    • Now, when the incoming payload is a raw Uint8Array (not base64-decoded into Node's shared Buffer pool), native endianness is little-endian (or elementSize === undefined), and bytes.byteOffset is aligned to elementSize, typed array and DataView views are constructed directly on (bytes.buffer, bytes.byteOffset, ...) without slicing.
    • ArrayBuffer payloads continue to slice subviews so byteLength and indexable bounds match the serialized view.
  3. Opt-in transferable ArrayBuffers over MessagePort (transferArrayBuffer):

    • Added transferArrayBuffer(viewOrBuffer) to mark an ArrayBuffer (or the backing ArrayBuffer of an ArrayBufferView) for zero-copy transfer over MessagePort transports.
    • Devaluator preserves partial views on marked buffers without copying, and MessagePortTransport.send() collects and consumes marked ArrayBuffers into the postMessage(message, transfer) transfer list (with a zero-cost boolean fast-path when transferArrayBuffer() has not been called).

@changeset-bot

changeset-bot Bot commented Nov 9, 2026 •

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 46e7785

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes changesets to release 1 package
Name Type
capnweb Minor

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@pkg-pr-new

pkg-pr-new Bot commented Nov 9, 2026 •

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/cloudflare/capnweb@292
npm i https://pkg.pr.new/cloudflare/capnweb/capnweb-validate@292

commit: 46e7785

This branch has not been deployed

No deployments
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.

1 participant