Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions cjs/deserialize.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,9 @@ const deserializer = ($, _) => {
case 'ArrayBuffer':
return as(new Uint8Array(value).buffer, value);
case 'DataView': {
const { buffer } = new Uint8Array(value);
return as(new DataView(buffer), value);
const {buffer, byteOffset, byteLength} = Array.isArray(value) ?
{buffer: value, byteOffset: 0, byteLength: value.length} : value;
return as(new DataView(new Uint8Array(buffer).buffer, byteOffset, byteLength), value);
}
case '-0': return -0;
}
Expand Down
5 changes: 4 additions & 1 deletion cjs/serialize.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,10 @@ const serializer = (strict, json, $, _) => {
if (type) {
let spread = value;
if (type === 'DataView') {
spread = new Uint8Array(value.buffer);
const {buffer, byteOffset, byteLength} = value;
return as([type, {
buffer: [...new Uint8Array(buffer)], byteOffset, byteLength
}], value);
}
else if (type === 'ArrayBuffer') {
spread = new Uint8Array(value);
Expand Down
5 changes: 3 additions & 2 deletions esm/deserialize.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,9 @@ const deserializer = ($, _) => {
case 'ArrayBuffer':
return as(new Uint8Array(value).buffer, value);
case 'DataView': {
const { buffer } = new Uint8Array(value);
return as(new DataView(buffer), value);
const {buffer, byteOffset, byteLength} = Array.isArray(value) ?
{buffer: value, byteOffset: 0, byteLength: value.length} : value;
return as(new DataView(new Uint8Array(buffer).buffer, byteOffset, byteLength), value);
}
case '-0': return -0;
}
Expand Down
5 changes: 4 additions & 1 deletion esm/serialize.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,10 @@ const serializer = (strict, json, $, _) => {
if (type) {
let spread = value;
if (type === 'DataView') {
spread = new Uint8Array(value.buffer);
const {buffer, byteOffset, byteLength} = value;
return as([type, {
buffer: [...new Uint8Array(buffer)], byteOffset, byteLength
}], value);
}
else if (type === 'ArrayBuffer') {
spread = new Uint8Array(value);
Expand Down
2 changes: 1 addition & 1 deletion structured-json.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

51 changes: 50 additions & 1 deletion test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,55 @@ const assert = (expected, result, message = '') => {
}
};

{
const buffer = new Uint8Array([10, 20, 30, 40]).buffer;
for (const [offset, length] of [[0, 2], [1, 2], [2, 2], [3, 0], [4, 0], [0, 0], [0, 4]]) {
const original = new DataView(buffer, offset, length);
const [record] = serialize(original);
assert(record.length, 2, 'keeps the type/value pair contract');
assert(JSON.stringify(record), JSON.stringify(['DataView', {
buffer: [10, 20, 30, 40], byteOffset: offset, byteLength: length
}]), 'uses named DataView fields for every view');
const native = typeof globalThis.structuredClone === 'function' ?
globalThis.structuredClone(original) : original;
for (const result of [
deserialize(serialize(original)),
deserialize(JSON.parse(JSON.stringify(serialize(original)))),
parse(stringify(original)),
structuredClone(original, {lossy: true})
]) {
assert(result instanceof DataView, true);
assert(result.byteOffset, native.byteOffset, 'preserves DataView byteOffset');
assert(result.byteLength, native.byteLength, 'preserves DataView byteLength');
assert(result.buffer.byteLength, native.buffer.byteLength);
assert([...new Uint8Array(result.buffer)].join(','), [...new Uint8Array(native.buffer)].join(','));
for (let i = 0; i < length; i++)
assert(result.getUint8(i), native.getUint8(i));
let outOfBounds = false;
try {
result.getUint8(length);
}
catch (error) {
outOfBounds = error instanceof RangeError;
}
assert(outOfBounds, true, 'keeps reads outside the DataView out of bounds');
if (length) {
result.setUint8(0, 255);
assert(original.getUint8(0), native.getUint8(0), 'does not share the source buffer');
}
}
}
for (const bytes of [[], [10, 20, 30, 40]]) {
const legacy = deserialize([['DataView', bytes]]);
assert(legacy.byteOffset, 0);
assert(legacy.byteLength, bytes.length);
assert([...new Uint8Array(legacy.buffer)].join(','), bytes.join(','));
}
const empty = deserialize(serialize(new DataView(new ArrayBuffer(0))));
assert(empty.byteOffset, 0);
assert(empty.byteLength, 0);
}

// @see https://github.com/ungap/structured-clone/issues/4
const withUndefined = stringify({foo: 'test', bar: undefined, foobar: null});
assert(withUndefined, '[[2,[[1,2],[3,4],[5,6]]],[0,"foo"],[0,"test"],[0,"bar"],[-1],[0,"foobar"],[0,null]]');
Expand Down Expand Up @@ -66,7 +115,7 @@ function test(firstRun = false) {
console.timeEnd('serialized in');

assert(JSON.stringify(serialized), [
`[[2,[[1,2],[3,4],[5,6],[7,8],[9,10],[11,12],[13,14],[15,16],[17,18],[19,20],[21,22],[24,25],[27,28],[29,30],[31,32],[33,34],[35,36],[37,38],[39,40]]],[0,"arr"],[1,[0,0,0]],[0,"bigint"],[8,"1"],[0,"boolean"],[0,true],[0,"number"],[0,123],[0,"string"],[0,""],[0,"undefined"],[-1],[0,"null"],[0,null],[0,"buffer"],["ArrayBuffer",[0,1,2,3,4,5,6,7]],[0,"dataview"],["DataView",[0,1,2,3,4,5,6,7]],[0,"int"],["Uint32Array",[1,2,3]],[0,"map"],[5,[[23,8]]],[0,"a"],[0,"set"],[6,[23,26]],[0,"b"],[0,"Bool"],["Boolean",false],[0,"Num"],["Number",0],[0,"Str"],["String",""],[0,"re"],[4,{"source":"test","flags":"gim"}],[0,"error"],[7,{"name":"Error","message":"test"}],[0,"BI"],["BigInt","1"],[0,"date"],[3,"${date.toISOString()}"]]`
`[[2,[[1,2],[3,4],[5,6],[7,8],[9,10],[11,12],[13,14],[15,16],[17,18],[19,20],[21,22],[24,25],[27,28],[29,30],[31,32],[33,34],[35,36],[37,38],[39,40]]],[0,"arr"],[1,[0,0,0]],[0,"bigint"],[8,"1"],[0,"boolean"],[0,true],[0,"number"],[0,123],[0,"string"],[0,""],[0,"undefined"],[-1],[0,"null"],[0,null],[0,"buffer"],["ArrayBuffer",[0,1,2,3,4,5,6,7]],[0,"dataview"],["DataView",{"buffer":[0,1,2,3,4,5,6,7],"byteOffset":0,"byteLength":8}],[0,"int"],["Uint32Array",[1,2,3]],[0,"map"],[5,[[23,8]]],[0,"a"],[0,"set"],[6,[23,26]],[0,"b"],[0,"Bool"],["Boolean",false],[0,"Num"],["Number",0],[0,"Str"],["String",""],[0,"re"],[4,{"source":"test","flags":"gim"}],[0,"error"],[7,{"name":"Error","message":"test"}],[0,"BI"],["BigInt","1"],[0,"date"],[3,"${date.toISOString()}"]]`
].join(','));

// firstRun && console.log(serialized);
Expand Down