| Leo Repp | 58b9f11 | 2021-11-22 11:57:47 +0100 | [diff] [blame^] | 1 | var DuplexStream = require('readable-stream/duplex') |
| 2 | , util = require('util') |
| 3 | , Buffer = require('safe-buffer').Buffer |
| 4 | |
| 5 | |
| 6 | function BufferList (callback) { |
| 7 | if (!(this instanceof BufferList)) |
| 8 | return new BufferList(callback) |
| 9 | |
| 10 | this._bufs = [] |
| 11 | this.length = 0 |
| 12 | |
| 13 | if (typeof callback == 'function') { |
| 14 | this._callback = callback |
| 15 | |
| 16 | var piper = function piper (err) { |
| 17 | if (this._callback) { |
| 18 | this._callback(err) |
| 19 | this._callback = null |
| 20 | } |
| 21 | }.bind(this) |
| 22 | |
| 23 | this.on('pipe', function onPipe (src) { |
| 24 | src.on('error', piper) |
| 25 | }) |
| 26 | this.on('unpipe', function onUnpipe (src) { |
| 27 | src.removeListener('error', piper) |
| 28 | }) |
| 29 | } else { |
| 30 | this.append(callback) |
| 31 | } |
| 32 | |
| 33 | DuplexStream.call(this) |
| 34 | } |
| 35 | |
| 36 | |
| 37 | util.inherits(BufferList, DuplexStream) |
| 38 | |
| 39 | |
| 40 | BufferList.prototype._offset = function _offset (offset) { |
| 41 | var tot = 0, i = 0, _t |
| 42 | if (offset === 0) return [ 0, 0 ] |
| 43 | for (; i < this._bufs.length; i++) { |
| 44 | _t = tot + this._bufs[i].length |
| 45 | if (offset < _t || i == this._bufs.length - 1) |
| 46 | return [ i, offset - tot ] |
| 47 | tot = _t |
| 48 | } |
| 49 | } |
| 50 | |
| 51 | |
| 52 | BufferList.prototype.append = function append (buf) { |
| 53 | var i = 0 |
| 54 | |
| 55 | if (Buffer.isBuffer(buf)) { |
| 56 | this._appendBuffer(buf); |
| 57 | } else if (Array.isArray(buf)) { |
| 58 | for (; i < buf.length; i++) |
| 59 | this.append(buf[i]) |
| 60 | } else if (buf instanceof BufferList) { |
| 61 | // unwrap argument into individual BufferLists |
| 62 | for (; i < buf._bufs.length; i++) |
| 63 | this.append(buf._bufs[i]) |
| 64 | } else if (buf != null) { |
| 65 | // coerce number arguments to strings, since Buffer(number) does |
| 66 | // uninitialized memory allocation |
| 67 | if (typeof buf == 'number') |
| 68 | buf = buf.toString() |
| 69 | |
| 70 | this._appendBuffer(Buffer.from(buf)); |
| 71 | } |
| 72 | |
| 73 | return this |
| 74 | } |
| 75 | |
| 76 | |
| 77 | BufferList.prototype._appendBuffer = function appendBuffer (buf) { |
| 78 | this._bufs.push(buf) |
| 79 | this.length += buf.length |
| 80 | } |
| 81 | |
| 82 | |
| 83 | BufferList.prototype._write = function _write (buf, encoding, callback) { |
| 84 | this._appendBuffer(buf) |
| 85 | |
| 86 | if (typeof callback == 'function') |
| 87 | callback() |
| 88 | } |
| 89 | |
| 90 | |
| 91 | BufferList.prototype._read = function _read (size) { |
| 92 | if (!this.length) |
| 93 | return this.push(null) |
| 94 | |
| 95 | size = Math.min(size, this.length) |
| 96 | this.push(this.slice(0, size)) |
| 97 | this.consume(size) |
| 98 | } |
| 99 | |
| 100 | |
| 101 | BufferList.prototype.end = function end (chunk) { |
| 102 | DuplexStream.prototype.end.call(this, chunk) |
| 103 | |
| 104 | if (this._callback) { |
| 105 | this._callback(null, this.slice()) |
| 106 | this._callback = null |
| 107 | } |
| 108 | } |
| 109 | |
| 110 | |
| 111 | BufferList.prototype.get = function get (index) { |
| 112 | return this.slice(index, index + 1)[0] |
| 113 | } |
| 114 | |
| 115 | |
| 116 | BufferList.prototype.slice = function slice (start, end) { |
| 117 | if (typeof start == 'number' && start < 0) |
| 118 | start += this.length |
| 119 | if (typeof end == 'number' && end < 0) |
| 120 | end += this.length |
| 121 | return this.copy(null, 0, start, end) |
| 122 | } |
| 123 | |
| 124 | |
| 125 | BufferList.prototype.copy = function copy (dst, dstStart, srcStart, srcEnd) { |
| 126 | if (typeof srcStart != 'number' || srcStart < 0) |
| 127 | srcStart = 0 |
| 128 | if (typeof srcEnd != 'number' || srcEnd > this.length) |
| 129 | srcEnd = this.length |
| 130 | if (srcStart >= this.length) |
| 131 | return dst || Buffer.alloc(0) |
| 132 | if (srcEnd <= 0) |
| 133 | return dst || Buffer.alloc(0) |
| 134 | |
| 135 | var copy = !!dst |
| 136 | , off = this._offset(srcStart) |
| 137 | , len = srcEnd - srcStart |
| 138 | , bytes = len |
| 139 | , bufoff = (copy && dstStart) || 0 |
| 140 | , start = off[1] |
| 141 | , l |
| 142 | , i |
| 143 | |
| 144 | // copy/slice everything |
| 145 | if (srcStart === 0 && srcEnd == this.length) { |
| 146 | if (!copy) { // slice, but full concat if multiple buffers |
| 147 | return this._bufs.length === 1 |
| 148 | ? this._bufs[0] |
| 149 | : Buffer.concat(this._bufs, this.length) |
| 150 | } |
| 151 | |
| 152 | // copy, need to copy individual buffers |
| 153 | for (i = 0; i < this._bufs.length; i++) { |
| 154 | this._bufs[i].copy(dst, bufoff) |
| 155 | bufoff += this._bufs[i].length |
| 156 | } |
| 157 | |
| 158 | return dst |
| 159 | } |
| 160 | |
| 161 | // easy, cheap case where it's a subset of one of the buffers |
| 162 | if (bytes <= this._bufs[off[0]].length - start) { |
| 163 | return copy |
| 164 | ? this._bufs[off[0]].copy(dst, dstStart, start, start + bytes) |
| 165 | : this._bufs[off[0]].slice(start, start + bytes) |
| 166 | } |
| 167 | |
| 168 | if (!copy) // a slice, we need something to copy in to |
| 169 | dst = Buffer.allocUnsafe(len) |
| 170 | |
| 171 | for (i = off[0]; i < this._bufs.length; i++) { |
| 172 | l = this._bufs[i].length - start |
| 173 | |
| 174 | if (bytes > l) { |
| 175 | this._bufs[i].copy(dst, bufoff, start) |
| 176 | bufoff += l |
| 177 | } else { |
| 178 | this._bufs[i].copy(dst, bufoff, start, start + bytes) |
| 179 | bufoff += l |
| 180 | break |
| 181 | } |
| 182 | |
| 183 | bytes -= l |
| 184 | |
| 185 | if (start) |
| 186 | start = 0 |
| 187 | } |
| 188 | |
| 189 | // safeguard so that we don't return uninitialized memory |
| 190 | if (dst.length > bufoff) return dst.slice(0, bufoff) |
| 191 | |
| 192 | return dst |
| 193 | } |
| 194 | |
| 195 | BufferList.prototype.shallowSlice = function shallowSlice (start, end) { |
| 196 | start = start || 0 |
| 197 | end = end || this.length |
| 198 | |
| 199 | if (start < 0) |
| 200 | start += this.length |
| 201 | if (end < 0) |
| 202 | end += this.length |
| 203 | |
| 204 | var startOffset = this._offset(start) |
| 205 | , endOffset = this._offset(end) |
| 206 | , buffers = this._bufs.slice(startOffset[0], endOffset[0] + 1) |
| 207 | |
| 208 | if (endOffset[1] == 0) |
| 209 | buffers.pop() |
| 210 | else |
| 211 | buffers[buffers.length-1] = buffers[buffers.length-1].slice(0, endOffset[1]) |
| 212 | |
| 213 | if (startOffset[1] != 0) |
| 214 | buffers[0] = buffers[0].slice(startOffset[1]) |
| 215 | |
| 216 | return new BufferList(buffers) |
| 217 | } |
| 218 | |
| 219 | BufferList.prototype.toString = function toString (encoding, start, end) { |
| 220 | return this.slice(start, end).toString(encoding) |
| 221 | } |
| 222 | |
| 223 | BufferList.prototype.consume = function consume (bytes) { |
| 224 | // first, normalize the argument, in accordance with how Buffer does it |
| 225 | bytes = Math.trunc(bytes) |
| 226 | // do nothing if not a positive number |
| 227 | if (Number.isNaN(bytes) || bytes <= 0) return this |
| 228 | |
| 229 | while (this._bufs.length) { |
| 230 | if (bytes >= this._bufs[0].length) { |
| 231 | bytes -= this._bufs[0].length |
| 232 | this.length -= this._bufs[0].length |
| 233 | this._bufs.shift() |
| 234 | } else { |
| 235 | this._bufs[0] = this._bufs[0].slice(bytes) |
| 236 | this.length -= bytes |
| 237 | break |
| 238 | } |
| 239 | } |
| 240 | return this |
| 241 | } |
| 242 | |
| 243 | |
| 244 | BufferList.prototype.duplicate = function duplicate () { |
| 245 | var i = 0 |
| 246 | , copy = new BufferList() |
| 247 | |
| 248 | for (; i < this._bufs.length; i++) |
| 249 | copy.append(this._bufs[i]) |
| 250 | |
| 251 | return copy |
| 252 | } |
| 253 | |
| 254 | |
| 255 | BufferList.prototype.destroy = function destroy () { |
| 256 | this._bufs.length = 0 |
| 257 | this.length = 0 |
| 258 | this.push(null) |
| 259 | } |
| 260 | |
| 261 | |
| 262 | ;(function () { |
| 263 | var methods = { |
| 264 | 'readDoubleBE' : 8 |
| 265 | , 'readDoubleLE' : 8 |
| 266 | , 'readFloatBE' : 4 |
| 267 | , 'readFloatLE' : 4 |
| 268 | , 'readInt32BE' : 4 |
| 269 | , 'readInt32LE' : 4 |
| 270 | , 'readUInt32BE' : 4 |
| 271 | , 'readUInt32LE' : 4 |
| 272 | , 'readInt16BE' : 2 |
| 273 | , 'readInt16LE' : 2 |
| 274 | , 'readUInt16BE' : 2 |
| 275 | , 'readUInt16LE' : 2 |
| 276 | , 'readInt8' : 1 |
| 277 | , 'readUInt8' : 1 |
| 278 | } |
| 279 | |
| 280 | for (var m in methods) { |
| 281 | (function (m) { |
| 282 | BufferList.prototype[m] = function (offset) { |
| 283 | return this.slice(offset, offset + methods[m])[m](0) |
| 284 | } |
| 285 | }(m)) |
| 286 | } |
| 287 | }()) |
| 288 | |
| 289 | |
| 290 | module.exports = BufferList |