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.
2.5 KiB
Fix DataView clones getting the wrong byteLength
Problem
Cloning a DataView that covers only part of its underlying ArrayBuffer
produces a clone with the wrong byteLength: the copy spans from the original
byteOffset to the end of the buffer instead of keeping the original length.
const clone = require('rfdc')()
const buffer = new ArrayBuffer(16)
const view = new DataView(buffer, 4, 8) // offset 4, length 8
const copy = clone({ a: view }).a
copy.byteLength // 12 (expected 8)
The value at the start of the view is copied correctly and the clone is independent of the source, but the clone can now read 4 bytes past the end of the original view.
Root cause
index.js, in copyBuffer:
return new cur.constructor(cur.buffer.slice(), cur.byteOffset, cur.length)
This path handles every ArrayBuffer.isView value, i.e. both TypedArrays and
DataView. It is correct for TypedArrays, whose third constructor argument is an
element count matching .length. A DataView is different:
- it has no
.lengthproperty, socur.lengthisundefined; - its third constructor argument is a byte length, not an element count.
new DataView(buffer, byteOffset, undefined) extends the view to the end of the
buffer, so a partial view's length is lost. (A DataView over the whole buffer
happens to work, because "to the end of the buffer" equals its byteLength.)
Fix
Handle DataView explicitly and size it with byteLength:
if (cur instanceof DataView) {
return new DataView(cur.buffer.slice(), cur.byteOffset, cur.byteLength)
}
TypedArrays keep using the existing .length-based path.
Test
Added tests (run for all four rfdc variants via the shared types() helper)
that clone a full-buffer DataView and a partial DataView, asserting byteOffset,
byteLength, the stored value, and independence from the source.
Counterfactual (revert the copyBuffer DataView branch): the partial-DataView
test fails with
not ok - byteLength preserved
is(cloned.byteLength, view.byteLength, 'byteLength preserved')
(expected 8, got 12), while the full-buffer case still passes.
Gate
- Tests:
node test/index.js->1..144, 144 ok, 0 failing, exit 0. The repository'snpm testinvokes thetapCLI, whose bundledesmloader crashes on load under Node 22 (Function.prototype.apply was called on undefined) independently of this change; running the test file directly executes the same assertions. - Lint:
standard index.js test/index.js-> exit 0.