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