在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);
};
複製代碼