buffer: reject negative SlowBuffer offsets
[platform/upstream/nodejs.git] / lib / buffer.js
1 // Copyright Joyent, Inc. and other Node contributors.
2 //
3 // Permission is hereby granted, free of charge, to any person obtaining a
4 // copy of this software and associated documentation files (the
5 // "Software"), to deal in the Software without restriction, including
6 // without limitation the rights to use, copy, modify, merge, publish,
7 // distribute, sublicense, and/or sell copies of the Software, and to permit
8 // persons to whom the Software is furnished to do so, subject to the
9 // following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included
12 // in all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17 // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18 // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19 // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20 // USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22 var SlowBuffer = process.binding('buffer').SlowBuffer;
23 var assert = require('assert');
24
25 exports.INSPECT_MAX_BYTES = 50;
26
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;
30
31
32 function toHex(n) {
33   if (n < 16) return '0' + n.toString(16);
34   return n.toString(16);
35 }
36
37
38 SlowBuffer.prototype.hexSlice = function(start, end) {
39   var len = this.length;
40
41   if (!start || start < 0) start = 0;
42   if (!end || end < 0 || end > len) end = len;
43
44   var out = '';
45   for (var i = start; i < end; i++) {
46     out += toHex(this[i]);
47   }
48   return out;
49 };
50
51
52
53 SlowBuffer.prototype.toString = function(encoding, start, end) {
54   encoding = String(encoding || 'utf8').toLowerCase();
55   start = +start || 0;
56   if (typeof end == 'undefined') end = this.length;
57
58   // Fastpath empty strings
59   if (+end == start) {
60     return '';
61   }
62
63   switch (encoding) {
64     case 'hex':
65       return this.hexSlice(start, end);
66
67     case 'utf8':
68     case 'utf-8':
69       return this.utf8Slice(start, end);
70
71     case 'ascii':
72       return this.asciiSlice(start, end);
73
74     case 'binary':
75       return this.binarySlice(start, end);
76
77     case 'base64':
78       return this.base64Slice(start, end);
79
80     case 'ucs2':
81     case 'ucs-2':
82     case 'utf16le':
83     case 'utf-16le':
84       return this.ucs2Slice(start, end);
85
86     default:
87       throw new Error('Unknown encoding');
88   }
89 };
90
91
92 SlowBuffer.prototype.hexWrite = function(string, offset, length) {
93   offset = +offset || 0;
94   var remaining = this.length - offset;
95   if (!length) {
96     length = remaining;
97   } else {
98     length = +length;
99     if (length > remaining) {
100       length = remaining;
101     }
102   }
103
104   // must be an even number of digits
105   var strLen = string.length;
106   if (strLen % 2) {
107     throw new Error('Invalid hex string');
108   }
109   if (length > strLen / 2) {
110     length = strLen / 2;
111   }
112   for (var i = 0; i < length; i++) {
113     var byte = parseInt(string.substr(i * 2, 2), 16);
114     if (isNaN(byte)) throw new Error('Invalid hex string');
115     this[offset + i] = byte;
116   }
117   SlowBuffer._charsWritten = i * 2;
118   return i;
119 };
120
121
122 SlowBuffer.prototype.write = function(string, offset, length, encoding) {
123   // Support both (string, offset, length, encoding)
124   // and the legacy (string, encoding, offset, length)
125   if (isFinite(offset)) {
126     if (!isFinite(length)) {
127       encoding = length;
128       length = undefined;
129     }
130   } else {  // legacy
131     var swap = encoding;
132     encoding = offset;
133     offset = length;
134     length = swap;
135   }
136
137   offset = +offset || 0;
138   var remaining = this.length - offset;
139   if (!length) {
140     length = remaining;
141   } else {
142     length = +length;
143     if (length > remaining) {
144       length = remaining;
145     }
146   }
147   encoding = String(encoding || 'utf8').toLowerCase();
148
149   switch (encoding) {
150     case 'hex':
151       return this.hexWrite(string, offset, length);
152
153     case 'utf8':
154     case 'utf-8':
155       return this.utf8Write(string, offset, length);
156
157     case 'ascii':
158       return this.asciiWrite(string, offset, length);
159
160     case 'binary':
161       return this.binaryWrite(string, offset, length);
162
163     case 'base64':
164       return this.base64Write(string, offset, length);
165
166     case 'ucs2':
167     case 'ucs-2':
168     case 'utf16le':
169     case 'utf-16le':
170       return this.ucs2Write(string, offset, length);
171
172     default:
173       throw new Error('Unknown encoding');
174   }
175 };
176
177
178 // slice(start, end)
179 SlowBuffer.prototype.slice = function(start, end) {
180   if (end === undefined) end = this.length;
181
182   if (end > this.length) {
183     throw new Error('oob');
184   }
185   if (start > end) {
186     throw new Error('oob');
187   }
188
189   return new Buffer(this, end - start, +start);
190 };
191
192
193 function coerce(length) {
194   // Coerce length to a number (possibly NaN), round up
195   // in case it's fractional (e.g. 123.456). Since NaN
196   // comparisons are always false, use to return zero.
197   length = Math.ceil(+length);
198   return length > 0 ? length : 0;
199 }
200
201
202 var zeroBuffer = new SlowBuffer(0);
203
204 // Buffer
205 function Buffer(subject, encoding, offset) {
206   if (!(this instanceof Buffer)) {
207     return new Buffer(subject, encoding, offset);
208   }
209
210   var type;
211
212   // Are we slicing?
213   if (typeof offset === 'number') {
214     if (!Buffer.isBuffer(subject)) {
215       throw new Error('First argument must be a Buffer when slicing');
216     }
217
218     this.length = coerce(encoding);
219     this.parent = subject.parent ? subject.parent : subject;
220     this.offset = offset;
221   } else {
222     // Find the length
223     switch (type = typeof subject) {
224       case 'number':
225         this.length = coerce(subject);
226         break;
227
228       case 'string':
229         this.length = Buffer.byteLength(subject, encoding);
230         break;
231
232       case 'object': // Assume object is an array
233         this.length = coerce(subject.length);
234         break;
235
236       default:
237         throw new Error('First argument needs to be a number, ' +
238                         'array or string.');
239     }
240
241     if (this.length > Buffer.poolSize) {
242       // Big buffer, just alloc one.
243       this.parent = new SlowBuffer(this.length);
244       this.offset = 0;
245
246     } else if (this.length > 0) {
247       // Small buffer.
248       if (!pool || pool.length - pool.used < this.length) allocPool();
249       this.parent = pool;
250       this.offset = pool.used;
251       pool.used += this.length;
252       if (pool.used & 7) pool.used = (pool.used + 8) & ~7;
253
254     } else {
255       // Zero-length buffer
256       this.parent = zeroBuffer;
257       this.offset = 0;
258     }
259
260     // Treat array-ish objects as a byte array.
261     if (isArrayIsh(subject)) {
262       for (var i = 0; i < this.length; i++) {
263         this.parent[i + this.offset] = subject[i];
264       }
265     } else if (type == 'string') {
266       // We are a string
267       this.length = this.write(subject, 0, encoding);
268     }
269   }
270
271   SlowBuffer.makeFastBuffer(this.parent, this, this.offset, this.length);
272 }
273
274 function isArrayIsh(subject) {
275   return Array.isArray(subject) || Buffer.isBuffer(subject) ||
276          subject && typeof subject === 'object' &&
277          typeof subject.length === 'number';
278 }
279
280 exports.SlowBuffer = SlowBuffer;
281 exports.Buffer = Buffer;
282
283 Buffer.poolSize = 8 * 1024;
284 var pool;
285
286 function allocPool() {
287   pool = new SlowBuffer(Buffer.poolSize);
288   pool.used = 0;
289 }
290
291
292 // Static methods
293 Buffer.isBuffer = function isBuffer(b) {
294   return b instanceof Buffer;
295 };
296
297
298 // Inspect
299 Buffer.prototype.inspect = function inspect() {
300   var out = [],
301       len = this.length,
302       name = this.constructor.name;
303
304   for (var i = 0; i < len; i++) {
305     out[i] = toHex(this[i]);
306     if (i == exports.INSPECT_MAX_BYTES) {
307       out[i + 1] = '...';
308       break;
309     }
310   }
311
312   return '<' + name + ' ' + out.join(' ') + '>';
313 };
314
315
316 Buffer.prototype.get = function get(i) {
317   if (i < 0 || i >= this.length) throw new Error('oob');
318   return this.parent[this.offset + i];
319 };
320
321
322 Buffer.prototype.set = function set(i, v) {
323   if (i < 0 || i >= this.length) throw new Error('oob');
324   return this.parent[this.offset + i] = v;
325 };
326
327
328 // write(string, offset = 0, length = buffer.length-offset, encoding = 'utf8')
329 Buffer.prototype.write = function(string, offset, length, encoding) {
330   // Support both (string, offset, length, encoding)
331   // and the legacy (string, encoding, offset, length)
332   if (isFinite(offset)) {
333     if (!isFinite(length)) {
334       encoding = length;
335       length = undefined;
336     }
337   } else {  // legacy
338     var swap = encoding;
339     encoding = offset;
340     offset = length;
341     length = swap;
342   }
343
344   offset = +offset || 0;
345   var remaining = this.length - offset;
346   if (!length) {
347     length = remaining;
348   } else {
349     length = +length;
350     if (length > remaining) {
351       length = remaining;
352     }
353   }
354   encoding = String(encoding || 'utf8').toLowerCase();
355
356   var ret;
357   switch (encoding) {
358     case 'hex':
359       ret = this.parent.hexWrite(string, this.offset + offset, length);
360       break;
361
362     case 'utf8':
363     case 'utf-8':
364       ret = this.parent.utf8Write(string, this.offset + offset, length);
365       break;
366
367     case 'ascii':
368       ret = this.parent.asciiWrite(string, this.offset + offset, length);
369       break;
370
371     case 'binary':
372       ret = this.parent.binaryWrite(string, this.offset + offset, length);
373       break;
374
375     case 'base64':
376       // Warning: maxLength not taken into account in base64Write
377       ret = this.parent.base64Write(string, this.offset + offset, length);
378       break;
379
380     case 'ucs2':
381     case 'ucs-2':
382     case 'utf16le':
383     case 'utf-16le':
384       ret = this.parent.ucs2Write(string, this.offset + offset, length);
385       break;
386
387     default:
388       throw new Error('Unknown encoding');
389   }
390
391   Buffer._charsWritten = SlowBuffer._charsWritten;
392
393   return ret;
394 };
395
396
397 // toString(encoding, start=0, end=buffer.length)
398 Buffer.prototype.toString = function(encoding, start, end) {
399   encoding = String(encoding || 'utf8').toLowerCase();
400
401   if (typeof start == 'undefined' || start < 0) {
402     start = 0;
403   } else if (start > this.length) {
404     start = this.length;
405   }
406
407   if (typeof end == 'undefined' || end > this.length) {
408     end = this.length;
409   } else if (end < 0) {
410     end = 0;
411   }
412
413   start = start + this.offset;
414   end = end + this.offset;
415
416   switch (encoding) {
417     case 'hex':
418       return this.parent.hexSlice(start, end);
419
420     case 'utf8':
421     case 'utf-8':
422       return this.parent.utf8Slice(start, end);
423
424     case 'ascii':
425       return this.parent.asciiSlice(start, end);
426
427     case 'binary':
428       return this.parent.binarySlice(start, end);
429
430     case 'base64':
431       return this.parent.base64Slice(start, end);
432
433     case 'ucs2':
434     case 'ucs-2':
435     case 'utf16le':
436     case 'utf-16le':
437       return this.parent.ucs2Slice(start, end);
438
439     default:
440       throw new Error('Unknown encoding');
441   }
442 };
443
444
445 // byteLength
446 Buffer.byteLength = SlowBuffer.byteLength;
447
448
449 // fill(value, start=0, end=buffer.length)
450 Buffer.prototype.fill = function fill(value, start, end) {
451   value || (value = 0);
452   start || (start = 0);
453   end || (end = this.length);
454
455   if (typeof value === 'string') {
456     value = value.charCodeAt(0);
457   }
458   if (!(typeof value === 'number') || isNaN(value)) {
459     throw new Error('value is not a number');
460   }
461
462   if (end < start) throw new Error('end < start');
463
464   // Fill 0 bytes; we're done
465   if (end === start) return 0;
466   if (this.length == 0) return 0;
467
468   if (start < 0 || start >= this.length) {
469     throw new Error('start out of bounds');
470   }
471
472   if (end < 0 || end > this.length) {
473     throw new Error('end out of bounds');
474   }
475
476   return this.parent.fill(value,
477                           start + this.offset,
478                           end + this.offset);
479 };
480
481
482 Buffer.concat = function(list, length) {
483   if (!Array.isArray(list)) {
484     throw new Error('Usage: Buffer.concat(list, [length])');
485   }
486
487   if (list.length === 0) {
488     return new Buffer(0);
489   } else if (list.length === 1) {
490     return list[0];
491   }
492
493   if (typeof length !== 'number') {
494     length = 0;
495     for (var i = 0; i < list.length; i++) {
496       var buf = list[i];
497       length += buf.length;
498     }
499   }
500
501   var buffer = new Buffer(length);
502   var pos = 0;
503   for (var i = 0; i < list.length; i++) {
504     var buf = list[i];
505     buf.copy(buffer, pos);
506     pos += buf.length;
507   }
508   return buffer;
509 };
510
511
512
513
514 // copy(targetBuffer, targetStart=0, sourceStart=0, sourceEnd=buffer.length)
515 Buffer.prototype.copy = function(target, target_start, start, end) {
516   var source = this;
517   start || (start = 0);
518   end || (end = this.length);
519   target_start || (target_start = 0);
520
521   if (end < start) throw new Error('sourceEnd < sourceStart');
522
523   // Copy 0 bytes; we're done
524   if (end === start) return 0;
525   if (target.length == 0 || source.length == 0) return 0;
526
527   if (target_start < 0 || target_start >= target.length) {
528     throw new Error('targetStart out of bounds');
529   }
530
531   if (start < 0 || start >= source.length) {
532     throw new Error('sourceStart out of bounds');
533   }
534
535   if (end < 0 || end > source.length) {
536     throw new Error('sourceEnd out of bounds');
537   }
538
539   // Are we oob?
540   if (end > this.length) {
541     end = this.length;
542   }
543
544   if (target.length - target_start < end - start) {
545     end = target.length - target_start + start;
546   }
547
548   return this.parent.copy(target.parent,
549                           target_start + target.offset,
550                           start + this.offset,
551                           end + this.offset);
552 };
553
554
555 // slice(start, end)
556 Buffer.prototype.slice = function(start, end) {
557   if (end === undefined) end = this.length;
558   if (end > this.length) throw new Error('oob');
559   if (start > end) throw new Error('oob');
560   if (start < 0) throw new Error('start out of bounds');
561   return new Buffer(this.parent, end - start, +start + this.offset);
562 };
563
564
565 // Legacy methods for backwards compatibility.
566
567 Buffer.prototype.utf8Slice = function(start, end) {
568   return this.toString('utf8', start, end);
569 };
570
571 Buffer.prototype.binarySlice = function(start, end) {
572   return this.toString('binary', start, end);
573 };
574
575 Buffer.prototype.asciiSlice = function(start, end) {
576   return this.toString('ascii', start, end);
577 };
578
579 Buffer.prototype.utf8Write = function(string, offset) {
580   return this.write(string, offset, 'utf8');
581 };
582
583 Buffer.prototype.binaryWrite = function(string, offset) {
584   return this.write(string, offset, 'binary');
585 };
586
587 Buffer.prototype.asciiWrite = function(string, offset) {
588   return this.write(string, offset, 'ascii');
589 };
590
591 Buffer.prototype.readUInt8 = function(offset, noAssert) {
592   var buffer = this;
593
594   if (!noAssert) {
595     assert.ok(offset !== undefined && offset !== null,
596         'missing offset');
597
598     assert.ok(offset < buffer.length,
599         'Trying to read beyond buffer length');
600   }
601
602   return buffer[offset];
603 };
604
605 function readUInt16(buffer, offset, isBigEndian, noAssert) {
606   var val = 0;
607
608
609   if (!noAssert) {
610     assert.ok(typeof (isBigEndian) === 'boolean',
611         'missing or invalid endian');
612
613     assert.ok(offset !== undefined && offset !== null,
614         'missing offset');
615
616     assert.ok(offset + 1 < buffer.length,
617         'Trying to read beyond buffer length');
618   }
619
620   if (isBigEndian) {
621     val = buffer[offset] << 8;
622     val |= buffer[offset + 1];
623   } else {
624     val = buffer[offset];
625     val |= buffer[offset + 1] << 8;
626   }
627
628   return val;
629 }
630
631 Buffer.prototype.readUInt16LE = function(offset, noAssert) {
632   return readUInt16(this, offset, false, noAssert);
633 };
634
635 Buffer.prototype.readUInt16BE = function(offset, noAssert) {
636   return readUInt16(this, offset, true, noAssert);
637 };
638
639 function readUInt32(buffer, offset, isBigEndian, noAssert) {
640   var val = 0;
641
642   if (!noAssert) {
643     assert.ok(typeof (isBigEndian) === 'boolean',
644         'missing or invalid endian');
645
646     assert.ok(offset !== undefined && offset !== null,
647         'missing offset');
648
649     assert.ok(offset + 3 < buffer.length,
650         'Trying to read beyond buffer length');
651   }
652
653   if (isBigEndian) {
654     val = buffer[offset + 1] << 16;
655     val |= buffer[offset + 2] << 8;
656     val |= buffer[offset + 3];
657     val = val + (buffer[offset] << 24 >>> 0);
658   } else {
659     val = buffer[offset + 2] << 16;
660     val |= buffer[offset + 1] << 8;
661     val |= buffer[offset];
662     val = val + (buffer[offset + 3] << 24 >>> 0);
663   }
664
665   return val;
666 }
667
668 Buffer.prototype.readUInt32LE = function(offset, noAssert) {
669   return readUInt32(this, offset, false, noAssert);
670 };
671
672 Buffer.prototype.readUInt32BE = function(offset, noAssert) {
673   return readUInt32(this, offset, true, noAssert);
674 };
675
676
677 /*
678  * Signed integer types, yay team! A reminder on how two's complement actually
679  * works. The first bit is the signed bit, i.e. tells us whether or not the
680  * number should be positive or negative. If the two's complement value is
681  * positive, then we're done, as it's equivalent to the unsigned representation.
682  *
683  * Now if the number is positive, you're pretty much done, you can just leverage
684  * the unsigned translations and return those. Unfortunately, negative numbers
685  * aren't quite that straightforward.
686  *
687  * At first glance, one might be inclined to use the traditional formula to
688  * translate binary numbers between the positive and negative values in two's
689  * complement. (Though it doesn't quite work for the most negative value)
690  * Mainly:
691  *  - invert all the bits
692  *  - add one to the result
693  *
694  * Of course, this doesn't quite work in Javascript. Take for example the value
695  * of -128. This could be represented in 16 bits (big-endian) as 0xff80. But of
696  * course, Javascript will do the following:
697  *
698  * > ~0xff80
699  * -65409
700  *
701  * Whoh there, Javascript, that's not quite right. But wait, according to
702  * Javascript that's perfectly correct. When Javascript ends up seeing the
703  * constant 0xff80, it has no notion that it is actually a signed number. It
704  * assumes that we've input the unsigned value 0xff80. Thus, when it does the
705  * binary negation, it casts it into a signed value, (positive 0xff80). Then
706  * when you perform binary negation on that, it turns it into a negative number.
707  *
708  * Instead, we're going to have to use the following general formula, that works
709  * in a rather Javascript friendly way. I'm glad we don't support this kind of
710  * weird numbering scheme in the kernel.
711  *
712  * (BIT-MAX - (unsigned)val + 1) * -1
713  *
714  * The astute observer, may think that this doesn't make sense for 8-bit numbers
715  * (really it isn't necessary for them). However, when you get 16-bit numbers,
716  * you do. Let's go back to our prior example and see how this will look:
717  *
718  * (0xffff - 0xff80 + 1) * -1
719  * (0x007f + 1) * -1
720  * (0x0080) * -1
721  */
722 Buffer.prototype.readInt8 = function(offset, noAssert) {
723   var buffer = this;
724   var neg;
725
726   if (!noAssert) {
727     assert.ok(offset !== undefined && offset !== null,
728         'missing offset');
729
730     assert.ok(offset < buffer.length,
731         'Trying to read beyond buffer length');
732   }
733
734   neg = buffer[offset] & 0x80;
735   if (!neg) {
736     return (buffer[offset]);
737   }
738
739   return ((0xff - buffer[offset] + 1) * -1);
740 };
741
742 function readInt16(buffer, offset, isBigEndian, noAssert) {
743   var neg, val;
744
745   if (!noAssert) {
746     assert.ok(typeof (isBigEndian) === 'boolean',
747         'missing or invalid endian');
748
749     assert.ok(offset !== undefined && offset !== null,
750         'missing offset');
751
752     assert.ok(offset + 1 < buffer.length,
753         'Trying to read beyond buffer length');
754   }
755
756   val = readUInt16(buffer, offset, isBigEndian, noAssert);
757   neg = val & 0x8000;
758   if (!neg) {
759     return val;
760   }
761
762   return (0xffff - val + 1) * -1;
763 }
764
765 Buffer.prototype.readInt16LE = function(offset, noAssert) {
766   return readInt16(this, offset, false, noAssert);
767 };
768
769 Buffer.prototype.readInt16BE = function(offset, noAssert) {
770   return readInt16(this, offset, true, noAssert);
771 };
772
773 function readInt32(buffer, offset, isBigEndian, noAssert) {
774   var neg, val;
775
776   if (!noAssert) {
777     assert.ok(typeof (isBigEndian) === 'boolean',
778         'missing or invalid endian');
779
780     assert.ok(offset !== undefined && offset !== null,
781         'missing offset');
782
783     assert.ok(offset + 3 < buffer.length,
784         'Trying to read beyond buffer length');
785   }
786
787   val = readUInt32(buffer, offset, isBigEndian, noAssert);
788   neg = val & 0x80000000;
789   if (!neg) {
790     return (val);
791   }
792
793   return (0xffffffff - val + 1) * -1;
794 }
795
796 Buffer.prototype.readInt32LE = function(offset, noAssert) {
797   return readInt32(this, offset, false, noAssert);
798 };
799
800 Buffer.prototype.readInt32BE = function(offset, noAssert) {
801   return readInt32(this, offset, true, noAssert);
802 };
803
804 function readFloat(buffer, offset, isBigEndian, noAssert) {
805   if (!noAssert) {
806     assert.ok(typeof (isBigEndian) === 'boolean',
807         'missing or invalid endian');
808
809     assert.ok(offset + 3 < buffer.length,
810         'Trying to read beyond buffer length');
811   }
812
813   return require('buffer_ieee754').readIEEE754(buffer, offset, isBigEndian,
814       23, 4);
815 }
816
817 Buffer.prototype.readFloatLE = function(offset, noAssert) {
818   return readFloat(this, offset, false, noAssert);
819 };
820
821 Buffer.prototype.readFloatBE = function(offset, noAssert) {
822   return readFloat(this, offset, true, noAssert);
823 };
824
825 function readDouble(buffer, offset, isBigEndian, noAssert) {
826   if (!noAssert) {
827     assert.ok(typeof (isBigEndian) === 'boolean',
828         'missing or invalid endian');
829
830     assert.ok(offset + 7 < buffer.length,
831         'Trying to read beyond buffer length');
832   }
833
834   return require('buffer_ieee754').readIEEE754(buffer, offset, isBigEndian,
835       52, 8);
836 }
837
838 Buffer.prototype.readDoubleLE = function(offset, noAssert) {
839   return readDouble(this, offset, false, noAssert);
840 };
841
842 Buffer.prototype.readDoubleBE = function(offset, noAssert) {
843   return readDouble(this, offset, true, noAssert);
844 };
845
846
847 /*
848  * We have to make sure that the value is a valid integer. This means that it is
849  * non-negative. It has no fractional component and that it does not exceed the
850  * maximum allowed value.
851  *
852  *      value           The number to check for validity
853  *
854  *      max             The maximum value
855  */
856 function verifuint(value, max) {
857   assert.ok(typeof (value) == 'number',
858       'cannot write a non-number as a number');
859
860   assert.ok(value >= 0,
861       'specified a negative value for writing an unsigned value');
862
863   assert.ok(value <= max, 'value is larger than maximum value for type');
864
865   assert.ok(Math.floor(value) === value, 'value has a fractional component');
866 }
867
868 Buffer.prototype.writeUInt8 = function(value, offset, noAssert) {
869   var buffer = this;
870
871   if (!noAssert) {
872     assert.ok(value !== undefined && value !== null,
873         'missing value');
874
875     assert.ok(offset !== undefined && offset !== null,
876         'missing offset');
877
878     assert.ok(offset < buffer.length,
879         'trying to write beyond buffer length');
880
881     verifuint(value, 0xff);
882   }
883
884   buffer[offset] = value;
885 };
886
887 function writeUInt16(buffer, value, offset, isBigEndian, noAssert) {
888   if (!noAssert) {
889     assert.ok(value !== undefined && value !== null,
890         'missing value');
891
892     assert.ok(typeof (isBigEndian) === 'boolean',
893         'missing or invalid endian');
894
895     assert.ok(offset !== undefined && offset !== null,
896         'missing offset');
897
898     assert.ok(offset + 1 < buffer.length,
899         'trying to write beyond buffer length');
900
901     verifuint(value, 0xffff);
902   }
903
904   if (isBigEndian) {
905     buffer[offset] = (value & 0xff00) >>> 8;
906     buffer[offset + 1] = value & 0x00ff;
907   } else {
908     buffer[offset + 1] = (value & 0xff00) >>> 8;
909     buffer[offset] = value & 0x00ff;
910   }
911 }
912
913 Buffer.prototype.writeUInt16LE = function(value, offset, noAssert) {
914   writeUInt16(this, value, offset, false, noAssert);
915 };
916
917 Buffer.prototype.writeUInt16BE = function(value, offset, noAssert) {
918   writeUInt16(this, value, offset, true, noAssert);
919 };
920
921 function writeUInt32(buffer, value, offset, isBigEndian, noAssert) {
922   if (!noAssert) {
923     assert.ok(value !== undefined && value !== null,
924         'missing value');
925
926     assert.ok(typeof (isBigEndian) === 'boolean',
927         'missing or invalid endian');
928
929     assert.ok(offset !== undefined && offset !== null,
930         'missing offset');
931
932     assert.ok(offset + 3 < buffer.length,
933         'trying to write beyond buffer length');
934
935     verifuint(value, 0xffffffff);
936   }
937
938   if (isBigEndian) {
939     buffer[offset] = (value >>> 24) & 0xff;
940     buffer[offset + 1] = (value >>> 16) & 0xff;
941     buffer[offset + 2] = (value >>> 8) & 0xff;
942     buffer[offset + 3] = value & 0xff;
943   } else {
944     buffer[offset + 3] = (value >>> 24) & 0xff;
945     buffer[offset + 2] = (value >>> 16) & 0xff;
946     buffer[offset + 1] = (value >>> 8) & 0xff;
947     buffer[offset] = value & 0xff;
948   }
949 }
950
951 Buffer.prototype.writeUInt32LE = function(value, offset, noAssert) {
952   writeUInt32(this, value, offset, false, noAssert);
953 };
954
955 Buffer.prototype.writeUInt32BE = function(value, offset, noAssert) {
956   writeUInt32(this, value, offset, true, noAssert);
957 };
958
959
960 /*
961  * We now move onto our friends in the signed number category. Unlike unsigned
962  * numbers, we're going to have to worry a bit more about how we put values into
963  * arrays. Since we are only worrying about signed 32-bit values, we're in
964  * slightly better shape. Unfortunately, we really can't do our favorite binary
965  * & in this system. It really seems to do the wrong thing. For example:
966  *
967  * > -32 & 0xff
968  * 224
969  *
970  * What's happening above is really: 0xe0 & 0xff = 0xe0. However, the results of
971  * this aren't treated as a signed number. Ultimately a bad thing.
972  *
973  * What we're going to want to do is basically create the unsigned equivalent of
974  * our representation and pass that off to the wuint* functions. To do that
975  * we're going to do the following:
976  *
977  *  - if the value is positive
978  *      we can pass it directly off to the equivalent wuint
979  *  - if the value is negative
980  *      we do the following computation:
981  *         mb + val + 1, where
982  *         mb   is the maximum unsigned value in that byte size
983  *         val  is the Javascript negative integer
984  *
985  *
986  * As a concrete value, take -128. In signed 16 bits this would be 0xff80. If
987  * you do out the computations:
988  *
989  * 0xffff - 128 + 1
990  * 0xffff - 127
991  * 0xff80
992  *
993  * You can then encode this value as the signed version. This is really rather
994  * hacky, but it should work and get the job done which is our goal here.
995  */
996
997 /*
998  * A series of checks to make sure we actually have a signed 32-bit number
999  */
1000 function verifsint(value, max, min) {
1001   assert.ok(typeof (value) == 'number',
1002       'cannot write a non-number as a number');
1003
1004   assert.ok(value <= max, 'value larger than maximum allowed value');
1005
1006   assert.ok(value >= min, 'value smaller than minimum allowed value');
1007
1008   assert.ok(Math.floor(value) === value, 'value has a fractional component');
1009 }
1010
1011 function verifIEEE754(value, max, min) {
1012   assert.ok(typeof (value) == 'number',
1013       'cannot write a non-number as a number');
1014
1015   assert.ok(value <= max, 'value larger than maximum allowed value');
1016
1017   assert.ok(value >= min, 'value smaller than minimum allowed value');
1018 }
1019
1020 Buffer.prototype.writeInt8 = function(value, offset, noAssert) {
1021   var buffer = this;
1022
1023   if (!noAssert) {
1024     assert.ok(value !== undefined && value !== null,
1025         'missing value');
1026
1027     assert.ok(offset !== undefined && offset !== null,
1028         'missing offset');
1029
1030     assert.ok(offset < buffer.length,
1031         'Trying to write beyond buffer length');
1032
1033     verifsint(value, 0x7f, -0x80);
1034   }
1035
1036   if (value >= 0) {
1037     buffer.writeUInt8(value, offset, noAssert);
1038   } else {
1039     buffer.writeUInt8(0xff + value + 1, offset, noAssert);
1040   }
1041 };
1042
1043 function writeInt16(buffer, value, offset, isBigEndian, noAssert) {
1044   if (!noAssert) {
1045     assert.ok(value !== undefined && value !== null,
1046         'missing value');
1047
1048     assert.ok(typeof (isBigEndian) === 'boolean',
1049         'missing or invalid endian');
1050
1051     assert.ok(offset !== undefined && offset !== null,
1052         'missing offset');
1053
1054     assert.ok(offset + 1 < buffer.length,
1055         'Trying to write beyond buffer length');
1056
1057     verifsint(value, 0x7fff, -0x8000);
1058   }
1059
1060   if (value >= 0) {
1061     writeUInt16(buffer, value, offset, isBigEndian, noAssert);
1062   } else {
1063     writeUInt16(buffer, 0xffff + value + 1, offset, isBigEndian, noAssert);
1064   }
1065 }
1066
1067 Buffer.prototype.writeInt16LE = function(value, offset, noAssert) {
1068   writeInt16(this, value, offset, false, noAssert);
1069 };
1070
1071 Buffer.prototype.writeInt16BE = function(value, offset, noAssert) {
1072   writeInt16(this, value, offset, true, noAssert);
1073 };
1074
1075 function writeInt32(buffer, value, offset, isBigEndian, noAssert) {
1076   if (!noAssert) {
1077     assert.ok(value !== undefined && value !== null,
1078         'missing value');
1079
1080     assert.ok(typeof (isBigEndian) === 'boolean',
1081         'missing or invalid endian');
1082
1083     assert.ok(offset !== undefined && offset !== null,
1084         'missing offset');
1085
1086     assert.ok(offset + 3 < buffer.length,
1087         'Trying to write beyond buffer length');
1088
1089     verifsint(value, 0x7fffffff, -0x80000000);
1090   }
1091
1092   if (value >= 0) {
1093     writeUInt32(buffer, value, offset, isBigEndian, noAssert);
1094   } else {
1095     writeUInt32(buffer, 0xffffffff + value + 1, offset, isBigEndian, noAssert);
1096   }
1097 }
1098
1099 Buffer.prototype.writeInt32LE = function(value, offset, noAssert) {
1100   writeInt32(this, value, offset, false, noAssert);
1101 };
1102
1103 Buffer.prototype.writeInt32BE = function(value, offset, noAssert) {
1104   writeInt32(this, value, offset, true, noAssert);
1105 };
1106
1107 function writeFloat(buffer, value, offset, isBigEndian, noAssert) {
1108   if (!noAssert) {
1109     assert.ok(value !== undefined && value !== null,
1110         'missing value');
1111
1112     assert.ok(typeof (isBigEndian) === 'boolean',
1113         'missing or invalid endian');
1114
1115     assert.ok(offset !== undefined && offset !== null,
1116         'missing offset');
1117
1118     assert.ok(offset + 3 < buffer.length,
1119         'Trying to write beyond buffer length');
1120
1121     verifIEEE754(value, 3.4028234663852886e+38, -3.4028234663852886e+38);
1122   }
1123
1124   require('buffer_ieee754').writeIEEE754(buffer, value, offset, isBigEndian,
1125       23, 4);
1126 }
1127
1128 Buffer.prototype.writeFloatLE = function(value, offset, noAssert) {
1129   writeFloat(this, value, offset, false, noAssert);
1130 };
1131
1132 Buffer.prototype.writeFloatBE = function(value, offset, noAssert) {
1133   writeFloat(this, value, offset, true, noAssert);
1134 };
1135
1136 function writeDouble(buffer, value, offset, isBigEndian, noAssert) {
1137   if (!noAssert) {
1138     assert.ok(value !== undefined && value !== null,
1139         'missing value');
1140
1141     assert.ok(typeof (isBigEndian) === 'boolean',
1142         'missing or invalid endian');
1143
1144     assert.ok(offset !== undefined && offset !== null,
1145         'missing offset');
1146
1147     assert.ok(offset + 7 < buffer.length,
1148         'Trying to write beyond buffer length');
1149
1150     verifIEEE754(value, 1.7976931348623157E+308, -1.7976931348623157E+308);
1151   }
1152
1153   require('buffer_ieee754').writeIEEE754(buffer, value, offset, isBigEndian,
1154       52, 8);
1155 }
1156
1157 Buffer.prototype.writeDoubleLE = function(value, offset, noAssert) {
1158   writeDouble(this, value, offset, false, noAssert);
1159 };
1160
1161 Buffer.prototype.writeDoubleBE = function(value, offset, noAssert) {
1162   writeDouble(this, value, offset, true, noAssert);
1163 };