--- /dev/null
+// This file was copied from https://github.com/substack/node-bufferlist
+// and modified to be able to copy bytes from the bufferlist directly into
+// a pre-existing fixed-size buffer without an additional memory allocation.
+
+// bufferlist.js
+// Treat a linked list of buffers as a single variable-size buffer.
+var Buffer = require('buffer').Buffer;
+var EventEmitter = require('events').EventEmitter;
+var bufferAllocUnsafe = require('../lib/utils').bufferAllocUnsafe;
+
+module.exports = BufferList;
+module.exports.BufferList = BufferList; // backwards compatibility
+
+function BufferList(opts) {
+ if (!(this instanceof BufferList)) return new BufferList(opts);
+ EventEmitter.call(this);
+ var self = this;
+
+ if (typeof(opts) == 'undefined') opts = {};
+
+ // default encoding to use for take(). Leaving as 'undefined'
+ // makes take() return a Buffer instead.
+ self.encoding = opts.encoding;
+
+ var head = { next : null, buffer : null };
+ var last = { next : null, buffer : null };
+
+ // length can get negative when advanced past the end
+ // and this is the desired behavior
+ var length = 0;
+ self.__defineGetter__('length', function () {
+ return length;
+ });
+
+ // keep an offset of the head to decide when to head = head.next
+ var offset = 0;
+
+ // Write to the bufferlist. Emits 'write'. Always returns true.
+ self.write = function (buf) {
+ if (!head.buffer) {
+ head.buffer = buf;
+ last = head;
+ }
+ else {
+ last.next = { next : null, buffer : buf };
+ last = last.next;
+ }
+ length += buf.length;
+ self.emit('write', buf);
+ return true;
+ };
+
+ self.end = function (buf) {
+ if (Buffer.isBuffer(buf)) self.write(buf);
+ };
+
+ // Push buffers to the end of the linked list. (deprecated)
+ // Return this (self).
+ self.push = function () {
+ var args = [].concat.apply([], arguments);
+ args.forEach(self.write);
+ return self;
+ };
+
+ // For each buffer, perform some action.
+ // If fn's result is a true value, cut out early.
+ // Returns this (self).
+ self.forEach = function (fn) {
+ if (!head.buffer) return bufferAllocUnsafe(0);
+
+ if (head.buffer.length - offset <= 0) return self;
+ var firstBuf = head.buffer.slice(offset);
+
+ var b = { buffer : firstBuf, next : head.next };
+
+ while (b && b.buffer) {
+ var r = fn(b.buffer);
+ if (r) break;
+ b = b.next;
+ }
+
+ return self;
+ };
+
+ // Create a single Buffer out of all the chunks or some subset specified by
+ // start and one-past the end (like slice) in bytes.
+ self.join = function (start, end) {
+ if (!head.buffer) return bufferAllocUnsafe(0);
+ if (start == undefined) start = 0;
+ if (end == undefined) end = self.length;
+
+ var big = bufferAllocUnsafe(end - start);
+ var ix = 0;
+ self.forEach(function (buffer) {
+ if (start < (ix + buffer.length) && ix < end) {
+ // at least partially contained in the range
+ buffer.copy(
+ big,
+ Math.max(0, ix - start),
+ Math.max(0, start - ix),
+ Math.min(buffer.length, end - ix)
+ );
+ }
+ ix += buffer.length;
+ if (ix > end) return true; // stop processing past end
+ });
+
+ return big;
+ };
+
+ self.joinInto = function (targetBuffer, targetStart, sourceStart, sourceEnd) {
+ if (!head.buffer) return new bufferAllocUnsafe(0);
+ if (sourceStart == undefined) sourceStart = 0;
+ if (sourceEnd == undefined) sourceEnd = self.length;
+
+ var big = targetBuffer;
+ if (big.length - targetStart < sourceEnd - sourceStart) {
+ throw new Error("Insufficient space available in target Buffer.");
+ }
+ var ix = 0;
+ self.forEach(function (buffer) {
+ if (sourceStart < (ix + buffer.length) && ix < sourceEnd) {
+ // at least partially contained in the range
+ buffer.copy(
+ big,
+ Math.max(targetStart, targetStart + ix - sourceStart),
+ Math.max(0, sourceStart - ix),
+ Math.min(buffer.length, sourceEnd - ix)
+ );
+ }
+ ix += buffer.length;
+ if (ix > sourceEnd) return true; // stop processing past end
+ });
+
+ return big;
+ };
+
+ // Advance the buffer stream by n bytes.
+ // If n the aggregate advance offset passes the end of the buffer list,
+ // operations such as .take() will return empty strings until enough data is
+ // pushed.
+ // Returns this (self).
+ self.advance = function (n) {
+ offset += n;
+ length -= n;
+ while (head.buffer && offset >= head.buffer.length) {
+ offset -= head.buffer.length;
+ head = head.next
+ ? head.next
+ : { buffer : null, next : null }
+ ;
+ }
+ if (head.buffer === null) last = { next : null, buffer : null };
+ self.emit('advance', n);
+ return self;
+ };
+
+ // Take n bytes from the start of the buffers.
+ // Returns a string.
+ // If there are less than n bytes in all the buffers or n is undefined,
+ // returns the entire concatenated buffer string.
+ self.take = function (n, encoding) {
+ if (n == undefined) n = self.length;
+ else if (typeof n !== 'number') {
+ encoding = n;
+ n = self.length;
+ }
+ var b = head;
+ if (!encoding) encoding = self.encoding;
+ if (encoding) {
+ var acc = '';
+ self.forEach(function (buffer) {
+ if (n <= 0) return true;
+ acc += buffer.toString(
+ encoding, 0, Math.min(n,buffer.length)
+ );
+ n -= buffer.length;
+ });
+ return acc;
+ } else {
+ // If no 'encoding' is specified, then return a Buffer.
+ return self.join(0, n);
+ }
+ };
+
+ // The entire concatenated buffer as a string.
+ self.toString = function () {
+ return self.take('binary');
+ };
+}
+require('util').inherits(BufferList, EventEmitter);