欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

NodeJS模块Buffer原理及使用方法解析

程序员文章站 2022-04-09 12:04:51
buffer 作为 nodejs 中重要的概念和功能,为开发者提供了操作二进制的能力。本文记录了几个问题,来加深对 buffer 的理解和使用: 认识缓冲器 如何申请堆外内存 如何计算字节长度...

buffer 作为 nodejs 中重要的概念和功能,为开发者提供了操作二进制的能力。本文记录了几个问题,来加深对 buffer 的理解和使用:

  • 认识缓冲器
  • 如何申请堆外内存
  • 如何计算字节长度
  • 如何计算字节长度
  • 如何转换字符编码
  • 理解共享内存与拷贝内存

认识 buffer(缓冲器)

buffer 是 nodejs 核心 api,它提供我们处理二进制数据流的功能。buffer 的使用和 es2017 的 uint8array 非常相似,但由于 node 的特性,专门提供了更深入的 api。

uint8array 的字面意思就是:8 位无符号整型数组。一个字节是 8bit,而字节的表示也是由两个 16 进制(4bit)的数字组成的。

const buf = buffer.alloc(1);
console.log(buf); // output: <buffer 00>

如何申请堆外内存

buffer 可以跳出 nodejs 对堆内内存大小的限制。nodejs12 提供了 4 种 api 来申请堆外内存:

  • buffer.from()
  • buffer.alloc(size[, fill[, encoding]])
  • buffer.allocunsafe(size)
  • buffer.allocunsafeslow(size)

buffer.alloc vs buffer.allocunsafe

在申请内存时,可能这片内存之前存储过其他数据。如果不清除原数据,那么会有数据泄漏的安全风险;如果清除原数据,速度上会慢一些。具体用哪种方式,根据实际情况定。

  • buffer.alloc:申请指定大小的内存,并且清除原数据,默认填充 0
  • buffer.allocunsafe:申请指定大小内存,但不清除原数据,速度更快

根据提供的 api,可以手动实现一个alloc:

buffer.allocunsafe vs buffer.allocunsafeslow

从命名上可以直接看出效果,buffer.allocunsafeslow更慢。因为当使用 buffer.allocunsafe 创建新的 buffer 实例时,如果要分配的内存小于 4kb,则会从一个预分配的 buffer 切割出来。 这可以避免垃圾回收机制因创建太多独立的 buffer 而过度使用。

这种方式通过消除跟踪和清理的需要来改进性能和内存使用。

如何计算字节长度

利用 buffer,可以获得数据的真实所占字节。例如一个汉字,它的字符长度是 1。但由于是 utf8 编码的汉字,所以占用 3 个字节。

直接利用buffer.bytelength()可以获得字符串指定编码的字节长度:

const str = "本文原文地址: xxoo521.com";

console.log(buffer.bytelength(str, "utf8")); // output: 31
console.log(str.length); // output: 19

也可以直接访问 buffer 实例的 length 属性(不推荐):

console.log(buffer.from(str, "utf8").length); // output: 31

如何转换字符编码

nodejs 当前支持的编码格式有:ascii、utf8、utf16le、ucs2、base64、latin1、binary、hex。其他编码需要借助三方库来完成。

下面,是用buffer.from()和buf.tostring()来封装的 nodejs 平台的编码转换函数:

共享内存与拷贝内存

在生成 buffer 实例,操作二进制数据的时候,千万要注意接口是基于共享内存,还是基于拷贝底层内存。

例如对于生成 buffer 实例的from(),不同类型的参数,nodejs 底层的行为是不同的。

为了更形象地解释,请看下面两段代码。

代码 1:

const buf1 = buffer.from("buffer");
const buf2 = buffer.from(buf1); // 拷贝参数中buffer的数据到新的实例
buf1[0]++;

console.log(buf1.tostring()); // output: cuffer
console.log(buf2.tostring()); // output: buffer

代码 2:

const arr = new uint8array(1);
arr[0] = 97;

const buf1 = buffer.from(arr.buffer);
console.log(buf1.tostring()); // output: a

arr[0] = 98;
console.log(buf1.tostring()); // output: b

在第二段代码中,传入buffer.from的参数类型是arraybuffer。因此buffer.from仅仅是创建视图,而不是拷贝底层内存。buf1 和 arr 的内存是共享的。

在操作 buffer 的过程中,需要特别注意共享和拷贝的区别,发生错误比较难排查。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。