1 // This file was copied from https://github.com/substack/node-bufferlist
2 // and modified to be able to copy bytes from the bufferlist directly into
3 // a pre-existing fixed-size buffer without an additional memory allocation.
6 // Treat a linked list of buffers as a single variable-size buffer.
7 var Buffer = require('buffer').Buffer;
8 var EventEmitter = require('events').EventEmitter;
9 var bufferAllocUnsafe = require('../lib/utils').bufferAllocUnsafe;
11 module.exports = BufferList;
12 module.exports.BufferList = BufferList; // backwards compatibility
14 function BufferList(opts) {
15 if (!(this instanceof BufferList)) return new BufferList(opts);
16 EventEmitter.call(this);
19 if (typeof(opts) == 'undefined') opts = {};
21 // default encoding to use for take(). Leaving as 'undefined'
22 // makes take() return a Buffer instead.
23 self.encoding = opts.encoding;
25 var head = { next : null, buffer : null };
26 var last = { next : null, buffer : null };
28 // length can get negative when advanced past the end
29 // and this is the desired behavior
31 self.__defineGetter__('length', function () {
35 // keep an offset of the head to decide when to head = head.next
38 // Write to the bufferlist. Emits 'write'. Always returns true.
39 self.write = function (buf) {
45 last.next = { next : null, buffer : buf };
49 self.emit('write', buf);
53 self.end = function (buf) {
54 if (Buffer.isBuffer(buf)) self.write(buf);
57 // Push buffers to the end of the linked list. (deprecated)
58 // Return this (self).
59 self.push = function () {
60 var args = [].concat.apply([], arguments);
61 args.forEach(self.write);
65 // For each buffer, perform some action.
66 // If fn's result is a true value, cut out early.
67 // Returns this (self).
68 self.forEach = function (fn) {
69 if (!head.buffer) return bufferAllocUnsafe(0);
71 if (head.buffer.length - offset <= 0) return self;
72 var firstBuf = head.buffer.slice(offset);
74 var b = { buffer : firstBuf, next : head.next };
76 while (b && b.buffer) {
85 // Create a single Buffer out of all the chunks or some subset specified by
86 // start and one-past the end (like slice) in bytes.
87 self.join = function (start, end) {
88 if (!head.buffer) return bufferAllocUnsafe(0);
89 if (start == undefined) start = 0;
90 if (end == undefined) end = self.length;
92 var big = bufferAllocUnsafe(end - start);
94 self.forEach(function (buffer) {
95 if (start < (ix + buffer.length) && ix < end) {
96 // at least partially contained in the range
99 Math.max(0, ix - start),
100 Math.max(0, start - ix),
101 Math.min(buffer.length, end - ix)
105 if (ix > end) return true; // stop processing past end
111 self.joinInto = function (targetBuffer, targetStart, sourceStart, sourceEnd) {
112 if (!head.buffer) return new bufferAllocUnsafe(0);
113 if (sourceStart == undefined) sourceStart = 0;
114 if (sourceEnd == undefined) sourceEnd = self.length;
116 var big = targetBuffer;
117 if (big.length - targetStart < sourceEnd - sourceStart) {
118 throw new Error("Insufficient space available in target Buffer.");
121 self.forEach(function (buffer) {
122 if (sourceStart < (ix + buffer.length) && ix < sourceEnd) {
123 // at least partially contained in the range
126 Math.max(targetStart, targetStart + ix - sourceStart),
127 Math.max(0, sourceStart - ix),
128 Math.min(buffer.length, sourceEnd - ix)
132 if (ix > sourceEnd) return true; // stop processing past end
138 // Advance the buffer stream by n bytes.
139 // If n the aggregate advance offset passes the end of the buffer list,
140 // operations such as .take() will return empty strings until enough data is
142 // Returns this (self).
143 self.advance = function (n) {
146 while (head.buffer && offset >= head.buffer.length) {
147 offset -= head.buffer.length;
150 : { buffer : null, next : null }
153 if (head.buffer === null) last = { next : null, buffer : null };
154 self.emit('advance', n);
158 // Take n bytes from the start of the buffers.
160 // If there are less than n bytes in all the buffers or n is undefined,
161 // returns the entire concatenated buffer string.
162 self.take = function (n, encoding) {
163 if (n == undefined) n = self.length;
164 else if (typeof n !== 'number') {
169 if (!encoding) encoding = self.encoding;
172 self.forEach(function (buffer) {
173 if (n <= 0) return true;
174 acc += buffer.toString(
175 encoding, 0, Math.min(n,buffer.length)
181 // If no 'encoding' is specified, then return a Buffer.
182 return self.join(0, n);
186 // The entire concatenated buffer as a string.
187 self.toString = function () {
188 return self.take('binary');
191 require('util').inherits(BufferList, EventEmitter);