Nodejs的Buffer对象(上)—— API废弃与更新

原创 Lin_Grady 教程 nodejs 131阅读 2018-07-19 13:57:15 举报

前言

一开始全文是基于《Nodejs深入浅出》,但是因为有段年头有些API已经是被废弃的,而且越往下走问题越多,最后只好自从查阅资料从头开始研究,所以这里的demo都是以Nodejs10.0为准。
因为内容实在太多,已经超过20000字限制,而且太长看着太烦了,所以我会分成三篇博文发布,后面发布了会补上跳转地址,分别是:
Nodejs的Buffer对象(上)—— API废弃与更新 (基于官网文档)
Nodejs的Buffer对象(中)—— 基本原理与使用要点(基于nodejs 深入浅出)
Nodejs的Buffer对象(下)—— 源代码实现浅析(基于nodejs源码)

因为靠自己整理输出,如果有哪些遗漏错误希望可以指教一下。

Buffer介绍

[ECMAScript 2015](ES6) 引入 TypedArray 之前,JavaScript 语言没有读取或操作二进制数据流的机制。 Buffer 类被引入作为 Node.js API 的一部分,使其可以在 TCP 流或文件系统操作等场景中处理二进制数据流。

TypedArray 现已被添加进 ES6 中,Buffer 类以一种更优化、更适合 Node.js 用例的方式实现了 Uint8Array API。

Buffer 类的实例类似于整数数组,但 Buffer 的大小是固定的、且在 V8 堆外分配物理内存。 Buffer 的大小在被创建时确定,且无法调整。

Buffer 类在 Node.js 中是一个全局变量,因此无需使用 require('buffer').Buffer。

(摘自Nodejs中文API)

new Buffer()

Node.js v6 之前的版本中,Buffer 实例是通过 Buffer 构造函数创建的,它根据提供的参数返回不同的 Buffer。

Node.js 8.0.0 之前,分配给这种 Buffer 实例的内存是没有初始化的,且可能包含敏感数据。 这种 Buffer 实例随后必须被初始化,可以使用 buf.fill(0) 或写满这个 Buffer。 虽然这种行为是为了提高性能而有意为之的,但开发经验表明,创建一个快速但未初始化的 Buffer 与创建一个慢点但更安全的 Buffer 之间需要有更明确的区分。从 Node.js 8.0.0 开始, Buffer(num) 和 new Buffer(num) 将返回一个初始化内存之后的 Buffer。因为 new Buffer() 的行为会根据所传入的第一个参数的值的数据类型而明显地改变,所以如果应用程序没有正确地校验传给 new Buffer() 的参数、或未能正确地初始化新分配的 Buffer 的内容,就有可能在无意中为他们的代码引入安全性与可靠性问题。

为了使 Buffer 实例的创建更可靠、更不容易出错,各种 new Buffer() 构造函数已被 废弃,并由 Buffer.from()、Buffer.alloc()、和 Buffer.allocUnsafe() 方法替代。

总的来说废弃原因有:
1,内存没有初始化且可能包含敏感数据;
2,引入安全性与可靠性问题;

类方法:Buffer.from()

使用入参较为品类众多。

Buffer.from(array)

返回一个新建的包含所提供的字节数组的副本的 Buffer。


Buffer.from(arrayBuffer[, byteOffset[, length]])

参数描述
arrayBuffer [ArrayBuffer,SharedArrayBuffer]ArrayBuffer 或 SharedArrayBuffer 或 TypedArray 的 .buffer 属性
byteOffset [integer]开始拷贝的索引。默认为 0
length [integer]拷贝的字节数。默认为 arrayBuffer.length - byteOffset

该方法将创建一个 ArrayBuffer 的视图,而不会复制底层内存
例如,当传入一个 TypedArray 实例的 .buffer 属性的引用时,这个新建的 Buffer 会像 TypedArray 那样共享同一分配的内存。


Buffer.from(buffer)

复制传入的 Buffer 实例的数据,并返回一个新的 Buffer 实例。


Buffer.from(string[, encoding])

参数描述
string编码字符串
encoding [string]string 的字符编码。 默认: 'utf8'

返回一个被 string 的值初始化的新的 Buffer 实例。

类方法:Buffer.alloc(size[, fill[, encoding]])

参数描述
size [integer]Buffer长度
fill [string,Buffer,integer]预填充值,默认:0
encoding [string]如果 fill 是字符串,则该值是它的字符编码。 默认:'utf8'

分配一个大小为 size 字节的新建的 Buffer,后期也可以通过Buffer对象的toString()转换编码。

注意:
1,8.2.0新增buffer.constants模块属性,里面有个buffer.constants.MAX_LENGTH属性,意思是单个Buffer实例允许的最大量度,在32位体系结构上,这个值是(2^30)-1 (~1GB)。 在64位体系结构上,这个值是(2^31)-1 (~2GB),也可在buffer.kMaxLength查看该值。
2,现代浏览器遵循 WHATWG 编码标准 将 'latin1' 和 ISO-8859-1 别名为 win-1252。 这意味着当进行例如 http.get() 这样的操作时,如果返回的字符编码是 WHATWG 规范列表中的,则有可能服务器真的返回 win-1252 编码的数据,此时使用 'latin1' 字符编码可能会错误地解码数据。

如果 size 大于 buffer.constants.MAX_LENGTH 或小于 0,则抛出 [RangeError] 错误。

类方法:Buffer.allocUnsafe(size)

和Buffer.alloc()的区别除了没有可选项之外(可以使用fill()填充),以这种方式创建的 Buffer 实例的底层内存是未初始化的。 新创建的 Buffer 的内容是未知的,且可能包含敏感数据。而size的注意事项和Buffer.alloc()一样。

Buffer 模块会预分配一个大小为 Buffer.poolSize(默认:8192,用于决定预分配的、内部 Buffer 实例池的大小的字节数,可修改) 的内部 Buffer 实例作为快速分配池, 用于使用 Buffer.allocUnsafe() 新创建的 Buffer 实例,以及废弃的 new Buffer(size) 构造器, 仅限于当 size 小于或等于 Buffer.poolSize >> 1 (即4096,不清楚的下面会提到)。

对这个预分配的内部内存池的使用,是调用 Buffer.alloc(size, fill) 和 Buffer.allocUnsafe(size).fill(fill) 的关键区别。 具体地说,Buffer.alloc(size, fill) 永远不会使用这个内部的 Buffer 池,但如果 size 小于或等于 Buffer.poolSize 的一半, Buffer.allocUnsafe(size).fill(fill) 会使用这个内部的 Buffer 池。 当应用程序需要 Buffer.allocUnsafe() 提供额外的性能时,这个细微的区别是非常重要的。

>> 和 >>> 计算符

(>>):数值除以2的幂函数后的最大整数值

(>>>):数值减半向下整取

Buffer.allocUnsafeSlow(size) 替代 SlowBuffer 类

(目测用法事项都一个样,可能只是觉得没必要单独弄个类所以把方法并入Buffer类里吧,底层实现有没变就不清楚了)

分配一个大小为 size 字节的新建的 Buffer 。 如果 size 大于 buffer.constants.MAX_LENGTH(上面Buffer.alloc(size[, fill[, encoding]])说过,不记得回顾一下) 或小于 0,则抛出 [RangeError] 错误。

以这种方式创建的 Buffer 实例的底层内存是未初始化的。 新创建的 Buffer 的内容是未知的,且可能包含敏感数据。 可以使用 buf.fill(0) 初始化 Buffer 实例为0。

当使用 Buffer.allocUnsafe() 分配新建的 Buffer 时,当分配的内存小于 4KB 时,默认会从一个单一的预分配的 Buffer 切割出来。 这使得应用程序可以避免垃圾回收机制因创建太多独立分配的 Buffer 实例而过度使用。 这个方法通过像大多数持久对象一样消除追踪与清理的需求,改善了性能与内存使用。

当然,在开发者可能需要在不确定的时间段从内存池保留一小块内存的情况下,使用 Buffer.allocUnsafeSlow() 创建一个非池的 Buffer 实例然后拷贝出相关的位元是合适的做法。

(官网搬下来,没跑过这段代码)

Buffer.allocUnsafeSlow() 应当仅仅作为开发者已经在他们的应用程序中观察到过度的内存保留之后的终极手段使用。

对比

区别fromallocallocUnsafeallocUnsafeSlow
入参类型array,arrayBuffer,Buffer,stringsizesizesize
返回返回新建包含所提供入参副本的 Buffer返回指定大小被填满的 Buffer 。 速度较慢但可确保不包含敏感数据。返回指定大小被填满的 Buffer,内存未初始化且可能包含敏感数据返回指定大小被填满的 Buffer,内存未初始化且可能包含敏感数据

区别

Buffer 模块会预分配一个大小为 Buffer.poolSize 的内部 Buffer 实例作为快速分配池

  • Buffer.alloc() 永远不会使用这个内部的 Buffer 池;
  • Buffer.allocUnsafe() 在size小于或等于 Buffer.poolSize 的一半时会使用Buffer 池;
  • Buffer.allocUnsafeSlow() 创建一个非池的 Buffer 实例;

类方法:Buffer.concat(list[, totalLength])

参数描述
list [Array]要合并的 Buffer 或 Uint8Array 实例的数组
totalLength [integer]合并时 list 中 Buffer 实例的总长度

返回一个合并了 list 中所有 Buffer 实例的新建的 Buffer 。

  • 如果 list 中没有元素、或 totalLength 为 0 ,则返回一个新建的长度为 0 的 Buffer 。
  • 如果没有提供 totalLength ,则从 list 中的 Buffer 实例计算得到。 为了计算 totalLength 会导致需要执行额外的循环,所以提供明确的长度会运行更快。
  • 如果提供了 totalLength,totalLength 必须是一个正整数。如果从 list 中计算得到的 Buffer 长度超过了 totalLength,则合并的结果将会被截断为 totalLength 的长度。

Buffer.concat()封装了小Buffer对象向大Buffer对象的复制过程,源码如下:

参考资料

Node.js v10.6.0 文档
nodejs 深入浅出
node/lib/buffer.js

评论 ( 0 )
最新评论
暂无评论

赶紧努力消灭 0 回复