Buffer 作为 nodejs 中重要的概念和功能,为开发者提供了操作二进制的能力。本文记录了几个问题,来加深对 Buffer 的理解和使用:
- 认识缓冲器
- 如何申请堆外内存
- 如何计算字节长度
- 如何计算字节长度
- 如何转换字符编码
- 理解共享内存与拷贝内存
认识 Buffer(缓冲器)
Buffer 是 nodejs 核心 API,它提供我们处理二进制数据流的功能。Buffer 的使用和 ES2017 的 Uint8Array 非常相似,但由于 node 的特性,专门提供了更深入的 api。
Uint8Array 的字面意思就是:8 位无符号整型数组。一个字节是 8bit,而字节的表示也是由两个 16 进制(4bit)的数字组成的。
const buf = Buffer.alloc(1);
console.log(buf); // output: <Buffer 00>
如何申请堆外内存
Buffer 可以跳出 nodejs 对堆内内存大小的限制。nodejs12 提供了 4 种 api 来申请堆外内存:
- Buffer.from()
- Buffer.alloc(size[, fill[, encoding]])
- Buffer.allocUnsafe(size)
- Buffer.allocUnsafeSlow(size)
Buffer.alloc vs Buffer.allocUnsafe
在申请内存时,可能这片内存之前存储过其他数据。如果不清除原数据,那么会有数据泄漏的安全风险;如果清除原数据,速度上会慢一些。具体用哪种方式,根据实际情况定。
- 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 而过度使用。
这种方式通过消除跟踪和清理的需要来改进性能和内存使用。
如何计算字节长度
利用 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、utf8、utf16le、ucs2、base64、latin1、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: aarr[0] = 98;
console.log(buf1.toString()); // output: b
在第二段代码中,传入Buffer.from的参数类型是arrayBuffer。因此Buffer.from仅仅是创建视图,而不是拷贝底层内存。buf1 和 arr 的内存是共享的。
在操作 Buffer 的过程中,需要特别注意共享和拷贝的区别,发生错误比较难排查。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
NodeJS,模块,Buffer
《魔兽世界》大逃杀!60人新游玩模式《强袭风暴》3月21日上线
暴雪近日发布了《魔兽世界》10.2.6 更新内容,新游玩模式《强袭风暴》即将于3月21 日在亚服上线,届时玩家将前往阿拉希高地展开一场 60 人大逃杀对战。
艾泽拉斯的冒险者已经征服了艾泽拉斯的大地及遥远的彼岸。他们在对抗世界上最致命的敌人时展现出过人的手腕,并且成功阻止终结宇宙等级的威胁。当他们在为即将于《魔兽世界》资料片《地心之战》中来袭的萨拉塔斯势力做战斗准备时,他们还需要在熟悉的阿拉希高地面对一个全新的敌人──那就是彼此。在《巨龙崛起》10.2.6 更新的《强袭风暴》中,玩家将会进入一个全新的海盗主题大逃杀式限时活动,其中包含极高的风险和史诗级的奖励。
《强袭风暴》不是普通的战场,作为一个独立于主游戏之外的活动,玩家可以用大逃杀的风格来体验《魔兽世界》,不分职业、不分装备(除了你在赛局中捡到的),光是技巧和战略的强弱之分就能决定出谁才是能坚持到最后的赢家。本次活动将会开放单人和双人模式,玩家在加入海盗主题的预赛大厅区域前,可以从强袭风暴角色画面新增好友。游玩游戏将可以累计名望轨迹,《巨龙崛起》和《魔兽世界:巫妖王之怒 经典版》的玩家都可以获得奖励。
更新日志
- 小骆驼-《草原狼2(蓝光CD)》[原抓WAV+CUE]
- 群星《欢迎来到我身边 电影原声专辑》[320K/MP3][105.02MB]
- 群星《欢迎来到我身边 电影原声专辑》[FLAC/分轨][480.9MB]
- 雷婷《梦里蓝天HQⅡ》 2023头版限量编号低速原抓[WAV+CUE][463M]
- 群星《2024好听新歌42》AI调整音效【WAV分轨】
- 王思雨-《思念陪着鸿雁飞》WAV
- 王思雨《喜马拉雅HQ》头版限量编号[WAV+CUE]
- 李健《无时无刻》[WAV+CUE][590M]
- 陈奕迅《酝酿》[WAV分轨][502M]
- 卓依婷《化蝶》2CD[WAV+CUE][1.1G]
- 群星《吉他王(黑胶CD)》[WAV+CUE]
- 齐秦《穿乐(穿越)》[WAV+CUE]
- 发烧珍品《数位CD音响测试-动向效果(九)》【WAV+CUE】
- 邝美云《邝美云精装歌集》[DSF][1.6G]
- 吕方《爱一回伤一回》[WAV+CUE][454M]