123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191 |
- // 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);
|