在node 8.0以前,buffer主要经过构造函数使用new Buffer(...)的方式来建立。由于根据参数的不一样,生成buffer的行为和结果都大不相同,因此从node 8.0版本开始,buffer的建立被拆分到几个不一样的方法(from, alloc, allocUnsafe)。html
在看详细的方法以前,须要知道的是,node为了节约系统资源提升效率,先预申请了一部份内存做为buffer pool。 若是申请的buffer的大小比较小的状况下,会优先从这个pool里面进行分配。 这部分操做,都放到了allocate函数里面:node
function allocate(size) {
if (size <= 0) {
return new FastBuffer(); // 没size,直接返回一个空buffer
}
if (size < (Buffer.poolSize >>> 1)) { // size小于pool的一半(4kb)
if (size > (poolSize - poolOffset)) // size比pool剩下的空间大的话,就再申请一个pool
createPool();
const b = new FastBuffer(allocPool, poolOffset, size);
poolOffset += size;
alignPool();
return b;
}
return createUnsafeBuffer(size); // 若是size比较大, 则直接申请一个未初始化的buffer
}
复制代码
Buffer.from比较复杂,根据入参大体有3种不一样的处理方式:c++
当传入的第一个参数为一个字符串时,会调用对应的fromString方法,代码以下:git
function fromString(string, encoding) {
// 肯定string在encode后的大小,若是string为0,则返回一个空buffer
let length;
if (typeof encoding !== 'string' || encoding.length === 0) {
if (string.length === 0)
return new FastBuffer();
encoding = 'utf8';
length = byteLengthUtf8(string);
} else {
length = byteLength(string, encoding, true);
if (length === -1)
throw new ERR_UNKNOWN_ENCODING(encoding);
if (string.length === 0)
return new FastBuffer();
}
// 若是大小大于poolSize的一半(4kb), 直接分配内存。
if (length >= (Buffer.poolSize >>> 1))
return createFromString(string, encoding);
// 若是大小大于pool里剩下的内存,则新建一个pool
if (length > (poolSize - poolOffset))
createPool();
let b = new FastBuffer(allocPool, poolOffset, length);
const actual = b.write(string, encoding);
if (actual !== length) {
b = new FastBuffer(allocPool, poolOffset, actual);
}
poolOffset += actual;
alignPool();
return b;
}
复制代码
c++扩展直接分配内存:github
void CreateFromString(const FunctionCallbackInfo<Value>& args) {
CHECK(args[0]->IsString());
CHECK(args[1]->IsString());
enum encoding enc = ParseEncoding(args.GetIsolate(),
args[1].As<String>(),
UTF8);
Local<Object> buf;
if (New(args.GetIsolate(), args[0].As<String>(), enc).ToLocal(&buf))
args.GetReturnValue().Set(buf);
}
复制代码
总结下来就是3个步骤:api
function fromArrayBuffer(obj, byteOffset, length) {
... byteOffset校对逻辑
return new FastBuffer(obj, byteOffset, length);
}
复制代码
若是传入的是一个arrayBuffer, 那就直接生成一个FastBuffer对象,而FastBuffer直接继承了Uint8Array,根据typedArray的定义,若是传入一个ArrayBuffer,那就返回一个新的TypedArray来共享这段内存数组
When called with a buffer, and optionally a byteOffset and a length argument, a new typed array view is created that views the specified ArrayBuffer. The byteOffset and length parameters specify the memory range that will be exposed by the typed array view. If both are omitted, all of buffer is viewed; if only length is omitted, the remainder of buffer is viewed.bash
function fromObject(obj) {
if (isUint8Array(obj)) {
const b = allocate(obj.length); // // 从pool里分配或者直接分配
if (b.length === 0)
return b;
_copy(obj, b, 0, 0, obj.length); // // 把数据拷贝到新分配的内存上。
return b;
}
if (obj.length !== undefined || isAnyArrayBuffer(obj.buffer)) {
if (typeof obj.length !== 'number') {
return new FastBuffer();
}
return fromArrayLike(obj);
}
if (obj.type === 'Buffer' && Array.isArray(obj.data)) {
return fromArrayLike(obj.data);
}
}
复制代码
// 把类数组的数据(array/buffer)都写入新的buffer
function fromArrayLike(obj) {
const length = obj.length;
const b = allocate(length);
for (var i = 0; i < length; i++)
b[i] = obj[i];
return b;
}
复制代码
Buffer.alloc(size, fill, encoding)接受3个参数,返回一个被初始化了的buffer:函数
Buffer.alloc = function alloc(size, fill, encoding) {
assertSize(size);
if (fill !== undefined && fill !== 0 && size > 0) {
const buf = createUnsafeBuffer(size);
return _fill(buf, fill, 0, buf.length, encoding);
}
return new FastBuffer(size);
};
// 建立一个未初始化的buffer
function createUnsafeBuffer(size) {
zeroFill[0] = 0;
try {
return new FastBuffer(size);
} finally {
zeroFill[0] = 1;
}
}
复制代码
经过FastBuffer直接生成一个Uint8Array, 若是传了fill,则还要对生成的Uint8Array作一个填充操做。ui
这个就简单了,直接调用allocate函数申请内存
Buffer.allocUnsafe = function allocUnsafe(size) {
assertSize(size);
return allocate(size);
};
复制代码
对应allocUnsafe还有一个allocUnsafeSlow函数,区别就是它直接申请了内存,没有走pool。
Buffer.allocUnsafeSlow = function allocUnsafeSlow(size) {
assertSize(size);
return createUnsafeBuffer(size);
};
复制代码