Node 学习小结 - Buffer生成策略

栏目: C++ · 发布时间: 5年前

内容简介:在node 8.0之前,buffer主要通过构造函数使用new Buffer(...)的方式来创建。因为根据参数的不同,生成buffer的行为和结果都大不相同,所以从node 8.0版本开始,buffer的创建被拆分到几个不同的方法(from, alloc, allocUnsafe)。在看详细的方法之前,需要知道的是,node为了节约系统资源提高效率,先预申请了一部分内存作为buffer pool。 如果申请的buffer的大小比较小的情况下,会优先从这个pool里面进行分配。 这部分操作,都放到了allo

在node 8.0之前,buffer主要通过构造函数使用new Buffer(...)的方式来创建。因为根据参数的不同,生成buffer的行为和结果都大不相同,所以从node 8.0版本开始,buffer的创建被拆分到几个不同的方法(from, alloc, allocUnsafe)。

在看详细的方法之前,需要知道的是,node为了节约系统资源提高效率,先预申请了一部分内存作为buffer pool。 如果申请的buffer的大小比较小的情况下,会优先从这个pool里面进行分配。 这部分操作,都放到了allocate函数里面:

function allocate(size) {
  if (size <= 0) {
    return new FastBuffer();  // 没size,直接返回一个空buffer
  }
  if (size < (Buffer.poolSize >>> 1)) {   // size小于pool的一半(4kb)
    if (size > (poolSize - poolOffset))  // size比pool剩下的空间大的话,就再申请一个pool
      createPool();
    const b = new FastBuffer(allocPool, poolOffset, size);
    poolOffset += size;
    alignPool();
    return b;
  }
  return createUnsafeBuffer(size);  // 如果size比较大, 则直接申请一个未初始化的buffer
}
复制代码

Buffer.from

Buffer.from比较复杂,根据入参大致有3种不同的处理方式:

Buffer.from(string, encoding)

当传入的第一个参数为一个字符串时,会调用对应的fromString方法,代码如下:

function fromString(string, encoding) {
  // 确定string在encode后的大小,如果string为0,则返回一个空buffer
  let length;
  if (typeof encoding !== 'string' || encoding.length === 0) {
    if (string.length === 0)
      return new FastBuffer();
    encoding = 'utf8';
    length = byteLengthUtf8(string);
  } else {
    length = byteLength(string, encoding, true);
    if (length === -1)
      throw new ERR_UNKNOWN_ENCODING(encoding);
    if (string.length === 0)
      return new FastBuffer();
  }

  // 如果大小大于poolSize的一半(4kb), 直接分配内存。
  if (length >= (Buffer.poolSize >>> 1))
    return createFromString(string, encoding);

  // 如果大小大于pool里剩下的内存,则新建一个pool
  if (length > (poolSize - poolOffset))
    createPool();
  let b = new FastBuffer(allocPool, poolOffset, length);
  const actual = b.write(string, encoding);
  if (actual !== length) {
    b = new FastBuffer(allocPool, poolOffset, actual);
  }
  poolOffset += actual;
  alignPool();
  return b;
}
复制代码

c++扩展直接分配内存:

void CreateFromString(const FunctionCallbackInfo<Value>& args) {
  CHECK(args[0]->IsString());
  CHECK(args[1]->IsString());

  enum encoding enc = ParseEncoding(args.GetIsolate(),
                                    args[1].As<String>(),
                                    UTF8);
  Local<Object> buf;

  if (New(args.GetIsolate(), args[0].As<String>(), enc).ToLocal(&buf))
    args.GetReturnValue().Set(buf);
}

复制代码

总结下来就是3个步骤:

  1. 通过encode的方式,确定最终需要的buffer大小
  2. 如果是个大buffer(>4kb),那就不走buffer pool了,直接通过c++拓展分配内存。
  3. 小buffer的话,看看上一个pool剩下的内存还够不够,够的话继续用,不够了就再新建一个内存来分配。

Buffer.from(arrayBuffer)

function fromArrayBuffer(obj, byteOffset, length) {
  ... byteOffset校对逻辑
  return new FastBuffer(obj, byteOffset, length);
}
复制代码

如果传入的是一个arrayBuffer, 那就直接生成一个FastBuffer对象,而FastBuffer直接继承了Uint8Array,根据typedArray的定义,如果传入一个ArrayBuffer,那就返回 一个新的TypedArray来共享这段内存

When called with a buffer, and optionally a byteOffset and a length argument, a new typed array view is created that views the specified ArrayBuffer. The byteOffset and length parameters specify the memory range that will be exposed by the typed array view. If both are omitted, all of buffer is viewed; if only length is omitted, the remainder of buffer is viewed.

Buffer.from(array/buffer/typedArray)

function fromObject(obj) {
  if (isUint8Array(obj)) {
    const b = allocate(obj.length);   // // 从pool里分配或者直接分配

    if (b.length === 0)
      return b;

    _copy(obj, b, 0, 0, obj.length);  // // 把数据拷贝到新分配的内存上。
    return b;
  }

  if (obj.length !== undefined || isAnyArrayBuffer(obj.buffer)) {
    if (typeof obj.length !== 'number') {
      return new FastBuffer();
    }
    return fromArrayLike(obj);
  }

  if (obj.type === 'Buffer' && Array.isArray(obj.data)) {
    return fromArrayLike(obj.data);
  }
}

复制代码
// 把类数组的数据(array/buffer)都写入新的buffer
function fromArrayLike(obj) {
  const length = obj.length;
  const b = allocate(length);
  for (var i = 0; i < length; i++)
    b[i] = obj[i];
  return b;
}
复制代码

Buffer.alloc

Buffer.alloc(size, fill, encoding)接受3个参数,返回一个被初始化了的buffer:

Buffer.alloc = function alloc(size, fill, encoding) {
  assertSize(size);
  if (fill !== undefined && fill !== 0 && size > 0) {
    const buf = createUnsafeBuffer(size);
    return _fill(buf, fill, 0, buf.length, encoding);
  }
  return new FastBuffer(size);
};

// 创建一个未初始化的buffer
function createUnsafeBuffer(size) {
  zeroFill[0] = 0;
  try {
    return new FastBuffer(size);
  } finally {
    zeroFill[0] = 1;
  }
}
复制代码

通过FastBuffer直接生成一个Uint8Array, 如果传了fill,则还要对生成的Uint8Array做一个填充操作。

Buffer.allocUnsafe

这个就简单了,直接调用allocate函数申请内存

Buffer.allocUnsafe = function allocUnsafe(size) {
  assertSize(size);
  return allocate(size);
};
复制代码

对应allocUnsafe还有一个allocUnsafeSlow函数,区别就是它直接申请了内存,没有走pool。

Buffer.allocUnsafeSlow = function allocUnsafeSlow(size) {
  assertSize(size);
  return createUnsafeBuffer(size);
};
复制代码

参考文档

  1. node github仓库
  2. node官方文档

以上所述就是小编给大家介绍的《Node 学习小结 - Buffer生成策略》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Data Structures and Algorithms

Data Structures and Algorithms

Alfred V. Aho、Jeffrey D. Ullman、John E. Hopcroft / Addison Wesley / 1983-1-11 / USD 74.20

The authors' treatment of data structures in Data Structures and Algorithms is unified by an informal notion of "abstract data types," allowing readers to compare different implementations of the same......一起来看看 《Data Structures and Algorithms》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

随机密码生成器
随机密码生成器

多种字符组合密码

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具