Add reading/writing of C integers to buffers
authorRobert Mustacchi <rm@joyent.com>
Sun, 1 May 2011 18:38:10 +0000 (11:38 -0700)
committerRyan Dahl <ry@tinyclouds.org>
Sun, 1 May 2011 21:02:33 +0000 (14:02 -0700)
doc/api/buffers.markdown
lib/buffer.js
test/simple/test-readint.js [new file with mode: 0644]
test/simple/test-readuint.js [new file with mode: 0644]
test/simple/test-writeint.js [new file with mode: 0644]
test/simple/test-writeuint.js [new file with mode: 0644]

index 88d59b2..68317af 100644 (file)
@@ -168,3 +168,198 @@ from the original Buffer.
 
     // abc
     // !bc
+
+### buffer.readUInt8(offset, endian)
+
+Reads an unsigned 8 bit integer from the buffer at the specified offset. Endian
+must be either 'big' or 'little' and specifies what endian ordering to read the
+bytes from the buffer in.
+
+Example:
+
+    var buf = new Buffer(4);
+
+    buf[0] = 0x3;
+    buf[1] = 0x4;
+    buf[2] = 0x23;
+    buf[3] = 0x42;
+
+    for (ii = 0; ii < buf.length; ii++) {
+      console.log(buf.readUInt8(ii, 'big');
+      console.log(buf.readUInt8(ii, 'little');
+    }
+
+    // 0x3
+    // 0x3
+    // 0x4
+    // 0x4
+    // 0x23
+    // 0x23
+    // 0x42
+    // 0x42
+
+### buffer.readUInt16(offset, endian)
+
+Reads an unsigned 16 bit integer from the buffer at the specified offset. Endian
+must be either 'big' or 'little' and specifies what endian ordering to read the
+bytes from the buffer in.
+
+Example:
+
+    var buf = new Buffer(4);
+
+    buf[0] = 0x3;
+    buf[1] = 0x4;
+    buf[2] = 0x23;
+    buf[3] = 0x42;
+
+    console.log(buf.readUInt16(0, 'big');
+    console.log(buf.readUInt16(0, 'little');
+    console.log(buf.readUInt16(1, 'big');
+    console.log(buf.readUInt16(1, 'little');
+    console.log(buf.readUInt16(2, 'big');
+    console.log(buf.readUInt16(2, 'little');
+
+    // 0x0304
+    // 0x0403
+    // 0x0423
+    // 0x2304
+    // 0x2342
+    // 0x4223
+
+### buffer.readUInt32(offset, endian)
+
+Reads an unsigned 32 bit integer from the buffer at the specified offset. Endian
+must be either 'big' or 'little' and specifies what endian ordering to read the
+bytes from the buffer in.
+
+Example:
+
+    var buf = new Buffer(4);
+
+    buf[0] = 0x3;
+    buf[1] = 0x4;
+    buf[2] = 0x23;
+    buf[3] = 0x42;
+
+    console.log(buf.readUInt32(0, 'big');
+    console.log(buf.readUInt32(0, 'little');
+
+    // 0x03042342
+    // 0x42230403
+
+### buffer.readInt8(offset, endian)
+
+Reads a signed 8 bit integer from the buffer at the specified offset. Endian
+must be either 'big' or 'little' and specifies what endian ordering to read the
+bytes from the buffer in.
+
+Works as `buffer.readUInt8`, except buffer contents are treated as twos
+complement signed values.
+
+### buffer.readInt16(offset, endian)
+
+Reads a signed 16 bit integer from the buffer at the specified offset. Endian
+must be either 'big' or 'little' and specifies what endian ordering to read the
+bytes from the buffer in.
+
+Works as `buffer.readUInt16`, except buffer contents are treated as twos
+complement signed values.
+
+### buffer.readInt32(offset, endian)
+
+Reads a signed 32 bit integer from the buffer at the specified offset. Endian
+must be either 'big' or 'little' and specifies what endian ordering to read the
+bytes from the buffer in.
+
+Works as `buffer.readUInt32`, except buffer contents are treated as twos
+complement signed values.
+
+### buffer.writeUInt8(value, offset, endian)
+
+Writes `value` to the buffer at the specified offset with specified endian
+format. Note, `value` must be a valid 8 bit unsigned integer.
+
+Example:
+
+    var buf = new Buffer(4);
+    buf.writeUInt8(0x3, 0, 'big');
+    buf.writeUInt8(0x4, 1, 'big');
+    buf.writeUInt8(0x23, 2, 'big');
+    buf.writeUInt8(0x42, 3, 'big');
+
+    console.log(buf);
+
+    buf.writeUInt8(0x3, 0, 'little');
+    buf.writeUInt8(0x4, 1, 'little');
+    buf.writeUInt8(0x23, 2, 'little');
+    buf.writeUInt8(0x42, 3, 'little');
+
+    console.log(buf);
+
+    // <Buffer 03 04 23 42>
+    // <Buffer 03 04 23 42>
+
+### buffer.writeUInt16(value, offset, endian)
+
+Writes `value` to the buffer at the specified offset with specified endian
+format. Note, `value` must be a valid 16 bit unsigned integer.
+
+Example:
+
+    var buf = new Buffer(4);
+    buf.writeUInt16(0xdead, 0, 'big');
+    buf.writeUInt16(0xbeef, 2, 'big');
+
+    console.log(buf);
+
+    buf.writeUInt16(0xdead, 0, 'little');
+    buf.writeUInt16(0xbeef, 2, 'little');
+
+    console.log(buf);
+
+    // <Buffer de ad be ef>
+    // <Buffer ad de ef be>
+
+### buffer.writeUInt32(value, offset, endian)
+
+Writes `value` to the buffer at the specified offset with specified endian
+format. Note, `value` must be a valid 32 bit unsigned integer.
+
+Example:
+
+    var buf = new Buffer(4);
+    buf.writeUInt32(0xfeedface, 0, 'big');
+
+    console.log(buf);
+
+    buf.writeUInt32(0xfeedface, 0, 'little');
+
+    console.log(buf);
+
+    // <Buffer fe ed fa ce>
+    // <Buffer ce fa ed fe>
+
+### buffer.writeInt8(value, offset, endian)
+
+Writes `value` to the buffer at the specified offset with specified endian
+format. Note, `value` must be a valid 16 bit signed integer.
+
+Works as `buffer.writeUInt8`, except value is written out as a two's complement
+signed integer into `buffer`.
+
+### buffer.writeInt16(value, offset, endian)
+
+Writes `value` to the buffer at the specified offset with specified endian
+format. Note, `value` must be a valid 16 bit unsigned integer.
+
+Works as `buffer.writeUInt16`, except value is written out as a two's complement
+signed integer into `buffer`.
+
+### buffer.writeInt32(value, offset, endian)
+
+Writes `value` to the buffer at the specified offset with specified endian
+format. Note, `value` must be a valid 16 bit signed integer.
+
+Works as `buffer.writeUInt832, except value is written out as a two's complement
+signed integer into `buffer`.
index 8ae841b..d0a7535 100644 (file)
@@ -20,6 +20,7 @@
 // USE OR OTHER DEALINGS IN THE SOFTWARE.
 
 var SlowBuffer = process.binding('buffer').SlowBuffer;
+var assert = require('assert');
 
 
 function toHex(n) {
@@ -45,7 +46,7 @@ SlowBuffer.prototype.hexSlice = function(start, end) {
   if (!end || end < 0 || end > len) end = len;
 
   var out = '';
-  for (var i = start; i < end; i ++) {
+  for (var i = start; i < end; i++) {
     out += toHex(this[i]);
   }
   return out;
@@ -98,13 +99,13 @@ SlowBuffer.prototype.hexWrite = function(string, offset) {
   if (len % 2) {
     throw new Error('Invalid hex string');
   }
-  for (var i = 0; i < len / 2; i ++) {
+  for (var i = 0; i < len / 2; i++) {
     var byte = parseInt(string.substr(i * 2, 2), 16);
     if (isNaN(byte)) throw new Error('Invalid hex string');
     this[offset + i] = byte;
   }
   return i;
-}
+};
 
 
 SlowBuffer.prototype.write = function(string, offset, encoding) {
@@ -449,3 +450,444 @@ Buffer.prototype.asciiWrite = function(string, offset) {
   return this.write(string, offset, 'ascii');
 };
 
+Buffer.prototype.readUInt8 = function(offset, endian) {
+  var buffer = this;
+
+  assert.ok(endian !== undefined && endian !== null,
+    'missing endian');
+
+  assert.ok(endian == 'big' || endian == 'little',
+    'bad endian value');
+
+  assert.ok(offset !== undefined && offset !== null,
+    'missing offset');
+
+  assert.ok(offset < buffer.length,
+    'Trying to read beyond buffer length');
+
+  return buffer[offset];
+};
+
+
+Buffer.prototype.readUInt16 = function(offset, endian) {
+  var val = 0;
+  var buffer = this;
+
+  assert.ok(endian !== undefined && endian !== null,
+    'missing endian');
+
+  assert.ok(endian == 'big' || endian == 'little',
+    'bad endian value');
+
+  assert.ok(offset !== undefined && offset !== null,
+    'missing offset');
+
+  assert.ok(offset + 1 < buffer.length,
+    'Trying to read beyond buffer length');
+
+  if (endian == 'big') {
+    val = buffer[offset] << 8;
+    val |= buffer[offset + 1];
+  } else {
+    val = buffer[offset];
+    val |= buffer[offset + 1] << 8;
+  }
+
+  return val;
+};
+
+
+Buffer.prototype.readUInt32 = function(offset, endian) {
+  var val = 0;
+  var buffer = this;
+
+  assert.ok(endian !== undefined && endian !== null,
+    'missing endian');
+
+  assert.ok(endian == 'big' || endian == 'little',
+    'bad endian value');
+
+  assert.ok(offset !== undefined && offset !== null,
+    'missing offset');
+
+  assert.ok(offset + 3 < buffer.length,
+    'Trying to read beyond buffer length');
+
+  if (endian == 'big') {
+    val = buffer[offset + 1] << 16;
+    val |= buffer[offset + 2] << 8;
+    val |= buffer[offset + 3];
+    val = val + (buffer[offset] << 24 >>> 0);
+  } else {
+    val = buffer[offset + 2] << 16;
+    val |= buffer[offset + 1] << 8;
+    val |= buffer[offset];
+    val = val + (buffer[offset + 3] << 24 >>> 0);
+  }
+
+  return val;
+};
+
+
+/*
+ * Signed integer types, yay team! A reminder on how two's complement actually
+ * works. The first bit is the signed bit, i.e. tells us whether or not the
+ * number should be positive or negative. If the two's complement value is
+ * positive, then we're done, as it's equivalent to the unsigned representation.
+ *
+ * Now if the number is positive, you're pretty much done, you can just leverage
+ * the unsigned translations and return those. Unfortunately, negative numbers
+ * aren't quite that straightforward.
+ *
+ * At first glance, one might be inclined to use the traditional formula to
+ * translate binary numbers between the positive and negative values in two's
+ * complement. (Though it doesn't quite work for the most negative value)
+ * Mainly:
+ *  - invert all the bits
+ *  - add one to the result
+ *
+ * Of course, this doesn't quite work in Javascript. Take for example the value
+ * of -128. This could be represented in 16 bits (big-endian) as 0xff80. But of
+ * course, Javascript will do the following:
+ *
+ * > ~0xff80
+ * -65409
+ *
+ * Whoh there, Javascript, that's not quite right. But wait, according to
+ * Javascript that's perfectly correct. When Javascript ends up seeing the
+ * constant 0xff80, it has no notion that it is actually a signed number. It
+ * assumes that we've input the unsigned value 0xff80. Thus, when it does the
+ * binary negation, it casts it into a signed value, (positive 0xff80). Then
+ * when you perform binary negation on that, it turns it into a negative number.
+ *
+ * Instead, we're going to have to use the following general formula, that works
+ * in a rather Javascript friendly way. I'm glad we don't support this kind of
+ * weird numbering scheme in the kernel.
+ *
+ * (BIT-MAX - (unsigned)val + 1) * -1
+ *
+ * The astute observer, may think that this doesn't make sense for 8-bit numbers
+ * (really it isn't necessary for them). However, when you get 16-bit numbers,
+ * you do. Let's go back to our prior example and see how this will look:
+ *
+ * (0xffff - 0xff80 + 1) * -1
+ * (0x007f + 1) * -1
+ * (0x0080) * -1
+ */
+Buffer.prototype.readInt8 = function(offset, endian) {
+  var buffer = this;
+  var neg;
+
+  assert.ok(endian !== undefined && endian !== null,
+    'missing endian');
+
+  assert.ok(endian == 'big' || endian == 'little',
+    'bad endian value');
+
+  assert.ok(offset !== undefined && offset !== null,
+    'missing offset');
+
+  assert.ok(offset < buffer.length,
+    'Trying to read beyond buffer length');
+
+  neg = buffer[offset] & 0x80;
+  if (!neg) {
+    return (buffer[offset]);
+  }
+
+  return ((0xff - buffer[offset] + 1) * -1);
+};
+
+
+Buffer.prototype.readInt16 = function(offset, endian) {
+  var buffer = this;
+  var neg;
+
+  assert.ok(endian !== undefined && endian !== null,
+    'missing endian');
+
+  assert.ok(endian == 'big' || endian == 'little',
+    'bad endian value');
+
+  assert.ok(offset !== undefined && offset !== null,
+    'missing offset');
+
+  assert.ok(offset + 1 < buffer.length,
+    'Trying to read beyond buffer length');
+
+  val = buffer.readUInt16(offset, endian);
+  neg = val & 0x8000;
+  if (!neg) {
+    return val;
+  }
+
+  return (0xffff - val + 1) * -1;
+};
+
+
+Buffer.prototype.readInt32 = function(offset, endian) {
+  var buffer = this;
+  var neg;
+
+  assert.ok(endian !== undefined && endian !== null,
+    'missing endian');
+
+  assert.ok(endian == 'big' || endian == 'little',
+    'bad endian value');
+
+  assert.ok(offset !== undefined && offset !== null,
+    'missing offset');
+
+  assert.ok(offset + 3 < buffer.length,
+    'Trying to read beyond buffer length');
+
+  val = buffer.readUInt32(offset, endian);
+  neg = val & 0x80000000;
+  if (!neg) {
+    return (val);
+  }
+
+  return (0xffffffff - val + 1) * -1;
+};
+
+
+/*
+ * We have to make sure that the value is a valid integer. This means that it is
+ * non-negative. It has no fractional component and that it does not exceed the
+ * maximum allowed value.
+ *
+ *      value           The number to check for validity
+ *
+ *      max             The maximum value
+ */
+function verifuint(value, max) {
+  assert.ok(typeof (value) == 'number',
+    'cannot write a non-number as a number');
+
+  assert.ok(value >= 0,
+    'specified a negative value for writing an unsigned value');
+
+  assert.ok(value <= max, 'value is larger than maximum value for type');
+
+  assert.ok(Math.floor(value) === value, 'value has a fractional component');
+}
+
+
+Buffer.prototype.writeUInt8 = function(value, offset, endian) {
+  var buffer = this;
+
+  assert.ok(value !== undefined && value !== null,
+    'missing value');
+
+  assert.ok(endian !== undefined && endian !== null,
+    'missing endian');
+
+  assert.ok(endian == 'big' || endian == 'little',
+    'bad endian value');
+
+  assert.ok(offset !== undefined && offset !== null,
+    'missing offset');
+
+  assert.ok(offset < buffer.length,
+    'trying to read beyond buffer length');
+
+  verifuint(value, 0xff);
+  buffer[offset] = value;
+};
+
+
+Buffer.prototype.writeUInt16 = function(value, offset, endian) {
+  var buffer = this;
+
+  assert.ok(value !== undefined && value !== null,
+    'missing value');
+
+  assert.ok(endian !== undefined && endian !== null,
+    'missing endian');
+
+  assert.ok(endian == 'big' || endian == 'little',
+    'bad endian value');
+
+  assert.ok(offset !== undefined && offset !== null,
+    'missing offset');
+
+  assert.ok(offset + 1 < buffer.length,
+    'trying to read beyond buffer length');
+
+  verifuint(value, 0xffff);
+
+  if (endian == 'big') {
+    buffer[offset] = (value & 0xff00) >>> 8;
+    buffer[offset + 1] = value & 0x00ff;
+  } else {
+    buffer[offset + 1] = (value & 0xff00) >>> 8;
+    buffer[offset] = value & 0x00ff;
+  }
+};
+
+
+Buffer.prototype.writeUInt32 = function(value, offset, endian) {
+  var buffer = this;
+
+  assert.ok(value !== undefined && value !== null,
+    'missing value');
+
+  assert.ok(endian !== undefined && endian !== null,
+    'missing endian');
+
+  assert.ok(endian == 'big' || endian == 'little',
+    'bad endian value');
+
+  assert.ok(offset !== undefined && offset !== null,
+    'missing offset');
+
+  assert.ok(offset + 3 < buffer.length,
+    'trying to read beyond buffer length');
+
+  verifuint(value, 0xffffffff);
+  if (endian == 'big') {
+    buffer[offset] = (value >>> 24) & 0xff;
+    buffer[offset + 1] = (value >>> 16) & 0xff;
+    buffer[offset + 2] = (value >>> 8) & 0xff;
+    buffer[offset + 3] = value & 0xff;
+  } else {
+    buffer[offset + 3] = (value >>> 24) & 0xff;
+    buffer[offset + 2] = (value >>> 16) & 0xff;
+    buffer[offset + 1] = (value >>> 8) & 0xff;
+    buffer[offset] = value & 0xff;
+  }
+};
+
+
+/*
+ * We now move onto our friends in the signed number category. Unlike unsigned
+ * numbers, we're going to have to worry a bit more about how we put values into
+ * arrays. Since we are only worrying about signed 32-bit values, we're in
+ * slightly better shape. Unfortunately, we really can't do our favorite binary
+ * & in this system. It really seems to do the wrong thing. For example:
+ *
+ * > -32 & 0xff
+ * 224
+ *
+ * What's happening above is really: 0xe0 & 0xff = 0xe0. However, the results of
+ * this aren't treated as a signed number. Ultimately a bad thing.
+ *
+ * What we're going to want to do is basically create the unsigned equivalent of
+ * our representation and pass that off to the wuint* functions. To do that
+ * we're going to do the following:
+ *
+ *  - if the value is positive
+ *      we can pass it directly off to the equivalent wuint
+ *  - if the value is negative
+ *      we do the following computation:
+ *         mb + val + 1, where
+ *         mb   is the maximum unsigned value in that byte size
+ *         val  is the Javascript negative integer
+ *
+ *
+ * As a concrete value, take -128. In signed 16 bits this would be 0xff80. If
+ * you do out the computations:
+ *
+ * 0xffff - 128 + 1
+ * 0xffff - 127
+ * 0xff80
+ *
+ * You can then encode this value as the signed version. This is really rather
+ * hacky, but it should work and get the job done which is our goal here.
+ */
+
+/*
+ * A series of checks to make sure we actually have a signed 32-bit number
+ */
+function verifsint(value, max, min) {
+  assert.ok(typeof (value) == 'number',
+    'cannot write a non-number as a number');
+
+  assert.ok(value <= max, 'value larger than maximum allowed value');
+
+  assert.ok(value >= min, 'value smaller than minimum allowed value');
+
+  assert.ok(Math.floor(value) === value, 'value has a fractional component');
+}
+
+Buffer.prototype.writeInt8 = function(value, offset, endian) {
+  var buffer = this;
+
+  assert.ok(value !== undefined && value !== null,
+    'missing value');
+
+  assert.ok(endian !== undefined && endian !== null,
+    'missing endian');
+
+  assert.ok(endian == 'big' || endian == 'little',
+    'bad endian value');
+
+  assert.ok(offset !== undefined && offset !== null,
+    'missing offset');
+
+  assert.ok(offset < buffer.length,
+    'Trying to read beyond buffer length');
+
+  verifsint(value, 0x7f, -0xf0);
+
+  if (value >= 0) {
+    buffer.writeUInt8(value, offset, endian);
+  } else {
+    buffer.writeUInt8(0xff + value + 1, offset, endian);
+  }
+};
+
+
+Buffer.prototype.writeInt16 = function(value, offset, endian) {
+  var buffer = this;
+
+  assert.ok(value !== undefined && value !== null,
+    'missing value');
+
+  assert.ok(endian !== undefined && endian !== null,
+    'missing endian');
+
+  assert.ok(endian == 'big' || endian == 'little',
+    'bad endian value');
+
+  assert.ok(offset !== undefined && offset !== null,
+    'missing offset');
+
+  assert.ok(offset + 1 < buffer.length,
+    'Trying to read beyond buffer length');
+
+  verifsint(value, 0x7fff, -0xf000);
+
+  if (value >= 0) {
+    buffer.writeUInt16(value, offset, endian);
+  } else {
+    buffer.writeUInt16(0xffff + value + 1, offset, endian);
+  }
+};
+
+
+Buffer.prototype.writeInt32 = function(value, offset, endian) {
+  var buffer = this;
+
+  assert.ok(value !== undefined && value !== null,
+    'missing value');
+
+  assert.ok(endian !== undefined && endian !== null,
+    'missing endian');
+
+  assert.ok(endian == 'big' || endian == 'little',
+    'bad endian value');
+
+  assert.ok(offset !== undefined && offset !== null,
+    'missing offset');
+
+  assert.ok(offset + 3 < buffer.length,
+    'Trying to read beyond buffer length');
+
+  verifsint(value, 0x7fffffff, -0xf0000000);
+  if (value >= 0) {
+    buffer.writeUInt32(value, offset, endian);
+  } else {
+    buffer.writeUInt32(0xffffffff + value + 1, offset, endian);
+  }
+};
diff --git a/test/simple/test-readint.js b/test/simple/test-readint.js
new file mode 100644 (file)
index 0000000..d2d4fea
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * Tests to verify we're reading in signed integers correctly
+ */
+var ASSERT = require('assert');
+
+/*
+ * Test 8 bit signed integers
+ */
+function test8() {
+  var data = new Buffer(4);
+
+  data[0] = 0x23;
+  ASSERT.equal(0x23, data.readInt8(0, 'big'));
+  ASSERT.equal(0x23, data.readInt8(0, 'little'));
+
+  data[0] = 0xff;
+  ASSERT.equal(-1, data.readInt8(0, 'big'));
+  ASSERT.equal(-1, data.readInt8(0, 'little'));
+
+  data[0] = 0x87;
+  data[1] = 0xab;
+  data[2] = 0x7c;
+  data[3] = 0xef;
+  ASSERT.equal(-121, data.readInt8(0, 'big'));
+  ASSERT.equal(-85, data.readInt8(1, 'big'));
+  ASSERT.equal(124, data.readInt8(2, 'big'));
+  ASSERT.equal(-17, data.readInt8(3, 'big'));
+  ASSERT.equal(-121, data.readInt8(0, 'little'));
+  ASSERT.equal(-85, data.readInt8(1, 'little'));
+  ASSERT.equal(124, data.readInt8(2, 'little'));
+  ASSERT.equal(-17, data.readInt8(3, 'little'));
+}
+
+
+function test16() {
+  var buffer = new Buffer(6);
+  buffer[0] = 0x16;
+  buffer[1] = 0x79;
+  ASSERT.equal(0x1679, buffer.readInt16(0, 'big'));
+  ASSERT.equal(0x7916, buffer.readInt16(0, 'little'));
+
+  buffer[0] = 0xff;
+  buffer[1] = 0x80;
+  ASSERT.equal(-128, buffer.readInt16(0, 'big'));
+  ASSERT.equal(-32513, buffer.readInt16(0, 'little'));
+
+  /* test offset with weenix */
+  buffer[0] = 0x77;
+  buffer[1] = 0x65;
+  buffer[2] = 0x65;
+  buffer[3] = 0x6e;
+  buffer[4] = 0x69;
+  buffer[5] = 0x78;
+  ASSERT.equal(0x7765, buffer.readInt16(0, 'big'));
+  ASSERT.equal(0x6565, buffer.readInt16(1, 'big'));
+  ASSERT.equal(0x656e, buffer.readInt16(2, 'big'));
+  ASSERT.equal(0x6e69, buffer.readInt16(3, 'big'));
+  ASSERT.equal(0x6978, buffer.readInt16(4, 'big'));
+  ASSERT.equal(0x6577, buffer.readInt16(0, 'little'));
+  ASSERT.equal(0x6565, buffer.readInt16(1, 'little'));
+  ASSERT.equal(0x6e65, buffer.readInt16(2, 'little'));
+  ASSERT.equal(0x696e, buffer.readInt16(3, 'little'));
+  ASSERT.equal(0x7869, buffer.readInt16(4, 'little'));
+}
+
+
+function test32() {
+  var buffer = new Buffer(6);
+  buffer[0] = 0x43;
+  buffer[1] = 0x53;
+  buffer[2] = 0x16;
+  buffer[3] = 0x79;
+  ASSERT.equal(0x43531679, buffer.readInt32(0, 'big'));
+  ASSERT.equal(0x79165343, buffer.readInt32(0, 'little'));
+
+  buffer[0] = 0xff;
+  buffer[1] = 0xfe;
+  buffer[2] = 0xef;
+  buffer[3] = 0xfa;
+  ASSERT.equal(-69638, buffer.readInt32(0, 'big'));
+  ASSERT.equal(-84934913, buffer.readInt32(0, 'little'));
+
+  buffer[0] = 0x42;
+  buffer[1] = 0xc3;
+  buffer[2] = 0x95;
+  buffer[3] = 0xa9;
+  buffer[4] = 0x36;
+  buffer[5] = 0x17;
+  ASSERT.equal(0x42c395a9, buffer.readInt32(0, 'big'));
+  ASSERT.equal(-1013601994, buffer.readInt32(1, 'big'));
+  ASSERT.equal(-1784072681, buffer.readInt32(2, 'big'));
+  ASSERT.equal(-1449802942, buffer.readInt32(0, 'little'));
+  ASSERT.equal(917083587, buffer.readInt32(1, 'little'));
+  ASSERT.equal(389458325, buffer.readInt32(2, 'little'));
+}
+
+
+test8();
+test16();
+test32();
diff --git a/test/simple/test-readuint.js b/test/simple/test-readuint.js
new file mode 100644 (file)
index 0000000..98648cc
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * A battery of tests to help us read a series of uints
+ */
+
+var ASSERT = require('assert');
+
+/*
+ * We need to check the following things:
+ *  - We are correctly resolving big endian (doesn't mean anything for 8 bit)
+ *  - Correctly resolving little endian (doesn't mean anything for 8 bit)
+ *  - Correctly using the offsets
+ *  - Correctly interpreting values that are beyond the signed range as unsigned
+ */
+function test8() {
+  var data = new Buffer(4);
+  data[0] = 23;
+  data[1] = 23;
+  data[2] = 23;
+  data[3] = 23;
+  ASSERT.equal(23, data.readUInt8(0, 'big'));
+  ASSERT.equal(23, data.readUInt8(0, 'little'));
+  ASSERT.equal(23, data.readUInt8(1, 'big'));
+  ASSERT.equal(23, data.readUInt8(1, 'little'));
+  ASSERT.equal(23, data.readUInt8(2, 'big'));
+  ASSERT.equal(23, data.readUInt8(2, 'little'));
+  ASSERT.equal(23, data.readUInt8(3, 'big'));
+  ASSERT.equal(23, data.readUInt8(3, 'little'));
+  data[0] = 255; /* If it became a signed int, would be -1 */
+  ASSERT.equal(255, data.readUInt8(0, 'big'));
+  ASSERT.equal(255, data.readUInt8(0, 'little'));
+}
+
+
+/*
+ * Test 16 bit unsigned integers. We need to verify the same set as 8 bit, only
+ * now some of the issues actually matter:
+ *  - We are correctly resolving big endian
+ *  - Correctly resolving little endian
+ *  - Correctly using the offsets
+ *  - Correctly interpreting values that are beyond the signed range as unsigned
+ */
+function test16() {
+  var data = new Buffer(4);
+
+  data[0] = 0;
+  data[1] = 0x23;
+  data[2] = 0x42;
+  data[3] = 0x3f;
+
+  ASSERT.equal(0x23, data.readUInt16(0, 'big'));
+  ASSERT.equal(0x2342, data.readUInt16(1, 'big'));
+  ASSERT.equal(0x423f, data.readUInt16(2, 'big'));
+
+  ASSERT.equal(0x2300, data.readUInt16(0, 'little'));
+  ASSERT.equal(0x4223, data.readUInt16(1, 'little'));
+  ASSERT.equal(0x3f42, data.readUInt16(2, 'little'));
+
+  data[0] = 0xfe;
+  data[1] = 0xfe;
+
+  ASSERT.equal(0xfefe, data.readUInt16(0, 'big'));
+  ASSERT.equal(0xfefe, data.readUInt16(0, 'little'));
+}
+
+
+/*
+ * Test 32 bit unsigned integers. We need to verify the same set as 8 bit, only
+ * now some of the issues actually matter:
+ *  - We are correctly resolving big endian
+ *  - Correctly using the offsets
+ *  - Correctly interpreting values that are beyond the signed range as unsigned
+ */
+function test32() {
+  var data = new Buffer(8);
+  data[0] = 0x32;
+  data[1] = 0x65;
+  data[2] = 0x42;
+  data[3] = 0x56;
+  data[4] = 0x23;
+  data[5] = 0xff;
+
+  ASSERT.equal(0x32654256, data.readUInt32(0, 'big'));
+  ASSERT.equal(0x65425623, data.readUInt32(1, 'big'));
+  ASSERT.equal(0x425623ff, data.readUInt32(2, 'big'));
+
+  ASSERT.equal(0x56426532, data.readUInt32(0, 'little'));
+  ASSERT.equal(0x23564265, data.readUInt32(1, 'little'));
+  ASSERT.equal(0xff235642, data.readUInt32(2, 'little'));
+}
+
+
+test8();
+test16();
+test32();
diff --git a/test/simple/test-writeint.js b/test/simple/test-writeint.js
new file mode 100644 (file)
index 0000000..8752111
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * Tests to verify we're writing signed integers correctly
+ */
+var ASSERT = require('assert');
+
+function test8() {
+  var buffer = new Buffer(4);
+  buffer.writeInt8(0x23, 0, 'big');
+  buffer.writeInt8(0x23, 1, 'little');
+  buffer.writeInt8(-5, 2, 'big');
+  buffer.writeInt8(-5, 3, 'little');
+
+  ASSERT.equal(0x23, buffer[0]);
+  ASSERT.equal(0x23, buffer[1]);
+  ASSERT.equal(0xfb, buffer[2]);
+  ASSERT.equal(0xfb, buffer[3]);
+
+  /* Make sure we handle truncation correctly */
+  ASSERT.throws(function() {
+    buffer.writeInt8(0xabc, 0, 'big');
+  });
+  ASSERT.throws(function() {
+    buffer.writeInt8(0xabc, 0, 'little');
+  });
+}
+
+
+function test16() {
+  var buffer = new Buffer(6);
+  buffer.writeInt16(0x0023, 0, 'big');
+  buffer.writeInt16(0x0023, 2, 'little');
+  ASSERT.equal(0x00, buffer[0]);
+  ASSERT.equal(0x23, buffer[1]);
+  ASSERT.equal(0x23, buffer[2]);
+  ASSERT.equal(0x00, buffer[3]);
+  buffer.writeInt16(-5, 0, 'big');
+  buffer.writeInt16(-5, 2, 'little');
+  ASSERT.equal(0xff, buffer[0]);
+  ASSERT.equal(0xfb, buffer[1]);
+  ASSERT.equal(0xfb, buffer[2]);
+  ASSERT.equal(0xff, buffer[3]);
+
+  buffer.writeInt16(-1679, 1, 'big');
+  buffer.writeInt16(-1679, 3, 'little');
+  ASSERT.equal(0xf9, buffer[1]);
+  ASSERT.equal(0x71, buffer[2]);
+  ASSERT.equal(0x71, buffer[3]);
+  ASSERT.equal(0xf9, buffer[4]);
+}
+
+
+function test32() {
+  var buffer = new Buffer(8);
+  buffer.writeInt32(0x23, 0, 'big');
+  buffer.writeInt32(0x23, 4, 'little');
+  ASSERT.equal(0x00, buffer[0]);
+  ASSERT.equal(0x00, buffer[1]);
+  ASSERT.equal(0x00, buffer[2]);
+  ASSERT.equal(0x23, buffer[3]);
+  ASSERT.equal(0x23, buffer[4]);
+  ASSERT.equal(0x00, buffer[5]);
+  ASSERT.equal(0x00, buffer[6]);
+  ASSERT.equal(0x00, buffer[7]);
+
+  buffer.writeInt32(-5, 0, 'big');
+  buffer.writeInt32(-5, 4, 'little');
+  ASSERT.equal(0xff, buffer[0]);
+  ASSERT.equal(0xff, buffer[1]);
+  ASSERT.equal(0xff, buffer[2]);
+  ASSERT.equal(0xfb, buffer[3]);
+  ASSERT.equal(0xfb, buffer[4]);
+  ASSERT.equal(0xff, buffer[5]);
+  ASSERT.equal(0xff, buffer[6]);
+  ASSERT.equal(0xff, buffer[7]);
+
+  buffer.writeInt32(-805306713, 0, 'big');
+  buffer.writeInt32(-805306713, 4, 'little');
+  ASSERT.equal(0xcf, buffer[0]);
+  ASSERT.equal(0xff, buffer[1]);
+  ASSERT.equal(0xfe, buffer[2]);
+  ASSERT.equal(0xa7, buffer[3]);
+  ASSERT.equal(0xa7, buffer[4]);
+  ASSERT.equal(0xfe, buffer[5]);
+  ASSERT.equal(0xff, buffer[6]);
+  ASSERT.equal(0xcf, buffer[7]);
+}
+
+
+test8();
+test16();
+test32();
diff --git a/test/simple/test-writeuint.js b/test/simple/test-writeuint.js
new file mode 100644 (file)
index 0000000..7306fe9
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * A battery of tests to help us read a series of uints
+ */
+var ASSERT = require('assert');
+
+/*
+ * We need to check the following things:
+ *  - We are correctly resolving big endian (doesn't mean anything for 8 bit)
+ *  - Correctly resolving little endian (doesn't mean anything for 8 bit)
+ *  - Correctly using the offsets
+ *  - Correctly interpreting values that are beyond the signed range as unsigned
+ */
+function test8() {
+  var data = new Buffer(4);
+  data.writeUInt8(23, 0, 'big');
+  data.writeUInt8(23, 1, 'big');
+  data.writeUInt8(23, 2, 'big');
+  data.writeUInt8(23, 3, 'big');
+  ASSERT.equal(23, data[0]);
+  ASSERT.equal(23, data[1]);
+  ASSERT.equal(23, data[2]);
+  ASSERT.equal(23, data[3]);
+  data.writeUInt8(23, 0, 'little');
+  data.writeUInt8(23, 1, 'little');
+  data.writeUInt8(23, 2, 'little');
+  data.writeUInt8(23, 3, 'little');
+  ASSERT.equal(23, data[0]);
+  ASSERT.equal(23, data[1]);
+  ASSERT.equal(23, data[2]);
+  ASSERT.equal(23, data[3]);
+  data.writeUInt8(255, 0, 'big');
+  ASSERT.equal(255, data[0]);
+  data.writeUInt8(255, 0, 'little');
+  ASSERT.equal(255, data[0]);
+}
+
+
+function test16() {
+  var value = 0x2343;
+  var data = new Buffer(4);
+  data.writeUInt16(value, 0, 'big');
+  ASSERT.equal(0x23, data[0]);
+  ASSERT.equal(0x43, data[1]);
+  data.writeUInt16(value, 1, 'big');
+  ASSERT.equal(0x23, data[1]);
+  ASSERT.equal(0x43, data[2]);
+  data.writeUInt16(value, 2, 'big');
+  ASSERT.equal(0x23, data[2]);
+  ASSERT.equal(0x43, data[3]);
+
+  data.writeUInt16(value, 0, 'little');
+  ASSERT.equal(0x23, data[1]);
+  ASSERT.equal(0x43, data[0]);
+
+  data.writeUInt16(value, 1, 'little');
+  ASSERT.equal(0x23, data[2]);
+  ASSERT.equal(0x43, data[1]);
+
+  data.writeUInt16(value, 2, 'little');
+  ASSERT.equal(0x23, data[3]);
+  ASSERT.equal(0x43, data[2]);
+
+  value = 0xff80;
+  data.writeUInt16(value, 0, 'little');
+  ASSERT.equal(0xff, data[1]);
+  ASSERT.equal(0x80, data[0]);
+
+  data.writeUInt16(value, 0, 'big');
+  ASSERT.equal(0xff, data[0]);
+  ASSERT.equal(0x80, data[1]);
+}
+
+
+function test32() {
+  var data = new Buffer(6);
+  var value = 0xe7f90a6d;
+
+  data.writeUInt32(value, 0, 'big');
+  ASSERT.equal(0xe7, data[0]);
+  ASSERT.equal(0xf9, data[1]);
+  ASSERT.equal(0x0a, data[2]);
+  ASSERT.equal(0x6d, data[3]);
+
+  data.writeUInt32(value, 1, 'big');
+  ASSERT.equal(0xe7, data[1]);
+  ASSERT.equal(0xf9, data[2]);
+  ASSERT.equal(0x0a, data[3]);
+  ASSERT.equal(0x6d, data[4]);
+
+  data.writeUInt32(value, 2, 'big');
+  ASSERT.equal(0xe7, data[2]);
+  ASSERT.equal(0xf9, data[3]);
+  ASSERT.equal(0x0a, data[4]);
+  ASSERT.equal(0x6d, data[5]);
+
+  data.writeUInt32(value, 0, 'little');
+  ASSERT.equal(0xe7, data[3]);
+  ASSERT.equal(0xf9, data[2]);
+  ASSERT.equal(0x0a, data[1]);
+  ASSERT.equal(0x6d, data[0]);
+
+  data.writeUInt32(value, 1, 'little');
+  ASSERT.equal(0xe7, data[4]);
+  ASSERT.equal(0xf9, data[3]);
+  ASSERT.equal(0x0a, data[2]);
+  ASSERT.equal(0x6d, data[1]);
+
+  data.writeUInt32(value, 2, 'little');
+  ASSERT.equal(0xe7, data[5]);
+  ASSERT.equal(0xf9, data[4]);
+  ASSERT.equal(0x0a, data[3]);
+  ASSERT.equal(0x6d, data[2]);
+}
+
+
+test8();
+test16();
+test32();