From 72b71cd26f4bd1a6b29bb5186025b8f3fa6dc9c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anton=20Klar=C3=A9n?= <fys09akl@student.lu.se> Date: Fri, 6 Mar 2015 16:40:54 +0100 Subject: [PATCH] Added tests and refactored/cleanup array decoding --- lib/js/index.html | 117 +------------------------------- lib/js/lc.js | 168 +++++++++++++++++++++++---------------------- lib/js/tester.js | 169 +++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 235 insertions(+), 219 deletions(-) diff --git a/lib/js/index.html b/lib/js/index.html index 8c355c0..ff10bd2 100644 --- a/lib/js/index.html +++ b/lib/js/index.html @@ -35,122 +35,7 @@ btn.onclick = function() { decodeFromUrl(urlInp.value); }; -decodeFromUrl('http://fair-2014-4.lab1.cs.lth.se:8080/logdata/clamping/experiments/e12c4b31-5b32-41c0-b6a3-a5ad045aa997/data'); - - -var referenceObject = { - log_message: { //struct - sequence: 10, //int - message: 'Test', //string - data: { //struct - f_value: 1.6, //float - d_value: 1000000.0, //double - l_value: 145, //long - s_value: 10, //short - b_value: 0xA, //byte - theStack: { //struct - level: 2, //int - name: 'Robot.Move.toPos(10)', //string - fatal: true //bool - } - }, - xyz: [1,2,3], //int[3] - vect: [8,7,6,5,4,3], //int[_] - fix_mat: [[1,2,3,4],[2,3,4,5],[3,4,5,6]], //int[3][_] - dyn_mat: [[1,2],[3,4]], //int[_][2] - fix_mul: [[1,2,3,4],[2,3,4,5],[3,4,5,6]], //int[_, 4] - dyn_mul: [[1,2],[3,4]], //int[_, 2] - fix_mulmat: [[[1,2,3],[4,5,6]]], //int[1, _][_] - fix_matmul: [[[1,2,3],[4,5,6]]], //int[_][2, _] - dyn_mulmat: [[[1,2,3],[4,5,6]]], //int[_, _][3] - dyn_matmul: [[[1,2,3],[4,5,6]]], //int[1][_, 3] - points: [ - {x: 4.6, y: 2.3}, - {x: 7.1, y: 1.0}, - {x: 6.2, y: 5.9} - ] //struct { float x; float y; } points[_] - } -}; - - - - - -function testObject(obj) { - console.log(JSON.stringify(obj)); - console.log(compareObjects(referenceObject, obj)); -} - -function compareObjects(o, p) { - var i, - keysO = Object.keys(o).sort(), - keysP = Object.keys(p).sort(); - if (keysO.length !== keysP.length) { - return false;//not the same nr of keys - } - if (keysO.join('') !== keysP.join('')) { - return false;//different keys - } - for (i=0;i<keysO.length;++i) { - var x = o[keysO[i]], - y = p[keysO[i]]; - if (x instanceof Array) { - if (!(y instanceof Array)) - return false; - //if (compareObjects(o[keysO[i]], p[keysO[i]] === false) return false - //would work, too, and perhaps is a better fit, still, this is easy, too - if (p[keysO[i]].sort().join('') !== o[keysO[i]].sort().join('')) - return false; - } - else if (o[keysO[i]] instanceof Date) { - if (!(p[keysO[i]] instanceof Date)) - return false; - if ((''+o[keysO[i]]) !== (''+p[keysO[i]])) - return false; - } - else if (o[keysO[i]] instanceof Function) { - if (!(p[keysO[i]] instanceof Function)) - return false; - //ignore functions, or check them regardless? - } - if (x instanceof Object) { - if (!(y instanceof Object)) - return false; - if (x === o) {//self reference? - if (y !== p) - return false; - } - else if (compareObjects(x, y) === false) { - return false;//WARNING: does not deal with circular refs other than ^^ - } - } - else if(Number(x) === x) { //number - if((x|0) === x) { //int - if(x!==y) { - return false; - } - } - else { - //float point, allow deviation - var diff = Math.abs(x-y); - if(diff > 1e-6) { - return false; - } - } - } - else if (x !== y) {//change !== to != for loose comparison - console.log('missmatch ' + x +':'+y); - return false;//not the same value - } - - - console.log('match ' + x +':'+y); - } - return true; -} -function isInt(n){ - return Number(n)===n && n%1===0; -} </script> + <script src='tester.js'></script> </body> </html> diff --git a/lib/js/lc.js b/lib/js/lc.js index 0182435..c3af377 100644 --- a/lib/js/lc.js +++ b/lib/js/lc.js @@ -115,7 +115,7 @@ LabComm.Buffer.prototype.getString = function() { /** * Reads a boolean (8-bits) from the buffer. - * @return {Boolean} A boolean + * @return {boolean} A boolean */ LabComm.Buffer.prototype.getBoolean = function() { return Boolean(this.data_.getUint8(this.offset_++)); @@ -235,13 +235,13 @@ LabComm.Decoder = function(callback) { * @param {string} type The string representation of the type * @param {string} parser The parsing function to be called when decoding current field * @param {number} level The current nesting level - * @param {string=} opt_arrayVar The array variable to put the result in + * @param {string=} opt_parentArray The array variable to put the result in * @return {string} The JSON part of the final structure * @private */ -LabComm.Decoder.prototype.buildDecoder_ = function(name, json, fb, type, parser, level, opt_arrayVar) { - if(opt_arrayVar) { - fb.push(this.indent_(level) + opt_arrayVar + ' = ' + parser + '; //'+name+'\n'); +LabComm.Decoder.prototype.buildDecoder_ = function(name, json, fb, type, parser, level, opt_parentArray) { + if(opt_parentArray) { + fb.push(this.indent_(level) + opt_parentArray + ' = ' + parser + '; //'+name+'\n'); return type; } @@ -251,10 +251,22 @@ LabComm.Decoder.prototype.buildDecoder_ = function(name, json, fb, type, parser, return '"'+name+'":'+'"'+type+'"'; }; +/** + * Creates indentation + * @param {number} level The indentation level to match + * @return {string} A string with the given level of indentation + * @private + */ LabComm.Decoder.prototype.indent_ = function(level) { return new Array(level + 1).join('\t'); }; +/** + * Splits the variable name based on array literal + * e.g. int[3] => {type: "int", dim: "[3]"} + * @param {string} str The string representation of an array type + * @return {{type: string, dim: string}} An object consisting of the split string + */ LabComm.Decoder.prototype.exctractArray_ = function(str) { var index = str.indexOf('['); if(index > 0) { @@ -263,113 +275,102 @@ LabComm.Decoder.prototype.exctractArray_ = function(str) { return {type: str, dim: ''}; } - /** * Decodes a signature recursively * @param {string} name The name of the current field * @param {Array<string>} json The final json representation of the fields * @param {Array<string>} fb The final function body of the decoder * @param {number} level The current nestling level - * @param {string=} opt_arrayVar The array variable to put the result in + * @param {string=} opt_parentArray The array variable to put the result in * @return {string} The JSON part of the sub structure * @throws {string} If decoding could not be performed * @private */ -LabComm.Decoder.prototype.decodeSignature_ = function(name, json, fb, level, opt_arrayVar) { +LabComm.Decoder.prototype.decodeSignature_ = function(name, json, fb, level, opt_parentArray) { var type = this.data.getVarint(); switch(type) { case LabComm.Constants.ARRAY: var arrayVariable = 'a'+this.uid_++; - var func = []; - func.push('"'+name+'":['); - if(!opt_arrayVar) { //only add first array to final result + if(!opt_parentArray) { + //only add top-most array to final result json.push('"'+name+'":'+arrayVariable); } + //read number of indecies var indecies = this.data.getVarint(); if(indecies === 0) { throw "Array cant have 0 dimensions"; } + //get dimensions of the indecies and build for loop header var dims = new Array(indecies); + var dimStr = new Array(indecies); + var dimVars = new Array(indecies); for(var i = 0; i < indecies; i++) { - dims[i] = this.data.getVarint(); - } - - //console.log(name + '[' + dims + ']'); - - var dimStr = []; - - if(indecies == 1) { - var dim = dims[0]; + var dim = this.data.getVarint(); + dims[i] = dim; + dimStr[i] = (dim === 0)? '_' : dim; // 0 means dynamic array if(dim === 0) { - dimStr.push('_'); var arrayDimVar = 'd' + this.uid_++; fb.push(this.indent_(level) + 'var ' + arrayDimVar + ' = data.getVarint();\n'); dim = arrayDimVar; - } else dimStr.push(dim); - fb.push(this.indent_(level) + 'var ' + arrayVariable + ' = new Array(' + dim + ');\n'); - var indexVar = 'i' + this.uid_++; - fb.push(this.indent_(level) + 'for(var '+indexVar+' = 0; '+indexVar+' < ' + dim + '; '+indexVar+'++) {\n') - var arrayType = this.decodeSignature_(name, json, fb, level+1, arrayVariable+'['+indexVar+']'); - fb.push(this.indent_(level) + '}\n'); // array for loop - if(opt_arrayVar) { - fb.push(this.indent_(level) + opt_arrayVar+' = '+arrayVariable+';\n'); } + dimVars[i] = dim; } - else { - var dimVars = new Array(indecies); - for(var i = 0; i < indecies; i++) { - var dim = dims[i]; - if(dim === 0) { - var arrayDimVar = 'd' + this.uid_++; - fb.push(this.indent_(level) + 'var ' + arrayDimVar + ' = data.getVarint();\n'); - dim = arrayDimVar; - dimStr.push('_'); - } else dimStr.push(dim); - dimVars[i] = dim; - } - var endFor = []; - var nextVar, indexVar, prevVar, firstVar = arrayVariable; - for(var i = 0; i < indecies; i++) { - indexVar = 'i' + this.uid_++; - fb.push(this.indent_(level+i) + 'var ' + arrayVariable + ' = new Array(' + dimVars[i] + ');\n'); - fb.push(this.indent_(level+i) + 'for(var '+indexVar+' = 0; '+indexVar+' < ' + dimVars[i] + '; '+indexVar+'++) {\n') - - var nextVar = 'a' + this.uid_++; - endFor.unshift( - this.indent_(level+i+1) + '}\n' + - this.indent_(level+i+1) + arrayVariable + '[' + indexVar + '] = ' + nextVar + ';\n' + - this.indent_(level+i) + '}\n' - ); - prevVar = arrayVariable; - arrayVariable = nextVar; - } - var arrayType = this.decodeSignature_(name, json, fb, level+indecies, prevVar+'['+indexVar+']'); - endFor.shift(); // remove last - - fb.push(endFor.join('')); - if(opt_arrayVar) { - fb.push(this.indent_(level) + opt_arrayVar+' = '+firstVar+';\n'); - } + //create for loop(s) + var endFor = [], + nextVar, indexVar, prevVar, + currentVar = arrayVariable; + for(var i = 0; i < indecies; i++) { + indexVar = 'i' + this.uid_++; + fb.push(this.indent_(level+i) + 'var ' + currentVar + ' = new Array(' + dimVars[i] + ');\n'); + fb.push(this.indent_(level+i) + 'for(var '+indexVar+' = 0; '+indexVar+' < ' + dimVars[i] + '; '+indexVar+'++) {\n') + + var nextVar = 'a' + this.uid_++; + endFor.unshift( + this.indent_(level+i+1) + '}\n' + + this.indent_(level+i+1) + arrayVariable + '[' + indexVar + '] = ' + nextVar + ';\n' + + this.indent_(level+i) + '}\n' + ); + prevVar = currentVar; + currentVar = nextVar; + } + endFor.shift(); // remove last + + //propagate decoding + var arrayType = this.decodeSignature_(name, json, fb, level+indecies, prevVar+'['+indexVar+']'); + + //if we only have one index we need to explicitly end the foor loop + if(indecies == 1) { + endFor.unshift(this.indent_(level) + '}\n'); } - if(opt_arrayVar) { + //create for loop footer + fb.push(endFor.join('')); + if(opt_parentArray) { + fb.push(this.indent_(level) + opt_parentArray+' = '+arrayVariable+';\n'); + } + + //if we have a parent array just return the type + if(opt_parentArray) { return arrayType + '['+dimStr.join(', ')+']'; } + var arrayParts = this.exctractArray_(arrayType); - if(arrayParts.type.charAt(0)=='{') { + + //we need to handle structs seperatly since they contains JSON object + if(arrayParts.type.charAt(0)=='{') { return '"'+name+'['+dimStr.join(', ')+']'+arrayParts.dim+'":'+arrayParts.type; } + return '"'+name+'['+dimStr.join(', ')+']'+arrayParts.dim+'":'+'"'+arrayParts.type+'"'; - break; case LabComm.Constants.STRUCT: var func = []; var subJson = []; var structVar = 's' + this.uid_++; - if(opt_arrayVar) { - func.push('{'); + if(opt_parentArray) { + func.push('{'); //we get the name from the parent array } else { func.push('"'+name+'":{'); @@ -386,8 +387,8 @@ LabComm.Decoder.prototype.decodeSignature_ = function(name, json, fb, level, opt } func.push("}"); - if(opt_arrayVar) { - fb.push(this.indent_(level) + opt_arrayVar + ' = {' + subJson.join('') + '};\n'); + if(opt_parentArray) { + fb.push(this.indent_(level) + opt_parentArray + ' = {' + subJson.join('') + '};\n'); } else { fb.push(this.indent_(level) + 'var ' + structVar + ' = {' + subJson.join('') + '};\n'); @@ -395,21 +396,21 @@ LabComm.Decoder.prototype.decodeSignature_ = function(name, json, fb, level, opt } return func.join(''); case LabComm.Constants.BOOLEAN: - return this.buildDecoder_(name, json, fb, "bool", "data.getBoolean()", level, opt_arrayVar); + return this.buildDecoder_(name, json, fb, "bool", "data.getBoolean()", level, opt_parentArray); case LabComm.Constants.BYTE: - return this.buildDecoder_(name, json, fb, "byte", "data.getByte()", level, opt_arrayVar); + return this.buildDecoder_(name, json, fb, "byte", "data.getByte()", level, opt_parentArray); case LabComm.Constants.SHORT: - return this.buildDecoder_(name, json, fb, "short", "data.getShort()", level, opt_arrayVar); + return this.buildDecoder_(name, json, fb, "short", "data.getShort()", level, opt_parentArray); case LabComm.Constants.INT: - return this.buildDecoder_(name, json, fb, "int", "data.getInt()", level, opt_arrayVar); + return this.buildDecoder_(name, json, fb, "int", "data.getInt()", level, opt_parentArray); case LabComm.Constants.LONG: - return this.buildDecoder_(name, json, fb, "long", "data.getLong()", level, opt_arrayVar); + return this.buildDecoder_(name, json, fb, "long", "data.getLong()", level, opt_parentArray); case LabComm.Constants.FLOAT: - return this.buildDecoder_(name, json, fb, "float", "data.getFloat()", level, opt_arrayVar); + return this.buildDecoder_(name, json, fb, "float", "data.getFloat()", level, opt_parentArray); case LabComm.Constants.DOUBLE: - return this.buildDecoder_(name, json, fb, "double", "data.getDouble()", level, opt_arrayVar); + return this.buildDecoder_(name, json, fb, "double", "data.getDouble()", level, opt_parentArray); case LabComm.Constants.STRING: - return this.buildDecoder_(name, json, fb, "string", "data.getString()", level, opt_arrayVar); + return this.buildDecoder_(name, json, fb, "string", "data.getString()", level, opt_parentArray); } throw "Unknown type found in signature: 0x" + type.toString(16); }; @@ -425,7 +426,7 @@ LabComm.Decoder.prototype.decodePackage = function(buffer) { var tag = this.data.getVarint(); var len = this.data.getVarint(); - console.log('0x' + tag.toString(16) + ':' + len); + //console.log('0x' + tag.toString(16) + ':' + len); switch(tag) { case LabComm.Constants.VERSION: @@ -441,10 +442,7 @@ LabComm.Decoder.prototype.decodePackage = function(buffer) { var json = [], fb = []; - var tmp = this.decodeSignature_(name, json, fb, 1); - console.log(tmp); - var struct = JSON.parse('{' + tmp + "}"); - console.log(struct); + var struct = JSON.parse('{' + this.decodeSignature_(name, json, fb, 1) + "}"); var func = fb.join('') + '\n\treturn {' + json.join('') + '};'; @@ -470,7 +468,7 @@ LabComm.Decoder.prototype.decodePackage = function(buffer) { this.callback_(decl, sample); - break; + break; } } }; diff --git a/lib/js/tester.js b/lib/js/tester.js index 76a17b7..c8b5944 100644 --- a/lib/js/tester.js +++ b/lib/js/tester.js @@ -1,43 +1,176 @@ +'use strict'; +(function() { +function decodeFromUrl(url) { + var oReq = new XMLHttpRequest(); + oReq.open("GET", url, true); + oReq.responseType = "arraybuffer"; + + oReq.onload = function (oEvent) { + var arrayBuffer = oReq.response; + if (arrayBuffer) { + var buff = new LabComm.Buffer(new DataView(arrayBuffer)); + window.decoder = new LabComm.Decoder(function(decl, data) { + testObject(decl, data); + }); + window.decoder.decodePackage(buff); + } + }; + + oReq.send(null); +} + +decodeFromUrl('http://fair-2014-4.lab1.cs.lth.se:8080/logdata/clamping/experiments/e12c4b31-5b32-41c0-b6a3-a5ad045aa997/data'); + +var referenceStruct ={ + "log_message":{ + "sequence":"int", + "message":"string", + "data":{ + "f_value":"float", + "d_value":"double", + "l_value":"long", + "s_value":"short", + "b_value":"byte", + "theStack":{ + "level":"int", + "name":"string", + "fatal":"bool" + } + }, + "xyz[3]":"int", + "vect[_]":"int", + "fix_mat[3][_]":"int", + "dyn_mat[_][2]":"int", + "fix_mul[_, 4]":"int", + "dyn_mul[_, 2]":"int", + "fix_mulmat[1, _][_]":"int", + "fix_matmul[_][2, _]":"int", + "dyn_mulmat[_, _][3]":"int", + "dyn_matmul[1][_, 3]":"int", + "points[_]":{ + "x":"float", + "y":"float" + } + } +}; + +var referenceObject = { + log_message: { //struct + sequence: 10, //int + message: 'Test', //string + data: { //struct + f_value: 1.6, //float + d_value: 1000000.0, //double + l_value: 145, //long + s_value: 10, //short + b_value: 0xA, //byte + theStack: { //struct + level: 2, //int + name: 'Robot.Move.toPos(10)', //string + fatal: true //bool + } + }, + xyz: [1,2,3], //int[3] + vect: [8,7,6,5,4,3], //int[_] + fix_mat: [[1,2,3,4],[2,3,4,5],[3,4,5,6]], //int[3][_] + dyn_mat: [[1,2],[3,4]], //int[_][2] + fix_mul: [[1,2,3,4],[2,3,4,5],[3,4,5,6]], //int[_, 4] + dyn_mul: [[1,2],[3,4]], //int[_, 2] + fix_mulmat: [[[1,2,3],[4,5,6]]], //int[1, _][_] + fix_matmul: [[[1,2,3],[4,5,6]]], //int[_][2, _] + dyn_mulmat: [[[1,2,3],[4,5,6]]], //int[_, _][3] + dyn_matmul: [[[1,2,3],[4,5,6]]], //int[1][_, 3] + points: [ + {x: 4.6, y: 2.3}, + {x: 7.1, y: 1.0}, + {x: 6.2, y: 5.9} + ] //struct { float x; float y; } points[_] + } +}; + +function testObject(decl, obj) { + var i = 0; + console.log('-----Running tests-----'); + testCase('Decode Sample Decalaration', referenceStruct, decl.struct) && i++; + testCase('Decode Data Sample', referenceObject, obj) && i++; + console.log('----------'); + console.log(i + ' of 2 tests success'); + console.log('----------'); +} + +function testCase(msg, r, t) { + var test = compareObjects(r, t); + console.log('Test "' + msg + '": ' + (test?'Success':'Fail')); + if(!test) { + console.log(t); + console.log('but expected: '); + console.log(r); + } + return test; +} + function compareObjects(o, p) { var i, keysO = Object.keys(o).sort(), keysP = Object.keys(p).sort(); - if (keysO.length !== keysP.length) + if (keysO.length !== keysP.length) { return false;//not the same nr of keys - if (keysO.join('') !== keysP.join('')) + } + if (keysO.join('') !== keysP.join('')) { return false;//different keys + } for (i=0;i<keysO.length;++i) { - if (o[keysO[i]] instanceof Array) { - if (!(p[keysO[i]] instanceof Array)) + var x = o[keysO[i]], + y = p[keysO[i]]; + if (x instanceof Array) { + if (!(y instanceof Array)) return false; //if (compareObjects(o[keysO[i]], p[keysO[i]] === false) return false //would work, too, and perhaps is a better fit, still, this is easy, too - if (p[keysO[i]].sort().join('') !== o[keysO[i]].sort().join('')) + if (y.join('') !== x.join('')) return false; } - else if (o[keysO[i]] instanceof Date) { - if (!(p[keysO[i]] instanceof Date) + else if (x instanceof Date) { + if (!(y instanceof Date)) return false; - if ((''+o[keysO[i]]) !== (''+p[keysO[i]])) + if ((''+x) !== (''+y)) return false; } - else if (o[keysO[i]] instanceof Function) { - if (!(p[keysO[i]] instanceof Function) + else if (x instanceof Function) { + if (!(y instanceof Function)) return false; - //ignore functions, or check them regardless? + //ignore functions } - else if (o[keysO[i]] instanceof Object) { - if (!(p[keysO[i]] instanceof Object) + if (x instanceof Object) { + if (!(y instanceof Object)) return false; - if (o[keysO[i]] === o) {//self reference? - if (p[keysO[i]] !== p) + if (x === o) {//self reference? + if (y !== p) return false; } - else if (compareObjects(o[keysO[i]], p[keysO[i]]) === false) + else if (compareObjects(x, y) === false) { return false;//WARNING: does not deal with circular refs other than ^^ + } + } + else if(Number(x) === x) { //number + if((x|0) === x) { //int + if(x!==y) { + return false; + } + } + else { //float point, allow deviation + var diff = Math.abs(x-y); + if(diff > 1e-6) { + return false; + } + } + } + else if (x !== y) { + return false; } - if (o[keysO[i]] !== p[keysO[i]])//change !== to != for loose comparison - return false;//not the same value } return true; } + +})(); + -- GitLab