如何在JavaScript中将ArrayBuffers与DataViews一起使用 [英] How to use ArrayBuffers with DataViews in JavaScript

查看:97
本文介绍了如何在JavaScript中将ArrayBuffers与DataViews一起使用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我见过的关于ArrayBuffer的唯一真正的教程来自

给人一种可以使用ArrayBuffer做到这一点的感觉:

  var x = new ArrayBuffer(10)x [0] = 1x [1] = 0...x [9] = 1 

也就是说,手动设置字节.但是我还没有看到有关此功能的任何文档.相反,似乎您应该使用TypedArray组件之一或DataView:

  var x = new ArrayBuffer(100)var y = new DataView(x)y.setUint32(0,1)console.log(y.getUint32(0))//1console.log(x [0])//未定义 

但是再次看来,在使用DataView操作ArrayBuffer之后,您实际上无法直接访问ArrayBuffer上的任何字节.

尝试使用ArrayBuffer和DataView做其他事情让我感到困惑:

  var x = new ArrayBuffer(100)var y = new DataView(x)y.setUint32(0,1)y.setUint32(1、2)console.log(y.getUint32(0))//0(不正确)console.log(y.getUint32(1))//2(正确)var x = new ArrayBuffer(100)var y = new DataView(x)y.setUint32(0,1)y.setUint32(2,2)console.log(y.getUint32(0))//0(不正确)console.log(y.getUint32(1))//0(?)console.log(y.getUint32(2))//2(正确)var x = new ArrayBuffer(100)var y = new DataView(x)y.setUint32(0,1)y.setUint32(3,2)console.log(y.getUint32(0))//0(不正确)console.log(y.getUint32(1))//0(?)console.log(y.getUint32(2))//0(?)console.log(y.getUint32(3))//2(正确) 

直到最后我得到4个字节,与32个字节的视图对齐.但那就更奇怪了:

  var x = new ArrayBuffer(100)var y = new DataView(x)y.setUint32(0,1)y.setUint32(4,2)console.log(y.getUint32(0))//1console.log(y.getUint32(1))//256console.log(y.getUint32(2))//65536console.log(y.getUint32(3))//16777216console.log(y.getUint32(4))//2 

这告诉我我需要手动将32位值放置在适当的位置,但是我不明白为什么其他值会像256和65536一样出现.

接下来,我希望能够打印出101011100100等字节,整个ArrayBuffer或其中的一部分.

最后,我希望能够编码8、16和32位以外的值,例如base64或4位,或奇数个位.没有通用的DataView API可以执行此操作,例如通用的 y.setUint(bitsCount,offset,value),不确定原因.

总而言之,在处理低级位管理时,我有很多不熟悉的地方.但是,我想学习如何使用它.因此,也许如果可以快速展示如何获得ArrayBuffer + DataView组合的实用知识,那将真的很有帮助.

我了解如何使用Uint8Array和相关的TypedArrays,我只是在尝试学习如何使用较低级别的ArrayBuffer.

解决方案

AFAIK DataView 并不是真正针对您的用途.它基本上是用于解析或创建已知格式的二进制文件并处理字节序问题.特别是它可以处理字节序问题,并且可以读取未对齐的16位和32位值.换句话说,使用 Float32Array 时,由于32位浮点数为4个字节,因此缓冲区的偏移量将是4的倍数.无法在非4字节边界上对齐 Float32Array .使用 DataView ,您可以传入偏移量,并且可以在任何字节边界上进行偏移.

至少在目前,DataView还比同一个API的纯JavaScript实现要慢.

因此,对于您的用例,您可能不想使用 DataView ,而是自己制作.

此外,操作随机大小的位字符串并不是很常见,因此,如果要按位进行读写,则必须编写自己的库.

ArrayBuffer 只是一个缓冲区.您不能直接访问其内容.您必须在该缓冲区中创建一个 ArrayBufferView .有很多不同类型的视图,例如 Int8Array Uint32Array Float32Array DataView .他们可以查看 ArrayBuffer 的全部或一部分.他们还可以创建自己的 ArrayBuffer .

  const view = new Uint32Array(100); 

它与

完全相同

  const view = new Uint32Array(new ArrayBuffer(400)); 

您可以通过其 buffer 属性访问任何视图的 ArrayBuffer .所以这三行

  const缓冲区= new ArrayBuffer(400);const view1 = new Uint32Array(buffer);const view2 = new Uint8Array(buffer); 

与这两行相同

  const view1 = new Uint32Array(100);const view2 = new Uint8Array(view1.buffer); 

第一个示例传递给 DataView 的偏移量以字节为单位,因此

  var x = new ArrayBuffer(100)var y = new DataView(x)y.setUint32(0,1)y.setUint32(1、2)console.log(y.getUint32(0))//0(不正确)console.log(y.getUint32(1))//2(正确) 

第一个 y.setUint32(0,1)设置缓冲区的前4个字节.字节0、1、2和3.第二个 y.setUint32(1,2)设置字节1、2、3、4.因此,您将覆盖字节1、2和3.

  var x = new ArrayBuffer(100)//x是100个字节[[0,0,0,0,0,0,0 ....var y = new DataView(x)y.setUint32(0,1);//默认为big endian//x现在为[0,0,0,1,0,0 ...y.setUint32(1、2)//offset1-+(然后用big endian 2覆盖4个字节)//||||//V V V V//x现在为[0,0,0,0,2,0,... BYTES !!!console.log(y.getUint32(0))//0(不正确)//这将获得前4个字节作为大字节序的Uint32,因此它正在执行////结果= x [0]<<24 + x [1]<<16 + x [2]<<8 + x [3]//0<<24 + 0<<16 + 0<<8 + 0//0 

做最后一个例子

  y.setUint32(0,1)y.setUint32(4,2)//x现在为[0,0,0,1,0,0,0,2,....]console.log(y.getUint32(0))//1//正在读取字节0到3//x [0]<<24 + x [1]<<16 + x [2]<<8 + x [3]//0<<24 + 0<<16 + 0<<8 +1 = 1console.log(y.getUint32(1))//256//正在读取字节1到4//x [1]<<24 + x [2]<<16 + x [3]<<8 + x [4]//0<<24 + 0<<16 +1<<8 + 0 = 256console.log(y.getUint32(2))//65536//正在读取字节2到5//x [2]<<24 + x [3]<<16 + x [4]<<8 + x [5]//0<<24 + 1<<16 + 0<<8 + 0 = 65536console.log(y.getUint32(3))//16777216//正在读取字节3到6//x [3]<<24 + x [4]<<16 + x [5]<<8 + x [6]//1<<24 + 0<<16 + 0<<8 + 0 = 16777216console.log(y.getUint32(4))//2//正在读取字节4至7//x [4]<<24 + x [5]<<16 + x [6]<<8 + x [7]//0<<24 + 0<<16 + 0<<8 + 2 = 2 

对其余部分应用相同的逻辑,并希望解释您的 DataView 问题?

您说过要通过参考MDN图并显示很多0和1来将它们打印为字节,这表示位是什么意思?您可以像这样打印一个数字

  const v = 0x3A;console.log(v.toString(2));  

或填充到8位

  const v = 0x3A;console.log(v.toString(2).padStart(8,'0'));  

要打印整个缓冲区,请假设您有一个 Uint8Array 视图(如果不创建一个视图)

  const v = new Uint8Array([0xDE,0xAD,0xBE,0xEF,0x21]);console.log(asBits(v));函数asBits(a){const nums = [];a.forEach(v => nums.push(v.toString(2).padStart(8,'0')));返回nums.join('');}  

The only real tutorial I have seen for ArrayBuffer is from HTML5Rocks. But I am wondering specifically how to manipulate the individual bytes. For example, this cartoon on ArrayBuffers from Mozilla shows an image of an ArrayBuffer wrapped in a Uint8Array view:

It gives the feeling that you can do this with an ArrayBuffer:

var x = new ArrayBuffer(10)
x[0] = 1
x[1] = 0
...
x[9] = 1

That is, manually setting the bytes. But I haven't seen any documentation on such a feature. Instead, it seems you are supposed to either use one of the TypedArray components, or the DataView:

var x = new ArrayBuffer(100)
var y = new DataView(x)
y.setUint32(0, 1)
console.log(y.getUint32(0)) // 1
console.log(x[0]) // undefined

But again it seems after manipulating the ArrayBuffer with the DataView, you can't actually access any of the bytes on the ArrayBuffer directly.

Trying other things with the ArrayBuffer and DataView I get confused:

var x = new ArrayBuffer(100)
var y = new DataView(x)
y.setUint32(0, 1)
y.setUint32(1, 2)
console.log(y.getUint32(0)) // 0 (incorrect)
console.log(y.getUint32(1)) // 2 (correct)

var x = new ArrayBuffer(100)
var y = new DataView(x)
y.setUint32(0, 1)
y.setUint32(2, 2)
console.log(y.getUint32(0)) // 0 (incorrect)
console.log(y.getUint32(1)) // 0 (?)
console.log(y.getUint32(2)) // 2 (correct)

var x = new ArrayBuffer(100)
var y = new DataView(x)
y.setUint32(0, 1)
y.setUint32(3, 2)
console.log(y.getUint32(0)) // 0 (incorrect)
console.log(y.getUint32(1)) // 0 (?)
console.log(y.getUint32(2)) // 0 (?)
console.log(y.getUint32(3)) // 2 (correct)

Until finally I get to 4 bytes which aligns with the 32 byte view. But then it is even more strange:

var x = new ArrayBuffer(100)
var y = new DataView(x)
y.setUint32(0, 1)
y.setUint32(4, 2)
console.log(y.getUint32(0)) // 1
console.log(y.getUint32(1)) // 256
console.log(y.getUint32(2)) // 65536
console.log(y.getUint32(3)) // 16777216
console.log(y.getUint32(4)) // 2

This tells me that I need to manually place the 32-bit values in the appropriate places, but I don't understand why the other values are coming about like 256 and 65536.

Next, I would like to be able to print out the bytes as 101011100100 etc., the whole ArrayBuffer, or just parts of it.

Finally, I would like to be able to encode values other than 8, 16, and 32 bits, such as base64, or 4 bits, or an odd number of bits. There isn't a generic DataView API for doing that, such as a generic y.setUint(bitsCount, offset, value), not sure why.

To summarize, there is a lot that I am unfamiliar with when it comes to dealing with low-level bit management. However, I would like to learn how to use it. So maybe if one could show quickly how to get a working knowledge of the ArrayBuffer + DataView combination, that would be really helpful.

I understand how to use the Uint8Array and related TypedArrays, I am just trying to learn how to use the lower-level ArrayBuffer.

解决方案

AFAIK DataView isn't really meant for what you're using it for. It's basically meant for parsing or creating a binary file in a known format and dealing with endian issues. In particular it handles endian issues AND it lets you read 16bit and 32bit values that are not aligned. In other words with Float32Array the offset into the buffer will be some multiple of 4 since a 32bit float is 4 bytes. There is no way to align a Float32Array on a non-4 byte boundary. With DataView you pass in the offset and it can by at any byte boundary.

DataView is also known to be slower than pure JavaScript implementations of the same API, at least at the moment.

So, for your use case you probably don't want to be using DataView but rather make your own.

Also, manipulating randomly sized strings of bits is not a very common need so if you want to read and write by bits you're going to have to write your own library.

ArrayBuffer is just a buffer. You can not access its content directly. You have to create an ArrayBufferView into that buffer. There's a bunch of different types of views, like Int8Array, Uint32Array, Float32Array and DataView. They can view all or part of an ArrayBuffer. They also can create their own ArrayBuffer.

const view = new Uint32Array(100);

It's exactly the same as

const view = new Uint32Array(new ArrayBuffer(400));

You can get access to the ArrayBuffer of any view via it's buffer property. So these 3 lines

const buffer = new ArrayBuffer(400);
const view1 = new Uint32Array(buffer);
const view2 = new Uint8Array(buffer);

are the same as these 2 lines

const view1 = new Uint32Array(100);
const view2 = new Uint8Array(view1.buffer);

As for your first example the offset passed to DataView is in bytes so this

var x = new ArrayBuffer(100)
var y = new DataView(x)
y.setUint32(0, 1)
y.setUint32(1, 2)
console.log(y.getUint32(0)) // 0 (incorrect)
console.log(y.getUint32(1)) // 2 (correct)

The first y.setUint32(0, 1) is setting the first 4 bytes of the buffer. Bytes 0, 1, 2, and 3. The second y.setUint32(1, 2) is setting bytes 1, 2, 3, 4. So you're overwriting bytes 1, 2, and 3.

var x = new ArrayBuffer(100)

// x is 100 bytes `[0, 0, 0, 0, 0, 0, 0 ....`

var y = new DataView(x)
y.setUint32(0, 1);  // default is big endian

// x is now [0, 0, 0, 1, 0, 0 ...

y.setUint32(1, 2)

//    offset1 --+ (then 4 bytes over written with big endian 2)
//              |  |  |  |
//              V  V  V  V
// x is now [0, 0, 0, 0, 2, 0, ...  BYTES!!!

console.log(y.getUint32(0)) // 0 (incorrect)

// this gets the first 4 bytes as a big endian Uint32 so it's doing
//
// result = x[0] << 24 + x[1] << 16 + x[2] << 8 + x[3]
//          0    << 24 + 0    << 16 + 0    << 8 + 0
//          0

Doing the last example

y.setUint32(0, 1)
y.setUint32(4, 2)

// x is now [0, 0, 0, 1, 0, 0, 0, 2, ....]

console.log(y.getUint32(0)) // 1

// this is reading the bytes 0 to 3
// x[0] << 24 + x[1] << 16 + x[2] << 8 + x[3]  
// 0    << 24 + 0    << 16 + 0    << 8 + 1    = 1 

console.log(y.getUint32(1)) // 256

// this is reading the bytes 1 to 4
// x[1] << 24 + x[2] << 16 + x[3] << 8 + x[4]  
// 0    << 24 + 0    << 16 + 1    << 8 + 0    = 256

console.log(y.getUint32(2)) // 65536

// this is reading the bytes 2 to 5
// x[2] << 24 + x[3] << 16 + x[4] << 8 + x[5]  
// 0    << 24 + 1    << 16 + 0    << 8 + 0    = 65536

console.log(y.getUint32(3)) // 16777216

// this is reading the bytes 3 to 6
// x[3] << 24 + x[4] << 16 + x[5] << 8 + x[6]  
// 1    << 24 + 0    << 16 + 0    << 8 + 0    = 16777216

console.log(y.getUint32(4)) // 2

// this is reading the bytes 4 to 7
// x[4] << 24 + x[5] << 16 + x[6] << 8 + x[7]  
// 0    << 24 + 0    << 16 + 0    << 8 + 2    = 2

Apply the same logic to the rest and hopefully your DataView issues are explained?

You said you wanted to print them as bytes by referencing the MDN diagram and showing lots of 0s and 1s did you mean bits? You can print a number it bits like this

const v = 0x3A;
console.log(v.toString(2));

or padded to 8bits

const v = 0x3A;
console.log(v.toString(2).padStart(8, '0'));

To print the whole buffer, assuming you have a Uint8Array view (if not make one)

const v = new Uint8Array([0xDE, 0xAD, 0xBE, 0xEF, 0x21]);
console.log(asBits(v));

function asBits(a) {
  const nums = [];
  a.forEach(v => nums.push(v.toString(2).padStart(8, '0')));
  return nums.join('');
}

这篇关于如何在JavaScript中将ArrayBuffers与DataViews一起使用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆