buffer-util.js
  1  'use strict';
  2  
  3  const { EMPTY_BUFFER } = require('./constants');
  4  
  5  /**
  6   * Merges an array of buffers into a new buffer.
  7   *
  8   * @param {Buffer[]} list The array of buffers to concat
  9   * @param {Number} totalLength The total length of buffers in the list
 10   * @return {Buffer} The resulting buffer
 11   * @public
 12   */
 13  function concat(list, totalLength) {
 14    if (list.length === 0) return EMPTY_BUFFER;
 15    if (list.length === 1) return list[0];
 16  
 17    const target = Buffer.allocUnsafe(totalLength);
 18    let offset = 0;
 19  
 20    for (let i = 0; i < list.length; i++) {
 21      const buf = list[i];
 22      target.set(buf, offset);
 23      offset += buf.length;
 24    }
 25  
 26    if (offset < totalLength) return target.slice(0, offset);
 27  
 28    return target;
 29  }
 30  
 31  /**
 32   * Masks a buffer using the given mask.
 33   *
 34   * @param {Buffer} source The buffer to mask
 35   * @param {Buffer} mask The mask to use
 36   * @param {Buffer} output The buffer where to store the result
 37   * @param {Number} offset The offset at which to start writing
 38   * @param {Number} length The number of bytes to mask.
 39   * @public
 40   */
 41  function _mask(source, mask, output, offset, length) {
 42    for (let i = 0; i < length; i++) {
 43      output[offset + i] = source[i] ^ mask[i & 3];
 44    }
 45  }
 46  
 47  /**
 48   * Unmasks a buffer using the given mask.
 49   *
 50   * @param {Buffer} buffer The buffer to unmask
 51   * @param {Buffer} mask The mask to use
 52   * @public
 53   */
 54  function _unmask(buffer, mask) {
 55    // Required until https://github.com/nodejs/node/issues/9006 is resolved.
 56    const length = buffer.length;
 57    for (let i = 0; i < length; i++) {
 58      buffer[i] ^= mask[i & 3];
 59    }
 60  }
 61  
 62  /**
 63   * Converts a buffer to an `ArrayBuffer`.
 64   *
 65   * @param {Buffer} buf The buffer to convert
 66   * @return {ArrayBuffer} Converted buffer
 67   * @public
 68   */
 69  function toArrayBuffer(buf) {
 70    if (buf.byteLength === buf.buffer.byteLength) {
 71      return buf.buffer;
 72    }
 73  
 74    return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
 75  }
 76  
 77  /**
 78   * Converts `data` to a `Buffer`.
 79   *
 80   * @param {*} data The data to convert
 81   * @return {Buffer} The buffer
 82   * @throws {TypeError}
 83   * @public
 84   */
 85  function toBuffer(data) {
 86    toBuffer.readOnly = true;
 87  
 88    if (Buffer.isBuffer(data)) return data;
 89  
 90    let buf;
 91  
 92    if (data instanceof ArrayBuffer) {
 93      buf = Buffer.from(data);
 94    } else if (ArrayBuffer.isView(data)) {
 95      buf = Buffer.from(data.buffer, data.byteOffset, data.byteLength);
 96    } else {
 97      buf = Buffer.from(data);
 98      toBuffer.readOnly = false;
 99    }
100  
101    return buf;
102  }
103  
104  try {
105    const bufferUtil = require('bufferutil');
106    const bu = bufferUtil.BufferUtil || bufferUtil;
107  
108    module.exports = {
109      concat,
110      mask(source, mask, output, offset, length) {
111        if (length < 48) _mask(source, mask, output, offset, length);
112        else bu.mask(source, mask, output, offset, length);
113      },
114      toArrayBuffer,
115      toBuffer,
116      unmask(buffer, mask) {
117        if (buffer.length < 32) _unmask(buffer, mask);
118        else bu.unmask(buffer, mask);
119      }
120    };
121  } catch (e) /* istanbul ignore next */ {
122    module.exports = {
123      concat,
124      mask: _mask,
125      toArrayBuffer,
126      toBuffer,
127      unmask: _unmask
128    };
129  }