From b255eaadd178716b69627034e3923df200d576b5 Mon Sep 17 00:00:00 2001 From: roblabla Date: Wed, 9 Sep 2015 22:38:03 +0000 Subject: [PATCH] Implement bitfields. Add tests for utils datatypes --- package.json | 4 +- src/datatypes/utils.js | 67 +++++++++++++ test/dataTypes/utils.js | 205 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 275 insertions(+), 1 deletion(-) create mode 100644 test/dataTypes/utils.js diff --git a/package.json b/package.json index 67d8c43..7c71fea 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ }, "scripts": { "prepublish": "gulp", - "test": "mocha --recursive --require source-map-support/register --reporter spec" + "test": "mocha --recursive --require intelli-espower-loader --require source-map-support/register --reporter spec" }, "keywords": [ "minecraft", @@ -34,8 +34,10 @@ "gulp-babel": "^5.1.0", "gulp-plumber": "^1.0.1", "gulp-sourcemaps": "^1.3.0", + "intelli-espower-loader": "^1.0.0", "mkdirp": "~0.3.4", "mocha": "~1.8.2", + "power-assert": "^1.0.0", "rimraf": "~2.1.1", "source-map-support": "^0.3.2", "zfill": "0.0.1" diff --git a/src/datatypes/utils.js b/src/datatypes/utils.js index d7c8097..d6ea8fe 100644 --- a/src/datatypes/utils.js +++ b/src/datatypes/utils.js @@ -8,6 +8,7 @@ module.exports = { 'string': [readString, writeString, sizeOfString], 'buffer': [readBuffer, writeBuffer, sizeOfBuffer], 'void': [readVoid, writeVoid, 0], + 'bitfield': [readBitField, writeBitField, sizeOfBitField] }; function readVarInt(buffer, offset) { @@ -143,3 +144,69 @@ function readVoid() { function writeVoid(value, buffer, offset) { return offset; } + +function generateBitMask(n) { + return (1 << n) - 1; +} + +function readBitField(buffer, offset, typeArgs, context) { + var beginOffset = offset; + var curVal = null; + var bits = 0; + var results = {}; + results.value = typeArgs.reduce(function(acc, item) { + var size = item.size; + var val = 0; + while (size > 0) { + if (bits == 0) { + curVal = buffer[offset++]; + bits = 8; + } + var bitsToRead = Math.min(size, bits); + val = (val << bitsToRead) | (curVal & generateBitMask(bits)) >> (bits - bitsToRead); + bits -= bitsToRead; + size -= bitsToRead; + } + if (item.signed && val >= 1 << (item.size - 1)) + val -= 1 << item.size; + acc[item.name] = val; + return acc; + }, {}); + results.size = offset - beginOffset; + return results; +} +function writeBitField(value, buffer, offset, typeArgs, context) { + var toWrite = 0; + var bits = 0; + typeArgs.forEach(function(item) { + var val = value[item.name]; + var size = item.size; + var signed = item.signed; + if ((!item.signed && val < 0) || (item.signed && val < -(1 << (size - 1)))) + throw new Error(value + " < " + item.signed ? (-(1 << (size - 1))) : 0); + else if ((!item.signed && val >= 1 << size) + || (item.signed && val >= (1 << (size - 1)) - 1)) + throw new Error(value + " >= " + iteme.signed ? (1 << size) : ((1 << (size - 1)) - 1)); + while (size > 0) { + var writeBits = Math.min(8 - bits, size); + toWrite = toWrite << writeBits | + ((val >> (size - writeBits)) & generateBitMask(writeBits)); + size -= writeBits; + bits += writeBits; + if (bits === 8) { + buffer[offset++] = toWrite; + bits = 0; + toWrite = 0; + } + } + }); + if (bits != 0) + buffer[offset++] = toWrite << (8 - bits); + return offset; +} + +function sizeOfBitField(value, typeArgs, context) { + return Math.ceil(typeArgs.reduce(function(acc, item) { + return acc + item.size; + }, 0) / 8); +} diff --git a/test/dataTypes/utils.js b/test/dataTypes/utils.js new file mode 100644 index 0000000..c7a7efb --- /dev/null +++ b/test/dataTypes/utils.js @@ -0,0 +1,205 @@ +var assert = require('power-assert'); +var expect = require('chai').expect; + +var utils = require('../../dist/datatypes/utils'); +var getReader = function(dataType) { return dataType[0]; }; +var getWriter = function(dataType) { return dataType[1]; }; +var getSizeOf = function(dataType) { return dataType[2]; }; + +describe('Utils', function() { + describe('.bool', function() { + it('Reads false value for binary 0', function() { + assert.deepEqual(getReader(utils.bool)(new Buffer([0]), 0), {value: false, size: 1}); + }); + it('Reads true for every other binary value', function() { + var buf = new Buffer([0]); + var i = 1; + while (i < 256) { + buf[0] = i++; + assert.deepEqual(getReader(utils.bool)(buf, 0), {value: true, size: 1}); + } + }); + it('Writes false', function() { + var buffer = new Buffer(1); + getWriter(utils.bool)(false, buffer, 0); + assert.deepEqual(buffer, new Buffer([0])); + }); + it('Writes true', function() { + var buffer = new Buffer(1); + getWriter(utils.bool)(true, buffer, 0); + assert.notDeepEqual(buffer, new Buffer([0])); + }); + it('Has a size of 1', function() { + assert.equal(typeof getSizeOf(utils.bool), "number"); + assert.equal(getSizeOf(utils.bool), 1); + }); + }); + describe('.varint', function() { + it.skip('Has no tests', function() { + }); + }); + describe('.buffer', function() { + it.skip('Has no tests', function() { + }); + }); + describe('.string', function() { + it.skip('Has no tests', function() { + }); + }); + describe('.void', function() { + it.skip('Has no tests', function() { + }); + }); + describe('.bitfield', function() { + it('Reads an unsigned 8 bit number', function() { + var buf = new Buffer([0xff]); + var typeArgs = [ + { "name": "one", "size": 8, "signed": false } + ]; + expect(getReader(utils.bitfield)(buf, 0, typeArgs, {})).to.deep.equal({ + value: { "one": 255 }, + size: 1 + }); + }); + it('Reads a signed 8 bit number', function() { + var buf = new Buffer([0xff]); + var typeArgs = [ + { "name": "one", "size": 8, "signed": true } + ]; + expect(getReader(utils.bitfield)(buf, 0, typeArgs, {})).to.deep.equal({ + value: { "one": -1 }, + size: 1 + }); + }); + it('Reads multiple signed 8 bit numbers', function() { + var buf = new Buffer([0xff, 0x80, 0x12]); + var typeArgs = [ + { "name": "one", "size": 8, "signed": true }, + { "name": "two", "size": 8, "signed": true }, + { "name": "three", "size": 8, "signed": true } + ]; + expect(getReader(utils.bitfield)(buf, 0, typeArgs, {})).to.deep.equal({ + value: { "one": -1, "two": -128, "three": 18 }, + size: 3 + }); + }); + it('Reads multiple unsigned 4 bit numbers', function() { + var buf = new Buffer([0xff, 0x80]); + var typeArgs = [ + { "name": "one", "size": 4, "signed": false }, + { "name": "two", "size": 4, "signed": false }, + { "name": "three", "size": 4, "signed": false } + ]; + expect(getReader(utils.bitfield)(buf, 0, typeArgs, {})).to.deep.equal({ + value: { "one": 15, "two": 15, "three": 8 }, + size: 2 + }); + }); + it('Reads multiple signed 4 bit numbers', function() { + var buf = new Buffer([0xff, 0x80]); + var typeArgs = [ + { "name": "one", "size": 4, "signed": true }, + { "name": "two", "size": 4, "signed": true }, + { "name": "three", "size": 4, "signed": true } + ]; + expect(getReader(utils.bitfield)(buf, 0, typeArgs, {})).to.deep.equal({ + value: { "one": -1, "two": -1, "three": -8 }, + size: 2 + }); + }); + it('Reads an unsigned 12 bit number', function() { + var buf = new Buffer([0xff, 0x80]); + var typeArgs = [ + { "name": "one", "size": 12, "signed": false } + ]; + assert.deepEqual(getReader(utils.bitfield)(buf, 0, typeArgs, {}), { + value: { "one": 4088 }, + size: 2 + }); + }); + it('Reads a complex structure', function() { + var buf = new Buffer([0x00, 0x00, 0x03, 0x05, 0x30, 0x42, 0xE0, 0x65]); + var typeArgs = [ + { "name": "x", "size": 26, "signed": true }, + { "name": "y", "size": 12, "signed": true }, + { "name": "z", "size": 26, "signed": true } + ]; + var value = { x: 12, y: 332, z: 4382821 }; + assert.deepEqual(getReader(utils.bitfield)(buf, 0, typeArgs, {}), { + value: value, + size: 8 + }); + }); + it('Writes an unsigned 8 bit number', function() { + var buf = new Buffer(1); + var typeArgs = [ + { "name": "one", "size": 8, "signed": false } + ]; + var value = { "one": 0xff }; + assert.equal(getWriter(utils.bitfield)(value, buf, 0, typeArgs, {}), 1); + assert.deepEqual(buf, new Buffer([0xff])); + }); + it('Writes a signed 8 bit number', function() { + var buf = new Buffer(1); + var typeArgs = [ + { "name": "one", "size": 8, "signed": true } + ]; + var value = { "one": -1 }; + assert.equal(getWriter(utils.bitfield)(value, buf, 0, typeArgs, {}), 1); + assert.deepEqual(buf, new Buffer([0xff])); + }); + it('Writes multiple signed 8 bit numbers', function() { + var buf = new Buffer(3); + var typeArgs = [ + { "name": "one", "size": 8, "signed": true }, + { "name": "two", "size": 8, "signed": true }, + { "name": "three", "size": 8, "signed": true } + ]; + var value = { "one": -1, "two": -128, "three": 18 }; + assert.equal(getWriter(utils.bitfield)(value, buf, 0, typeArgs, {}), 3); + assert.deepEqual(buf, new Buffer([0xff, 0x80, 0x12])); + }); + it('Writes multiple unsigned 4 bit numbers', function() { + var buf = new Buffer(2); + var typeArgs = [ + { "name": "one", "size": 4, "signed": false }, + { "name": "two", "size": 4, "signed": false }, + { "name": "three", "size": 4, "signed": false } + ]; + var value = { "one": 15, "two": 15, "three": 8 }; + assert.equal(getWriter(utils.bitfield)(value, buf, 0, typeArgs, {}), 2); + assert.deepEqual(buf, new Buffer([0xff, 0x80])); + }); + it('Writes multiple signed 4 bit numbers', function() { + var buf = new Buffer(2); + var typeArgs = [ + { "name": "one", "size": 4, "signed": true }, + { "name": "two", "size": 4, "signed": true }, + { "name": "three", "size": 4, "signed": true } + ]; + var value = { "one": -1, "two": -1, "three": -8 }; + assert.equal(getWriter(utils.bitfield)(value, buf, 0, typeArgs, {}), 2); + assert.deepEqual(buf, new Buffer([0xff, 0x80])); + }); + it('Writes an unsigned 12 bit number', function() { + var buf = new Buffer(2); + var typeArgs = [ + { "name": "one", "size": 12, "signed": false } + ]; + var value = { "one": 4088 }; + assert.equal(getWriter(utils.bitfield)(value, buf, 0, typeArgs, {}), 2); + assert.deepEqual(buf, new Buffer([0xff, 0x80])); + }); + it('Writes a complex structure', function() { + var buf = new Buffer(8); + var typeArgs = [ + { "name": "x", "size": 26, "signed": true }, + { "name": "y", "size": 12, "signed": true }, + { "name": "z", "size": 26, "signed": true } + ]; + var value = { x: 12, y: 332, z: 4382821 }; + assert.equal(getWriter(utils.bitfield)(value, buf, 0, typeArgs, {}), 8); + assert.deepEqual(buf, new Buffer([0x00, 0x00, 0x03, 0x05, 0x30, 0x42, 0xE0, 0x65])); + }); + }); +});