zoukankan      html  css  js  c++  java
  • 深入Node模块Buffer-学会操作二进制

    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: a

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

    在第二段代码中,传入Buffer.from的参数类型是arrayBuffer。因此Buffer.from仅仅是创建视图,而不是拷贝底层内存。buf1 和 arr 的内存是共享的。

    在操作 Buffer 的过程中,需要特别注意共享和拷贝的区别,发生错误比较难排查。

    参考链接

    最后

    1. 觉得不错,帮忙点个推荐呗,您的支持是对我最大的激励
    2. 欢迎我的公众号:「心谭博客」,只专注于前端 + 算法的原创分享

    由于个人精力有限,很多系列和历史文章没有即时同步,请前往「前端图谱」&「算法题解」,保证您有所收获。

  • 相关阅读:
    R语言-单一变量分析
    计算机网络和Internet之核心网络
    android Gui系统之WMS(1)----window flags & view flags
    Computer Network and Internet(1)
    android Gui系统之SurfaceFlinger(5)---Vsync(2)
    android Gui系统之SurfaceFlinger(4)---Vsync(1)
    android Gui系统之SurfaceFlinger(3)---SurfaceFlinger
    android Gui系统之SurfaceFlinger(2)---BufferQueue
    android Gui系统之SurfaceFlinger(1)---SurfaceFlinger概论
    敏捷软件开发(4)--- TEMPLATE METHOD & STRATEGY 模式
  • 原文地址:https://www.cnblogs.com/geyouneihan/p/12249010.html
Copyright © 2011-2022 走看看