From b8357a64641c3a4d4393b7374716cf1f2e496dda Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Sun, 9 Mar 2014 17:54:44 -0700 Subject: [PATCH] Unmiified js files, mostly --- background.html | 5 + js-deps/ByteBuffer.min.js | 2373 ++++++++++++++++++++++- js-deps/ProtoBuf.min.js | 3812 ++++++++++++++++++++++++++++++++++++- js-deps/aes.js | 236 ++- js-deps/cipher-core.js | 863 +++++++++ js-deps/core.js | 712 +++++++ js-deps/enc-base64.js | 109 ++ js-deps/mode-ctr-min.js | 44 + js-deps/pad-nopadding.js | 16 + options.html | 5 + popup.html | 5 + test.html | 9 +- 12 files changed, 8012 insertions(+), 177 deletions(-) create mode 100644 js-deps/cipher-core.js create mode 100644 js-deps/core.js create mode 100644 js-deps/enc-base64.js create mode 100644 js-deps/mode-ctr-min.js create mode 100644 js-deps/pad-nopadding.js diff --git a/background.html b/background.html index 5de109e0..548a7b27 100644 --- a/background.html +++ b/background.html @@ -3,7 +3,12 @@ + + + + + diff --git a/js-deps/ByteBuffer.min.js b/js-deps/ByteBuffer.min.js index 4bc46b7e..c0b939b8 100644 --- a/js-deps/ByteBuffer.min.js +++ b/js-deps/ByteBuffer.min.js @@ -1,55 +1,2320 @@ /* - ByteBuffer.js (c) 2013 Daniel Wirtz - Released under the Apache License, Version 2.0 - see: https://github.com/dcodeIO/ByteBuffer.js for details -*/ -(function(n){function q(l){function c(a,b,d){a="undefined"!==typeof a?parseInt(a,10):c.DEFAULT_CAPACITY;1>a&&(a=c.DEFAULT_CAPACITY);this.array=d?null:new ArrayBuffer(a);this.view=d?null:new DataView(this.array);this.offset=0;this.markedOffset=-1;this.length=0;this.littleEndian="undefined"!=typeof b?!!b:!1}var p=null;if("function"===typeof require)try{var n=require("buffer"),p=n&&"function"===typeof n.Buffer&&"function"===typeof n.Buffer.isBuffer?n.Buffer:null}catch(q){}c.VERSION="2.3.1";c.DEFAULT_CAPACITY= -16;c.LITTLE_ENDIAN=!0;c.BIG_ENDIAN=!1;c.Long=l||null;c.isByteBuffer=function(a){return a&&(a instanceof c||"object"===typeof a&&(null===a.array||a.array instanceof ArrayBuffer)&&(null===a.view||a.view instanceof DataView)&&"number"===typeof a.offset&&"number"===typeof a.markedOffset&&"number"===typeof a.length&&"boolean"===typeof a.littleEndian)};c.allocate=function(a,b){return new c(a,b)};c.wrap=function(a,b,d){"boolean"===typeof b&&(d=b,b="utf8");if("string"===typeof a)switch(b){case "base64":return c.decode64(a, -d);case "hex":return c.decodeHex(a,d);case "binary":return c.decodeBinary(a,d);default:return(new c(c.DEFAULT_CAPACITY,d)).writeUTF8String(a).flip()}if(p&&p.isBuffer(a)){b=(new Uint8Array(a)).buffer;if(b===a){b=new ArrayBuffer(a.length);for(var e=new Uint8Array(b),f=0,g=a.length;fa)return!1;null===this.array&&(this.array=new ArrayBuffer(a),this.view=new DataView(this.array));if(this.array.byteLengtha||a>this.array.byteLength||1>b||b>this.array.byteLength)throw Error(this+" cannot be sliced: Index out of bounds (0-"+this.array.byteLength+" -> "+a+"-"+b+")");d=this.clone();d.offset= -a;d.length=b;return d};c.prototype.ensureCapacity=function(a){return null===this.array?this.resize(a):this.array.byteLength=a?2*this.array.byteLength:a):this};c.prototype.flip=function(){this.length=null==this.array?0:this.offset;this.offset=0;return this};c.prototype.mark=function(a){if(null==this.array)throw Error(this+" cannot be marked: Already destroyed");a="undefined"!==typeof a?parseInt(a,10):this.offset;if(0>a||a>this.array.byteLength)throw Error(this+ -" cannot be marked: Offset to mark is less than 0 or bigger than the capacity ("+this.array.byteLength+"): "+a);this.markedOffset=a;return this};c.prototype.reset=function(){if(null===this.array)throw Error(this+" cannot be reset: Already destroyed");0<=this.markedOffset?(this.offset=this.markedOffset,this.markedOffset=-1):this.length=this.offset=0;return this};c.prototype.clone=function(){var a=new c(-1,this.littleEndian,!0);a.array=this.array;a.view=this.view;a.offset=this.offset;a.markedOffset= -this.markedOffset;a.length=this.length;return a};c.prototype.copy=function(){if(null==this.array)return this.clone();var a=new c(this.array.byteLength,this.littleEndian),b=new Uint8Array(this.array);(new Uint8Array(a.array)).set(b);a.offset=this.offset;a.markedOffset=this.markedOffset;a.length=this.length;return a};c.prototype.remaining=function(){return null===this.array?0:this.length-this.offset};c.prototype.capacity=function(){return null!=this.array?this.array.byteLength:0};c.prototype.compact= -function(){if(null==this.array)throw Error(this+" cannot be compacted: Already destroyed");this.offset>this.length&&this.flip();if(this.offset===this.length)return this.array=new ArrayBuffer(0),this.view=null,this;if(0===this.offset&&this.length===this.array.byteLength)return this;var a=new Uint8Array(this.array),b=new ArrayBuffer(this.length-this.offset);(new Uint8Array(b)).set(a.subarray(this.offset,this.length));this.array=b;this.markedOffset=this.markedOffset>=this.offset?this.markedOffset-this.offset: --1;this.offset=0;this.length=this.array.byteLength;return this};c.prototype.destroy=function(){null!==this.array&&(this.view=this.array=null,this.offset=0,this.markedOffset=-1,this.length=0);return this};c.prototype.reverse=function(){if(null===this.array)throw Error(this+" cannot be reversed: Already destroyed");Array.prototype.reverse.call(new Uint8Array(this.array));var a=this.offset;this.offset=this.array.byteLength-this.length;this.markedOffset=-1;this.length=this.array.byteLength-a;this.view= -new DataView(this.array);return this};c.prototype.append=function(a,b){a instanceof c||(a=c.wrap(a));if(null===a.array)throw Error(a+" cannot be appended to "+this+": Already destroyed");var d=a.length-a.offset;if(0==d)return this;0>d&&(a=a.clone().flip(),d=a.length-a.offset);b="undefined"!==typeof b?b:(this.offset+=d)-d;this.ensureCapacity(b+d);d=new Uint8Array(a.array);(new Uint8Array(this.array)).set(d.subarray(a.offset,a.length),b);return this};c.prototype.prepend=function(a,b){a instanceof c|| -(a=c.wrap(a));if(null===a.array)throw a+" cannot be prepended to "+this+": Already destroyed";var d=a.length-a.offset;if(0==d)return this;0>d&&(a=a.clone().flip(),d=a.length-a.offset);var e="undefined"===typeof b;b="undefined"!==typeof b?b:this.offset;var f=d-b;0=this.array.byteLength)throw Error("Cannot read int8 from "+this+" at "+a+": Capacity overflow");return this.view.getInt8(a)};c.prototype.writeByte=c.prototype.writeInt8;c.prototype.readByte=c.prototype.readInt8;c.prototype.writeUint8=function(a,b){b="undefined"!==typeof b?b:(this.offset+=1)-1;this.ensureCapacity(b+1);this.view.setUint8(b,a);return this}; -c.prototype.readUint8=function(a){a="undefined"!==typeof a?a:(this.offset+=1)-1;if(a+1>this.array.byteLength)throw Error("Cannot read uint8 from "+this+" at "+a+": Capacity overflow");return this.view.getUint8(a)};c.prototype.writeInt16=function(a,b){b="undefined"!==typeof b?b:(this.offset+=2)-2;this.ensureCapacity(b+2);this.view.setInt16(b,a,this.littleEndian);return this};c.prototype.readInt16=function(a){a="undefined"!==typeof a?a:(this.offset+=2)-2;if(a+2>this.array.byteLength)throw Error("Cannot read int16 from "+ -this+" at "+a+": Capacity overflow");return this.view.getInt16(a,this.littleEndian)};c.prototype.writeShort=c.prototype.writeInt16;c.prototype.readShort=c.prototype.readInt16;c.prototype.writeUint16=function(a,b){b="undefined"!==typeof b?b:(this.offset+=2)-2;this.ensureCapacity(b+2);this.view.setUint16(b,a,this.littleEndian);return this};c.prototype.readUint16=function(a){a="undefined"!==typeof a?a:(this.offset+=2)-2;if(a+2>this.array.byteLength)throw Error("Cannot read int16 from "+this+" at "+a+ -": Capacity overflow");return this.view.getUint16(a,this.littleEndian)};c.prototype.writeInt32=function(a,b){b="undefined"!==typeof b?b:(this.offset+=4)-4;this.ensureCapacity(b+4);this.view.setInt32(b,a,this.littleEndian);return this};c.prototype.readInt32=function(a){a="undefined"!==typeof a?a:(this.offset+=4)-4;if(a+4>this.array.byteLength)throw Error("Cannot read int32 from "+this+" at "+a+": Capacity overflow");return this.view.getInt32(a,this.littleEndian)};c.prototype.writeInt=c.prototype.writeInt32; -c.prototype.readInt=c.prototype.readInt32;c.prototype.writeUint32=function(a,b){b="undefined"!=typeof b?b:(this.offset+=4)-4;this.ensureCapacity(b+4);this.view.setUint32(b,a,this.littleEndian);return this};c.prototype.readUint32=function(a){a="undefined"!==typeof a?a:(this.offset+=4)-4;if(a+4>this.array.byteLength)throw Error("Cannot read uint32 from "+this+" at "+a+": Capacity overflow");return this.view.getUint32(a,this.littleEndian)};c.prototype.writeFloat32=function(a,b){b="undefined"!==typeof b? -b:(this.offset+=4)-4;this.ensureCapacity(b+4);this.view.setFloat32(b,a,this.littleEndian);return this};c.prototype.readFloat32=function(a){a="undefined"!==typeof a?a:(this.offset+=4)-4;if(null===this.array||a+4>this.array.byteLength)throw Error("Cannot read float32 from "+this+" at "+a+": Capacity overflow");return this.view.getFloat32(a,this.littleEndian)};c.prototype.writeFloat=c.prototype.writeFloat32;c.prototype.readFloat=c.prototype.readFloat32;c.prototype.writeFloat64=function(a,b){b="undefined"!== -typeof b?b:(this.offset+=8)-8;this.ensureCapacity(b+8);this.view.setFloat64(b,a,this.littleEndian);return this};c.prototype.readFloat64=function(a){a="undefined"!==typeof a?a:(this.offset+=8)-8;if(null===this.array||a+8>this.array.byteLength)throw Error("Cannot read float64 from "+this+" at "+a+": Capacity overflow");return this.view.getFloat64(a,this.littleEndian)};c.prototype.writeDouble=c.prototype.writeFloat64;c.prototype.readDouble=c.prototype.readFloat64;l&&(c.prototype.writeInt64=function(a, -b){b="undefined"!==typeof b?b:(this.offset+=8)-8;"object"===typeof a&&a instanceof l||(a=l.fromNumber(a,!1));this.ensureCapacity(b+8);this.littleEndian?(this.view.setInt32(b,a.getLowBits(),!0),this.view.setInt32(b+4,a.getHighBits(),!0)):(this.view.setInt32(b,a.getHighBits(),!1),this.view.setInt32(b+4,a.getLowBits(),!1));return this},c.prototype.readInt64=function(a){a="undefined"!==typeof a?a:(this.offset+=8)-8;if(null===this.array||a+8>this.array.byteLength)throw this.offset-=8,Error("Cannot read int64 from "+ -this+" at "+a+": Capacity overflow");return this.littleEndian?l.fromBits(this.view.getInt32(a,!0),this.view.getInt32(a+4,!0),!1):l.fromBits(this.view.getInt32(a+4,!1),this.view.getInt32(a,!1),!1)},c.prototype.writeUint64=function(a,b){b="undefined"!==typeof b?b:(this.offset+=8)-8;"object"===typeof a&&a instanceof l||(a=l.fromNumber(a,!0));this.ensureCapacity(b+8);this.littleEndian?(this.view.setUint32(b,a.getLowBitsUnsigned(),!0),this.view.setUint32(b+4,a.getHighBitsUnsigned(),!0)):(this.view.setUint32(b, -a.getHighBitsUnsigned(),!1),this.view.setUint32(b+4,a.getLowBitsUnsigned(),!1));return this},c.prototype.readUint64=function(a){a="undefined"!==typeof a?a:(this.offset+=8)-8;if(null===this.array||a+8>this.array.byteLength)throw this.offset-=8,Error("Cannot read int64 from "+this+" at "+a+": Capacity overflow");return this.littleEndian?l.fromBits(this.view.getUint32(a,!0),this.view.getUint32(a+4,!0),!0):l.fromBits(this.view.getUint32(a+4,!1),this.view.getUint32(a,!1),!0)},c.prototype.writeLong=c.prototype.writeInt64, -c.prototype.readLong=c.prototype.readInt64);c.MAX_VARINT32_BYTES=5;c.prototype.writeVarint32=function(a,b){var d="undefined"===typeof b;b="undefined"!==typeof b?b:this.offset;a>>>=0;this.ensureCapacity(b+c.calculateVarint32(a));var e=this.view,f=0;e.setUint8(b,a|128);128<=a?(e.setUint8(b+1,a>>7|128),16384<=a?(e.setUint8(b+2,a>>14|128),2097152<=a?(e.setUint8(b+3,a>>21|128),268435456<=a?(e.setUint8(b+4,a>>28&127),f=5):(e.setUint8(b+3,e.getUint8(b+3)&127),f=4)):(e.setUint8(b+2,e.getUint8(b+2)&127),f= -3)):(e.setUint8(b+1,e.getUint8(b+1)&127),f=2)):(e.setUint8(b,e.getUint8(b)&127),f=1);return d?(this.offset+=f,this):f};c.prototype.readVarint32=function(a){var b="undefined"===typeof a;a="undefined"!==typeof a?a:this.offset;var d=0,e,f=this.view,g=0;do e=f.getUint8(a+d),d>>0),++d;while(e&128);g|=0;return b?(this.offset+=d,g):{value:g,length:d}};c.prototype.writeZigZagVarint32=function(a,b){return this.writeVarint32(c.zigZagEncode32(a),b)};c.prototype.readZigZagVarint32= -function(a){a=this.readVarint32(a);return"object"===typeof a?(a.value=c.zigZagDecode32(a.value),a):c.zigZagDecode32(a)};c.MAX_VARINT64_BYTES=10;l&&(c.prototype.writeVarint64=function(a,b){var d="undefined"===typeof b;b="undefined"!==typeof b?b:this.offset;"object"===typeof a&&a instanceof l||(a=l.fromNumber(a,!1));var e=a.toInt()>>>0,f=a.shiftRightUnsigned(28).toInt()>>>0,g=a.shiftRightUnsigned(56).toInt()>>>0,k=c.calculateVarint64(a);this.ensureCapacity(b+k);var h=this.view;switch(k){case 10:h.setUint8(b+ -9,g>>>7|128);case 9:h.setUint8(b+8,g|128);case 8:h.setUint8(b+7,f>>>21|128);case 7:h.setUint8(b+6,f>>>14|128);case 6:h.setUint8(b+5,f>>>7|128);case 5:h.setUint8(b+4,f|128);case 4:h.setUint8(b+3,e>>>21|128);case 3:h.setUint8(b+2,e>>>14|128);case 2:h.setUint8(b+1,e>>>7|128);case 1:h.setUint8(b+0,e|128)}h.setUint8(b+k-1,h.getUint8(b+k-1)&127);return d?(this.offset+=k,this):k},c.prototype.readVarint64=function(a){var b="undefined"===typeof a,d=a="undefined"!==typeof a?a:this.offset,c=this.view,f,g=0, -k=0,h;h=c.getUint8(a++);f=h&127;if(h&128&&(h=c.getUint8(a++),f|=(h&127)<<7,h&128&&(h=c.getUint8(a++),f|=(h&127)<<14,h&128&&(h=c.getUint8(a++),f|=(h&127)<<21,h&128&&(h=c.getUint8(a++),g=h&127,h&128&&(h=c.getUint8(a++),g|=(h&127)<<7,h&128&&(h=c.getUint8(a++),g|=(h&127)<<14,h&128&&(h=c.getUint8(a++),g|=(h&127)<<21,h&128&&(h=c.getUint8(a++),k=h&127,h&128&&(h=c.getUint8(a++),k|=(h&127)<<7,h&128))))))))))throw Error("Data must be corrupt: Buffer overrun");c=l.from28Bits(f,g,k,!1);return b?(this.offset= -a,c):{value:c,length:a-d}},c.prototype.writeZigZagVarint64=function(a,b){return this.writeVarint64(c.zigZagEncode64(a),b)},c.prototype.readZigZagVarint64=function(a){a=this.readVarint64(a);return"object"!==typeof a||a instanceof l?c.zigZagDecode64(a):(a.value=c.zigZagDecode64(a.value),a)});c.prototype.writeVarint=c.prototype.writeVarint32;c.prototype.readVarint=c.prototype.readVarint32;c.prototype.writeZigZagVarint=c.prototype.writeZigZagVarint32;c.prototype.readZigZagVarint=c.prototype.readZigZagVarint32; -c.calculateVarint32=function(a){a>>>=0;return 128>a?1:16384>a?2:2097152>a?3:268435456>a?4:5};l&&(c.calculateVarint64=function(a){"object"===typeof a&&a instanceof l||(a=l.fromNumber(a,!1));var b=a.toInt()>>>0,d=a.shiftRightUnsigned(28).toInt()>>>0;a=a.shiftRightUnsigned(56).toInt()>>>0;return 0==a?0==d?16384>b?128>b?1:2:2097152>b?3:4:16384>d?128>d?5:6:2097152>d?7:8:128>a?9:10});c.zigZagEncode32=function(a){return((a|=0)<<1^a>>31)>>>0};c.zigZagDecode32=function(a){return a>>>1^-(a&1)|0};l&&(c.zigZagEncode64= -function(a){"object"===typeof a&&a instanceof l?a.unsigned&&(a=a.toSigned()):a=l.fromNumber(a,!1);return a.shiftLeft(1).xor(a.shiftRight(63)).toUnsigned()},c.zigZagDecode64=function(a){"object"===typeof a&&a instanceof l?a.unsigned||(a=a.toUnsigned()):a=l.fromNumber(a,!0);return a.shiftRightUnsigned(1).xor(a.and(l.ONE).toSigned().negate()).toSigned()});c.decodeUTF8Char=function(a,b){var d=a.readUint8(b),c,f,g,k,h,l=b;if(0==(d&128))b+=1;else if(192==(d&224))c=a.readUint8(b+1),d=(d&31)<<6|c&63,b+=2; -else if(224==(d&240))c=a.readUint8(b+1),f=a.readUint8(b+2),d=(d&15)<<12|(c&63)<<6|f&63,b+=3;else if(240==(d&248))c=a.readUint8(b+1),f=a.readUint8(b+2),g=a.readUint8(b+3),d=(d&7)<<18|(c&63)<<12|(f&63)<<6|g&63,b+=4;else if(248==(d&252))c=a.readUint8(b+1),f=a.readUint8(b+2),g=a.readUint8(b+3),k=a.readUint8(b+4),d=(d&3)<<24|(c&63)<<18|(f&63)<<12|(g&63)<<6|k&63,b+=5;else if(252==(d&254))c=a.readUint8(b+1),f=a.readUint8(b+2),g=a.readUint8(b+3),k=a.readUint8(b+4),h=a.readUint8(b+5),d=(d&1)<<30|(c&63)<<24| -(f&63)<<18|(g&63)<<12|(k&63)<<6|h&63,b+=6;else throw Error("Cannot decode UTF8 character at offset "+b+": charCode (0x"+d.toString(16)+") is invalid");return{"char":d,length:b-l}};c.encodeUTF8Char=function(a,b,c){var e=c;if(0>a)throw Error("Cannot encode UTF8 character: charCode ("+a+") is negative");if(128>a)b.writeUint8(a&127,c),c+=1;else if(2048>a)b.writeUint8(a>>6&31|192,c).writeUint8(a&63|128,c+1),c+=2;else if(65536>a)b.writeUint8(a>>12&15|224,c).writeUint8(a>>6&63|128,c+1).writeUint8(a&63|128, -c+2),c+=3;else if(2097152>a)b.writeUint8(a>>18&7|240,c).writeUint8(a>>12&63|128,c+1).writeUint8(a>>6&63|128,c+2).writeUint8(a&63|128,c+3),c+=4;else if(67108864>a)b.writeUint8(a>>24&3|248,c).writeUint8(a>>18&63|128,c+1).writeUint8(a>>12&63|128,c+2).writeUint8(a>>6&63|128,c+3).writeUint8(a&63|128,c+4),c+=5;else if(2147483648>a)b.writeUint8(a>>30&1|252,c).writeUint8(a>>24&63|128,c+1).writeUint8(a>>18&63|128,c+2).writeUint8(a>>12&63|128,c+3).writeUint8(a>>6&63|128,c+4).writeUint8(a&63|128,c+5),c+=6;else throw Error("Cannot encode UTF8 character: charCode (0x"+ -a.toString(16)+") is too large (>= 0x80000000)");return c-e};c.calculateUTF8Char=function(a){if(0>a)throw Error("Cannot calculate length of UTF8 character: charCode ("+a+") is negative");if(128>a)return 1;if(2048>a)return 2;if(65536>a)return 3;if(2097152>a)return 4;if(67108864>a)return 5;if(2147483648>a)return 6;throw Error("Cannot calculate length of UTF8 character: charCode (0x"+a.toString(16)+") is too large (>= 0x80000000)");};c.a=function(a){a=""+a;for(var b=0,d=0,e=a.length;dg?a.readUint8(g++):0,e=a.length>g?a.readUint8(g++):0,f=b<<16|d<<8|e,b=f>>18&63,d=f>>12&63,e=f>>6&63,f&=63,h[k++]=m.charAt(b)+m.charAt(d)+m.charAt(e)+m.charAt(f);while(gd||0>e||0>g||0>k)throw Error("Illegal argument: Not a valid base64 encoded string");f=d<<18|e<<12|g<<6|k;d=f>>16&255;e=f>>8&255;f&=255;64==g?l.writeUint8(d):64==k?l.writeUint8(d).writeUint8(e):l.writeUint8(d).writeUint8(e).writeUint8(f)}while(h< -a.length);return l.flip()};c.encodeHex=function(a){a instanceof c?a.lengthb.length&&(b="0"+b),d.push(b);return d.join("")};c.decodeHex=function(a,b){if("string"!==typeof a)throw Error("Illegal argument: Not a string");if(0!==a.length%2)throw Error("Illegal argument: Not a hex encoded string");for(var d=new c(a.length/2,b),e=0,f=a.length;e< -f;e+=2)d.writeUint8(parseInt(a.substring(e,e+2),16));return d.flip()};c.encodeBinary=function(a){a instanceof c?a.lengtha?"+":"")+b-a)+" bytes");return d?(this.offset=b,f):{string:f,length:b-g}};c.prototype.writeLString=function(a,b){a= -""+a;var d="undefined"===typeof b;b="undefined"!==typeof b?b:this.offset;var e=c.encodeUTF8Char(a.length,this,b),e=e+this.writeUTF8String(a,b+e);return d?(this.offset+=e,this):e};c.prototype.readLString=function(a){var b="undefined"===typeof a;a="undefined"!==typeof a?a:this.offset;var d=c.decodeUTF8Char(this,a);a=this.readUTF8String(d["char"],a+d.length);return b?(this.offset+=d.length+a.length,a.string):{string:a.string,length:d.length+a.length}};c.prototype.writeVString=function(a,b){a=""+a;var d= -"undefined"===typeof b;b="undefined"!==typeof b?b:this.offset;var e=this.writeVarint32(c.a(a),b),e=e+this.writeUTF8String(a,b+e);return d?(this.offset+=e,this):e};c.prototype.readVString=function(a){var b="undefined"===typeof a;a="undefined"!==typeof a?a:this.offset;var c=this.readVarint32(a);a=this.readUTF8StringBytes(c.value,a+c.length);return b?(this.offset+=c.length+a.length,a.string):{string:a.string,length:c.length+a.length}};c.prototype.writeCString=function(a,b){var c="undefined"===typeof b; -b="undefined"!==typeof b?b:this.offset;var e=this.writeUTF8String(""+a,b);this.writeUint8(0,b+e);return c?(this.offset+=e+1,this):e+1};c.prototype.readCString=function(a){var b="undefined"===typeof a;a="undefined"!==typeof a?a:this.offset;var d,e="",f=a;do d=c.decodeUTF8Char(this,a),a+=d.length,0!=d["char"]&&(e+=String.fromCharCode(d["char"]));while(0!=d["char"]);return b?(this.offset=a,e):{string:e,length:a-f}};c.prototype.writeJSON=function(a,b,c){c="function"===typeof c?c:JSON.stringify;return this.writeLString(c(a), -b)};c.prototype.readJSON=function(a,b){b="function"===typeof b?b:JSON.parse;var c=this.readLString(a);return"string"===typeof c?b(c):{data:b(c.string),length:c.length}};c.prototype.toColumns=function(a){if(null===this.array)return"DESTROYED";a="undefined"!==typeof a?parseInt(a,10):16;1>a&&(a=16);for(var b="",c=[],e,f=this.view,b=0==this.offset&&0==this.length?b+"|":0==this.length?b+">":0==this.offset?b+"<":b+" ",g=0,k=this.array.byteLength;ge.length&&(e="0"+e);b+=e;b=g+1==this.offset&&g+1==this.length?b+"|":g+1==this.offset?b+"<":g+1==this.length?b+">":b+" "}" "!=b&&c.push(b);g=0;for(k=c.length;ge?String.fromCharCode(e):".";""!=b&&(c[h]+=" "+b);return c.join("\n")};c.prototype.printDebug=function(a){"function"!==typeof a&&(a= -console.log.bind(console));a((null!=this.array?"ByteBuffer(offset="+this.offset+",markedOffset="+this.markedOffset+",length="+this.length+",capacity="+this.array.byteLength+")":"ByteBuffer(DESTROYED)")+"\n-------------------------------------------------------------------\n"+this.toColumns()+"\n")};c.prototype.toHex=function(a){var b="",d=this.view,e,f;if(a){if(null===this.array)return"DESTROYED";b=0==this.offset&&0==this.length?b+"|":0==this.length?b+">":0==this.offset?b+"<":b+" ";e=0;for(f=this.array.byteLength;e< -f;++e)a=d.getUint8(e).toString(16).toUpperCase(),2>a.length&&(a="0"+a),b+=a,b=e+1===this.offset&&e+1===this.length?b+"|":e+1==this.offset?b+"<":e+1==this.length?b+">":b+" ";return b}return c.encodeHex(this)};c.prototype.toBinary=function(){return c.encodeBinary(this)};c.prototype.toBase64=function(){return null===this.array||this.offset>=this.length?"":c.encode64(this)};c.prototype.toUTF8=function(){return null===this.array||this.offset>=this.length?"":this.readUTF8StringBytes(this.length-this.offset, -this.offset).string};c.prototype.toString=function(a){switch(a||""){case "utf8":return this.toUTF8();case "base64":return this.toBase64();case "hex":return this.toHex();case "binary":return this.toBinary();case "debug":return this.toHex(!0);default:return null===this.array?"ByteBuffer(DESTROYED)":"ByteBuffer(offset="+this.offset+",markedOffset="+this.markedOffset+",length="+this.length+",capacity="+this.array.byteLength+")"}};c.prototype.toArrayBuffer=function(a){if(null===this.array)return null; -var b=this.clone();b.offset>b.length&&b.flip();var c=!1;if(0b)var c=a,a=b,b=c;return new p((new Uint8Array(this.array)).subarray(a,b))});return c}"undefined"!==typeof module&&module.exports?module.exports=q(require("long")):"undefined"!==typeof define&&define.amd?define("ByteBuffer",["Math/Long"],function(l){return q(l)}): -(n.dcodeIO||(n.dcodeIO={}),n.dcodeIO.ByteBuffer=q(n.dcodeIO.Long))})(this); + Copyright 2013 Daniel Wirtz + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +/** + * @license ByteBuffer.js (c) 2013 Daniel Wirtz + * Released under the Apache License, Version 2.0 + * see: https://github.com/dcodeIO/ByteBuffer.js for details + */ // +(function(global) { + "use strict"; + + // Note that this library carefully avoids using the array access operator + // (i.e. buffer[x]) on ArrayBufferView subclasses (e.g. Uint8Array), and + // uses DataView instead. This is required for IE 8 compatibility. + + /** + * @param {Function=} Long + * @returns {Function} + * @inner + */ + function loadByteBuffer(Long) { + + // Support node's Buffer if available, see http://nodejs.org/api/buffer.html + var Buffer = null; + if (typeof require === 'function') { + try { + var nodeBuffer = require("buffer"); + Buffer = nodeBuffer && typeof nodeBuffer['Buffer'] === 'function' && + typeof nodeBuffer['Buffer']['isBuffer'] === 'function' ? nodeBuffer['Buffer'] : null; + } catch (e) {} + } + + /** + * Constructs a new ByteBuffer. + * @class A full-featured ByteBuffer implementation in JavaScript using typed arrays. + * @exports ByteBuffer + * @param {number=} capacity Initial capacity. Defaults to {@link ByteBuffer.DEFAULT_CAPACITY}. + * @param {boolean=} littleEndian `true` to use little endian multi byte values, defaults to `false` for big + * endian. + * @param {boolean=} sparse If set to `true`, a ByteBuffer with array=view=null will be created which have to be + * set manually afterwards. Defaults to `false`. + * @expose + */ + var ByteBuffer = function(capacity, littleEndian, sparse) { + capacity = typeof capacity !== 'undefined' ? parseInt(capacity, 10) : ByteBuffer.DEFAULT_CAPACITY; + if (capacity < 1) capacity = ByteBuffer.DEFAULT_CAPACITY; + + /** + * Backing ArrayBuffer. + * @type {?ArrayBuffer} + * @expose + */ + this.array = sparse ? null : new ArrayBuffer(capacity); + + /** + * DataView to mess with the ArrayBuffer. + * @type {?DataView} + * @expose + */ + this.view = sparse ? null : new DataView(this.array); + + /** + * Current read/write offset. Length- and capacity-independent index. Contents are the bytes between offset + * and length, which are both absolute indexes. There is no capacity property, use + * {@link ByteBuffer#capacity} instead. + * @type {number} + * @expose + */ + this.offset = 0; + + /** + * Marked offset set through {@link ByteBuffer#mark}. Defaults to `-1` (no marked offset). + * @type {number} + * @expose + */ + this.markedOffset = -1; + + /** + * Length of the contained data. Offset- and capacity-independent index. Contents are the bytes between + * offset and length, which are both absolute indexes. There is no capacity property, use + * {@link ByteBuffer#capacity} instead. + * @type {number} + * @expose + */ + this.length = 0; + + /** + * Whether to use little endian multi byte values, defaults to `false` for big endian. + * @type {boolean} + * @expose + */ + this.littleEndian = typeof littleEndian != 'undefined' ? !!littleEndian : false; + }; + + /** + * Version string. + * @type {string} + * @const + * @expose + */ + ByteBuffer.VERSION = "2.3.1"; + + /** + * Default buffer capacity of `16`. The ByteBuffer will be automatically resized by a factor of 2 if required. + * @type {number} + * @const + * @expose + */ + ByteBuffer.DEFAULT_CAPACITY = 16; + + /** + * Little endian constant for usage in constructors instead of a boolean value. Evaluates to `true`. + * @type {boolean} + * @const + * @expose + */ + ByteBuffer.LITTLE_ENDIAN = true; + + /** + * Big endian constant for usage in constructors instead of a boolean value. Evaluates to `false`. + * @type {boolean} + * @const + * @expose + */ + ByteBuffer.BIG_ENDIAN = false; + + /** + * Long class for int64 support. May be `null` if the Long class has not been loaded and int64 support is + * not available. + * @type {?Long} + * @const + * @expose + */ + ByteBuffer.Long = Long || null; + + /** + * Tests if the specified type is a ByteBuffer or ByteBuffer-like. + * @param {*} bb ByteBuffer to test + * @returns {boolean} true if it is a ByteBuffer or ByteBuffer-like, otherwise false + * @expose + */ + ByteBuffer.isByteBuffer = function(bb) { + return bb && ( + (bb instanceof ByteBuffer) || ( + typeof bb === 'object' && + (bb.array === null || bb.array instanceof ArrayBuffer) && + (bb.view === null || bb.view instanceof DataView) && + typeof bb.offset === 'number' && + typeof bb.markedOffset === 'number' && + typeof bb.length === 'number' && + typeof bb.littleEndian === 'boolean' + ) + ); + }; + + /** + * Allocates a new ByteBuffer. + * @param {number=} capacity Initial capacity. Defaults to {@link ByteBuffer.DEFAULT_CAPACITY}. + * @param {boolean=} littleEndian `true` to use little endian multi byte values, defaults to `false` for big + * endian. + * @returns {!ByteBuffer} + * @expose + */ + ByteBuffer.allocate = function(capacity, littleEndian) { + return new ByteBuffer(capacity, littleEndian); + }; + + /** + * Converts a node.js <= 0.8 Buffer to an ArrayBuffer. + * @param {!Buffer} b Buffer to convert + * @returns {?ArrayBuffer} Converted buffer + * @inner + */ + function b2ab(b) { + var ab = new ArrayBuffer(b.length), + view = new Uint8Array(ab); + for (var i=0, k=b.length; i < k; ++i) view[i] = b[i]; + return ab; + } + + /** + * Wraps an ArrayBuffer, any object containing an ArrayBuffer, a node buffer or a string. Sets the created + * ByteBuffer's offset to 0 and its length to the wrapped object's byte length. + * @param {!ArrayBuffer|!Buffer|!{array: !ArrayBuffer}|!{buffer: !ArrayBuffer}|string} buffer Anything that can + * be wrapped + * @param {(string|boolean)=} enc String encoding if a string is provided (hex, utf8, binary, defaults to base64) + * @param {boolean=} littleEndian `true` to use little endian multi byte values, defaults to `false` for big + * endian. + * @returns {!ByteBuffer} + * @throws {Error} If the specified object cannot be wrapped + * @expose + */ + ByteBuffer.wrap = function(buffer, enc, littleEndian) { + if (typeof enc === 'boolean') { + littleEndian = enc; + enc = "utf8"; + } + // Wrap a string + if (typeof buffer === 'string') { + switch (enc) { + case "base64": + return ByteBuffer.decode64(buffer, littleEndian); + case "hex": + return ByteBuffer.decodeHex(buffer, littleEndian); + case "binary": + return ByteBuffer.decodeBinary(buffer, littleEndian); + default: + return new ByteBuffer(ByteBuffer.DEFAULT_CAPACITY, littleEndian).writeUTF8String(buffer).flip(); + } + } + var b; + // Wrap Buffer + if (Buffer && Buffer.isBuffer(buffer)) { + b = new Uint8Array(buffer).buffer; // noop on node <= 0.8 + buffer = (b === buffer) ? b2ab(buffer) : b; + } + // Refuse to wrap anything that's null or not an object + if (buffer === null || typeof buffer !== 'object') { + throw(new Error("Cannot wrap null or non-object")); + } + // Wrap ByteBuffer by cloning (preserve offsets) + if (ByteBuffer.isByteBuffer(buffer)) { + return ByteBuffer.prototype.clone.call(buffer); // Also makes ByteBuffer-like a ByteBuffer + } + // Wrap any object that is or contains an ArrayBuffer + if (!!buffer["array"]) { + buffer = buffer["array"]; + } else if (!!buffer["buffer"]) { + buffer = buffer["buffer"]; + } + if (!(buffer instanceof ArrayBuffer)) { + throw(new Error("Cannot wrap buffer of type "+typeof(buffer)+", "+buffer.constructor.name)); + } + b = new ByteBuffer(0, littleEndian, true); + b.array = buffer; + b.view = b.array.byteLength > 0 ? new DataView(b.array) : null; + b.offset = 0; + b.length = buffer.byteLength; + return b; + }; + + /** + * Switches little endian byte order. + * @param {boolean=} littleEndian Defaults to `true`, otherwise uses big endian + * @returns {!ByteBuffer} this + * @expose + */ + ByteBuffer.prototype.LE = function(littleEndian) { + this.littleEndian = typeof littleEndian !== 'undefined' ? !!littleEndian : true; + return this; + }; + + /** + * Switches big endian byte order. + * @param {boolean=} bigEndian Defaults to `true`, otherwise uses little endian + * @returns {!ByteBuffer} this + * @expose + */ + ByteBuffer.prototype.BE = function(bigEndian) { + this.littleEndian = typeof bigEndian !== 'undefined' ? !bigEndian : false; + return this; + }; + + /** + * Resizes the ByteBuffer to the given capacity. Will do nothing if already that large or larger. + * @param {number} capacity New capacity + * @returns {!ByteBuffer} this + * @expose + */ + ByteBuffer.prototype.resize = function(capacity) { + if (capacity < 1) return false; + if (this.array === null) { // Silently recreate + this.array = new ArrayBuffer(capacity); + this.view = new DataView(this.array); + } + if (this.array.byteLength < capacity) { + var src = this.array; + var srcView = new Uint8Array(src); + var dst = new ArrayBuffer(capacity); + var dstView = new Uint8Array(dst); + dstView.set(srcView); + this.array = dst; + this.view = new DataView(dst); + } + return this; + }; + + /** + * Slices the ByteBuffer. This is independent of the ByteBuffer's actual offsets. Does not compact the underlying + * ArrayBuffer (use {@link ByteBuffer#compact} or {@link ByteBuffer.wrap} instead). + * @param {number=} begin Begin offset, defaults to {@link ByteBuffer#offset}. + * @param {number=} end End offset, defaults to {@link ByteBuffer#length}. + * @returns {!ByteBuffer} Clone of this ByteBuffer with slicing applied, backed by the same ArrayBuffer + * @throws {Error} If the buffer cannot be sliced + * @expose + */ + ByteBuffer.prototype.slice = function(begin, end) { + if (this.array == null) { + throw(new Error(this+" cannot be sliced: Already destroyed")); + } + if (typeof begin === 'undefined') begin = this.offset; + if (typeof end === 'undefined') end = this.length; + if (end <= begin) { + var t = end; end = begin; begin = t; + } + if (begin < 0 || begin > this.array.byteLength || end < 1 || end > this.array.byteLength) { + throw(new Error(this+" cannot be sliced: Index out of bounds (0-"+this.array.byteLength+" -> "+begin+"-"+end+")")); + } + var b = this.clone(); + b.offset = begin; + b.length = end; + return b; + }; + + /** + * Makes sure that the specified capacity is available. If the current capacity is exceeded, it will be doubled. + * If double the previous capacity is less than the required capacity, the required capacity will be used. + * @param {number} capacity Required capacity + * @returns {!ByteBuffer} this + * @expose + */ + ByteBuffer.prototype.ensureCapacity = function(capacity) { + if (this.array === null) + return this.resize(capacity); + if (this.array.byteLength < capacity) + return this.resize(this.array.byteLength*2 >= capacity ? this.array.byteLength*2 : capacity); + return this; + }; + + /** + * Makes the buffer ready for a new sequence of write or relative read operations. Sets `length=offset` and + * `offset=0`. Always make sure to flip a buffer when all relative writing operations are complete. + * @returns {!ByteBuffer} this + * @expose + */ + ByteBuffer.prototype.flip = function() { + this.length = this.array == null ? 0 : this.offset; + this.offset = 0; + return this; + }; + + /** + * Marks an offset to be used with {@link ByteBuffer#reset}. + * @param {number=} offset Offset to mark. Defaults to {@link ByteBuffer#offset}. + * @returns {!ByteBuffer} this + * @throws {Error} If the mark cannot be set + * @see ByteBuffer#reset + * @expose + */ + ByteBuffer.prototype.mark = function(offset) { + if (this.array == null) { + throw(new Error(this+" cannot be marked: Already destroyed")); + } + offset = typeof offset !== 'undefined' ? parseInt(offset, 10) : this.offset; + if (offset < 0 || offset > this.array.byteLength) { + throw(new Error(this+" cannot be marked: Offset to mark is less than 0 or bigger than the capacity ("+this.array.byteLength+"): "+offset)); + } + this.markedOffset = offset; + return this; + }; + + /** + * Resets the ByteBuffer. If an offset has been marked through {@link ByteBuffer#mark} before, the offset will + * be set to the marked offset and the marked offset will be discarded. Length will not be altered. If there is + * no marked offset, sets `offset=0` and `length=0`. + * @returns {!ByteBuffer} this + * @see ByteBuffer#mark + * @expose + */ + ByteBuffer.prototype.reset = function() { + if (this.array === null) { + throw(new Error(this+" cannot be reset: Already destroyed")); + } + if (this.markedOffset >= 0) { + this.offset = this.markedOffset; + this.markedOffset = -1; + } else { + this.offset = 0; + this.length = 0; + } + return this; + }; + + /** + * Clones this ByteBuffer. The returned cloned ByteBuffer shares the same backing array but will have its own + * offsets. + * @returns {!ByteBuffer} Clone + * @expose + */ + ByteBuffer.prototype.clone = function() { + var b = new ByteBuffer(-1, this.littleEndian, /* no init, undocumented */ true); + b.array = this.array; + b.view = this.view; + b.offset = this.offset; + b.markedOffset = this.markedOffset; + b.length = this.length; + return b; + }; + + /** + * Copies this ByteBuffer. The copy has its own backing array and uses the same offsets as this one. + * @returns {!ByteBuffer} Copy + * @expose + */ + ByteBuffer.prototype.copy = function() { + if (this.array == null) { + return this.clone(); + } + var b = new ByteBuffer(this.array.byteLength, this.littleEndian); + var src = new Uint8Array(this.array); + var dst = new Uint8Array(b.array); + dst.set(src); + b.offset = this.offset; + b.markedOffset = this.markedOffset; + b.length = this.length; + return b; + }; + + /** + * Gets the number of remaining readable bytes. Contents are the bytes between offset and length, so this + * returns `length-offset`. + * @returns {number} Remaining readable bytes. May be negative if `offset>length`. + * @expose + */ + ByteBuffer.prototype.remaining = function() { + if (this.array === null) return 0; + return this.length - this.offset; + }; + + /** + * Gets the capacity of the backing buffer. This is independent from {@link ByteBuffer#length} and returns the + * size of the entire backing array. + * @returns {number} Capacity of the backing array or 0 if destroyed + * @expose + */ + ByteBuffer.prototype.capacity = function() { + return this.array != null ? this.array.byteLength : 0; + }; + + /** + * Compacts the ByteBuffer to be backed by an ArrayBuffer of its actual length. Will set `offset=0` and + * `length=capacity`. + * @returns {!ByteBuffer} this + * @throws {Error} If the buffer cannot be compacted + * @expose + */ + ByteBuffer.prototype.compact = function() { + if (this.array == null) { + throw(new Error(this+" cannot be compacted: Already destroyed")); + } + if (this.offset > this.length) { + this.flip(); + } + if (this.offset === this.length) { + this.array = new ArrayBuffer(0); + this.view = null; // A DataView on a zero-length AB would throw + return this; + } + if (this.offset === 0 && this.length === this.array.byteLength) { + return this; // Already compacted + } + var srcView = new Uint8Array(this.array); + var dst = new ArrayBuffer(this.length-this.offset); + var dstView = new Uint8Array(dst); + dstView.set(srcView.subarray(this.offset, this.length)); + this.array = dst; + if (this.markedOffset >= this.offset) { + this.markedOffset -= this.offset; + } else { + this.markedOffset = -1; + } + this.offset = 0; + this.length = this.array.byteLength; + return this; + }; + + /** + * Manually destroys the ByteBuffer, releasing references to the backing array. Manually destroying a ByteBuffer + * is usually not required but may be useful in limited memory environments. Most successive operations will + * rise an error until {@link ByteBuffer#resize} or {@link ByteBuffer#ensureCapacity} is called to reinitialize + * the backing array. + * @returns {!ByteBuffer} this + * @expose + */ + ByteBuffer.prototype.destroy = function() { + if (this.array !== null) { + this.array = null; + this.view = null; + this.offset = 0; + this.markedOffset = -1; + this.length = 0; + } + return this; + }; + + /** + * Reverses the backing array and adapts offset and length to retain the same relative position on the reversed + * data in inverse order. Example: "00<01 02>03 04".reverse() = "04 03<02 01>00". Also clears the marked + * offset. + * @returns {!ByteBuffer} this + * @throws {Error} If the buffer is already destroyed + * @expose + */ + ByteBuffer.prototype.reverse = function() { + if (this.array === null) { + throw(new Error(this+" cannot be reversed: Already destroyed")); + } + Array.prototype.reverse.call(new Uint8Array(this.array)); + var o = this.offset; + this.offset = this.array.byteLength - this.length; + this.markedOffset = -1; + this.length = this.array.byteLength - o; + this.view = new DataView(this.array); + return this; + }; + + /** + * Appends another ByteBuffer to this one. Appends only the portion between offset and length of the specified + * ByteBuffer and overwrites any contents behind the specified offset up to the number of bytes contained in + * the specified ByteBuffer. Offset and length of the specified ByteBuffer will remain the same. + * @param {!*} src ByteBuffer or any object that can be wrapped to append + * @param {number=} offset Offset to append at. Defaults to {@link ByteBuffer#offset}. + * @returns {!ByteBuffer} this + * @throws {Error} If the specified buffer is already destroyed + * @expose + */ + ByteBuffer.prototype.append = function(src, offset) { + if (!(src instanceof ByteBuffer)) { + src = ByteBuffer.wrap(src); + } + if (src.array === null) { + throw(new Error(src+" cannot be appended to "+this+": Already destroyed")); + } + var n = src.length - src.offset; + if (n == 0) return this; // Nothing to append + if (n < 0) { + src = src.clone().flip(); + n = src.length - src.offset; + } + offset = typeof offset !== 'undefined' ? offset : (this.offset+=n)-n; + this.ensureCapacity(offset+n); // Reinitializes if required + var srcView = new Uint8Array(src.array); + var dstView = new Uint8Array(this.array); + dstView.set(srcView.subarray(src.offset, src.length), offset); + return this; + }; + + /** + * Prepends another ByteBuffer to this one. Prepends only the portion between offset and length of the specified + * ByteBuffer and overwrites any contents before the specified offsets up to the number of bytes contained in + * the specified ByteBuffer. Offset and length of the specified ByteBuffer will remain the same. + * @param {!*} src ByteBuffer or any object that can be wrapped to prepend + * @param {number=} offset Offset to prepend at. Defaults to {@link ByteBuffer#offset}. + * @returns {!ByteBuffer} this + * @throws {Error} If the specified buffer is already destroyed + * @expose + */ + ByteBuffer.prototype.prepend = function(src, offset) { + if (!(src instanceof ByteBuffer)) { + src = ByteBuffer.wrap(src); + } + if (src.array === null) { + throw(src+" cannot be prepended to "+this+": Already destroyed"); + } + var n = src.length - src.offset; + if (n == 0) return this; // Nothing to prepend + if (n < 0) { + src = src.clone().flip(); + n = src.length - src.offset; + } + var modify = typeof offset === 'undefined'; + offset = typeof offset !== 'undefined' ? offset : this.offset; + var diff = n-offset; + if (diff > 0) { + // Doesn't fit, so maybe resize and move the contents that are already contained + this.ensureCapacity(this.length+diff); + this.append(this, n); + this.offset += diff; + this.length += diff; + this.append(src, 0); + } else { + this.append(src, offset-n); + } + if (modify) { + this.offset -= n; + } + return this; + }; + + /** + * Writes an 8bit signed integer. + * @param {number} value Value + * @param {number=} offset Offset to write to. Will use and advance {@link ByteBuffer#offset} if + * omitted. + * @returns {!ByteBuffer} this + * @expose + */ + ByteBuffer.prototype.writeInt8 = function(value, offset) { + offset = typeof offset != 'undefined' ? offset : (this.offset+=1)-1; + this.ensureCapacity(offset+1); + this.view.setInt8(offset, value); + return this; + }; + + /** + * Reads an 8bit signed integer. + * @param {number=} offset Offset to read from. Will use and advance {@link ByteBuffer#offset} if omitted. + * @returns {number} + * @throws {Error} If offset is out of bounds + * @expose + */ + ByteBuffer.prototype.readInt8 = function(offset) { + offset = typeof offset !== 'undefined' ? offset : (this.offset+=1)-1; + if (offset >= this.array.byteLength) { + throw(new Error("Cannot read int8 from "+this+" at "+offset+": Capacity overflow")); + } + return this.view.getInt8(offset); + }; + + /** + * Writes a byte. This is an alias of {ByteBuffer#writeInt8}. + * @function + * @param {number} value Value to write + * @param {number=} offset Offset to write to. Will use and advance {@link ByteBuffer#offset} if omitted. + * @returns {!ByteBuffer} this + * @expose + */ + ByteBuffer.prototype.writeByte = ByteBuffer.prototype.writeInt8; + + /** + * Reads a byte. This is an alias of {@link ByteBuffer#readInt8}. + * @function + * @param {number=} offset Offset to read from. Will use and advance {@link ByteBuffer#offset} if omitted. + * @returns {number} + * @throws {Error} If offset is out of bounds + * @expose + */ + ByteBuffer.prototype.readByte = ByteBuffer.prototype.readInt8; + + /** + * Writes an 8bit unsigned integer. + * @param {number} value Value to write + * @param {number=} offset Offset to write to. Will use and advance {@link ByteBuffer#offset} if omitted. + * @returns {!ByteBuffer} this + * @expose + */ + ByteBuffer.prototype.writeUint8 = function(value, offset) { + offset = typeof offset !== 'undefined' ? offset : (this.offset+=1)-1; + this.ensureCapacity(offset+1); + this.view.setUint8(offset, value); + return this; + }; + + /** + * Reads an 8bit unsigned integer. + * @param {number=} offset Offset to read from. Will use and advance {@link ByteBuffer#offset} if omitted. + * @returns {number} + * @throws {Error} If offset is out of bounds + * @expose + */ + ByteBuffer.prototype.readUint8 = function(offset) { + offset = typeof offset !== 'undefined' ? offset : (this.offset+=1)-1; + if (offset+1 > this.array.byteLength) { + throw(new Error("Cannot read uint8 from "+this+" at "+offset+": Capacity overflow")); + } + return this.view.getUint8(offset); + }; + + /** + * Writes a 16bit signed integer. + * @param {number} value Value to write + * @param {number=} offset Offset to write to. Will use and advance {@link ByteBuffer#offset} if omitted. + * @returns {!ByteBuffer} this + * @expose + */ + ByteBuffer.prototype.writeInt16 = function(value, offset) { + offset = typeof offset !== 'undefined' ? offset : (this.offset+=2)-2; + this.ensureCapacity(offset+2); + this.view.setInt16(offset, value, this.littleEndian); + return this; + }; + + /** + * Reads a 16bit signed integer. + * @param {number=} offset Offset to read from. Will use and advance {@link ByteBuffer#offset} if omitted. + * @returns {number} + * @throws {Error} If offset is out of bounds + * @expose + */ + ByteBuffer.prototype.readInt16 = function(offset) { + offset = typeof offset !== 'undefined' ? offset : (this.offset+=2)-2; + if (offset+2 > this.array.byteLength) { + throw(new Error("Cannot read int16 from "+this+" at "+offset+": Capacity overflow")); + } + return this.view.getInt16(offset, this.littleEndian); + }; + + /** + * Writes a short value. This is an alias of {@link ByteBuffer#writeInt16}. + * @function + * @param {number} value Value to write + * @param {number=} offset Offset to write to. Will use and advance {@link ByteBuffer#offset} if omitted. + * @returns {!ByteBuffer} this + * @expose + */ + ByteBuffer.prototype.writeShort = ByteBuffer.prototype.writeInt16; + + /** + * Reads a short value. This is an alias of {@link ByteBuffer#readInt16}. + * @function + * @param {number=} offset Offset to read from. Will use and advance {@link ByteBuffer#offset} if omitted. + * @returns {number} + * @throws {Error} If offset is out of bounds + * @expose + */ + ByteBuffer.prototype.readShort = ByteBuffer.prototype.readInt16; + + /** + * Writes a 16bit unsigned integer. + * @param {number} value Value to write + * @param {number=} offset Offset to write to. Will use and advance {@link ByteBuffer#offset} if omitted. + * @returns {!ByteBuffer} this + * @expose + */ + ByteBuffer.prototype.writeUint16 = function(value, offset) { + offset = typeof offset !== 'undefined' ? offset : (this.offset+=2)-2; + this.ensureCapacity(offset+2); + this.view.setUint16(offset, value, this.littleEndian); + return this; + }; + + /** + * Reads a 16bit unsigned integer. + * @param {number=} offset Offset to read from. Will use and advance {@link ByteBuffer#offset} if omitted. + * @returns {number} + * @throws {Error} If offset is out of bounds + * @expose + */ + ByteBuffer.prototype.readUint16 = function(offset) { + offset = typeof offset !== 'undefined' ? offset : (this.offset+=2)-2; + if (offset+2 > this.array.byteLength) { + throw(new Error("Cannot read int16 from "+this+" at "+offset+": Capacity overflow")); + } + return this.view.getUint16(offset, this.littleEndian); + }; + + /** + * Writes a 32bit signed integer. + * @param {number} value Value to write + * @param {number=} offset Offset to write to. Will use and advance {@link ByteBuffer#offset} if omitted. + * @returns {!ByteBuffer} this + * @expose + */ + ByteBuffer.prototype.writeInt32 = function(value, offset) { + offset = typeof offset !== 'undefined' ? offset : (this.offset+=4)-4; + this.ensureCapacity(offset+4); + this.view.setInt32(offset, value, this.littleEndian); + return this; + }; + + /** + * Reads a 32bit signed integer. + * @param {number=} offset Offset to read from. Will use and advance {@link ByteBuffer#offset} if omitted. + * @returns {number} + * @throws {Error} If offset is out of bounds + * @expose + */ + ByteBuffer.prototype.readInt32 = function(offset) { + offset = typeof offset !== 'undefined' ? offset : (this.offset+=4)-4; + if (offset+4 > this.array.byteLength) { + throw(new Error("Cannot read int32 from "+this+" at "+offset+": Capacity overflow")); + } + return this.view.getInt32(offset, this.littleEndian); + }; + + /** + * Writes an integer. This is an alias of {@link ByteBuffer#writeInt32}. + * @function + * @param {number} value Value to write + * @param {number=} offset Offset to write to. Will use and advance {@link ByteBuffer#offset} if omitted. + * @returns {!ByteBuffer} this + * @expose + */ + ByteBuffer.prototype.writeInt = ByteBuffer.prototype.writeInt32; + + /** + * Reads an integer. This is an alias of {@link ByteBuffer#readInt32}. + * @function + * @param {number=} offset Offset to read from. Will use and advance {@link ByteBuffer#offset} if omitted. + * @returns {number} + * @throws {Error} If offset is out of bounds + * @expose + */ + ByteBuffer.prototype.readInt = ByteBuffer.prototype.readInt32; + + /** + * Writes a 32bit unsigned integer. + * @param {number} value Value to write + * @param {number=} offset Offset to write to. Will use and advance {@link ByteBuffer#offset} if omitted. + * @returns {!ByteBuffer} this + * @expose + */ + ByteBuffer.prototype.writeUint32 = function(value, offset) { + offset = typeof offset != 'undefined' ? offset : (this.offset+=4)-4; + this.ensureCapacity(offset+4); + this.view.setUint32(offset, value, this.littleEndian); + return this; + }; + + /** + * Reads a 32bit unsigned integer. + * @param {number=} offset Offset to read from. Will use and advance {@link ByteBuffer#offset} if omitted. + * @returns {number} + * @throws {Error} If offset is out of bounds + * @expose + */ + ByteBuffer.prototype.readUint32 = function(offset) { + offset = typeof offset !== 'undefined' ? offset : (this.offset+=4)-4; + if (offset+4 > this.array.byteLength) { + throw(new Error("Cannot read uint32 from "+this+" at "+offset+": Capacity overflow")); + } + return this.view.getUint32(offset, this.littleEndian); + }; + + /** + * Writes a 32bit float. + * @param {number} value Value to write + * @param {number=} offset Offset to write to. Will use and advance {@link ByteBuffer#offset} if omitted. + * @returns {!ByteBuffer} this + * @expose + */ + ByteBuffer.prototype.writeFloat32 = function(value, offset) { + offset = typeof offset !== 'undefined' ? offset : (this.offset+=4)-4; + this.ensureCapacity(offset+4); + this.view.setFloat32(offset, value, this.littleEndian); + return this; + }; + + /** + * Reads a 32bit float. + * @param {number=} offset Offset to read from. Will use and advance {@link ByteBuffer#offset} if omitted. + * @returns {number} + * @throws {Error} If offset is out of bounds + * @expose + */ + ByteBuffer.prototype.readFloat32 = function(offset) { + offset = typeof offset !== 'undefined' ? offset : (this.offset+=4)-4; + if (this.array === null || offset+4 > this.array.byteLength) { + throw(new Error("Cannot read float32 from "+this+" at "+offset+": Capacity overflow")); + } + return this.view.getFloat32(offset, this.littleEndian); + }; + + /** + * Writes a float. This is an alias of {@link ByteBuffer#writeFloat32}. + * @function + * @param {number} value Value to write + * @param {number=} offset Offset to write to. Will use and advance {@link ByteBuffer#offset} if omitted. + * @returns {!ByteBuffer} this + * @expose + */ + ByteBuffer.prototype.writeFloat = ByteBuffer.prototype.writeFloat32; + + /** + * Reads a float. This is an alias of {@link ByteBuffer#readFloat32}. + * @function + * @param {number=} offset Offset to read from. Will use and advance {@link ByteBuffer#offset} if omitted. + * @returns {number} + * @throws {Error} If offset is out of bounds + * @expose + */ + ByteBuffer.prototype.readFloat = ByteBuffer.prototype.readFloat32; + + /** + * Writes a 64bit float. + * @param {number} value Value to write + * @param {number=} offset Offset to write to. Will use and advance {@link ByteBuffer#offset} if omitted. + * @returns {!ByteBuffer} this + * @expose + */ + ByteBuffer.prototype.writeFloat64 = function(value, offset) { + offset = typeof offset !== 'undefined' ? offset : (this.offset+=8)-8; + this.ensureCapacity(offset+8); + this.view.setFloat64(offset, value, this.littleEndian); + return this; + }; + + /** + * Reads a 64bit float. + * @param {number=} offset Offset to read from. Will use and advance {@link ByteBuffer#offset} if omitted. + * @returns {number} + * @throws {Error} If offset is out of bounds + * @expose + */ + ByteBuffer.prototype.readFloat64 = function(offset) { + offset = typeof offset !== 'undefined' ? offset : (this.offset+=8)-8; + if (this.array === null || offset+8 > this.array.byteLength) { + throw(new Error("Cannot read float64 from "+this+" at "+offset+": Capacity overflow")); + } + return this.view.getFloat64(offset, this.littleEndian); + }; + + /** + * Writes a double. This is an alias of {@link ByteBuffer#writeFloat64}. + * @function + * @param {number} value Value to write + * @param {number=} offset Offset to write to. Will use and advance {@link ByteBuffer#offset} if omitted. + * @returns {!ByteBuffer} this + * @expose + */ + ByteBuffer.prototype.writeDouble = ByteBuffer.prototype.writeFloat64; + + /** + * Reads a double. This is an alias of {@link ByteBuffer#readFloat64}. + * @function + * @param {number=} offset Offset to read from. Will use and advance {@link ByteBuffer#offset} if omitted. + * @returns {number} + * @throws {Error} If offset is out of bounds + * @expose + */ + ByteBuffer.prototype.readDouble = ByteBuffer.prototype.readFloat64; + + // Available with Long.js only + if (Long) { + + /** + * Writes a 64bit integer. Requires Long.js. + * @function + * @param {number|!Long} value Value to write + * @param {number=} offset Offset to write to. Will use and advance {@link ByteBuffer#offset} if omitted. + * @returns {!ByteBuffer} this + * @expose + */ + ByteBuffer.prototype.writeInt64 = function(value, offset) { + offset = typeof offset !== 'undefined' ? offset : (this.offset+=8)-8; + if (!(typeof value === 'object' && value instanceof Long)) value = Long.fromNumber(value, false); + this.ensureCapacity(offset+8); + if (this.littleEndian) { + this.view.setInt32(offset, value.getLowBits(), true); + this.view.setInt32(offset+4, value.getHighBits(), true); + } else { + this.view.setInt32(offset, value.getHighBits(), false); + this.view.setInt32(offset+4, value.getLowBits(), false); + } + return this; + }; + + /** + * Reads a 64bit integer. Requires Long.js. + * @param {number=} offset Offset to read from. Will use and advance {@link ByteBuffer#offset} if omitted. + * @returns {!Long} + * @throws {Error} If offset is out of bounds + * @expose + */ + ByteBuffer.prototype.readInt64 = function(offset) { + offset = typeof offset !== 'undefined' ? offset : (this.offset+=8)-8; + if (this.array === null || offset+8 > this.array.byteLength) { + this.offset -= 8; + throw(new Error("Cannot read int64 from "+this+" at "+offset+": Capacity overflow")); + } + var value; + if (this.littleEndian) { + value = Long.fromBits(this.view.getInt32(offset, true), this.view.getInt32(offset+4, true), false); + } else { + value = Long.fromBits(this.view.getInt32(offset+4, false), this.view.getInt32(offset, false), false); + } + return value; + }; + + /** + * Writes a 64bit unsigned integer. Requires Long.js. + * @function + * @param {number|!Long} value Value to write + * @param {number=} offset Offset to write to. Will use and advance {@link ByteBuffer#offset} if omitted. + * @returns {!ByteBuffer} this + * @expose + */ + ByteBuffer.prototype.writeUint64 = function(value, offset) { + offset = typeof offset !== 'undefined' ? offset : (this.offset+=8)-8; + if (!(typeof value === 'object' && value instanceof Long)) value = Long.fromNumber(value, true); + this.ensureCapacity(offset+8); + if (this.littleEndian) { + this.view.setUint32(offset, value.getLowBitsUnsigned(), true); + this.view.setUint32(offset+4, value.getHighBitsUnsigned(), true); + } else { + this.view.setUint32(offset, value.getHighBitsUnsigned(), false); + this.view.setUint32(offset+4, value.getLowBitsUnsigned(), false); + } + return this; + }; + + /** + * Reads a 64bit unsigned integer. Requires Long.js. + * @param {number=} offset Offset to read from. Will use and advance {@link ByteBuffer#offset} if omitted. + * @returns {!Long} + * @throws {Error} If offset is out of bounds + * @expose + */ + ByteBuffer.prototype.readUint64 = function(offset) { + offset = typeof offset !== 'undefined' ? offset : (this.offset+=8)-8; + if (this.array === null || offset+8 > this.array.byteLength) { + this.offset -= 8; + throw(new Error("Cannot read int64 from "+this+" at "+offset+": Capacity overflow")); + } + var value; + if (this.littleEndian) { + value = Long.fromBits(this.view.getUint32(offset, true), this.view.getUint32(offset+4, true), true); + } else { + value = Long.fromBits(this.view.getUint32(offset+4, false), this.view.getUint32(offset, false), true); + } + return value; + }; + + /** + * Writes a long. This is an alias of {@link ByteBuffer#writeInt64}. + * @function + * @param {number|!Long} value Value to write + * @param {number=} offset Offset to write to. Will use and advance {@link ByteBuffer#offset} if omitted. + * @returns {!ByteBuffer} this + * @expose + */ + ByteBuffer.prototype.writeLong = ByteBuffer.prototype.writeInt64; + + /** + * Reads a long. This is an alias of {@link ByteBuffer#readInt64}. + * @function + * @param {number=} offset Offset to read from. Will use and advance {@link ByteBuffer#offset} if omitted. + * @returns {!Long} + * @throws {Error} If offset is out of bounds + * @expose + */ + ByteBuffer.prototype.readLong = ByteBuffer.prototype.readInt64; + + } + + /** + * Maximum number of bytes used by 32bit base 128 variable-length integer. + * @type {number} + * @const + * @expose + */ + ByteBuffer.MAX_VARINT32_BYTES = 5; + + /** + * Writes a 32bit base 128 variable-length integer as used in protobuf. + * @param {number} value Value to write + * @param {number=} offset Offset to write to. Will use and advance {@link ByteBuffer#offset} if omitted. + * @returns {!ByteBuffer|number} this if offset is omitted, else the actual number of bytes written. + * @expose + */ + ByteBuffer.prototype.writeVarint32 = function(value, offset) { + var advance = typeof offset === 'undefined'; + offset = typeof offset !== 'undefined' ? offset : this.offset; + // ref: http://code.google.com/searchframe#WTeibokF6gE/trunk/src/google/protobuf/io/coded_stream.cc + value = value >>> 0; + this.ensureCapacity(offset+ByteBuffer.calculateVarint32(value)); + var dst = this.view, + size = 0; + dst.setUint8(offset, value | 0x80); + if (value >= (1 << 7)) { + dst.setUint8(offset+1, (value >> 7) | 0x80); + if (value >= (1 << 14)) { + dst.setUint8(offset+2, (value >> 14) | 0x80); + if (value >= (1 << 21)) { + dst.setUint8(offset+3, (value >> 21) | 0x80); + if (value >= (1 << 28)) { + dst.setUint8(offset+4, (value >> 28) & 0x7F); + size = 5; + } else { + dst.setUint8(offset+3, dst.getUint8(offset+3) & 0x7F); + size = 4; + } + } else { + dst.setUint8(offset+2, dst.getUint8(offset+2) & 0x7F); + size = 3; + } + } else { + dst.setUint8(offset+1, dst.getUint8(offset+1) & 0x7F); + size = 2; + } + } else { + dst.setUint8(offset, dst.getUint8(offset) & 0x7F); + size = 1; + } + if (advance) { + this.offset += size; + return this; + } else { + return size; + } + }; + + /** + * Reads a 32bit base 128 variable-length integer as used in protobuf. + * @param {number=} offset Offset to read from. Will use and advance {@link ByteBuffer#offset} if omitted. + * @returns {number|!{value: number, length: number}} The value read if offset is omitted, else the value read + * and the actual number of bytes read. + * @throws {Error} If it's not a valid varint + * @expose + */ + ByteBuffer.prototype.readVarint32 = function(offset) { + var advance = typeof offset === 'undefined'; + offset = typeof offset !== 'undefined' ? offset : this.offset; + // ref: src/google/protobuf/io/coded_stream.cc + + var count = 0, b, + src = this.view; + var value = 0 >>> 0; + do { + b = src.getUint8(offset+count); + if (count < ByteBuffer.MAX_VARINT32_BYTES) { + value |= ((b&0x7F)<<(7*count)) >>> 0; + } + ++count; + } while (b & 0x80); + value = value | 0; // Make sure to discard the higher order bits + if (advance) { + this.offset += count; + return value; + } else { + return { + "value": value, + "length": count + }; + } + }; + + /** + * Writes a zigzag encoded 32bit base 128 encoded variable-length integer as used in protobuf. + * @param {number} value Value to write + * @param {number=} offset Offset to write to. Will use and advance {@link ByteBuffer#offset} if omitted. + * @returns {!ByteBuffer|number} this if offset is omitted, else the actual number of bytes written. + * @expose + */ + ByteBuffer.prototype.writeZigZagVarint32 = function(value, offset) { + return this.writeVarint32(ByteBuffer.zigZagEncode32(value), offset); + }; + + /** + * Reads a zigzag encoded 32bit base 128 variable-length integer as used in protobuf. + * @param {number=} offset Offset to read from. Will use and advance {@link ByteBuffer#offset} if omitted. + * @returns {number|!{value: number, length: number}} The value read if offset is omitted, else the value read + * and the actual number of bytes read. + * @throws {Error} If it's not a valid varint + * @expose + */ + ByteBuffer.prototype.readZigZagVarint32 = function(offset) { + var dec = this.readVarint32(offset); + if (typeof dec === 'object') { + dec['value'] = ByteBuffer.zigZagDecode32(dec['value']); + return dec; + } + return ByteBuffer.zigZagDecode32(dec); + }; + + /** + * Maximum number of bytes used by a 64bit base 128 variable-length integer. + * @type {number} + * @const + * @expose + */ + ByteBuffer.MAX_VARINT64_BYTES = 10; + + /** + * @type {number} + * @const + * @inner + */ + var TWO_PWR_7_DBL = 1 << 7; + + /** + * @type {number} + * @const + * @inner + */ + var TWO_PWR_14_DBL = TWO_PWR_7_DBL * TWO_PWR_7_DBL; + + /** + * @type {number} + * @const + * @inner + */ + var TWO_PWR_21_DBL = TWO_PWR_7_DBL * TWO_PWR_14_DBL; + + /** + * @type {number} + * @const + * @inner + */ + var TWO_PWR_28_DBL = TWO_PWR_14_DBL * TWO_PWR_14_DBL; + + // Available with Long.js only + if (Long) { + + /** + * Writes a 64bit base 128 variable-length integer as used in protobuf. + * @param {number|Long} value Value to write + * @param {number=} offset Offset to write to. Will use and advance {@link ByteBuffer#offset} if omitted. + * @returns {!ByteBuffer|number} this if offset is omitted, else the actual number of bytes written. + * @expose + */ + ByteBuffer.prototype.writeVarint64 = function(value, offset) { + var advance = typeof offset === 'undefined'; + offset = typeof offset !== 'undefined' ? offset : this.offset; + if (!(typeof value === 'object' && value instanceof Long)) value = Long.fromNumber(value, false); + + var part0 = value.toInt() >>> 0, + part1 = value.shiftRightUnsigned(28).toInt() >>> 0, + part2 = value.shiftRightUnsigned(56).toInt() >>> 0, + size = ByteBuffer.calculateVarint64(value); + + this.ensureCapacity(offset+size); + var dst = this.view; + switch (size) { + case 10: dst.setUint8(offset+9, (part2 >>> 7) | 0x80); + case 9 : dst.setUint8(offset+8, (part2 ) | 0x80); + case 8 : dst.setUint8(offset+7, (part1 >>> 21) | 0x80); + case 7 : dst.setUint8(offset+6, (part1 >>> 14) | 0x80); + case 6 : dst.setUint8(offset+5, (part1 >>> 7) | 0x80); + case 5 : dst.setUint8(offset+4, (part1 ) | 0x80); + case 4 : dst.setUint8(offset+3, (part0 >>> 21) | 0x80); + case 3 : dst.setUint8(offset+2, (part0 >>> 14) | 0x80); + case 2 : dst.setUint8(offset+1, (part0 >>> 7) | 0x80); + case 1 : dst.setUint8(offset+0, (part0 ) | 0x80); + } + dst.setUint8(offset+size-1, dst.getUint8(offset+size-1) & 0x7F); + if (advance) { + this.offset += size; + return this; + } else { + return size; + } + }; + + /** + * Reads a 32bit base 128 variable-length integer as used in protobuf. Requires Long.js. + * @param {number=} offset Offset to read from. Will use and advance {@link ByteBuffer#offset} if omitted. + * @returns {!Long|!{value: Long, length: number}} The value read if offset is omitted, else the value read and + * the actual number of bytes read. + * @throws {Error} If it's not a valid varint + * @expose + */ + ByteBuffer.prototype.readVarint64 = function(offset) { + var advance = typeof offset === 'undefined'; + offset = typeof offset !== 'undefined' ? offset : this.offset; + var start = offset; + // ref: src/google/protobuf/io/coded_stream.cc + + var src = this.view, + part0, part1 = 0, part2 = 0, b; + b = src.getUint8(offset++); part0 = (b & 0x7F) ; if (b & 0x80) { + b = src.getUint8(offset++); part0 |= (b & 0x7F) << 7; if (b & 0x80) { + b = src.getUint8(offset++); part0 |= (b & 0x7F) << 14; if (b & 0x80) { + b = src.getUint8(offset++); part0 |= (b & 0x7F) << 21; if (b & 0x80) { + b = src.getUint8(offset++); part1 = (b & 0x7F) ; if (b & 0x80) { + b = src.getUint8(offset++); part1 |= (b & 0x7F) << 7; if (b & 0x80) { + b = src.getUint8(offset++); part1 |= (b & 0x7F) << 14; if (b & 0x80) { + b = src.getUint8(offset++); part1 |= (b & 0x7F) << 21; if (b & 0x80) { + b = src.getUint8(offset++); part2 = (b & 0x7F) ; if (b & 0x80) { + b = src.getUint8(offset++); part2 |= (b & 0x7F) << 7; if (b & 0x80) { + throw(new Error("Data must be corrupt: Buffer overrun")); }}}}}}}}}} + + var value = Long.from28Bits(part0, part1, part2, false); + if (advance) { + this.offset = offset; + return value; + } else { + return { + "value": value, + "length": offset-start + }; + } + }; + + /** + * Writes a zigzag encoded 64bit base 128 encoded variable-length integer as used in protobuf. + * @param {number} value Value to write + * @param {number=} offset Offset to write to. Defaults to {@link ByteBuffer#offset} which will be modified only if omitted. + * @returns {!ByteBuffer|number} this if offset is omitted, else the actual number of bytes written. + * @expose + */ + ByteBuffer.prototype.writeZigZagVarint64 = function(value, offset) { + return this.writeVarint64(ByteBuffer.zigZagEncode64(value), offset); + }; + + /** + * Reads a zigzag encoded 64bit base 128 variable-length integer as used in protobuf. + * @param {number=} offset Offset to read from. Defaults to {@link ByteBuffer#offset} which will be modified only if omitted. + * @returns {Long|!{value: Long, length: number}} The value read if offset is omitted, else the value read and the actual number of bytes read. + * @throws {Error} If it's not a valid varint + * @expose + */ + ByteBuffer.prototype.readZigZagVarint64 = function(offset) { + var dec = this.readVarint64(offset); + if (typeof dec === 'object' && !(dec instanceof Long)) { + dec['value'] = ByteBuffer.zigZagDecode64(dec['value']); + return dec; + } + return ByteBuffer.zigZagDecode64(dec); + }; + + } + + /** + * Writes a base 128 variable-length integer as used in protobuf. This is an alias of {@link ByteBuffer#writeVarint32}. + * @function + * @param {number} value Value to write + * @param {number=} offset Offset to write to. Defaults to {@link ByteBuffer#offset} which will be modified only if omitted. + * @returns {!ByteBuffer|number} this if offset is omitted, else the actual number of bytes written. + * @expose + */ + ByteBuffer.prototype.writeVarint = ByteBuffer.prototype.writeVarint32; + + /** + * Reads a base 128 variable-length integer as used in protobuf. This is an alias of {@link ByteBuffer#readVarint32}. + * @function + * @param {number=} offset Offset to read from. Defaults to {@link ByteBuffer#offset} which will be modified only if omitted. + * @returns {number|{value: number, length: number}} The value read if offset is omitted, else the value read and the actual number of bytes read. + * @expose + */ + ByteBuffer.prototype.readVarint = ByteBuffer.prototype.readVarint32; + + /** + * Writes a zigzag encoded base 128 encoded variable-length integer as used in protobuf. This is an alias of {@link ByteBuffer#writeZigZagVarint32}. + * @function + * @param {number} value Value to write + * @param {number=} offset Offset to write to. Defaults to {@link ByteBuffer#offset} which will be modified only if omitted. + * @returns {!ByteBuffer|number} this if offset is omitted, else the actual number of bytes written. + * @expose + */ + ByteBuffer.prototype.writeZigZagVarint = ByteBuffer.prototype.writeZigZagVarint32; + + /** + * Reads a zigzag encoded base 128 variable-length integer as used in protobuf. This is an alias of {@link ByteBuffer#readZigZagVarint32}. + * @function + * @param {number=} offset Offset to read from. Defaults to {@link ByteBuffer#offset} which will be modified only if omitted. + * @returns {number|{value: number, length: number}} The value read if offset is omitted, else the value read and the actual number of bytes read. + * @throws {Error} If it's not a valid varint + * @expose + */ + ByteBuffer.prototype.readZigZagVarint = ByteBuffer.prototype.readZigZagVarint32; + + /** + * Calculates the actual number of bytes required to encode a 32bit base 128 variable-length integer. + * @param {number} value Value to encode + * @returns {number} Number of bytes required. Capped to {@link ByteBuffer.MAX_VARINT32_BYTES} + * @expose + */ + ByteBuffer.calculateVarint32 = function(value) { + // ref: src/google/protobuf/io/coded_stream.cc + value = value >>> 0; + if (value < TWO_PWR_7_DBL) { + return 1; + } else if (value < TWO_PWR_14_DBL) { + return 2; + } else if (value < TWO_PWR_21_DBL) { + return 3; + } else if (value < TWO_PWR_28_DBL) { + return 4; + } else { + return 5; + } + }; + + // Available with Long.js only + if (Long) { + + /** + * Calculates the actual number of bytes required to encode a 64bit base 128 variable-length integer. + * @param {number|!Long} value Value to encode + * @returns {number} Number of bytes required. Capped to {@link ByteBuffer.MAX_VARINT64_BYTES} + * @expose + */ + ByteBuffer.calculateVarint64 = function(value) { + // ref: src/google/protobuf/io/coded_stream.cc + if (!(typeof value === 'object' && value instanceof Long)) value = Long.fromNumber(value, false); + + var part0 = value.toInt() >>> 0, + part1 = value.shiftRightUnsigned(28).toInt() >>> 0, + part2 = value.shiftRightUnsigned(56).toInt() >>> 0; + + if (part2 == 0) { + if (part1 == 0) { + if (part0 < TWO_PWR_14_DBL) { + return part0 < TWO_PWR_7_DBL ? 1 : 2; + } else { + return part0 < TWO_PWR_21_DBL ? 3 : 4; + } + } else { + if (part1 < TWO_PWR_14_DBL) { + return part1 < TWO_PWR_7_DBL ? 5 : 6; + } else { + return part1 < TWO_PWR_21_DBL ? 7 : 8; + } + } + } else { + return part2 < TWO_PWR_7_DBL ? 9 : 10; + } + }; + + } + + /** + * Encodes a signed 32bit integer so that it can be effectively used with varint encoding. + * @param {number} n Signed 32bit integer + * @returns {number} Unsigned zigzag encoded 32bit integer + * @expose + */ + ByteBuffer.zigZagEncode32 = function(n) { + // ref: src/google/protobuf/wire_format_lite.h + return (((n |= 0) << 1) ^ (n >> 31)) >>> 0; + }; + + /** + * Decodes a zigzag encoded signed 32bit integer. + * @param {number} n Unsigned zigzag encoded 32bit integer + * @returns {number} Signed 32bit integer + * @expose + */ + ByteBuffer.zigZagDecode32 = function(n) { + // ref: src/google/protobuf/wire_format_lite.h + return ((n >>> 1) ^ -(n & 1)) | 0; + }; + + // Available with Long.js only + if (Long) { + + /** + * Encodes a signed 64bit integer so that it can be effectively used with varint encoding. + * @param {number|!Long} n Signed long + * @returns {!Long} Unsigned zigzag encoded long + * @expose + */ + ByteBuffer.zigZagEncode64 = function(n) { + // ref: src/google/protobuf/wire_format_lite.h + if (typeof n === 'object' && n instanceof Long) { + if (n.unsigned) n = n.toSigned(); + } else { + n = Long.fromNumber(n, false); + } + return n.shiftLeft(1).xor(n.shiftRight(63)).toUnsigned(); + }; + + /** + * Decodes a zigzag encoded signed 64bit integer. + * @param {!Long|number} n Unsigned zigzag encoded long or JavaScript number + * @returns {!Long} Signed long + * @throws {Error} If long support is not available + * @expose + */ + ByteBuffer.zigZagDecode64 = function(n) { + // ref: src/google/protobuf/wire_format_lite.h + if (typeof n === 'object' && n instanceof Long) { + if (!n.unsigned) n = n.toUnsigned(); + } else { + n = Long.fromNumber(n, true); + } + return n.shiftRightUnsigned(1).xor(n.and(Long.ONE).toSigned().negate()).toSigned(); + }; + + } + + /** + * Decodes a single UTF8 character from the specified ByteBuffer. The ByteBuffer's offsets are not modified. + * @param {!ByteBuffer} src + * @param {number} offset Offset to read from + * @returns {!{char: number, length: number}} Decoded char code and the actual number of bytes read + * @throws {Error} If the character cannot be decoded or there is a capacity overflow + * @expose + */ + ByteBuffer.decodeUTF8Char = function(src, offset) { + var a = src.readUint8(offset), b, c, d, e, f, start = offset, charCode; + // ref: http://en.wikipedia.org/wiki/UTF-8#Description + // It's quite huge but should be pretty fast. + if ((a&0x80)==0) { + charCode = a; + offset += 1; + } else if ((a&0xE0)==0xC0) { + b = src.readUint8(offset+1); + charCode = ((a&0x1F)<<6) | (b&0x3F); + offset += 2; + } else if ((a&0xF0)==0xE0) { + b = src.readUint8(offset+1); + c = src.readUint8(offset+2); + charCode = ((a&0x0F)<<12) | ((b&0x3F)<<6) | (c&0x3F); + offset += 3; + } else if ((a&0xF8)==0xF0) { + b = src.readUint8(offset+1); + c = src.readUint8(offset+2); + d = src.readUint8(offset+3); + charCode = ((a&0x07)<<18) | ((b&0x3F)<<12) | ((c&0x3F)<<6) | (d&0x3F); + offset += 4; + } else if ((a&0xFC)==0xF8) { + b = src.readUint8(offset+1); + c = src.readUint8(offset+2); + d = src.readUint8(offset+3); + e = src.readUint8(offset+4); + charCode = ((a&0x03)<<24) | ((b&0x3F)<<18) | ((c&0x3F)<<12) | ((d&0x3F)<<6) | (e&0x3F); + offset += 5; + } else if ((a&0xFE)==0xFC) { + b = src.readUint8(offset+1); + c = src.readUint8(offset+2); + d = src.readUint8(offset+3); + e = src.readUint8(offset+4); + f = src.readUint8(offset+5); + charCode = ((a&0x01)<<30) | ((b&0x3F)<<24) | ((c&0x3F)<<18) | ((d&0x3F)<<12) | ((e&0x3F)<<6) | (f&0x3F); + offset += 6; + } else { + throw(new Error("Cannot decode UTF8 character at offset "+offset+": charCode (0x"+a.toString(16)+") is invalid")); + } + return { + "char": charCode , + "length": offset-start + }; + }; + + /** + * Encodes a single UTF8 character to the specified ByteBuffer. The ByteBuffer's offsets are not modified. + * @param {number} charCode Character to encode as char code + * @param {!ByteBuffer} dst ByteBuffer to encode to + * @param {number} offset Offset to write to + * @returns {number} Actual number of bytes written + * @throws {Error} If the character cannot be encoded + * @expose + */ + ByteBuffer.encodeUTF8Char = function(charCode, dst, offset) { + var start = offset; + // ref: http://en.wikipedia.org/wiki/UTF-8#Description + // It's quite huge but should be pretty fast. + if (charCode < 0) { + throw(new Error("Cannot encode UTF8 character: charCode ("+charCode+") is negative")); + } + if (charCode < 0x80) { + dst.writeUint8(charCode&0x7F, offset); + offset += 1; + } else if (charCode < 0x800) { + dst.writeUint8(((charCode>>6)&0x1F)|0xC0, offset) + .writeUint8((charCode&0x3F)|0x80, offset+1); + offset += 2; + } else if (charCode < 0x10000) { + dst.writeUint8(((charCode>>12)&0x0F)|0xE0, offset) + .writeUint8(((charCode>>6)&0x3F)|0x80, offset+1) + .writeUint8((charCode&0x3F)|0x80, offset+2); + offset += 3; + } else if (charCode < 0x200000) { + dst.writeUint8(((charCode>>18)&0x07)|0xF0, offset) + .writeUint8(((charCode>>12)&0x3F)|0x80, offset+1) + .writeUint8(((charCode>>6)&0x3F)|0x80, offset+2) + .writeUint8((charCode&0x3F)|0x80, offset+3); + offset += 4; + } else if (charCode < 0x4000000) { + dst.writeUint8(((charCode>>24)&0x03)|0xF8, offset) + .writeUint8(((charCode>>18)&0x3F)|0x80, offset+1) + .writeUint8(((charCode>>12)&0x3F)|0x80, offset+2) + .writeUint8(((charCode>>6)&0x3F)|0x80, offset+3) + .writeUint8((charCode&0x3F)|0x80, offset+4); + offset += 5; + } else if (charCode < 0x80000000) { + dst.writeUint8(((charCode>>30)&0x01)|0xFC, offset) + .writeUint8(((charCode>>24)&0x3F)|0x80, offset+1) + .writeUint8(((charCode>>18)&0x3F)|0x80, offset+2) + .writeUint8(((charCode>>12)&0x3F)|0x80, offset+3) + .writeUint8(((charCode>>6)&0x3F)|0x80, offset+4) + .writeUint8((charCode&0x3F)|0x80, offset+5); + offset += 6; + } else { + throw(new Error("Cannot encode UTF8 character: charCode (0x"+charCode.toString(16)+") is too large (>= 0x80000000)")); + } + return offset-start; + }; + + /** + * Calculates the actual number of bytes required to encode the specified char code. + * @param {number} charCode Character to encode as char code + * @returns {number} Number of bytes required to encode the specified char code + * @throws {Error} If the character cannot be calculated (too large) + * @expose + */ + ByteBuffer.calculateUTF8Char = function(charCode) { + if (charCode < 0) { + throw(new Error("Cannot calculate length of UTF8 character: charCode ("+charCode+") is negative")); + } + if (charCode < 0x80) { + return 1; + } else if (charCode < 0x800) { + return 2; + } else if (charCode < 0x10000) { + return 3; + } else if (charCode < 0x200000) { + return 4; + } else if (charCode < 0x4000000) { + return 5; + } else if (charCode < 0x80000000) { + return 6; + } else { + throw(new Error("Cannot calculate length of UTF8 character: charCode (0x"+charCode.toString(16)+") is too large (>= 0x80000000)")); + } + }; + + /** + * Calculates the number of bytes required to store an UTF8 encoded string. + * @param {string} str String to calculate + * @returns {number} Number of bytes required + */ + ByteBuffer.calculateUTF8String = function(str) { + str = ""+str; + var bytes = 0; + for (var i=0, k=str.length; i i ? bb.readUint8(i++) : 0; + o3 = bb.length > i ? bb.readUint8(i++) : 0; + bits = o1 << 16 | o2 << 8 | o3; + h1 = bits >> 18 & 0x3f; + h2 = bits >> 12 & 0x3f; + h3 = bits >> 6 & 0x3f; + h4 = bits & 0x3f; + out[oi++] = B64.charAt(h1) + B64.charAt(h2) + B64.charAt(h3) + B64.charAt(h4); + } while (i < bb.length); + var enc = out.join(''), + r = (bb.length - bb.offset) % 3; + return (r ? enc.slice(0, r - 3) : enc) + '==='.slice(r || 3); + }; + + /** + * Decodes a base64 encoded string to a ByteBuffer. + * @param {string} str Base64 encoded string + * @param {boolean=} littleEndian `true` to use little endian byte order, defaults to `false` for big endian. + * @returns {!ByteBuffer} ByteBuffer + * @throws {Error} If the argument is not a valid base64 encoded string + * @expose + */ + ByteBuffer.decode64 = function(str, littleEndian) { + // ref: http://phpjs.org/functions/base64_decode/ + if (typeof str !== 'string') { + throw(new Error("Illegal argument: Not a string")); + } + var o1, o2, o3, h1, h2, h3, h4, bits, i = 0, + out = new ByteBuffer(Math.ceil(str.length / 3), littleEndian); + do { + h1 = B64.indexOf(str.charAt(i++)); + h2 = B64.indexOf(str.charAt(i++)); + h3 = B64.indexOf(str.charAt(i++)); + h4 = B64.indexOf(str.charAt(i++)); + if (h1 < 0 || h2 < 0 || h3 < 0 || h4 < 0) { + throw(new Error("Illegal argument: Not a valid base64 encoded string")); + } + bits = h1 << 18 | h2 << 12 | h3 << 6 | h4; + o1 = bits >> 16 & 0xff; + o2 = bits >> 8 & 0xff; + o3 = bits & 0xff; + if (h3 == 64) { + out.writeUint8(o1); + } else if (h4 == 64) { + out.writeUint8(o1) + .writeUint8(o2); + } else { + out.writeUint8(o1) + .writeUint8(o2) + .writeUint8(o3); + } + } while (i < str.length); + return out.flip(); + }; + + /** + * Encodes a ByteBuffer to a hex encoded string. + * @param {!ByteBuffer} bb ByteBuffer to encode. Will be cloned and flipped if length < offset. + * @returns {string} Hex encoded string + * @throws {Error} If the argument is not a valid ByteBuffer + * @expose + */ + ByteBuffer.encodeHex = function(bb) { + if (!(bb instanceof ByteBuffer)) { + bb = ByteBuffer.wrap(bb); + } else if (bb.length < bb.offset) { + bb = bb.clone().flip(); + } + if (bb.array === null) return ""; + var val, out = []; + for (var i=bb.offset, k=bb.length; i 255) throw(new Error("Illegal argument: Not a binary string (char code "+val+")")); + view.setUint8(i, val); + } + var bb = new ByteBuffer(k, littleEndian, true); + bb.array = dst; + bb.view = view; + bb.length = k; + return bb; + }; + + /** + * Writes an UTF8 string. + * @param {string} str String to write + * @param {number=} offset Offset to write to. Will use and advance {@link ByteBuffer#offset} if omitted. + * @returns {!ByteBuffer|number} this if offset is omitted, else the actual number of bytes written. + * @expose + */ + ByteBuffer.prototype.writeUTF8String = function(str, offset) { + var advance = typeof offset === 'undefined'; + offset = typeof offset !== 'undefined' ? offset : this.offset; + var start = offset; + var encLen = ByteBuffer.calculateUTF8String(str); // See [1] + this.ensureCapacity(offset+encLen); + for (var i=0, j=str.length; ilength ? "+" : "")+offset-length)+" bytes")); + } + if (advance) { + this.offset = offset; + return result; + } else { + return { + "string": result, + "length": offset-start + } + } + }; + + /** + * Writes a string with prepended number of characters, which is also encoded as an UTF8 character.. + * @param {string} str String to write + * @param {number=} offset Offset to write to. Will use and advance {@link ByteBuffer#offset} if omitted. + * @returns {!ByteBuffer|number} this if offset is omitted, else the actual number of bytes written. + * @expose + */ + ByteBuffer.prototype.writeLString = function(str, offset) { + str = ""+str; + var advance = typeof offset === 'undefined'; + offset = typeof offset !== 'undefined' ? offset : this.offset; + var encLen = ByteBuffer.encodeUTF8Char(str.length, this, offset); + encLen += this.writeUTF8String(str, offset+encLen); + if (advance) { + this.offset += encLen; + return this; + } else { + return encLen; + } + }; + + /** + * Reads a string with a prepended number of characters, which is also encoded as an UTF8 character. + * @param {number=} offset Offset to read from. Will use and advance {@link ByteBuffer#offset} if omitted. + * @returns {string|{string: string, length: number}} The string read if offset is omitted, else the string read + * and the actual number of bytes read. + * @throws {Error} If the string cannot be decoded + * @expose + */ + ByteBuffer.prototype.readLString = function(offset) { + var advance = typeof offset === 'undefined'; + offset = typeof offset !== 'undefined' ? offset : this.offset; + var lenDec = ByteBuffer.decodeUTF8Char(this, offset), + dec = this.readUTF8String(lenDec["char"], offset+lenDec["length"]); + if (advance) { + this.offset += lenDec["length"]+dec["length"]; + return dec["string"]; + } else { + return { + "string": dec["string"], + "length": lenDec["length"]+dec["length"] + }; + } + }; + + /** + * Writes a string with prepended number of characters, which is encoded as a 32bit base 128 variable-length + * integer. + * @param {string} str String to write + * @param {number=} offset Offset to write to. Will use and advance {@link ByteBuffer#offset} if omitted. + * @returns {!ByteBuffer|number} this if offset is omitted, else the actual number of bytes written + * @expose + */ + ByteBuffer.prototype.writeVString = function(str, offset) { + str = ""+str; + var advance = typeof offset === 'undefined'; + offset = typeof offset !== 'undefined' ? offset : this.offset; + var encLen = this.writeVarint32(ByteBuffer.calculateUTF8String(str), offset); + encLen += this.writeUTF8String(str, offset+encLen); + if (advance) { + this.offset += encLen; + return this; + } else { + return encLen; + } + }; + + /** + * Reads a string with prepended number of characters, which is encoded as a 32bit base 128 variable-length + * integer. + * @param {number=} offset Offset to read from. Will use and advance {@link ByteBuffer#offset} if omitted. + * @returns {string|!{string: string, length: number}} The string read if offset is omitted, else the string + * read and the actual number of bytes read. + * @throws {Error} If the string cannot be decoded or if it is not preceeded by a valid varint + * @expose + */ + ByteBuffer.prototype.readVString = function(offset) { + var advance = typeof offset === 'undefined'; + offset = typeof offset !== 'undefined' ? offset : this.offset; + var lenDec = this.readVarint32(offset); + var dec = this.readUTF8StringBytes(lenDec["value"], offset+lenDec["length"]); + if (advance) { + this.offset += lenDec["length"]+dec["length"]; + return dec["string"]; + } else { + return { + "string": dec["string"], + "length": lenDec["length"]+dec["length"] + }; + } + }; + + /** + * Writes a string followed by a NULL character (Uint8). Beware: The source string must not contain NULL + * characters unless this is actually intended. This is not checked. If you have the option it is recommended + * to use {@link ByteBuffer#writeLString} or {@link ByteBuffer#writeVString} with the corresponding reading + * methods instead. + * @param {string} str String to write + * @param {number=} offset Offset to write to. Will use and advance {@link ByteBuffer#offset} if omitted. + * @returns {!ByteBuffer|number} this if offset is omitted, else the actual number of bytes written + * @expose + */ + ByteBuffer.prototype.writeCString = function(str, offset) { + str = ""+str; + var advance = typeof offset === 'undefined'; + offset = typeof offset !== 'undefined' ? offset : this.offset; + var encLen = this.writeUTF8String(str, offset); + this.writeUint8(0, offset+encLen); + if (advance) { + this.offset += encLen+1; + return this; + } else { + return encLen+1; + } + }; + + /** + * Reads a string followed by a NULL character (Uint8). + * @param {number=} offset Offset to read from. Will use and advance {@link ByteBuffer#offset} if omitted. + * @returns {string|!{string: string, length: number}} The string read if offset is omitted, else the string + * read and the actual number of bytes read. + * @throws {Error} If the string cannot be decoded + * @expose + */ + ByteBuffer.prototype.readCString = function(offset) { + var advance = typeof offset === 'undefined'; + offset = typeof offset !== 'undefined' ? offset : this.offset; + var dec, result = "", start = offset; + do { + dec = ByteBuffer.decodeUTF8Char(this, offset); + offset += dec["length"]; + if (dec["char"] != 0) result += String.fromCharCode(dec["char"]); + } while (dec["char"] != 0); + if (advance) { + this.offset = offset; + return result; + } else { + return { + "string": result, + "length": offset-start + }; + } + }; + + /** + * Serializes and writes a JSON payload. + * @param {*} data Data payload to serialize + * @param {number=} offset Offset to write to. Will use and advance {@link ByteBuffer#offset} if omitted. + * @param {function(*)=} stringify Stringify implementation to use. Defaults to {@link JSON.stringify}. + * @returns {!ByteBuffer|number} this if offset is omitted, else the actual number if bytes written + * @expose + */ + ByteBuffer.prototype.writeJSON = function(data, offset, stringify) { + stringify = typeof stringify === 'function' ? stringify : JSON.stringify; + return this.writeLString(stringify(data), offset); + }; + + /** + * Reads a JSON payload and unserializes it. + * @param {number=} offset Offset to read from. Will use and advance {@link ByteBuffer#offset} if omitted. + * @param {function(string)=} parse Parse implementation to use. Defaults to {@link JSON.parse}. + * @returns {!*|!{data: *, length: number}} Data payload if offset is omitted, else the data payload and the + * actual number of bytes read + * @throws {Error} If the data cannot be decoded + * @expose + */ + ByteBuffer.prototype.readJSON = function(offset, parse) { + parse = typeof parse === 'function' ? parse : JSON.parse; + var result = this.readLString(offset); + if (typeof result === 'string') { + return parse(result); + } else { + return { + "data": parse(result["string"]), + "length": result["length"] + }; + } + }; + + /** + * Returns a textual two columns (hex, ascii) representation of this ByteBuffer's backing array. + * @param {number=} wrap Wrap length. Defaults to 16. + * @returns {string} Hex representation as of " 00<01 02>03... ASCII DATA" with marked offsets + * @expose + */ + ByteBuffer.prototype.toColumns = function(wrap) { + if (this.array === null) return "DESTROYED"; + wrap = typeof wrap !== 'undefined' ? parseInt(wrap, 10) : 16; + if (wrap < 1) wrap = 16; + + // Left colum: hex with offsets + var out = "", + lines = [], + val, + view = this.view; + if (this.offset == 0 && this.length == 0) { + out += "|"; + } else if (this.length == 0) { + out += ">"; + } else if (this.offset == 0) { + out += "<"; + } else { + out += " "; + } + for (var i=0, k=this.array.byteLength; i0 && i%wrap == 0) { + while (out.length < 3*wrap+1) out += " "; // Make it equal to maybe show something on the right + lines.push(out); + out = " "; + } + val = view.getUint8(i).toString(16).toUpperCase(); + if (val.length < 2) val = "0"+val; + out += val; + if (i+1 == this.offset && i+1 == this.length) { + out += "|"; + } else if (i+1 == this.offset) { + out += "<"; + } else if (i+1 == this.length) { + out += ">"; + } else { + out += " "; + } + } + if (out != " ") { + lines.push(out); + } + // Make it equal + for (i=0, k=lines.length; i0 && i%wrap == 0) { + lines[n] += " "+out; + out = ""; n++; + } + val = view.getUint8(i); + out += val > 32 && val < 127 ? String.fromCharCode(val) : "."; + } + if (out != "") { + lines[n] += " "+out; + } + return lines.join("\n"); + }; + + /** + * Prints debug information about this ByteBuffer's contents. + * @param {function(string)=} out Output function to call, defaults to console.log + * @expose + */ + ByteBuffer.prototype.printDebug = function(out) { + if (typeof out !== 'function') out = console.log.bind(console); + out( + (this.array != null ? "ByteBuffer(offset="+this.offset+",markedOffset="+this.markedOffset+",length="+this.length+",capacity="+this.array.byteLength+")" : "ByteBuffer(DESTROYED)")+"\n"+ + "-------------------------------------------------------------------\n"+ + this.toColumns()+"\n" + ); + }; + + /** + * Returns the ByteBuffer's contents between offset and length as a hex string. + * @param {boolean=} debug `true` to return the entire backing array with marked offsets, defaults to `false` + * @returns {string} Hex string or debug string + * @expose + */ + ByteBuffer.prototype.toHex = function(debug) { + var out = "", + val, + view = this.view, + i, k; + if (!debug) { + return ByteBuffer.encodeHex(this); + } else { + if (this.array === null) return "DESTROYED"; + if (this.offset == 0 && this.length == 0) { + out += "|"; + } else if (this.length == 0) { + out += ">"; + } else if (this.offset == 0) { + out += "<"; + } else { + out += " "; + } + for (i=0, k=this.array.byteLength; i= this.length) return ""; + return ByteBuffer.encode64(this); + }; + + /** + * Returns the ByteBuffer's contents as an UTF8 encoded string. + * @returns {string} + * @expose + */ + ByteBuffer.prototype.toUTF8 = function() { + if (this.array === null || this.offset >= this.length) return ""; + return this.readUTF8StringBytes(this.length - this.offset, this.offset)["string"]; + }; + + /** + * Converts the ByteBuffer to a string. + * @param {string=} enc Output encoding. Returns an informative string representation by default but also allows + * direct conversion to "utf8", "hex", "base64" and "binary" encoding. "debug" returns a hex representation with + * marked offsets. + * @returns {string} String representation + * @expose + */ + ByteBuffer.prototype.toString = function(enc) { + enc = enc || ""; + switch (enc) { + case "utf8": + return this.toUTF8(); + case "base64": + return this.toBase64(); + case "hex": + return this.toHex(); + case "binary": + return this.toBinary(); + case "debug": + return this.toHex(true); + default: + if (this.array === null) { + return "ByteBuffer(DESTROYED)"; + } + return "ByteBuffer(offset="+this.offset+",markedOffset="+this.markedOffset+",length="+this.length+",capacity="+this.array.byteLength+")"; + } + }; + + /** + * Returns an ArrayBuffer compacted to contain this ByteBuffer's actual contents. Will transparently + * {@link ByteBuffer#flip} the ByteBuffer if its offset is larger than its length. Will return a reference to + * the unmodified backing buffer if offset=0 and length=capacity unless forceCopy is set to true. + * @param {boolean=} forceCopy `true` forces the creation of a copy, defaults to `false` + * @returns {?ArrayBuffer} Compacted ArrayBuffer or null if already destroyed + * @expose + */ + ByteBuffer.prototype.toArrayBuffer = function(forceCopy) { + if (this.array === null) return null; + var b = this.clone(); + if (b.offset > b.length) { + b.flip(); + } + var copied = false; + if (b.offset > 0 || b.length < b.array.byteLength) { + b.compact(); // Will always create a new backing buffer because of the above condition + copied = true; + } + return forceCopy && !copied ? b.copy().array : b.array; + }; + + // Available with node.js only + if (Buffer) { + + /** + * Returns a node Buffer compacted to contain this ByteBuffer's actual contents. Will transparently + * {@link ByteBuffer#flip} the ByteBuffer if its offset is larger than its length. Will also copy all data (not + * a reference). + * @returns {?Buffer} Compacted node Buffer or null if already destroyed + * @expose + */ + ByteBuffer.prototype.toBuffer = function() { + if (this.array === null) return null; + var offset = this.offset, length = this.length; + if (offset > length) { + var temp = offset; + offset = length; + length = temp; + } + return new Buffer(new Uint8Array(this.array).subarray(offset, length)); + }; + + } + + return ByteBuffer; + } + + // Enable module loading if available + if (typeof module !== 'undefined' && module["exports"]) { // CommonJS + module["exports"] = loadByteBuffer(require("long")); + } else if (typeof define !== 'undefined' && define["amd"]) { // AMD + define("ByteBuffer", ["Math/Long"], function(Long) { return loadByteBuffer(Long); }); + } else { // Shim + if (!global["dcodeIO"]) global["dcodeIO"] = {}; + global["dcodeIO"]["ByteBuffer"] = loadByteBuffer(global["dcodeIO"]["Long"]); + } + +})(this); diff --git a/js-deps/ProtoBuf.min.js b/js-deps/ProtoBuf.min.js index cf479d8f..731eb3ad 100644 --- a/js-deps/ProtoBuf.min.js +++ b/js-deps/ProtoBuf.min.js @@ -1,93 +1,3721 @@ /* - ProtoBuf.js (c) 2013 Daniel Wirtz - Released under the Apache License, Version 2.0 - see: https://github.com/dcodeIO/ProtoBuf.js for details -*/ -(function(r){function s(p){var g={VERSION:"2.0.3",WIRE_TYPES:{}};g.WIRE_TYPES.VARINT=0;g.WIRE_TYPES.BITS64=1;g.WIRE_TYPES.LDELIM=2;g.WIRE_TYPES.STARTGROUP=3;g.WIRE_TYPES.ENDGROUP=4;g.WIRE_TYPES.BITS32=5;g.TYPES={int32:{name:"int32",wireType:g.WIRE_TYPES.VARINT},uint32:{name:"uint32",wireType:g.WIRE_TYPES.VARINT},sint32:{name:"sint32",wireType:g.WIRE_TYPES.VARINT},int64:{name:"int64",wireType:g.WIRE_TYPES.VARINT},uint64:{name:"uint64",wireType:g.WIRE_TYPES.VARINT},sint64:{name:"sint64",wireType:g.WIRE_TYPES.VARINT}, -bool:{name:"bool",wireType:g.WIRE_TYPES.VARINT},"double":{name:"double",wireType:g.WIRE_TYPES.BITS64},string:{name:"string",wireType:g.WIRE_TYPES.LDELIM},bytes:{name:"bytes",wireType:g.WIRE_TYPES.LDELIM},fixed32:{name:"fixed32",wireType:g.WIRE_TYPES.BITS32},sfixed32:{name:"sfixed32",wireType:g.WIRE_TYPES.BITS32},fixed64:{name:"fixed64",wireType:g.WIRE_TYPES.BITS64},sfixed64:{name:"sfixed64",wireType:g.WIRE_TYPES.BITS64},"float":{name:"float",wireType:g.WIRE_TYPES.BITS32},"enum":{name:"enum",wireType:g.WIRE_TYPES.VARINT}, -message:{name:"message",wireType:g.WIRE_TYPES.LDELIM}};g.Long=p.Long;g.convertFieldsToCamelCase=!1;g.Util=function(){Object.create||(Object.create=function(b){function c(){}if(1=this.source.length)return null;if(this.readingString)return this.readingString=!1,this._readString();var b,k;do{for(b=!1;c.WHITESPACE.test(k=this.source.charAt(this.index));)if(this.index++,"\n"===k&&this.line++,this.index===this.source.length)return null;if("/"===this.source.charAt(this.index))if("/"===this.source.charAt(++this.index)){for(;"\n"!==this.source.charAt(this.index);)if(this.index++,this.index==this.source.length)return null;this.index++;this.line++;b=!0}else if("*"=== -this.source.charAt(this.index)){for(k="";"*/"!==k+(k=this.source.charAt(this.index));)if(this.index++,"\n"===k&&this.line++,this.index===this.source.length)return null;this.index++;b=!0}else throw Error("Invalid comment at line "+this.line+": /"+this.source.charAt(this.index)+" ('/' or '*' expected)");}while(b);if(this.index===this.source.length)return null;b=this.index;c.DELIM.lastIndex=0;if(c.DELIM.test(this.source.charAt(b)))b++;else for(b++;ba?"-":"")+c);};c.prototype._parseId=function(c,a){var d=-1,e=1;"-"==c.charAt(0)&& -(e=-1,c=c.substring(1));if(b.NUMBER_DEC.test(c))d=parseInt(c);else if(b.NUMBER_HEX.test(c))d=parseInt(c.substring(2),16);else if(b.NUMBER_OCT.test(c))d=parseInt(c.substring(1),8);else throw Error("Illegal ID value at line "+this.tn.line+": "+(0>e?"-":"")+c);d=e*d|0;if(!a&&0>d)throw Error("Illegal ID range at line "+this.tn.line+": "+(0>e?"-":"")+c);return d};c.prototype._parsePackage=function(c){c=this.tn.next();if(!b.TYPEREF.test(c))throw Error("Illegal package name at line "+this.tn.line+": "+c); -var a=c;c=this.tn.next();if(c!=b.END)throw Error("Illegal end of package definition at line "+this.tn.line+": "+c+" ('"+b.END+"' expected)");return a};c.prototype._parseImport=function(c){c=this.tn.next();"public"===c&&(c=this.tn.next());if(c!==b.STRINGOPEN)throw Error("Illegal begin of import value at line "+this.tn.line+": "+c+" ('"+b.STRINGOPEN+"' expected)");var a=this.tn.next();c=this.tn.next();if(c!==b.STRINGCLOSE)throw Error("Illegal end of import value at line "+this.tn.line+": "+c+" ('"+ -b.STRINGCLOSE+"' expected)");c=this.tn.next();if(c!==b.END)throw Error("Illegal end of import definition at line "+this.tn.line+": "+c+" ('"+b.END+"' expected)");return a};c.prototype._parseOption=function(c,a){a=this.tn.next();var d=!1;a==b.COPTOPEN&&(d=!0,a=this.tn.next());if(!b.NAME.test(a))throw Error("Illegal option name in message "+c.name+" at line "+this.tn.line+": "+a);var e=a;a=this.tn.next();if(d){if(a!==b.COPTCLOSE)throw Error("Illegal custom option name delimiter in message "+c.name+ -", option "+e+" at line "+this.tn.line+": "+a+" ('"+b.COPTCLOSE+"' expected)");e="("+e+")";a=this.tn.next();b.FQTYPEREF.test(a)&&(e+=a,a=this.tn.next())}if(a!==b.EQUAL)throw Error("Illegal option operator in message "+c.name+", option "+e+" at line "+this.tn.line+": "+a+" ('"+b.EQUAL+"' expected)");a=this.tn.next();if(a===b.STRINGOPEN){if(d=this.tn.next(),a=this.tn.next(),a!==b.STRINGCLOSE)throw Error("Illegal end of option value in message "+c.name+", option "+e+" at line "+this.tn.line+": "+a+" ('"+ -b.STRINGCLOSE+"' expected)");}else if(b.NUMBER.test(a))d=this._parseNumber(a,!0);else if(b.TYPEREF.test(a))d=a;else throw Error("Illegal option value in message "+c.name+", option "+e+" at line "+this.tn.line+": "+a);a=this.tn.next();if(a!==b.END)throw Error("Illegal end of option in message "+c.name+", option "+e+" at line "+this.tn.line+": "+a+" ('"+b.END+"' expected)");c.options[e]=d};c.prototype._parseIgnoredBlock=function(c,a){var d=this.tn.next();if(!b.TYPEREF.test(d))throw Error("Illegal "+ -a+" type in "+c.name+": "+d);var e=d,d=this.tn.next();if(d!==b.OPEN)throw Error("Illegal OPEN in "+c.name+" after "+a+" "+e+" at line "+this.tn.line+": "+d);var h=1;do{d=this.tn.next();if(null===d)throw Error("Unexpected EOF in "+c.name+", "+a+" (ignored) at line "+this.tn.line+": "+e);if(d===b.OPEN)h++;else if(d===b.CLOSE&&(d=this.tn.peek(),d===b.END&&this.tn.next(),h--,0===h))break}while(1)};c.prototype._parseIgnoredStatement=function(c,a){var d;do{d=this.tn.next();if(null===d)throw Error("Unexpected EOF in "+ -c.name+", "+a+" (ignored) at line "+this.tn.line);if(d===b.END)break}while(1)};c.prototype._parseService=function(c,a){var d=this.tn.next();if(!b.NAME.test(d))throw Error("Illegal service name at line "+this.tn.line+": "+d);var e=d,h={name:e,rpc:{},options:{}},d=this.tn.next();if(d!==b.OPEN)throw Error("Illegal OPEN after service "+e+" at line "+this.tn.line+": "+d+" ('"+b.OPEN+"' expected)");do if(d=this.tn.next(),"option"===d)this._parseOption(h,d);else if("rpc"===d)this._parseServiceRPC(h,d);else if(d!== -b.CLOSE)throw Error("Illegal type for service "+e+" at line "+this.tn.line+": "+d);while(d!==b.CLOSE);c.services.push(h)};c.prototype._parseServiceRPC=function(c,a){var d=a;a=this.tn.next();if(!b.NAME.test(a))throw Error("Illegal RPC method name in service "+c.name+" at line "+this.tn.line+": "+a);var e=a,h={request:null,response:null,options:{}};a=this.tn.next();if(a!==b.COPTOPEN)throw Error("Illegal start of request type in RPC service "+c.name+"#"+e+" at line "+this.tn.line+": "+a+" ('"+b.COPTOPEN+ -"' expected)");a=this.tn.next();if(!b.TYPEREF.test(a))throw Error("Illegal request type in RPC service "+c.name+"#"+e+" at line "+this.tn.line+": "+a);h.request=a;a=this.tn.next();if(a!=b.COPTCLOSE)throw Error("Illegal end of request type in RPC service "+c.name+"#"+e+" at line "+this.tn.line+": "+a+" ('"+b.COPTCLOSE+"' expected)");a=this.tn.next();if("returns"!==a.toLowerCase())throw Error("Illegal request/response delimiter in RPC service "+c.name+"#"+e+" at line "+this.tn.line+": "+a+" ('returns' expected)"); -a=this.tn.next();if(a!=b.COPTOPEN)throw Error("Illegal start of response type in RPC service "+c.name+"#"+e+" at line "+this.tn.line+": "+a+" ('"+b.COPTOPEN+"' expected)");a=this.tn.next();h.response=a;a=this.tn.next();if(a!==b.COPTCLOSE)throw Error("Illegal end of response type in RPC service "+c.name+"#"+e+" at line "+this.tn.line+": "+a+" ('"+b.COPTCLOSE+"' expected)");a=this.tn.next();if(a===b.OPEN){do if(a=this.tn.next(),"option"===a)this._parseOption(h,a);else if(a!==b.CLOSE)throw Error("Illegal start of option in RPC service "+ -c.name+"#"+e+" at line "+this.tn.line+": "+a+" ('option' expected)");while(a!==b.CLOSE)}else if(a!==b.END)throw Error("Illegal method delimiter in RPC service "+c.name+"#"+e+" at line "+this.tn.line+": "+a+" ('"+b.END+"' or '"+b.OPEN+"' expected)");"undefined"===typeof c[d]&&(c[d]={});c[d][e]=h};c.prototype._parseMessage=function(c,a){var d={};a=this.tn.next();if(!b.NAME.test(a))throw Error("Illegal message name"+(c?" in message "+c.name:"")+" at line "+this.tn.line+": "+a);d.name=a;a=this.tn.next(); -if(a!=b.OPEN)throw Error("Illegal OPEN after message "+d.name+" at line "+this.tn.line+": "+a+" ('"+b.OPEN+"' expected)");d.fields=[];d.enums=[];d.messages=[];d.options={};do if(a=this.tn.next(),a===b.CLOSE){a=this.tn.peek();a===b.END&&this.tn.next();break}else if(b.RULE.test(a))this._parseMessageField(d,a);else if("enum"===a)this._parseEnum(d,a);else if("message"===a)this._parseMessage(d,a);else if("option"===a)this._parseOption(d,a);else if("extensions"===a)d.extensions=this._parseExtensions(d, -a);else if("extend"===a)this._parseExtend(d,a);else throw Error("Illegal token in message "+d.name+" at line "+this.tn.line+": "+a+" (type or '"+b.CLOSE+"' expected)");while(1);c.messages.push(d);return d};c.prototype._parseMessageField=function(c,a){var d={};d.rule=a;a=this.tn.next();if(!b.TYPE.test(a)&&!b.TYPEREF.test(a))throw Error("Illegal field type in message "+c.name+" at line "+this.tn.line+": "+a);d.type=a;a=this.tn.next();if(!b.NAME.test(a))throw Error("Illegal field name in message "+c.name+ -" at line "+this.tn.line+": "+a);d.name=a;a=this.tn.next();if(a!==b.EQUAL)throw Error("Illegal field number operator in message "+c.name+"#"+d.name+" at line "+this.tn.line+": "+a+" ('"+b.EQUAL+"' expected)");a=this.tn.next();try{d.id=this._parseId(a)}catch(e){throw Error("Illegal field id in message "+c.name+"#"+d.name+" at line "+this.tn.line+": "+a);}d.options={};a=this.tn.next();a===b.OPTOPEN&&(this._parseFieldOptions(c,d,a),a=this.tn.next());if(a!==b.END)throw Error("Illegal field delimiter in message "+ -c.name+"#"+d.name+" at line "+this.tn.line+": "+a+" ('"+b.END+"' expected)");c.fields.push(d)};c.prototype._parseFieldOptions=function(c,a,d){var e=!0;do{d=this.tn.next();if(d===b.OPTCLOSE)break;else if(d===b.OPTEND){if(e)throw Error("Illegal start of message field options in message "+c.name+"#"+a.name+" at line "+this.tn.line+": "+d);d=this.tn.next()}this._parseFieldOption(c,a,d);e=!1}while(1)};c.prototype._parseFieldOption=function(c,a,d){var e=!1;d===b.COPTOPEN&&(d=this.tn.next(),e=!0);if(!b.NAME.test(d))throw Error("Illegal field option in message "+ -c.name+"#"+a.name+" at line "+this.tn.line+": "+d);var h=d;d=this.tn.next();if(e){if(d!==b.COPTCLOSE)throw Error("Illegal custom field option name delimiter in message "+c.name+"#"+a.name+" at line "+this.tn.line+": "+d+" (')' expected)");h="("+h+")";d=this.tn.next();b.FQTYPEREF.test(d)&&(h+=d,d=this.tn.next())}if(d!==b.EQUAL)throw Error("Illegal field option operation in message "+c.name+"#"+a.name+" at line "+this.tn.line+": "+d+" ('=' expected)");d=this.tn.next();if(d===b.STRINGOPEN){if(e=this.tn.next(), -d=this.tn.next(),d!=b.STRINGCLOSE)throw Error("Illegal end of field value in message "+c.name+"#"+a.name+", option "+h+" at line "+this.tn.line+": "+d+" ('"+b.STRINGCLOSE+"' expected)");}else if(b.NUMBER.test(d,!0))e=this._parseNumber(d,!0);else if(b.BOOL.test(d))e="true"===d.toLowerCase();else if(b.TYPEREF.test(d))e=d;else throw Error("Illegal field option value in message "+c.name+"#"+a.name+", option "+h+" at line "+this.tn.line+": "+d);a.options[h]=e};c.prototype._parseEnum=function(c,a){var d= -{};a=this.tn.next();if(!b.NAME.test(a))throw Error("Illegal enum name in message "+c.name+" at line "+this.tn.line+": "+a);d.name=a;a=this.tn.next();if(a!==b.OPEN)throw Error("Illegal OPEN after enum "+d.name+" at line "+this.tn.line+": "+a);d.values=[];d.options={};do{a=this.tn.next();if(a===b.CLOSE){a=this.tn.peek();a===b.END&&this.tn.next();break}if("option"==a)this._parseOption(d,a);else{if(!b.NAME.test(a))throw Error("Illegal enum value name in enum "+d.name+" at line "+this.tn.line+": "+a); -this._parseEnumValue(d,a)}}while(1);c.enums.push(d)};c.prototype._parseEnumValue=function(c,a){var d={};d.name=a;a=this.tn.next();if(a!==b.EQUAL)throw Error("Illegal enum value operator in enum "+c.name+" at line "+this.tn.line+": "+a+" ('"+b.EQUAL+"' expected)");a=this.tn.next();try{d.id=this._parseId(a,!0)}catch(e){throw Error("Illegal enum value id in enum "+c.name+" at line "+this.tn.line+": "+a);}c.values.push(d);a=this.tn.next();a===b.OPTOPEN&&(this._parseFieldOptions(c,{options:{}},a),a=this.tn.next()); -if(a!==b.END)throw Error("Illegal enum value delimiter in enum "+c.name+" at line "+this.tn.line+": "+a+" ('"+b.END+"' expected)");};c.prototype._parseExtensions=function(c,a){var d=[];a=this.tn.next();"min"===a?d.push(b.ID_MIN):"max"===a?d.push(b.ID_MAX):d.push(this._parseNumber(a));a=this.tn.next();if("to"!==a)throw"Illegal extensions delimiter in message "+c.name+" at line "+this.tn.line+" ('to' expected)";a=this.tn.next();"min"===a?d.push(b.ID_MIN):"max"===a?d.push(b.ID_MAX):d.push(this._parseNumber(a)); -a=this.tn.next();if(a!==b.END)throw Error("Illegal extension delimiter in message "+c.name+" at line "+this.tn.line+": "+a+" ('"+b.END+"' expected)");return d};c.prototype._parseExtend=function(c,a){a=this.tn.next();if(!b.TYPEREF.test(a))throw Error("Illegal extended message name at line "+this.tn.line+": "+a);var d={};d.ref=a;d.fields=[];a=this.tn.next();if(a!==b.OPEN)throw Error("Illegal OPEN in extend "+d.name+" at line "+this.tn.line+": "+a+" ('"+b.OPEN+"' expected)");do if(a=this.tn.next(),a=== -b.CLOSE){a=this.tn.peek();a==b.END&&this.tn.next();break}else if(b.RULE.test(a))this._parseMessageField(d,a);else throw Error("Illegal token in extend "+d.name+" at line "+this.tn.line+": "+a+" (rule or '"+b.CLOSE+"' expected)");while(1);c.messages.push(d);return d};c.prototype.toString=function(){return"Parser"};return c}(g,g.Lang,g.DotProto.Tokenizer);g.Reflect=function(c){var b={},g=function(a,c){this.parent=a;this.name=c};g.prototype.fqn=function(){var a=this.name,c=this;do{c=c.parent;if(null== -c)break;a=c.name+"."+a}while(1);return a};g.prototype.toString=function(c){var b=this.fqn();c&&(this instanceof a?b="Message "+b:this instanceof a.Field?b="Message.Field "+b:this instanceof e?b="Enum "+b:this instanceof e.Value?b="Enum.Value "+b:this instanceof h?b="Service "+b:this instanceof h.Method?b=this instanceof h.RPCMethod?"Service.RPCMethod "+b:"Service.Method "+b:this instanceof k&&(b="Namespace "+b));return b};g.prototype.build=function(){throw Error(this.toString(!0)+" cannot be built directly"); -};b.T=g;var k=function(a,c,b){g.call(this,a,c);this.children=[];this.options=b||{}};k.prototype=Object.create(g.prototype);k.prototype.getChildren=function(a){a=a||null;if(null==a)return this.children.slice();for(var c=[],b=0;b>3,k=this.getChild(h);if(k)k.repeated&&!k.options.packed? -e.add(k.name,k.decode(g,a)):e.set(k.name,k.decode(g,a));else switch(g){case c.WIRE_TYPES.VARINT:a.readVarint32();break;case c.WIRE_TYPES.BITS32:a.offset+=4;break;case c.WIRE_TYPES.BITS64:a.offset+=8;break;case c.WIRE_TYPES.LDELIM:g=a.readVarint32();a.offset+=g;break;default:throw Error("Illegal wire type of unknown field "+h+" in "+this.toString(!0)+"#decode: "+g);}}d=this.getChildren(c.Reflect.Field);for(g=0;g>>0;if(c.Long){if(this.type==c.TYPES.int64||this.type==c.TYPES.sint64||this.type==c.TYPES.sfixed64)return"object"==typeof a&&a instanceof c.Long?a.unsigned?a.toSigned():a:c.Long.fromNumber(a,!1);if(this.type==c.TYPES.uint64||this.type==c.TYPES.fixed64)return"object"==typeof a&&a instanceof c.Long?a.unsigned?a:a.toUnsigned():c.Long.fromNumber(a,!0)}if(this.type==c.TYPES.bool)return"string"===typeof a?"true"===a:!!a;if(this.type==c.TYPES["float"]||this.type==c.TYPES["double"])return parseFloat(a); -if(this.type==c.TYPES.string)return""+a;if(this.type==c.TYPES.bytes)return a&&a instanceof p?a:p.wrap(a);if(this.type==c.TYPES["enum"]){h=this.resolvedType.getChildren(e.Value);for(d=0;da.length&&(b=b.clone().flip()),b.writeVarint32(a.remaining()),b.append(a);else if(this.type==c.TYPES.message){var d=(new p).LE();this.resolvedType.encode(a, -d);b.writeVarint32(d.offset);b.append(d.flip())}else throw Error("[INTERNAL] Illegal value to encode in "+this.toString(!0)+": "+a+" (unknown type)");return b}};d.prototype.decode=function(a,b,d){if(a!=this.type.wireType&&(d||a!=c.WIRE_TYPES.LDELIM||!this.repeated))throw Error("Illegal wire type for field "+this.toString(!0)+": "+a+" ("+this.type.wireType+" expected)");if(a==c.WIRE_TYPES.LDELIM&&this.repeated&&this.options.packed&&!d){a=b.readVarint32();a=b.offset+a;for(d=[];b.offset>>0;if(this.type==c.TYPES.sint32)return b.readZigZagVarint32()|0;if(this.type==c.TYPES.fixed32)return b.readUint32()>>>0;if(this.type==c.TYPES.sfixed32)return b.readInt32()|0;if(this.type==c.TYPES.int64)return b.readVarint64();if(this.type==c.TYPES.uint64)return b.readVarint64().toUnsigned();if(this.type==c.TYPES.sint64)return b.readZigZagVarint64();if(this.type==c.TYPES.fixed64)return b.readUint64(); -if(this.type==c.TYPES.sfixed64)return b.readInt64();if(this.type==c.TYPES.bool)return!!b.readVarint32();if(this.type==c.TYPES["enum"])return b.readVarint32();if(this.type==c.TYPES["float"])return b.readFloat();if(this.type==c.TYPES["double"])return b.readDouble();if(this.type==c.TYPES.string)return b.readVString();if(this.type==c.TYPES.bytes){a=b.readVarint32();if(b.remaining()c.Lang.ID_MAX&&(h.extensions[1]=c.Lang.ID_MAX));this.ptr.addChild(h);0h.extensions[1])throw Error("Illegal extended field id in message "+h.name+": "+e.fields[f].id+" ("+h.extensions.join(" to ")+" expected)");h.addChild(new g.Message.Field(h,e.fields[f].rule, -e.fields[f].type,e.fields[f].name,e.fields[f].id,e.fields[f].options))}else{if(!/\.?google\.protobuf\./.test(e.ref))throw Error("Extended message "+e.ref+" is not defined");}else throw Error("Not a valid message, enum, service or extend definition: "+JSON.stringify(e));else throw Error("Not a valid namespace definition: "+JSON.stringify(a));this.ptr=this.ptr.parent}this.resolved=!1;this.result=null;return this}};k.isValidImport=function(a){return!/google\/protobuf\//.test(a)};k.prototype["import"]= -function(a,b){if("string"===typeof b){c.Util.IS_NODE&&(b=require("path").resolve(b));if(this.files[b])return this.reset(),this;this.files[b]=!0}if(a.imports&&0 + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +/** + * @license ProtoBuf.js (c) 2013 Daniel Wirtz + * Released under the Apache License, Version 2.0 + * see: https://github.com/dcodeIO/ProtoBuf.js for details + */ +(function(global) { + "use strict"; + + function loadProtoBuf(ByteBuffer) { + + /** + * The ProtoBuf namespace. + * @exports ProtoBuf + * @namespace + * @expose + */ + var ProtoBuf = {}; + + /** + * ProtoBuf.js version. + * @type {string} + * @const + * @expose + */ + ProtoBuf.VERSION = "2.0.5"; + + /** + * Wire types. + * @type {Object.} + * @const + * @expose + */ + ProtoBuf.WIRE_TYPES = {}; + + /** + * Varint wire type. + * @type {number} + * @expose + */ + ProtoBuf.WIRE_TYPES.VARINT = 0; + + /** + * Fixed 64 bits wire type. + * @type {number} + * @const + * @expose + */ + ProtoBuf.WIRE_TYPES.BITS64 = 1; + + /** + * Length delimited wire type. + * @type {number} + * @const + * @expose + */ + ProtoBuf.WIRE_TYPES.LDELIM = 2; + + /** + * Start group wire type. + * @type {number} + * @const + * @deprecated Not supported. + * @expose + */ + ProtoBuf.WIRE_TYPES.STARTGROUP = 3; + + /** + * End group wire type. + * @type {number} + * @const + * @deprecated Not supported. + * @expose + */ + ProtoBuf.WIRE_TYPES.ENDGROUP = 4; + + /** + * Fixed 32 bits wire type. + * @type {number} + * @const + * @expose + */ + ProtoBuf.WIRE_TYPES.BITS32 = 5; + + /** + * Types. + * @dict + * @type {Object.} + * @const + * @expose + */ + ProtoBuf.TYPES = { + // According to the protobuf spec. + "int32": { + name: "int32", + wireType: ProtoBuf.WIRE_TYPES.VARINT + }, + "uint32": { + name: "uint32", + wireType: ProtoBuf.WIRE_TYPES.VARINT + }, + "sint32": { + name: "sint32", + wireType: ProtoBuf.WIRE_TYPES.VARINT + }, + "int64": { + name: "int64", + wireType: ProtoBuf.WIRE_TYPES.VARINT + }, + "uint64": { + name: "uint64", + wireType: ProtoBuf.WIRE_TYPES.VARINT + }, + "sint64": { + name: "sint64", + wireType: ProtoBuf.WIRE_TYPES.VARINT + }, + "bool": { + name: "bool", + wireType: ProtoBuf.WIRE_TYPES.VARINT + }, + "double": { + name: "double", + wireType: ProtoBuf.WIRE_TYPES.BITS64 + }, + "string": { + name: "string", + wireType: ProtoBuf.WIRE_TYPES.LDELIM + }, + "bytes": { + name: "bytes", + wireType: ProtoBuf.WIRE_TYPES.LDELIM + }, + "fixed32": { + name: "fixed32", + wireType: ProtoBuf.WIRE_TYPES.BITS32 + }, + "sfixed32": { + name: "sfixed32", + wireType: ProtoBuf.WIRE_TYPES.BITS32 + }, + "fixed64": { + name: "fixed64", + wireType: ProtoBuf.WIRE_TYPES.BITS64 + }, + "sfixed64": { + name: "sfixed64", + wireType: ProtoBuf.WIRE_TYPES.BITS64 + }, + "float": { + name: "float", + wireType: ProtoBuf.WIRE_TYPES.BITS32 + }, + "enum": { + name: "enum", + wireType: ProtoBuf.WIRE_TYPES.VARINT + }, + "message": { + name: "message", + wireType: ProtoBuf.WIRE_TYPES.LDELIM + } + }; + + /** + * @type {?Long} + */ + ProtoBuf.Long = ByteBuffer.Long; + + /** + * If set to `true`, field names will be converted from underscore notation to camel case. Defaults to `false`. + * Must be set prior to parsing. + * @type {boolean} + * @expose + */ + ProtoBuf.convertFieldsToCamelCase = false; + + /** + * @alias ProtoBuf.Util + * @expose + */ + ProtoBuf.Util = (function() { + "use strict"; + + // Object.create polyfill + // ref: https://developer.mozilla.org/de/docs/JavaScript/Reference/Global_Objects/Object/create + if (!Object.create) { + /** @expose */ + Object.create = function (o) { + if (arguments.length > 1) { + throw new Error('Object.create implementation only accepts the first parameter.'); + } + function F() {} + F.prototype = o; + return new F(); + }; + } + + /** + * ProtoBuf utilities. + * @exports ProtoBuf.Util + * @namespace + */ + var Util = {}; + + /** + * Flag if running in node or not. + * @type {boolean} + * @const + * @expose + */ + Util.IS_NODE = (typeof window === 'undefined' || !window.window) && typeof require === 'function' && typeof process !== 'undefined' && typeof process["nextTick"] === 'function'; + + /** + * Constructs a XMLHttpRequest object. + * @return {XMLHttpRequest} + * @throws {Error} If XMLHttpRequest is not supported + * @expose + */ + Util.XHR = function() { + // No dependencies please, ref: http://www.quirksmode.org/js/xmlhttp.html + var XMLHttpFactories = [ + function () {return new XMLHttpRequest()}, + function () {return new ActiveXObject("Msxml2.XMLHTTP")}, + function () {return new ActiveXObject("Msxml3.XMLHTTP")}, + function () {return new ActiveXObject("Microsoft.XMLHTTP")} + ]; + /** @type {?XMLHttpRequest} */ + var xhr = null; + for (var i=0;i} + * @namespace + * @expose + */ + var Lang = { // Look, so cute! + OPEN: "{", + CLOSE: "}", + OPTOPEN: "[", + OPTCLOSE: "]", + OPTEND: ",", + EQUAL: "=", + END: ";", + STRINGOPEN: '"', + STRINGCLOSE: '"', + COPTOPEN: '(', + COPTCLOSE: ')', + + DELIM: /[\s\{\}=;\[\],"\(\)]/g, + + KEYWORD: /^(?:package|option|import|message|enum|extend|service|syntax|extensions)$/, + RULE: /^(?:required|optional|repeated)$/, + TYPE: /^(?:double|float|int32|uint32|sint32|int64|uint64|sint64|fixed32|sfixed32|fixed64|sfixed64|bool|string|bytes)$/, + NAME: /^[a-zA-Z][a-zA-Z_0-9]*$/, + OPTNAME: /^(?:[a-zA-Z][a-zA-Z_0-9]*|\([a-zA-Z][a-zA-Z_0-9]*\))$/, + TYPEDEF: /^[a-zA-Z][a-zA-Z_0-9]*$/, + TYPEREF: /^(?:\.?[a-zA-Z][a-zA-Z_0-9]*)+$/, + FQTYPEREF: /^(?:\.[a-zA-Z][a-zA-Z_0-9]*)+$/, + NUMBER: /^-?(?:[1-9][0-9]*|0|0x[0-9a-fA-F]+|0[0-7]+|[0-9]*\.[0-9]+)$/, + NUMBER_DEC: /^(?:[1-9][0-9]*|0)$/, + NUMBER_HEX: /^0x[0-9a-fA-F]+$/, + NUMBER_OCT: /^0[0-7]+$/, + NUMBER_FLT: /^[0-9]*\.[0-9]+$/, + ID: /^(?:[1-9][0-9]*|0|0x[0-9a-fA-F]+|0[0-7]+)$/, + NEGID: /^\-?(?:[1-9][0-9]*|0|0x[0-9a-fA-F]+|0[0-7]+)$/, + WHITESPACE: /\s/, + STRING: /"([^"\\]*(\\.[^"\\]*)*)"/g, + BOOL: /^(?:true|false)$/i, + + ID_MIN: 1, + ID_MAX: 0x1FFFFFFF + }; + return Lang; + })(); + + /** + * Utilities to parse .proto files. + * @namespace + * @expose + */ + ProtoBuf.DotProto = {}; // Not present in "noparse" builds + + /** + * @alias ProtoBuf.DotProto.Tokenizer + * @expose + */ + ProtoBuf.DotProto.Tokenizer = (function(Lang) { + + /** + * Constructs a new Tokenizer. + * @exports ProtoBuf.DotProto.Tokenizer + * @class A ProtoBuf .proto Tokenizer. + * @param {string} proto Proto to tokenize + * @constructor + */ + var Tokenizer = function(proto) { + + /** + * Source to parse. + * @type {string} + * @expose + */ + this.source = ""+proto; + + /** + * Current index. + * @type {number} + * @expose + */ + this.index = 0; + + /** + * Current line. + * @type {number} + * @expose + */ + this.line = 1; + + /** + * Stacked values. + * @type {Array} + * @expose + */ + this.stack = []; + + /** + * Whether currently reading a string or not. + * @type {boolean} + * @expose + */ + this.readingString = false; + }; + + /** + * Reads a string beginning at the current index. + * @return {string} The string + * @throws {Error} If it's not a valid string + * @private + */ + Tokenizer.prototype._readString = function() { + Lang.STRING.lastIndex = this.index-1; // Include the open quote + var match; + if ((match = Lang.STRING.exec(this.source)) !== null) { + var s = match[1]; + this.index = Lang.STRING.lastIndex; + this.stack.push(Lang.STRINGCLOSE); + return s; + } + throw(new Error("Illegal string value at line "+this.line+", index "+this.index)); + }; + + /** + * Gets the next token and advances by one. + * @return {?string} Token or `null` on EOF + * @throws {Error} If it's not a valid proto file + * @expose + */ + Tokenizer.prototype.next = function() { + if (this.stack.length > 0) { + return this.stack.shift(); + } + if (this.index >= this.source.length) { + return null; // No more tokens + } + if (this.readingString) { + this.readingString = false; + return this._readString(); + } + var repeat, last; + do { + repeat = false; + // Strip white spaces + while (Lang.WHITESPACE.test(last = this.source.charAt(this.index))) { + this.index++; + if (last === "\n") this.line++; + if (this.index === this.source.length) return null; + } + // Strip comments + if (this.source.charAt(this.index) === '/') { + if (this.source.charAt(++this.index) === '/') { // Single line + while (this.source.charAt(this.index) !== "\n") { + this.index++; + if (this.index == this.source.length) return null; + } + this.index++; + this.line++; + repeat = true; + } else if (this.source.charAt(this.index) === '*') { /* Block */ + last = ''; + while (last+(last=this.source.charAt(this.index)) !== '*/') { + this.index++; + if (last === "\n") this.line++; + if (this.index === this.source.length) return null; + } + this.index++; + repeat = true; + } else { + throw(new Error("Invalid comment at line "+this.line+": /"+this.source.charAt(this.index)+" ('/' or '*' expected)")); + } + } + } while (repeat); + if (this.index === this.source.length) return null; + + // Read the next token + var end = this.index; + Lang.DELIM.lastIndex = 0; + var delim = Lang.DELIM.test(this.source.charAt(end)); + if (!delim) { + end++; + while(end < this.source.length && !Lang.DELIM.test(this.source.charAt(end))) { + end++; + } + } else { + end++; + } + var token = this.source.substring(this.index, this.index = end); + if (token === Lang.STRINGOPEN) { + this.readingString = true; + } + return token; + }; + + /** + * Peeks for the next token. + * @return {?string} Token or `null` on EOF + * @throws {Error} If it's not a valid proto file + * @expose + */ + Tokenizer.prototype.peek = function() { + if (this.stack.length == 0) { + var token = this.next(); + if (token === null) return null; + this.stack.push(token); + } + return this.stack[0]; + }; + + /** + * Returns a string representation of this object. + * @return {string} String representation as of "Tokenizer(index/length)" + * @expose + */ + Tokenizer.prototype.toString = function() { + return "Tokenizer("+this.index+"/"+this.source.length+" at line "+this.line+")"; + }; + + return Tokenizer; + + })(ProtoBuf.Lang); + + /** + * @alias ProtoBuf.DotProto.Parser + * @expose + */ + ProtoBuf.DotProto.Parser = (function(ProtoBuf, Lang, Tokenizer) { + "use strict"; + + /** + * Constructs a new Parser. + * @exports ProtoBuf.DotProto.Parser + * @class A ProtoBuf .proto parser. + * @param {string} proto Protocol source + * @constructor + */ + var Parser = function(proto) { + + /** + * Tokenizer. + * @type {ProtoBuf.DotProto.Tokenizer} + * @expose + */ + this.tn = new Tokenizer(proto); + }; + + /** + * Runs the parser. + * @return {{package: string|null, messages: Array., enums: Array., imports: Array., options: object}} + * @throws {Error} If the source cannot be parsed + * @expose + */ + Parser.prototype.parse = function() { + var topLevel = { + "name": "[ROOT]", // temporary + "package": null, + "messages": [], + "enums": [], + "imports": [], + "options": {}, + "services": [] + }; + var token, header = true; + do { + token = this.tn.next(); + if (token == null) { + break; // No more messages + } + if (token == 'package') { + if (!header) { + throw(new Error("Illegal package definition at line "+this.tn.line+": Must be declared before the first message or enum")); + } + if (topLevel["package"] !== null) { + throw(new Error("Illegal package definition at line "+this.tn.line+": Package already declared")); + } + topLevel["package"] = this._parsePackage(token); + } else if (token == 'import') { + if (!header) { + throw(new Error("Illegal import definition at line "+this.tn.line+": Must be declared before the first message or enum")); + } + topLevel.imports.push(this._parseImport(token)); + } else if (token === 'message') { + this._parseMessage(topLevel, token); + header = false; + } else if (token === 'enum') { + this._parseEnum(topLevel, token); + header = false; + } else if (token === 'option') { + if (!header) { + throw(new Error("Illegal option definition at line "+this.tn.line+": Must be declared before the first message or enum")); + } + this._parseOption(topLevel, token); + } else if (token === 'service') { + this._parseService(topLevel, token); + } else if (token === 'extend') { + this._parseExtend(topLevel, token); + } else if (token === 'syntax') { + this._parseIgnoredStatement(topLevel, token); + } else { + throw(new Error("Illegal top level declaration at line "+this.tn.line+": "+token)); + } + } while (true); + delete topLevel["name"]; + return topLevel; + }; + + /** + * Parses a number value. + * @param {string} val Number value to parse + * @return {number} Number + * @throws {Error} If the number value is invalid + * @private + */ + Parser.prototype._parseNumber = function(val) { + var sign = 1; + if (val.charAt(0) == '-') { + sign = -1; val = val.substring(1); + } + if (Lang.NUMBER_DEC.test(val)) { + return sign*parseInt(val, 10); + } else if (Lang.NUMBER_HEX.test(val)) { + return sign*parseInt(val.substring(2), 16); + } else if (Lang.NUMBER_OCT.test(val)) { + return sign*parseInt(val.substring(1), 8); + } else if (Lang.NUMBER_FLT.test(val)) { + return sign*parseFloat(val); + } + throw(new Error("Illegal number value at line "+this.tn.line+": "+(sign < 0 ? '-' : '')+val)); + }; + + /** + * Parses an ID value. + * @param {string} val ID value to parse + * @param {boolean=} neg Whether the ID may be negative, defaults to `false` + * @returns {number} ID + * @throws {Error} If the ID value is invalid + * @private + */ + Parser.prototype._parseId = function(val, neg) { + var id = -1; + var sign = 1; + if (val.charAt(0) == '-') { + sign = -1; val = val.substring(1); + } + if (Lang.NUMBER_DEC.test(val)) { + id = parseInt(val); + } else if (Lang.NUMBER_HEX.test(val)) { + id = parseInt(val.substring(2), 16); + } else if (Lang.NUMBER_OCT.test(val)) { + id = parseInt(val.substring(1), 8); + } else { + throw(new Error("Illegal ID value at line "+this.tn.line+": "+(sign < 0 ? '-' : '')+val)); + } + id = (sign*id)|0; // Force to 32bit + if (!neg && id < 0) { + throw(new Error("Illegal ID range at line "+this.tn.line+": "+(sign < 0 ? '-' : '')+val)); + } + return id; + }; + + /** + * Parses the package definition. + * @param {string} token Initial token + * @return {string} Package name + * @throws {Error} If the package definition cannot be parsed + * @private + */ + Parser.prototype._parsePackage = function(token) { + token = this.tn.next(); + if (!Lang.TYPEREF.test(token)) { + throw(new Error("Illegal package name at line "+this.tn.line+": "+token)); + } + var pkg = token; + token = this.tn.next(); + if (token != Lang.END) { + throw(new Error("Illegal end of package definition at line "+this.tn.line+": "+token+" ('"+Lang.END+"' expected)")); + } + return pkg; + }; + + /** + * Parses an import definition. + * @param {string} token Initial token + * @return {string} Import file name + * @throws {Error} If the import definition cannot be parsed + * @private + */ + Parser.prototype._parseImport = function(token) { + token = this.tn.next(); + if (token === "public") { + token = this.tn.next(); + } + if (token !== Lang.STRINGOPEN) { + throw(new Error("Illegal begin of import value at line "+this.tn.line+": "+token+" ('"+Lang.STRINGOPEN+"' expected)")); + } + var imported = this.tn.next(); + token = this.tn.next(); + if (token !== Lang.STRINGCLOSE) { + throw(new Error("Illegal end of import value at line "+this.tn.line+": "+token+" ('"+Lang.STRINGCLOSE+"' expected)")); + } + token = this.tn.next(); + if (token !== Lang.END) { + throw(new Error("Illegal end of import definition at line "+this.tn.line+": "+token+" ('"+Lang.END+"' expected)")); + } + return imported; + }; + + /** + * Parses a namespace option. + * @param {Object} parent Parent definition + * @param {string} token Initial token + * @throws {Error} If the option cannot be parsed + * @private + */ + Parser.prototype._parseOption = function(parent, token) { + token = this.tn.next(); + var custom = false; + if (token == Lang.COPTOPEN) { + custom = true; + token = this.tn.next(); + } + if (!Lang.NAME.test(token)) { + // we can allow options of the form google.protobuf.* since they will just get ignored anyways + if (!/google\.protobuf\./.test(token)) { + throw(new Error("Illegal option name in message "+parent.name+" at line "+this.tn.line+": "+token)); + } + } + var name = token; + token = this.tn.next(); + if (custom) { // (my_method_option).foo, (my_method_option), some_method_option + if (token !== Lang.COPTCLOSE) { + throw(new Error("Illegal custom option name delimiter in message "+parent.name+", option "+name+" at line "+this.tn.line+": "+token+" ('"+Lang.COPTCLOSE+"' expected)")); + } + name = '('+name+')'; + token = this.tn.next(); + if (Lang.FQTYPEREF.test(token)) { + name += token; + token = this.tn.next(); + } + } + if (token !== Lang.EQUAL) { + throw(new Error("Illegal option operator in message "+parent.name+", option "+name+" at line "+this.tn.line+": "+token+" ('"+Lang.EQUAL+"' expected)")); + } + var value; + token = this.tn.next(); + if (token === Lang.STRINGOPEN) { + value = this.tn.next(); + token = this.tn.next(); + if (token !== Lang.STRINGCLOSE) { + throw(new Error("Illegal end of option value in message "+parent.name+", option "+name+" at line "+this.tn.line+": "+token+" ('"+Lang.STRINGCLOSE+"' expected)")); + } + } else { + if (Lang.NUMBER.test(token)) { + value = this._parseNumber(token, true); + } else if (Lang.TYPEREF.test(token)) { + value = token; + } else { + throw(new Error("Illegal option value in message "+parent.name+", option "+name+" at line "+this.tn.line+": "+token)); + } + } + token = this.tn.next(); + if (token !== Lang.END) { + throw(new Error("Illegal end of option in message "+parent.name+", option "+name+" at line "+this.tn.line+": "+token+" ('"+Lang.END+"' expected)")); + } + parent["options"][name] = value; + }; + + /** + * Parses an ignored block of the form ['keyword', 'typeref', '{' ... '}']. + * @param {Object} parent Parent definition + * @param {string} keyword Initial token + * @throws {Error} If the directive cannot be parsed + * @private + */ + Parser.prototype._parseIgnoredBlock = function(parent, keyword) { + var token = this.tn.next(); + if (!Lang.TYPEREF.test(token)) { + throw(new Error("Illegal "+keyword+" type in "+parent.name+": "+token)); + } + var name = token; + token = this.tn.next(); + if (token !== Lang.OPEN) { + throw(new Error("Illegal OPEN in "+parent.name+" after "+keyword+" "+name+" at line "+this.tn.line+": "+token)); + } + var depth = 1; + do { + token = this.tn.next(); + if (token === null) { + throw(new Error("Unexpected EOF in "+parent.name+", "+keyword+" (ignored) at line "+this.tn.line+": "+name)); + } + if (token === Lang.OPEN) { + depth++; + } else if (token === Lang.CLOSE) { + token = this.tn.peek(); + if (token === Lang.END) this.tn.next(); + depth--; + if (depth === 0) { + break; + } + } + } while(true); + }; + + /** + * Parses an ignored statement of the form ['keyword', ..., ';']. + * @param {Object} parent Parent definition + * @param {string} keyword Initial token + * @throws {Error} If the directive cannot be parsed + * @private + */ + Parser.prototype._parseIgnoredStatement = function(parent, keyword) { + var token; + do { + token = this.tn.next(); + if (token === null) { + throw(new Error("Unexpected EOF in "+parent.name+", "+keyword+" (ignored) at line "+this.tn.line)); + } + if (token === Lang.END) break; + } while (true); + }; + + /** + * Parses a service definition. + * @param {Object} parent Parent definition + * @param {string} keyword Initial token + * @throws {Error} If the service cannot be parsed + * @private + */ + Parser.prototype._parseService = function(parent, keyword) { + var token = this.tn.next(); + if (!Lang.NAME.test(token)) { + throw(new Error("Illegal service name at line "+this.tn.line+": "+token)); + } + var name = token; + var svc = { + "name": name, + "rpc": {}, + "options": {} + }; + token = this.tn.next(); + if (token !== Lang.OPEN) { + throw(new Error("Illegal OPEN after service "+name+" at line "+this.tn.line+": "+token+" ('"+Lang.OPEN+"' expected)")); + } + do { + token = this.tn.next(); + if (token === "option") { + this._parseOption(svc, token); + } else if (token === 'rpc') { + this._parseServiceRPC(svc, token); + } else if (token !== Lang.CLOSE) { + throw(new Error("Illegal type for service "+name+" at line "+this.tn.line+": "+token)); + } + } while (token !== Lang.CLOSE); + parent["services"].push(svc); + }; + + /** + * Parses a RPC service definition of the form ['rpc', name, (request), 'returns', (response)]. + * @param {Object} svc Parent definition + * @param {string} token Initial token + * @private + */ + Parser.prototype._parseServiceRPC = function(svc, token) { + var type = token; + token = this.tn.next(); + if (!Lang.NAME.test(token)) { + throw(new Error("Illegal RPC method name in service "+svc["name"]+" at line "+this.tn.line+": "+token)); + } + var name = token; + var method = { + "request": null, + "response": null, + "options": {} + }; + token = this.tn.next(); + if (token !== Lang.COPTOPEN) { + throw(new Error("Illegal start of request type in RPC service "+svc["name"]+"#"+name+" at line "+this.tn.line+": "+token+" ('"+Lang.COPTOPEN+"' expected)")); + } + token = this.tn.next(); + if (!Lang.TYPEREF.test(token)) { + throw(new Error("Illegal request type in RPC service "+svc["name"]+"#"+name+" at line "+this.tn.line+": "+token)); + } + method["request"] = token; + token = this.tn.next(); + if (token != Lang.COPTCLOSE) { + throw(new Error("Illegal end of request type in RPC service "+svc["name"]+"#"+name+" at line "+this.tn.line+": "+token+" ('"+Lang.COPTCLOSE+"' expected)")) + } + token = this.tn.next(); + if (token.toLowerCase() !== "returns") { + throw(new Error("Illegal request/response delimiter in RPC service "+svc["name"]+"#"+name+" at line "+this.tn.line+": "+token+" ('returns' expected)")); + } + token = this.tn.next(); + if (token != Lang.COPTOPEN) { + throw(new Error("Illegal start of response type in RPC service "+svc["name"]+"#"+name+" at line "+this.tn.line+": "+token+" ('"+Lang.COPTOPEN+"' expected)")); + } + token = this.tn.next(); + method["response"] = token; + token = this.tn.next(); + if (token !== Lang.COPTCLOSE) { + throw(new Error("Illegal end of response type in RPC service "+svc["name"]+"#"+name+" at line "+this.tn.line+": "+token+" ('"+Lang.COPTCLOSE+"' expected)")) + } + token = this.tn.next(); + if (token === Lang.OPEN) { + do { + token = this.tn.next(); + if (token === 'option') { + this._parseOption(method, token); // <- will fail for the custom-options example + } else if (token !== Lang.CLOSE) { + throw(new Error("Illegal start of option in RPC service "+svc["name"]+"#"+name+" at line "+this.tn.line+": "+token+" ('option' expected)")); + } + } while (token !== Lang.CLOSE); + } else if (token !== Lang.END) { + throw(new Error("Illegal method delimiter in RPC service "+svc["name"]+"#"+name+" at line "+this.tn.line+": "+token+" ('"+Lang.END+"' or '"+Lang.OPEN+"' expected)")); + } + if (typeof svc[type] === 'undefined') svc[type] = {}; + svc[type][name] = method; + }; + + /** + * Parses a message definition. + * @param {Object} parent Parent definition + * @param {string} token First token + * @return {Object} + * @throws {Error} If the message cannot be parsed + * @private + */ + Parser.prototype._parseMessage = function(parent, token) { + /** @dict */ + var msg = {}; // Note: At some point we might want to exclude the parser, so we need a dict. + token = this.tn.next(); + if (!Lang.NAME.test(token)) { + throw(new Error("Illegal message name"+(parent ? " in message "+parent["name"] : "")+" at line "+this.tn.line+": "+token)); + } + msg["name"] = token; + token = this.tn.next(); + if (token != Lang.OPEN) { + throw(new Error("Illegal OPEN after message "+msg.name+" at line "+this.tn.line+": "+token+" ('"+Lang.OPEN+"' expected)")); + } + msg["fields"] = []; // Note: Using arrays to support also browser that cannot preserve order of object keys. + msg["enums"] = []; + msg["messages"] = []; + msg["options"] = {}; + // msg["extensions"] = undefined + do { + token = this.tn.next(); + if (token === Lang.CLOSE) { + token = this.tn.peek(); + if (token === Lang.END) this.tn.next(); + break; + } else if (Lang.RULE.test(token)) { + this._parseMessageField(msg, token); + } else if (token === "enum") { + this._parseEnum(msg, token); + } else if (token === "message") { + this._parseMessage(msg, token); + } else if (token === "option") { + this._parseOption(msg, token); + } else if (token === "extensions") { + msg["extensions"] = this._parseExtensions(msg, token); + } else if (token === "extend") { + this._parseExtend(msg, token); + } else { + throw(new Error("Illegal token in message "+msg.name+" at line "+this.tn.line+": "+token+" (type or '"+Lang.CLOSE+"' expected)")); + } + } while (true); + parent["messages"].push(msg); + return msg; + }; + + /** + * Parses a message field. + * @param {Object} msg Message definition + * @param {string} token Initial token + * @throws {Error} If the message field cannot be parsed + * @private + */ + Parser.prototype._parseMessageField = function(msg, token) { + /** @dict */ + var fld = {}; + fld["rule"] = token; + token = this.tn.next(); + if (!Lang.TYPE.test(token) && !Lang.TYPEREF.test(token)) { + throw(new Error("Illegal field type in message "+msg.name+" at line "+this.tn.line+": "+token)); + } + fld["type"] = token; + token = this.tn.next(); + if (!Lang.NAME.test(token)) { + throw(new Error("Illegal field name in message "+msg.name+" at line "+this.tn.line+": "+token)); + } + fld["name"] = token; + token = this.tn.next(); + if (token !== Lang.EQUAL) { + throw(new Error("Illegal field number operator in message "+msg.name+"#"+fld.name+" at line "+this.tn.line+": "+token+" ('"+Lang.EQUAL+"' expected)")); + } + token = this.tn.next(); + try { + fld["id"] = this._parseId(token); + } catch (e) { + throw(new Error("Illegal field id in message "+msg.name+"#"+fld.name+" at line "+this.tn.line+": "+token)); + } + /** @dict */ + fld["options"] = {}; + token = this.tn.next(); + if (token === Lang.OPTOPEN) { + this._parseFieldOptions(msg, fld, token); + token = this.tn.next(); + } + if (token !== Lang.END) { + throw(new Error("Illegal field delimiter in message "+msg.name+"#"+fld.name+" at line "+this.tn.line+": "+token+" ('"+Lang.END+"' expected)")); + } + msg["fields"].push(fld); + }; + + /** + * Parses a set of field option definitions. + * @param {Object} msg Message definition + * @param {Object} fld Field definition + * @param {string} token Initial token + * @throws {Error} If the message field options cannot be parsed + * @private + */ + Parser.prototype._parseFieldOptions = function(msg, fld, token) { + var first = true; + do { + token = this.tn.next(); + if (token === Lang.OPTCLOSE) { + break; + } else if (token === Lang.OPTEND) { + if (first) { + throw(new Error("Illegal start of message field options in message "+msg.name+"#"+fld.name+" at line "+this.tn.line+": "+token)); + } + token = this.tn.next(); + } + this._parseFieldOption(msg, fld, token); + first = false; + } while (true); + }; + + /** + * Parses a single field option. + * @param {Object} msg Message definition + * @param {Object} fld Field definition + * @param {string} token Initial token + * @throws {Error} If the mesage field option cannot be parsed + * @private + */ + Parser.prototype._parseFieldOption = function(msg, fld, token) { + var custom = false; + if (token === Lang.COPTOPEN) { + token = this.tn.next(); + custom = true; + } + if (!Lang.NAME.test(token)) { + throw(new Error("Illegal field option in message "+msg.name+"#"+fld.name+" at line "+this.tn.line+": "+token)); + } + var name = token; + token = this.tn.next(); + if (custom) { + if (token !== Lang.COPTCLOSE) { + throw(new Error("Illegal custom field option name delimiter in message "+msg.name+"#"+fld.name+" at line "+this.tn.line+": "+token+" (')' expected)")); + } + name = '('+name+')'; + token = this.tn.next(); + if (Lang.FQTYPEREF.test(token)) { + name += token; + token = this.tn.next(); + } + } + if (token !== Lang.EQUAL) { + throw(new Error("Illegal field option operation in message "+msg.name+"#"+fld.name+" at line "+this.tn.line+": "+token+" ('=' expected)")); + } + var value; + token = this.tn.next(); + if (token === Lang.STRINGOPEN) { + value = this.tn.next(); + token = this.tn.next(); + if (token != Lang.STRINGCLOSE) { + throw(new Error("Illegal end of field value in message "+msg.name+"#"+fld.name+", option "+name+" at line "+this.tn.line+": "+token+" ('"+Lang.STRINGCLOSE+"' expected)")); + } + } else if (Lang.NUMBER.test(token, true)) { + value = this._parseNumber(token, true); + } else if (Lang.BOOL.test(token)) { + value = token.toLowerCase() === 'true'; + } else if (Lang.TYPEREF.test(token)) { + value = token; // TODO: Resolve? + } else { + throw(new Error("Illegal field option value in message "+msg.name+"#"+fld.name+", option "+name+" at line "+this.tn.line+": "+token)); + } + fld["options"][name] = value; + }; + + /** + * Parses an enum. + * @param {Object} msg Message definition + * @param {string} token Initial token + * @throws {Error} If the enum cannot be parsed + * @private + */ + Parser.prototype._parseEnum = function(msg, token) { + /** @dict */ + var enm = {}; + token = this.tn.next(); + if (!Lang.NAME.test(token)) { + throw(new Error("Illegal enum name in message "+msg.name+" at line "+this.tn.line+": "+token)); + } + enm["name"] = token; + token = this.tn.next(); + if (token !== Lang.OPEN) { + throw(new Error("Illegal OPEN after enum "+enm.name+" at line "+this.tn.line+": "+token)); + } + enm["values"] = []; + enm["options"] = {}; + do { + token = this.tn.next(); + if (token === Lang.CLOSE) { + token = this.tn.peek(); + if (token === Lang.END) this.tn.next(); + break; + } + if (token == 'option') { + this._parseOption(enm, token); + } else { + if (!Lang.NAME.test(token)) { + throw(new Error("Illegal enum value name in enum "+enm.name+" at line "+this.tn.line+": "+token)); + } + this._parseEnumValue(enm, token); + } + } while (true); + msg["enums"].push(enm); + }; + + /** + * Parses an enum value. + * @param {Object} enm Enum definition + * @param {string} token Initial token + * @throws {Error} If the enum value cannot be parsed + * @private + */ + Parser.prototype._parseEnumValue = function(enm, token) { + /** @dict */ + var val = {}; + val["name"] = token; + token = this.tn.next(); + if (token !== Lang.EQUAL) { + throw(new Error("Illegal enum value operator in enum "+enm.name+" at line "+this.tn.line+": "+token+" ('"+Lang.EQUAL+"' expected)")); + } + token = this.tn.next(); + try { + val["id"] = this._parseId(token, true); + } catch (e) { + throw(new Error("Illegal enum value id in enum "+enm.name+" at line "+this.tn.line+": "+token)); + } + enm["values"].push(val); + token = this.tn.next(); + if (token === Lang.OPTOPEN) { + var opt = { 'options' : {} }; // TODO: Actually expose them somehow. + this._parseFieldOptions(enm, opt, token); + token = this.tn.next(); + } + if (token !== Lang.END) { + throw(new Error("Illegal enum value delimiter in enum "+enm.name+" at line "+this.tn.line+": "+token+" ('"+Lang.END+"' expected)")); + } + }; + + /** + * Parses an extensions statement. + * @param {Object} msg Message object + * @param {string} token Initial token + * @throws {Error} If the extensions statement cannot be parsed + * @private + */ + Parser.prototype._parseExtensions = function(msg, token) { + /** @type {Array.} */ + var range = []; + token = this.tn.next(); + if (token === "min") { // FIXME: Does the official implementation support this? + range.push(Lang.ID_MIN); + } else if (token === "max") { + range.push(Lang.ID_MAX); + } else { + range.push(this._parseNumber(token)); + } + token = this.tn.next(); + if (token !== 'to') { + throw("Illegal extensions delimiter in message "+msg.name+" at line "+this.tn.line+" ('to' expected)"); + } + token = this.tn.next(); + if (token === "min") { + range.push(Lang.ID_MIN); + } else if (token === "max") { + range.push(Lang.ID_MAX); + } else { + range.push(this._parseNumber(token)); + } + token = this.tn.next(); + if (token !== Lang.END) { + throw(new Error("Illegal extension delimiter in message "+msg.name+" at line "+this.tn.line+": "+token+" ('"+Lang.END+"' expected)")); + } + return range; + }; + + /** + * Parses an extend block. + * @param {Object} parent Parent object + * @param {string} token Initial token + * @throws {Error} If the extend block cannot be parsed + * @private + */ + Parser.prototype._parseExtend = function(parent, token) { + token = this.tn.next(); + if (!Lang.TYPEREF.test(token)) { + throw(new Error("Illegal extended message name at line "+this.tn.line+": "+token)); + } + /** @dict */ + var ext = {}; + ext["ref"] = token; + ext["fields"] = []; + token = this.tn.next(); + if (token !== Lang.OPEN) { + throw(new Error("Illegal OPEN in extend "+ext.name+" at line "+this.tn.line+": "+token+" ('"+Lang.OPEN+"' expected)")); + } + do { + token = this.tn.next(); + if (token === Lang.CLOSE) { + token = this.tn.peek(); + if (token == Lang.END) this.tn.next(); + break; + } else if (Lang.RULE.test(token)) { + this._parseMessageField(ext, token); + } else { + throw(new Error("Illegal token in extend "+ext.name+" at line "+this.tn.line+": "+token+" (rule or '"+Lang.CLOSE+"' expected)")); + } + } while (true); + parent["messages"].push(ext); + return ext; + }; + + /** + * Returns a string representation of this object. + * @returns {string} String representation as of "Parser" + */ + Parser.prototype.toString = function() { + return "Parser"; + }; + + return Parser; + + })(ProtoBuf, ProtoBuf.Lang, ProtoBuf.DotProto.Tokenizer); + + /** + * @alias ProtoBuf.Reflect + * @expose + */ + ProtoBuf.Reflect = (function(ProtoBuf) { + "use strict"; + + /** + * @exports ProtoBuf.Reflect + * @namespace + */ + var Reflect = {}; + + /** + * Constructs a Reflect base class. + * @exports ProtoBuf.Reflect.T + * @constructor + * @param {ProtoBuf.Reflect.T} parent Parent object + * @param {string} name Object name + */ + var T = function(parent, name) { + /** + * Parent object. + * @type {ProtoBuf.Reflect.T|null} + * @expose + */ + this.parent = parent; + + /** + * Object name in namespace. + * @type {string} + * @expose + */ + this.name = name; + }; + + /** + * Returns the fully qualified name of this object. + * @returns {string} Fully qualified name as of ".PATH.TO.THIS" + * @expose + */ + T.prototype.fqn = function() { + var name = this.name, + ptr = this; + do { + ptr = ptr.parent; + if (ptr == null) break; + name = ptr.name+"."+name; + } while (true); + return name; + }; + + /** + * Returns a string representation of this Reflect object (its fully qualified name). + * @param {boolean=} includeClass Set to true to include the class name. Defaults to false. + * @return String representation + * @expose + */ + T.prototype.toString = function(includeClass) { + var name = this.fqn(); + if (includeClass) { + if (this instanceof Message) { + name = "Message "+name; + } else if (this instanceof Message.Field) { + name = "Message.Field "+name; + } else if (this instanceof Enum) { + name = "Enum "+name; + } else if (this instanceof Enum.Value) { + name = "Enum.Value "+name; + } else if (this instanceof Service) { + name = "Service "+name; + } else if (this instanceof Service.Method) { + if (this instanceof Service.RPCMethod) { + name = "Service.RPCMethod "+name; + } else { + name = "Service.Method "+name; // Should not happen as it is abstract + } + } else if (this instanceof Namespace) { + name = "Namespace "+name; + } + } + return name; + }; + + /** + * Builds this type. + * @throws {Error} If this type cannot be built directly + * @expose + */ + T.prototype.build = function() { + throw(new Error(this.toString(true)+" cannot be built directly")); + }; + + /** + * @alias ProtoBuf.Reflect.T + * @expose + */ + Reflect.T = T; + + /** + * Constructs a new Namespace. + * @exports ProtoBuf.Reflect.Namespace + * @param {ProtoBuf.Reflect.Namespace|null} parent Namespace parent + * @param {string} name Namespace name + * @param {Object.} options Namespace options + * @constructor + * @extends ProtoBuf.Reflect.T + */ + var Namespace = function(parent, name, options) { + T.call(this, parent, name); + + /** + * Children inside the namespace. + * @type {Array.} + */ + this.children = []; + + /** + * Options. + * @type {Object.} + */ + this.options = options || {}; + }; + + // Extends T + Namespace.prototype = Object.create(T.prototype); + + /** + * Returns an array of the namespace's children. + * @param {ProtoBuf.Reflect.T=} type Filter type (returns instances of this type only). Defaults to null (all children). + * @return {Array.} + * @expose + */ + Namespace.prototype.getChildren = function(type) { + type = type || null; + if (type == null) { + return this.children.slice(); + } + var children = []; + for (var i=0; i} Runtime namespace + * @expose + */ + Namespace.prototype.build = function() { + /** @dict */ + var ns = {}; + var children = this.getChildren(), child; + for (var i=0; i} + */ + Namespace.prototype.buildOpt = function() { + var opt = {}; + var keys = Object.keys(this.options); + for (var i=0; i}null} Option value or NULL if there is no such option + */ + Namespace.prototype.getOption = function(name) { + if (typeof name == 'undefined') { + return this.options; + } + return typeof this.options[name] != 'undefined' ? this.options[name] : null; + }; + + /** + * @alias ProtoBuf.Reflect.Namespace + * @expose + */ + Reflect.Namespace = Namespace; + + /** + * Constructs a new Message. + * @exports ProtoBuf.Reflect.Message + * @param {ProtoBuf.Reflect.Namespace} parent Parent message or namespace + * @param {string} name Message name + * @param {Object.} options Message options + * @constructor + * @extends ProtoBuf.Reflect.Namespace + */ + var Message = function(parent, name, options) { + Namespace.call(this, parent, name, options); + + /** + * Extensions range. + * @type {!Array.} + * @expose + */ + this.extensions = [ProtoBuf.Lang.ID_MIN, ProtoBuf.Lang.ID_MAX]; + + /** + * Runtime message class. + * @type {?function(new:ProtoBuf.Builder.Message)} + * @expose + */ + this.clazz = null; + }; + + // Extends Namespace + Message.prototype = Object.create(Namespace.prototype); + + /** + * Builds the message and returns the runtime counterpart, which is a fully functional class. + * @see ProtoBuf.Builder.Message + * @param {boolean=} rebuild Whether to rebuild or not, defaults to false + * @return {ProtoBuf.Reflect.Message} Message class + * @throws {Error} If the message cannot be built + * @expose + */ + Message.prototype.build = function(rebuild) { + if (this.clazz && !rebuild) return this.clazz; + + // We need to create a prototyped Message class in an isolated scope + var clazz = (function(ProtoBuf, T) { + var fields = T.getChildren(Reflect.Message.Field); + + /** + * Constructs a new runtime Message. + * @name ProtoBuf.Builder.Message + * @class Barebone of all runtime messages. + * @param {Object.|...[string]} values Preset values + * @constructor + * @throws {Error} If the message cannot be created + */ + var Message = function(values) { + ProtoBuf.Builder.Message.call(this); + var i, field; + + // Create fields on the object itself to allow setting and getting through Message#fieldname + for (i=0; i} + * @expose + */ + var O_o; // for cc + + if (Object.defineProperty) { + Object.defineProperty(Message, '$options', { + 'value': T.buildOpt(), + 'enumerable': false, + 'configurable': false, + 'writable': false + }); + } + + return Message; + + })(ProtoBuf, this); + + // Static enums and prototyped sub-messages + var children = this.getChildren(); + for (var i=0; i 0)) { + var tag = buffer.readVarint32(); + var wireType = tag & 0x07, + id = tag >> 3; + var field = this.getChild(id); // Message.Field only + if (!field) { + // "messages created by your new code can be parsed by your old code: old binaries simply ignore the new field when parsing." + switch (wireType) { + case ProtoBuf.WIRE_TYPES.VARINT: + buffer.readVarint32(); + break; + case ProtoBuf.WIRE_TYPES.BITS32: + buffer.offset += 4; + break; + case ProtoBuf.WIRE_TYPES.BITS64: + buffer.offset += 8; + break; + case ProtoBuf.WIRE_TYPES.LDELIM: + var len = buffer.readVarint32(); + buffer.offset += len; + break; + default: + throw(new Error("Illegal wire type of unknown field "+id+" in "+this.toString(true)+"#decode: "+wireType)); + } + continue; + } + if (field.repeated && !field.options["packed"]) { + msg.add(field.name, field.decode(wireType, buffer)); + } else { + msg.set(field.name, field.decode(wireType, buffer)); + } + } + // Check if all required fields are present + var fields = this.getChildren(ProtoBuf.Reflect.Field); + for (var i=0; i=} options Options + * @constructor + * @extends ProtoBuf.Reflect.T + */ + var Field = function(message, rule, type, name, id, options) { + T.call(this, message, name); + + /** + * Message field required flag. + * @type {boolean} + * @expose + */ + this.required = rule == "required"; + + /** + * Message field repeated flag. + * @type {boolean} + * @expose + */ + this.repeated = rule == "repeated"; + + /** + * Message field type. Type reference string if unresolved, protobuf type if resolved. + * @type {string|{name: string, wireType: number} + * @expose + */ + this.type = type; + + /** + * Resolved type reference inside the global namespace. + * @type {ProtoBuf.Reflect.T|null} + * @expose + */ + this.resolvedType = null; + + /** + * Unique message field id. + * @type {number} + * @expose + */ + this.id = id; + + /** + * Message field options. + * @type {!Object.} + * @dict + * @expose + */ + this.options = options || {}; + + /** + * Original field name. + * @type {string} + * @expose + */ + this.originalName = this.name; // Used to revert camelcase transformation on naming collisions + + // Convert field names to camel case notation if the override is set + if (ProtoBuf.convertFieldsToCamelCase) { + this.name = this.name.replace(/_([a-zA-Z])/g, function($0, $1) { + return $1.toUpperCase(); + }); + } + }; + + // Extends T + Field.prototype = Object.create(T.prototype); + + /** + * Checks if the given value can be set for this field. + * @param {*} value Value to check + * @param {boolean=} skipRepeated Whether to skip the repeated value check or not. Defaults to false. + * @return {*} Verified, maybe adjusted, value + * @throws {Error} If the value cannot be set for this field + * @expose + */ + Field.prototype.verifyValue = function(value, skipRepeated) { + skipRepeated = skipRepeated || false; + if (value === null) { // NULL values for optional fields + if (this.required) { + throw(new Error("Illegal value for "+this.toString(true)+": "+value+" (required)")); + } + return null; + } + var i; + if (this.repeated && !skipRepeated) { // Repeated values as arrays + if (!ProtoBuf.Util.isArray(value)) { + value = [value]; + } + var res = []; + for (i=0; i>> 0; // Do not cast NaN as it'd become 0 + } + if (ProtoBuf.Long) { + // Signed 64bit + if (this.type == ProtoBuf.TYPES["int64"] || this.type == ProtoBuf.TYPES["sint64"] || this.type == ProtoBuf.TYPES["sfixed64"]) { + if (!(typeof value == 'object' && value instanceof ProtoBuf.Long)) { + return ProtoBuf.Long.fromNumber(value, false); + } + return value.unsigned ? value.toSigned() : value; + } + // Unsigned 64bit + if (this.type == ProtoBuf.TYPES["uint64"] || this.type == ProtoBuf.TYPES["fixed64"]) { + if (!(typeof value == 'object' && value instanceof ProtoBuf.Long)) { + return ProtoBuf.Long.fromNumber(value, true); + } + return value.unsigned ? value : value.toUnsigned(); + } + } + // Bool + if (this.type == ProtoBuf.TYPES["bool"]) { + if (typeof value === 'string') return value === 'true'; + else return !!value; + } + // Float + if (this.type == ProtoBuf.TYPES["float"] || this.type == ProtoBuf.TYPES["double"]) { + return parseFloat(value); // May also become NaN, +Infinity, -Infinity + } + // Length-delimited string + if (this.type == ProtoBuf.TYPES["string"]) { + return ""+value; + } + // Length-delimited bytes + if (this.type == ProtoBuf.TYPES["bytes"]) { + if (value && value instanceof ByteBuffer) { + return value; + } + return ByteBuffer.wrap(value); + } + // Constant enum value + if (this.type == ProtoBuf.TYPES["enum"]) { + var values = this.resolvedType.getChildren(Enum.Value); + for (i=0; i 1) { // We need to move the contents + var contents = buffer.slice(start, buffer.offset); + start += varintLen-1; + buffer.offset = start; + buffer.append(contents); + } + buffer.writeVarint32(len, start-varintLen); + } else { + // "If your message definition has repeated elements (without the [packed=true] option), the encoded + // message has zero or more key-value pairs with the same tag number" + for (i=0; i value.length) { // Forgot to flip? + buffer = buffer.clone().flip(); + } + buffer.writeVarint32(value.remaining()); + buffer.append(value); + + // Embedded message + } else if (this.type == ProtoBuf.TYPES["message"]) { + var bb = new ByteBuffer().LE(); + this.resolvedType.encode(value, bb); + buffer.writeVarint32(bb.offset); + buffer.append(bb.flip()); + } else { + // We should never end here + throw(new Error("[INTERNAL] Illegal value to encode in "+this.toString(true)+": "+value+" (unknown type)")); + } + return buffer; + }; + + /** + * Decode the field value from the specified buffer. + * @param {number} wireType Leading wire type + * @param {ByteBuffer} buffer ByteBuffer to decode from + * @param {boolean=} skipRepeated Whether to skip the repeated check or not. Defaults to false. + * @return {*} Decoded value + * @throws {Error} If the field cannot be decoded + * @expose + */ + Field.prototype.decode = function(wireType, buffer, skipRepeated) { + var value, nBytes; + if (wireType != this.type.wireType && (skipRepeated || (wireType != ProtoBuf.WIRE_TYPES.LDELIM || !this.repeated))) { + throw(new Error("Illegal wire type for field "+this.toString(true)+": "+wireType+" ("+this.type.wireType+" expected)")); + } + if (wireType == ProtoBuf.WIRE_TYPES.LDELIM && this.repeated && this.options["packed"]) { + if (!skipRepeated) { + nBytes = buffer.readVarint32(); + nBytes = buffer.offset + nBytes; // Limit + var values = []; + while (buffer.offset < nBytes) { + values.push(this.decode(this.type.wireType, buffer, true)); + } + return values; + } + // Read the next value otherwise... + + } + // 32bit signed varint + if (this.type == ProtoBuf.TYPES["int32"]) { + return buffer.readVarint32() | 0; + } + + // 32bit unsigned varint + if (this.type == ProtoBuf.TYPES["uint32"]) { + return buffer.readVarint32() >>> 0; + } + + // 32bit signed varint zig-zag + if (this.type == ProtoBuf.TYPES["sint32"]) { + return buffer.readZigZagVarint32() | 0; + } + + // Fixed 32bit unsigned + if (this.type == ProtoBuf.TYPES["fixed32"]) { + return buffer.readUint32() >>> 0; + } + + // Fixed 32bit signed + if (this.type == ProtoBuf.TYPES["sfixed32"]) { + return buffer.readInt32() | 0; + } + + // 64bit signed varint + if (this.type == ProtoBuf.TYPES["int64"]) { + return buffer.readVarint64(); + } + + // 64bit unsigned varint + if (this.type == ProtoBuf.TYPES["uint64"]) { + return buffer.readVarint64().toUnsigned(); + } + + // 64bit signed varint zig-zag + if (this.type == ProtoBuf.TYPES["sint64"]) { + return buffer.readZigZagVarint64(); + } + + // Fixed 64bit unsigned + if (this.type == ProtoBuf.TYPES["fixed64"]) { + return buffer.readUint64(); + } + + // Fixed 64bit signed + if (this.type == ProtoBuf.TYPES["sfixed64"]) { + return buffer.readInt64(); + } + + // Bool varint + if (this.type == ProtoBuf.TYPES["bool"]) { + return !!buffer.readVarint32(); + } + + // Constant enum value varint) + if (this.type == ProtoBuf.TYPES["enum"]) { + return buffer.readVarint32(); // The following Builder.Message#set will already throw + } + + // 32bit float + if (this.type == ProtoBuf.TYPES["float"]) { + return buffer.readFloat(); + } + // 64bit float + if (this.type == ProtoBuf.TYPES["double"]) { + return buffer.readDouble(); + } + + // Length-delimited string + if (this.type == ProtoBuf.TYPES["string"]){ + return buffer.readVString(); + } + + // Length-delimited bytes + if (this.type == ProtoBuf.TYPES["bytes"]) { + nBytes = buffer.readVarint32(); + if (buffer.remaining() < nBytes) { + throw(new Error("Illegal number of bytes for "+this.toString(true)+": "+nBytes+" required but got only "+buffer.remaining())); + } + value = buffer.clone(); // Offset already set + value.length = value.offset+nBytes; + buffer.offset += nBytes; + return value; + } + + // Length-delimited embedded message + if (this.type == ProtoBuf.TYPES["message"]) { + nBytes = buffer.readVarint32(); + return this.resolvedType.decode(buffer, nBytes); + } + + // We should never end here + throw(new Error("[INTERNAL] Illegal wire type for "+this.toString(true)+": "+wireType)); + }; + + /** + * @alias ProtoBuf.Reflect.Message.Field + * @expose + */ + Reflect.Message.Field = Field; + + /** + * Constructs a new Enum. + * @exports ProtoBuf.Reflect.Enum + * @param {!ProtoBuf.Reflect.T} parent Parent Reflect object + * @param {string} name Enum name + * @param {Object.=} options Enum options + * @constructor + * @extends ProtoBuf.Reflect.Namespace + */ + var Enum = function(parent, name, options) { + Namespace.call(this, parent, name, options); + + /** + * Runtime enum object. + * @type {Object.|null} + * @expose + */ + this.object = null; + }; + + // Extends Namespace + Enum.prototype = Object.create(Namespace.prototype); + + /** + * Builds this enum and returns the runtime counterpart. + * @return {Object} + * @expose + */ + Enum.prototype.build = function() { + var enm = {}; + var values = this.getChildren(Enum.Value); + for (var i=0; i=} options Options + * @constructor + * @extends ProtoBuf.Reflect.Namespace + */ + var Service = function(root, name, options) { + Namespace.call(this, root, name, options); + + /** + * Built runtime service class. + * @type {?function(new:ProtoBuf.Builder.Service)} + */ + this.clazz = null; + }; + + // Extends Namespace + Service.prototype = Object.create(Namespace.prototype); + + /** + * Builds the service and returns the runtime counterpart, which is a fully functional class. + * @see ProtoBuf.Builder.Service + * @param {boolean=} rebuild Whether to rebuild or not + * @return {Function} Service class + * @throws {Error} If the message cannot be built + * @expose + */ + Service.prototype.build = function(rebuild) { + if (this.clazz && !rebuild) return this.clazz; + return this.clazz = (function(ProtoBuf, T) { + + /** + * Constructs a new runtime Service. + * @name ProtoBuf.Builder.Service + * @param {function(string, ProtoBuf.Builder.Message, function(Error, ProtoBuf.Builder.Message=))=} rpcImpl RPC implementation receiving the method name and the message + * @class Barebone of all runtime services. + * @constructor + * @throws {Error} If the service cannot be created + */ + var Service = function(rpcImpl) { + ProtoBuf.Builder.Service.call(this); + + /** + * Service implementation. + * @name ProtoBuf.Builder.Service#rpcImpl + * @type {!function(string, ProtoBuf.Builder.Message, function(Error, ProtoBuf.Builder.Message=))} + * @expose + */ + this.rpcImpl = rpcImpl || function(name, msg, callback) { + // This is what a user has to implement: A function receiving the method name, the actual message to + // send (type checked) and the callback that's either provided with the error as its first + // argument or null and the actual response message. + setTimeout(callback.bind(this, new Error("Not implemented, see: https://github.com/dcodeIO/ProtoBuf.js/wiki/Services")), 0); // Must be async! + }; + }; + + // Extends ProtoBuf.Builder.Service + Service.prototype = Object.create(ProtoBuf.Builder.Service.prototype); + + if (Object.defineProperty) { + Object.defineProperty(Service, "$options", { + "value": T.buildOpt(), + "enumerable": false, + "configurable": false, + "writable": false + }); + Object.defineProperty(Service.prototype, "$options", { + "value": Service["$options"], + "enumerable": false, + "configurable": false, + "writable": false + }); + } + + /** + * Asynchronously performs an RPC call using the given RPC implementation. + * @name ProtoBuf.Builder.Service.[Method] + * @function + * @param {!function(string, ProtoBuf.Builder.Message, function(Error, ProtoBuf.Builder.Message=))} rpcImpl RPC implementation + * @param {ProtoBuf.Builder.Message} req Request + * @param {function(Error, (ProtoBuf.Builder.Message|ByteBuffer|Buffer|string)=)} callback Callback receiving + * the error if any and the response either as a pre-parsed message or as its raw bytes + * @abstract + */ + + /** + * Asynchronously performs an RPC call using the instance's RPC implementation. + * @name ProtoBuf.Builder.Service#[Method] + * @function + * @param {ProtoBuf.Builder.Message} req Request + * @param {function(Error, (ProtoBuf.Builder.Message|ByteBuffer|Buffer|string)=)} callback Callback receiving + * the error if any and the response either as a pre-parsed message or as its raw bytes + * @abstract + */ + + var rpc = T.getChildren(Reflect.Service.RPCMethod); + for (var i=0; i=} options Options + * @constructor + * @extends ProtoBuf.Reflect.T + */ + var Method = function(svc, name, options) { + T.call(this, svc, name); + + /** + * Options. + * @type {Object.} + * @expose + */ + this.options = options || {}; + }; + + // Extends T + Method.prototype = Object.create(T.prototype); + + /** + * Builds the method's '$options' property. + * @name ProtoBuf.Reflect.Service.Method#buildOpt + * @function + * @return {Object.} + */ + Method.prototype.buildOpt = Namespace.prototype.buildOpt; + + /** + * @alias ProtoBuf.Reflect.Service.Method + * @expose + */ + Reflect.Service.Method = Method; + + /** + * RPC service method. + * @exports ProtoBuf.Reflect.Service.RPCMethod + * @param {!ProtoBuf.Reflect.Service} svc Service + * @param {string} name Method name + * @param {string} request Request message name + * @param {string} response Response message name + * @param {Object.=} options Options + * @constructor + * @extends ProtoBuf.Reflect.Service.Method + */ + var RPCMethod = function(svc, name, request, response, options) { + Method.call(this, svc, name, options); + + /** + * Request message name. + * @type {string} + * @expose + */ + this.requestName = request; + + /** + * Response message name. + * @type {string} + * @expose + */ + this.responseName = response; + + /** + * Resolved request message type. + * @type {ProtoBuf.Reflect.Message} + * @expose + */ + this.resolvedRequestType = null; + + /** + * Resolved response message type. + * @type {ProtoBuf.Reflect.Message} + * @expose + */ + this.resolvedResponseType = null; + }; + + // Extends Method + RPCMethod.prototype = Object.create(Method.prototype); + + /** + * @alias ProtoBuf.Reflect.Service.RPCMethod + * @expose + */ + Reflect.Service.RPCMethod = RPCMethod; + + return Reflect; + })(ProtoBuf); + + /** + * @alias ProtoBuf.Builder + * @expose + */ + ProtoBuf.Builder = (function(ProtoBuf, Lang, Reflect) { + "use strict"; + + /** + * Constructs a new Builder. + * @exports ProtoBuf.Builder + * @class Provides the functionality to build protocol messages. + * @constructor + */ + var Builder = function() { + + /** + * Namespace. + * @type {ProtoBuf.Reflect.Namespace} + * @expose + */ + this.ns = new Reflect.Namespace(null, ""); // Global namespace + + /** + * Namespace pointer. + * @type {ProtoBuf.Reflect.T} + * @expose + */ + this.ptr = this.ns; + + /** + * Resolved flag. + * @type {boolean} + * @expose + */ + this.resolved = false; + + /** + * The current building result. + * @type {Object.|null} + * @expose + */ + this.result = null; + + /** + * Imported files. + * @type {Array.} + * @expose + */ + this.files = {}; + + /** + * Import root override. + * @type {?string} + * @expose + */ + this.importRoot = null; + }; + + /** + * Resets the pointer to the global namespace. + * @expose + */ + Builder.prototype.reset = function() { + this.ptr = this.ns; + }; + + /** + * Defines a package on top of the current pointer position and places the pointer on it. + * @param {string} pkg + * @param {Object.=} options + * @return {ProtoBuf.Builder} this + * @throws {Error} If the package name is invalid + * @expose + */ + Builder.prototype.define = function(pkg, options) { + if (typeof pkg !== 'string' || !Lang.TYPEREF.test(pkg)) { + throw(new Error("Illegal package name: "+pkg)); + } + var part = pkg.split("."), i; + for (i=0; i} def Definition + * @return {boolean} true if valid, else false + * @expose + */ + Builder.isValidMessage = function(def) { + // Messages require a string name + if (typeof def["name"] !== 'string' || !Lang.NAME.test(def["name"])) { + return false; + } + // Messages must not contain values (that'd be an enum) or methods (that'd be a service) + if (typeof def["values"] !== 'undefined' || typeof def["rpc"] !== 'undefined') { + return false; + } + // Fields, enums and messages are arrays if provided + var i; + if (typeof def["fields"] !== 'undefined') { + if (!ProtoBuf.Util.isArray(def["fields"])) { + return false; + } + var ids = [], id; // IDs must be unique + for (i=0; i= 0) { + return false; + } + ids.push(id); + } + ids = null; + } + if (typeof def["enums"] !== 'undefined') { + if (!ProtoBuf.Util.isArray(def["enums"])) { + return false; + } + for (i=0; i + var keys = Object.keys(def["options"]); + for (var i=0; i>} defs Messages, enums or services to create + * @return {ProtoBuf.Builder} this + * @throws {Error} If a message definition is invalid + * @expose + */ + Builder.prototype.create = function(defs) { + if (!defs) return; // Nothing to create + if (!ProtoBuf.Util.isArray(defs)) { + defs = [defs]; + } + if (defs.length == 0) return; + + // It's quite hard to keep track of scopes and memory here, so let's do this iteratively. + var stack = [], def, obj, subObj, i, j; + stack.push(defs); // One level [a, b, c] + while (stack.length > 0) { + defs = stack.pop(); + if (ProtoBuf.Util.isArray(defs)) { // Stack always contains entire namespaces + while (defs.length > 0) { + def = defs.shift(); // Namespace always contains an array of messages, enums and services + if (Builder.isValidMessage(def)) { + obj = new Reflect.Message(this.ptr, def["name"], def["options"]); + // Create fields + if (def["fields"] && def["fields"].length > 0) { + for (i=0; i 0) { + for (i=0; i 0) { + for (i=0; i ProtoBuf.Lang.ID_MAX) { + obj.extensions[1] = ProtoBuf.Lang.ID_MAX; + } + } + this.ptr.addChild(obj); // Add to current namespace + if (subObj.length > 0) { + stack.push(defs); // Push the current level back + defs = subObj; // Continue processing sub level + subObj = null; + this.ptr = obj; // And move the pointer to this namespace + obj = null; + continue; + } + subObj = null; + obj = null; + } else if (Builder.isValidEnum(def)) { + obj = new Reflect.Enum(this.ptr, def["name"], def["options"]); + for (i=0; i obj.extensions[1]) { + throw(new Error("Illegal extended field id in message "+obj.name+": "+def['fields'][i]['id']+" ("+obj.extensions.join(' to ')+" expected)")); + } + obj.addChild(new Reflect.Message.Field(obj, def["fields"][i]["rule"], def["fields"][i]["type"], def["fields"][i]["name"], def["fields"][i]["id"], def["fields"][i]["options"])); + } + /* if (this.ptr instanceof Reflect.Message) { + this.ptr.addChild(obj); // Reference the extended message here to enable proper lookups + } */ + } else { + if (!/\.?google\.protobuf\./.test(def["ref"])) { // Silently skip internal extensions + throw(new Error("Extended message "+def["ref"]+" is not defined")); + } + } + } else { + throw(new Error("Not a valid message, enum, service or extend definition: "+JSON.stringify(def))); + } + def = null; + } + // Break goes here + } else { + throw(new Error("Not a valid namespace definition: "+JSON.stringify(defs))); + } + defs = null; + this.ptr = this.ptr.parent; // This namespace is s done + } + this.resolved = false; // Require re-resolve + this.result = null; // Require re-build + return this; + }; + + /** + * Tests if the specified file is a valid import. + * @param {string} filename + * @returns {boolean} true if valid, false if it should be skipped + * @expose + */ + Builder.isValidImport = function(filename) { + // Ignore google/protobuf/descriptor.proto (for example) as it makes use of low-level + // bootstrapping directives that are not required and therefore cannot be parsed by ProtoBuf.js. + return !(/google\/protobuf\//.test(filename)); + }; + + /** + * Imports another definition into this builder. + * @param {Object.} json Parsed import + * @param {(string|{root: string, file: string})=} filename Imported file name + * @return {ProtoBuf.Builder} this + * @throws {Error} If the definition or file cannot be imported + * @expose + */ + Builder.prototype["import"] = function(json, filename) { + if (typeof filename === 'string') { + if (ProtoBuf.Util.IS_NODE) { + var path = require("path"); + filename = path.resolve(filename); + } + if (!!this.files[filename]) { + this.reset(); + return this; // Skip duplicate imports + } + this.files[filename] = true; + } + if (!!json['imports'] && json['imports'].length > 0) { + var importRoot, delim = '/', resetRoot = false; + if (typeof filename === 'object') { // If an import root is specified, override + this.importRoot = filename["root"]; resetRoot = true; // ... and reset afterwards + importRoot = this.importRoot; + filename = filename["file"]; + if (importRoot.indexOf("\\") >= 0 || filename.indexOf("\\") >= 0) delim = '\\'; + } else if (typeof filename === 'string') { + if (this.importRoot) { // If import root is overridden, use it + importRoot = this.importRoot; + } else { // Otherwise compute from filename + if (filename.indexOf("/") >= 0) { // Unix + importRoot = filename.replace(/\/[^\/]*$/, ""); + if (/* /file.proto */ importRoot === "") importRoot = "/"; + } else if (filename.indexOf("\\") >= 0) { // Windows + importRoot = filename.replace(/\\[^\\]*$/, ""); delim = '\\'; + } else { + importRoot = "."; + } + } + } else { + importRoot = null; + } + + for (var i=0; i= 0) { + return false; + } + ids.push(id); + } + ids = null; + } + return true; + }; + + /** + * Resolves all namespace objects. + * @throws {Error} If a type cannot be resolved + * @expose + */ + Builder.prototype.resolveAll = function() { + // Resolve all reflected objects + var res; + if (this.ptr == null || typeof this.ptr.type === 'object') return; // Done (already resolved) + if (this.ptr instanceof Reflect.Namespace) { + // Build all children + var children = this.ptr.getChildren(); + for (var i=0; i} + * @throws {Error} If a type could not be resolved + * @expose + */ + Builder.prototype.build = function(path) { + this.reset(); + if (!this.resolved) { + this.resolveAll(); + this.resolved = true; + this.result = null; // Require re-build + } + if (this.result == null) { // (Re-)Build + this.result = this.ns.build(); + } + if (!path) { + return this.result; + } else { + var part = path.split("."); + var ptr = this.result; // Build namespace pointer (no hasChild etc.) + for (var i=0; i=} options Top level options + * @return {ProtoBuf.Builder} New Builder + * @expose + */ + ProtoBuf.newBuilder = function(pkg, options) { + var builder = new ProtoBuf.Builder(); + if (typeof pkg !== 'undefined' && pkg !== null) { + builder.define(pkg, options); + } + return builder; + }; + + /** + * Loads a .json definition and returns the Builder. + * @param {!*|string} json JSON definition + * @param {(ProtoBuf.Builder|string|{root: string, file: string})=} builder Builder to append to. Will create a new one if omitted. + * @param {(string|{root: string, file: string})=} filename The corresponding file name if known. Must be specified for imports. + * @return {ProtoBuf.Builder} Builder to create new messages + * @throws {Error} If the definition cannot be parsed or built + * @expose + */ + ProtoBuf.loadJson = function(json, builder, filename) { + if (typeof builder === 'string' || (builder && typeof builder["file"] === 'string' && typeof builder["root"] === 'string')) { + filename = builder; + builder = null; + } + if (!builder || typeof builder !== 'object') builder = ProtoBuf.newBuilder(); + if (typeof json === 'string') json = JSON.parse(json); + builder["import"](json, filename); + builder.resolveAll(); + builder.build(); + return builder; + }; + + /** + * Loads a .json file and returns the Builder. + * @param {string|{root: string, file: string}} filename Path to json file or an object specifying 'file' with + * an overridden 'root' path for all imported files. + * @param {function(ProtoBuf.Builder)=} callback Callback that will receive the Builder as its first argument. + * If the request has failed, builder will be NULL. If omitted, the file will be read synchronously and this + * function will return the Builder or NULL if the request has failed. + * @param {ProtoBuf.Builder=} builder Builder to append to. Will create a new one if omitted. + * @return {?ProtoBuf.Builder|undefined} The Builder if synchronous (no callback specified, will be NULL if the + * request has failed), else undefined + * @expose + */ + ProtoBuf.loadJsonFile = function(filename, callback, builder) { + if (callback && typeof callback === 'object') { + builder = callback; + callback = null; + } else if (!callback || typeof callback !== 'function') { + callback = null; + } + if (callback) { + ProtoBuf.Util.fetch(typeof filename === 'object' ? filename["root"]+"/"+filename["file"] : filename, function(contents) { + try { + callback(ProtoBuf.loadJson(JSON.parse(contents), builder, filename)); + } catch (err) { + callback(err); + } + }); + } else { + var contents = ProtoBuf.Util.fetch(typeof filename === 'object' ? filename["root"]+"/"+filename["file"] : filename); + return contents !== null ? ProtoBuf.loadJson(JSON.parse(contents), builder, filename) : null; + } + }; + + return ProtoBuf; + } + + // Enable module loading if available + if (typeof module != 'undefined' && module["exports"]) { // CommonJS + module["exports"] = loadProtoBuf(require("bytebuffer")); + } else if (typeof define != 'undefined' && define["amd"]) { // AMD + define("ProtoBuf", ["ByteBuffer"], loadProtoBuf); + } else { // Shim + if (!global["dcodeIO"]) { + global["dcodeIO"] = {}; + } + global["dcodeIO"]["ProtoBuf"] = loadProtoBuf(global["dcodeIO"]["ByteBuffer"]); + } + +})(this); \ No newline at end of file diff --git a/js-deps/aes.js b/js-deps/aes.js index 827503cb..13bfd80a 100644 --- a/js-deps/aes.js +++ b/js-deps/aes.js @@ -4,32 +4,210 @@ code.google.com/p/crypto-js (c) 2009-2013 by Jeff Mott. All rights reserved. code.google.com/p/crypto-js/wiki/License */ -var CryptoJS=CryptoJS||function(u,p){var d={},l=d.lib={},s=function(){},t=l.Base={extend:function(a){s.prototype=this;var c=new s;a&&c.mixIn(a);c.hasOwnProperty("init")||(c.init=function(){c.$super.init.apply(this,arguments)});c.init.prototype=c;c.$super=this;return c},create:function(){var a=this.extend();a.init.apply(a,arguments);return a},init:function(){},mixIn:function(a){for(var c in a)a.hasOwnProperty(c)&&(this[c]=a[c]);a.hasOwnProperty("toString")&&(this.toString=a.toString)},clone:function(){return this.init.prototype.extend(this)}}, -r=l.WordArray=t.extend({init:function(a,c){a=this.words=a||[];this.sigBytes=c!=p?c:4*a.length},toString:function(a){return(a||v).stringify(this)},concat:function(a){var c=this.words,e=a.words,j=this.sigBytes;a=a.sigBytes;this.clamp();if(j%4)for(var k=0;k>>2]|=(e[k>>>2]>>>24-8*(k%4)&255)<<24-8*((j+k)%4);else if(65535>>2]=e[k>>>2];else c.push.apply(c,e);this.sigBytes+=a;return this},clamp:function(){var a=this.words,c=this.sigBytes;a[c>>>2]&=4294967295<< -32-8*(c%4);a.length=u.ceil(c/4)},clone:function(){var a=t.clone.call(this);a.words=this.words.slice(0);return a},random:function(a){for(var c=[],e=0;e>>2]>>>24-8*(j%4)&255;e.push((k>>>4).toString(16));e.push((k&15).toString(16))}return e.join("")},parse:function(a){for(var c=a.length,e=[],j=0;j>>3]|=parseInt(a.substr(j, -2),16)<<24-4*(j%8);return new r.init(e,c/2)}},b=w.Latin1={stringify:function(a){var c=a.words;a=a.sigBytes;for(var e=[],j=0;j>>2]>>>24-8*(j%4)&255));return e.join("")},parse:function(a){for(var c=a.length,e=[],j=0;j>>2]|=(a.charCodeAt(j)&255)<<24-8*(j%4);return new r.init(e,c)}},x=w.Utf8={stringify:function(a){try{return decodeURIComponent(escape(b.stringify(a)))}catch(c){throw Error("Malformed UTF-8 data");}},parse:function(a){return b.parse(unescape(encodeURIComponent(a)))}}, -q=l.BufferedBlockAlgorithm=t.extend({reset:function(){this._data=new r.init;this._nDataBytes=0},_append:function(a){"string"==typeof a&&(a=x.parse(a));this._data.concat(a);this._nDataBytes+=a.sigBytes},_process:function(a){var c=this._data,e=c.words,j=c.sigBytes,k=this.blockSize,b=j/(4*k),b=a?u.ceil(b):u.max((b|0)-this._minBufferSize,0);a=b*k;j=u.min(4*a,j);if(a){for(var q=0;q>>2]>>>24-8*(r%4)&255)<<16|(l[r+1>>>2]>>>24-8*((r+1)%4)&255)<<8|l[r+2>>>2]>>>24-8*((r+2)%4)&255,v=0;4>v&&r+0.75*v>>6*(3-v)&63));if(l=t.charAt(64))for(;d.length%4;)d.push(l);return d.join("")},parse:function(d){var l=d.length,s=this._map,t=s.charAt(64);t&&(t=d.indexOf(t),-1!=t&&(l=t));for(var t=[],r=0,w=0;w< -l;w++)if(w%4){var v=s.indexOf(d.charAt(w-1))<<2*(w%4),b=s.indexOf(d.charAt(w))>>>6-2*(w%4);t[r>>>2]|=(v|b)<<24-8*(r%4);r++}return p.create(t,r)},_map:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="}})(); -(function(u){function p(b,n,a,c,e,j,k){b=b+(n&a|~n&c)+e+k;return(b<>>32-j)+n}function d(b,n,a,c,e,j,k){b=b+(n&c|a&~c)+e+k;return(b<>>32-j)+n}function l(b,n,a,c,e,j,k){b=b+(n^a^c)+e+k;return(b<>>32-j)+n}function s(b,n,a,c,e,j,k){b=b+(a^(n|~c))+e+k;return(b<>>32-j)+n}for(var t=CryptoJS,r=t.lib,w=r.WordArray,v=r.Hasher,r=t.algo,b=[],x=0;64>x;x++)b[x]=4294967296*u.abs(u.sin(x+1))|0;r=r.MD5=v.extend({_doReset:function(){this._hash=new w.init([1732584193,4023233417,2562383102,271733878])}, -_doProcessBlock:function(q,n){for(var a=0;16>a;a++){var c=n+a,e=q[c];q[c]=(e<<8|e>>>24)&16711935|(e<<24|e>>>8)&4278255360}var a=this._hash.words,c=q[n+0],e=q[n+1],j=q[n+2],k=q[n+3],z=q[n+4],r=q[n+5],t=q[n+6],w=q[n+7],v=q[n+8],A=q[n+9],B=q[n+10],C=q[n+11],u=q[n+12],D=q[n+13],E=q[n+14],x=q[n+15],f=a[0],m=a[1],g=a[2],h=a[3],f=p(f,m,g,h,c,7,b[0]),h=p(h,f,m,g,e,12,b[1]),g=p(g,h,f,m,j,17,b[2]),m=p(m,g,h,f,k,22,b[3]),f=p(f,m,g,h,z,7,b[4]),h=p(h,f,m,g,r,12,b[5]),g=p(g,h,f,m,t,17,b[6]),m=p(m,g,h,f,w,22,b[7]), -f=p(f,m,g,h,v,7,b[8]),h=p(h,f,m,g,A,12,b[9]),g=p(g,h,f,m,B,17,b[10]),m=p(m,g,h,f,C,22,b[11]),f=p(f,m,g,h,u,7,b[12]),h=p(h,f,m,g,D,12,b[13]),g=p(g,h,f,m,E,17,b[14]),m=p(m,g,h,f,x,22,b[15]),f=d(f,m,g,h,e,5,b[16]),h=d(h,f,m,g,t,9,b[17]),g=d(g,h,f,m,C,14,b[18]),m=d(m,g,h,f,c,20,b[19]),f=d(f,m,g,h,r,5,b[20]),h=d(h,f,m,g,B,9,b[21]),g=d(g,h,f,m,x,14,b[22]),m=d(m,g,h,f,z,20,b[23]),f=d(f,m,g,h,A,5,b[24]),h=d(h,f,m,g,E,9,b[25]),g=d(g,h,f,m,k,14,b[26]),m=d(m,g,h,f,v,20,b[27]),f=d(f,m,g,h,D,5,b[28]),h=d(h,f, -m,g,j,9,b[29]),g=d(g,h,f,m,w,14,b[30]),m=d(m,g,h,f,u,20,b[31]),f=l(f,m,g,h,r,4,b[32]),h=l(h,f,m,g,v,11,b[33]),g=l(g,h,f,m,C,16,b[34]),m=l(m,g,h,f,E,23,b[35]),f=l(f,m,g,h,e,4,b[36]),h=l(h,f,m,g,z,11,b[37]),g=l(g,h,f,m,w,16,b[38]),m=l(m,g,h,f,B,23,b[39]),f=l(f,m,g,h,D,4,b[40]),h=l(h,f,m,g,c,11,b[41]),g=l(g,h,f,m,k,16,b[42]),m=l(m,g,h,f,t,23,b[43]),f=l(f,m,g,h,A,4,b[44]),h=l(h,f,m,g,u,11,b[45]),g=l(g,h,f,m,x,16,b[46]),m=l(m,g,h,f,j,23,b[47]),f=s(f,m,g,h,c,6,b[48]),h=s(h,f,m,g,w,10,b[49]),g=s(g,h,f,m, -E,15,b[50]),m=s(m,g,h,f,r,21,b[51]),f=s(f,m,g,h,u,6,b[52]),h=s(h,f,m,g,k,10,b[53]),g=s(g,h,f,m,B,15,b[54]),m=s(m,g,h,f,e,21,b[55]),f=s(f,m,g,h,v,6,b[56]),h=s(h,f,m,g,x,10,b[57]),g=s(g,h,f,m,t,15,b[58]),m=s(m,g,h,f,D,21,b[59]),f=s(f,m,g,h,z,6,b[60]),h=s(h,f,m,g,C,10,b[61]),g=s(g,h,f,m,j,15,b[62]),m=s(m,g,h,f,A,21,b[63]);a[0]=a[0]+f|0;a[1]=a[1]+m|0;a[2]=a[2]+g|0;a[3]=a[3]+h|0},_doFinalize:function(){var b=this._data,n=b.words,a=8*this._nDataBytes,c=8*b.sigBytes;n[c>>>5]|=128<<24-c%32;var e=u.floor(a/ -4294967296);n[(c+64>>>9<<4)+15]=(e<<8|e>>>24)&16711935|(e<<24|e>>>8)&4278255360;n[(c+64>>>9<<4)+14]=(a<<8|a>>>24)&16711935|(a<<24|a>>>8)&4278255360;b.sigBytes=4*(n.length+1);this._process();b=this._hash;n=b.words;for(a=0;4>a;a++)c=n[a],n[a]=(c<<8|c>>>24)&16711935|(c<<24|c>>>8)&4278255360;return b},clone:function(){var b=v.clone.call(this);b._hash=this._hash.clone();return b}});t.MD5=v._createHelper(r);t.HmacMD5=v._createHmacHelper(r)})(Math); -(function(){var u=CryptoJS,p=u.lib,d=p.Base,l=p.WordArray,p=u.algo,s=p.EvpKDF=d.extend({cfg:d.extend({keySize:4,hasher:p.MD5,iterations:1}),init:function(d){this.cfg=this.cfg.extend(d)},compute:function(d,r){for(var p=this.cfg,s=p.hasher.create(),b=l.create(),u=b.words,q=p.keySize,p=p.iterations;u.length>>2]&255}};d.BlockCipher=v.extend({cfg:v.cfg.extend({mode:b,padding:q}),reset:function(){v.reset.call(this);var a=this.cfg,b=a.iv,a=a.mode;if(this._xformMode==this._ENC_XFORM_MODE)var c=a.createEncryptor;else c=a.createDecryptor,this._minBufferSize=1;this._mode=c.call(a, -this,b&&b.words)},_doProcessBlock:function(a,b){this._mode.processBlock(a,b)},_doFinalize:function(){var a=this.cfg.padding;if(this._xformMode==this._ENC_XFORM_MODE){a.pad(this._data,this.blockSize);var b=this._process(!0)}else b=this._process(!0),a.unpad(b);return b},blockSize:4});var n=d.CipherParams=l.extend({init:function(a){this.mixIn(a)},toString:function(a){return(a||this.formatter).stringify(this)}}),b=(p.format={}).OpenSSL={stringify:function(a){var b=a.ciphertext;a=a.salt;return(a?s.create([1398893684, -1701076831]).concat(a).concat(b):b).toString(r)},parse:function(a){a=r.parse(a);var b=a.words;if(1398893684==b[0]&&1701076831==b[1]){var c=s.create(b.slice(2,4));b.splice(0,4);a.sigBytes-=16}return n.create({ciphertext:a,salt:c})}},a=d.SerializableCipher=l.extend({cfg:l.extend({format:b}),encrypt:function(a,b,c,d){d=this.cfg.extend(d);var l=a.createEncryptor(c,d);b=l.finalize(b);l=l.cfg;return n.create({ciphertext:b,key:c,iv:l.iv,algorithm:a,mode:l.mode,padding:l.padding,blockSize:a.blockSize,formatter:d.format})}, -decrypt:function(a,b,c,d){d=this.cfg.extend(d);b=this._parse(b,d.format);return a.createDecryptor(c,d).finalize(b.ciphertext)},_parse:function(a,b){return"string"==typeof a?b.parse(a,this):a}}),p=(p.kdf={}).OpenSSL={execute:function(a,b,c,d){d||(d=s.random(8));a=w.create({keySize:b+c}).compute(a,d);c=s.create(a.words.slice(b),4*c);a.sigBytes=4*b;return n.create({key:a,iv:c,salt:d})}},c=d.PasswordBasedCipher=a.extend({cfg:a.cfg.extend({kdf:p}),encrypt:function(b,c,d,l){l=this.cfg.extend(l);d=l.kdf.execute(d, -b.keySize,b.ivSize);l.iv=d.iv;b=a.encrypt.call(this,b,c,d.key,l);b.mixIn(d);return b},decrypt:function(b,c,d,l){l=this.cfg.extend(l);c=this._parse(c,l.format);d=l.kdf.execute(d,b.keySize,b.ivSize,c.salt);l.iv=d.iv;return a.decrypt.call(this,b,c,d.key,l)}})}(); -(function(){for(var u=CryptoJS,p=u.lib.BlockCipher,d=u.algo,l=[],s=[],t=[],r=[],w=[],v=[],b=[],x=[],q=[],n=[],a=[],c=0;256>c;c++)a[c]=128>c?c<<1:c<<1^283;for(var e=0,j=0,c=0;256>c;c++){var k=j^j<<1^j<<2^j<<3^j<<4,k=k>>>8^k&255^99;l[e]=k;s[k]=e;var z=a[e],F=a[z],G=a[F],y=257*a[k]^16843008*k;t[e]=y<<24|y>>>8;r[e]=y<<16|y>>>16;w[e]=y<<8|y>>>24;v[e]=y;y=16843009*G^65537*F^257*z^16843008*e;b[k]=y<<24|y>>>8;x[k]=y<<16|y>>>16;q[k]=y<<8|y>>>24;n[k]=y;e?(e=z^a[a[a[G^z]]],j^=a[a[j]]):e=j=1}var H=[0,1,2,4,8, -16,32,64,128,27,54],d=d.AES=p.extend({_doReset:function(){for(var a=this._key,c=a.words,d=a.sigBytes/4,a=4*((this._nRounds=d+6)+1),e=this._keySchedule=[],j=0;j>>24]<<24|l[k>>>16&255]<<16|l[k>>>8&255]<<8|l[k&255]):(k=k<<8|k>>>24,k=l[k>>>24]<<24|l[k>>>16&255]<<16|l[k>>>8&255]<<8|l[k&255],k^=H[j/d|0]<<24);e[j]=e[j-d]^k}c=this._invKeySchedule=[];for(d=0;dd||4>=j?k:b[l[k>>>24]]^x[l[k>>>16&255]]^q[l[k>>> -8&255]]^n[l[k&255]]},encryptBlock:function(a,b){this._doCryptBlock(a,b,this._keySchedule,t,r,w,v,l)},decryptBlock:function(a,c){var d=a[c+1];a[c+1]=a[c+3];a[c+3]=d;this._doCryptBlock(a,c,this._invKeySchedule,b,x,q,n,s);d=a[c+1];a[c+1]=a[c+3];a[c+3]=d},_doCryptBlock:function(a,b,c,d,e,j,l,f){for(var m=this._nRounds,g=a[b]^c[0],h=a[b+1]^c[1],k=a[b+2]^c[2],n=a[b+3]^c[3],p=4,r=1;r>>24]^e[h>>>16&255]^j[k>>>8&255]^l[n&255]^c[p++],s=d[h>>>24]^e[k>>>16&255]^j[n>>>8&255]^l[g&255]^c[p++],t= -d[k>>>24]^e[n>>>16&255]^j[g>>>8&255]^l[h&255]^c[p++],n=d[n>>>24]^e[g>>>16&255]^j[h>>>8&255]^l[k&255]^c[p++],g=q,h=s,k=t;q=(f[g>>>24]<<24|f[h>>>16&255]<<16|f[k>>>8&255]<<8|f[n&255])^c[p++];s=(f[h>>>24]<<24|f[k>>>16&255]<<16|f[n>>>8&255]<<8|f[g&255])^c[p++];t=(f[k>>>24]<<24|f[n>>>16&255]<<16|f[g>>>8&255]<<8|f[h&255])^c[p++];n=(f[n>>>24]<<24|f[g>>>16&255]<<16|f[h>>>8&255]<<8|f[k&255])^c[p++];a[b]=q;a[b+1]=s;a[b+2]=t;a[b+3]=n},keySize:8});u.AES=p._createHelper(d)})(); +(function () { + // Shortcuts + var C = CryptoJS; + var C_lib = C.lib; + var BlockCipher = C_lib.BlockCipher; + var C_algo = C.algo; + + // Lookup tables + var SBOX = []; + var INV_SBOX = []; + var SUB_MIX_0 = []; + var SUB_MIX_1 = []; + var SUB_MIX_2 = []; + var SUB_MIX_3 = []; + var INV_SUB_MIX_0 = []; + var INV_SUB_MIX_1 = []; + var INV_SUB_MIX_2 = []; + var INV_SUB_MIX_3 = []; + + // Compute lookup tables + (function () { + // Compute double table + var d = []; + for (var i = 0; i < 256; i++) { + if (i < 128) { + d[i] = i << 1; + } else { + d[i] = (i << 1) ^ 0x11b; + } + } + + // Walk GF(2^8) + var x = 0; + var xi = 0; + for (var i = 0; i < 256; i++) { + // Compute sbox + var sx = xi ^ (xi << 1) ^ (xi << 2) ^ (xi << 3) ^ (xi << 4); + sx = (sx >>> 8) ^ (sx & 0xff) ^ 0x63; + SBOX[x] = sx; + INV_SBOX[sx] = x; + + // Compute multiplication + var x2 = d[x]; + var x4 = d[x2]; + var x8 = d[x4]; + + // Compute sub bytes, mix columns tables + var t = (d[sx] * 0x101) ^ (sx * 0x1010100); + SUB_MIX_0[x] = (t << 24) | (t >>> 8); + SUB_MIX_1[x] = (t << 16) | (t >>> 16); + SUB_MIX_2[x] = (t << 8) | (t >>> 24); + SUB_MIX_3[x] = t; + + // Compute inv sub bytes, inv mix columns tables + var t = (x8 * 0x1010101) ^ (x4 * 0x10001) ^ (x2 * 0x101) ^ (x * 0x1010100); + INV_SUB_MIX_0[sx] = (t << 24) | (t >>> 8); + INV_SUB_MIX_1[sx] = (t << 16) | (t >>> 16); + INV_SUB_MIX_2[sx] = (t << 8) | (t >>> 24); + INV_SUB_MIX_3[sx] = t; + + // Compute next counter + if (!x) { + x = xi = 1; + } else { + x = x2 ^ d[d[d[x8 ^ x2]]]; + xi ^= d[d[xi]]; + } + } + }()); + + // Precomputed Rcon lookup + var RCON = [0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36]; + + /** + * AES block cipher algorithm. + */ + var AES = C_algo.AES = BlockCipher.extend({ + _doReset: function () { + // Shortcuts + var key = this._key; + var keyWords = key.words; + var keySize = key.sigBytes / 4; + + // Compute number of rounds + var nRounds = this._nRounds = keySize + 6 + + // Compute number of key schedule rows + var ksRows = (nRounds + 1) * 4; + + // Compute key schedule + var keySchedule = this._keySchedule = []; + for (var ksRow = 0; ksRow < ksRows; ksRow++) { + if (ksRow < keySize) { + keySchedule[ksRow] = keyWords[ksRow]; + } else { + var t = keySchedule[ksRow - 1]; + + if (!(ksRow % keySize)) { + // Rot word + t = (t << 8) | (t >>> 24); + + // Sub word + t = (SBOX[t >>> 24] << 24) | (SBOX[(t >>> 16) & 0xff] << 16) | (SBOX[(t >>> 8) & 0xff] << 8) | SBOX[t & 0xff]; + + // Mix Rcon + t ^= RCON[(ksRow / keySize) | 0] << 24; + } else if (keySize > 6 && ksRow % keySize == 4) { + // Sub word + t = (SBOX[t >>> 24] << 24) | (SBOX[(t >>> 16) & 0xff] << 16) | (SBOX[(t >>> 8) & 0xff] << 8) | SBOX[t & 0xff]; + } + + keySchedule[ksRow] = keySchedule[ksRow - keySize] ^ t; + } + } + + // Compute inv key schedule + var invKeySchedule = this._invKeySchedule = []; + for (var invKsRow = 0; invKsRow < ksRows; invKsRow++) { + var ksRow = ksRows - invKsRow; + + if (invKsRow % 4) { + var t = keySchedule[ksRow]; + } else { + var t = keySchedule[ksRow - 4]; + } + + if (invKsRow < 4 || ksRow <= 4) { + invKeySchedule[invKsRow] = t; + } else { + invKeySchedule[invKsRow] = INV_SUB_MIX_0[SBOX[t >>> 24]] ^ INV_SUB_MIX_1[SBOX[(t >>> 16) & 0xff]] ^ + INV_SUB_MIX_2[SBOX[(t >>> 8) & 0xff]] ^ INV_SUB_MIX_3[SBOX[t & 0xff]]; + } + } + }, + + encryptBlock: function (M, offset) { + this._doCryptBlock(M, offset, this._keySchedule, SUB_MIX_0, SUB_MIX_1, SUB_MIX_2, SUB_MIX_3, SBOX); + }, + + decryptBlock: function (M, offset) { + // Swap 2nd and 4th rows + var t = M[offset + 1]; + M[offset + 1] = M[offset + 3]; + M[offset + 3] = t; + + this._doCryptBlock(M, offset, this._invKeySchedule, INV_SUB_MIX_0, INV_SUB_MIX_1, INV_SUB_MIX_2, INV_SUB_MIX_3, INV_SBOX); + + // Inv swap 2nd and 4th rows + var t = M[offset + 1]; + M[offset + 1] = M[offset + 3]; + M[offset + 3] = t; + }, + + _doCryptBlock: function (M, offset, keySchedule, SUB_MIX_0, SUB_MIX_1, SUB_MIX_2, SUB_MIX_3, SBOX) { + // Shortcut + var nRounds = this._nRounds; + + // Get input, add round key + var s0 = M[offset] ^ keySchedule[0]; + var s1 = M[offset + 1] ^ keySchedule[1]; + var s2 = M[offset + 2] ^ keySchedule[2]; + var s3 = M[offset + 3] ^ keySchedule[3]; + + // Key schedule row counter + var ksRow = 4; + + // Rounds + for (var round = 1; round < nRounds; round++) { + // Shift rows, sub bytes, mix columns, add round key + var t0 = SUB_MIX_0[s0 >>> 24] ^ SUB_MIX_1[(s1 >>> 16) & 0xff] ^ SUB_MIX_2[(s2 >>> 8) & 0xff] ^ SUB_MIX_3[s3 & 0xff] ^ keySchedule[ksRow++]; + var t1 = SUB_MIX_0[s1 >>> 24] ^ SUB_MIX_1[(s2 >>> 16) & 0xff] ^ SUB_MIX_2[(s3 >>> 8) & 0xff] ^ SUB_MIX_3[s0 & 0xff] ^ keySchedule[ksRow++]; + var t2 = SUB_MIX_0[s2 >>> 24] ^ SUB_MIX_1[(s3 >>> 16) & 0xff] ^ SUB_MIX_2[(s0 >>> 8) & 0xff] ^ SUB_MIX_3[s1 & 0xff] ^ keySchedule[ksRow++]; + var t3 = SUB_MIX_0[s3 >>> 24] ^ SUB_MIX_1[(s0 >>> 16) & 0xff] ^ SUB_MIX_2[(s1 >>> 8) & 0xff] ^ SUB_MIX_3[s2 & 0xff] ^ keySchedule[ksRow++]; + + // Update state + s0 = t0; + s1 = t1; + s2 = t2; + s3 = t3; + } + + // Shift rows, sub bytes, add round key + var t0 = ((SBOX[s0 >>> 24] << 24) | (SBOX[(s1 >>> 16) & 0xff] << 16) | (SBOX[(s2 >>> 8) & 0xff] << 8) | SBOX[s3 & 0xff]) ^ keySchedule[ksRow++]; + var t1 = ((SBOX[s1 >>> 24] << 24) | (SBOX[(s2 >>> 16) & 0xff] << 16) | (SBOX[(s3 >>> 8) & 0xff] << 8) | SBOX[s0 & 0xff]) ^ keySchedule[ksRow++]; + var t2 = ((SBOX[s2 >>> 24] << 24) | (SBOX[(s3 >>> 16) & 0xff] << 16) | (SBOX[(s0 >>> 8) & 0xff] << 8) | SBOX[s1 & 0xff]) ^ keySchedule[ksRow++]; + var t3 = ((SBOX[s3 >>> 24] << 24) | (SBOX[(s0 >>> 16) & 0xff] << 16) | (SBOX[(s1 >>> 8) & 0xff] << 8) | SBOX[s2 & 0xff]) ^ keySchedule[ksRow++]; + + // Set output + M[offset] = t0; + M[offset + 1] = t1; + M[offset + 2] = t2; + M[offset + 3] = t3; + }, + + keySize: 256/32 + }); + + /** + * Shortcut functions to the cipher's object interface. + * + * @example + * + * var ciphertext = CryptoJS.AES.encrypt(message, key, cfg); + * var plaintext = CryptoJS.AES.decrypt(ciphertext, key, cfg); + */ + C.AES = BlockCipher._createHelper(AES); +}()); diff --git a/js-deps/cipher-core.js b/js-deps/cipher-core.js new file mode 100644 index 00000000..09862098 --- /dev/null +++ b/js-deps/cipher-core.js @@ -0,0 +1,863 @@ +/* +CryptoJS v3.1.2 +code.google.com/p/crypto-js +(c) 2009-2013 by Jeff Mott. All rights reserved. +code.google.com/p/crypto-js/wiki/License +*/ +/** + * Cipher core components. + */ +CryptoJS.lib.Cipher || (function (undefined) { + // Shortcuts + var C = CryptoJS; + var C_lib = C.lib; + var Base = C_lib.Base; + var WordArray = C_lib.WordArray; + var BufferedBlockAlgorithm = C_lib.BufferedBlockAlgorithm; + var C_enc = C.enc; + var Utf8 = C_enc.Utf8; + var Base64 = C_enc.Base64; + var C_algo = C.algo; + var EvpKDF = C_algo.EvpKDF; + + /** + * Abstract base cipher template. + * + * @property {number} keySize This cipher's key size. Default: 4 (128 bits) + * @property {number} ivSize This cipher's IV size. Default: 4 (128 bits) + * @property {number} _ENC_XFORM_MODE A constant representing encryption mode. + * @property {number} _DEC_XFORM_MODE A constant representing decryption mode. + */ + var Cipher = C_lib.Cipher = BufferedBlockAlgorithm.extend({ + /** + * Configuration options. + * + * @property {WordArray} iv The IV to use for this operation. + */ + cfg: Base.extend(), + + /** + * Creates this cipher in encryption mode. + * + * @param {WordArray} key The key. + * @param {Object} cfg (Optional) The configuration options to use for this operation. + * + * @return {Cipher} A cipher instance. + * + * @static + * + * @example + * + * var cipher = CryptoJS.algo.AES.createEncryptor(keyWordArray, { iv: ivWordArray }); + */ + createEncryptor: function (key, cfg) { + return this.create(this._ENC_XFORM_MODE, key, cfg); + }, + + /** + * Creates this cipher in decryption mode. + * + * @param {WordArray} key The key. + * @param {Object} cfg (Optional) The configuration options to use for this operation. + * + * @return {Cipher} A cipher instance. + * + * @static + * + * @example + * + * var cipher = CryptoJS.algo.AES.createDecryptor(keyWordArray, { iv: ivWordArray }); + */ + createDecryptor: function (key, cfg) { + return this.create(this._DEC_XFORM_MODE, key, cfg); + }, + + /** + * Initializes a newly created cipher. + * + * @param {number} xformMode Either the encryption or decryption transormation mode constant. + * @param {WordArray} key The key. + * @param {Object} cfg (Optional) The configuration options to use for this operation. + * + * @example + * + * var cipher = CryptoJS.algo.AES.create(CryptoJS.algo.AES._ENC_XFORM_MODE, keyWordArray, { iv: ivWordArray }); + */ + init: function (xformMode, key, cfg) { + // Apply config defaults + this.cfg = this.cfg.extend(cfg); + + // Store transform mode and key + this._xformMode = xformMode; + this._key = key; + + // Set initial values + this.reset(); + }, + + /** + * Resets this cipher to its initial state. + * + * @example + * + * cipher.reset(); + */ + reset: function () { + // Reset data buffer + BufferedBlockAlgorithm.reset.call(this); + + // Perform concrete-cipher logic + this._doReset(); + }, + + /** + * Adds data to be encrypted or decrypted. + * + * @param {WordArray|string} dataUpdate The data to encrypt or decrypt. + * + * @return {WordArray} The data after processing. + * + * @example + * + * var encrypted = cipher.process('data'); + * var encrypted = cipher.process(wordArray); + */ + process: function (dataUpdate) { + // Append + this._append(dataUpdate); + + // Process available blocks + return this._process(); + }, + + /** + * Finalizes the encryption or decryption process. + * Note that the finalize operation is effectively a destructive, read-once operation. + * + * @param {WordArray|string} dataUpdate The final data to encrypt or decrypt. + * + * @return {WordArray} The data after final processing. + * + * @example + * + * var encrypted = cipher.finalize(); + * var encrypted = cipher.finalize('data'); + * var encrypted = cipher.finalize(wordArray); + */ + finalize: function (dataUpdate) { + // Final data update + if (dataUpdate) { + this._append(dataUpdate); + } + + // Perform concrete-cipher logic + var finalProcessedData = this._doFinalize(); + + return finalProcessedData; + }, + + keySize: 128/32, + + ivSize: 128/32, + + _ENC_XFORM_MODE: 1, + + _DEC_XFORM_MODE: 2, + + /** + * Creates shortcut functions to a cipher's object interface. + * + * @param {Cipher} cipher The cipher to create a helper for. + * + * @return {Object} An object with encrypt and decrypt shortcut functions. + * + * @static + * + * @example + * + * var AES = CryptoJS.lib.Cipher._createHelper(CryptoJS.algo.AES); + */ + _createHelper: (function () { + function selectCipherStrategy(key) { + if (typeof key == 'string') { + return PasswordBasedCipher; + } else { + return SerializableCipher; + } + } + + return function (cipher) { + return { + encrypt: function (message, key, cfg) { + return selectCipherStrategy(key).encrypt(cipher, message, key, cfg); + }, + + decrypt: function (ciphertext, key, cfg) { + return selectCipherStrategy(key).decrypt(cipher, ciphertext, key, cfg); + } + }; + }; + }()) + }); + + /** + * Abstract base stream cipher template. + * + * @property {number} blockSize The number of 32-bit words this cipher operates on. Default: 1 (32 bits) + */ + var StreamCipher = C_lib.StreamCipher = Cipher.extend({ + _doFinalize: function () { + // Process partial blocks + var finalProcessedBlocks = this._process(!!'flush'); + + return finalProcessedBlocks; + }, + + blockSize: 1 + }); + + /** + * Mode namespace. + */ + var C_mode = C.mode = {}; + + /** + * Abstract base block cipher mode template. + */ + var BlockCipherMode = C_lib.BlockCipherMode = Base.extend({ + /** + * Creates this mode for encryption. + * + * @param {Cipher} cipher A block cipher instance. + * @param {Array} iv The IV words. + * + * @static + * + * @example + * + * var mode = CryptoJS.mode.CBC.createEncryptor(cipher, iv.words); + */ + createEncryptor: function (cipher, iv) { + return this.Encryptor.create(cipher, iv); + }, + + /** + * Creates this mode for decryption. + * + * @param {Cipher} cipher A block cipher instance. + * @param {Array} iv The IV words. + * + * @static + * + * @example + * + * var mode = CryptoJS.mode.CBC.createDecryptor(cipher, iv.words); + */ + createDecryptor: function (cipher, iv) { + return this.Decryptor.create(cipher, iv); + }, + + /** + * Initializes a newly created mode. + * + * @param {Cipher} cipher A block cipher instance. + * @param {Array} iv The IV words. + * + * @example + * + * var mode = CryptoJS.mode.CBC.Encryptor.create(cipher, iv.words); + */ + init: function (cipher, iv) { + this._cipher = cipher; + this._iv = iv; + } + }); + + /** + * Cipher Block Chaining mode. + */ + var CBC = C_mode.CBC = (function () { + /** + * Abstract base CBC mode. + */ + var CBC = BlockCipherMode.extend(); + + /** + * CBC encryptor. + */ + CBC.Encryptor = CBC.extend({ + /** + * Processes the data block at offset. + * + * @param {Array} words The data words to operate on. + * @param {number} offset The offset where the block starts. + * + * @example + * + * mode.processBlock(data.words, offset); + */ + processBlock: function (words, offset) { + // Shortcuts + var cipher = this._cipher; + var blockSize = cipher.blockSize; + + // XOR and encrypt + xorBlock.call(this, words, offset, blockSize); + cipher.encryptBlock(words, offset); + + // Remember this block to use with next block + this._prevBlock = words.slice(offset, offset + blockSize); + } + }); + + /** + * CBC decryptor. + */ + CBC.Decryptor = CBC.extend({ + /** + * Processes the data block at offset. + * + * @param {Array} words The data words to operate on. + * @param {number} offset The offset where the block starts. + * + * @example + * + * mode.processBlock(data.words, offset); + */ + processBlock: function (words, offset) { + // Shortcuts + var cipher = this._cipher; + var blockSize = cipher.blockSize; + + // Remember this block to use with next block + var thisBlock = words.slice(offset, offset + blockSize); + + // Decrypt and XOR + cipher.decryptBlock(words, offset); + xorBlock.call(this, words, offset, blockSize); + + // This block becomes the previous block + this._prevBlock = thisBlock; + } + }); + + function xorBlock(words, offset, blockSize) { + // Shortcut + var iv = this._iv; + + // Choose mixing block + if (iv) { + var block = iv; + + // Remove IV for subsequent blocks + this._iv = undefined; + } else { + var block = this._prevBlock; + } + + // XOR blocks + for (var i = 0; i < blockSize; i++) { + words[offset + i] ^= block[i]; + } + } + + return CBC; + }()); + + /** + * Padding namespace. + */ + var C_pad = C.pad = {}; + + /** + * PKCS #5/7 padding strategy. + */ + var Pkcs7 = C_pad.Pkcs7 = { + /** + * Pads data using the algorithm defined in PKCS #5/7. + * + * @param {WordArray} data The data to pad. + * @param {number} blockSize The multiple that the data should be padded to. + * + * @static + * + * @example + * + * CryptoJS.pad.Pkcs7.pad(wordArray, 4); + */ + pad: function (data, blockSize) { + // Shortcut + var blockSizeBytes = blockSize * 4; + + // Count padding bytes + var nPaddingBytes = blockSizeBytes - data.sigBytes % blockSizeBytes; + + // Create padding word + var paddingWord = (nPaddingBytes << 24) | (nPaddingBytes << 16) | (nPaddingBytes << 8) | nPaddingBytes; + + // Create padding + var paddingWords = []; + for (var i = 0; i < nPaddingBytes; i += 4) { + paddingWords.push(paddingWord); + } + var padding = WordArray.create(paddingWords, nPaddingBytes); + + // Add padding + data.concat(padding); + }, + + /** + * Unpads data that had been padded using the algorithm defined in PKCS #5/7. + * + * @param {WordArray} data The data to unpad. + * + * @static + * + * @example + * + * CryptoJS.pad.Pkcs7.unpad(wordArray); + */ + unpad: function (data) { + // Get number of padding bytes from last byte + var nPaddingBytes = data.words[(data.sigBytes - 1) >>> 2] & 0xff; + + // Remove padding + data.sigBytes -= nPaddingBytes; + } + }; + + /** + * Abstract base block cipher template. + * + * @property {number} blockSize The number of 32-bit words this cipher operates on. Default: 4 (128 bits) + */ + var BlockCipher = C_lib.BlockCipher = Cipher.extend({ + /** + * Configuration options. + * + * @property {Mode} mode The block mode to use. Default: CBC + * @property {Padding} padding The padding strategy to use. Default: Pkcs7 + */ + cfg: Cipher.cfg.extend({ + mode: CBC, + padding: Pkcs7 + }), + + reset: function () { + // Reset cipher + Cipher.reset.call(this); + + // Shortcuts + var cfg = this.cfg; + var iv = cfg.iv; + var mode = cfg.mode; + + // Reset block mode + if (this._xformMode == this._ENC_XFORM_MODE) { + var modeCreator = mode.createEncryptor; + } else /* if (this._xformMode == this._DEC_XFORM_MODE) */ { + var modeCreator = mode.createDecryptor; + + // Keep at least one block in the buffer for unpadding + this._minBufferSize = 1; + } + this._mode = modeCreator.call(mode, this, iv && iv.words); + }, + + _doProcessBlock: function (words, offset) { + this._mode.processBlock(words, offset); + }, + + _doFinalize: function () { + // Shortcut + var padding = this.cfg.padding; + + // Finalize + if (this._xformMode == this._ENC_XFORM_MODE) { + // Pad data + padding.pad(this._data, this.blockSize); + + // Process final blocks + var finalProcessedBlocks = this._process(!!'flush'); + } else /* if (this._xformMode == this._DEC_XFORM_MODE) */ { + // Process final blocks + var finalProcessedBlocks = this._process(!!'flush'); + + // Unpad data + padding.unpad(finalProcessedBlocks); + } + + return finalProcessedBlocks; + }, + + blockSize: 128/32 + }); + + /** + * A collection of cipher parameters. + * + * @property {WordArray} ciphertext The raw ciphertext. + * @property {WordArray} key The key to this ciphertext. + * @property {WordArray} iv The IV used in the ciphering operation. + * @property {WordArray} salt The salt used with a key derivation function. + * @property {Cipher} algorithm The cipher algorithm. + * @property {Mode} mode The block mode used in the ciphering operation. + * @property {Padding} padding The padding scheme used in the ciphering operation. + * @property {number} blockSize The block size of the cipher. + * @property {Format} formatter The default formatting strategy to convert this cipher params object to a string. + */ + var CipherParams = C_lib.CipherParams = Base.extend({ + /** + * Initializes a newly created cipher params object. + * + * @param {Object} cipherParams An object with any of the possible cipher parameters. + * + * @example + * + * var cipherParams = CryptoJS.lib.CipherParams.create({ + * ciphertext: ciphertextWordArray, + * key: keyWordArray, + * iv: ivWordArray, + * salt: saltWordArray, + * algorithm: CryptoJS.algo.AES, + * mode: CryptoJS.mode.CBC, + * padding: CryptoJS.pad.PKCS7, + * blockSize: 4, + * formatter: CryptoJS.format.OpenSSL + * }); + */ + init: function (cipherParams) { + this.mixIn(cipherParams); + }, + + /** + * Converts this cipher params object to a string. + * + * @param {Format} formatter (Optional) The formatting strategy to use. + * + * @return {string} The stringified cipher params. + * + * @throws Error If neither the formatter nor the default formatter is set. + * + * @example + * + * var string = cipherParams + ''; + * var string = cipherParams.toString(); + * var string = cipherParams.toString(CryptoJS.format.OpenSSL); + */ + toString: function (formatter) { + return (formatter || this.formatter).stringify(this); + } + }); + + /** + * Format namespace. + */ + var C_format = C.format = {}; + + /** + * OpenSSL formatting strategy. + */ + var OpenSSLFormatter = C_format.OpenSSL = { + /** + * Converts a cipher params object to an OpenSSL-compatible string. + * + * @param {CipherParams} cipherParams The cipher params object. + * + * @return {string} The OpenSSL-compatible string. + * + * @static + * + * @example + * + * var openSSLString = CryptoJS.format.OpenSSL.stringify(cipherParams); + */ + stringify: function (cipherParams) { + // Shortcuts + var ciphertext = cipherParams.ciphertext; + var salt = cipherParams.salt; + + // Format + if (salt) { + var wordArray = WordArray.create([0x53616c74, 0x65645f5f]).concat(salt).concat(ciphertext); + } else { + var wordArray = ciphertext; + } + + return wordArray.toString(Base64); + }, + + /** + * Converts an OpenSSL-compatible string to a cipher params object. + * + * @param {string} openSSLStr The OpenSSL-compatible string. + * + * @return {CipherParams} The cipher params object. + * + * @static + * + * @example + * + * var cipherParams = CryptoJS.format.OpenSSL.parse(openSSLString); + */ + parse: function (openSSLStr) { + // Parse base64 + var ciphertext = Base64.parse(openSSLStr); + + // Shortcut + var ciphertextWords = ciphertext.words; + + // Test for salt + if (ciphertextWords[0] == 0x53616c74 && ciphertextWords[1] == 0x65645f5f) { + // Extract salt + var salt = WordArray.create(ciphertextWords.slice(2, 4)); + + // Remove salt from ciphertext + ciphertextWords.splice(0, 4); + ciphertext.sigBytes -= 16; + } + + return CipherParams.create({ ciphertext: ciphertext, salt: salt }); + } + }; + + /** + * A cipher wrapper that returns ciphertext as a serializable cipher params object. + */ + var SerializableCipher = C_lib.SerializableCipher = Base.extend({ + /** + * Configuration options. + * + * @property {Formatter} format The formatting strategy to convert cipher param objects to and from a string. Default: OpenSSL + */ + cfg: Base.extend({ + format: OpenSSLFormatter + }), + + /** + * Encrypts a message. + * + * @param {Cipher} cipher The cipher algorithm to use. + * @param {WordArray|string} message The message to encrypt. + * @param {WordArray} key The key. + * @param {Object} cfg (Optional) The configuration options to use for this operation. + * + * @return {CipherParams} A cipher params object. + * + * @static + * + * @example + * + * var ciphertextParams = CryptoJS.lib.SerializableCipher.encrypt(CryptoJS.algo.AES, message, key); + * var ciphertextParams = CryptoJS.lib.SerializableCipher.encrypt(CryptoJS.algo.AES, message, key, { iv: iv }); + * var ciphertextParams = CryptoJS.lib.SerializableCipher.encrypt(CryptoJS.algo.AES, message, key, { iv: iv, format: CryptoJS.format.OpenSSL }); + */ + encrypt: function (cipher, message, key, cfg) { + // Apply config defaults + cfg = this.cfg.extend(cfg); + + // Encrypt + var encryptor = cipher.createEncryptor(key, cfg); + var ciphertext = encryptor.finalize(message); + + // Shortcut + var cipherCfg = encryptor.cfg; + + // Create and return serializable cipher params + return CipherParams.create({ + ciphertext: ciphertext, + key: key, + iv: cipherCfg.iv, + algorithm: cipher, + mode: cipherCfg.mode, + padding: cipherCfg.padding, + blockSize: cipher.blockSize, + formatter: cfg.format + }); + }, + + /** + * Decrypts serialized ciphertext. + * + * @param {Cipher} cipher The cipher algorithm to use. + * @param {CipherParams|string} ciphertext The ciphertext to decrypt. + * @param {WordArray} key The key. + * @param {Object} cfg (Optional) The configuration options to use for this operation. + * + * @return {WordArray} The plaintext. + * + * @static + * + * @example + * + * var plaintext = CryptoJS.lib.SerializableCipher.decrypt(CryptoJS.algo.AES, formattedCiphertext, key, { iv: iv, format: CryptoJS.format.OpenSSL }); + * var plaintext = CryptoJS.lib.SerializableCipher.decrypt(CryptoJS.algo.AES, ciphertextParams, key, { iv: iv, format: CryptoJS.format.OpenSSL }); + */ + decrypt: function (cipher, ciphertext, key, cfg) { + // Apply config defaults + cfg = this.cfg.extend(cfg); + + // Convert string to CipherParams + ciphertext = this._parse(ciphertext, cfg.format); + + // Decrypt + var plaintext = cipher.createDecryptor(key, cfg).finalize(ciphertext.ciphertext); + + return plaintext; + }, + + /** + * Converts serialized ciphertext to CipherParams, + * else assumed CipherParams already and returns ciphertext unchanged. + * + * @param {CipherParams|string} ciphertext The ciphertext. + * @param {Formatter} format The formatting strategy to use to parse serialized ciphertext. + * + * @return {CipherParams} The unserialized ciphertext. + * + * @static + * + * @example + * + * var ciphertextParams = CryptoJS.lib.SerializableCipher._parse(ciphertextStringOrParams, format); + */ + _parse: function (ciphertext, format) { + if (typeof ciphertext == 'string') { + return format.parse(ciphertext, this); + } else { + return ciphertext; + } + } + }); + + /** + * Key derivation function namespace. + */ + var C_kdf = C.kdf = {}; + + /** + * OpenSSL key derivation function. + */ + var OpenSSLKdf = C_kdf.OpenSSL = { + /** + * Derives a key and IV from a password. + * + * @param {string} password The password to derive from. + * @param {number} keySize The size in words of the key to generate. + * @param {number} ivSize The size in words of the IV to generate. + * @param {WordArray|string} salt (Optional) A 64-bit salt to use. If omitted, a salt will be generated randomly. + * + * @return {CipherParams} A cipher params object with the key, IV, and salt. + * + * @static + * + * @example + * + * var derivedParams = CryptoJS.kdf.OpenSSL.execute('Password', 256/32, 128/32); + * var derivedParams = CryptoJS.kdf.OpenSSL.execute('Password', 256/32, 128/32, 'saltsalt'); + */ + execute: function (password, keySize, ivSize, salt) { + // Generate random salt + if (!salt) { + salt = WordArray.random(64/8); + } + + // Derive key and IV + var key = EvpKDF.create({ keySize: keySize + ivSize }).compute(password, salt); + + // Separate key and IV + var iv = WordArray.create(key.words.slice(keySize), ivSize * 4); + key.sigBytes = keySize * 4; + + // Return params + return CipherParams.create({ key: key, iv: iv, salt: salt }); + } + }; + + /** + * A serializable cipher wrapper that derives the key from a password, + * and returns ciphertext as a serializable cipher params object. + */ + var PasswordBasedCipher = C_lib.PasswordBasedCipher = SerializableCipher.extend({ + /** + * Configuration options. + * + * @property {KDF} kdf The key derivation function to use to generate a key and IV from a password. Default: OpenSSL + */ + cfg: SerializableCipher.cfg.extend({ + kdf: OpenSSLKdf + }), + + /** + * Encrypts a message using a password. + * + * @param {Cipher} cipher The cipher algorithm to use. + * @param {WordArray|string} message The message to encrypt. + * @param {string} password The password. + * @param {Object} cfg (Optional) The configuration options to use for this operation. + * + * @return {CipherParams} A cipher params object. + * + * @static + * + * @example + * + * var ciphertextParams = CryptoJS.lib.PasswordBasedCipher.encrypt(CryptoJS.algo.AES, message, 'password'); + * var ciphertextParams = CryptoJS.lib.PasswordBasedCipher.encrypt(CryptoJS.algo.AES, message, 'password', { format: CryptoJS.format.OpenSSL }); + */ + encrypt: function (cipher, message, password, cfg) { + // Apply config defaults + cfg = this.cfg.extend(cfg); + + // Derive key and other params + var derivedParams = cfg.kdf.execute(password, cipher.keySize, cipher.ivSize); + + // Add IV to config + cfg.iv = derivedParams.iv; + + // Encrypt + var ciphertext = SerializableCipher.encrypt.call(this, cipher, message, derivedParams.key, cfg); + + // Mix in derived params + ciphertext.mixIn(derivedParams); + + return ciphertext; + }, + + /** + * Decrypts serialized ciphertext using a password. + * + * @param {Cipher} cipher The cipher algorithm to use. + * @param {CipherParams|string} ciphertext The ciphertext to decrypt. + * @param {string} password The password. + * @param {Object} cfg (Optional) The configuration options to use for this operation. + * + * @return {WordArray} The plaintext. + * + * @static + * + * @example + * + * var plaintext = CryptoJS.lib.PasswordBasedCipher.decrypt(CryptoJS.algo.AES, formattedCiphertext, 'password', { format: CryptoJS.format.OpenSSL }); + * var plaintext = CryptoJS.lib.PasswordBasedCipher.decrypt(CryptoJS.algo.AES, ciphertextParams, 'password', { format: CryptoJS.format.OpenSSL }); + */ + decrypt: function (cipher, ciphertext, password, cfg) { + // Apply config defaults + cfg = this.cfg.extend(cfg); + + // Convert string to CipherParams + ciphertext = this._parse(ciphertext, cfg.format); + + // Derive key and other params + var derivedParams = cfg.kdf.execute(password, cipher.keySize, cipher.ivSize, ciphertext.salt); + + // Add IV to config + cfg.iv = derivedParams.iv; + + // Decrypt + var plaintext = SerializableCipher.decrypt.call(this, cipher, ciphertext, derivedParams.key, cfg); + + return plaintext; + } + }); +}()); diff --git a/js-deps/core.js b/js-deps/core.js new file mode 100644 index 00000000..996aa07c --- /dev/null +++ b/js-deps/core.js @@ -0,0 +1,712 @@ +/* +CryptoJS v3.1.2 +code.google.com/p/crypto-js +(c) 2009-2013 by Jeff Mott. All rights reserved. +code.google.com/p/crypto-js/wiki/License +*/ +/** + * CryptoJS core components. + */ +var CryptoJS = CryptoJS || (function (Math, undefined) { + /** + * CryptoJS namespace. + */ + var C = {}; + + /** + * Library namespace. + */ + var C_lib = C.lib = {}; + + /** + * Base object for prototypal inheritance. + */ + var Base = C_lib.Base = (function () { + function F() {} + + return { + /** + * Creates a new object that inherits from this object. + * + * @param {Object} overrides Properties to copy into the new object. + * + * @return {Object} The new object. + * + * @static + * + * @example + * + * var MyType = CryptoJS.lib.Base.extend({ + * field: 'value', + * + * method: function () { + * } + * }); + */ + extend: function (overrides) { + // Spawn + F.prototype = this; + var subtype = new F(); + + // Augment + if (overrides) { + subtype.mixIn(overrides); + } + + // Create default initializer + if (!subtype.hasOwnProperty('init')) { + subtype.init = function () { + subtype.$super.init.apply(this, arguments); + }; + } + + // Initializer's prototype is the subtype object + subtype.init.prototype = subtype; + + // Reference supertype + subtype.$super = this; + + return subtype; + }, + + /** + * Extends this object and runs the init method. + * Arguments to create() will be passed to init(). + * + * @return {Object} The new object. + * + * @static + * + * @example + * + * var instance = MyType.create(); + */ + create: function () { + var instance = this.extend(); + instance.init.apply(instance, arguments); + + return instance; + }, + + /** + * Initializes a newly created object. + * Override this method to add some logic when your objects are created. + * + * @example + * + * var MyType = CryptoJS.lib.Base.extend({ + * init: function () { + * // ... + * } + * }); + */ + init: function () { + }, + + /** + * Copies properties into this object. + * + * @param {Object} properties The properties to mix in. + * + * @example + * + * MyType.mixIn({ + * field: 'value' + * }); + */ + mixIn: function (properties) { + for (var propertyName in properties) { + if (properties.hasOwnProperty(propertyName)) { + this[propertyName] = properties[propertyName]; + } + } + + // IE won't copy toString using the loop above + if (properties.hasOwnProperty('toString')) { + this.toString = properties.toString; + } + }, + + /** + * Creates a copy of this object. + * + * @return {Object} The clone. + * + * @example + * + * var clone = instance.clone(); + */ + clone: function () { + return this.init.prototype.extend(this); + } + }; + }()); + + /** + * An array of 32-bit words. + * + * @property {Array} words The array of 32-bit words. + * @property {number} sigBytes The number of significant bytes in this word array. + */ + var WordArray = C_lib.WordArray = Base.extend({ + /** + * Initializes a newly created word array. + * + * @param {Array} words (Optional) An array of 32-bit words. + * @param {number} sigBytes (Optional) The number of significant bytes in the words. + * + * @example + * + * var wordArray = CryptoJS.lib.WordArray.create(); + * var wordArray = CryptoJS.lib.WordArray.create([0x00010203, 0x04050607]); + * var wordArray = CryptoJS.lib.WordArray.create([0x00010203, 0x04050607], 6); + */ + init: function (words, sigBytes) { + words = this.words = words || []; + + if (sigBytes != undefined) { + this.sigBytes = sigBytes; + } else { + this.sigBytes = words.length * 4; + } + }, + + /** + * Converts this word array to a string. + * + * @param {Encoder} encoder (Optional) The encoding strategy to use. Default: CryptoJS.enc.Hex + * + * @return {string} The stringified word array. + * + * @example + * + * var string = wordArray + ''; + * var string = wordArray.toString(); + * var string = wordArray.toString(CryptoJS.enc.Utf8); + */ + toString: function (encoder) { + return (encoder || Hex).stringify(this); + }, + + /** + * Concatenates a word array to this word array. + * + * @param {WordArray} wordArray The word array to append. + * + * @return {WordArray} This word array. + * + * @example + * + * wordArray1.concat(wordArray2); + */ + concat: function (wordArray) { + // Shortcuts + var thisWords = this.words; + var thatWords = wordArray.words; + var thisSigBytes = this.sigBytes; + var thatSigBytes = wordArray.sigBytes; + + // Clamp excess bits + this.clamp(); + + // Concat + if (thisSigBytes % 4) { + // Copy one byte at a time + for (var i = 0; i < thatSigBytes; i++) { + var thatByte = (thatWords[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff; + thisWords[(thisSigBytes + i) >>> 2] |= thatByte << (24 - ((thisSigBytes + i) % 4) * 8); + } + } else if (thatWords.length > 0xffff) { + // Copy one word at a time + for (var i = 0; i < thatSigBytes; i += 4) { + thisWords[(thisSigBytes + i) >>> 2] = thatWords[i >>> 2]; + } + } else { + // Copy all words at once + thisWords.push.apply(thisWords, thatWords); + } + this.sigBytes += thatSigBytes; + + // Chainable + return this; + }, + + /** + * Removes insignificant bits. + * + * @example + * + * wordArray.clamp(); + */ + clamp: function () { + // Shortcuts + var words = this.words; + var sigBytes = this.sigBytes; + + // Clamp + words[sigBytes >>> 2] &= 0xffffffff << (32 - (sigBytes % 4) * 8); + words.length = Math.ceil(sigBytes / 4); + }, + + /** + * Creates a copy of this word array. + * + * @return {WordArray} The clone. + * + * @example + * + * var clone = wordArray.clone(); + */ + clone: function () { + var clone = Base.clone.call(this); + clone.words = this.words.slice(0); + + return clone; + }, + + /** + * Creates a word array filled with random bytes. + * + * @param {number} nBytes The number of random bytes to generate. + * + * @return {WordArray} The random word array. + * + * @static + * + * @example + * + * var wordArray = CryptoJS.lib.WordArray.random(16); + */ + random: function (nBytes) { + var words = []; + for (var i = 0; i < nBytes; i += 4) { + words.push((Math.random() * 0x100000000) | 0); + } + + return new WordArray.init(words, nBytes); + } + }); + + /** + * Encoder namespace. + */ + var C_enc = C.enc = {}; + + /** + * Hex encoding strategy. + */ + var Hex = C_enc.Hex = { + /** + * Converts a word array to a hex string. + * + * @param {WordArray} wordArray The word array. + * + * @return {string} The hex string. + * + * @static + * + * @example + * + * var hexString = CryptoJS.enc.Hex.stringify(wordArray); + */ + stringify: function (wordArray) { + // Shortcuts + var words = wordArray.words; + var sigBytes = wordArray.sigBytes; + + // Convert + var hexChars = []; + for (var i = 0; i < sigBytes; i++) { + var bite = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff; + hexChars.push((bite >>> 4).toString(16)); + hexChars.push((bite & 0x0f).toString(16)); + } + + return hexChars.join(''); + }, + + /** + * Converts a hex string to a word array. + * + * @param {string} hexStr The hex string. + * + * @return {WordArray} The word array. + * + * @static + * + * @example + * + * var wordArray = CryptoJS.enc.Hex.parse(hexString); + */ + parse: function (hexStr) { + // Shortcut + var hexStrLength = hexStr.length; + + // Convert + var words = []; + for (var i = 0; i < hexStrLength; i += 2) { + words[i >>> 3] |= parseInt(hexStr.substr(i, 2), 16) << (24 - (i % 8) * 4); + } + + return new WordArray.init(words, hexStrLength / 2); + } + }; + + /** + * Latin1 encoding strategy. + */ + var Latin1 = C_enc.Latin1 = { + /** + * Converts a word array to a Latin1 string. + * + * @param {WordArray} wordArray The word array. + * + * @return {string} The Latin1 string. + * + * @static + * + * @example + * + * var latin1String = CryptoJS.enc.Latin1.stringify(wordArray); + */ + stringify: function (wordArray) { + // Shortcuts + var words = wordArray.words; + var sigBytes = wordArray.sigBytes; + + // Convert + var latin1Chars = []; + for (var i = 0; i < sigBytes; i++) { + var bite = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff; + latin1Chars.push(String.fromCharCode(bite)); + } + + return latin1Chars.join(''); + }, + + /** + * Converts a Latin1 string to a word array. + * + * @param {string} latin1Str The Latin1 string. + * + * @return {WordArray} The word array. + * + * @static + * + * @example + * + * var wordArray = CryptoJS.enc.Latin1.parse(latin1String); + */ + parse: function (latin1Str) { + // Shortcut + var latin1StrLength = latin1Str.length; + + // Convert + var words = []; + for (var i = 0; i < latin1StrLength; i++) { + words[i >>> 2] |= (latin1Str.charCodeAt(i) & 0xff) << (24 - (i % 4) * 8); + } + + return new WordArray.init(words, latin1StrLength); + } + }; + + /** + * UTF-8 encoding strategy. + */ + var Utf8 = C_enc.Utf8 = { + /** + * Converts a word array to a UTF-8 string. + * + * @param {WordArray} wordArray The word array. + * + * @return {string} The UTF-8 string. + * + * @static + * + * @example + * + * var utf8String = CryptoJS.enc.Utf8.stringify(wordArray); + */ + stringify: function (wordArray) { + try { + return decodeURIComponent(escape(Latin1.stringify(wordArray))); + } catch (e) { + throw new Error('Malformed UTF-8 data'); + } + }, + + /** + * Converts a UTF-8 string to a word array. + * + * @param {string} utf8Str The UTF-8 string. + * + * @return {WordArray} The word array. + * + * @static + * + * @example + * + * var wordArray = CryptoJS.enc.Utf8.parse(utf8String); + */ + parse: function (utf8Str) { + return Latin1.parse(unescape(encodeURIComponent(utf8Str))); + } + }; + + /** + * Abstract buffered block algorithm template. + * + * The property blockSize must be implemented in a concrete subtype. + * + * @property {number} _minBufferSize The number of blocks that should be kept unprocessed in the buffer. Default: 0 + */ + var BufferedBlockAlgorithm = C_lib.BufferedBlockAlgorithm = Base.extend({ + /** + * Resets this block algorithm's data buffer to its initial state. + * + * @example + * + * bufferedBlockAlgorithm.reset(); + */ + reset: function () { + // Initial values + this._data = new WordArray.init(); + this._nDataBytes = 0; + }, + + /** + * Adds new data to this block algorithm's buffer. + * + * @param {WordArray|string} data The data to append. Strings are converted to a WordArray using UTF-8. + * + * @example + * + * bufferedBlockAlgorithm._append('data'); + * bufferedBlockAlgorithm._append(wordArray); + */ + _append: function (data) { + // Convert string to WordArray, else assume WordArray already + if (typeof data == 'string') { + data = Utf8.parse(data); + } + + // Append + this._data.concat(data); + this._nDataBytes += data.sigBytes; + }, + + /** + * Processes available data blocks. + * + * This method invokes _doProcessBlock(offset), which must be implemented by a concrete subtype. + * + * @param {boolean} doFlush Whether all blocks and partial blocks should be processed. + * + * @return {WordArray} The processed data. + * + * @example + * + * var processedData = bufferedBlockAlgorithm._process(); + * var processedData = bufferedBlockAlgorithm._process(!!'flush'); + */ + _process: function (doFlush) { + // Shortcuts + var data = this._data; + var dataWords = data.words; + var dataSigBytes = data.sigBytes; + var blockSize = this.blockSize; + var blockSizeBytes = blockSize * 4; + + // Count blocks ready + var nBlocksReady = dataSigBytes / blockSizeBytes; + if (doFlush) { + // Round up to include partial blocks + nBlocksReady = Math.ceil(nBlocksReady); + } else { + // Round down to include only full blocks, + // less the number of blocks that must remain in the buffer + nBlocksReady = Math.max((nBlocksReady | 0) - this._minBufferSize, 0); + } + + // Count words ready + var nWordsReady = nBlocksReady * blockSize; + + // Count bytes ready + var nBytesReady = Math.min(nWordsReady * 4, dataSigBytes); + + // Process blocks + if (nWordsReady) { + for (var offset = 0; offset < nWordsReady; offset += blockSize) { + // Perform concrete-algorithm logic + this._doProcessBlock(dataWords, offset); + } + + // Remove processed words + var processedWords = dataWords.splice(0, nWordsReady); + data.sigBytes -= nBytesReady; + } + + // Return processed words + return new WordArray.init(processedWords, nBytesReady); + }, + + /** + * Creates a copy of this object. + * + * @return {Object} The clone. + * + * @example + * + * var clone = bufferedBlockAlgorithm.clone(); + */ + clone: function () { + var clone = Base.clone.call(this); + clone._data = this._data.clone(); + + return clone; + }, + + _minBufferSize: 0 + }); + + /** + * Abstract hasher template. + * + * @property {number} blockSize The number of 32-bit words this hasher operates on. Default: 16 (512 bits) + */ + var Hasher = C_lib.Hasher = BufferedBlockAlgorithm.extend({ + /** + * Configuration options. + */ + cfg: Base.extend(), + + /** + * Initializes a newly created hasher. + * + * @param {Object} cfg (Optional) The configuration options to use for this hash computation. + * + * @example + * + * var hasher = CryptoJS.algo.SHA256.create(); + */ + init: function (cfg) { + // Apply config defaults + this.cfg = this.cfg.extend(cfg); + + // Set initial values + this.reset(); + }, + + /** + * Resets this hasher to its initial state. + * + * @example + * + * hasher.reset(); + */ + reset: function () { + // Reset data buffer + BufferedBlockAlgorithm.reset.call(this); + + // Perform concrete-hasher logic + this._doReset(); + }, + + /** + * Updates this hasher with a message. + * + * @param {WordArray|string} messageUpdate The message to append. + * + * @return {Hasher} This hasher. + * + * @example + * + * hasher.update('message'); + * hasher.update(wordArray); + */ + update: function (messageUpdate) { + // Append + this._append(messageUpdate); + + // Update the hash + this._process(); + + // Chainable + return this; + }, + + /** + * Finalizes the hash computation. + * Note that the finalize operation is effectively a destructive, read-once operation. + * + * @param {WordArray|string} messageUpdate (Optional) A final message update. + * + * @return {WordArray} The hash. + * + * @example + * + * var hash = hasher.finalize(); + * var hash = hasher.finalize('message'); + * var hash = hasher.finalize(wordArray); + */ + finalize: function (messageUpdate) { + // Final message update + if (messageUpdate) { + this._append(messageUpdate); + } + + // Perform concrete-hasher logic + var hash = this._doFinalize(); + + return hash; + }, + + blockSize: 512/32, + + /** + * Creates a shortcut function to a hasher's object interface. + * + * @param {Hasher} hasher The hasher to create a helper for. + * + * @return {Function} The shortcut function. + * + * @static + * + * @example + * + * var SHA256 = CryptoJS.lib.Hasher._createHelper(CryptoJS.algo.SHA256); + */ + _createHelper: function (hasher) { + return function (message, cfg) { + return new hasher.init(cfg).finalize(message); + }; + }, + + /** + * Creates a shortcut function to the HMAC's object interface. + * + * @param {Hasher} hasher The hasher to use in this HMAC helper. + * + * @return {Function} The shortcut function. + * + * @static + * + * @example + * + * var HmacSHA256 = CryptoJS.lib.Hasher._createHmacHelper(CryptoJS.algo.SHA256); + */ + _createHmacHelper: function (hasher) { + return function (message, key) { + return new C_algo.HMAC.init(hasher, key).finalize(message); + }; + } + }); + + /** + * Algorithm namespace. + */ + var C_algo = C.algo = {}; + + return C; +}(Math)); diff --git a/js-deps/enc-base64.js b/js-deps/enc-base64.js new file mode 100644 index 00000000..739f4a84 --- /dev/null +++ b/js-deps/enc-base64.js @@ -0,0 +1,109 @@ +/* +CryptoJS v3.1.2 +code.google.com/p/crypto-js +(c) 2009-2013 by Jeff Mott. All rights reserved. +code.google.com/p/crypto-js/wiki/License +*/ +(function () { + // Shortcuts + var C = CryptoJS; + var C_lib = C.lib; + var WordArray = C_lib.WordArray; + var C_enc = C.enc; + + /** + * Base64 encoding strategy. + */ + var Base64 = C_enc.Base64 = { + /** + * Converts a word array to a Base64 string. + * + * @param {WordArray} wordArray The word array. + * + * @return {string} The Base64 string. + * + * @static + * + * @example + * + * var base64String = CryptoJS.enc.Base64.stringify(wordArray); + */ + stringify: function (wordArray) { + // Shortcuts + var words = wordArray.words; + var sigBytes = wordArray.sigBytes; + var map = this._map; + + // Clamp excess bits + wordArray.clamp(); + + // Convert + var base64Chars = []; + for (var i = 0; i < sigBytes; i += 3) { + var byte1 = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff; + var byte2 = (words[(i + 1) >>> 2] >>> (24 - ((i + 1) % 4) * 8)) & 0xff; + var byte3 = (words[(i + 2) >>> 2] >>> (24 - ((i + 2) % 4) * 8)) & 0xff; + + var triplet = (byte1 << 16) | (byte2 << 8) | byte3; + + for (var j = 0; (j < 4) && (i + j * 0.75 < sigBytes); j++) { + base64Chars.push(map.charAt((triplet >>> (6 * (3 - j))) & 0x3f)); + } + } + + // Add padding + var paddingChar = map.charAt(64); + if (paddingChar) { + while (base64Chars.length % 4) { + base64Chars.push(paddingChar); + } + } + + return base64Chars.join(''); + }, + + /** + * Converts a Base64 string to a word array. + * + * @param {string} base64Str The Base64 string. + * + * @return {WordArray} The word array. + * + * @static + * + * @example + * + * var wordArray = CryptoJS.enc.Base64.parse(base64String); + */ + parse: function (base64Str) { + // Shortcuts + var base64StrLength = base64Str.length; + var map = this._map; + + // Ignore padding + var paddingChar = map.charAt(64); + if (paddingChar) { + var paddingIndex = base64Str.indexOf(paddingChar); + if (paddingIndex != -1) { + base64StrLength = paddingIndex; + } + } + + // Convert + var words = []; + var nBytes = 0; + for (var i = 0; i < base64StrLength; i++) { + if (i % 4) { + var bits1 = map.indexOf(base64Str.charAt(i - 1)) << ((i % 4) * 2); + var bits2 = map.indexOf(base64Str.charAt(i)) >>> (6 - (i % 4) * 2); + words[nBytes >>> 2] |= (bits1 | bits2) << (24 - (nBytes % 4) * 8); + nBytes++; + } + } + + return WordArray.create(words, nBytes); + }, + + _map: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=' + }; +}()); diff --git a/js-deps/mode-ctr-min.js b/js-deps/mode-ctr-min.js new file mode 100644 index 00000000..f93e4abf --- /dev/null +++ b/js-deps/mode-ctr-min.js @@ -0,0 +1,44 @@ +/* +CryptoJS v3.1.2 +code.google.com/p/crypto-js +(c) 2009-2013 by Jeff Mott. All rights reserved. +code.google.com/p/crypto-js/wiki/License +*/ +/** + * Counter block mode. + */ +CryptoJS.mode.CTR = (function () { + var CTR = CryptoJS.lib.BlockCipherMode.extend(); + + var Encryptor = CTR.Encryptor = CTR.extend({ + processBlock: function (words, offset) { + // Shortcuts + var cipher = this._cipher + var blockSize = cipher.blockSize; + var iv = this._iv; + var counter = this._counter; + + // Generate keystream + if (iv) { + counter = this._counter = iv.slice(0); + + // Remove IV for subsequent blocks + this._iv = undefined; + } + var keystream = counter.slice(0); + cipher.encryptBlock(keystream, 0); + + // Increment counter + counter[blockSize - 1] = (counter[blockSize - 1] + 1) | 0 + + // Encrypt + for (var i = 0; i < blockSize; i++) { + words[offset + i] ^= keystream[i]; + } + } + }); + + CTR.Decryptor = Encryptor; + + return CTR; +}()); diff --git a/js-deps/pad-nopadding.js b/js-deps/pad-nopadding.js new file mode 100644 index 00000000..3bd58742 --- /dev/null +++ b/js-deps/pad-nopadding.js @@ -0,0 +1,16 @@ +/* +CryptoJS v3.1.2 +code.google.com/p/crypto-js +(c) 2009-2013 by Jeff Mott. All rights reserved. +code.google.com/p/crypto-js/wiki/License +*/ +/** + * A noop padding strategy. + */ +CryptoJS.pad.NoPadding = { + pad: function () { + }, + + unpad: function () { + } +}; diff --git a/options.html b/options.html index cc25a5b7..4aa16b6e 100644 --- a/options.html +++ b/options.html @@ -28,7 +28,12 @@ + + + + + diff --git a/popup.html b/popup.html index 5065e090..49408cef 100644 --- a/popup.html +++ b/popup.html @@ -16,7 +16,12 @@ + + + + + diff --git a/test.html b/test.html index 6a7ca454..7a699064 100644 --- a/test.html +++ b/test.html @@ -13,9 +13,14 @@ - - + + + + + + +