From 99f52dbf7f7dbed45d61644e4e06642a58391379 Mon Sep 17 00:00:00 2001 From: dualfroz Date: Fri, 4 Sep 2026 09:22:12 +0200 Subject: [PATCH] fix: preserve byteLength when cloning a DataView copyBuffer routed DataView through the TypedArray path, passing cur.length (undefined on a DataView) as the constructor's third argument. Since that argument is a byte length for DataView, a partial view was extended to the end of the buffer. Clone DataView explicitly using byteLength. --- index.js | 5 +++++ test/index.js | 27 +++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/index.js b/index.js index a964561..e9467a3 100644 --- a/index.js +++ b/index.js @@ -6,6 +6,11 @@ function copyBuffer (cur) { return Buffer.from(cur) } + // A DataView takes a byte length, not an element count like a TypedArray. + if (cur instanceof DataView) { + return new DataView(cur.buffer.slice(), cur.byteOffset, cur.byteLength) + } + return new cur.constructor(cur.buffer.slice(), cur.byteOffset, cur.length) } diff --git a/test/index.js b/test/index.js index 5bdd9ac..de4e4db 100644 --- a/test/index.js +++ b/test/index.js @@ -283,6 +283,33 @@ function types (clone, label) { deepEqual(Array.from(cloned.view2), [input2, input3], 'cloned value content is correct') deepEqual(Array.from(cloned.view3), [input1, 0, input2, input3], 'cloned value content is correct') }) + test(`${label} copies a DataView over the full buffer correctly`, async ({ ok, is, isNot }) => { + const input = rnd(1000) + const buffer = new ArrayBuffer(8) + const view = new DataView(buffer) + view.setInt32(0, input) + const cloned = clone({ a: view }).a + ok(cloned instanceof DataView, 'cloned value is instance of DataView') + isNot(cloned, view, 'cloned value is not same as input value') + is(cloned.byteOffset, view.byteOffset, 'byteOffset preserved') + is(cloned.byteLength, view.byteLength, 'byteLength preserved') + is(cloned.getInt32(0), input, 'cloned value content is correct') + }) + test(`${label} copies a partial DataView correctly`, async ({ ok, is, isNot }) => { + const input = rnd(100) + const buffer = new ArrayBuffer(16) + const view = new DataView(buffer, 4, 8) // offset 4, length 8 of a 16-byte buffer + view.setInt32(0, input) + const cloned = clone({ a: view }).a + ok(cloned instanceof DataView, 'cloned value is instance of DataView') + isNot(cloned, view, 'cloned value is not same as input value') + is(cloned.byteOffset, view.byteOffset, 'byteOffset preserved') + is(cloned.byteLength, view.byteLength, 'byteLength preserved') + is(cloned.getInt32(0), input, 'cloned value content is correct') + // Mutating the source must not affect the clone. + view.setInt32(0, input + 1) + is(cloned.getInt32(0), input, 'clone is independent of the source') + }) test(`${label} - maps`, async ({ same, isNot }) => { const map = new Map([['a', 1]]) same(Array.from(clone(map)), [['a', 1]], 'same value')