Rev 915 | Blame | Compare with Previous | Last modification | View Log | RSS feed
// 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;module.exports = BufferList;module.exports.BufferList = BufferList; // backwards compatibilityfunction 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;// constructor to use for Buffer-esque operationsself.construct = opts.construct || Buffer;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 behaviorvar length = 0;self.__defineGetter__('length', function () {return length;});// keep an offset of the head to decide when to head = head.nextvar 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 new self.construct(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 new self.construct(0);if (start == undefined) start = 0;if (end == undefined) end = self.length;var big = new self.construct(end - start);var ix = 0;self.forEach(function (buffer) {if (start < (ix + buffer.length) && ix < end) {// at least partially contained in the rangebuffer.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 self.construct(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 rangebuffer.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);