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