diff --git a/doc/.gitignore b/doc/.gitignore index df2a6e60dbf0dc336f4bc67ea1ef1b99119b2bda..28be7898ff6dfff29c153c7b0609b0d3918ee7ba 100644 --- a/doc/.gitignore +++ b/doc/.gitignore @@ -4,4 +4,5 @@ tech_report.blg tech_report.fdb_latexmk tech_report.fls tech_report.log -tech_report.pdf \ No newline at end of file +tech_report.pdf +tech_report.synctex.gz \ No newline at end of file diff --git a/lib/js/index.html b/lib/js/index.html index 89acdf300a83041136f4f371d4604334135d784e..8c355c0b4103b5c43090e7db7b9231ce3040ac26 100644 --- a/lib/js/index.html +++ b/lib/js/index.html @@ -20,7 +20,7 @@ function decodeFromUrl(url) { if (arrayBuffer) { var buff = new LabComm.Buffer(new DataView(arrayBuffer)); window.decoder = new LabComm.Decoder(function(decl, data) { - console.log(data); + testObject(data); }); window.decoder.decodePackage(buff); } @@ -35,6 +35,122 @@ 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> </body> </html> diff --git a/lib/js/lc.js b/lib/js/lc.js index 4b87362c90357668d8e1201465645110d1682dd8..0182435b00c0f637ec504f48b3df0d8c19cac477 100644 --- a/lib/js/lc.js +++ b/lib/js/lc.js @@ -193,7 +193,7 @@ LabComm.Buffer.prototype.getDouble = function() { * @param {number} length The number of bytes to skip */ LabComm.Buffer.prototype.skip = function(length) { - this.offset_ += len; + this.offset_ += length; }; @@ -218,14 +218,14 @@ LabComm.Decoder = function(callback) { * @private */ this.callback_ = callback || function(a,b) {}; -}; -/** - * A unique id for variable naming inside the decoder functions - * @type {number} - * @private - */ -LabComm.Decoder.prototype.uid_ = 1; + /** + * A unique id for variable naming inside the decoder functions + * @type {number} + * @private + */ + this.uid_ = 1; +}; /** * Builds a part of the decoder function @@ -234,69 +234,182 @@ LabComm.Decoder.prototype.uid_ = 1; * @param {Array<string>} fb The final function body of the decoder * @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 * @return {string} The JSON part of the final structure * @private */ -LabComm.Decoder.prototype.buildDecoder_ = function(name, json, fb, type, parser) { +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'); + return type; + } + var varname = 'v'+this.uid_++; json.push('"'+name+'":'+varname); - fb.push('var '+varname+' = ' + parser); + fb.push(this.indent_(level) + 'var '+varname+' = ' + parser + '; //'+name+'\n'); return '"'+name+'":'+'"'+type+'"'; }; +LabComm.Decoder.prototype.indent_ = function(level) { + return new Array(level + 1).join('\t'); +}; + +LabComm.Decoder.prototype.exctractArray_ = function(str) { + var index = str.indexOf('['); + if(index > 0) { + return {type: str.substring(0,index), dim: str.substring(index)}; + } + 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 * @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) { +LabComm.Decoder.prototype.decodeSignature_ = function(name, json, fb, level, opt_arrayVar) { var type = this.data.getVarint(); switch(type) { case LabComm.Constants.ARRAY: - var dim = this.data.getVarint(); - console.log("array"); - if(dim === 0) { + var arrayVariable = 'a'+this.uid_++; + var func = []; + func.push('"'+name+'":['); + if(!opt_arrayVar) { //only add first array to final result + json.push('"'+name+'":'+arrayVariable); + } + + var indecies = this.data.getVarint(); + if(indecies === 0) { throw "Array cant have 0 dimensions"; } - //TODO: implement - throw "Array decoding not implemented"; + + var dims = 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]; + 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'); + } + } + 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'); + } + + } + + if(opt_arrayVar) { + return arrayType + '['+dimStr.join(', ')+']'; + } + var arrayParts = this.exctractArray_(arrayType); + 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 = []; - func.push('"'+name+'":{'); - json.push('"'+name+'":{'); + var subJson = []; + var structVar = 's' + this.uid_++; + if(opt_arrayVar) { + func.push('{'); + } + else { + func.push('"'+name+'":{'); + } var fields = this.data.getVarint(); + //TODO: fields == 0 means a void type while(fields--) { var fn = this.data.getString(); - func.push(this.decodeSignature_(fn, json, fb)); + func.push(this.decodeSignature_(fn, subJson, fb, level)); if(fields!=0) { - json.push(','); + subJson.push(','); func.push(","); } } func.push("}"); - json.push("}"); + + if(opt_arrayVar) { + fb.push(this.indent_(level) + opt_arrayVar + ' = {' + subJson.join('') + '};\n'); + } + else { + fb.push(this.indent_(level) + 'var ' + structVar + ' = {' + subJson.join('') + '};\n'); + json.push('"'+name+'":' + structVar); + } return func.join(''); case LabComm.Constants.BOOLEAN: - return this.buildDecoder_(name, json, fb, "bool", "data.getBoolean()"); + return this.buildDecoder_(name, json, fb, "bool", "data.getBoolean()", level, opt_arrayVar); case LabComm.Constants.BYTE: - return this.buildDecoder_(name, json, fb, "byte", "data.getByte()"); + return this.buildDecoder_(name, json, fb, "byte", "data.getByte()", level, opt_arrayVar); case LabComm.Constants.SHORT: - return this.buildDecoder_(name, json, fb, "short", "data.getShort()"); + return this.buildDecoder_(name, json, fb, "short", "data.getShort()", level, opt_arrayVar); case LabComm.Constants.INT: - return this.buildDecoder_(name, json, fb, "int", "data.getInt()"); + return this.buildDecoder_(name, json, fb, "int", "data.getInt()", level, opt_arrayVar); case LabComm.Constants.LONG: - return this.buildDecoder_(name, json, fb, "long", "data.getLong()"); + return this.buildDecoder_(name, json, fb, "long", "data.getLong()", level, opt_arrayVar); case LabComm.Constants.FLOAT: - return this.buildDecoder_(name, json, fb, "float", "data.getFloat()"); + return this.buildDecoder_(name, json, fb, "float", "data.getFloat()", level, opt_arrayVar); case LabComm.Constants.DOUBLE: - return this.buildDecoder_(name, json, fb, "double", "data.getDouble()"); + return this.buildDecoder_(name, json, fb, "double", "data.getDouble()", level, opt_arrayVar); case LabComm.Constants.STRING: - return this.buildDecoder_(name, json, fb, "string", "data.getString()"); + return this.buildDecoder_(name, json, fb, "string", "data.getString()", level, opt_arrayVar); } throw "Unknown type found in signature: 0x" + type.toString(16); }; @@ -328,15 +441,25 @@ LabComm.Decoder.prototype.decodePackage = function(buffer) { var json = [], fb = []; - var struct = JSON.parse('{' + this.decodeSignature_(name, json, fb) + "}"); + var tmp = this.decodeSignature_(name, json, fb, 1); + console.log(tmp); + var struct = JSON.parse('{' + tmp + "}"); + console.log(struct); - var func = fb.join(';\n\t') + ';\n\treturn {' + json.join('') + '}'; + var func = fb.join('') + '\n\treturn {' + json.join('') + '};'; this.declarations_[index] = new LabComm.SampleDeclaration(index, name, struct, new Function('data', func)); break; - case LabComm.Constants.SAMPLE_REF: case LabComm.Constants.TYPE_DEF: + //TODO: same as sample_def + this.data.skip(len); + break; case LabComm.Constants.TYPE_BINDING: + var sampleIndex = this.data.getVarint(); + var typeIndex = this.data.getVarint(); + //TODO: bind? + break; + case LabComm.Constants.SAMPLE_REF: case LabComm.Constants.PRAGMA: this.data.skip(len); console.log("skipping unhandled package of size " + len); @@ -347,8 +470,8 @@ LabComm.Decoder.prototype.decodePackage = function(buffer) { this.callback_(decl, sample); - break; - } + break; + } } }; diff --git a/lib/js/tester.js b/lib/js/tester.js new file mode 100644 index 0000000000000000000000000000000000000000..76a17b77f6c72a988d2bd40bd1e6971afc7649bc --- /dev/null +++ b/lib/js/tester.js @@ -0,0 +1,43 @@ +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) { + if (o[keysO[i]] instanceof Array) { + if (!(p[keysO[i]] 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? + } + else if (o[keysO[i]] instanceof Object) { + if (!(p[keysO[i]] instanceof Object) + return false; + if (o[keysO[i]] === o) {//self reference? + if (p[keysO[i]] !== p) + return false; + } + else if (compareObjects(o[keysO[i]], p[keysO[i]]) === false) + return false;//WARNING: does not deal with circular refs other than ^^ + } + if (o[keysO[i]] !== p[keysO[i]])//change !== to != for loose comparison + return false;//not the same value + } + return true; +}