当前位置: 首页 > 后端技术 > Node.js

Node.js源码分析——Buffer的8k池实现

时间:2023-04-03 17:35:34 Node.js

Node.js源码分析——Buffer的8kPool实现欢迎来到我的博客阅读:《Node.js源码解析-Buffer的8k池实现》在Node.js中,大文件一般都是以Buffer的形式存储的。Buffer相比strings,可以节省decode/encodeProcess,节省CPU成本说到Buffer,就不得不提到Buffer的8kpool,下面我们就来看看8kpool是如何实现的。Node.jsv8.4.0中实现了8kpool,可以使用以下方法获取Buffer实例:newBuffer()(不推荐)Buffer.from()Buffer.alloc()Buffer.allocUnsafe()Buffer.allocUnsafeSlow()从命名上看,Buffer.allocUnsafe()和Buffer.allocUnsafeSlow()都是不安全的,存在泄露内存敏感信息的危险。后面会讨论不安全的问题。我们先来看看如何获??取Buffer实例。从图中可以看出,只有allocate()和fromString()两个函数与8kpoolrelated//lib/buffer.jsfunctionallocate(size){if(size<=0){returnnewFastBuffer();}if(size<(Buffer.poolSize>>>1)){//<4kif(size>(poolSize-poolOffset))//大于剩余容量createPool();varb=newFastBuffer(allocPool,poolOffset,size);poolOffset+=大小;对齐池();返回b;}else{//>4kreturncreateUnsafeBuffer(size);}}functionfromString(string,encoding){//...if(length>=(Buffer.poolSize>>>1))//>4kreturnbinding.createFromString(string,encoding);if(length>(poolSize-poolOffset))//大于剩余容量createPool();varb=newFastBuffer(allocPool,poolOffset,length);constactual=b.write(string,encoding);if(actual!==length){//byteLength()可能被错误估计了,尽管不太可能b=newFastBuffer(allocPool,poolOffset,actual);}poolOffset+=实际的;对齐池();returnb;}allocate()和fromString()分为大于4k和小于4k两种情况处理小于4k,先检查8k池的剩余容量,如果大于剩余容量则新建一个直接8k池,然后固定poolOffset,最后调用alignPool()//lib/buffer.jsBuffer.poolSize=8*1024;varpoolSize,poolOffset,allocPool;functioncreateUnsafeArrayBuffer(size){//...returnnewArrayBuffer(尺寸);//...}functioncreatePool(){poolSize=Buffer.poolSize;//8kallocPool=createUnsafeArrayBuffer(poolSize);poolOffset=0;}createPool();functionalignPool(){if(poolOffset&0x7){//对于校准,只有8poolOffset的倍数|=0x7;//xxx111poolOffset++;//xx(x+1)000}}通过调用alignPool()来校准poolOffset,poolOffset只能是8的倍数,也就是说每次8k池容量不够时至少要使用8字节内存,调用createPool()创建一个new8kpoolcreatePool()内部调用createUnsafeArrayBuffer()获取对应大小的ArrayBuffer实例。关于ArrayBuffer,这里是来自MDN的介绍:ArrayBuffer对象是用来表示一个通用的,固定长度的原始二进制数据缓冲区,因为ArrayBuffer是原始二进制数据,所以是不安全的,有泄露内存敏感信息的危险.为什么不安全?从图中我们知道,获取一个Buffer实例的方法有4种。有的不安全,有的不依赖newBuffer()Buffer.from()和Buffer.alloc()不是一种。让我们看看为什么有些是不安全的//安全if(isAnyArrayBuffer(value))returnfromArrayBuffer(value,encodingOrOffset,length);//安全varb=fromObject(value);//安全//...};functionfromString(string,encoding){//...varb=newFastBuffer(allocPool,poolOffset,length);constactual=b.write(string,encoding);//...}functionfromArrayBuffer(obj,byteOffset,length){//...returnnewFastBuffer(obj,byteOffset,length);}functionfromObject(obj){if(isUint8Array(obj)){constb=allocate(obj.length);//...binding.copy(obj,b,0,0,obj.length);}if(obj!=null){//...returnfromArrayLike(obj);//...}}functionfromArrayLike(obj){//...constb=allocate(length);for(vari=0;i0&&fill!==undefined){//...returncreateUnsafeBuffer(size).fill(fill,encoding);}}returnnewFastBuffer(size);};可以看出来:Buffer.from()和Buffer.alloc():得到原始buffer后,替换原始数据,所以是安全的Buffer.allocUnsafe()和Buffer.allocUnsafeSlow():直接使用原始buffer,所以不安全Buffer.from()和Buffer.alloc()Buffer.from()ArrayBuffer:直接使用ArrayBuffer创建FastBufferString:小于4k使用8k池,大于4k调用binding.createFromString()Object:小于4k使用8k池,大于4k调用createUnsafeBuffer()Buffer.alloc():需要填充buffer,给定字符填充,否则填充0()参考:https://github.com/nodejs/node/blob/master/lib/缓冲区.js