-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathbufferlist.js
142 lines (122 loc) · 4.47 KB
/
bufferlist.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
// bufferlist.js
// Treat a linked list of buffers as a single variable-size buffer.
var Buffer = require('buffer').Buffer;
var EventEmitter = require('events').EventEmitter;
BufferList.prototype = new EventEmitter;
exports.BufferList = BufferList;
function BufferList(opts) {
if (!(this instanceof BufferList)) return new BufferList(opts);
if (typeof(opts) == 'undefined') opts = {}
// default encoding to use for take()
this.encoding = opts.encoding;
if (!this.encoding) this.encoding = 'binary';
// constructor to use for Buffer-esque operations
this.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 behavior
var length = 0;
this.__defineGetter__('length', function () {
return length;
});
// keep an offset of the head to decide when to head = head.next
var offset = 0;
// Push buffers to the end of the linked list.
// Return this (self).
this.push = function () {
var args = [].concat.apply([], arguments);
args.forEach(function (buf) {
if (!head.buffer) {
head.buffer = buf;
last = head;
}
else {
last.next = { next : null, buffer : buf };
last = last.next;
}
length += buf.length;
});
this.emit('push', args);
return this;
};
// For each buffer, perform some action.
// If fn's result is a true value, cut out early.
// Returns this (self).
this.forEach = function (fn) {
if (!head.buffer) return new this.construct(0);
if (head.buffer.length - offset <= 0) return this;
var firstBuf = new this.construct(head.buffer.length - offset);
head.buffer.copy(firstBuf, 0, offset, head.buffer.length);
var b = { buffer : firstBuf, next : head.next };
while (b && b.buffer) {
var r = fn(b.buffer);
if (r) break;
b = b.next;
}
return this;
};
// Create a single Buffer out of all the chunks or some subset specified by
// start and one-past the end (like slice) in bytes.
this.join = function (start, end) {
if (!head.buffer) return new this.construct(0);
if (start == undefined) start = 0;
if (end == undefined) end = this.length;
var big = new this.construct(end - start);
var ix = 0;
this.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;
};
// 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).
this.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 }
;
}
this.emit('advance', n);
return this;
};
// 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.
this.take = function (n) {
if (n == undefined) n = this.length;
var b = head;
var acc = '';
var encoding = this.encoding;
this.forEach(function (buffer) {
if (n <= 0) return true;
acc += buffer.toString(
encoding, 0, Math.min(n,buffer.length)
);
n -= buffer.length;
});
return acc;
};
// The entire concatenated buffer as a string.
this.toString = function () {
return this.take();
};
};