在剛接觸Nodejs的時候,有些概念總讓學前端的我感到困惑(雖然大學的時候也是在搞後端,世界上最好的語言,you know)。我能夠很快理解File System,Path等帶有明顯功能的模塊,卻一會兒不能理解Buffer這個玄而又玄的東西。由於,在前端的js實踐中,我不多去考慮什麼編碼方式,字符集之類的東西。二進制的理解僅限於大學課堂而已。本文與其說是在探討Node的Buffer模塊,倒不如說是來探討下如何從字符集,編碼的角度來理解Buffer這個模塊設立的意義。Node閒談系列不涉及具體的API講解,只會勾勾畫畫一些本身認爲比較重要的特性。javascript
正如咱們學習編程的第一節課同樣,咱們明白,計算機就是一個二進制生物。它只能理解1和0,或者說有和沒有。「太極生兩儀,兩儀生四象,四象生八卦」。咱們在計算上看到的不管是任何東西,都是經過某種特殊的編碼方式,或者說是約定展示出來的。html
咱們很熟悉的ASCII碼就是這樣一種規範,和摩爾斯碼同樣,固定的值表示固定的含義。ASCII碼用1個字節8位來表示2^8=256種狀態。好比大寫字母A對應的是65,B對應66,是否是很簡單。ASCII碼並非佔滿了全部的256個位置,只用到了一半128位。在一個字節中,只佔用後面7位,最前面一位統一標識爲0。前端
可是咱們很容易就發現,ASCII碼遠遠是不夠的,最起碼咱們漢字就表示不了。後面考慮了許多其餘解決方案,咱們在此很少敘述,直接說最終解決方案——Unicode。Unicode想法很直接,就是想把全世界全部的字符都囊括進去。咱們通常認爲Unicode用兩個字節16位表示,而且徹底囊括了ASCII字符集。好比漢字「好」在unicode裏面二進制表示是 0101 1001 0111 1101。將其轉換成16進制就是U597D(U只是表示它們是unicode碼)。之因此我前面說是「通常認爲」,是由於這種想法是不許確的。Unicode一個平面(plane)是兩個字節。咱們常常談論的是它的一個基本平面,編碼是U+0000到U+FFFF,常見字符都在這個平面。Unicode還有16個輔助平面,碼點範圍是U+010000一直到U+10FFFF。通常而言,咱們只須要把關注點放在基本平面就好,而且要習慣Unicode的表示方式。由於,這是畢竟在各類編碼方式間轉化的「硬通貨」。java
咱們經常談到的utf-8,utf-16這些是什麼呢?這些都是具體的編碼方式,而Unicode是個字符集。以utf-8爲例,它在unicode碼的基礎上,進行從新編碼,把一些自己不須要佔滿2個字節的轉化爲1個字節。好比ASCII裏面的那些字符,在unicode裏面,第一個字節全是0,簡直是空間的浪費,也會把漢字編碼城3個字節。你盡能夠在控制檯試下Buffer.from('我','utf8')
看下編碼後佔的字節數。node
javascript使用哪一種編碼方式?git
javascript採用Unicode字符集,可是只支持一種編碼方式。那就是USC-2。是否是沒有據說過?你能夠把它理解成utf-16。但它和utf-16究竟是什麼關係呢?github
二者的關係簡單說,就是UTF-16取代了UCS-2,或者說UCS-2整合進了UTF-16。因此,如今只有UTF-16,沒有UCS-2。編程
UCS-2只支持兩個字節,而在它後面纔出來的UTF-16在UCS-2的基礎上,利用輔助平面能夠支持4個字節。既然是UCS-2整合進UTF-16,那就存在有的字符UTF-16有,而UCS-2不存在的狀況。出現這種狀況怎麼辦?你們能夠參考下參考資料裏面阮老師的講述。後端
相關重要的APIapi
在Buffer生成的過程當中,最大的關注點就是內存的申請和分配。原先new Buffer()
生成Buffer的方法已經不建議再次使用,它和Buffer.allocUnsafe()
方法同樣,可能包含敏感數據。
爲何會包含敏感數據呢?在生成buffer的過程當中,不是一步到位,要分爲兩步走,1,申請內存空間,2,申請的內存空間進行填充。Buffer.allocUnsafe()
方法只完成了第一步。不完成第二步的後果就是,申請的空間可能「殘留了」之前內存上的數據。畢竟一起內存在計算機中老是申請了再釋放,釋放了再申請,不免就會致使一些數據沒有被及時清理乾淨。固然,因爲少了第二步操做,速度天然快了很多。
const a = Buffer.allocUnsafe(10); console.log(a) //<Buffer f0 4e a8 6f 01 02 00 00 00 20> 打印結果老是不同的,但咱們發現每一位上極可能不是00,這些數據就屬於敏感數據。
可使用buf.fill(0)
進行後期的填充。但爲了不漏洞產生,應該避免使用Buffer.allocUnsafe()來分配內存。
Buffer.alloc()
比Buffer.allocUnsafe()
安全的緣由在於它在第二步會把全部的舊數據清除掉,填充成0。
const a = Buffer.alloc(10); console.log(a) //<Buffer 00 00 00 00 00 00 00 00 00 00> 打印結果每一位都是0。
看到這裏,你是否是覺得Buffer.alloc()
和Buffer.allocUnsafe()
的區別僅限於有沒有填充數據?其實並非的。真正與Buffer.alloc()
差異在是否填充數據的是Buffer.allocUnsafeSlow()
。原來,使用Buffer.allocUnsafe()
分配內存須要藉助共享內存池(shared internal memory pool)。而Buffer.alloc()
和Buffer.allocUnsafeSlow()
是直接在內存空間上開闢相應大小的內存空間。
Buffer.allocUnsafe() (與以前的 new Buffer(size) 機制相似)是三者中分配內存速度最快的方式,它採用了共享內存池(shared internal memory pool)這一方式,經過預先分配必定大小的一段內存,從中再向 JavaScript 分配相應大小的片斷,避免頻繁的向系統申請內存分配,來達到較高的效率。共享內存池的默認值 poolSize 爲 8KB(可從新賦值),只有當須要分配的內存小於等於 poolSize 的一半時,Buffer.allocUnsafe() 纔會從共享內存池從分配空間。
這裏的知識點到爲止,詳細的探討之後能夠能夠考慮專門寫一篇來講明,參考資料的內容也是至關不錯,建議閱讀。
相關重要的API
buffer只有可以讀寫,纔可以顯示其存在的價值。查看Buffer的文檔,Buffer的讀寫方法中有很是多以「BE」和「LE」結尾的方法。他們分別表明什麼呢?
大字序和小字序
「BE」表示的是「big endian」大端字節序,而「LE」天然表示「little endian」小端字節序。字節序是幹什麼的呢?咱們在描述一個字符的unicode碼的時候,習慣性地從左到右去寫。爲何不能夠從右右往左寫呢?多個字節不管是讀取仍是寫入,總要有一個順序,這就是「字節序」。大端序就是咱們常看到的高位字節在前,低位字節在後,小端序剛好相反。
爲何要區分大端序和小端序呢?不能都統一從一個方向讀取,寫入麼?
計算機電路先處理低位字節,效率比較高,由於計算都是從低位開始的。因此,計算機的內部處理都是小端字節序。
可是,人類仍是習慣讀寫大端字節序。因此,除了計算機的內部處理,其餘的場合幾乎都是大端字節序,好比網絡傳輸和文件儲存。
字節序的處理,就是一句話:"只有讀取的時候,才必須區分字節序,其餘狀況都不用考慮。"
好,下面咱們舉個實際的例子。
var buf = Buffer.from([1,3,5,7]); //<Buffer 01 03 05 07> buf.readInt16BE(0) //259 從buf中讀取16位的整數,因此讀取的第一個字符對應的碼點是 01 03轉化成10進制就是1*16^2+3 = 259。 buf.readInt16LE(0) //756 // 小字序從右往左讀取,第一個字符對應的碼點是 03 01 轉化成10進制就是3*16^2+1 = 756
讀取和寫入是一個相反的過程,道理是同樣的。
//官方示例 const buf = Buffer.allocUnsafe(4); buf.writeUInt8(0x3, 0); buf.writeUInt8(0x4, 1); buf.writeUInt8(0x23, 2); buf.writeUInt8(0x42, 3); // Prints: <Buffer 03 04 23 42> console.log(buf);
Buffer還有不少頗有意思的方面須要進一步學習,待之後再進一步補充本文或者寫幾篇相關更爲深刻的文章。
未完待續。。。