有趣的NodeJS模塊 - Buffer

Buffer 做爲 nodejs 中重要的概念和功能,爲開發者提供了操做二進制的能力。本文記錄了幾個問題,來加深對 Buffer 的理解和使用:javascript

  • 認識緩衝器
  • 如何申請堆外內存
  • 如何計算字節長度
  • 如何計算字節長度
  • 如何轉換字符編碼
  • 理解共享內存與拷貝內存
🔍 關注公衆號「心譚博客」 / 👉 查看原文: xxoo521.com / 歡迎交流和指正

認識 Buffer(緩衝器)

Buffer 是 nodejs 核心 API,它提供咱們處理二進制數據流的功能。Buffer 的使用和 ES2017 的 Uint8Array 很是類似,但因爲 node 的特性,專門提供了更深刻的 api。html

Uint8Array 的字面意思就是:8 位無符號整型數組。一個字節是 8bit,而字節的表示也是由兩個 16 進制(4bit)的數字組成的。前端

const buf = Buffer.alloc(1);
console.log(buf); // output: <Buffer 00>

如何申請堆外內存

Buffer 能夠跳出 nodejs 對堆內內存大小的限制。nodejs12 提供了 4 種 api 來申請堆外內存:java

  • Buffer.from()
  • Buffer.alloc(size[, fill[, encoding]])
  • Buffer.allocUnsafe(size)
  • Buffer.allocUnsafeSlow(size)

Buffer.alloc vs Buffer.allocUnsafe

在申請內存時,可能這片內存以前存儲過其餘數據。若是不清除原數據,那麼會有數據泄漏的安全風險;若是清除原數據,速度上會慢一些。具體用哪一種方式,根據實際狀況定。node

  • Buffer.alloc:申請指定大小的內存,而且清除原數據,默認填充 0
  • Buffer.allocUnsafe:申請指定大小內存,但不清除原數據,速度更快

根據提供的 api,能夠手動實現一個alloc算法

function pollifyAlloc(size, fill = 0, encoding = "utf8") {
    const buf = Buffer.allocUnsafe(size);
    buf.fill(fill, 0, size, encoding);
    return buf;
}

Buffer.allocUnsafe vs Buffer.allocUnsafeSlow

從命名上能夠直接看出效果,Buffer.allocUnsafeSlow更慢。由於當使用 Buffer.allocUnsafe 建立新的 Buffer 實例時,若是要分配的內存小於 4KB,則會從一個預分配的 Buffer 切割出來。 這能夠避免垃圾回收機制因建立太多獨立的 Buffer 而過分使用。api

這種方式經過消除跟蹤和清理的須要來改進性能和內存使用。數組

如何計算字節長度

利用 Buffer,能夠得到數據的真實所佔字節。例如一個漢字,它的字符長度是 1。但因爲是 utf8 編碼的漢字,因此佔用 3 個字節。安全

直接利用Buffer.byteLength()能夠得到字符串指定編碼的字節長度:函數

const str = "本文原文地址: xxoo521.com";

console.log(Buffer.byteLength(str, "utf8")); // output: 31
console.log(str.length); // output: 19

也能夠直接訪問 Buffer 實例的 length 屬性(不推薦):

console.log(Buffer.from(str, "utf8").length); // output: 31

如何轉換字符編碼

Nodejs 當前支持的編碼格式有:ascii、utf八、utf16le、ucs二、base6四、latin一、binary、hex。其餘編碼須要藉助三方庫來完成。

下面,是用Buffer.from()buf.toString()來封裝的 nodejs 平臺的編碼轉換函數:

function trans(str, from = "utf8", to = "utf8") {
    const buf = Buffer.from(str, from);
    return buf.toString(to);
}

// output: 5Y6f5paH5Zyw5Z2AOiB4eG9vNTIxLmNvbQ==
console.log(trans("原文地址: xxoo521.com", "utf8", "base64"));

共享內存與拷貝內存

在生成 Buffer 實例,操做二進制數據的時候,千萬要注意接口是基於共享內存,仍是基於拷貝底層內存。

例如對於生成 Buffer 實例的from(),不一樣類型的參數,nodejs 底層的行爲是不一樣的。

爲了更形象地解釋,請看下面兩段代碼。

代碼 1

const buf1 = Buffer.from("buffer");
const buf2 = Buffer.from(buf1); // 拷貝參數中buffer的數據到新的實例
buf1[0]++;

console.log(buf1.toString()); // output: cuffer
console.log(buf2.toString()); // output: buffer

代碼 2

const arr = new Uint8Array(1);
arr[0] = 97;

const buf1 = Buffer.from(arr.buffer);
console.log(buf1.toString()); // output: a

arr[0] = 98;
console.log(buf1.toString()); // output: b

在第二段代碼中,傳入Buffer.from的參數類型是arrayBuffer。所以Buffer.from僅僅是建立視圖,而不是拷貝底層內存。buf1 和 arr 的內存是共享的。

在操做 Buffer 的過程當中,須要特別注意共享和拷貝的區別,發生錯誤比較難排查。

參考連接


專一前端與算法的系列乾貨分享,歡迎關注(¬‿¬)

相關文章
相關標籤/搜索