buffer: clean up copy() asserts and tests
[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: ' + 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: ' + 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
284 Buffer.isEncoding = function(encoding) {
285   switch (encoding && encoding.toLowerCase()) {
286     case 'hex':
287     case 'utf8':
288     case 'utf-8':
289     case 'ascii':
290     case 'binary':
291     case 'base64':
292     case 'ucs2':
293     case 'ucs-2':
294     case 'utf16le':
295     case 'utf-16le':
296     case 'raw':
297       return true;
298
299     default:
300       return false;
301   }
302 };
303
304
305
306 Buffer.poolSize = 8 * 1024;
307 var pool;
308
309 function allocPool() {
310   pool = new SlowBuffer(Buffer.poolSize);
311   pool.used = 0;
312 }
313
314
315 // Static methods
316 Buffer.isBuffer = function isBuffer(b) {
317   return b instanceof Buffer;
318 };
319
320
321 // Inspect
322 Buffer.prototype.inspect = function inspect() {
323   var out = [],
324       len = this.length,
325       name = this.constructor.name;
326
327   for (var i = 0; i < len; i++) {
328     out[i] = toHex(this[i]);
329     if (i == exports.INSPECT_MAX_BYTES) {
330       out[i + 1] = '...';
331       break;
332     }
333   }
334
335   return '<' + name + ' ' + out.join(' ') + '>';
336 };
337
338
339 Buffer.prototype.get = function get(i) {
340   if (i < 0 || i >= this.length) throw new Error('oob');
341   return this.parent[this.offset + i];
342 };
343
344
345 Buffer.prototype.set = function set(i, v) {
346   if (i < 0 || i >= this.length) throw new Error('oob');
347   return this.parent[this.offset + i] = v;
348 };
349
350
351 // write(string, offset = 0, length = buffer.length-offset, encoding = 'utf8')
352 Buffer.prototype.write = function(string, offset, length, encoding) {
353   // Support both (string, offset, length, encoding)
354   // and the legacy (string, encoding, offset, length)
355   if (isFinite(offset)) {
356     if (!isFinite(length)) {
357       encoding = length;
358       length = undefined;
359     }
360   } else {  // legacy
361     var swap = encoding;
362     encoding = offset;
363     offset = length;
364     length = swap;
365   }
366
367   offset = +offset || 0;
368   var remaining = this.length - offset;
369   if (!length) {
370     length = remaining;
371   } else {
372     length = +length;
373     if (length > remaining) {
374       length = remaining;
375     }
376   }
377   encoding = String(encoding || 'utf8').toLowerCase();
378
379   var ret;
380   switch (encoding) {
381     case 'hex':
382       ret = this.parent.hexWrite(string, this.offset + offset, length);
383       break;
384
385     case 'utf8':
386     case 'utf-8':
387       ret = this.parent.utf8Write(string, this.offset + offset, length);
388       break;
389
390     case 'ascii':
391       ret = this.parent.asciiWrite(string, this.offset + offset, length);
392       break;
393
394     case 'binary':
395       ret = this.parent.binaryWrite(string, this.offset + offset, length);
396       break;
397
398     case 'base64':
399       // Warning: maxLength not taken into account in base64Write
400       ret = this.parent.base64Write(string, this.offset + offset, length);
401       break;
402
403     case 'ucs2':
404     case 'ucs-2':
405     case 'utf16le':
406     case 'utf-16le':
407       ret = this.parent.ucs2Write(string, this.offset + offset, length);
408       break;
409
410     default:
411       throw new Error('Unknown encoding: ' + encoding);
412   }
413
414   Buffer._charsWritten = SlowBuffer._charsWritten;
415
416   return ret;
417 };
418
419
420 Buffer.prototype.toJSON = function() {
421   return Array.prototype.slice.call(this, 0);
422 };
423
424
425 // toString(encoding, start=0, end=buffer.length)
426 Buffer.prototype.toString = function(encoding, start, end) {
427   encoding = String(encoding || 'utf8').toLowerCase();
428
429   if (typeof start == 'undefined' || start < 0) {
430     start = 0;
431   } else if (start > this.length) {
432     start = this.length;
433   }
434
435   if (typeof end == 'undefined' || end > this.length) {
436     end = this.length;
437   } else if (end < 0) {
438     end = 0;
439   }
440
441   start = start + this.offset;
442   end = end + this.offset;
443
444   switch (encoding) {
445     case 'hex':
446       return this.parent.hexSlice(start, end);
447
448     case 'utf8':
449     case 'utf-8':
450       return this.parent.utf8Slice(start, end);
451
452     case 'ascii':
453       return this.parent.asciiSlice(start, end);
454
455     case 'binary':
456       return this.parent.binarySlice(start, end);
457
458     case 'base64':
459       return this.parent.base64Slice(start, end);
460
461     case 'ucs2':
462     case 'ucs-2':
463     case 'utf16le':
464     case 'utf-16le':
465       return this.parent.ucs2Slice(start, end);
466
467     default:
468       throw new Error('Unknown encoding: ' + encoding);
469   }
470 };
471
472
473 // byteLength
474 Buffer.byteLength = SlowBuffer.byteLength;
475
476
477 // fill(value, start=0, end=buffer.length)
478 Buffer.prototype.fill = function fill(value, start, end) {
479   value || (value = 0);
480   start || (start = 0);
481   end || (end = this.length);
482
483   if (typeof value === 'string') {
484     value = value.charCodeAt(0);
485   }
486   if (!(typeof value === 'number') || isNaN(value)) {
487     throw new Error('value is not a number');
488   }
489
490   if (end < start) throw new Error('end < start');
491
492   // Fill 0 bytes; we're done
493   if (end === start) return 0;
494   if (this.length == 0) return 0;
495
496   if (start < 0 || start >= this.length) {
497     throw new Error('start out of bounds');
498   }
499
500   if (end < 0 || end > this.length) {
501     throw new Error('end out of bounds');
502   }
503
504   return this.parent.fill(value,
505                           start + this.offset,
506                           end + this.offset);
507 };
508
509
510 Buffer.concat = function(list, length) {
511   if (!Array.isArray(list)) {
512     throw new Error('Usage: Buffer.concat(list, [length])');
513   }
514
515   if (list.length === 0) {
516     return new Buffer(0);
517   } else if (list.length === 1) {
518     return list[0];
519   }
520
521   if (typeof length !== 'number') {
522     length = 0;
523     for (var i = 0; i < list.length; i++) {
524       var buf = list[i];
525       length += buf.length;
526     }
527   }
528
529   var buffer = new Buffer(length);
530   var pos = 0;
531   for (var i = 0; i < list.length; i++) {
532     var buf = list[i];
533     buf.copy(buffer, pos);
534     pos += buf.length;
535   }
536   return buffer;
537 };
538
539
540
541
542 // copy(targetBuffer, targetStart=0, sourceStart=0, sourceEnd=buffer.length)
543 Buffer.prototype.copy = function(target, target_start, start, end) {
544   // set undefined/NaN or out of bounds values equal to their default
545   if (!(target_start >= 0)) target_start = 0;
546   if (!(start >= 0)) start = 0;
547   if (!(end < this.length)) end = this.length;
548
549   // Copy 0 bytes; we're done
550   if (end === start ||
551       target.length === 0 ||
552       this.length === 0 ||
553       start > this.length)
554     return 0;
555
556   if (end < start)
557     throw new RangeError('sourceEnd < sourceStart');
558
559   if (target_start >= target.length)
560     throw new RangeError('targetStart out of bounds');
561
562   if (target.length - target_start < end - start)
563     end = target.length - target_start + start;
564
565   return this.parent.copy(target.parent || target,
566                           target_start + (target.offset || 0),
567                           start + this.offset,
568                           end + this.offset);
569 };
570
571
572 // slice(start, end)
573 Buffer.prototype.slice = function(start, end) {
574   if (end === undefined) end = this.length;
575   if (end > this.length) throw new Error('oob');
576   if (start > end) throw new Error('oob');
577   if (start < 0) throw new Error('start out of bounds');
578   return new Buffer(this.parent, end - start, +start + this.offset);
579 };
580
581
582 // Legacy methods for backwards compatibility.
583
584 Buffer.prototype.utf8Slice = function(start, end) {
585   return this.toString('utf8', start, end);
586 };
587
588 Buffer.prototype.binarySlice = function(start, end) {
589   return this.toString('binary', start, end);
590 };
591
592 Buffer.prototype.asciiSlice = function(start, end) {
593   return this.toString('ascii', start, end);
594 };
595
596 Buffer.prototype.utf8Write = function(string, offset) {
597   return this.write(string, offset, 'utf8');
598 };
599
600 Buffer.prototype.binaryWrite = function(string, offset) {
601   return this.write(string, offset, 'binary');
602 };
603
604 Buffer.prototype.asciiWrite = function(string, offset) {
605   return this.write(string, offset, 'ascii');
606 };
607
608
609 /*
610  * Need to make sure that buffer isn't trying to write out of bounds.
611  * This check is far too slow internally for fast buffers.
612  */
613 function checkOffset(offset, ext, length) {
614   if ((offset % 1) !== 0 || offset < 0)
615     throw new RangeError('offset is not uint');
616   if (offset + ext > length)
617     throw new RangeError('Trying to access beyond buffer length');
618 }
619
620
621 Buffer.prototype.readUInt8 = function(offset, noAssert) {
622   if (!noAssert)
623     checkOffset(offset, 1, this.length);
624   return this[offset];
625 };
626
627
628 function readUInt16(buffer, offset, isBigEndian) {
629   var val = 0;
630   if (isBigEndian) {
631     val = buffer[offset] << 8;
632     val |= buffer[offset + 1];
633   } else {
634     val = buffer[offset];
635     val |= buffer[offset + 1] << 8;
636   }
637
638   return val;
639 }
640
641
642 Buffer.prototype.readUInt16LE = function(offset, noAssert) {
643   if (!noAssert)
644     checkOffset(offset, 2, this.length);
645   return readUInt16(this, offset, false, noAssert);
646 };
647
648
649 Buffer.prototype.readUInt16BE = function(offset, noAssert) {
650   if (!noAssert)
651     checkOffset(offset, 2, this.length);
652   return readUInt16(this, offset, true, noAssert);
653 };
654
655
656 function readUInt32(buffer, offset, isBigEndian, noAssert) {
657   var val = 0;
658
659   if (isBigEndian) {
660     val = buffer[offset + 1] << 16;
661     val |= buffer[offset + 2] << 8;
662     val |= buffer[offset + 3];
663     val = val + (buffer[offset] << 24 >>> 0);
664   } else {
665     val = buffer[offset + 2] << 16;
666     val |= buffer[offset + 1] << 8;
667     val |= buffer[offset];
668     val = val + (buffer[offset + 3] << 24 >>> 0);
669   }
670
671   return val;
672 }
673
674
675 Buffer.prototype.readUInt32LE = function(offset, noAssert) {
676   if (!noAssert)
677     checkOffset(offset, 4, this.length);
678   return readUInt32(this, offset, false, noAssert);
679 };
680
681
682 Buffer.prototype.readUInt32BE = function(offset, noAssert) {
683   if (!noAssert)
684     checkOffset(offset, 4, this.length);
685   return readUInt32(this, offset, true, noAssert);
686 };
687
688
689 /*
690  * Signed integer types, yay team! A reminder on how two's complement actually
691  * works. The first bit is the signed bit, i.e. tells us whether or not the
692  * number should be positive or negative. If the two's complement value is
693  * positive, then we're done, as it's equivalent to the unsigned representation.
694  *
695  * Now if the number is positive, you're pretty much done, you can just leverage
696  * the unsigned translations and return those. Unfortunately, negative numbers
697  * aren't quite that straightforward.
698  *
699  * At first glance, one might be inclined to use the traditional formula to
700  * translate binary numbers between the positive and negative values in two's
701  * complement. (Though it doesn't quite work for the most negative value)
702  * Mainly:
703  *  - invert all the bits
704  *  - add one to the result
705  *
706  * Of course, this doesn't quite work in Javascript. Take for example the value
707  * of -128. This could be represented in 16 bits (big-endian) as 0xff80. But of
708  * course, Javascript will do the following:
709  *
710  * > ~0xff80
711  * -65409
712  *
713  * Whoh there, Javascript, that's not quite right. But wait, according to
714  * Javascript that's perfectly correct. When Javascript ends up seeing the
715  * constant 0xff80, it has no notion that it is actually a signed number. It
716  * assumes that we've input the unsigned value 0xff80. Thus, when it does the
717  * binary negation, it casts it into a signed value, (positive 0xff80). Then
718  * when you perform binary negation on that, it turns it into a negative number.
719  *
720  * Instead, we're going to have to use the following general formula, that works
721  * in a rather Javascript friendly way. I'm glad we don't support this kind of
722  * weird numbering scheme in the kernel.
723  *
724  * (BIT-MAX - (unsigned)val + 1) * -1
725  *
726  * The astute observer, may think that this doesn't make sense for 8-bit numbers
727  * (really it isn't necessary for them). However, when you get 16-bit numbers,
728  * you do. Let's go back to our prior example and see how this will look:
729  *
730  * (0xffff - 0xff80 + 1) * -1
731  * (0x007f + 1) * -1
732  * (0x0080) * -1
733  */
734
735 Buffer.prototype.readInt8 = function(offset, noAssert) {
736   if (!noAssert)
737     checkOffset(offset, 1, this.length);
738   if (!(this[offset] & 0x80))
739     return (this[offset]);
740   return ((0xff - this[offset] + 1) * -1);
741 };
742
743
744 function readInt16(buffer, offset, isBigEndian) {
745   var val = readUInt16(buffer, offset, isBigEndian);
746
747   if (!(val & 0x8000))
748     return val;
749   return (0xffff - val + 1) * -1;
750 }
751
752
753 Buffer.prototype.readInt16LE = function(offset, noAssert) {
754   if (!noAssert)
755     checkOffset(offset, 2, this.length);
756   return readInt16(this, offset, false);
757 };
758
759
760 Buffer.prototype.readInt16BE = function(offset, noAssert) {
761   if (!noAssert)
762     checkOffset(offset, 2, this.length);
763   return readInt16(this, offset, true);
764 };
765
766
767 function readInt32(buffer, offset, isBigEndian) {
768   var val = readUInt32(buffer, offset, isBigEndian);
769
770   if (!(val & 0x80000000))
771     return (val);
772   return (0xffffffff - val + 1) * -1;
773 }
774
775
776 Buffer.prototype.readInt32LE = function(offset, noAssert) {
777   if (!noAssert)
778     checkOffset(offset, 2, this.length);
779   return readInt32(this, offset, false);
780 };
781
782
783 Buffer.prototype.readInt32BE = function(offset, noAssert) {
784   if (!noAssert)
785     checkOffset(offset, 2, this.length);
786   return readInt32(this, offset, true);
787 };
788
789 Buffer.prototype.readFloatLE = function(offset, noAssert) {
790   if (!noAssert)
791     checkOffset(offset, 4, this.length);
792   return this.parent.readFloatLE(this.offset + offset, !!noAssert);
793 };
794
795
796 Buffer.prototype.readFloatBE = function(offset, noAssert) {
797   if (!noAssert)
798     checkOffset(offset, 4, this.length);
799   return this.parent.readFloatBE(this.offset + offset, !!noAssert);
800 };
801
802
803 Buffer.prototype.readDoubleLE = function(offset, noAssert) {
804   if (!noAssert)
805     checkOffset(offset, 8, this.length);
806   return this.parent.readDoubleLE(this.offset + offset, !!noAssert);
807 };
808
809
810 Buffer.prototype.readDoubleBE = function(offset, noAssert) {
811   if (!noAssert)
812     checkOffset(offset, 8, this.length);
813   return this.parent.readDoubleBE(this.offset + offset, !!noAssert);
814 };
815
816
817 function checkInt(buffer, value, offset, ext, max, min) {
818   if ((value % 1) !== 0 || value > max || value < min)
819     throw TypeError('value is out of bounds');
820   if ((offset % 1) !== 0 || offset < 0)
821     throw TypeError('offset is not uint');
822   if (offset + ext > buffer.length || buffer.length + offset < 0)
823     throw RangeError('Trying to write outside buffer length');
824 }
825
826
827 Buffer.prototype.writeUInt8 = function(value, offset, noAssert) {
828   if (!noAssert)
829     checkInt(this, value, offset, 1, 0xff, 0);
830   this[offset] = value;
831 };
832
833
834 function writeUInt16(buffer, value, offset, isBigEndian) {
835   if (isBigEndian) {
836     buffer[offset] = (value & 0xff00) >>> 8;
837     buffer[offset + 1] = value & 0x00ff;
838   } else {
839     buffer[offset + 1] = (value & 0xff00) >>> 8;
840     buffer[offset] = value & 0x00ff;
841   }
842 }
843
844
845 Buffer.prototype.writeUInt16LE = function(value, offset, noAssert) {
846   if (!noAssert)
847     checkInt(this, value, offset, 2, 0xffff, 0);
848   writeUInt16(this, value, offset, false);
849 };
850
851
852 Buffer.prototype.writeUInt16BE = function(value, offset, noAssert) {
853   if (!noAssert)
854     checkInt(this, value, offset, 2, 0xffff, 0);
855   writeUInt16(this, value, offset, true);
856 };
857
858
859 function writeUInt32(buffer, value, offset, isBigEndian) {
860   if (isBigEndian) {
861     buffer[offset] = (value >>> 24) & 0xff;
862     buffer[offset + 1] = (value >>> 16) & 0xff;
863     buffer[offset + 2] = (value >>> 8) & 0xff;
864     buffer[offset + 3] = value & 0xff;
865   } else {
866     buffer[offset + 3] = (value >>> 24) & 0xff;
867     buffer[offset + 2] = (value >>> 16) & 0xff;
868     buffer[offset + 1] = (value >>> 8) & 0xff;
869     buffer[offset] = value & 0xff;
870   }
871 }
872
873
874 Buffer.prototype.writeUInt32LE = function(value, offset, noAssert) {
875   if (!noAssert)
876     checkInt(this, value, offset, 4, 0xffffffff, 0);
877   writeUInt32(this, value, offset, false);
878 };
879
880
881 Buffer.prototype.writeUInt32BE = function(value, offset, noAssert) {
882   if (!noAssert)
883     checkInt(this, value, offset, 4, 0xffffffff, 0);
884   writeUInt32(this, value, offset, true);
885 };
886
887
888 /*
889  * We now move onto our friends in the signed number category. Unlike unsigned
890  * numbers, we're going to have to worry a bit more about how we put values into
891  * arrays. Since we are only worrying about signed 32-bit values, we're in
892  * slightly better shape. Unfortunately, we really can't do our favorite binary
893  * & in this system. It really seems to do the wrong thing. For example:
894  *
895  * > -32 & 0xff
896  * 224
897  *
898  * What's happening above is really: 0xe0 & 0xff = 0xe0. However, the results of
899  * this aren't treated as a signed number. Ultimately a bad thing.
900  *
901  * What we're going to want to do is basically create the unsigned equivalent of
902  * our representation and pass that off to the wuint* functions. To do that
903  * we're going to do the following:
904  *
905  *  - if the value is positive
906  *      we can pass it directly off to the equivalent wuint
907  *  - if the value is negative
908  *      we do the following computation:
909  *         mb + val + 1, where
910  *         mb   is the maximum unsigned value in that byte size
911  *         val  is the Javascript negative integer
912  *
913  *
914  * As a concrete value, take -128. In signed 16 bits this would be 0xff80. If
915  * you do out the computations:
916  *
917  * 0xffff - 128 + 1
918  * 0xffff - 127
919  * 0xff80
920  *
921  * You can then encode this value as the signed version. This is really rather
922  * hacky, but it should work and get the job done which is our goal here.
923  */
924
925 Buffer.prototype.writeInt8 = function(value, offset, noAssert) {
926   if (!noAssert)
927     checkInt(this, value, offset, 1, 0x7f, -0x80);
928   if (value < 0) value = 0xff + value + 1;
929   this[offset] = value;
930 };
931
932
933 Buffer.prototype.writeInt16LE = function(value, offset, noAssert) {
934   if (!noAssert)
935     checkInt(this, value, offset, 2, 0x7fff, -0x8000);
936   if (value < 0) value = 0xffff + value + 1;
937   writeUInt16(this, value, offset, false);
938 };
939
940
941 Buffer.prototype.writeInt16BE = function(value, offset, noAssert) {
942   if (!noAssert)
943     checkInt(this, value, offset, 2, 0x7fff, -0x8000);
944   if (value < 0) value = 0xffff + value + 1;
945   writeUInt16(this, value, offset, true);
946 };
947
948
949 Buffer.prototype.writeInt32LE = function(value, offset, noAssert) {
950   if (!noAssert)
951     checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000);
952   if (value < 0) value = 0xffffffff + value + 1;
953   writeUInt32(this, value, offset, false);
954 };
955
956
957 Buffer.prototype.writeInt32BE = function(value, offset, noAssert) {
958   if (!noAssert)
959     checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000);
960   if (value < 0) value = 0xffffffff + value + 1;
961   writeUInt32(this, value, offset, true);
962 };
963
964
965 Buffer.prototype.writeFloatLE = function(value, offset, noAssert) {
966   if (!noAssert)
967     checkOffset(offset, 4, this.length);
968   this.parent.writeFloatLE(value, this.offset + offset, !!noAssert);
969 };
970
971
972 Buffer.prototype.writeFloatBE = function(value, offset, noAssert) {
973   if (!noAssert)
974     checkOffset(offset, 4, this.length);
975   this.parent.writeFloatBE(value, this.offset + offset, !!noAssert);
976 };
977
978
979 Buffer.prototype.writeDoubleLE = function(value, offset, noAssert) {
980   if (!noAssert)
981     checkOffset(offset, 8, this.length);
982   this.parent.writeDoubleLE(value, this.offset + offset, !!noAssert);
983 };
984
985
986 Buffer.prototype.writeDoubleBE = function(value, offset, noAssert) {
987   if (!noAssert)
988     checkOffset(offset, 8, this.length);
989   this.parent.writeDoubleBE(value, this.offset + offset, !!noAssert);
990 };