Offset is outside the bounds of the DataView, the debugger shows it is inside the bounds

Tags: ,



I get the error Offset is outside the bounds of the DataView for the following code

let data = [...] // some array of Int16
let buf = new ArrayBuffer(data.length);
let dataView = new DataView(buf);

data.forEach((b, i) => {
   dataView.setInt16(i, b);
});

Here is the debug view in Chrome

enter image description here

You can see that i is 47999 and the buffer size of my DataView is 48000. What am I missing here?

Answer

This is because an Int16Array has a 2 bytes per element. So its .length will be twice smaller than its buffer’s actual size, use its .byteLength instead to create a new ArrayBuffer of the same size.
Also, setting an int16 will actually set two bytes at a time.

So at some point, your loop will try to set a byte that doesn’t exist, and it will throw that error.

But that’s not all with your code. Since forEach()‘s iteration value i is based on the .length value of the TypedArray, you also need to multiply it by the TypedArray’s bytes per element to set a correct offset in DataView.setInt16.

const data = new Int16Array( [ 0xFFFF, 0xFF00, 0x00FF, 0x000 ] );

console.log( "length:", data.length );
console.log( "byteLength:", data.byteLength );

const buf = new ArrayBuffer(data.byteLength);
const dataView = new DataView(buf);

data.forEach( (b, i) => {
  dataView.setInt16( i * data.BYTES_PER_ELEMENT, b );
} );
console.log( new Int16Array( buf ) );
// -1, 255, -256, 0

Now, I’m not sure what you were wanting to do with this snippet, but to make a copy of your TypedArray, then you’d have to check for the endianness of the computer and then use the third parameter of DataView.setInt16( byteOffset, value, littleEndian ), but you could also simply do:

const data = new Int16Array( [ 0xFFFF, 0xFF00, 0x00FF, 0x000 ] );
const buf = data.buffer.slice();

// ensure they are not the same ArrayBuffer
data.fill( 0 );
console.log( "data: ", data ); // 0, 0, 0 ,0
console.log( "copy:", new Int16Array( buf ) );
// -1, 256, 255, 0

If you wanted to swap from little endian to big endian, then you could also make it way faster than using a DataView by first checking the computer’s endianness and swapping the values using .map if necessary.

const data = new Int16Array( [ 0xFFFF, 0xFF00, 0x00FF, 0x000 ] );
// check for the computer's endianness
const is_little_endian = new Uint8Array( new Uint32Array( [ 0x12345678 ] ).buffer )[ 0 ] === 0x78;
console.log( is_little_endian );

const buf = is_little_endian ?
  data.map( (val) => (val<<8) | (val>>8) & 0xFF ).buffer : data.buffer.slice();

// ensure they are not the same ArrayBuffer
data.fill( 0 );
console.log( "data: ", data ); // 0, 0, 0 ,0
console.log( "copy:", new Int16Array( buf ) );
// -1, 255, -256, 0


Source: stackoverflow