Смещение находится за пределами DataView, отладчик показывает, что оно находится внутри границ

#javascript #arraybuffer

#javascript #arraybuffer

Вопрос:

Я получаю сообщение об ошибке Offset is outside the bounds of the DataView для следующего кода

 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);
});
  

Вот отладочный вид в Chrome

введите описание изображения здесь

Вы можете видеть, что это i есть 47999 , а размер моего буфера DataView равен 48000 . Чего я здесь не понимаю?

Комментарии:

1. Вы пробовали, const buf = new ArrayBuffer(16); как это здесь developer.mozilla.org/en-US/docs/Web/JavaScript/Reference /… в первом примере?

Ответ №1:

Это потому, что Int16Array имеет 2 байта на элемент. Таким образом, его .length размер будет в два раза меньше фактического размера его буфера, используйте его .byteLength вместо этого для создания нового ArrayBuffer того же размера.
Кроме того, установка int16 фактически установит два байта за раз.

Итак, в какой-то момент ваш цикл попытается установить байт, который не существует, и он выдаст эту ошибку.

Но это еще не все с вашим кодом. Поскольку forEach() значение итерации i основано на .length значении TypedArray, вам также необходимо умножить его на байты TypedArray на элемент, чтобы установить правильное смещение в 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  

Теперь я не уверен, что вы хотели сделать с этим фрагментом, но чтобы создать копию вашего TypedArray, вам пришлось бы проверить порядковый номер компьютера, а затем использовать третий параметр DataView.setInt16( byteOffset, value, littleEndian ) , но вы также могли бы просто сделать:

 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  

Если вы хотите переключиться с младшего порядкового номера на большой порядковый номер, то вы также могли бы сделать это намного быстрее, чем при использовании DataView, сначала проверив порядковый номер компьютера и при необходимости поменяв местами значения с помощью .map . При необходимости.

 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) amp; 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  

Комментарии:

1. Спасибо, это очень понятно. Я пропустил точку byteLength

2. Пожалуйста, я только что заметил, что я также пропустил один момент в вашем коде, вы специально опустили третий параметр setInt16 , чтобы он был установлен как BigEndian? В этом случае простой slice() фрагмент не будет делать то же самое.

3. На самом деле это своего рода проблема X, Y. В итоге я получил ваш ответ по последним частям.