From 9812e31e8ba43be9c50d07f83cbf459427a5d877 Mon Sep 17 00:00:00 2001 From: Robert Mustacchi Date: Sun, 1 May 2011 11:38:10 -0700 Subject: [PATCH] Add reading/writing of C integers to buffers --- doc/api/buffers.markdown | 195 +++++++++++++++ lib/buffer.js | 448 +++++++++++++++++++++++++++++++++- test/simple/test-readint.js | 100 ++++++++ test/simple/test-readuint.js | 94 +++++++ test/simple/test-writeint.js | 91 +++++++ test/simple/test-writeuint.js | 118 +++++++++ 6 files changed, 1043 insertions(+), 3 deletions(-) create mode 100644 test/simple/test-readint.js create mode 100644 test/simple/test-readuint.js create mode 100644 test/simple/test-writeint.js create mode 100644 test/simple/test-writeuint.js diff --git a/doc/api/buffers.markdown b/doc/api/buffers.markdown index 88d59b20fbe..68317aff03b 100644 --- a/doc/api/buffers.markdown +++ b/doc/api/buffers.markdown @@ -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.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.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.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`. diff --git a/lib/buffer.js b/lib/buffer.js index 8ae841b5e72..d0a7535455c 100644 --- a/lib/buffer.js +++ b/lib/buffer.js @@ -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 index 00000000000..d2d4feafd84 --- /dev/null +++ b/test/simple/test-readint.js @@ -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 index 00000000000..98648cc936b --- /dev/null +++ b/test/simple/test-readuint.js @@ -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 index 00000000000..87521110310 --- /dev/null +++ b/test/simple/test-writeint.js @@ -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 index 00000000000..7306fe931d8 --- /dev/null +++ b/test/simple/test-writeuint.js @@ -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();