#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. В итоге я получил ваш ответ по последним частям.