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

Node.js Buffer用法解读

程序员文章站 2022-08-28 08:09:17
buffer是什么? buffer作为存在于全局对象上,无需引入模块即可使用,你绝对不可以忽略它。 可以理解buffer是在内存中开辟的一片区域,用于存放二进制...

buffer是什么?

buffer作为存在于全局对象上,无需引入模块即可使用,你绝对不可以忽略它。

可以理解buffer是在内存中开辟的一片区域,用于存放二进制数据。buffer所开辟的是堆外内存。

buffer的应用场景有哪些?


怎么理解流呢?流是数据的集合(与数据、字符串类似),但是流的数据不能一次性获取到,数据也不会全部load到内存中,因此流非常适合大数据处理以及断断续续返回chunk的外部源。流的生产者与消费者之间的速度通常是不一致的,因此需要buffer来暂存一些数据。buffer大小通过highwatermark参数指定,默认情况下是16kb。

存储需要占用大量内存的数据

buffer 对象占用的内存空间是不计算在 node.js 进程内存空间限制上的,所以可以用来存储大对象,但是对象的大小还是有限制的。一般情况下32位系统大约是1g,64位系统大约是2g。

如何创建buffer

除了流自动隐式创建buffer之外,也可以手动创建buffer,方式如下:

buffer中存储的数据已确定

buffer.from(obj)  // obj支持的类型string, buffer, arraybuffer, array, or array-like object

注意:buffer.from不支持传入数字,如下所示:

buffer.from(1234);

buffer.js:208
  throw new errors.typeerror(
  ^

typeerror [err_invalid_arg_type]: the "value" argument must not be of type number. received type number
  at function.from (buffer.js:208:11)
  ...

若要传入数字可以采用传入数组的方式:

const buf = buffer.from([1, 2, 3, 4]);
console.log(buf); // <buffer 01 02 03 04>

但是这种方式存在一个问题,当存入不同的数值的时候buffer中记录的二进制数据会相同,如下所示:

const buf2 = buffer.from([127, -1]);
console.log(buf2);   // <buffer 7f ff>

const buf3 = buffer.from([127, 255]);
console.log(buf3);  // <buffer 7f ff>

console.log(buf3.equals(buf2)); // true

当要记录的一组数全部落在0到255(readuint8来读取)这个范围, 或者全部落在-128到127(readint8来读取)这个范围那么就没有问题,否则的话就强烈不推荐使用buffer.from来保存一组数。因为不同的数字读取时应该调用不同的方法。

buffer存储数据未确定

buffer.alloc、buffer.allocunsafe、buffer.allocunsafeslow

buffer.alloc会用0值填充已分配的内存,所以相比后两者速度上要慢,但是也较为安全。当然也可以通过--zero-fill-buffers flag使allocunsafe、allocunsafeslow在分配完内存后也进行0值填充。

node --zero-fill-buffers index.js

当分配的空间小于4kb的时候,allocunsafe会直接从之前预分配的buffer里面slice空间,因此速度比allocunsafeslow要快,当大于等于4kb的时候二者速度相差无异。

// 分配空间等于4kb
function createbuffer(fn, size) {
 console.time('buf-' + fn);
 for (var i = 0; i < 100000; i++) {
  buffer[fn](size);
 }
 console.timeend('buf-' + fn);
}
createbuffer('alloc', 4096);
createbuffer('allocunsafe', 4096);
createbuffer('allocunsafeslow', 4096);

// 输出
buf-alloc:      294.002ms
buf-allocunsafe:   224.072ms
buf-allocunsafeslow: 209.22ms

function createbuffer(fn, size) {
 console.time('buf-' + fn);
 for (var i = 0; i < 100000; i++) {
  buffer[fn](size);
 }
 console.timeend('buf-' + fn);
}
createbuffer('alloc', 4095);
createbuffer('allocunsafe', 4095);
createbuffer('allocunsafeslow', 4095);
// 输出
buf-alloc:      296.965ms
buf-allocunsafe:   135.877ms
buf-allocunsafeslow: 205.225ms

需要谨记一点:new buffer(xxxx) 方式已经不推荐使用了

buffer使用

buffer转字符串

const buf = buffer.from('test');
console.log(buf.tostring('utf8'));         // test
console.log(buf.tostring('utf8', 0, 2));      // te

buffer转json

const buf = buffer.from([0x1, 0x2, 0x3, 0x4, 0x5]);
console.log(buf.tojson());  // { type: 'buffer', data: [ 1, 2, 3, 4, 5 ] }

buffer裁剪,裁剪后返回的新的buffer与原buffer指向同一块内存

buf.slice([start[, end]])
  1. start 起始位置
  2. end 结束位置(不包含)

示例:

var buf1 = buffer.from('test');
var buf2 = buf1.slice(1, 3).fill('xx');
console.log("buf2 content: " + buf2.tostring()); // xx
console.log("buf1 content: " + buf1.tostring()); // txxt

buffer拷贝,buffer与数组不同,buffer的长度一旦确定就不再变化,因此当拷贝的源buffer比目标buffer大时只会复制部分的值

buf.copy(target[, targetstart[, sourcestart[, sourceend]]])

示例:

var buf1 = buffer.from('abcdefghijkl');
var buf2 = buffer.from('abcdef');

buf1.copy(buf2, 1);
console.log(buf2.tostring()); //abcdef

buffer相等判断,比较的是二进制值

buf.equals(otherbuffer)

示例:

const buf1 = buffer.from('abc');
const buf2 = buffer.from('414243', 'hex'); 
console.log(buf1.equals(buf2));  // true

除了equals之外,compare其实也可以用于判断是否相等(当结果为0则相等),不过compare更主要的作用是用于对数组内的buffer实例排序。

buffer是否包含特定值

buf.includes(value[, byteoffset][, encoding])
buf.indexof(value[, byteoffset][, encoding])

示例:

const buf = buffer.from('this is a buffer');
console.log(buf.includes('this')); // true
console.log(buf.indexof('this')); // 0

写入读取数值

写入方法:

位数固定且超过1个字节的: write{double| float | int16 | int32|  uint16 | uint32 }{be|le}(value, offset)

位数不固定的: write{int | uint}{be | le}(value, offset, bytelength) //此方法提供了更灵活的位数表示数据(比如3位、5位)

位数固定是1个字节的:     write{int8 | unit8}(value, offset)

读取方法:

位数固定且超过1个字节的: read{double| float | int16 | int32 | uint16 | uint32 }{be|le}(offset)

位数不固定的:  read{int | uint}{be | le}(offset, bytelength)

位数固定是1个字节的: read{int8 | unit8}(offset)

double、float、int16、int32、uint16、uint32既确定了表征数字的位数,也确定了是否包含负数,因此定义了不同的数据范围。同时由于表征数字的位数都超过8位,无法用一个字节来表示,因此就涉及到了计算机的字节序区分(大端字节序与小端字节序)

关于大端小端的区别可以这么理解:数值的高位在buffer的起始位置的是大端,数值的低位buffer的起始位置则是小端

const buf = buffer.allocunsafe(2);
buf.writeint16be(256, 0) 
console.log(buf);      // <buffer 01 00> 
buf.writeint16le(256, 0)
console.log(buf);      // <buffer 00 01>

这里可以查看数值的不同进制之间的转换,如果是大端的话,则直接按顺序(0100)拼接16进制即可,如果是小端则需要调换一下顺序才是正确的表示方式。

buffer合并

buffer.concat(list[, totallength]) //totallength不是必须的,如果不提供的话会为了计算totallength会多一次遍历

const buf1 = buffer.from('this is');
const buf2 = buffer.from(' funny');
console.log(buffer.concat([buf1, buf2], buf1.length + buf2.length));
// <buffer 74 68 69 73 20 69 73 20 66 75 6e 6e 79>

清空buffer

清空buffer数据最快的办法是buffer.fill(0)

buffer模块与buffer的关系

buffer是全局global上的一个引用,指向的其实是buffer.buffer

 const buffer = require('buffer');
 console.log(buffer.buffer === buffer); //true

buffer模块上还有其他一些属性和方法

const buffer = require('buffer');
console.log(buffer);
{ buffer:
  { [function: buffer]
   poolsize: 8192,
   from: [function: from],
   alloc: [function: alloc],
   allocunsafe: [function: allocunsafe],
   allocunsafeslow: [function: allocunsafeslow],
   isbuffer: [function: isbuffer],
   compare: [function: compare],
   isencoding: [function: isencoding],
   concat: [function: concat],
   bytelength: [function: bytelength],
   [symbol(node.isencoding)]: [function: isencoding] },
 slowbuffer: [function: slowbuffer],
 transcode: [function: transcode],
 inspect_max_bytes: 50,
 kmaxlength: 2147483647,
 kstringmaxlength: 1073741799,
 constants: { max_length: 2147483647, max_string_length: 1073741799 } }

上面的kmaxlength与max_length代表了新建buffer时内存大小的最大值,当超过限制值后就会报错

32为机器上是(2^30)-1(~1gb)

64位机器上是(2^31)-1(~2gb)

buffer释放

我们无法手动对buffer实例进行gc,只能依靠v8来进行,我们唯一能做的就是解除对buffer实例的引用

参考资料



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