Websocket
[VSoRC/.git] / node_modules / websocket / vendor / FastBufferList.js
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 var bufferAllocUnsafe = require('../lib/utils').bufferAllocUnsafe;
10
11 module.exports = BufferList;
12 module.exports.BufferList = BufferList; // backwards compatibility
13
14 function BufferList(opts) {
15     if (!(this instanceof BufferList)) return new BufferList(opts);
16     EventEmitter.call(this);
17     var self = this;
18     
19     if (typeof(opts) == 'undefined') opts = {};
20     
21     // default encoding to use for take(). Leaving as 'undefined'
22     // makes take() return a Buffer instead.
23     self.encoding = opts.encoding;
24     
25     var head = { next : null, buffer : null };
26     var last = { next : null, buffer : null };
27     
28     // length can get negative when advanced past the end
29     // and this is the desired behavior
30     var length = 0;
31     self.__defineGetter__('length', function () {
32         return length;
33     });
34     
35     // keep an offset of the head to decide when to head = head.next
36     var offset = 0;
37     
38     // Write to the bufferlist. Emits 'write'. Always returns true.
39     self.write = function (buf) {
40         if (!head.buffer) {
41             head.buffer = buf;
42             last = head;
43         }
44         else {
45             last.next = { next : null, buffer : buf };
46             last = last.next;
47         }
48         length += buf.length;
49         self.emit('write', buf);
50         return true;
51     };
52     
53     self.end = function (buf) {
54         if (Buffer.isBuffer(buf)) self.write(buf);
55     };
56     
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);
62         return self;
63     };
64     
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);
70         
71         if (head.buffer.length - offset <= 0) return self;
72         var firstBuf = head.buffer.slice(offset);
73         
74         var b = { buffer : firstBuf, next : head.next };
75         
76         while (b && b.buffer) {
77             var r = fn(b.buffer);
78             if (r) break;
79             b = b.next;
80         }
81         
82         return self;
83     };
84     
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;
91         
92         var big = bufferAllocUnsafe(end - start);
93         var ix = 0;
94         self.forEach(function (buffer) {
95             if (start < (ix + buffer.length) && ix < end) {
96                 // at least partially contained in the range
97                 buffer.copy(
98                     big,
99                     Math.max(0, ix - start),
100                     Math.max(0, start - ix),
101                     Math.min(buffer.length, end - ix)
102                 );
103             }
104             ix += buffer.length;
105             if (ix > end) return true; // stop processing past end
106         });
107         
108         return big;
109     };
110     
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;
115         
116         var big = targetBuffer;
117         if (big.length - targetStart < sourceEnd - sourceStart) {
118             throw new Error("Insufficient space available in target Buffer.");
119         }
120         var ix = 0;
121         self.forEach(function (buffer) {
122             if (sourceStart < (ix + buffer.length) && ix < sourceEnd) {
123                 // at least partially contained in the range
124                 buffer.copy(
125                     big,
126                     Math.max(targetStart, targetStart + ix - sourceStart),
127                     Math.max(0, sourceStart - ix),
128                     Math.min(buffer.length, sourceEnd - ix)
129                 );
130             }
131             ix += buffer.length;
132             if (ix > sourceEnd) return true; // stop processing past end
133         });
134         
135         return big;
136     };
137     
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
141     // pushed.
142     // Returns this (self).
143     self.advance = function (n) {
144         offset += n;
145         length -= n;
146         while (head.buffer && offset >= head.buffer.length) {
147             offset -= head.buffer.length;
148             head = head.next
149                 ? head.next
150                 : { buffer : null, next : null }
151             ;
152         }
153         if (head.buffer === null) last = { next : null, buffer : null };
154         self.emit('advance', n);
155         return self;
156     };
157     
158     // Take n bytes from the start of the buffers.
159     // Returns a string.
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') {
165             encoding = n;
166             n = self.length;
167         }
168         var b = head;
169         if (!encoding) encoding = self.encoding;
170         if (encoding) {
171             var acc = '';
172             self.forEach(function (buffer) {
173                 if (n <= 0) return true;
174                 acc += buffer.toString(
175                     encoding, 0, Math.min(n,buffer.length)
176                 );
177                 n -= buffer.length;
178             });
179             return acc;
180         } else {
181             // If no 'encoding' is specified, then return a Buffer.
182             return self.join(0, n);
183         }
184     };
185     
186     // The entire concatenated buffer as a string.
187     self.toString = function () {
188         return self.take('binary');
189     };
190 }
191 require('util').inherits(BufferList, EventEmitter);