2015-06-05 2 views
0

У меня есть ArrayBuffer данных, который состоит из Uint8s. Каждый из них находится в сетевом порядке байтов (big-endian). Моя цель состоит в том, чтобы преобразовать каждый Uint8 в порядок little-endian, но затем поместить их обратно в ArrayBuffer.Магазин типизированного массива в ArrayBuffer

Я знаю, что могу легко отделить отдельные Uints с помощью массива напечатанный, например, так:

var uintArr = new Uint8Array(ArrayBuffer); 

Оттуда я могу поменять порядка байтов каждого элемента, и есть массив прямой порядок байтов Uint8s.

Я не знаю, как вернуть этот массив в массив ArrayBuffer.

Есть ли способ сделать это в Javascript?

ответ

2

ArrayBuffer - это общий массив байтов для всех видов. Типированный массив просто означает, что в массиве есть связанный тип, такой как uint8, int16 и т. Д. Все Uin8Array, Int32Array и т. Д. Являются видами поверх ArrayBuffer, чтобы включить чтение и запись в том виде, который они представляют.

(U) В массивах int8 нет байтового порядка, так как они являются одиночными байтами (т. Е. Ничего не изменять). Байты должны представлять что-то более широкое, такое как (u) int-16 или -32 (64-битные целые числа не поддерживаются до ES7, но у вас есть 32-битные и 64-битные поплавки IEEE).

Все, что вы читаете или записываете через представления, заканчивается в том же массиве ArrayBuffer, на который они указывают, - вы можете даже иметь несколько видов для одного и того же буфера.

поменять заказ на 16-бит, вы можете просто прочитать ArrayBuffer с помощью представления Uint16Array и подкачку вручную:

var buffer16 = new Uint16Array(buffer); // use same buffer as for the Uin8Array view 

for(var i = 0, v; i < buffer16.length; i++) { 
    v = buffer16[i]; 
    buffer16[i] = ((v & 0xff) << 8) | ((v & 0xff00) >>> 8); // mask and shift the two bytes 
} 

Если вы имели вид 8-битный для того же самого буфера теперь вы можете прочитать отдельные байты с новым порядком.

для 32-бита вы могли бы сделать:

var buffer32 = new Uint32Array(buffer); 

for(var i = 0, v; i < buffer32.length; i++) { 
    v = buffer32[i]; 
    buffer32[i] = ((v & 0xff) << 24) |  // mask, move byte 0 to 3 
       ((v & 0xff00) << 8) |  // mask, move byte 1 to 2 
       ((v & 0xff0000) >>> 8) | // mask, move byte 2 to 1 unsigned 
       ((v & 0xff000000) >>> 24); // mask, move byte 3 to 0 unsigned 
} 

Однако эти виды требуют выровненных буферов, означающих длину буфера для (и) Int16 должны быть выровнены для четных длин, в то время как 32 бита должен быть выравнивается до 4 байтов.

Если буфер не вы можете вместо этого использовать DataView, чтобы читать и писать любые длины, в любом положении, за счет некоторой производительности:

var view = new DataView(buffer); 
var alignedLength = ((buffer.length/2)|0) * 2; // aligned to 16-bit 

// 16-bit 
for(var i = 0, v; i < alignedLength; i += 2) { 
    v = view.getUint16(i, false); // read as big-endian 
    view.setUint16(i, v, true); // write as little-endian 
} 

и 32 бита используют getUint32/setUint32 вместо этого, приращение i с 4. Обратите внимание, что вы должны «следить за» последним чтением, если ArrayBuffer не выровнен с 2 или 4 байтами (обычно вы должны выровнять длину buffer.length и использовать это в цикле, как показано, использовать 4 для 32-битных).

Если ваш исходный буфер содержит смешанные длины (например, если он содержит необработанный двоичный файл), вам придется анализировать каждое значение в соответствии со спецификацией формата файла. Для этого используйте DataView.

Примеры

var buffer = new ArrayBuffer(4), 
 
    b8 = new Uint8Array(buffer), 
 
    b16 = new Uint16Array(buffer), 
 
    b32 = new Uint32Array(buffer), 
 
    view = new DataView(buffer); 
 

 
setData(); 
 
show("Original unsigned big-endian"); 
 

 

 
// swap the value using 16-bit array 
 
for(var i = 0, v; i < b16.length; i++) { 
 
    v = b16[i]; 
 
    b16[i] = ((v & 0xff) << 8) | ((v & 0xff00) >>> 8); 
 
} 
 
show("Byte-order swapped 16-bits"); 
 

 
setData(); 
 
for(var i = 0, v; i < b32.length; i++) { 
 
    v = b32[i]; 
 
    b32[i] = ((v & 0xff) << 24) | 
 
      ((v & 0xff00) << 8) | 
 
      ((v & 0xff0000) >>> 8) | 
 
      ((v & 0xff000000) >>> 24); 
 
} 
 
show("Byte-order swapped 32-bits"); 
 

 
setData(); 
 
for(var i = 0, v; i < buffer.byteLength; i += 2) { 
 
    v = view.getUint16(i, false); // big-endian 
 
    view.setUint16(i, v, true); // little-endian 
 
} 
 
show("Byte-order swapped 16-bit using DataView"); 
 

 
setData(); 
 
for(var i = 0, v; i < buffer.byteLength; i += 4) { 
 
    v = view.getUint32(i, false); // big-endian 
 
    view.setUint32(i, v, true); // little-endian 
 
} 
 
show("Byte-order swapped 32-bit using DataView"); 
 

 

 
function valToHex(v) { 
 
    return (v>>>0).toString(16) 
 
} 
 

 
function setData() { 
 
    b8[0] = 255; // "network" order/big.endian 0xff804020 
 
    b8[1] = 128; 
 
    b8[2] = 64; 
 
    b8[3] = 32; 
 
} 
 

 
function show(pre) { 
 
    document.write(pre + ": "); 
 
    document.write("0x" + valToHex(b8[0])); 
 
    document.write(valToHex(b8[1])); 
 
    document.write(valToHex(b8[2])); 
 
    document.write(valToHex(b8[3]) + "<br>"); 
 
}
body {font:16px monospace}

+0

лол я действительно не думал.Конечно, Uint8s - всего лишь один байт. Спасибо за подробный и тщательный ответ. – user2871915

Смежные вопросы