Subversion Repositories qbpwcf-lib(archive)

Rev

Rev 915 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 liveuser 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.
4
 
5
// bufferlist.js
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
 
10
module.exports = BufferList;
11
module.exports.BufferList = BufferList; // backwards compatibility
12
 
13
function BufferList(opts) {
14
    if (!(this instanceof BufferList)) return new BufferList(opts);
15
    EventEmitter.call(this);
16
    var self = this;
17
 
18
    if (typeof(opts) == 'undefined') opts = {};
19
 
20
    // default encoding to use for take(). Leaving as 'undefined'
21
    // makes take() return a Buffer instead.
22
    self.encoding = opts.encoding;
23
 
24
    // constructor to use for Buffer-esque operations
25
    self.construct = opts.construct || Buffer;
26
 
27
    var head = { next : null, buffer : null };
28
    var last = { next : null, buffer : null };
29
 
30
    // length can get negative when advanced past the end
31
    // and this is the desired behavior
32
    var length = 0;
33
    self.__defineGetter__('length', function () {
34
        return length;
35
    });
36
 
37
    // keep an offset of the head to decide when to head = head.next
38
    var offset = 0;
39
 
40
    // Write to the bufferlist. Emits 'write'. Always returns true.
41
    self.write = function (buf) {
42
        if (!head.buffer) {
43
            head.buffer = buf;
44
            last = head;
45
        }
46
        else {
47
            last.next = { next : null, buffer : buf };
48
            last = last.next;
49
        }
50
        length += buf.length;
51
        self.emit('write', buf);
52
        return true;
53
    };
54
 
55
    self.end = function (buf) {
56
        if (Buffer.isBuffer(buf)) self.write(buf);
57
    };
58
 
59
    // Push buffers to the end of the linked list. (deprecated)
60
    // Return this (self).
61
    self.push = function () {
62
        var args = [].concat.apply([], arguments);
63
        args.forEach(self.write);
64
        return self;
65
    };
66
 
67
    // For each buffer, perform some action.
68
    // If fn's result is a true value, cut out early.
69
    // Returns this (self).
70
    self.forEach = function (fn) {
71
        if (!head.buffer) return new self.construct(0);
72
 
73
        if (head.buffer.length - offset <= 0) return self;
74
        var firstBuf = head.buffer.slice(offset);
75
 
76
        var b = { buffer : firstBuf, next : head.next };
77
 
78
        while (b && b.buffer) {
79
            var r = fn(b.buffer);
80
            if (r) break;
81
            b = b.next;
82
        }
83
 
84
        return self;
85
    };
86
 
87
    // Create a single Buffer out of all the chunks or some subset specified by
88
    // start and one-past the end (like slice) in bytes.
89
    self.join = function (start, end) {
90
        if (!head.buffer) return new self.construct(0);
91
        if (start == undefined) start = 0;
92
        if (end == undefined) end = self.length;
93
 
94
        var big = new self.construct(end - start);
95
        var ix = 0;
96
        self.forEach(function (buffer) {
97
            if (start < (ix + buffer.length) && ix < end) {
98
                // at least partially contained in the range
99
                buffer.copy(
100
                    big,
101
                    Math.max(0, ix - start),
102
                    Math.max(0, start - ix),
103
                    Math.min(buffer.length, end - ix)
104
                );
105
            }
106
            ix += buffer.length;
107
            if (ix > end) return true; // stop processing past end
108
        });
109
 
110
        return big;
111
    };
112
 
113
    self.joinInto = function (targetBuffer, targetStart, sourceStart, sourceEnd) {
114
        if (!head.buffer) return new self.construct(0);
115
        if (sourceStart == undefined) sourceStart = 0;
116
        if (sourceEnd == undefined) sourceEnd = self.length;
117
 
118
        var big = targetBuffer;
119
        if (big.length - targetStart < sourceEnd - sourceStart) {
120
            throw new Error("Insufficient space available in target Buffer.");
121
        }
122
        var ix = 0;
123
        self.forEach(function (buffer) {
124
            if (sourceStart < (ix + buffer.length) && ix < sourceEnd) {
125
                // at least partially contained in the range
126
                buffer.copy(
127
                    big,
128
                    Math.max(targetStart, targetStart + ix - sourceStart),
129
                    Math.max(0, sourceStart - ix),
130
                    Math.min(buffer.length, sourceEnd - ix)
131
                );
132
            }
133
            ix += buffer.length;
134
            if (ix > sourceEnd) return true; // stop processing past end
135
        });
136
 
137
        return big;
138
    };
139
 
140
    // Advance the buffer stream by n bytes.
141
    // If n the aggregate advance offset passes the end of the buffer list,
142
    // operations such as .take() will return empty strings until enough data is
143
    // pushed.
144
    // Returns this (self).
145
    self.advance = function (n) {
146
        offset += n;
147
        length -= n;
148
        while (head.buffer && offset >= head.buffer.length) {
149
            offset -= head.buffer.length;
150
            head = head.next
151
                ? head.next
152
                : { buffer : null, next : null }
153
            ;
154
        }
155
        if (head.buffer === null) last = { next : null, buffer : null };
156
        self.emit('advance', n);
157
        return self;
158
    };
159
 
160
    // Take n bytes from the start of the buffers.
161
    // Returns a string.
162
    // If there are less than n bytes in all the buffers or n is undefined,
163
    // returns the entire concatenated buffer string.
164
    self.take = function (n, encoding) {
165
        if (n == undefined) n = self.length;
166
        else if (typeof n !== 'number') {
167
            encoding = n;
168
            n = self.length;
169
        }
170
        var b = head;
171
        if (!encoding) encoding = self.encoding;
172
        if (encoding) {
173
            var acc = '';
174
            self.forEach(function (buffer) {
175
                if (n <= 0) return true;
176
                acc += buffer.toString(
177
                    encoding, 0, Math.min(n,buffer.length)
178
                );
179
                n -= buffer.length;
180
            });
181
            return acc;
182
        } else {
183
            // If no 'encoding' is specified, then return a Buffer.
184
            return self.join(0, n);
185
        }
186
    };
187
 
188
    // The entire concatenated buffer as a string.
189
    self.toString = function () {
190
        return self.take('binary');
191
    };
192
}
193
require('util').inherits(BufferList, EventEmitter);