stringify assumes typed arrays own their own buffer
Opened this issue · 0 comments
tl;dr: Typed arrays can share a backing store, meaning that their buffer
does not have a one-to-one correspondence with their "actual" contents, contrary to stringify's assumption.
The code
const a = Uint8Array.from([1, 2, 3, 4, 5, 6])
const b = a.subarray(0, 3)
const c = a.subarray(3, 6)
produces three arrays which all share an ArrayBuffer. b
and c
see different slices of a
, and appear to be 3 elements long.
Because stringify(b)
and stringify(c)
don't pay attention to the offset and length of the array within the buffer, stringifying every array produces [["Uint8Array","AQIDBAUG"]]
, which parse
s back into the original 6-element array.
This poses a big problem for my use case, where I'm using a library which loads data into an arraybuffer and then parses it into subarrays to avoid making lots of copies, but I'm also using devalue, via Nuxt, to pass the data back to the browser. The end result is that when they arrive in the browser all the arrays have turned back into the top-level buffer.
uneval(a)
produces new Uint8Array([1,2,3])
which isn't quite right, but is much less surprising, and also works for me.
My current workaround is doing eval(uneval(a))
before handing the data over to Nuxt.
Additionally, it isn't an issue for my use case, but Rich has said that a goal of devalue is that after passing data through it, you get exactly the same structure back. Neither uneval nor stringify quite do that here because they don't handle the buffer being shared between arrays. e.g. after a[0] = 42
, b[0]
should be 42
as well, but this is not the case if the values are passed through eval(uneval({a, b, c}))
, because the arrays no longer share their backing store.