Express全系列教程之(十六):认识Buffer

一、简介

在nodejs中,我们无法直接发送二进制数据,如图像、视频等媒体文件。而Buffer的出现就是专门用来存储二进制数据的。Buffer是node.js的核心模块,因此它可以直接通过Buffer.from();来将数据存储进被分配的内存中。

Buffer.from(str);

我们写入的虽然是字符串,但当我们打印后得到的确实16进制数据,也因为如此,一个Buffer所对应的取值范围为00-FF,即0-255之间,而对应的二进制则是:00000000-11111111(一位代表四位二进制数)。
在这里我们可以了解一下数据存储规则:

一位二进制=1bit
8bit=1byte(一字节)
1kb=1024byte

在这里,因为字母汉字在所占内存容量也有所不同,如下:

	var str="Hello 李"
	var buf=Buffer.from(str,"UTF-8");
	console.log(str.length);	// 7
	console.log(buf.length);	// 9

我们可以看到,这里一个字是占3个字节的。

二、创建Buffer 类

1.可以通过 Buffer.alloc(size);来创建一个固定长度的Buffer类,且空间是连续的;
Nodejs并不会去操作内存,它把这一工作交给C++完成内存分配申请,再通过JavaScript进行管理。

var buf=Buffer.alloc(5);	//分配长度为5的连续内存空间,他的初始值为0

我们也可以为创建的内存空间赋值:

buf[0]=39;		//39
buf[1]=-100;		//156
buf[2]=257;		//256
buf[3]=0xaa		//170
buf[5]=1			//打印buf时,值不会变化

值得注意的是Buffer一旦创建,它的值就不会改变。想在创建时分配器初始值,则可写为:
Buffer.alloc(10, 1); 它每一项的初始值都会为01

2.通过Buffer.allocUnsafe(10);来创建固定长度Buffer,通过allocUnsafe创建的内存空间将不会初始化数据,返回的 Buffer 实例可能包含旧数据。

四、缓冲区操作

1.写入缓冲区

buf.write(string[, offset[, length]][, encoding]);

string - 写入缓冲区的字符串。
offset - 缓冲区开始写入的索引值,默认为 0 。
length - 写入的字节数,默认为 buffer.length
encoding - 使用的编码。默认为 'utf8' 。

如果Buffer分配空间不足,则只会写入一部分:

buf = Buffer.alloc(10);
len = buf.write("www.bilibili.com");

console.log("字节数 : "+  len);		//字节数10

2.读取缓冲区数据

buf.toString([encoding[, start[, end]]]);

encoding - 使用的编码。默认为 'utf8' 。
start - 指定开始读取的索引位置,默认为 0。
end - 结束位置,默认为缓冲区的末尾。

实例:

buf = Buffer.alloc(26);
for (var i = 0 ; i < 26 ; i++) {
  buf[i] = i + 97;
}

console.log( buf.toString('ascii'));       // 输出: abcdefghijklmnopqrstuvwxyz
console.log( buf.toString('ascii',0,5));   // 输出: abcde
console.log( buf.toString('utf8',0,5));    // 输出: abcde
console.log( buf.toString(undefined,0,5)); // 使用 'utf8' 编码, 并输出: abcde

3.合并缓冲区

通过 Buffer.concat(list[, totalLength]);合并缓冲区:

var buffer1 = Buffer.from(('张三'));
var buffer2 = Buffer.from(('李四'));
var buffer3 = Buffer.concat([buffer1,buffer2]);
console.log("buffer3: " + buffer3.toString());		//buffer3:张三李四

4.缓冲区前后比较

buf.compare(otherBuffer);可以对缓冲区进行比较:

var buffer1 = Buffer.from('ABC');
var buffer2 = Buffer.from('ABCD');
var result = buffer1.compare(buffer2);

if(result < 0) {
   console.log(buffer1 + " 在 " + buffer2 + "之前");	//ABC在ABCD之前
}else if(result == 0){
   console.log(buffer1 + " 与 " + buffer2 + "相同");
}else {
   console.log(buffer1 + " 在 " + buffer2 + "之后");
}

5.拷贝缓冲区

buf.copy(targetBuffer[, targetStart[, sourceStart[, sourceEnd]]]);

targetBuffer - 要拷贝的 Buffer 对象。
targetStart - 拷贝到目标第几位上,默认0
sourceStart - 从第几位开始拷贝, 默认: 0
sourceEnd - 拷贝结束位,默认: buffer.length

如下实例:

	var buf1 = Buffer.from('abcdefgh');
	var buf2 = Buffer.from('ABC');
	buf2.copy(buf1, 2,0);
	console.log(buf1.toString());	//abABCfgh

6.缓冲区长度

buf.length;即可获得缓冲区长度

var buffer = Buffer.from('www.runoob.com');
//  缓冲区长度
console.log("buffer length: " + buffer.length);	//buffer length: 14

五、关于内存分配

之前js只能处理字符串数据类型,且对字符串的操作都是得到一个新的字符串,不会对源字符串做出改变,而buffer会通过buffer[index]的方式对固定位置的字节进行更改。Buffer是一个典型的Javascript和C++结合的模块,性能相关部分用C++实现,非性能相关部分用javascript实现。Node在进程启动时Buffer就已经加装进入内存,并将其放入全局对象,因此无需require。
Buffer对象的内存分配不是在V8的堆内存中,在Node的C++层面实现内存的申请。
Nodejs采用slab分配机制,slab是一种动态内存管理机制,slab有三种状态:

full:完全分配状态
partial:部分分配状态
empty:没有被分配状态

nodejs会根据分配内存大小对Buffer对象进行分类,申请内存小于4kb时,就会存入初始化的slab单元中。当继续申请小于4kb且当前第一个初始化的8k空间足够的情况下会继续存入第一个初始化的8kkong空间池(如果被初始化的8k池的空间剩余2k,这个时候再去申请一个大于2k并且小于4k的内存空间,就会去新申请一个slab单元空间,上次初始化的slab单元的剩余2k内存就会被浪费掉,无法再使用)。
Buffer的分配过程中主要使用一个局部变量pool作为中间处理对象,处于分配状态的slab单元都指向它。

var pool;
function allocPool() {
  pool = new SlowBuffer(Buffer.poolSize);
  pool.used = 0;
}

如果pool没有被创建,将会创建一个新的slab单元指向它:

if (!pool || pool.length - pool.used < this.length) allocPool();

同时当前Buffer对象的parent属性指向该slab,并记录下是从这个slab的哪个位置(offset)开始使用的,slab对象自身也记录被使用了多少字节:

this.parent = pool; 
this.offset = pool.used; 
pool.used += this.length;
if (pool.used & 7) pool.used = (pool.used + 8) & ~7;

这时候的slab状态为partial。当再次创建一个Buffer对象时,构造过程中将会判断这个slab的剩余空间是否足够。如果足够,使用剩余空间。如果slab剩余的空间不够,将会构造新的slab,原slab中剩余的空间会造成浪费。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值