Skip to content

Commit 6eff679

Browse files
authored
stream: copy SAB-backed chunks in iter consumers
Ensure bytes() copies single chunks backed by SharedArrayBuffer so arrayBuffer() and arrayBufferSync() return ArrayBuffer instances as specified. Signed-off-by: Kamat, Trivikram <16024985+trivikr@users.noreply.github.com> Assisted-by: openai:gpt-5.5 PR-URL: #64382 Fixes: #64381 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Mattias Buelens <mattias@buelens.com>
1 parent ab41cf0 commit 6eff679

3 files changed

Lines changed: 22 additions & 17 deletions

File tree

lib/internal/streams/iter/consumers.js

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ const {
2222
TypedArrayPrototypeGetBuffer,
2323
TypedArrayPrototypeGetByteLength,
2424
TypedArrayPrototypeGetByteOffset,
25+
TypedArrayPrototypeSet,
26+
Uint8Array,
2527
} = primordials;
2628

2729
const {
@@ -164,21 +166,17 @@ async function collectAsync(source, signal, limit) {
164166

165167
/**
166168
* Convert a Uint8Array to its backing ArrayBuffer, slicing if necessary.
167-
* Handles both ArrayBuffer and SharedArrayBuffer backing stores.
168169
* @param {Uint8Array} data
169-
* @returns {ArrayBuffer|SharedArrayBuffer}
170+
* @returns {ArrayBuffer}
170171
*/
171172
function toArrayBuffer(data) {
172173
const byteOffset = TypedArrayPrototypeGetByteOffset(data);
173174
const byteLength = TypedArrayPrototypeGetByteLength(data);
174175
const buffer = TypedArrayPrototypeGetBuffer(data);
175-
// SharedArrayBuffer is not available in primordials, so use
176-
// direct property access for its byteLength and slice.
177176
if (isSharedArrayBuffer(buffer)) {
178-
if (byteOffset === 0 && byteLength === buffer.byteLength) {
179-
return buffer;
180-
}
181-
return buffer.slice(byteOffset, byteOffset + byteLength);
177+
const copy = new Uint8Array(byteLength);
178+
TypedArrayPrototypeSet(copy, data);
179+
return TypedArrayPrototypeGetBuffer(copy);
182180
}
183181
if (byteOffset === 0 &&
184182
byteLength === ArrayBufferPrototypeGetByteLength(buffer)) {

lib/internal/streams/iter/utils.js

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,12 @@ function allUint8Array(chunks) {
203203
return true;
204204
}
205205

206+
function copyBytes(chunk) {
207+
const copy = new Uint8Array(TypedArrayPrototypeGetByteLength(chunk));
208+
TypedArrayPrototypeSet(copy, chunk);
209+
return copy;
210+
}
211+
206212
/**
207213
* Concatenate multiple Uint8Arrays into a single Uint8Array.
208214
* @param {Uint8Array[]} chunks
@@ -220,16 +226,15 @@ function concatBytes(chunks) {
220226
// If non-zero offset, skip the remaining buffer checks.
221227
if (TypedArrayPrototypeGetByteOffset(chunk) === 0) {
222228
const buf = TypedArrayPrototypeGetBuffer(chunk);
223-
// SharedArrayBuffer is not available in primordials, so use
224-
// direct property access for its byteLength.
225-
const bufByteLength = isSharedArrayBuffer(buf) ?
226-
buf.byteLength :
227-
ArrayBufferPrototypeGetByteLength(buf);
228-
if (TypedArrayPrototypeGetByteLength(chunk) === bufByteLength) {
229+
if (
230+
!isSharedArrayBuffer(buf) &&
231+
TypedArrayPrototypeGetByteLength(chunk) ===
232+
ArrayBufferPrototypeGetByteLength(buf)
233+
) {
229234
return chunk;
230235
}
231236
}
232-
return new Uint8Array(chunk);
237+
return copyBytes(chunk);
233238
}
234239
// Multiple chunks: concatenate
235240
let totalByteLength = 0;

test/parallel/test-stream-iter-sharedarraybuffer.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,15 @@ function testBytesSyncSAB() {
5252
const sab = new SharedArrayBuffer(5);
5353
new Uint8Array(sab).set([104, 101, 108, 108, 111]); // 'hello'
5454
const data = bytesSync(fromSync(sab));
55+
assert.ok(data.buffer instanceof ArrayBuffer);
5556
assert.deepStrictEqual(data, new Uint8Array([104, 101, 108, 108, 111]));
5657
}
5758

5859
async function testBytesAsyncSAB() {
5960
const sab = new SharedArrayBuffer(5);
6061
new Uint8Array(sab).set([104, 101, 108, 108, 111]); // 'hello'
6162
const data = await bytes(from(sab));
63+
assert.ok(data.buffer instanceof ArrayBuffer);
6264
assert.deepStrictEqual(data, new Uint8Array([104, 101, 108, 108, 111]));
6365
}
6466

@@ -80,15 +82,15 @@ function testArrayBufferSyncSAB() {
8082
const sab = new SharedArrayBuffer(4);
8183
new Uint8Array(sab).set([1, 2, 3, 4]);
8284
const result = arrayBufferSync(fromSync(sab));
83-
assert.ok(result instanceof SharedArrayBuffer || result instanceof ArrayBuffer);
85+
assert.ok(result instanceof ArrayBuffer);
8486
assert.deepStrictEqual(new Uint8Array(result), new Uint8Array([1, 2, 3, 4]));
8587
}
8688

8789
async function testArrayBufferAsyncSAB() {
8890
const sab = new SharedArrayBuffer(4);
8991
new Uint8Array(sab).set([1, 2, 3, 4]);
9092
const result = await arrayBuffer(from(sab));
91-
assert.ok(result instanceof SharedArrayBuffer || result instanceof ArrayBuffer);
93+
assert.ok(result instanceof ArrayBuffer);
9294
assert.deepStrictEqual(new Uint8Array(result), new Uint8Array([1, 2, 3, 4]));
9395
}
9496

0 commit comments

Comments
 (0)