Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

buffer 缓冲区

基础

源代码:lib/buffer.js

作用:表示固定长度的字节序列,

  • 是js中Uinit8Array类的子类
  • 是直接处理二进制数据的全局类型

用法

  • 引入

    • es6
      import Buffer from 'buffer';
      
    • commonjs
      const Buffer = require('buffer')
      
  • 分配

    • v16

      //创建长度为10字节的缓冲区
      const buf1 = Buffer.alloc(10)
      //最大为 buffer.constants.MAX_LENGTH个字节
      
      //这个会更快,但是可能有旧数据残留
      const buf2 = Buffer.allocUnsafe(10);
      //是从预先分配的池子(大小Buffer.poolSize)中取的,一般4KB一下会取
      
      //这个不会从池子里面取,会稍慢,用户保留一小块内存的情况
      const buf3 = Buffer.allocUnsafeSlow(10);
      
    • v15以前

      const buf1 = new Buffer(10)
      
  • 填充

    • 公式 buf.fill(value[, offset[, end]][, encoding])
    
    
  • 分配+填充

    • v16

      //创建10字节缓冲区,每个字节都用值1去填充
      const buf4 = Buffer.alloc(10,1)
      //参数2不填则默认填充0
      //原理:通过调用 buf.fill(fill) 进行初始化
      
      //创建包含字节1、2、3的缓冲区
      const buf5 = Buffer.from([1, 2, 3]);
      这里面的每个元素,都要在0-255范围内
      否则默认只取低八位
      
      for (const b of buf5) {
          console.log(b);
      }
      // 打印:
      //   1
      //   2
      //   3
      
      //创建包含字符串的UTF-8字节的缓冲区
      const buf6 = Buffer.from('tést');
      
    • v15以前

      const buf6 = new Buffer('tést')
      
  • 指定字符编码

    const buf7 = Buffer.alloc(11, 'aGVsbG8gd29ybGQ=', 'base64');
    console.log(buf7);
    // 打印: <Buffer 68 65 6c 6c 6f 20 77 6f 72 6c 64>
    
    const buf8 = Buffer.from('hello world', 'utf8');
    
    console.log(buf8.toString('hex'));
    // 打印: 68656c6c6f20776f726c64
    console.log(buf8.toString('base64'));
    // 打印: aGVsbG8gd29ybGQ=
    

    将 Buffer 转换为 字符串 称为解码 将 字符串 转换为 Buffer 称为编码

  • Buffer.byteLength获取字符串占用字节

    const str = '\u00bd + \u00bc = \u00be';
    
    console.log(`${str}: ${str.length} characters, ` +
                `${Buffer.byteLength(str, 'utf8')} bytes`);
    // 打印: ½ + ¼ = ¾: 9 characters, 12 bytes
    
  • Buffer.compare多用于排序

const buf1 = Buffer.from('1234');
const buf2 = Buffer.from('0123');
const arr = [buf1, buf2];

console.log(arr.sort(Buffer.compare));
// 打印: [ <Buffer 30 31 32 33>, <Buffer 31 32 33 34> ]
// (此结果相当于:[buf2, buf1]。)
  • Buffer.concat拼接

    const buf1 = Buffer.alloc(10);
    const buf2 = Buffer.alloc(14);
    const buf3 = Buffer.alloc(18);
    const totalLength = buf1.length + buf2.length + buf3.length;
    
    console.log(totalLength);
    // 打印: 42
    
    const bufA = Buffer.concat([buf1, buf2, buf3], totalLength);
    //totalLength可省略,会自动算
    
    console.log(bufA);
    // 打印: <Buffer 00 00 00 00 ...>
    console.log(bufA.length);
    // 打印: 42
    
  • Buffer.from全量复制

    const buf1 = Buffer.from('buffer');
    const buf2 = Buffer.from(buf1);
    
    buf1[0] = 0x61;
    
    console.log(buf1.toString());
    // 打印: auffer
    console.log(buf2.toString());
    // 打印: buffer
    
  • buf.copy部分复制

    • 复制到另一个buf
      // 创建两个 `Buffer` 实例。
      const buf1 = Buffer.allocUnsafe(26);
      const buf2 = Buffer.allocUnsafe(26).fill('!');
      
      for (let i = 0; i < 26; i++) {
        // 97 是 'a' 的十进制 ASCII 值。
        buf1[i] = i + 97;
      }
      
      // 将 `buf1` 字节 16 到 19 复制到 `buf2` 中,从 `buf2` 的字节 8 开始。
      buf1.copy(buf2, 8, 16, 20);
      // 这相当于:
      // buf2.set(buf1.subarray(16, 20), 8);
      
      console.log(buf2.toString('ascii', 0, 25));
      // 打印: !!!!!!!!qrst!!!!!!!!!!!!!
      
    • 复制到自己上面重叠区域
      const buf = Buffer.allocUnsafe(26);
      
      for (let i = 0; i < 26; i++) {
        // 97 是 'a' 的十进制 ASCII 值。
        buf[i] = i + 97;
      }
      
      buf.copy(buf, 0, 4, 10);
      
      console.log(buf.toString());
      // 打印: efghijghijklmnopqrstuvwxyz
      
  • 判断

    • Buffer.isBuffer(obj)是否是buffer对象
    • Buffer.isEncoding(encoding)内部是否支持此编码
  • 常量

    • Buffer.poolSize默认为8192(字节)= 8KB
  • buf[i]通过索引访问和修改单个字节

    const str = 'Node.js';
    const buf = Buffer.allocUnsafe(str.length);
    
    for (let i = 0; i < str.length; i++) {
      buf[i] = str.charCodeAt(i);
    }
    
    console.log(buf.toString('utf8'));
    // 打印: Node.js
    
  • 迭代器

    • buf.entries()
      const buf = Buffer.from('bu');
      
      for (const pair of buf.entries()) {
        console.log(pair);
      }
      // 打印:
      //   [0, 98]
      //   [1, 117]
      
    • buf.keys()
    • buf.values()
  • buf.equals(otherBuffer)比较是否有相同字节

  • 查找

    • buf.indexOf(value)返回出现的第一个索引位置
    • buf.lastIndexOf(value)返回出现的最后第一个索引位置
    • buf.includes(value)判断是否有
  • 等价的两种方法——返回新Buffer

    • buf.slice(start[,end])
      • 返回新的Buffer,但是引用相同内存!!!
      • 这跟Array对象里面的slice复制有差异!!!
      • 如果要想复制,可以采用如下办法
        const copiedBuf = Uint8Array.prototype.slice.call(buf);
        
    • buf.subarray(start[,end])
      • 这个跟上面slice一样,也是返回新的Buffer,引用相同内存

TypedArray转化为Buffer

import { Buffer } from 'buffer';

const arr = new Uint16Array(2);

arr[0] = 5000;
arr[1] = 4000;

// 复制 `arr` 的内容。
const buf1 = Buffer.from(arr);

// 与 `arr` 共享内存。
const buf2 = Buffer.from(arr.buffer);

console.log(buf1);
// 打印: <Buffer 88 a0>
console.log(buf2);
// 打印: <Buffer 88 13 a0 0f>

arr[1] = 6000;

console.log(buf1);
// 打印: <Buffer 88 a0>
console.log(buf2);
// 打印: <Buffer 88 13 70 17>

arr[1] = 3000;

console.log(buf1);
// 打印: <Buffer 88 a0>
console.log(buf2);
// 打印: <Buffer b8 0b 70 17>

buffer.

分析可见

  • 复制arr的内容,根本得不到真正的数据
  • 要使用arr.buffer才能得到,但代价就是需要共享内存

可以只取一部分变成buffer 方法:指定偏移量和截取长度

import { Buffer } from 'buffer';

const arr = new Uint16Array(20);
const buf = Buffer.from(arr.buffer, 0, 16);
//从0号开始,截取16个

console.log(buf.length);
// 打印: 16

Buffer转化为TypedArray

import { Buffer } from 'buffer';

const buf = Buffer.from([1, 2, 3, 4]);

//以buf为参数
const uint32array = new Uint32Array(buf);
console.log(uint32array);
// 打印: Uint32Array(4) [ 1, 2, 3, 4 ]

//以buf.buffer为参数
const uint16array = new Uint16Array(
  buf.buffer,
  buf.byteOffset,
  buf.length / Uint16Array.BYTES_PER_ELEMENT
);
console.log(uint16array);
// 打印: Uint16Array(2) [ 513, 1027 ]

分析可见

  • 以buf为参数,会把每个元素解释为整型,赋予数组
  • 以buf.buffer为参数,单个元素占16位时,由于共享内存,所以直接合并两个字节,解释为单个数组元素

15.7版本新增Bolb类(目前仍是实验性功能)

作用:封装了不可变的原始数据,可在多个工作线程之间安全地共享

import { Blob } from 'buffer';

const blob = new Blob(['hello there']);

blob.text().then(console.log);
//打印:hello there

配套有MessagePort类,可以发送多个目的地

好处:无需立即复制数据,只有在调用 arrayBuffer() 或 text() 方法时才会复制

const mc1 = new MessageChannel();

mc1.port1.onmessage = async ({ data }) => {
  console.log(await data.arrayBuffer());
  mc1.port1.close();
};

mc1.port2.postMessage(blob);

//可以新建多个MessageChannel