将存储为节点缓冲区的字节数组创建一个类型化数组。

9

关于从缓冲区创建类型化数组的问题,来自节点 docs

缓冲区的内存被解释为一个数组,而不是字节数组。也就是说,new Uint32Array(new Buffer([1,2,3,4])) 创建了一个包含元素 [1,2,3,4] 的 4 元素 Uint32Array,而不是一个只有单个元素 [0x1020304][0x4030201]Uint32Array

这与普通 JavaScript 不同,在其中从 ArrayBuffer 创建类型化数组视图会将 ArrayBuffer 的内存作为字节使用(类似于 C++ 中的 reinterpret_cast)。在操作节点缓冲区时,我需要这种行为。

我可以将Buffer转换为ArrayBuffer,但这对我的应用程序来说太慢了。(我尝试过许多方法--但它们都是O(n)时间。)(编辑: 我找到的最快方法是this,它是一个单独的memmove操作,非常快,但仍然至少有瞬时2倍的内存消耗,直到对原始缓冲区的引用被释放。) 有没有一种(快速/O(1))的方法可以从Buffer中获取一个类型化数组,使用Buffer的内容作为字节而不是元素?(需要的类型化数组元素大小大于1个字节,不用说。)

这个数组应该进行什么类型的操作?如果是只读的,为什么不通过偏移量直接引用缓冲区中的元素呢?例如:buf.readUInt32LE(i*4)其中i是数组索引? - stdob--
1
我需要快速而重复地遍历数组。read* 方法非常慢。 - ZachB
我认为没有更快的方法:如果我们需要索引“i”的Int32值:var k = i*4; return buf[k] | buf[k+1]<<8 | buf[k+2]<<16 | buf[k+3]<<256; - stdob--
我发明了一辆自行车:如果我们看一下buffer.js模块 - 这正是实现读取的方式。 - stdob--
2个回答

7
据我所知,在不将数据复制到内存中的情况下是不可能做到这一点的。 即使您的示例new Uint32Array(new Buffer([1,2,3,4]))也是如此(这意味着它不是O(1))。
请注意,类型化数组只是ArrayBuffer的视图(不是Buffer,这是不可能的)。new Uint32Array(array)创建一个大小为4 * array.length字节的ArrayBuffer。您可以使用uint32Array.buffer访问它。构造函数对待您的Buffer与普通Array没有区别。
我知道的最好的解决方案是您已经找到的方案
使用Uint32Array来尝试做这件事的另一个问题是它取决于平台的字节顺序。您可以像这样迭代Buffer,或者如果您想要安全,则使用DataView

3

从node 4.0开始,缓存区(Buffers)是Uint8Arrays,并且可以直接在其上构建新的视图:

var b = new Buffer([1,2,3,4]);
new Uint32Array(b.buffer, b.byteOffset);

这个小例子假设b比 ~4096(缓冲区拥有自己的内存块与共享较大块之间的阈值)更大。有关更多信息,请参见将二进制NodeJS缓冲区转换为JavaScript ArrayBuffer - 通常应调用b.slice()仅操作您拥有的内存。

警告:new Uint32Array(b.buffer, b.byteOffset).length -> 2046 不适用。你是正确的——它可以直接构造,但是"作为uint8array"不等同于bytes。 - shaunc
@shaunc 它确实可以工作,看一下结果的内容。意外的长度是 Node 的共享数组缓冲区分配器的副作用。当您的缓冲区较小或者为了安全起见总是使用 b.slice - ZachB
new Uint32Array(b.buffer, b.byteOffset).slice(0,4) -> Uint32Array [ 67305985, 0, 8192, 0 ]?如果我将两个“b”都重写为b.slice(0,4),结果会相同吗?(注:node 9.3.0)哎呀...我猜是对的:)抱怨...在您编辑之前,不允许我通过点赞来更正我的错误--任何编辑都可以,我会点赞的。抱歉。 - shaunc
没问题,已澄清。干杯。 - ZachB

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接