Node.js Buffer解读

Buffer是什么?

Buffer做为存在于全局对象上,无需引入模块便可使用,你绝对不能够忽略它。 能够理解Buffer是在内存中开辟的一片区域,用于存放二进制数据。Buffer所开辟的是堆外内存。html

Buffer的应用场景有哪些?

怎么理解流呢?流是数据的集合(与数据、字符串相似),可是流的数据不能一次性获取到,数据也不会所有load到内存中,所以流很是适合大数据处理以及断断续续返回chunk的外部源。流的生产者与消费者之间的速度一般是不一致的,所以须要buffer来暂存一些数据。buffer大小经过highWaterMark参数指定,默认状况下是16Kb。node

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

Buffer 对象占用的内存空间是不计算在 Node.js 进程内存空间限制上的,因此能够用来存储大对象,可是对象的大小仍是有限制的。通常状况下32位系统大约是1G,64位系统大约是2G。linux

如何建立Buffer

除了流自动隐式建立Buffer以外,也能够手动建立Buffer,方式以下:git

Buffer中存储的数据已肯定

Buffer.from(obj) // obj支持的类型string, buffer, arrayBuffer, array, or array-like objectgithub

注意:Buffer.from不支持传入数字,以下所示:json

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来保存一组数。由于不一样的数字读取时应该调用不一样的方法。bash

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]])
start 起始位置
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、Int1六、Int3二、UInt1六、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>
复制代码

https://tool.lu/hexconvert/ 这里能够查看数值的不一样进制之间的转换,若是是大端的话,则直接按顺序(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实例的引用

参考资料

cenalulu.github.io/linux/chara…

www.ruanyifeng.com/blog/2007/1…

medium.freecodecamp.org/do-you-want…

www.barretlee.com/blog/2017/0…

medium.freecodecamp.org/node-js-str…

www.runoob.com/nodejs/node…

相关文章
相关标签/搜索