如何在 JavaScript 中将 ArrayBuffers 与 DataView 一起使用
How to use ArrayBuffers with DataViews in JavaScript
我看到的关于 ArrayBuffer 的唯一真实教程来自 HTML5Rocks. But I am wondering specifically how to manipulate the individual bytes. For example, this cartoon,Mozilla 的 ArrayBuffers 显示了包裹在 Uint8Array 视图中的 ArrayBuffer 的图像:
给人的感觉是你可以用 ArrayBuffer 做到这一点:
var x = new ArrayBuffer(10)
x[0] = 1
x[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)) // 1
console.log(x[0]) // undefined
但在使用 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 (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)
直到最后我得到与 32 字节视图对齐的 4 字节。但接下来就更奇怪了:
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
这告诉我需要手动将 32 位值放在适当的位置,但我不明白为什么会出现其他值,例如 256 和 65536。
接下来,我希望能够将字节打印为 101011100100 等,整个 ArrayBuffer,或者只是它的一部分。
最后,我希望能够对 8、16 和 32 位以外的值进行编码,例如 base64,或 4 位,或奇数位。没有用于执行此操作的通用 DataView API,例如通用 y.setUint(bitsCount, offset, value)
,不知道为什么。
总而言之,在处理低级位管理时,有很多我不熟悉的地方。但是,我想学习如何使用它。因此,如果有人可以快速展示如何获得 ArrayBuffer + DataView 组合的工作知识,那将非常有帮助。
我了解如何使用 Uint8Array 和相关的 TypedArray,我只是想学习如何使用较低级别的 ArrayBuffer。
AFAIK DataView
并不真正适合您使用它的目的。它基本上用于解析或创建已知格式的二进制文件并处理字节序问题。特别是它处理字节顺序问题,它允许您读取未对齐的 16 位和 32 位值。换句话说,Float32Array
缓冲区中的偏移量将是 4 的倍数,因为 32 位浮点数是 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
。所以这 3 行
const buffer = 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 (incorrect)
console.log(y.getUint32(1)) // 2 (correct)
第一个 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 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
做最后一个例子
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
对其余部分应用相同的逻辑,希望您的 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));
function asBits(a) {
const nums = [];
a.forEach(v => nums.push(v.toString(2).padStart(8, '0')));
return nums.join('');
}
我看到的关于 ArrayBuffer 的唯一真实教程来自 HTML5Rocks. But I am wondering specifically how to manipulate the individual bytes. For example, this cartoon,Mozilla 的 ArrayBuffers 显示了包裹在 Uint8Array 视图中的 ArrayBuffer 的图像:
给人的感觉是你可以用 ArrayBuffer 做到这一点:
var x = new ArrayBuffer(10)
x[0] = 1
x[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)) // 1
console.log(x[0]) // undefined
但在使用 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 (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)
直到最后我得到与 32 字节视图对齐的 4 字节。但接下来就更奇怪了:
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
这告诉我需要手动将 32 位值放在适当的位置,但我不明白为什么会出现其他值,例如 256 和 65536。
接下来,我希望能够将字节打印为 101011100100 等,整个 ArrayBuffer,或者只是它的一部分。
最后,我希望能够对 8、16 和 32 位以外的值进行编码,例如 base64,或 4 位,或奇数位。没有用于执行此操作的通用 DataView API,例如通用 y.setUint(bitsCount, offset, value)
,不知道为什么。
总而言之,在处理低级位管理时,有很多我不熟悉的地方。但是,我想学习如何使用它。因此,如果有人可以快速展示如何获得 ArrayBuffer + DataView 组合的工作知识,那将非常有帮助。
我了解如何使用 Uint8Array 和相关的 TypedArray,我只是想学习如何使用较低级别的 ArrayBuffer。
AFAIK DataView
并不真正适合您使用它的目的。它基本上用于解析或创建已知格式的二进制文件并处理字节序问题。特别是它处理字节顺序问题,它允许您读取未对齐的 16 位和 32 位值。换句话说,Float32Array
缓冲区中的偏移量将是 4 的倍数,因为 32 位浮点数是 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
。所以这 3 行
const buffer = 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 (incorrect)
console.log(y.getUint32(1)) // 2 (correct)
第一个 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 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
做最后一个例子
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
对其余部分应用相同的逻辑,希望您的 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));
function asBits(a) {
const nums = [];
a.forEach(v => nums.push(v.toString(2).padStart(8, '0')));
return nums.join('');
}