Skip to content
This repository
Browse code

buffer: use smalloc as backing data store

Memory allocations are now done through smalloc. The Buffer cc class has
been removed completely, but for backwards compatibility have left the
namespace as Buffer.

The .parent attribute is only set if the Buffer is a slice of an
allocation. Which is then set to the alloc object (not a Buffer).

The .offset attribute is now a ReadOnly set to 0, for backwards
compatibility. I'd like to remove it in the future (pre v1.0).

A few alterations have been made to how arguments are either coerced or
thrown. All primitives will now be coerced to their respective values,
and (most) all out of range index requests will throw.

The indexes that are coerced were left for backwards compatibility. For
example: Buffer slice operates more like Array slice, and coerces
instead of throwing out of range indexes. This may change in the future.

The reason for wanting to throw for out of range indexes is because
giving js access to raw memory has high potential risk. To mitigate that
it's easier to make sure the developer is always quickly alerted to the
fact that their code is attempting to access beyond memory bounds.

Because SlowBuffer will be deprecated, and simply returns a new Buffer
instance, all tests on SlowBuffer have been removed.

Heapdumps will now show usage under "smalloc" instead of "Buffer".

ParseArrayIndex was added to node_internals to support proper uint
argument checking/coercion for external array data indexes.

SlabAllocator had to be updated since handle_ no longer exists.
  • Loading branch information...
commit 3a2f273bd73bc94a6e93f342d629106a9f022f2d 1 parent 252cdfa
Trevor Norris trevnorris authored
133 doc/api/buffer.markdown
Source Rendered
@@ -80,6 +80,69 @@ Allocates a new buffer containing the given `str`.
80 80 Returns true if the `encoding` is a valid encoding argument, or false
81 81 otherwise.
82 82
  83 +### Class Method: Buffer.isBuffer(obj)
  84 +
  85 +* `obj` Object
  86 +* Return: Boolean
  87 +
  88 +Tests if `obj` is a `Buffer`.
  89 +
  90 +### Class Method: Buffer.byteLength(string, [encoding])
  91 +
  92 +* `string` String
  93 +* `encoding` String, Optional, Default: 'utf8'
  94 +* Return: Number
  95 +
  96 +Gives the actual byte length of a string. `encoding` defaults to `'utf8'`.
  97 +This is not the same as `String.prototype.length` since that returns the
  98 +number of *characters* in a string.
  99 +
  100 +Example:
  101 +
  102 + str = '\u00bd + \u00bc = \u00be';
  103 +
  104 + console.log(str + ": " + str.length + " characters, " +
  105 + Buffer.byteLength(str, 'utf8') + " bytes");
  106 +
  107 + // ½ + ¼ = ¾: 9 characters, 12 bytes
  108 +
  109 +### Class Method: Buffer.concat(list, [totalLength])
  110 +
  111 +* `list` {Array} List of Buffer objects to concat
  112 +* `totalLength` {Number} Total length of the buffers when concatenated
  113 +
  114 +Returns a buffer which is the result of concatenating all the buffers in
  115 +the list together.
  116 +
  117 +If the list has no items, or if the totalLength is 0, then it returns a
  118 +zero-length buffer.
  119 +
  120 +If the list has exactly one item, then the first item of the list is
  121 +returned.
  122 +
  123 +If the list has more than one item, then a new Buffer is created.
  124 +
  125 +If totalLength is not provided, it is read from the buffers in the list.
  126 +However, this adds an additional loop to the function, so it is faster
  127 +to provide the length explicitly.
  128 +
  129 +### buf.length
  130 +
  131 +* Number
  132 +
  133 +The size of the buffer in bytes. Note that this is not necessarily the size
  134 +of the contents. `length` refers to the amount of memory allocated for the
  135 +buffer object. It does not change when the contents of the buffer are changed.
  136 +
  137 + buf = new Buffer(1234);
  138 +
  139 + console.log(buf.length);
  140 + buf.write("some string", 0, "ascii");
  141 + console.log(buf.length);
  142 +
  143 + // 1234
  144 + // 1234
  145 +
83 146 ### buf.write(string, [offset], [length], [encoding])
84 147
85 148 * `string` String - data to be written to buffer
@@ -155,69 +218,6 @@ Example: copy an ASCII string into a buffer, one byte at a time:
155 218
156 219 // node.js
157 220
158   -### Class Method: Buffer.isBuffer(obj)
159   -
160   -* `obj` Object
161   -* Return: Boolean
162   -
163   -Tests if `obj` is a `Buffer`.
164   -
165   -### Class Method: Buffer.byteLength(string, [encoding])
166   -
167   -* `string` String
168   -* `encoding` String, Optional, Default: 'utf8'
169   -* Return: Number
170   -
171   -Gives the actual byte length of a string. `encoding` defaults to `'utf8'`.
172   -This is not the same as `String.prototype.length` since that returns the
173   -number of *characters* in a string.
174   -
175   -Example:
176   -
177   - str = '\u00bd + \u00bc = \u00be';
178   -
179   - console.log(str + ": " + str.length + " characters, " +
180   - Buffer.byteLength(str, 'utf8') + " bytes");
181   -
182   - // ½ + ¼ = ¾: 9 characters, 12 bytes
183   -
184   -### Class Method: Buffer.concat(list, [totalLength])
185   -
186   -* `list` {Array} List of Buffer objects to concat
187   -* `totalLength` {Number} Total length of the buffers when concatenated
188   -
189   -Returns a buffer which is the result of concatenating all the buffers in
190   -the list together.
191   -
192   -If the list has no items, or if the totalLength is 0, then it returns a
193   -zero-length buffer.
194   -
195   -If the list has exactly one item, then the first item of the list is
196   -returned.
197   -
198   -If the list has more than one item, then a new Buffer is created.
199   -
200   -If totalLength is not provided, it is read from the buffers in the list.
201   -However, this adds an additional loop to the function, so it is faster
202   -to provide the length explicitly.
203   -
204   -### buf.length
205   -
206   -* Number
207   -
208   -The size of the buffer in bytes. Note that this is not necessarily the size
209   -of the contents. `length` refers to the amount of memory allocated for the
210   -buffer object. It does not change when the contents of the buffer are changed.
211   -
212   - buf = new Buffer(1234);
213   -
214   - console.log(buf.length);
215   - buf.write("some string", 0, "ascii");
216   - console.log(buf.length);
217   -
218   - // 1234
219   - // 1234
220   -
221 221 ### buf.copy(targetBuffer, [targetStart], [sourceStart], [sourceEnd])
222 222
223 223 * `targetBuffer` Buffer object - Buffer to copy into
@@ -694,11 +694,8 @@ Note that this is a property on the buffer module returned by
694 694
695 695 ## Class: SlowBuffer
696 696
697   -This class is primarily for internal use. JavaScript programs should
698   -use Buffer instead of using SlowBuffer.
  697 +Deprecated. SlowBuffer now returns an instance of Buffer.
699 698
700 699 In order to avoid the overhead of allocating many C++ Buffer objects for
701 700 small blocks of memory in the lifetime of a server, Node allocates memory
702   -in 8Kb (8192 byte) chunks. If a buffer is smaller than this size, then it
703   -will be backed by a parent SlowBuffer object. If it is larger than this,
704   -then Node will allocate a SlowBuffer slab for it directly.
  701 +in 8Kb (8192 byte) chunks. This is now handled by Smalloc.
654 lib/buffer.js
@@ -19,299 +19,202 @@
19 19 // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20 20 // USE OR OTHER DEALINGS IN THE SOFTWARE.
21 21
22   -var SlowBuffer = process.binding('buffer').SlowBuffer;
  22 +var smalloc = process.binding('smalloc');
  23 +var buffer = process.binding('buffer');
23 24 var assert = require('assert');
  25 +var alloc = smalloc.alloc;
  26 +var sliceOnto = smalloc.sliceOnto;
  27 +var kMaxLength = smalloc.kMaxLength;
24 28
  29 +exports.Buffer = Buffer;
  30 +// backwards compatibility (DEPRECATE)
  31 +exports.SlowBuffer = Buffer;
25 32 exports.INSPECT_MAX_BYTES = 50;
26 33
27   -// Make SlowBuffer inherit from Buffer.
28   -// This is an exception to the rule that __proto__ is not allowed in core.
29   -SlowBuffer.prototype.__proto__ = Buffer.prototype;
  34 +// add methods to Buffer prototype
  35 +buffer.setupBufferJS(Buffer);
30 36
  37 +function Buffer(subject, encoding) {
  38 + if (!(this instanceof Buffer))
  39 + return new Buffer(subject, encoding);
31 40
32   -function clamp(index, len, defaultValue) {
33   - if (typeof index !== 'number') return defaultValue;
34   - index = ~~index; // Coerce to integer.
35   - if (index >= len) return len;
36   - if (index >= 0) return index;
37   - index += len;
38   - if (index >= 0) return index;
39   - return 0;
40   -}
  41 + var type = typeof subject;
41 42
  43 + switch (type) {
  44 + case 'number':
  45 + this.length = subject > 0 ? Math.floor(subject) : 0;
  46 + break;
42 47
43   -function toHex(n) {
44   - if (n < 16) return '0' + n.toString(16);
45   - return n.toString(16);
46   -}
  48 + case 'string':
  49 + this.length = Buffer.byteLength(subject, encoding = encoding || 'utf8');
  50 + break;
47 51
  52 + case 'object':
  53 + this.length = +subject.length > 0 ? Math.floor(+subject.length) : 0;
  54 + break;
48 55
49   -SlowBuffer.prototype.toString = function(encoding, start, end) {
50   - encoding = String(encoding || 'utf8').toLowerCase();
51   - start = +start || 0;
52   - if (typeof end !== 'number') end = this.length;
  56 + // undef first arg returns unallocated buffer, also assumes length passed.
  57 + // this is a stop-gap for now while look for better architecture.
  58 + // for internal use only.
  59 + case 'undefined':
  60 + this.length = encoding;
  61 + return;
53 62
54   - // Fastpath empty strings
55   - if (+end == start) {
56   - return '';
  63 + default:
  64 + throw new TypeError('must start with number, buffer, array or string');
57 65 }
58 66
59   - switch (encoding) {
60   - case 'hex':
61   - return this.hexSlice(start, end);
62   -
63   - case 'utf8':
64   - case 'utf-8':
65   - return this.utf8Slice(start, end);
  67 + if (this.length > kMaxLength)
  68 + throw new RangeError('length > kMaxLength');
66 69
67   - case 'ascii':
68   - return this.asciiSlice(start, end);
  70 + alloc(this, this.length);
69 71
70   - case 'binary':
71   - return this.binarySlice(start, end);
  72 + if (type !== 'number') {
  73 + if (type === 'string') {
  74 + // FIXME: the number of bytes hasn't changed, so why change the length?
  75 + this.length = this.write(subject, 0, encoding);
  76 + } else {
  77 + if (subject instanceof Buffer)
  78 + this.copy(subject, 0, 0, this.length);
  79 + else if (typeof subject.length === 'number' || Array.isArray(subject))
  80 + for (var i = 0; i < this.length; i++)
  81 + this[i] = subject[i];
  82 + }
  83 + }
  84 +}
72 85
73   - case 'base64':
74   - return this.base64Slice(start, end);
75 86
76   - case 'ucs2':
77   - case 'ucs-2':
78   - case 'utf16le':
79   - case 'utf-16le':
80   - return this.ucs2Slice(start, end);
  87 +// Static methods
81 88
82   - default:
83   - throw new TypeError('Unknown encoding: ' + encoding);
84   - }
  89 +Buffer.isBuffer = function isBuffer(b) {
  90 + return b instanceof Buffer;
85 91 };
86 92
87 93
88   -SlowBuffer.prototype.write = function(string, offset, length, encoding) {
89   - // Support both (string, offset, length, encoding)
90   - // and the legacy (string, encoding, offset, length)
91   - if (isFinite(offset)) {
92   - if (!isFinite(length)) {
93   - encoding = length;
94   - length = undefined;
95   - }
96   - } else { // legacy
97   - var swap = encoding;
98   - encoding = offset;
99   - offset = length;
100   - length = swap;
101   - }
102   -
103   - offset = +offset || 0;
104   - var remaining = this.length - offset;
105   - if (!length) {
106   - length = remaining;
107   - } else {
108   - length = +length;
109   - if (length > remaining) {
110   - length = remaining;
111   - }
112   - }
113   - encoding = String(encoding || 'utf8').toLowerCase();
114   -
115   - switch (encoding) {
  94 +Buffer.isEncoding = function(encoding) {
  95 + switch ((encoding + '').toLowerCase()) {
116 96 case 'hex':
117   - return this.hexWrite(string, offset, length);
118   -
119 97 case 'utf8':
120 98 case 'utf-8':
121   - return this.utf8Write(string, offset, length);
122   -
123 99 case 'ascii':
124   - return this.asciiWrite(string, offset, length);
125   -
126 100 case 'binary':
127   - return this.binaryWrite(string, offset, length);
128   -
129 101 case 'base64':
130   - return this.base64Write(string, offset, length);
131   -
132 102 case 'ucs2':
133 103 case 'ucs-2':
134 104 case 'utf16le':
135 105 case 'utf-16le':
136   - return this.ucs2Write(string, offset, length);
  106 + case 'raw':
  107 + return true;
137 108
138 109 default:
139   - throw new TypeError('Unknown encoding: ' + encoding);
  110 + return false;
140 111 }
141 112 };
142 113
143 114
144   -// slice(start, end)
145   -SlowBuffer.prototype.slice = function(start, end) {
146   - var len = this.length;
147   - start = clamp(start, len, 0);
148   - end = clamp(end, len, len);
149   - return new Buffer(this, end - start, start);
150   -};
151   -
152   -
153   -var zeroBuffer = new SlowBuffer(0);
154   -
155   -// Buffer
156   -function Buffer(subject, encoding, offset) {
157   - if (!(this instanceof Buffer)) {
158   - return new Buffer(subject, encoding, offset);
159   - }
160   -
161   - var type;
162   -
163   - // Are we slicing?
164   - if (typeof offset === 'number') {
165   - if (!Buffer.isBuffer(subject)) {
166   - throw new TypeError('First argument must be a Buffer when slicing');
167   - }
  115 +Buffer.concat = function(list, length) {
  116 + if (!Array.isArray(list))
  117 + throw new TypeError('Usage: Buffer.concat(list[, length])');
168 118
169   - this.length = +encoding > 0 ? Math.ceil(encoding) : 0;
170   - this.parent = subject.parent ? subject.parent : subject;
171   - this.offset = offset;
  119 + if (typeof length === 'undefined') {
  120 + length = 0;
  121 + for (var i = 0; i < list.length; i++)
  122 + length += list[i].length;
172 123 } else {
173   - // Find the length
174   - switch (type = typeof subject) {
175   - case 'number':
176   - this.length = +subject > 0 ? Math.ceil(subject) : 0;
177   - break;
178   -
179   - case 'string':
180   - this.length = Buffer.byteLength(subject, encoding);
181   - break;
182   -
183   - case 'object': // Assume object is array-ish
184   - this.length = +subject.length > 0 ? Math.ceil(subject.length) : 0;
185   - break;
186   -
187   - default:
188   - throw new TypeError('First argument needs to be a number, ' +
189   - 'array or string.');
190   - }
  124 + length = ~~length;
  125 + }
191 126
192   - if (this.length > Buffer.poolSize) {
193   - // Big buffer, just alloc one.
194   - this.parent = new SlowBuffer(this.length);
195   - this.offset = 0;
  127 + if (length < 0) length = 0;
196 128
197   - } else if (this.length > 0) {
198   - // Small buffer.
199   - if (!pool || pool.length - pool.used < this.length) allocPool();
200   - this.parent = pool;
201   - this.offset = pool.used;
202   - pool.used += this.length;
203   - if (pool.used & 7) pool.used = (pool.used + 8) & ~7;
  129 + if (list.length === 0)
  130 + return new Buffer(0);
  131 + else if (list.length === 1)
  132 + return list[0];
204 133
205   - } else {
206   - // Zero-length buffer
207   - this.parent = zeroBuffer;
208   - this.offset = 0;
209   - }
  134 + if (length < 0)
  135 + throw new RangeError('length is not a positive number');
210 136
211   - // optimize by branching logic for new allocations
212   - if (typeof subject !== 'number') {
213   - if (type === 'string') {
214   - // We are a string
215   - this.length = this.write(subject, 0, encoding);
216   - // if subject is buffer then use built-in copy method
217   - } else if (Buffer.isBuffer(subject)) {
218   - if (subject.parent)
219   - subject.parent.copy(this.parent,
220   - this.offset,
221   - subject.offset,
222   - this.length + subject.offset);
223   - else
224   - subject.copy(this.parent, this.offset, 0, this.length);
225   - } else if (isArrayIsh(subject)) {
226   - for (var i = 0; i < this.length; i++)
227   - this.parent[i + this.offset] = subject[i];
228   - }
229   - }
  137 + var buffer = new Buffer(length);
  138 + var pos = 0;
  139 + for (var i = 0; i < list.length; i++) {
  140 + var buf = list[i];
  141 + buf.copy(buffer, pos);
  142 + pos += buf.length;
230 143 }
231 144
232   - SlowBuffer.makeFastBuffer(this.parent, this, this.offset, this.length);
233   -}
  145 + return buffer;
  146 +};
234 147
235   -function isArrayIsh(subject) {
236   - return Array.isArray(subject) ||
237   - subject && typeof subject === 'object' &&
238   - typeof subject.length === 'number';
239   -}
240 148
241   -exports.SlowBuffer = SlowBuffer;
242   -exports.Buffer = Buffer;
  149 +// toString(encoding, start=0, end=buffer.length)
  150 +Buffer.prototype.toString = function(encoding, start, end) {
  151 + encoding = !!encoding ? (encoding + '').toLowerCase() : 'utf8';
243 152
  153 + start = ~~start;
  154 + end = typeof end === 'undefined' ? this.length : ~~end;
244 155
245   -Buffer.isEncoding = function(encoding) {
246   - switch (encoding && encoding.toLowerCase()) {
  156 + if (start < 0) start = 0;
  157 + if (end > this.length) end = this.length;
  158 + if (end <= start) return '';
  159 +
  160 + switch (encoding) {
247 161 case 'hex':
  162 + return this.hexSlice(start, end);
  163 +
248 164 case 'utf8':
249 165 case 'utf-8':
  166 + return this.utf8Slice(start, end);
  167 +
250 168 case 'ascii':
  169 + return this.asciiSlice(start, end);
  170 +
251 171 case 'binary':
  172 + return this.binarySlice(start, end);
  173 +
252 174 case 'base64':
  175 + return this.base64Slice(start, end);
  176 +
253 177 case 'ucs2':
254 178 case 'ucs-2':
255 179 case 'utf16le':
256 180 case 'utf-16le':
257   - case 'raw':
258   - return true;
  181 + return this.ucs2Slice(start, end);
259 182
260 183 default:
261   - return false;
  184 + throw new TypeError('Unknown encoding: ' + encoding);
262 185 }
263 186 };
264 187
265 188
266   -
267   -Buffer.poolSize = 8 * 1024;
268   -var pool;
269   -
270   -function allocPool() {
271   - pool = new SlowBuffer(Buffer.poolSize);
272   - pool.used = 0;
273   -}
274   -
275   -
276   -// Static methods
277   -Buffer.isBuffer = function isBuffer(b) {
278   - return b instanceof Buffer;
279   -};
280   -
281   -
282 189 // Inspect
283 190 Buffer.prototype.inspect = function inspect() {
284   - var out = [],
285   - len = this.length,
286   - name = this.constructor.name;
287   -
288   - for (var i = 0; i < len; i++) {
289   - out[i] = toHex(this[i]);
290   - if (i == exports.INSPECT_MAX_BYTES) {
291   - out[i + 1] = '...';
292   - break;
293   - }
294   - }
295   -
296   - return '<' + name + ' ' + out.join(' ') + '>';
  191 + var str = '';
  192 + if (this.length > 0)
  193 + str = this.hexSlice(0, this.length).match(/.{2}/g).join(' ');
  194 + return '<' + this.constructor.name + ' ' + str + '>';
297 195 };
298 196
299 197
  198 +// TODO(trevnorris): DEPRECATE
300 199 Buffer.prototype.get = function get(offset) {
  200 + offset = ~~offset;
301 201 if (offset < 0 || offset >= this.length)
302   - throw new RangeError('offset is out of bounds');
303   - return this.parent[this.offset + offset];
  202 + throw new RangeError('index out of range');
  203 + return this[offset];
304 204 };
305 205
306 206
  207 +// TODO(trevnorris): DEPRECATE
307 208 Buffer.prototype.set = function set(offset, v) {
  209 + offset = ~~offset;
308 210 if (offset < 0 || offset >= this.length)
309   - throw new RangeError('offset is out of bounds');
310   - return this.parent[this.offset + offset] = v;
  211 + throw new RangeError('index out of range');
  212 + return this[offset] = v;
311 213 };
312 214
313 215
314   -// write(string, offset = 0, length = buffer.length-offset, encoding = 'utf8')
  216 +// TODO(trevnorris): fix these checks to follow new standard
  217 +// write(string, offset = 0, length = buffer.length, encoding = 'utf8')
315 218 Buffer.prototype.write = function(string, offset, length, encoding) {
316 219 // Support both (string, offset, length, encoding)
317 220 // and the legacy (string, encoding, offset, length)
@@ -320,6 +223,7 @@ Buffer.prototype.write = function(string, offset, length, encoding) {
320 223 encoding = length;
321 224 length = undefined;
322 225 }
  226 + // TODO(trevnorris): DEPRECATE
323 227 } else { // legacy
324 228 var swap = encoding;
325 229 encoding = offset;
@@ -337,7 +241,11 @@ Buffer.prototype.write = function(string, offset, length, encoding) {
337 241 length = remaining;
338 242 }
339 243 }
340   - encoding = String(encoding || 'utf8').toLowerCase();
  244 +
  245 + if (typeof encoding === 'undefined')
  246 + encoding = 'utf8';
  247 + else
  248 + encoding = (encoding + '').toLowerCase();
341 249
342 250 if (string.length > 0 && (length < 0 || offset < 0))
343 251 throw new RangeError('attempt to write beyond buffer bounds');
@@ -345,32 +253,32 @@ Buffer.prototype.write = function(string, offset, length, encoding) {
345 253 var ret;
346 254 switch (encoding) {
347 255 case 'hex':
348   - ret = this.parent.hexWrite(string, this.offset + offset, length);
  256 + ret = this.hexWrite(string, offset, length);
349 257 break;
350 258
351 259 case 'utf8':
352 260 case 'utf-8':
353   - ret = this.parent.utf8Write(string, this.offset + offset, length);
  261 + ret = this.utf8Write(string, offset, length);
354 262 break;
355 263
356 264 case 'ascii':
357   - ret = this.parent.asciiWrite(string, this.offset + offset, length);
  265 + ret = this.asciiWrite(string, offset, length);
358 266 break;
359 267
360 268 case 'binary':
361   - ret = this.parent.binaryWrite(string, this.offset + offset, length);
  269 + ret = this.binaryWrite(string, offset, length);
362 270 break;
363 271
364 272 case 'base64':
365 273 // Warning: maxLength not taken into account in base64Write
366   - ret = this.parent.base64Write(string, this.offset + offset, length);
  274 + ret = this.base64Write(string, offset, length);
367 275 break;
368 276
369 277 case 'ucs2':
370 278 case 'ucs-2':
371 279 case 'utf16le':
372 280 case 'utf-16le':
373   - ret = this.parent.ucs2Write(string, this.offset + offset, length);
  281 + ret = this.ucs2Write(string, offset, length);
374 282 break;
375 283
376 284 default:
@@ -389,203 +297,48 @@ Buffer.prototype.toJSON = function() {
389 297 };
390 298
391 299
392   -// toString(encoding, start=0, end=buffer.length)
393   -Buffer.prototype.toString = function(encoding, start, end) {
394   - encoding = String(encoding || 'utf8').toLowerCase();
395   -
396   - if (typeof start !== 'number' || start < 0) {
397   - start = 0;
398   - } else if (start > this.length) {
399   - start = this.length;
400   - }
401   -
402   - if (typeof end !== 'number' || end > this.length) {
403   - end = this.length;
404   - } else if (end < 0) {
405   - end = 0;
406   - }
407   -
408   - start = start + this.offset;
409   - end = end + this.offset;
410   -
411   - switch (encoding) {
412   - case 'hex':
413   - return this.parent.hexSlice(start, end);
414   -
415   - case 'utf8':
416   - case 'utf-8':
417   - return this.parent.utf8Slice(start, end);
418   -
419   - case 'ascii':
420   - return this.parent.asciiSlice(start, end);
421   -
422   - case 'binary':
423   - return this.parent.binarySlice(start, end);
424   -
425   - case 'base64':
426   - return this.parent.base64Slice(start, end);
427   -
428   - case 'ucs2':
429   - case 'ucs-2':
430   - case 'utf16le':
431   - case 'utf-16le':
432   - return this.parent.ucs2Slice(start, end);
433   -
434   - default:
435   - throw new TypeError('Unknown encoding: ' + encoding);
436   - }
437   -};
438   -
439   -
440   -// byteLength
441   -Buffer.byteLength = SlowBuffer.byteLength;
442   -
443   -
444   -// fill(value, start=0, end=buffer.length)
445   -Buffer.prototype.fill = function fill(value, start, end) {
446   - value || (value = 0);
447   - start || (start = 0);
448   - end || (end = this.length);
449   -
450   - if (typeof value === 'string') {
451   - value = value.charCodeAt(0);
452   - }
453   - if (typeof value !== 'number' || isNaN(value)) {
454   - throw new TypeError('value is not a number');
455   - }
456   -
457   - if (end < start) throw new RangeError('end < start');
458   -
459   - // Fill 0 bytes; we're done
460   - if (end === start) return 0;
461   - if (this.length == 0) return 0;
462   -
463   - if (start < 0 || start >= this.length) {
464   - throw new RangeError('start out of bounds');
465   - }
466   -
467   - if (end < 0 || end > this.length) {
468   - throw new RangeError('end out of bounds');
469   - }
470   -
471   - this.parent.fill(value,
472   - start + this.offset,
473   - end + this.offset);
474   - return this;
475   -};
476   -
477   -
478   -Buffer.concat = function(list, length) {
479   - if (!Array.isArray(list)) {
480   - throw new TypeError('Usage: Buffer.concat(list, [length])');
481   - }
482   -
483   - if (list.length === 0) {
484   - return new Buffer(0);
485   - } else if (list.length === 1) {
486   - return list[0];
  300 +// TODO(trevnorris): currently works like Array.prototype.slice(), which
  301 +// doesn't follow the new standard for throwing on out of range indexes.
  302 +Buffer.prototype.slice = function(start, end) {
  303 + var len = this.length;
  304 + start = ~~start;
  305 + end = typeof end === 'undefined' ? len : ~~end;
  306 +
  307 + if (start < 0) {
  308 + start += len;
  309 + if (start < 0)
  310 + start = 0;
  311 + } else if (start > len) {
  312 + start = len;
487 313 }
488 314
489   - if (typeof length !== 'number') {
490   - length = 0;
491   - for (var i = 0; i < list.length; i++) {
492   - var buf = list[i];
493   - length += buf.length;
494   - }
  315 + if (end < 0) {
  316 + end += len;
  317 + if (end < 0)
  318 + end = 0;
  319 + } else if (end > len) {
  320 + end = len;
495 321 }
496 322
497   - var buffer = new Buffer(length);
498   - var pos = 0;
499   - for (var i = 0; i < list.length; i++) {
500   - var buf = list[i];
501   - buf.copy(buffer, pos);
502   - pos += buf.length;
503   - }
504   - return buffer;
505   -};
506   -
507   -
508   -
509   -
510   -// copy(targetBuffer, targetStart=0, sourceStart=0, sourceEnd=buffer.length)
511   -Buffer.prototype.copy = function(target, target_start, start, end) {
512   - // set undefined/NaN or out of bounds values equal to their default
513   - if (!(target_start >= 0)) target_start = 0;
514   - if (!(start >= 0)) start = 0;
515   - if (!(end < this.length)) end = this.length;
516   -
517   - // Copy 0 bytes; we're done
518   - if (end === start ||
519   - target.length === 0 ||
520   - this.length === 0 ||
521   - start > this.length)
522   - return 0;
523   -
524 323 if (end < start)
525   - throw new RangeError('sourceEnd < sourceStart');
526   -
527   - if (target_start >= target.length)
528   - throw new RangeError('targetStart out of bounds');
529   -
530   - if (target.length - target_start < end - start)
531   - end = target.length - target_start + start;
532   -
533   - return this.parent.copy(target.parent || target,
534   - target_start + (target.offset || 0),
535   - start + this.offset,
536   - end + this.offset);
537   -};
538   -
539   -
540   -// slice(start, end)
541   -Buffer.prototype.slice = function(start, end) {
542   - var len = this.length;
543   - start = clamp(start, len, 0);
544   - end = clamp(end, len, len);
545   - return new Buffer(this.parent, end - start, start + this.offset);
546   -};
547   -
548   -
549   -// Legacy methods for backwards compatibility.
550   -
551   -Buffer.prototype.utf8Slice = function(start, end) {
552   - return this.toString('utf8', start, end);
553   -};
  324 + end = start;
554 325
555   -Buffer.prototype.binarySlice = function(start, end) {
556   - return this.toString('binary', start, end);
557   -};
  326 + var buf = new Buffer();
  327 + buf.parent = sliceOnto(this, buf, start, end);
  328 + buf.length = end - start;
558 329
559   -Buffer.prototype.asciiSlice = function(start, end) {
560   - return this.toString('ascii', start, end);
  330 + return buf;
561 331 };
562 332
563   -Buffer.prototype.utf8Write = function(string, offset) {
564   - return this.write(string, offset, 'utf8');
565   -};
566   -
567   -Buffer.prototype.binaryWrite = function(string, offset) {
568   - return this.write(string, offset, 'binary');
569   -};
570 333
571   -Buffer.prototype.asciiWrite = function(string, offset) {
572   - return this.write(string, offset, 'ascii');
573   -};
574   -
575   -
576   -/*
577   - * Need to make sure that buffer isn't trying to write out of bounds.
578   - * This check is far too slow internally for fast buffers.
579   - */
580 334 function checkOffset(offset, ext, length) {
581   - if ((offset % 1) !== 0 || offset < 0)
582   - throw new RangeError('offset is not uint');
583   - if (offset + ext > length)
584   - throw new RangeError('Trying to access beyond buffer length');
  335 + if (offset < 0 || offset + ext > length)
  336 + throw new RangeError('index out of range');
585 337 }
586 338
587 339
588 340 Buffer.prototype.readUInt8 = function(offset, noAssert) {
  341 + offset = ~~offset;
589 342 if (!noAssert)
590 343 checkOffset(offset, 1, this.length);
591 344 return this[offset];
@@ -601,12 +354,12 @@ function readUInt16(buffer, offset, isBigEndian) {
601 354 val = buffer[offset];
602 355 val |= buffer[offset + 1] << 8;
603 356 }
604   -
605 357 return val;
606 358 }
607 359
608 360
609 361 Buffer.prototype.readUInt16LE = function(offset, noAssert) {
  362 + offset = ~~offset;
610 363 if (!noAssert)
611 364 checkOffset(offset, 2, this.length);
612 365 return readUInt16(this, offset, false, noAssert);
@@ -614,6 +367,7 @@ Buffer.prototype.readUInt16LE = function(offset, noAssert) {
614 367
615 368
616 369 Buffer.prototype.readUInt16BE = function(offset, noAssert) {
  370 + offset = ~~offset;
617 371 if (!noAssert)
618 372 checkOffset(offset, 2, this.length);
619 373 return readUInt16(this, offset, true, noAssert);
@@ -622,7 +376,6 @@ Buffer.prototype.readUInt16BE = function(offset, noAssert) {
622 376
623 377 function readUInt32(buffer, offset, isBigEndian, noAssert) {
624 378 var val = 0;
625   -
626 379 if (isBigEndian) {
627 380 val = buffer[offset + 1] << 16;
628 381 val |= buffer[offset + 2] << 8;
@@ -634,12 +387,12 @@ function readUInt32(buffer, offset, isBigEndian, noAssert) {
634 387 val |= buffer[offset];
635 388 val = val + (buffer[offset + 3] << 24 >>> 0);
636 389 }
637   -
638 390 return val;
639 391 }
640 392
641 393
642 394 Buffer.prototype.readUInt32LE = function(offset, noAssert) {
  395 + offset = ~~offset;
643 396 if (!noAssert)
644 397 checkOffset(offset, 4, this.length);
645 398 return readUInt32(this, offset, false, noAssert);
@@ -647,6 +400,7 @@ Buffer.prototype.readUInt32LE = function(offset, noAssert) {
647 400
648 401
649 402 Buffer.prototype.readUInt32BE = function(offset, noAssert) {
  403 + offset = ~~offset;
650 404 if (!noAssert)
651 405 checkOffset(offset, 4, this.length);
652 406 return readUInt32(this, offset, true, noAssert);
@@ -700,6 +454,7 @@ Buffer.prototype.readUInt32BE = function(offset, noAssert) {
700 454 */
701 455
702 456 Buffer.prototype.readInt8 = function(offset, noAssert) {
  457 + offset = ~~offset;
703 458 if (!noAssert)
704 459 checkOffset(offset, 1, this.length);
705 460 if (!(this[offset] & 0x80))
@@ -710,7 +465,6 @@ Buffer.prototype.readInt8 = function(offset, noAssert) {
710 465
711 466 function readInt16(buffer, offset, isBigEndian) {
712 467 var val = readUInt16(buffer, offset, isBigEndian);
713   -
714 468 if (!(val & 0x8000))
715 469 return val;
716 470 return (0xffff - val + 1) * -1;
@@ -718,6 +472,7 @@ function readInt16(buffer, offset, isBigEndian) {
718 472
719 473
720 474 Buffer.prototype.readInt16LE = function(offset, noAssert) {
  475 + offset = ~~offset;
721 476 if (!noAssert)
722 477 checkOffset(offset, 2, this.length);
723 478 return readInt16(this, offset, false);
@@ -725,6 +480,7 @@ Buffer.prototype.readInt16LE = function(offset, noAssert) {
725 480
726 481
727 482 Buffer.prototype.readInt16BE = function(offset, noAssert) {
  483 + offset = ~~offset;
728 484 if (!noAssert)
729 485 checkOffset(offset, 2, this.length);
730 486 return readInt16(this, offset, true);
@@ -733,7 +489,6 @@ Buffer.prototype.readInt16BE = function(offset, noAssert) {
733 489
734 490 function readInt32(buffer, offset, isBigEndian) {
735 491 var val = readUInt32(buffer, offset, isBigEndian);
736   -
737 492 if (!(val & 0x80000000))
738 493 return (val);
739 494 return (0xffffffff - val + 1) * -1;
@@ -741,6 +496,7 @@ function readInt32(buffer, offset, isBigEndian) {
741 496
742 497
743 498 Buffer.prototype.readInt32LE = function(offset, noAssert) {
  499 + offset = ~~offset;
744 500 if (!noAssert)
745 501 checkOffset(offset, 4, this.length);
746 502 return readInt32(this, offset, false);
@@ -748,50 +504,24 @@ Buffer.prototype.readInt32LE = function(offset, noAssert) {
748 504
749 505
750 506 Buffer.prototype.readInt32BE = function(offset, noAssert) {
  507 + offset = ~~offset;
751 508 if (!noAssert)
752 509 checkOffset(offset, 4, this.length);
753 510 return readInt32(this, offset, true);
754 511 };
755 512
756   -Buffer.prototype.readFloatLE = function(offset, noAssert) {
757   - if (!noAssert)
758   - checkOffset(offset, 4, this.length);
759   - return this.parent.readFloatLE(this.offset + offset, !!noAssert);
760   -};
761   -
762   -
763   -Buffer.prototype.readFloatBE = function(offset, noAssert) {
764   - if (!noAssert)
765   - checkOffset(offset, 4, this.length);
766   - return this.parent.readFloatBE(this.offset + offset, !!noAssert);
767   -};
768   -
769   -
770   -Buffer.prototype.readDoubleLE = function(offset, noAssert) {
771   - if (!noAssert)
772   - checkOffset(offset, 8, this.length);
773   - return this.parent.readDoubleLE(this.offset + offset, !!noAssert);
774   -};
775   -
776   -
777   -Buffer.prototype.readDoubleBE = function(offset, noAssert) {
778   - if (!noAssert)
779   - checkOffset(offset, 8, this.length);
780   - return this.parent.readDoubleBE(this.offset + offset, !!noAssert);
781   -};
782   -
783 513
784 514 function checkInt(buffer, value, offset, ext, max, min) {
785   - if ((value % 1) !== 0 || value > max || value < min)
  515 + if (value > max || value < min)
786 516 throw TypeError('value is out of bounds');
787   - if ((offset % 1) !== 0 || offset < 0)
788   - throw TypeError('offset is not uint');
789   - if (offset + ext > buffer.length || buffer.length + offset < 0)
790   - throw RangeError('Trying to write outside buffer length');
  517 + if (offset < 0 || offset + ext > buffer.length || buffer.length + offset < 0)
  518 + throw RangeError('index out of range');
791 519 }
792 520
793 521
794 522 Buffer.prototype.writeUInt8 = function(value, offset, noAssert) {
  523 + value = +value;
  524 + offset = ~~offset;
795 525 if (!noAssert)
796 526 checkInt(this, value, offset, 1, 0xff, 0);
797 527 this[offset] = value;
@@ -810,6 +540,8 @@ function writeUInt16(buffer, value, offset, isBigEndian) {
810 540
811 541
812 542 Buffer.prototype.writeUInt16LE = function(value, offset, noAssert) {
  543 + value = +value;
  544 + offset = ~~offset;
813 545 if (!noAssert)
814 546 checkInt(this, value, offset, 2, 0xffff, 0);
815 547 writeUInt16(this, value, offset, false);
@@ -817,6 +549,8 @@ Buffer.prototype.writeUInt16LE = function(value, offset, noAssert) {
817 549
818 550
819 551 Buffer.prototype.writeUInt16BE = function(value, offset, noAssert) {
  552 + value = +value;
  553 + offset = ~~offset;
820 554 if (!noAssert)
821 555 checkInt(this, value, offset, 2, 0xffff, 0);
822 556 writeUInt16(this, value, offset, true);
@@ -839,6 +573,8 @@ function writeUInt32(buffer, value, offset, isBigEndian) {
839 573
840 574
841 575 Buffer.prototype.writeUInt32LE = function(value, offset, noAssert) {
  576 + value = +value;
  577 + offset = ~~offset;
842 578 if (!noAssert)
843 579 checkInt(this, value, offset, 4, 0xffffffff, 0);
844 580 writeUInt32(this, value, offset, false);
@@ -846,6 +582,8 @@ Buffer.prototype.writeUInt32LE = function(value, offset, noAssert) {
846 582
847 583
848 584 Buffer.prototype.writeUInt32BE = function(value, offset, noAssert) {
  585 + value = +value;
  586 + offset = ~~offset;
849 587 if (!noAssert)
850 588 checkInt(this, value, offset, 4, 0xffffffff, 0);
851 589 writeUInt32(this, value, offset, true);
@@ -890,6 +628,8 @@ Buffer.prototype.writeUInt32BE = function(value, offset, noAssert) {
890 628 */
891 629
892 630 Buffer.prototype.writeInt8 = function(value, offset, noAssert) {
  631 + value = +value;
  632 + offset = ~~offset;
893 633 if (!noAssert)
894 634 checkInt(this, value, offset, 1, 0x7f, -0x80);
895 635 if (value < 0) value = 0xff + value + 1;
@@ -898,6 +638,8 @@ Buffer.prototype.writeInt8 = function(value, offset, noAssert) {
898 638
899 639
900 640 Buffer.prototype.writeInt16LE = function(value, offset, noAssert) {
  641 + value = +value;
  642 + offset = ~~offset;
901 643 if (!noAssert)
902 644 checkInt(this, value, offset, 2, 0x7fff, -0x8000);
903 645 if (value < 0) value = 0xffff + value + 1;
@@ -906,6 +648,8 @@ Buffer.prototype.writeInt16LE = function(value, offset, noAssert) {
906 648
907 649
908 650 Buffer.prototype.writeInt16BE = function(value, offset, noAssert) {
  651 + value = +value;
  652 + offset = ~~offset;
909 653 if (!noAssert)
910 654 checkInt(this, value, offset, 2, 0x7fff, -0x8000);
911 655 if (value < 0) value = 0xffff + value + 1;
@@ -914,6 +658,8 @@ Buffer.prototype.writeInt16BE = function(value, offset, noAssert) {
914 658
915 659
916 660 Buffer.prototype.writeInt32LE = function(value, offset, noAssert) {
  661 + value = +value;
  662 + offset = ~~offset;
917 663 if (!noAssert)
918 664 checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000);
919 665 if (value < 0) value = 0xffffffff + value + 1;
@@ -922,36 +668,10 @@ Buffer.prototype.writeInt32LE = function(value, offset, noAssert) {
922 668
923 669
924 670 Buffer.prototype.writeInt32BE = function(value, offset, noAssert) {
  671 + value = +value;
  672 + offset = ~~offset;
925 673 if (!noAssert)
926 674 checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000);
927 675 if (value < 0) value = 0xffffffff + value + 1;
928 676 writeUInt32(this, value, offset, true);
929 677 };
930   -
931   -
932   -Buffer.prototype.writeFloatLE = function(value, offset, noAssert) {
933   - if (!noAssert)
934   - checkOffset(offset, 4, this.length);
935   - this.parent.writeFloatLE(value, this.offset + offset, !!noAssert);
936   -};
937   -
938   -
939   -Buffer.prototype.writeFloatBE = function(value, offset, noAssert) {
940   - if (!noAssert)
941   - checkOffset(offset, 4, this.length);
942   - this.parent.writeFloatBE(value, this.offset + offset, !!noAssert);
943   -};
944   -
945   -
946   -Buffer.prototype.writeDoubleLE = function(value, offset, noAssert) {
947   - if (!noAssert)
948   - checkOffset(offset, 8, this.length);
949   - this.parent.writeDoubleLE(value, this.offset + offset, !!noAssert);
950   -};
951   -
952   -
953   -Buffer.prototype.writeDoubleBE = function(value, offset, noAssert) {
954   - if (!noAssert)
955   - checkOffset(offset, 8, this.length);
956   - this.parent.writeDoubleBE(value, this.offset + offset, !!noAssert);
957   -};
1  src/node.js
@@ -163,7 +163,6 @@
163 163 global.GLOBAL = global;
164 164 global.root = global;
165 165 global.Buffer = NativeModule.require('buffer').Buffer;
166   - process.binding('buffer').setFastBufferConstructor(global.Buffer);
167 166 process.domain = null;
168 167 process._exiting = false;
169 168 };
723 src/node_buffer.cc
@@ -20,367 +20,369 @@
20 20 // USE OR OTHER DEALINGS IN THE SOFTWARE.
21 21
22 22
23   -#include "node_buffer.h"
24   -
25 23 #include "node.h"
  24 +#include "node_internals.h"
  25 +#include "node_buffer.h"
  26 +#include "smalloc.h"
26 27 #include "string_bytes.h"
27 28
28 29 #include "v8.h"
29 30 #include "v8-profiler.h"
30 31
31 32 #include <assert.h>
32   -#include <string.h> // memcpy
  33 +#include <string.h>
33 34 #include <limits.h>
34 35
35   -#define MIN(a,b) ((a) < (b) ? (a) : (b))
  36 +#define MIN(a, b) ((a) < (b) ? (a) : (b))
36 37
37   -#define BUFFER_CLASS_ID (0xBABE)
  38 +#define CHECK_NOT_OOB(r) \
  39 + do { if (!(r)) return ThrowRangeError("out of range index"); } while (0)
38 40
39   -namespace node {
  41 +#define ARGS_THIS(argT) \
  42 + Local<Object> obj = argT; \
  43 + size_t obj_length = obj->GetIndexedPropertiesExternalArrayDataLength(); \
  44 + char* obj_data = static_cast<char*>( \
  45 + obj->GetIndexedPropertiesExternalArrayData());
40 46
41   -using namespace v8;
42   -
43   -#define SLICE_ARGS(start_arg, end_arg) \
44   - if (!start_arg->IsInt32() || !end_arg->IsInt32()) { \
45   - return ThrowException(Exception::TypeError( \
46   - String::New("Bad argument."))); \
47   - } \
48   - int32_t start = start_arg->Int32Value(); \
49   - int32_t end = end_arg->Int32Value(); \
50   - if (start < 0 || end < 0) { \
51   - return ThrowException(Exception::TypeError( \
52   - String::New("Bad argument."))); \
53   - } \
54   - if (!(start <= end)) { \
55   - return ThrowException(Exception::Error( \
56   - String::New("Must have start <= end"))); \
57   - } \
58   - if ((size_t)end > parent->length_) { \
59   - return ThrowException(Exception::Error( \
60   - String::New("end cannot be longer than parent.length"))); \
61   - }
  47 +#define SLICE_START_END(start_arg, end_arg, end_max) \
  48 + size_t start; \
  49 + size_t end; \
  50 + CHECK_NOT_OOB(ParseArrayIndex(start_arg, 0, &start)); \
  51 + CHECK_NOT_OOB(ParseArrayIndex(end_arg, end_max, &end)); \
  52 + if (end < start) end = start; \
  53 + CHECK_NOT_OOB(end <= end_max); \
  54 + size_t length = end - start;
62 55
  56 +namespace node {
63 57
64   -static Persistent<String> length_symbol;
65   -static Persistent<String> write_sym;
66   -static Persistent<Function> fast_buffer_constructor;
67   -Persistent<FunctionTemplate> Buffer::constructor_template;
  58 +namespace Buffer {
68 59
69 60
70   -Handle<Object> Buffer::New(Handle<String> string) {
71   - HandleScope scope(node_isolate);
  61 +using v8::Arguments;
  62 +using v8::Function;
  63 +using v8::FunctionTemplate;
  64 +using v8::Handle;
  65 +using v8::HandleScope;
  66 +using v8::Local;
  67 +using v8::Number;
  68 +using v8::Object;
  69 +using v8::Persistent;
  70 +using v8::String;
  71 +using v8::Uint32;
  72 +using v8::Undefined;