• 【NODE.JS】Buffer


    Buffer是一个Array的对象,主要用于操作字节。

    目录

    1. 模块结构

    2. Buffer对象

    3. Buffer内存分配

    4. Buffer的转换

    5. Buffer的拼接

    6. 正确拼接Buffer


    1. 模块结构

    Buffer是一个的JavaScript与C++结合的模块。性能相关的部分由C++实现,非性能相关的部分用JavaScript实现。

     Buffer所占的内存不是通过V8分配的,属于堆外内存。

    Node在进程启动时就家长了Buffer,并将其放置在全局对象上,所以无需require() 直接使用。

    2. Buffer对象

    其类似数组,元素为16进制的两位数,即0到255的数值

    可以通过访问length属性得到长度,同时可以通过下标访问元素

    如果元素赋值小于0,就将该值逐次加到256,直到得到一个0到255之间的整数。

    如果大于255,就逐次减256,直到得到0到255区间内的值

    如果是小数,则舍弃小数部分,只保留整数部分

    3. Buffer内存分配

    Buffer对象的内存分配不是在V8中的堆内存中,而是在Node的C++层面实现内存的申请的。

    Node在内存使用上应用的是在C++层面申请内存、在JavaScript中分配内存的策略。

    slab分配机制

    一种动态内存管理机制,slab就是一块申请好的固定大小的内存区域,具有三种状态

    full:完全分配状态

    partial:部分分配状态

    empty:没有被分配状态

     

    4. Buffer的转换

    Buffer对象可以与字符串之间相互转换:

    ASCII、UTF-8、UTF-16LE/UCS-2、Base64、Binary、Hex

    (1)字符串转Buffer

    通过构造函数完成

    new Buffer(str, [encoding]);

    一个Buffer对象可以存储不同编码类型的字符串转码的值,可以调用write()方法实现

    buf.write(string, [offset], [length], [encoding])

    (2)Buffer转字符串

    buf.toString([encoding], [start], [end])

     可以设置endcoding,start、end三个三参数实现整体或者局部的转换

    (3)Buffer不支持的编码类型

    由于支持的编码类型有限, 可以使用以下方法判断是否支持转换

    Buffer.isEncoding(encoding)

    将编码类型输入,就可以知道是否支持转换,对于不支持的编码可以使用 iconv 和 iconv-lite两个模块支持。

    iconv-lite 采用纯JS实现,iconv通过C++调用libiconv库实现

    前者比后者更加轻量,无须编译和处理环境依赖直接使用。

    5. Buffer的拼接

    1. var fs = require('fs');
    2. var rs = fs.createReadSteam('test.md');
    3. var data = '';
    4. rs.on("data", funciton (chunk){
    5. data += chunk;
    6. });
    7. rs.on("end", funciton (){
    8. console.log(data);
    9. })

     正常读取字符流的过程,但是在英文环境中是没有问题的,在中文环境中会出现问题。

    因为 

    data += chunk;

    实际上执行或者等价的代码是

    data = data.toString() + chunk.toString();

    所以当我们如果限制每次读取的Buffer长度时,就有可能在读取中文时出现乱码,最简单的解决办法就是提高每一次读取的长度,长度越大出现的概率越低

    同时可以通过设置编码的方法

    readable.setEncoding(encoding)

    此方法在被调用时,可读流对象内部设置了一个decoder对象。

    每次data事件都通过该decoder对象进行Buffer到字符串的解码,然后传递给调用者。

    通过setEncoding() 的方式不可否认能解决大部分的乱码问题,但并不能从根本上解决该问题。

    6. 正确拼接Buffer

    淘汰掉setEncoding() 方法后,剩下的解决方案只有将多个小Buffer对象拼接为一个Buffer对象,然后通过iconv-tite一类的模块来转码方式。+=的方式显然不行,正确的Buffer拼接方法应该如下面展示的形式:

    1. var chunks = [];
    2. var size = 0;
    3. res.on('data', function (chunk){
    4. chunks.push(chunk);
    5. size += chunk.length;
    6. });
    7. res.on('end', funciton (){
    8. var buf = Buffer.concat(chunks, size);
    9. var str = iconv.decode(buf, 'utf8');
    10. });

    正确的拼接方式是用一个数组来存储接收到的所有Buffer片段并记录下所有片段的总长度,然后调用Buffer.concat() 方法生成一个合成的Buffer对象。Buffer.concat() 方法封装了从小Buffer对象向大Buffer对象的复制过程, 实现过程十分细腻,值得围观学习:

    1. Buffer.concat = funcion(list, length){
    2. if(!Array.isArray(list)) {
    3. throw new Error('Usage: Buffer.concat(list, [length])');
    4. }
    5. if(list.length === 0) {
    6. return new Buffer(0);
    7. }else if (list.length === 1) {
    8. return linst[0];
    9. }
    10. if (typeof length !== 'number') {
    11. length = 0;
    12. for(var i = 0; i < list.length; i++) {
    13. var buf = list[i];
    14. length += buf.length;
    15. }
    16. }
    17. var buffer = new Buffer(length);
    18. var pos = 0;
    19. for (var i = 0; i < list.length; i++) {
    20. var buf = list[i];
    21. buf.copy(buffer, pos);
    22. pos += buf.length;
    23. }
    24. return buffer;
    25. }

  • 相关阅读:
    emqx启用JWT令牌认证(包含hmac-based和public-key)
    【嵌入式基础】内存(Cache,RAM,ROM,Flash)
    亚马逊刷关键词软件——亚马逊鲲鹏系统
    【Spring Cloud】深入探索 Nacos 注册中心的原理,服务的注册与发现,服务分层模型,负载均衡策略,微服务的权重设置,环境隔离
    C++ 之 perf+火焰图分析与Debug
    MA-SAM:模态不可知的三维医学图像分割SAM自适应
    进程与线程的相爱相杀
    ren域名有价值吗?值不值得投资?ren域名的应用范围有哪些?
    关于dcmtk对JPEG2000的支持
    etoken是什么意思,有什么作用?
  • 原文地址:https://blog.csdn.net/weixin_42078672/article/details/127833023