0) {
+ if(p < this.DB && (d = this[i]>>p) > 0) { m = true; r = int2char(d); }
+ while(i >= 0) {
+ if(p < k) {
+ d = (this[i]&((1<>(p+=this.DB-k);
+ }
+ else {
+ d = (this[i]>>(p-=k))&km;
+ if(p <= 0) { p += this.DB; --i; }
+ }
+ if(d > 0) m = true;
+ if(m) r += int2char(d);
+ }
+ }
+ return m?r:"0";
+ }
+
+ // (public) -this
+ function bnNegate() { var r = nbi(); BigInteger.ZERO.subTo(this,r); return r; }
+
+ // (public) |this|
+ function bnAbs() { return (this.s<0)?this.negate():this; }
+
+ // (public) return + if this > a, - if this < a, 0 if equal
+ function bnCompareTo(a) {
+ var r = this.s-a.s;
+ if(r != 0) return r;
+ var i = this.t;
+ r = i-a.t;
+ if(r != 0) return (this.s<0)?-r:r;
+ while(--i >= 0) if((r=this[i]-a[i]) != 0) return r;
+ return 0;
+ }
+
+ // returns bit length of the integer x
+ function nbits(x) {
+ var r = 1, t;
+ if((t=x>>>16) != 0) { x = t; r += 16; }
+ if((t=x>>8) != 0) { x = t; r += 8; }
+ if((t=x>>4) != 0) { x = t; r += 4; }
+ if((t=x>>2) != 0) { x = t; r += 2; }
+ if((t=x>>1) != 0) { x = t; r += 1; }
+ return r;
+ }
+
+ // (public) return the number of bits in "this"
+ function bnBitLength() {
+ if(this.t <= 0) return 0;
+ return this.DB*(this.t-1)+nbits(this[this.t-1]^(this.s&this.DM));
+ }
+
+ // (protected) r = this << n*DB
+ function bnpDLShiftTo(n,r) {
+ var i;
+ for(i = this.t-1; i >= 0; --i) r[i+n] = this[i];
+ for(i = n-1; i >= 0; --i) r[i] = 0;
+ r.t = this.t+n;
+ r.s = this.s;
+ }
+
+ // (protected) r = this >> n*DB
+ function bnpDRShiftTo(n,r) {
+ for(var i = n; i < this.t; ++i) r[i-n] = this[i];
+ r.t = Math.max(this.t-n,0);
+ r.s = this.s;
+ }
+
+ // (protected) r = this << n
+ function bnpLShiftTo(n,r) {
+ var bs = n%this.DB;
+ var cbs = this.DB-bs;
+ var bm = (1<= 0; --i) {
+ r[i+ds+1] = (this[i]>>cbs)|c;
+ c = (this[i]&bm)<= 0; --i) r[i] = 0;
+ r[ds] = c;
+ r.t = this.t+ds+1;
+ r.s = this.s;
+ r.clamp();
+ }
+
+ // (protected) r = this >> n
+ function bnpRShiftTo(n,r) {
+ r.s = this.s;
+ var ds = Math.floor(n/this.DB);
+ if(ds >= this.t) { r.t = 0; return; }
+ var bs = n%this.DB;
+ var cbs = this.DB-bs;
+ var bm = (1<>bs;
+ for(var i = ds+1; i < this.t; ++i) {
+ r[i-ds-1] |= (this[i]&bm)<>bs;
+ }
+ if(bs > 0) r[this.t-ds-1] |= (this.s&bm)<>= this.DB;
+ }
+ if(a.t < this.t) {
+ c -= a.s;
+ while(i < this.t) {
+ c += this[i];
+ r[i++] = c&this.DM;
+ c >>= this.DB;
+ }
+ c += this.s;
+ }
+ else {
+ c += this.s;
+ while(i < a.t) {
+ c -= a[i];
+ r[i++] = c&this.DM;
+ c >>= this.DB;
+ }
+ c -= a.s;
+ }
+ r.s = (c<0)?-1:0;
+ if(c < -1) r[i++] = this.DV+c;
+ else if(c > 0) r[i++] = c;
+ r.t = i;
+ r.clamp();
+ }
+
+ // (protected) r = this * a, r != this,a (HAC 14.12)
+ // "this" should be the larger one if appropriate.
+ function bnpMultiplyTo(a,r) {
+ var x = this.abs(), y = a.abs();
+ var i = x.t;
+ r.t = i+y.t;
+ while(--i >= 0) r[i] = 0;
+ for(i = 0; i < y.t; ++i) r[i+x.t] = x.am(0,y[i],r,i,0,x.t);
+ r.s = 0;
+ r.clamp();
+ if(this.s != a.s) BigInteger.ZERO.subTo(r,r);
+ }
+
+ // (protected) r = this^2, r != this (HAC 14.16)
+ function bnpSquareTo(r) {
+ var x = this.abs();
+ var i = r.t = 2*x.t;
+ while(--i >= 0) r[i] = 0;
+ for(i = 0; i < x.t-1; ++i) {
+ var c = x.am(i,x[i],r,2*i,0,1);
+ if((r[i+x.t]+=x.am(i+1,2*x[i],r,2*i+1,c,x.t-i-1)) >= x.DV) {
+ r[i+x.t] -= x.DV;
+ r[i+x.t+1] = 1;
+ }
+ }
+ if(r.t > 0) r[r.t-1] += x.am(i,x[i],r,2*i,0,1);
+ r.s = 0;
+ r.clamp();
+ }
+
+ // (protected) divide this by m, quotient and remainder to q, r (HAC 14.20)
+ // r != q, this != m. q or r may be null.
+ function bnpDivRemTo(m,q,r) {
+ var pm = m.abs();
+ if(pm.t <= 0) return;
+ var pt = this.abs();
+ if(pt.t < pm.t) {
+ if(q != null) q.fromInt(0);
+ if(r != null) this.copyTo(r);
+ return;
+ }
+ if(r == null) r = nbi();
+ var y = nbi(), ts = this.s, ms = m.s;
+ var nsh = this.DB-nbits(pm[pm.t-1]); // normalize modulus
+ if(nsh > 0) { pm.lShiftTo(nsh,y); pt.lShiftTo(nsh,r); }
+ else { pm.copyTo(y); pt.copyTo(r); }
+ var ys = y.t;
+ var y0 = y[ys-1];
+ if(y0 == 0) return;
+ var yt = y0*(1<1)?y[ys-2]>>this.F2:0);
+ var d1 = this.FV/yt, d2 = (1<= 0) {
+ r[r.t++] = 1;
+ r.subTo(t,r);
+ }
+ BigInteger.ONE.dlShiftTo(ys,t);
+ t.subTo(y,y); // "negative" y so we can replace sub with am later
+ while(y.t < ys) y[y.t++] = 0;
+ while(--j >= 0) {
+ // Estimate quotient digit
+ var qd = (r[--i]==y0)?this.DM:Math.floor(r[i]*d1+(r[i-1]+e)*d2);
+ if((r[i]+=y.am(0,qd,r,j,0,ys)) < qd) { // Try it out
+ y.dlShiftTo(j,t);
+ r.subTo(t,r);
+ while(r[i] < --qd) r.subTo(t,r);
+ }
+ }
+ if(q != null) {
+ r.drShiftTo(ys,q);
+ if(ts != ms) BigInteger.ZERO.subTo(q,q);
+ }
+ r.t = ys;
+ r.clamp();
+ if(nsh > 0) r.rShiftTo(nsh,r); // Denormalize remainder
+ if(ts < 0) BigInteger.ZERO.subTo(r,r);
+ }
+
+ // (public) this mod a
+ function bnMod(a) {
+ var r = nbi();
+ this.abs().divRemTo(a,null,r);
+ if(this.s < 0 && r.compareTo(BigInteger.ZERO) > 0) a.subTo(r,r);
+ return r;
+ }
+
+ // Modular reduction using "classic" algorithm
+ function Classic(m) { this.m = m; }
+ function cConvert(x) {
+ if(x.s < 0 || x.compareTo(this.m) >= 0) return x.mod(this.m);
+ else return x;
+ }
+ function cRevert(x) { return x; }
+ function cReduce(x) { x.divRemTo(this.m,null,x); }
+ function cMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); }
+ function cSqrTo(x,r) { x.squareTo(r); this.reduce(r); }
+
+ Classic.prototype.convert = cConvert;
+ Classic.prototype.revert = cRevert;
+ Classic.prototype.reduce = cReduce;
+ Classic.prototype.mulTo = cMulTo;
+ Classic.prototype.sqrTo = cSqrTo;
+
+ // (protected) return "-1/this % 2^DB"; useful for Mont. reduction
+ // justification:
+ // xy == 1 (mod m)
+ // xy = 1+km
+ // xy(2-xy) = (1+km)(1-km)
+ // x[y(2-xy)] = 1-k^2m^2
+ // x[y(2-xy)] == 1 (mod m^2)
+ // if y is 1/x mod m, then y(2-xy) is 1/x mod m^2
+ // should reduce x and y(2-xy) by m^2 at each step to keep size bounded.
+ // JS multiply "overflows" differently from C/C++, so care is needed here.
+ function bnpInvDigit() {
+ if(this.t < 1) return 0;
+ var x = this[0];
+ if((x&1) == 0) return 0;
+ var y = x&3; // y == 1/x mod 2^2
+ y = (y*(2-(x&0xf)*y))&0xf; // y == 1/x mod 2^4
+ y = (y*(2-(x&0xff)*y))&0xff; // y == 1/x mod 2^8
+ y = (y*(2-(((x&0xffff)*y)&0xffff)))&0xffff; // y == 1/x mod 2^16
+ // last step - calculate inverse mod DV directly;
+ // assumes 16 < DB <= 32 and assumes ability to handle 48-bit ints
+ y = (y*(2-x*y%this.DV))%this.DV; // y == 1/x mod 2^dbits
+ // we really want the negative inverse, and -DV < y < DV
+ return (y>0)?this.DV-y:-y;
+ }
+
+ // Montgomery reduction
+ function Montgomery(m) {
+ this.m = m;
+ this.mp = m.invDigit();
+ this.mpl = this.mp&0x7fff;
+ this.mph = this.mp>>15;
+ this.um = (1<<(m.DB-15))-1;
+ this.mt2 = 2*m.t;
+ }
+
+ // xR mod m
+ function montConvert(x) {
+ var r = nbi();
+ x.abs().dlShiftTo(this.m.t,r);
+ r.divRemTo(this.m,null,r);
+ if(x.s < 0 && r.compareTo(BigInteger.ZERO) > 0) this.m.subTo(r,r);
+ return r;
+ }
+
+ // x/R mod m
+ function montRevert(x) {
+ var r = nbi();
+ x.copyTo(r);
+ this.reduce(r);
+ return r;
+ }
+
+ // x = x/R mod m (HAC 14.32)
+ function montReduce(x) {
+ while(x.t <= this.mt2) // pad x so am has enough room later
+ x[x.t++] = 0;
+ for(var i = 0; i < this.m.t; ++i) {
+ // faster way of calculating u0 = x[i]*mp mod DV
+ var j = x[i]&0x7fff;
+ var u0 = (j*this.mpl+(((j*this.mph+(x[i]>>15)*this.mpl)&this.um)<<15))&x.DM;
+ // use am to combine the multiply-shift-add into one call
+ j = i+this.m.t;
+ x[j] += this.m.am(0,u0,x,i,0,this.m.t);
+ // propagate carry
+ while(x[j] >= x.DV) { x[j] -= x.DV; x[++j]++; }
+ }
+ x.clamp();
+ x.drShiftTo(this.m.t,x);
+ if(x.compareTo(this.m) >= 0) x.subTo(this.m,x);
+ }
+
+ // r = "x^2/R mod m"; x != r
+ function montSqrTo(x,r) { x.squareTo(r); this.reduce(r); }
+
+ // r = "xy/R mod m"; x,y != r
+ function montMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); }
+
+ Montgomery.prototype.convert = montConvert;
+ Montgomery.prototype.revert = montRevert;
+ Montgomery.prototype.reduce = montReduce;
+ Montgomery.prototype.mulTo = montMulTo;
+ Montgomery.prototype.sqrTo = montSqrTo;
+
+ // (protected) true iff this is even
+ function bnpIsEven() { return ((this.t>0)?(this[0]&1):this.s) == 0; }
+
+ // (protected) this^e, e < 2^32, doing sqr and mul with "r" (HAC 14.79)
+ function bnpExp(e,z) {
+ if(e > 0xffffffff || e < 1) return BigInteger.ONE;
+ var r = nbi(), r2 = nbi(), g = z.convert(this), i = nbits(e)-1;
+ g.copyTo(r);
+ while(--i >= 0) {
+ z.sqrTo(r,r2);
+ if((e&(1< 0) z.mulTo(r2,g,r);
+ else { var t = r; r = r2; r2 = t; }
+ }
+ return z.revert(r);
+ }
+
+ // (public) this^e % m, 0 <= e < 2^32
+ function bnModPowInt(e,m) {
+ var z;
+ if(e < 256 || m.isEven()) z = new Classic(m); else z = new Montgomery(m);
+ return this.exp(e,z);
+ }
+
+ // protected
+ BigInteger.prototype.copyTo = bnpCopyTo;
+ BigInteger.prototype.fromInt = bnpFromInt;
+ BigInteger.prototype.fromString = bnpFromString;
+ BigInteger.prototype.clamp = bnpClamp;
+ BigInteger.prototype.dlShiftTo = bnpDLShiftTo;
+ BigInteger.prototype.drShiftTo = bnpDRShiftTo;
+ BigInteger.prototype.lShiftTo = bnpLShiftTo;
+ BigInteger.prototype.rShiftTo = bnpRShiftTo;
+ BigInteger.prototype.subTo = bnpSubTo;
+ BigInteger.prototype.multiplyTo = bnpMultiplyTo;
+ BigInteger.prototype.squareTo = bnpSquareTo;
+ BigInteger.prototype.divRemTo = bnpDivRemTo;
+ BigInteger.prototype.invDigit = bnpInvDigit;
+ BigInteger.prototype.isEven = bnpIsEven;
+ BigInteger.prototype.exp = bnpExp;
+
+ // public
+ BigInteger.prototype.toString = bnToString;
+ BigInteger.prototype.negate = bnNegate;
+ BigInteger.prototype.abs = bnAbs;
+ BigInteger.prototype.compareTo = bnCompareTo;
+ BigInteger.prototype.bitLength = bnBitLength;
+ BigInteger.prototype.mod = bnMod;
+ BigInteger.prototype.modPowInt = bnModPowInt;
+
+ // "constants"
+ BigInteger.ZERO = nbv(0);
+ BigInteger.ONE = nbv(1);
+
+ // jsbn2 stuff
+
+ // (protected) convert from radix string
+ function bnpFromRadix(s,b) {
+ this.fromInt(0);
+ if(b == null) b = 10;
+ var cs = this.chunkSize(b);
+ var d = Math.pow(b,cs), mi = false, j = 0, w = 0;
+ for(var i = 0; i < s.length; ++i) {
+ var x = intAt(s,i);
+ if(x < 0) {
+ if(s.charAt(i) == "-" && this.signum() == 0) mi = true;
+ continue;
+ }
+ w = b*w+x;
+ if(++j >= cs) {
+ this.dMultiply(d);
+ this.dAddOffset(w,0);
+ j = 0;
+ w = 0;
+ }
+ }
+ if(j > 0) {
+ this.dMultiply(Math.pow(b,j));
+ this.dAddOffset(w,0);
+ }
+ if(mi) BigInteger.ZERO.subTo(this,this);
+ }
+
+ // (protected) return x s.t. r^x < DV
+ function bnpChunkSize(r) { return Math.floor(Math.LN2*this.DB/Math.log(r)); }
+
+ // (public) 0 if this == 0, 1 if this > 0
+ function bnSigNum() {
+ if(this.s < 0) return -1;
+ else if(this.t <= 0 || (this.t == 1 && this[0] <= 0)) return 0;
+ else return 1;
+ }
+
+ // (protected) this *= n, this >= 0, 1 < n < DV
+ function bnpDMultiply(n) {
+ this[this.t] = this.am(0,n-1,this,0,0,this.t);
+ ++this.t;
+ this.clamp();
+ }
+
+ // (protected) this += n << w words, this >= 0
+ function bnpDAddOffset(n,w) {
+ if(n == 0) return;
+ while(this.t <= w) this[this.t++] = 0;
+ this[w] += n;
+ while(this[w] >= this.DV) {
+ this[w] -= this.DV;
+ if(++w >= this.t) this[this.t++] = 0;
+ ++this[w];
+ }
+ }
+
+ // (protected) convert to radix string
+ function bnpToRadix(b) {
+ if(b == null) b = 10;
+ if(this.signum() == 0 || b < 2 || b > 36) return "0";
+ var cs = this.chunkSize(b);
+ var a = Math.pow(b,cs);
+ var d = nbv(a), y = nbi(), z = nbi(), r = "";
+ this.divRemTo(d,y,z);
+ while(y.signum() > 0) {
+ r = (a+z.intValue()).toString(b).substr(1) + r;
+ y.divRemTo(d,y,z);
+ }
+ return z.intValue().toString(b) + r;
+ }
+
+ // (public) return value as integer
+ function bnIntValue() {
+ if(this.s < 0) {
+ if(this.t == 1) return this[0]-this.DV;
+ else if(this.t == 0) return -1;
+ }
+ else if(this.t == 1) return this[0];
+ else if(this.t == 0) return 0;
+ // assumes 16 < DB < 32
+ return ((this[1]&((1<<(32-this.DB))-1))<>= this.DB;
+ }
+ if(a.t < this.t) {
+ c += a.s;
+ while(i < this.t) {
+ c += this[i];
+ r[i++] = c&this.DM;
+ c >>= this.DB;
+ }
+ c += this.s;
+ }
+ else {
+ c += this.s;
+ while(i < a.t) {
+ c += a[i];
+ r[i++] = c&this.DM;
+ c >>= this.DB;
+ }
+ c += a.s;
+ }
+ r.s = (c<0)?-1:0;
+ if(c > 0) r[i++] = c;
+ else if(c < -1) r[i++] = this.DV+c;
+ r.t = i;
+ r.clamp();
+ }
+
+ BigInteger.prototype.fromRadix = bnpFromRadix;
+ BigInteger.prototype.chunkSize = bnpChunkSize;
+ BigInteger.prototype.signum = bnSigNum;
+ BigInteger.prototype.dMultiply = bnpDMultiply;
+ BigInteger.prototype.dAddOffset = bnpDAddOffset;
+ BigInteger.prototype.toRadix = bnpToRadix;
+ BigInteger.prototype.intValue = bnIntValue;
+ BigInteger.prototype.addTo = bnpAddTo;
+
+ //======= end jsbn =======
+
+ // Emscripten wrapper
+ var Wrapper = {
+ abs: function(l, h) {
+ var x = new goog.math.Long(l, h);
+ var ret;
+ if (x.isNegative()) {
+ ret = x.negate();
+ } else {
+ ret = x;
+ }
+ HEAP32[tempDoublePtr>>2] = ret.low_;
+ HEAP32[tempDoublePtr+4>>2] = ret.high_;
+ },
+ ensureTemps: function() {
+ if (Wrapper.ensuredTemps) return;
+ Wrapper.ensuredTemps = true;
+ Wrapper.two32 = new BigInteger();
+ Wrapper.two32.fromString('4294967296', 10);
+ Wrapper.two64 = new BigInteger();
+ Wrapper.two64.fromString('18446744073709551616', 10);
+ Wrapper.temp1 = new BigInteger();
+ Wrapper.temp2 = new BigInteger();
+ },
+ lh2bignum: function(l, h) {
+ var a = new BigInteger();
+ a.fromString(h.toString(), 10);
+ var b = new BigInteger();
+ a.multiplyTo(Wrapper.two32, b);
+ var c = new BigInteger();
+ c.fromString(l.toString(), 10);
+ var d = new BigInteger();
+ c.addTo(b, d);
+ return d;
+ },
+ stringify: function(l, h, unsigned) {
+ var ret = new goog.math.Long(l, h).toString();
+ if (unsigned && ret[0] == '-') {
+ // unsign slowly using jsbn bignums
+ Wrapper.ensureTemps();
+ var bignum = new BigInteger();
+ bignum.fromString(ret, 10);
+ ret = new BigInteger();
+ Wrapper.two64.addTo(bignum, ret);
+ ret = ret.toString(10);
+ }
+ return ret;
+ },
+ fromString: function(str, base, min, max, unsigned) {
+ Wrapper.ensureTemps();
+ var bignum = new BigInteger();
+ bignum.fromString(str, base);
+ var bigmin = new BigInteger();
+ bigmin.fromString(min, 10);
+ var bigmax = new BigInteger();
+ bigmax.fromString(max, 10);
+ if (unsigned && bignum.compareTo(BigInteger.ZERO) < 0) {
+ var temp = new BigInteger();
+ bignum.addTo(Wrapper.two64, temp);
+ bignum = temp;
+ }
+ var error = false;
+ if (bignum.compareTo(bigmin) < 0) {
+ bignum = bigmin;
+ error = true;
+ } else if (bignum.compareTo(bigmax) > 0) {
+ bignum = bigmax;
+ error = true;
+ }
+ var ret = goog.math.Long.fromString(bignum.toString()); // min-max checks should have clamped this to a range goog.math.Long can handle well
+ HEAP32[tempDoublePtr>>2] = ret.low_;
+ HEAP32[tempDoublePtr+4>>2] = ret.high_;
+ if (error) throw 'range error';
+ }
+ };
+ return Wrapper;
+})();
+
+//======= end closure i64 code =======
+
+
+
+// === Auto-generated postamble setup entry stuff ===
+
+if (memoryInitializer) {
+ if (typeof Module['locateFile'] === 'function') {
+ memoryInitializer = Module['locateFile'](memoryInitializer);
+ } else if (Module['memoryInitializerPrefixURL']) {
+ memoryInitializer = Module['memoryInitializerPrefixURL'] + memoryInitializer;
+ }
+ if (ENVIRONMENT_IS_NODE || ENVIRONMENT_IS_SHELL) {
+ var data = Module['readBinary'](memoryInitializer);
+ HEAPU8.set(data, STATIC_BASE);
+ } else {
+ addRunDependency('memory initializer');
+ Browser.asyncLoad(memoryInitializer, function(data) {
+ HEAPU8.set(data, STATIC_BASE);
+ removeRunDependency('memory initializer');
+ }, function(data) {
+ throw 'could not load memory initializer ' + memoryInitializer;
+ });
+ }
+}
+
+function ExitStatus(status) {
+ this.name = "ExitStatus";
+ this.message = "Program terminated with exit(" + status + ")";
+ this.status = status;
+};
+ExitStatus.prototype = new Error();
+ExitStatus.prototype.constructor = ExitStatus;
+
+var initialStackTop;
+var preloadStartTime = null;
+var calledMain = false;
+
+dependenciesFulfilled = function runCaller() {
+ // If run has never been called, and we should call run (INVOKE_RUN is true, and Module.noInitialRun is not false)
+ if (!Module['calledRun'] && shouldRunNow) run();
+ if (!Module['calledRun']) dependenciesFulfilled = runCaller; // try this again later, after new deps are fulfilled
+}
+
+Module['callMain'] = Module.callMain = function callMain(args) {
+ assert(runDependencies == 0, 'cannot call main when async dependencies remain! (listen on __ATMAIN__)');
+ assert(__ATPRERUN__.length == 0, 'cannot call main when preRun functions remain to be called');
+
+ args = args || [];
+
+ ensureInitRuntime();
+
+ var argc = args.length+1;
+ function pad() {
+ for (var i = 0; i < 4-1; i++) {
+ argv.push(0);
+ }
+ }
+ var argv = [allocate(intArrayFromString(Module['thisProgram']), 'i8', ALLOC_NORMAL) ];
+ pad();
+ for (var i = 0; i < argc-1; i = i + 1) {
+ argv.push(allocate(intArrayFromString(args[i]), 'i8', ALLOC_NORMAL));
+ pad();
+ }
+ argv.push(0);
+ argv = allocate(argv, 'i32', ALLOC_NORMAL);
+
+ initialStackTop = STACKTOP;
+
+ try {
+
+ var ret = Module['_main'](argc, argv, 0);
+
+
+ // if we're not running an evented main loop, it's time to exit
+ exit(ret);
+ }
+ catch(e) {
+ if (e instanceof ExitStatus) {
+ // exit() throws this once it's done to make sure execution
+ // has been stopped completely
+ return;
+ } else if (e == 'SimulateInfiniteLoop') {
+ // running an evented main loop, don't immediately exit
+ Module['noExitRuntime'] = true;
+ return;
+ } else {
+ if (e && typeof e === 'object' && e.stack) Module.printErr('exception thrown: ' + [e, e.stack]);
+ throw e;
+ }
+ } finally {
+ calledMain = true;
+ }
+}
+
+
+
+
+function run(args) {
+ args = args || Module['arguments'];
+
+ if (preloadStartTime === null) preloadStartTime = Date.now();
+
+ if (runDependencies > 0) {
+ return;
+ }
+
+ preRun();
+
+ if (runDependencies > 0) return; // a preRun added a dependency, run will be called later
+ if (Module['calledRun']) return; // run may have just been called through dependencies being fulfilled just in this very frame
+
+ function doRun() {
+ if (Module['calledRun']) return; // run may have just been called while the async setStatus time below was happening
+ Module['calledRun'] = true;
+
+ if (ABORT) return;
+
+ ensureInitRuntime();
+
+ preMain();
+
+ if (ENVIRONMENT_IS_WEB && preloadStartTime !== null) {
+ Module.printErr('pre-main prep time: ' + (Date.now() - preloadStartTime) + ' ms');
+ }
+
+ if (Module['onRuntimeInitialized']) Module['onRuntimeInitialized']();
+
+ if (Module['_main'] && shouldRunNow) Module['callMain'](args);
+
+ postRun();
+ }
+
+ if (Module['setStatus']) {
+ Module['setStatus']('Running...');
+ setTimeout(function() {
+ setTimeout(function() {
+ Module['setStatus']('');
+ }, 1);
+ doRun();
+ }, 1);
+ } else {
+ doRun();
+ }
+}
+Module['run'] = Module.run = run;
+
+function exit(status) {
+ if (Module['noExitRuntime']) {
+ return;
+ }
+
+ ABORT = true;
+ EXITSTATUS = status;
+ STACKTOP = initialStackTop;
+
+ // exit the runtime
+ exitRuntime();
+
+ if (ENVIRONMENT_IS_NODE) {
+ // Work around a node.js bug where stdout buffer is not flushed at process exit:
+ // Instead of process.exit() directly, wait for stdout flush event.
+ // See https://github.com/joyent/node/issues/1669 and https://github.com/kripken/emscripten/issues/2582
+ // Workaround is based on https://github.com/RReverser/acorn/commit/50ab143cecc9ed71a2d66f78b4aec3bb2e9844f6
+ process['stdout']['once']('drain', function () {
+ process['exit'](status);
+ });
+ console.log(' '); // Make sure to print something to force the drain event to occur, in case the stdout buffer was empty.
+ // Work around another node bug where sometimes 'drain' is never fired - make another effort
+ // to emit the exit status, after a significant delay (if node hasn't fired drain by then, give up)
+ setTimeout(function() {
+ process['exit'](status);
+ }, 500);
+ } else
+ if (ENVIRONMENT_IS_SHELL && typeof quit === 'function') {
+ quit(status);
+ }
+ // if we reach here, we must throw an exception to halt the current execution
+ throw new ExitStatus(status);
+}
+Module['exit'] = Module.exit = exit;
+
+function abort(text) {
+ if (text) {
+ Module.print(text);
+ Module.printErr(text);
+ }
+
+ ABORT = true;
+ EXITSTATUS = 1;
+
+ var extra = '\nIf this abort() is unexpected, build with -s ASSERTIONS=1 which can give more information.';
+
+ throw 'abort() at ' + stackTrace() + extra;
+}
+Module['abort'] = Module.abort = abort;
+
+// {{PRE_RUN_ADDITIONS}}
+
+if (Module['preInit']) {
+ if (typeof Module['preInit'] == 'function') Module['preInit'] = [Module['preInit']];
+ while (Module['preInit'].length > 0) {
+ Module['preInit'].pop()();
+ }
+}
+
+// shouldRunNow refers to calling main(), not run().
+var shouldRunNow = true;
+if (Module['noInitialRun']) {
+ shouldRunNow = false;
+}
+
+
+run();
+
+// {{POST_RUN_ADDITIONS}}
+
+
+
+
+
+
+// {{MODULE_ADDITIONS}}
+
+
+
+
+/* vim: ts=4:sw=4:expandtab */
+var Internal = Internal || {};
+
+(function() {
+ 'use strict';
+
+ // Insert some bytes into the emscripten memory and return a pointer
+ function _allocate(bytes) {
+ var address = Module._malloc(bytes.length);
+ Module.HEAPU8.set(bytes, address);
+
+ return address;
+ }
+
+ function _readBytes(address, length, array) {
+ array.set(Module.HEAPU8.subarray(address, address + length));
+ }
+
+ var basepoint = new Uint8Array(32);
+ basepoint[0] = 9;
+
+ Internal.curve25519 = {
+ keyPair: function(privKey) {
+ var priv = new Uint8Array(privKey);
+ priv[0] &= 248;
+ priv[31] &= 127;
+ priv[31] |= 64;
+
+ // Where to store the result
+ var publicKey_ptr = Module._malloc(32);
+
+ // Get a pointer to the private key
+ var privateKey_ptr = _allocate(priv);
+
+ // The basepoint for generating public keys
+ var basepoint_ptr = _allocate(basepoint);
+
+ // The return value is just 0, the operation is done in place
+ var err = Module._curve25519_donna(publicKey_ptr,
+ privateKey_ptr,
+ basepoint_ptr);
+
+ var res = new Uint8Array(32);
+ _readBytes(publicKey_ptr, 32, res);
+
+ Module._free(publicKey_ptr);
+ Module._free(privateKey_ptr);
+ Module._free(basepoint_ptr);
+
+ return { pubKey: res.buffer, privKey: priv.buffer };
+ },
+ sharedSecret: function(pubKey, privKey) {
+ // Where to store the result
+ var sharedKey_ptr = Module._malloc(32);
+
+ // Get a pointer to our private key
+ var privateKey_ptr = _allocate(new Uint8Array(privKey));
+
+ // Get a pointer to their public key, the basepoint when you're
+ // generating a shared secret
+ var basepoint_ptr = _allocate(new Uint8Array(pubKey));
+
+ // Return value is 0 here too of course
+ var err = Module._curve25519_donna(sharedKey_ptr,
+ privateKey_ptr,
+ basepoint_ptr);
+
+ var res = new Uint8Array(32);
+ _readBytes(sharedKey_ptr, 32, res);
+
+ Module._free(sharedKey_ptr);
+ Module._free(privateKey_ptr);
+ Module._free(basepoint_ptr);
+
+ return res.buffer;
+ },
+ sign: function(privKey, message) {
+ // Where to store the result
+ var signature_ptr = Module._malloc(64);
+
+ // Get a pointer to our private key
+ var privateKey_ptr = _allocate(new Uint8Array(privKey));
+
+ // Get a pointer to the message
+ var message_ptr = _allocate(new Uint8Array(message));
+
+ var err = Module._curve25519_sign(signature_ptr,
+ privateKey_ptr,
+ message_ptr,
+ message.byteLength);
+
+ var res = new Uint8Array(64);
+ _readBytes(signature_ptr, 64, res);
+
+ Module._free(signature_ptr);
+ Module._free(privateKey_ptr);
+ Module._free(message_ptr);
+
+ return res.buffer;
+ },
+ verify: function(pubKey, message, sig) {
+ // Get a pointer to their public key
+ var publicKey_ptr = _allocate(new Uint8Array(pubKey));
+
+ // Get a pointer to the signature
+ var signature_ptr = _allocate(new Uint8Array(sig));
+
+ // Get a pointer to the message
+ var message_ptr = _allocate(new Uint8Array(message));
+
+ var res = Module._curve25519_verify(signature_ptr,
+ publicKey_ptr,
+ message_ptr,
+ message.byteLength);
+
+ Module._free(publicKey_ptr);
+ Module._free(signature_ptr);
+ Module._free(message_ptr);
+
+ return res !== 0;
+ }
+ };
+
+ Internal.curve25519_async = {
+ keyPair: function(privKey) {
+ return new Promise(function(resolve) {
+ resolve(Internal.curve25519.keyPair(privKey));
+ });
+ },
+ sharedSecret: function(pubKey, privKey) {
+ return new Promise(function(resolve) {
+ resolve(Internal.curve25519.sharedSecret(pubKey, privKey));
+ });
+ },
+ sign: function(privKey, message) {
+ return new Promise(function(resolve) {
+ resolve(Internal.curve25519.sign(privKey, message));
+ });
+ },
+ verify: function(pubKey, message, sig) {
+ return new Promise(function(resolve, reject) {
+ if (Internal.curve25519.verify(pubKey, message, sig)) {
+ reject(new Error("Invalid signature"));
+ } else {
+ resolve();
+ }
+ });
+ },
+ };
+
+})();
+
+;(function() {
+
+'use strict';
+
+// I am the...workee?
+var origCurve25519 = Internal.curve25519_async;
+
+Internal.startWorker = function(url) {
+ Internal.stopWorker(); // there can be only one
+ Internal.curve25519_async = new Curve25519Worker(url);
+};
+
+Internal.stopWorker = function() {
+ if (Internal.curve25519_async instanceof Curve25519Worker) {
+ var worker = Internal.curve25519_async.worker;
+ Internal.curve25519_async = origCurve25519;
+ worker.terminate();
+ }
+};
+
+libsignal.worker = {
+ startWorker: Internal.startWorker,
+ stopWorker: Internal.stopWorker,
+};
+
+function Curve25519Worker(url) {
+ this.jobs = {};
+ this.jobId = 0;
+ this.worker = new Worker(url);
+ this.worker.onmessage = function(e) {
+ var job = this.jobs[e.data.id];
+ if (e.data.error && typeof job.onerror === 'function') {
+ job.onerror(new Error(e.data.error));
+ } else if (typeof job.onsuccess === 'function') {
+ job.onsuccess(e.data.result);
+ }
+ delete this.jobs[e.data.id];
+ }.bind(this);
+}
+
+Curve25519Worker.prototype = {
+ constructor: Curve25519Worker,
+ postMessage: function(methodName, args, onsuccess, onerror) {
+ return new Promise(function(resolve, reject) {
+ this.jobs[this.jobId] = { onsuccess: resolve, onerror: reject };
+ this.worker.postMessage({ id: this.jobId, methodName: methodName, args: args });
+ this.jobId++;
+ }.bind(this));
+ },
+ keyPair: function(privKey) {
+ return this.postMessage('keyPair', [privKey]);
+ },
+ sharedSecret: function(pubKey, privKey) {
+ return this.postMessage('sharedSecret', [pubKey, privKey]);
+ },
+ sign: function(privKey, message) {
+ return this.postMessage('sign', [privKey, message]);
+ },
+ verify: function(pubKey, message, sig) {
+ return this.postMessage('verify', [pubKey, message, sig]);
+ }
+};
+
+})();
+
+/*
+ Copyright 2013 Daniel Wirtz
+ Copyright 2009 The Closure Library Authors. All Rights Reserved.
+
+ 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 long.js (c) 2013 Daniel Wirtz
+ * Released under the Apache License, Version 2.0
+ * see: https://github.com/dcodeIO/long.js for details
+ */
+(function(global, factory) {
+
+ /* AMD */ if (typeof define === 'function' && define["amd"])
+ define([], factory);
+ /* CommonJS */ else if (typeof require === 'function' && typeof module === "object" && module && module["exports"])
+ module["exports"] = factory();
+ /* Global */ else
+ (global["dcodeIO"] = global["dcodeIO"] || {})["Long"] = factory();
+
+})(this, function() {
+ "use strict";
+
+ /**
+ * Constructs a 64 bit two's-complement integer, given its low and high 32 bit values as *signed* integers.
+ * See the from* functions below for more convenient ways of constructing Longs.
+ * @exports Long
+ * @class A Long class for representing a 64 bit two's-complement integer value.
+ * @param {number} low The low (signed) 32 bits of the long
+ * @param {number} high The high (signed) 32 bits of the long
+ * @param {boolean=} unsigned Whether unsigned or not, defaults to `false` for signed
+ * @constructor
+ */
+ function Long(low, high, unsigned) {
+
+ /**
+ * The low 32 bits as a signed value.
+ * @type {number}
+ */
+ this.low = low | 0;
+
+ /**
+ * The high 32 bits as a signed value.
+ * @type {number}
+ */
+ this.high = high | 0;
+
+ /**
+ * Whether unsigned or not.
+ * @type {boolean}
+ */
+ this.unsigned = !!unsigned;
+ }
+
+ // The internal representation of a long is the two given signed, 32-bit values.
+ // We use 32-bit pieces because these are the size of integers on which
+ // Javascript performs bit-operations. For operations like addition and
+ // multiplication, we split each number into 16 bit pieces, which can easily be
+ // multiplied within Javascript's floating-point representation without overflow
+ // or change in sign.
+ //
+ // In the algorithms below, we frequently reduce the negative case to the
+ // positive case by negating the input(s) and then post-processing the result.
+ // Note that we must ALWAYS check specially whether those values are MIN_VALUE
+ // (-2^63) because -MIN_VALUE == MIN_VALUE (since 2^63 cannot be represented as
+ // a positive number, it overflows back into a negative). Not handling this
+ // case would often result in infinite recursion.
+ //
+ // Common constant values ZERO, ONE, NEG_ONE, etc. are defined below the from*
+ // methods on which they depend.
+
+ /**
+ * An indicator used to reliably determine if an object is a Long or not.
+ * @type {boolean}
+ * @const
+ * @private
+ */
+ Long.prototype.__isLong__;
+
+ Object.defineProperty(Long.prototype, "__isLong__", {
+ value: true,
+ enumerable: false,
+ configurable: false
+ });
+
+ /**
+ * @function
+ * @param {*} obj Object
+ * @returns {boolean}
+ * @inner
+ */
+ function isLong(obj) {
+ return (obj && obj["__isLong__"]) === true;
+ }
+
+ /**
+ * Tests if the specified object is a Long.
+ * @function
+ * @param {*} obj Object
+ * @returns {boolean}
+ */
+ Long.isLong = isLong;
+
+ /**
+ * A cache of the Long representations of small integer values.
+ * @type {!Object}
+ * @inner
+ */
+ var INT_CACHE = {};
+
+ /**
+ * A cache of the Long representations of small unsigned integer values.
+ * @type {!Object}
+ * @inner
+ */
+ var UINT_CACHE = {};
+
+ /**
+ * @param {number} value
+ * @param {boolean=} unsigned
+ * @returns {!Long}
+ * @inner
+ */
+ function fromInt(value, unsigned) {
+ var obj, cachedObj, cache;
+ if (unsigned) {
+ value >>>= 0;
+ if (cache = (0 <= value && value < 256)) {
+ cachedObj = UINT_CACHE[value];
+ if (cachedObj)
+ return cachedObj;
+ }
+ obj = fromBits(value, (value | 0) < 0 ? -1 : 0, true);
+ if (cache)
+ UINT_CACHE[value] = obj;
+ return obj;
+ } else {
+ value |= 0;
+ if (cache = (-128 <= value && value < 128)) {
+ cachedObj = INT_CACHE[value];
+ if (cachedObj)
+ return cachedObj;
+ }
+ obj = fromBits(value, value < 0 ? -1 : 0, false);
+ if (cache)
+ INT_CACHE[value] = obj;
+ return obj;
+ }
+ }
+
+ /**
+ * Returns a Long representing the given 32 bit integer value.
+ * @function
+ * @param {number} value The 32 bit integer in question
+ * @param {boolean=} unsigned Whether unsigned or not, defaults to `false` for signed
+ * @returns {!Long} The corresponding Long value
+ */
+ Long.fromInt = fromInt;
+
+ /**
+ * @param {number} value
+ * @param {boolean=} unsigned
+ * @returns {!Long}
+ * @inner
+ */
+ function fromNumber(value, unsigned) {
+ if (isNaN(value) || !isFinite(value))
+ return unsigned ? UZERO : ZERO;
+ if (unsigned) {
+ if (value < 0)
+ return UZERO;
+ if (value >= TWO_PWR_64_DBL)
+ return MAX_UNSIGNED_VALUE;
+ } else {
+ if (value <= -TWO_PWR_63_DBL)
+ return MIN_VALUE;
+ if (value + 1 >= TWO_PWR_63_DBL)
+ return MAX_VALUE;
+ }
+ if (value < 0)
+ return fromNumber(-value, unsigned).neg();
+ return fromBits((value % TWO_PWR_32_DBL) | 0, (value / TWO_PWR_32_DBL) | 0, unsigned);
+ }
+
+ /**
+ * Returns a Long representing the given value, provided that it is a finite number. Otherwise, zero is returned.
+ * @function
+ * @param {number} value The number in question
+ * @param {boolean=} unsigned Whether unsigned or not, defaults to `false` for signed
+ * @returns {!Long} The corresponding Long value
+ */
+ Long.fromNumber = fromNumber;
+
+ /**
+ * @param {number} lowBits
+ * @param {number} highBits
+ * @param {boolean=} unsigned
+ * @returns {!Long}
+ * @inner
+ */
+ function fromBits(lowBits, highBits, unsigned) {
+ return new Long(lowBits, highBits, unsigned);
+ }
+
+ /**
+ * Returns a Long representing the 64 bit integer that comes by concatenating the given low and high bits. Each is
+ * assumed to use 32 bits.
+ * @function
+ * @param {number} lowBits The low 32 bits
+ * @param {number} highBits The high 32 bits
+ * @param {boolean=} unsigned Whether unsigned or not, defaults to `false` for signed
+ * @returns {!Long} The corresponding Long value
+ */
+ Long.fromBits = fromBits;
+
+ /**
+ * @function
+ * @param {number} base
+ * @param {number} exponent
+ * @returns {number}
+ * @inner
+ */
+ var pow_dbl = Math.pow; // Used 4 times (4*8 to 15+4)
+
+ /**
+ * @param {string} str
+ * @param {(boolean|number)=} unsigned
+ * @param {number=} radix
+ * @returns {!Long}
+ * @inner
+ */
+ function fromString(str, unsigned, radix) {
+ if (str.length === 0)
+ throw Error('empty string');
+ if (str === "NaN" || str === "Infinity" || str === "+Infinity" || str === "-Infinity")
+ return ZERO;
+ if (typeof unsigned === 'number') {
+ // For goog.math.long compatibility
+ radix = unsigned,
+ unsigned = false;
+ } else {
+ unsigned = !! unsigned;
+ }
+ radix = radix || 10;
+ if (radix < 2 || 36 < radix)
+ throw RangeError('radix');
+
+ var p;
+ if ((p = str.indexOf('-')) > 0)
+ throw Error('interior hyphen');
+ else if (p === 0) {
+ return fromString(str.substring(1), unsigned, radix).neg();
+ }
+
+ // Do several (8) digits each time through the loop, so as to
+ // minimize the calls to the very expensive emulated div.
+ var radixToPower = fromNumber(pow_dbl(radix, 8));
+
+ var result = ZERO;
+ for (var i = 0; i < str.length; i += 8) {
+ var size = Math.min(8, str.length - i),
+ value = parseInt(str.substring(i, i + size), radix);
+ if (size < 8) {
+ var power = fromNumber(pow_dbl(radix, size));
+ result = result.mul(power).add(fromNumber(value));
+ } else {
+ result = result.mul(radixToPower);
+ result = result.add(fromNumber(value));
+ }
+ }
+ result.unsigned = unsigned;
+ return result;
+ }
+
+ /**
+ * Returns a Long representation of the given string, written using the specified radix.
+ * @function
+ * @param {string} str The textual representation of the Long
+ * @param {(boolean|number)=} unsigned Whether unsigned or not, defaults to `false` for signed
+ * @param {number=} radix The radix in which the text is written (2-36), defaults to 10
+ * @returns {!Long} The corresponding Long value
+ */
+ Long.fromString = fromString;
+
+ /**
+ * @function
+ * @param {!Long|number|string|!{low: number, high: number, unsigned: boolean}} val
+ * @returns {!Long}
+ * @inner
+ */
+ function fromValue(val) {
+ if (val /* is compatible */ instanceof Long)
+ return val;
+ if (typeof val === 'number')
+ return fromNumber(val);
+ if (typeof val === 'string')
+ return fromString(val);
+ // Throws for non-objects, converts non-instanceof Long:
+ return fromBits(val.low, val.high, val.unsigned);
+ }
+
+ /**
+ * Converts the specified value to a Long.
+ * @function
+ * @param {!Long|number|string|!{low: number, high: number, unsigned: boolean}} val Value
+ * @returns {!Long}
+ */
+ Long.fromValue = fromValue;
+
+ // NOTE: the compiler should inline these constant values below and then remove these variables, so there should be
+ // no runtime penalty for these.
+
+ /**
+ * @type {number}
+ * @const
+ * @inner
+ */
+ var TWO_PWR_16_DBL = 1 << 16;
+
+ /**
+ * @type {number}
+ * @const
+ * @inner
+ */
+ var TWO_PWR_24_DBL = 1 << 24;
+
+ /**
+ * @type {number}
+ * @const
+ * @inner
+ */
+ var TWO_PWR_32_DBL = TWO_PWR_16_DBL * TWO_PWR_16_DBL;
+
+ /**
+ * @type {number}
+ * @const
+ * @inner
+ */
+ var TWO_PWR_64_DBL = TWO_PWR_32_DBL * TWO_PWR_32_DBL;
+
+ /**
+ * @type {number}
+ * @const
+ * @inner
+ */
+ var TWO_PWR_63_DBL = TWO_PWR_64_DBL / 2;
+
+ /**
+ * @type {!Long}
+ * @const
+ * @inner
+ */
+ var TWO_PWR_24 = fromInt(TWO_PWR_24_DBL);
+
+ /**
+ * @type {!Long}
+ * @inner
+ */
+ var ZERO = fromInt(0);
+
+ /**
+ * Signed zero.
+ * @type {!Long}
+ */
+ Long.ZERO = ZERO;
+
+ /**
+ * @type {!Long}
+ * @inner
+ */
+ var UZERO = fromInt(0, true);
+
+ /**
+ * Unsigned zero.
+ * @type {!Long}
+ */
+ Long.UZERO = UZERO;
+
+ /**
+ * @type {!Long}
+ * @inner
+ */
+ var ONE = fromInt(1);
+
+ /**
+ * Signed one.
+ * @type {!Long}
+ */
+ Long.ONE = ONE;
+
+ /**
+ * @type {!Long}
+ * @inner
+ */
+ var UONE = fromInt(1, true);
+
+ /**
+ * Unsigned one.
+ * @type {!Long}
+ */
+ Long.UONE = UONE;
+
+ /**
+ * @type {!Long}
+ * @inner
+ */
+ var NEG_ONE = fromInt(-1);
+
+ /**
+ * Signed negative one.
+ * @type {!Long}
+ */
+ Long.NEG_ONE = NEG_ONE;
+
+ /**
+ * @type {!Long}
+ * @inner
+ */
+ var MAX_VALUE = fromBits(0xFFFFFFFF|0, 0x7FFFFFFF|0, false);
+
+ /**
+ * Maximum signed value.
+ * @type {!Long}
+ */
+ Long.MAX_VALUE = MAX_VALUE;
+
+ /**
+ * @type {!Long}
+ * @inner
+ */
+ var MAX_UNSIGNED_VALUE = fromBits(0xFFFFFFFF|0, 0xFFFFFFFF|0, true);
+
+ /**
+ * Maximum unsigned value.
+ * @type {!Long}
+ */
+ Long.MAX_UNSIGNED_VALUE = MAX_UNSIGNED_VALUE;
+
+ /**
+ * @type {!Long}
+ * @inner
+ */
+ var MIN_VALUE = fromBits(0, 0x80000000|0, false);
+
+ /**
+ * Minimum signed value.
+ * @type {!Long}
+ */
+ Long.MIN_VALUE = MIN_VALUE;
+
+ /**
+ * @alias Long.prototype
+ * @inner
+ */
+ var LongPrototype = Long.prototype;
+
+ /**
+ * Converts the Long to a 32 bit integer, assuming it is a 32 bit integer.
+ * @returns {number}
+ */
+ LongPrototype.toInt = function toInt() {
+ return this.unsigned ? this.low >>> 0 : this.low;
+ };
+
+ /**
+ * Converts the Long to a the nearest floating-point representation of this value (double, 53 bit mantissa).
+ * @returns {number}
+ */
+ LongPrototype.toNumber = function toNumber() {
+ if (this.unsigned)
+ return ((this.high >>> 0) * TWO_PWR_32_DBL) + (this.low >>> 0);
+ return this.high * TWO_PWR_32_DBL + (this.low >>> 0);
+ };
+
+ /**
+ * Converts the Long to a string written in the specified radix.
+ * @param {number=} radix Radix (2-36), defaults to 10
+ * @returns {string}
+ * @override
+ * @throws {RangeError} If `radix` is out of range
+ */
+ LongPrototype.toString = function toString(radix) {
+ radix = radix || 10;
+ if (radix < 2 || 36 < radix)
+ throw RangeError('radix');
+ if (this.isZero())
+ return '0';
+ if (this.isNegative()) { // Unsigned Longs are never negative
+ if (this.eq(MIN_VALUE)) {
+ // We need to change the Long value before it can be negated, so we remove
+ // the bottom-most digit in this base and then recurse to do the rest.
+ var radixLong = fromNumber(radix),
+ div = this.div(radixLong),
+ rem1 = div.mul(radixLong).sub(this);
+ return div.toString(radix) + rem1.toInt().toString(radix);
+ } else
+ return '-' + this.neg().toString(radix);
+ }
+
+ // Do several (6) digits each time through the loop, so as to
+ // minimize the calls to the very expensive emulated div.
+ var radixToPower = fromNumber(pow_dbl(radix, 6), this.unsigned),
+ rem = this;
+ var result = '';
+ while (true) {
+ var remDiv = rem.div(radixToPower),
+ intval = rem.sub(remDiv.mul(radixToPower)).toInt() >>> 0,
+ digits = intval.toString(radix);
+ rem = remDiv;
+ if (rem.isZero())
+ return digits + result;
+ else {
+ while (digits.length < 6)
+ digits = '0' + digits;
+ result = '' + digits + result;
+ }
+ }
+ };
+
+ /**
+ * Gets the high 32 bits as a signed integer.
+ * @returns {number} Signed high bits
+ */
+ LongPrototype.getHighBits = function getHighBits() {
+ return this.high;
+ };
+
+ /**
+ * Gets the high 32 bits as an unsigned integer.
+ * @returns {number} Unsigned high bits
+ */
+ LongPrototype.getHighBitsUnsigned = function getHighBitsUnsigned() {
+ return this.high >>> 0;
+ };
+
+ /**
+ * Gets the low 32 bits as a signed integer.
+ * @returns {number} Signed low bits
+ */
+ LongPrototype.getLowBits = function getLowBits() {
+ return this.low;
+ };
+
+ /**
+ * Gets the low 32 bits as an unsigned integer.
+ * @returns {number} Unsigned low bits
+ */
+ LongPrototype.getLowBitsUnsigned = function getLowBitsUnsigned() {
+ return this.low >>> 0;
+ };
+
+ /**
+ * Gets the number of bits needed to represent the absolute value of this Long.
+ * @returns {number}
+ */
+ LongPrototype.getNumBitsAbs = function getNumBitsAbs() {
+ if (this.isNegative()) // Unsigned Longs are never negative
+ return this.eq(MIN_VALUE) ? 64 : this.neg().getNumBitsAbs();
+ var val = this.high != 0 ? this.high : this.low;
+ for (var bit = 31; bit > 0; bit--)
+ if ((val & (1 << bit)) != 0)
+ break;
+ return this.high != 0 ? bit + 33 : bit + 1;
+ };
+
+ /**
+ * Tests if this Long's value equals zero.
+ * @returns {boolean}
+ */
+ LongPrototype.isZero = function isZero() {
+ return this.high === 0 && this.low === 0;
+ };
+
+ /**
+ * Tests if this Long's value is negative.
+ * @returns {boolean}
+ */
+ LongPrototype.isNegative = function isNegative() {
+ return !this.unsigned && this.high < 0;
+ };
+
+ /**
+ * Tests if this Long's value is positive.
+ * @returns {boolean}
+ */
+ LongPrototype.isPositive = function isPositive() {
+ return this.unsigned || this.high >= 0;
+ };
+
+ /**
+ * Tests if this Long's value is odd.
+ * @returns {boolean}
+ */
+ LongPrototype.isOdd = function isOdd() {
+ return (this.low & 1) === 1;
+ };
+
+ /**
+ * Tests if this Long's value is even.
+ * @returns {boolean}
+ */
+ LongPrototype.isEven = function isEven() {
+ return (this.low & 1) === 0;
+ };
+
+ /**
+ * Tests if this Long's value equals the specified's.
+ * @param {!Long|number|string} other Other value
+ * @returns {boolean}
+ */
+ LongPrototype.equals = function equals(other) {
+ if (!isLong(other))
+ other = fromValue(other);
+ if (this.unsigned !== other.unsigned && (this.high >>> 31) === 1 && (other.high >>> 31) === 1)
+ return false;
+ return this.high === other.high && this.low === other.low;
+ };
+
+ /**
+ * Tests if this Long's value equals the specified's. This is an alias of {@link Long#equals}.
+ * @function
+ * @param {!Long|number|string} other Other value
+ * @returns {boolean}
+ */
+ LongPrototype.eq = LongPrototype.equals;
+
+ /**
+ * Tests if this Long's value differs from the specified's.
+ * @param {!Long|number|string} other Other value
+ * @returns {boolean}
+ */
+ LongPrototype.notEquals = function notEquals(other) {
+ return !this.eq(/* validates */ other);
+ };
+
+ /**
+ * Tests if this Long's value differs from the specified's. This is an alias of {@link Long#notEquals}.
+ * @function
+ * @param {!Long|number|string} other Other value
+ * @returns {boolean}
+ */
+ LongPrototype.neq = LongPrototype.notEquals;
+
+ /**
+ * Tests if this Long's value is less than the specified's.
+ * @param {!Long|number|string} other Other value
+ * @returns {boolean}
+ */
+ LongPrototype.lessThan = function lessThan(other) {
+ return this.comp(/* validates */ other) < 0;
+ };
+
+ /**
+ * Tests if this Long's value is less than the specified's. This is an alias of {@link Long#lessThan}.
+ * @function
+ * @param {!Long|number|string} other Other value
+ * @returns {boolean}
+ */
+ LongPrototype.lt = LongPrototype.lessThan;
+
+ /**
+ * Tests if this Long's value is less than or equal the specified's.
+ * @param {!Long|number|string} other Other value
+ * @returns {boolean}
+ */
+ LongPrototype.lessThanOrEqual = function lessThanOrEqual(other) {
+ return this.comp(/* validates */ other) <= 0;
+ };
+
+ /**
+ * Tests if this Long's value is less than or equal the specified's. This is an alias of {@link Long#lessThanOrEqual}.
+ * @function
+ * @param {!Long|number|string} other Other value
+ * @returns {boolean}
+ */
+ LongPrototype.lte = LongPrototype.lessThanOrEqual;
+
+ /**
+ * Tests if this Long's value is greater than the specified's.
+ * @param {!Long|number|string} other Other value
+ * @returns {boolean}
+ */
+ LongPrototype.greaterThan = function greaterThan(other) {
+ return this.comp(/* validates */ other) > 0;
+ };
+
+ /**
+ * Tests if this Long's value is greater than the specified's. This is an alias of {@link Long#greaterThan}.
+ * @function
+ * @param {!Long|number|string} other Other value
+ * @returns {boolean}
+ */
+ LongPrototype.gt = LongPrototype.greaterThan;
+
+ /**
+ * Tests if this Long's value is greater than or equal the specified's.
+ * @param {!Long|number|string} other Other value
+ * @returns {boolean}
+ */
+ LongPrototype.greaterThanOrEqual = function greaterThanOrEqual(other) {
+ return this.comp(/* validates */ other) >= 0;
+ };
+
+ /**
+ * Tests if this Long's value is greater than or equal the specified's. This is an alias of {@link Long#greaterThanOrEqual}.
+ * @function
+ * @param {!Long|number|string} other Other value
+ * @returns {boolean}
+ */
+ LongPrototype.gte = LongPrototype.greaterThanOrEqual;
+
+ /**
+ * Compares this Long's value with the specified's.
+ * @param {!Long|number|string} other Other value
+ * @returns {number} 0 if they are the same, 1 if the this is greater and -1
+ * if the given one is greater
+ */
+ LongPrototype.compare = function compare(other) {
+ if (!isLong(other))
+ other = fromValue(other);
+ if (this.eq(other))
+ return 0;
+ var thisNeg = this.isNegative(),
+ otherNeg = other.isNegative();
+ if (thisNeg && !otherNeg)
+ return -1;
+ if (!thisNeg && otherNeg)
+ return 1;
+ // At this point the sign bits are the same
+ if (!this.unsigned)
+ return this.sub(other).isNegative() ? -1 : 1;
+ // Both are positive if at least one is unsigned
+ return (other.high >>> 0) > (this.high >>> 0) || (other.high === this.high && (other.low >>> 0) > (this.low >>> 0)) ? -1 : 1;
+ };
+
+ /**
+ * Compares this Long's value with the specified's. This is an alias of {@link Long#compare}.
+ * @function
+ * @param {!Long|number|string} other Other value
+ * @returns {number} 0 if they are the same, 1 if the this is greater and -1
+ * if the given one is greater
+ */
+ LongPrototype.comp = LongPrototype.compare;
+
+ /**
+ * Negates this Long's value.
+ * @returns {!Long} Negated Long
+ */
+ LongPrototype.negate = function negate() {
+ if (!this.unsigned && this.eq(MIN_VALUE))
+ return MIN_VALUE;
+ return this.not().add(ONE);
+ };
+
+ /**
+ * Negates this Long's value. This is an alias of {@link Long#negate}.
+ * @function
+ * @returns {!Long} Negated Long
+ */
+ LongPrototype.neg = LongPrototype.negate;
+
+ /**
+ * Returns the sum of this and the specified Long.
+ * @param {!Long|number|string} addend Addend
+ * @returns {!Long} Sum
+ */
+ LongPrototype.add = function add(addend) {
+ if (!isLong(addend))
+ addend = fromValue(addend);
+
+ // Divide each number into 4 chunks of 16 bits, and then sum the chunks.
+
+ var a48 = this.high >>> 16;
+ var a32 = this.high & 0xFFFF;
+ var a16 = this.low >>> 16;
+ var a00 = this.low & 0xFFFF;
+
+ var b48 = addend.high >>> 16;
+ var b32 = addend.high & 0xFFFF;
+ var b16 = addend.low >>> 16;
+ var b00 = addend.low & 0xFFFF;
+
+ var c48 = 0, c32 = 0, c16 = 0, c00 = 0;
+ c00 += a00 + b00;
+ c16 += c00 >>> 16;
+ c00 &= 0xFFFF;
+ c16 += a16 + b16;
+ c32 += c16 >>> 16;
+ c16 &= 0xFFFF;
+ c32 += a32 + b32;
+ c48 += c32 >>> 16;
+ c32 &= 0xFFFF;
+ c48 += a48 + b48;
+ c48 &= 0xFFFF;
+ return fromBits((c16 << 16) | c00, (c48 << 16) | c32, this.unsigned);
+ };
+
+ /**
+ * Returns the difference of this and the specified Long.
+ * @param {!Long|number|string} subtrahend Subtrahend
+ * @returns {!Long} Difference
+ */
+ LongPrototype.subtract = function subtract(subtrahend) {
+ if (!isLong(subtrahend))
+ subtrahend = fromValue(subtrahend);
+ return this.add(subtrahend.neg());
+ };
+
+ /**
+ * Returns the difference of this and the specified Long. This is an alias of {@link Long#subtract}.
+ * @function
+ * @param {!Long|number|string} subtrahend Subtrahend
+ * @returns {!Long} Difference
+ */
+ LongPrototype.sub = LongPrototype.subtract;
+
+ /**
+ * Returns the product of this and the specified Long.
+ * @param {!Long|number|string} multiplier Multiplier
+ * @returns {!Long} Product
+ */
+ LongPrototype.multiply = function multiply(multiplier) {
+ if (this.isZero())
+ return ZERO;
+ if (!isLong(multiplier))
+ multiplier = fromValue(multiplier);
+ if (multiplier.isZero())
+ return ZERO;
+ if (this.eq(MIN_VALUE))
+ return multiplier.isOdd() ? MIN_VALUE : ZERO;
+ if (multiplier.eq(MIN_VALUE))
+ return this.isOdd() ? MIN_VALUE : ZERO;
+
+ if (this.isNegative()) {
+ if (multiplier.isNegative())
+ return this.neg().mul(multiplier.neg());
+ else
+ return this.neg().mul(multiplier).neg();
+ } else if (multiplier.isNegative())
+ return this.mul(multiplier.neg()).neg();
+
+ // If both longs are small, use float multiplication
+ if (this.lt(TWO_PWR_24) && multiplier.lt(TWO_PWR_24))
+ return fromNumber(this.toNumber() * multiplier.toNumber(), this.unsigned);
+
+ // Divide each long into 4 chunks of 16 bits, and then add up 4x4 products.
+ // We can skip products that would overflow.
+
+ var a48 = this.high >>> 16;
+ var a32 = this.high & 0xFFFF;
+ var a16 = this.low >>> 16;
+ var a00 = this.low & 0xFFFF;
+
+ var b48 = multiplier.high >>> 16;
+ var b32 = multiplier.high & 0xFFFF;
+ var b16 = multiplier.low >>> 16;
+ var b00 = multiplier.low & 0xFFFF;
+
+ var c48 = 0, c32 = 0, c16 = 0, c00 = 0;
+ c00 += a00 * b00;
+ c16 += c00 >>> 16;
+ c00 &= 0xFFFF;
+ c16 += a16 * b00;
+ c32 += c16 >>> 16;
+ c16 &= 0xFFFF;
+ c16 += a00 * b16;
+ c32 += c16 >>> 16;
+ c16 &= 0xFFFF;
+ c32 += a32 * b00;
+ c48 += c32 >>> 16;
+ c32 &= 0xFFFF;
+ c32 += a16 * b16;
+ c48 += c32 >>> 16;
+ c32 &= 0xFFFF;
+ c32 += a00 * b32;
+ c48 += c32 >>> 16;
+ c32 &= 0xFFFF;
+ c48 += a48 * b00 + a32 * b16 + a16 * b32 + a00 * b48;
+ c48 &= 0xFFFF;
+ return fromBits((c16 << 16) | c00, (c48 << 16) | c32, this.unsigned);
+ };
+
+ /**
+ * Returns the product of this and the specified Long. This is an alias of {@link Long#multiply}.
+ * @function
+ * @param {!Long|number|string} multiplier Multiplier
+ * @returns {!Long} Product
+ */
+ LongPrototype.mul = LongPrototype.multiply;
+
+ /**
+ * Returns this Long divided by the specified. The result is signed if this Long is signed or
+ * unsigned if this Long is unsigned.
+ * @param {!Long|number|string} divisor Divisor
+ * @returns {!Long} Quotient
+ */
+ LongPrototype.divide = function divide(divisor) {
+ if (!isLong(divisor))
+ divisor = fromValue(divisor);
+ if (divisor.isZero())
+ throw Error('division by zero');
+ if (this.isZero())
+ return this.unsigned ? UZERO : ZERO;
+ var approx, rem, res;
+ if (!this.unsigned) {
+ // This section is only relevant for signed longs and is derived from the
+ // closure library as a whole.
+ if (this.eq(MIN_VALUE)) {
+ if (divisor.eq(ONE) || divisor.eq(NEG_ONE))
+ return MIN_VALUE; // recall that -MIN_VALUE == MIN_VALUE
+ else if (divisor.eq(MIN_VALUE))
+ return ONE;
+ else {
+ // At this point, we have |other| >= 2, so |this/other| < |MIN_VALUE|.
+ var halfThis = this.shr(1);
+ approx = halfThis.div(divisor).shl(1);
+ if (approx.eq(ZERO)) {
+ return divisor.isNegative() ? ONE : NEG_ONE;
+ } else {
+ rem = this.sub(divisor.mul(approx));
+ res = approx.add(rem.div(divisor));
+ return res;
+ }
+ }
+ } else if (divisor.eq(MIN_VALUE))
+ return this.unsigned ? UZERO : ZERO;
+ if (this.isNegative()) {
+ if (divisor.isNegative())
+ return this.neg().div(divisor.neg());
+ return this.neg().div(divisor).neg();
+ } else if (divisor.isNegative())
+ return this.div(divisor.neg()).neg();
+ res = ZERO;
+ } else {
+ // The algorithm below has not been made for unsigned longs. It's therefore
+ // required to take special care of the MSB prior to running it.
+ if (!divisor.unsigned)
+ divisor = divisor.toUnsigned();
+ if (divisor.gt(this))
+ return UZERO;
+ if (divisor.gt(this.shru(1))) // 15 >>> 1 = 7 ; with divisor = 8 ; true
+ return UONE;
+ res = UZERO;
+ }
+
+ // Repeat the following until the remainder is less than other: find a
+ // floating-point that approximates remainder / other *from below*, add this
+ // into the result, and subtract it from the remainder. It is critical that
+ // the approximate value is less than or equal to the real value so that the
+ // remainder never becomes negative.
+ rem = this;
+ while (rem.gte(divisor)) {
+ // Approximate the result of division. This may be a little greater or
+ // smaller than the actual value.
+ approx = Math.max(1, Math.floor(rem.toNumber() / divisor.toNumber()));
+
+ // We will tweak the approximate result by changing it in the 48-th digit or
+ // the smallest non-fractional digit, whichever is larger.
+ var log2 = Math.ceil(Math.log(approx) / Math.LN2),
+ delta = (log2 <= 48) ? 1 : pow_dbl(2, log2 - 48),
+
+ // Decrease the approximation until it is smaller than the remainder. Note
+ // that if it is too large, the product overflows and is negative.
+ approxRes = fromNumber(approx),
+ approxRem = approxRes.mul(divisor);
+ while (approxRem.isNegative() || approxRem.gt(rem)) {
+ approx -= delta;
+ approxRes = fromNumber(approx, this.unsigned);
+ approxRem = approxRes.mul(divisor);
+ }
+
+ // We know the answer can't be zero... and actually, zero would cause
+ // infinite recursion since we would make no progress.
+ if (approxRes.isZero())
+ approxRes = ONE;
+
+ res = res.add(approxRes);
+ rem = rem.sub(approxRem);
+ }
+ return res;
+ };
+
+ /**
+ * Returns this Long divided by the specified. This is an alias of {@link Long#divide}.
+ * @function
+ * @param {!Long|number|string} divisor Divisor
+ * @returns {!Long} Quotient
+ */
+ LongPrototype.div = LongPrototype.divide;
+
+ /**
+ * Returns this Long modulo the specified.
+ * @param {!Long|number|string} divisor Divisor
+ * @returns {!Long} Remainder
+ */
+ LongPrototype.modulo = function modulo(divisor) {
+ if (!isLong(divisor))
+ divisor = fromValue(divisor);
+ return this.sub(this.div(divisor).mul(divisor));
+ };
+
+ /**
+ * Returns this Long modulo the specified. This is an alias of {@link Long#modulo}.
+ * @function
+ * @param {!Long|number|string} divisor Divisor
+ * @returns {!Long} Remainder
+ */
+ LongPrototype.mod = LongPrototype.modulo;
+
+ /**
+ * Returns the bitwise NOT of this Long.
+ * @returns {!Long}
+ */
+ LongPrototype.not = function not() {
+ return fromBits(~this.low, ~this.high, this.unsigned);
+ };
+
+ /**
+ * Returns the bitwise AND of this Long and the specified.
+ * @param {!Long|number|string} other Other Long
+ * @returns {!Long}
+ */
+ LongPrototype.and = function and(other) {
+ if (!isLong(other))
+ other = fromValue(other);
+ return fromBits(this.low & other.low, this.high & other.high, this.unsigned);
+ };
+
+ /**
+ * Returns the bitwise OR of this Long and the specified.
+ * @param {!Long|number|string} other Other Long
+ * @returns {!Long}
+ */
+ LongPrototype.or = function or(other) {
+ if (!isLong(other))
+ other = fromValue(other);
+ return fromBits(this.low | other.low, this.high | other.high, this.unsigned);
+ };
+
+ /**
+ * Returns the bitwise XOR of this Long and the given one.
+ * @param {!Long|number|string} other Other Long
+ * @returns {!Long}
+ */
+ LongPrototype.xor = function xor(other) {
+ if (!isLong(other))
+ other = fromValue(other);
+ return fromBits(this.low ^ other.low, this.high ^ other.high, this.unsigned);
+ };
+
+ /**
+ * Returns this Long with bits shifted to the left by the given amount.
+ * @param {number|!Long} numBits Number of bits
+ * @returns {!Long} Shifted Long
+ */
+ LongPrototype.shiftLeft = function shiftLeft(numBits) {
+ if (isLong(numBits))
+ numBits = numBits.toInt();
+ if ((numBits &= 63) === 0)
+ return this;
+ else if (numBits < 32)
+ return fromBits(this.low << numBits, (this.high << numBits) | (this.low >>> (32 - numBits)), this.unsigned);
+ else
+ return fromBits(0, this.low << (numBits - 32), this.unsigned);
+ };
+
+ /**
+ * Returns this Long with bits shifted to the left by the given amount. This is an alias of {@link Long#shiftLeft}.
+ * @function
+ * @param {number|!Long} numBits Number of bits
+ * @returns {!Long} Shifted Long
+ */
+ LongPrototype.shl = LongPrototype.shiftLeft;
+
+ /**
+ * Returns this Long with bits arithmetically shifted to the right by the given amount.
+ * @param {number|!Long} numBits Number of bits
+ * @returns {!Long} Shifted Long
+ */
+ LongPrototype.shiftRight = function shiftRight(numBits) {
+ if (isLong(numBits))
+ numBits = numBits.toInt();
+ if ((numBits &= 63) === 0)
+ return this;
+ else if (numBits < 32)
+ return fromBits((this.low >>> numBits) | (this.high << (32 - numBits)), this.high >> numBits, this.unsigned);
+ else
+ return fromBits(this.high >> (numBits - 32), this.high >= 0 ? 0 : -1, this.unsigned);
+ };
+
+ /**
+ * Returns this Long with bits arithmetically shifted to the right by the given amount. This is an alias of {@link Long#shiftRight}.
+ * @function
+ * @param {number|!Long} numBits Number of bits
+ * @returns {!Long} Shifted Long
+ */
+ LongPrototype.shr = LongPrototype.shiftRight;
+
+ /**
+ * Returns this Long with bits logically shifted to the right by the given amount.
+ * @param {number|!Long} numBits Number of bits
+ * @returns {!Long} Shifted Long
+ */
+ LongPrototype.shiftRightUnsigned = function shiftRightUnsigned(numBits) {
+ if (isLong(numBits))
+ numBits = numBits.toInt();
+ numBits &= 63;
+ if (numBits === 0)
+ return this;
+ else {
+ var high = this.high;
+ if (numBits < 32) {
+ var low = this.low;
+ return fromBits((low >>> numBits) | (high << (32 - numBits)), high >>> numBits, this.unsigned);
+ } else if (numBits === 32)
+ return fromBits(high, 0, this.unsigned);
+ else
+ return fromBits(high >>> (numBits - 32), 0, this.unsigned);
+ }
+ };
+
+ /**
+ * Returns this Long with bits logically shifted to the right by the given amount. This is an alias of {@link Long#shiftRightUnsigned}.
+ * @function
+ * @param {number|!Long} numBits Number of bits
+ * @returns {!Long} Shifted Long
+ */
+ LongPrototype.shru = LongPrototype.shiftRightUnsigned;
+
+ /**
+ * Converts this Long to signed.
+ * @returns {!Long} Signed long
+ */
+ LongPrototype.toSigned = function toSigned() {
+ if (!this.unsigned)
+ return this;
+ return fromBits(this.low, this.high, false);
+ };
+
+ /**
+ * Converts this Long to unsigned.
+ * @returns {!Long} Unsigned long
+ */
+ LongPrototype.toUnsigned = function toUnsigned() {
+ if (this.unsigned)
+ return this;
+ return fromBits(this.low, this.high, true);
+ };
+
+ /**
+ * Converts this Long to its byte representation.
+ * @param {boolean=} le Whether little or big endian, defaults to big endian
+ * @returns {!Array.} Byte representation
+ */
+ LongPrototype.toBytes = function(le) {
+ return le ? this.toBytesLE() : this.toBytesBE();
+ }
+
+ /**
+ * Converts this Long to its little endian byte representation.
+ * @returns {!Array.} Little endian byte representation
+ */
+ LongPrototype.toBytesLE = function() {
+ var hi = this.high,
+ lo = this.low;
+ return [
+ lo & 0xff,
+ (lo >>> 8) & 0xff,
+ (lo >>> 16) & 0xff,
+ (lo >>> 24) & 0xff,
+ hi & 0xff,
+ (hi >>> 8) & 0xff,
+ (hi >>> 16) & 0xff,
+ (hi >>> 24) & 0xff
+ ];
+ }
+
+ /**
+ * Converts this Long to its big endian byte representation.
+ * @returns {!Array.} Big endian byte representation
+ */
+ LongPrototype.toBytesBE = function() {
+ var hi = this.high,
+ lo = this.low;
+ return [
+ (hi >>> 24) & 0xff,
+ (hi >>> 16) & 0xff,
+ (hi >>> 8) & 0xff,
+ hi & 0xff,
+ (lo >>> 24) & 0xff,
+ (lo >>> 16) & 0xff,
+ (lo >>> 8) & 0xff,
+ lo & 0xff
+ ];
+ }
+
+ return Long;
+});
+
+/*
+ Copyright 2013-2014 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-2014 Daniel Wirtz
+ * This version of ByteBuffer.js uses an ArrayBuffer as its backing buffer which is accessed through a DataView and is
+ * compatible with modern browsers.
+ * Released under the Apache License, Version 2.0
+ * see: https://github.com/dcodeIO/ByteBuffer.js for details
+ */ //
+(function(global) {
+ "use strict";
+
+ /**
+ * @param {function(new: Long, number, number, boolean=)=} Long
+ * @returns {function(new: ByteBuffer, number=, boolean=, boolean=)}}
+ * @inner
+ */
+ function loadByteBuffer(Long) {
+
+ /**
+ * Constructs a new ByteBuffer.
+ * @class The swiss army knife for binary data in JavaScript.
+ * @exports ByteBuffer
+ * @constructor
+ * @param {number=} capacity Initial capacity. Defaults to {@link ByteBuffer.DEFAULT_CAPACITY}.
+ * @param {boolean=} littleEndian Whether to use little or big endian byte order. Defaults to
+ * {@link ByteBuffer.DEFAULT_ENDIAN}.
+ * @param {boolean=} noAssert Whether to skip assertions of offsets and values. Defaults to
+ * {@link ByteBuffer.DEFAULT_NOASSERT}.
+ * @expose
+ */
+ var ByteBuffer = function(capacity, littleEndian, noAssert) {
+ if (typeof capacity === 'undefined') capacity = ByteBuffer.DEFAULT_CAPACITY;
+ if (typeof littleEndian === 'undefined') littleEndian = ByteBuffer.DEFAULT_ENDIAN;
+ if (typeof noAssert === 'undefined') noAssert = ByteBuffer.DEFAULT_NOASSERT;
+ if (!noAssert) {
+ capacity = capacity | 0;
+ if (capacity < 0)
+ throw RangeError("Illegal capacity");
+ littleEndian = !!littleEndian;
+ noAssert = !!noAssert;
+ }
+
+ /**
+ * Backing buffer.
+ * @type {!ArrayBuffer}
+ * @expose
+ */
+ this.buffer = capacity === 0 ? EMPTY_BUFFER : new ArrayBuffer(capacity);
+
+ /**
+ * Data view to manipulate the backing buffer. Becomes `null` if the backing buffer has a capacity of `0`.
+ * @type {?DataView}
+ * @expose
+ */
+ this.view = capacity === 0 ? null : new DataView(this.buffer);
+
+ /**
+ * Absolute read/write offset.
+ * @type {number}
+ * @expose
+ * @see ByteBuffer#flip
+ * @see ByteBuffer#clear
+ */
+ this.offset = 0;
+
+ /**
+ * Marked offset.
+ * @type {number}
+ * @expose
+ * @see ByteBuffer#mark
+ * @see ByteBuffer#reset
+ */
+ this.markedOffset = -1;
+
+ /**
+ * Absolute limit of the contained data. Set to the backing buffer's capacity upon allocation.
+ * @type {number}
+ * @expose
+ * @see ByteBuffer#flip
+ * @see ByteBuffer#clear
+ */
+ this.limit = capacity;
+
+ /**
+ * Whether to use little endian byte order, defaults to `false` for big endian.
+ * @type {boolean}
+ * @expose
+ */
+ this.littleEndian = typeof littleEndian !== 'undefined' ? !!littleEndian : false;
+
+ /**
+ * Whether to skip assertions of offsets and values, defaults to `false`.
+ * @type {boolean}
+ * @expose
+ */
+ this.noAssert = !!noAssert;
+ };
+
+ /**
+ * ByteBuffer version.
+ * @type {string}
+ * @const
+ * @expose
+ */
+ ByteBuffer.VERSION = "3.5.5";
+
+ /**
+ * Little endian constant that can be used instead of its boolean value. Evaluates to `true`.
+ * @type {boolean}
+ * @const
+ * @expose
+ */
+ ByteBuffer.LITTLE_ENDIAN = true;
+
+ /**
+ * Big endian constant that can be used instead of its boolean value. Evaluates to `false`.
+ * @type {boolean}
+ * @const
+ * @expose
+ */
+ ByteBuffer.BIG_ENDIAN = false;
+
+ /**
+ * Default initial capacity of `16`.
+ * @type {number}
+ * @expose
+ */
+ ByteBuffer.DEFAULT_CAPACITY = 16;
+
+ /**
+ * Default endianess of `false` for big endian.
+ * @type {boolean}
+ * @expose
+ */
+ ByteBuffer.DEFAULT_ENDIAN = ByteBuffer.BIG_ENDIAN;
+
+ /**
+ * Default no assertions flag of `false`.
+ * @type {boolean}
+ * @expose
+ */
+ ByteBuffer.DEFAULT_NOASSERT = false;
+
+ /**
+ * A `Long` class for representing a 64-bit two's-complement integer value. May be `null` if Long.js has not been loaded
+ * and int64 support is not available.
+ * @type {?Long}
+ * @const
+ * @see https://github.com/dcodeIO/Long.js
+ * @expose
+ */
+ ByteBuffer.Long = Long || null;
+
+ /**
+ * @alias ByteBuffer.prototype
+ * @inner
+ */
+ var ByteBufferPrototype = ByteBuffer.prototype;
+
+ // helpers
+
+ /**
+ * @type {!ArrayBuffer}
+ * @inner
+ */
+ var EMPTY_BUFFER = new ArrayBuffer(0);
+
+ /**
+ * String.fromCharCode reference for compile-time renaming.
+ * @type {function(...number):string}
+ * @inner
+ */
+ var stringFromCharCode = String.fromCharCode;
+
+ /**
+ * Creates a source function for a string.
+ * @param {string} s String to read from
+ * @returns {function():number|null} Source function returning the next char code respectively `null` if there are
+ * no more characters left.
+ * @throws {TypeError} If the argument is invalid
+ * @inner
+ */
+ function stringSource(s) {
+ var i=0; return function() {
+ return i < s.length ? s.charCodeAt(i++) : null;
+ };
+ }
+
+ /**
+ * Creates a destination function for a string.
+ * @returns {function(number=):undefined|string} Destination function successively called with the next char code.
+ * Returns the final string when called without arguments.
+ * @inner
+ */
+ function stringDestination() {
+ var cs = [], ps = []; return function() {
+ if (arguments.length === 0)
+ return ps.join('')+stringFromCharCode.apply(String, cs);
+ if (cs.length + arguments.length > 1024)
+ ps.push(stringFromCharCode.apply(String, cs)),
+ cs.length = 0;
+ Array.prototype.push.apply(cs, arguments);
+ };
+ }
+
+ /**
+ * Allocates a new ByteBuffer backed by a buffer of the specified capacity.
+ * @param {number=} capacity Initial capacity. Defaults to {@link ByteBuffer.DEFAULT_CAPACITY}.
+ * @param {boolean=} littleEndian Whether to use little or big endian byte order. Defaults to
+ * {@link ByteBuffer.DEFAULT_ENDIAN}.
+ * @param {boolean=} noAssert Whether to skip assertions of offsets and values. Defaults to
+ * {@link ByteBuffer.DEFAULT_NOASSERT}.
+ * @returns {!ByteBuffer}
+ * @expose
+ */
+ ByteBuffer.allocate = function(capacity, littleEndian, noAssert) {
+ return new ByteBuffer(capacity, littleEndian, noAssert);
+ };
+
+ /**
+ * Concatenates multiple ByteBuffers into one.
+ * @param {!Array.} buffers Buffers to concatenate
+ * @param {(string|boolean)=} encoding String encoding if `buffers` contains a string ("base64", "hex", "binary",
+ * defaults to "utf8")
+ * @param {boolean=} littleEndian Whether to use little or big endian byte order for the resulting ByteBuffer. Defaults
+ * to {@link ByteBuffer.DEFAULT_ENDIAN}.
+ * @param {boolean=} noAssert Whether to skip assertions of offsets and values for the resulting ByteBuffer. Defaults to
+ * {@link ByteBuffer.DEFAULT_NOASSERT}.
+ * @returns {!ByteBuffer} Concatenated ByteBuffer
+ * @expose
+ */
+ ByteBuffer.concat = function(buffers, encoding, littleEndian, noAssert) {
+ if (typeof encoding === 'boolean' || typeof encoding !== 'string') {
+ noAssert = littleEndian;
+ littleEndian = encoding;
+ encoding = undefined;
+ }
+ var capacity = 0;
+ for (var i=0, k=buffers.length, length; i 0) capacity += length;
+ }
+ if (capacity === 0)
+ return new ByteBuffer(0, littleEndian, noAssert);
+ var bb = new ByteBuffer(capacity, littleEndian, noAssert),
+ bi;
+ var view = new Uint8Array(bb.buffer);
+ i=0; while (i} buffer Anything that can be wrapped
+ * @param {(string|boolean)=} encoding String encoding if `buffer` is a string ("base64", "hex", "binary", defaults to
+ * "utf8")
+ * @param {boolean=} littleEndian Whether to use little or big endian byte order. Defaults to
+ * {@link ByteBuffer.DEFAULT_ENDIAN}.
+ * @param {boolean=} noAssert Whether to skip assertions of offsets and values. Defaults to
+ * {@link ByteBuffer.DEFAULT_NOASSERT}.
+ * @returns {!ByteBuffer} A ByteBuffer wrapping `buffer`
+ * @expose
+ */
+ ByteBuffer.wrap = function(buffer, encoding, littleEndian, noAssert) {
+ if (typeof encoding !== 'string') {
+ noAssert = littleEndian;
+ littleEndian = encoding;
+ encoding = undefined;
+ }
+ if (typeof buffer === 'string') {
+ if (typeof encoding === 'undefined')
+ encoding = "utf8";
+ switch (encoding) {
+ case "base64":
+ return ByteBuffer.fromBase64(buffer, littleEndian);
+ case "hex":
+ return ByteBuffer.fromHex(buffer, littleEndian);
+ case "binary":
+ return ByteBuffer.fromBinary(buffer, littleEndian);
+ case "utf8":
+ return ByteBuffer.fromUTF8(buffer, littleEndian);
+ case "debug":
+ return ByteBuffer.fromDebug(buffer, littleEndian);
+ default:
+ throw Error("Unsupported encoding: "+encoding);
+ }
+ }
+ if (buffer === null || typeof buffer !== 'object')
+ throw TypeError("Illegal buffer");
+ var bb;
+ if (ByteBuffer.isByteBuffer(buffer)) {
+ bb = ByteBufferPrototype.clone.call(buffer);
+ bb.markedOffset = -1;
+ return bb;
+ }
+ if (buffer instanceof Uint8Array) { // Extract ArrayBuffer from Uint8Array
+ bb = new ByteBuffer(0, littleEndian, noAssert);
+ if (buffer.length > 0) { // Avoid references to more than one EMPTY_BUFFER
+ bb.buffer = buffer.buffer;
+ bb.offset = buffer.byteOffset;
+ bb.limit = buffer.byteOffset + buffer.length;
+ bb.view = buffer.length > 0 ? new DataView(buffer.buffer) : null;
+ }
+ } else if (buffer instanceof ArrayBuffer) { // Reuse ArrayBuffer
+ bb = new ByteBuffer(0, littleEndian, noAssert);
+ if (buffer.byteLength > 0) {
+ bb.buffer = buffer;
+ bb.offset = 0;
+ bb.limit = buffer.byteLength;
+ bb.view = buffer.byteLength > 0 ? new DataView(buffer) : null;
+ }
+ } else if (Object.prototype.toString.call(buffer) === "[object Array]") { // Create from octets
+ bb = new ByteBuffer(buffer.length, littleEndian, noAssert);
+ bb.limit = buffer.length;
+ for (i=0; i>>= 0;
+ if (offset < 0 || offset + 0 > this.buffer.byteLength)
+ throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength);
+ }
+ offset += 1;
+ var capacity0 = this.buffer.byteLength;
+ if (offset > capacity0)
+ this.resize((capacity0 *= 2) > offset ? capacity0 : offset);
+ offset -= 1;
+ this.view.setInt8(offset, value);
+ if (relative) this.offset += 1;
+ return this;
+ };
+
+ /**
+ * Writes an 8bit signed integer. This is an alias of {@link ByteBuffer#writeInt8}.
+ * @function
+ * @param {number} value Value to write
+ * @param {number=} offset Offset to write to. Will use and advance {@link ByteBuffer#offset} by `1` if omitted.
+ * @returns {!ByteBuffer} this
+ * @expose
+ */
+ ByteBufferPrototype.writeByte = ByteBufferPrototype.writeInt8;
+
+ /**
+ * Reads an 8bit signed integer.
+ * @param {number=} offset Offset to read from. Will use and advance {@link ByteBuffer#offset} by `1` if omitted.
+ * @returns {number} Value read
+ * @expose
+ */
+ ByteBufferPrototype.readInt8 = function(offset) {
+ var relative = typeof offset === 'undefined';
+ if (relative) offset = this.offset;
+ if (!this.noAssert) {
+ if (typeof offset !== 'number' || offset % 1 !== 0)
+ throw TypeError("Illegal offset: "+offset+" (not an integer)");
+ offset >>>= 0;
+ if (offset < 0 || offset + 1 > this.buffer.byteLength)
+ throw RangeError("Illegal offset: 0 <= "+offset+" (+"+1+") <= "+this.buffer.byteLength);
+ }
+ var value = this.view.getInt8(offset);
+ if (relative) this.offset += 1;
+ return value;
+ };
+
+ /**
+ * Reads an 8bit signed integer. This is an alias of {@link ByteBuffer#readInt8}.
+ * @function
+ * @param {number=} offset Offset to read from. Will use and advance {@link ByteBuffer#offset} by `1` if omitted.
+ * @returns {number} Value read
+ * @expose
+ */
+ ByteBufferPrototype.readByte = ByteBufferPrototype.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} by `1` if omitted.
+ * @returns {!ByteBuffer} this
+ * @expose
+ */
+ ByteBufferPrototype.writeUint8 = function(value, offset) {
+ var relative = typeof offset === 'undefined';
+ if (relative) offset = this.offset;
+ if (!this.noAssert) {
+ if (typeof value !== 'number' || value % 1 !== 0)
+ throw TypeError("Illegal value: "+value+" (not an integer)");
+ value >>>= 0;
+ if (typeof offset !== 'number' || offset % 1 !== 0)
+ throw TypeError("Illegal offset: "+offset+" (not an integer)");
+ offset >>>= 0;
+ if (offset < 0 || offset + 0 > this.buffer.byteLength)
+ throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength);
+ }
+ offset += 1;
+ var capacity1 = this.buffer.byteLength;
+ if (offset > capacity1)
+ this.resize((capacity1 *= 2) > offset ? capacity1 : offset);
+ offset -= 1;
+ this.view.setUint8(offset, value);
+ if (relative) this.offset += 1;
+ return this;
+ };
+
+ /**
+ * Reads an 8bit unsigned integer.
+ * @param {number=} offset Offset to read from. Will use and advance {@link ByteBuffer#offset} by `1` if omitted.
+ * @returns {number} Value read
+ * @expose
+ */
+ ByteBufferPrototype.readUint8 = function(offset) {
+ var relative = typeof offset === 'undefined';
+ if (relative) offset = this.offset;
+ if (!this.noAssert) {
+ if (typeof offset !== 'number' || offset % 1 !== 0)
+ throw TypeError("Illegal offset: "+offset+" (not an integer)");
+ offset >>>= 0;
+ if (offset < 0 || offset + 1 > this.buffer.byteLength)
+ throw RangeError("Illegal offset: 0 <= "+offset+" (+"+1+") <= "+this.buffer.byteLength);
+ }
+ var value = this.view.getUint8(offset);
+ if (relative) this.offset += 1;
+ return value;
+ };
+
+ // types/ints/int16
+
+ /**
+ * 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} by `2` if omitted.
+ * @throws {TypeError} If `offset` or `value` is not a valid number
+ * @throws {RangeError} If `offset` is out of bounds
+ * @expose
+ */
+ ByteBufferPrototype.writeInt16 = function(value, offset) {
+ var relative = typeof offset === 'undefined';
+ if (relative) offset = this.offset;
+ if (!this.noAssert) {
+ if (typeof value !== 'number' || value % 1 !== 0)
+ throw TypeError("Illegal value: "+value+" (not an integer)");
+ value |= 0;
+ if (typeof offset !== 'number' || offset % 1 !== 0)
+ throw TypeError("Illegal offset: "+offset+" (not an integer)");
+ offset >>>= 0;
+ if (offset < 0 || offset + 0 > this.buffer.byteLength)
+ throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength);
+ }
+ offset += 2;
+ var capacity2 = this.buffer.byteLength;
+ if (offset > capacity2)
+ this.resize((capacity2 *= 2) > offset ? capacity2 : offset);
+ offset -= 2;
+ this.view.setInt16(offset, value, this.littleEndian);
+ if (relative) this.offset += 2;
+ return this;
+ };
+
+ /**
+ * Writes a 16bit signed integer. 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} by `2` if omitted.
+ * @throws {TypeError} If `offset` or `value` is not a valid number
+ * @throws {RangeError} If `offset` is out of bounds
+ * @expose
+ */
+ ByteBufferPrototype.writeShort = ByteBufferPrototype.writeInt16;
+
+ /**
+ * Reads a 16bit signed integer.
+ * @param {number=} offset Offset to read from. Will use and advance {@link ByteBuffer#offset} by `2` if omitted.
+ * @returns {number} Value read
+ * @throws {TypeError} If `offset` is not a valid number
+ * @throws {RangeError} If `offset` is out of bounds
+ * @expose
+ */
+ ByteBufferPrototype.readInt16 = function(offset) {
+ var relative = typeof offset === 'undefined';
+ if (relative) offset = this.offset;
+ if (!this.noAssert) {
+ if (typeof offset !== 'number' || offset % 1 !== 0)
+ throw TypeError("Illegal offset: "+offset+" (not an integer)");
+ offset >>>= 0;
+ if (offset < 0 || offset + 2 > this.buffer.byteLength)
+ throw RangeError("Illegal offset: 0 <= "+offset+" (+"+2+") <= "+this.buffer.byteLength);
+ }
+ var value = this.view.getInt16(offset, this.littleEndian);
+ if (relative) this.offset += 2;
+ return value;
+ };
+
+ /**
+ * Reads a 16bit signed integer. This is an alias of {@link ByteBuffer#readInt16}.
+ * @function
+ * @param {number=} offset Offset to read from. Will use and advance {@link ByteBuffer#offset} by `2` if omitted.
+ * @returns {number} Value read
+ * @throws {TypeError} If `offset` is not a valid number
+ * @throws {RangeError} If `offset` is out of bounds
+ * @expose
+ */
+ ByteBufferPrototype.readShort = ByteBufferPrototype.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} by `2` if omitted.
+ * @throws {TypeError} If `offset` or `value` is not a valid number
+ * @throws {RangeError} If `offset` is out of bounds
+ * @expose
+ */
+ ByteBufferPrototype.writeUint16 = function(value, offset) {
+ var relative = typeof offset === 'undefined';
+ if (relative) offset = this.offset;
+ if (!this.noAssert) {
+ if (typeof value !== 'number' || value % 1 !== 0)
+ throw TypeError("Illegal value: "+value+" (not an integer)");
+ value >>>= 0;
+ if (typeof offset !== 'number' || offset % 1 !== 0)
+ throw TypeError("Illegal offset: "+offset+" (not an integer)");
+ offset >>>= 0;
+ if (offset < 0 || offset + 0 > this.buffer.byteLength)
+ throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength);
+ }
+ offset += 2;
+ var capacity3 = this.buffer.byteLength;
+ if (offset > capacity3)
+ this.resize((capacity3 *= 2) > offset ? capacity3 : offset);
+ offset -= 2;
+ this.view.setUint16(offset, value, this.littleEndian);
+ if (relative) this.offset += 2;
+ return this;
+ };
+
+ /**
+ * Reads a 16bit unsigned integer.
+ * @param {number=} offset Offset to read from. Will use and advance {@link ByteBuffer#offset} by `2` if omitted.
+ * @returns {number} Value read
+ * @throws {TypeError} If `offset` is not a valid number
+ * @throws {RangeError} If `offset` is out of bounds
+ * @expose
+ */
+ ByteBufferPrototype.readUint16 = function(offset) {
+ var relative = typeof offset === 'undefined';
+ if (relative) offset = this.offset;
+ if (!this.noAssert) {
+ if (typeof offset !== 'number' || offset % 1 !== 0)
+ throw TypeError("Illegal offset: "+offset+" (not an integer)");
+ offset >>>= 0;
+ if (offset < 0 || offset + 2 > this.buffer.byteLength)
+ throw RangeError("Illegal offset: 0 <= "+offset+" (+"+2+") <= "+this.buffer.byteLength);
+ }
+ var value = this.view.getUint16(offset, this.littleEndian);
+ if (relative) this.offset += 2;
+ return value;
+ };
+
+ // types/ints/int32
+
+ /**
+ * Writes a 32bit signed integer.
+ * @param {number} value Value to write
+ * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by `4` if omitted.
+ * @expose
+ */
+ ByteBufferPrototype.writeInt32 = function(value, offset) {
+ var relative = typeof offset === 'undefined';
+ if (relative) offset = this.offset;
+ if (!this.noAssert) {
+ if (typeof value !== 'number' || value % 1 !== 0)
+ throw TypeError("Illegal value: "+value+" (not an integer)");
+ value |= 0;
+ if (typeof offset !== 'number' || offset % 1 !== 0)
+ throw TypeError("Illegal offset: "+offset+" (not an integer)");
+ offset >>>= 0;
+ if (offset < 0 || offset + 0 > this.buffer.byteLength)
+ throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength);
+ }
+ offset += 4;
+ var capacity4 = this.buffer.byteLength;
+ if (offset > capacity4)
+ this.resize((capacity4 *= 2) > offset ? capacity4 : offset);
+ offset -= 4;
+ this.view.setInt32(offset, value, this.littleEndian);
+ if (relative) this.offset += 4;
+ return this;
+ };
+
+ /**
+ * Writes a 32bit signed integer. This is an alias of {@link ByteBuffer#writeInt32}.
+ * @param {number} value Value to write
+ * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by `4` if omitted.
+ * @expose
+ */
+ ByteBufferPrototype.writeInt = ByteBufferPrototype.writeInt32;
+
+ /**
+ * Reads a 32bit signed integer.
+ * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by `4` if omitted.
+ * @returns {number} Value read
+ * @expose
+ */
+ ByteBufferPrototype.readInt32 = function(offset) {
+ var relative = typeof offset === 'undefined';
+ if (relative) offset = this.offset;
+ if (!this.noAssert) {
+ if (typeof offset !== 'number' || offset % 1 !== 0)
+ throw TypeError("Illegal offset: "+offset+" (not an integer)");
+ offset >>>= 0;
+ if (offset < 0 || offset + 4 > this.buffer.byteLength)
+ throw RangeError("Illegal offset: 0 <= "+offset+" (+"+4+") <= "+this.buffer.byteLength);
+ }
+ var value = this.view.getInt32(offset, this.littleEndian);
+ if (relative) this.offset += 4;
+ return value;
+ };
+
+ /**
+ * Reads a 32bit signed integer. This is an alias of {@link ByteBuffer#readInt32}.
+ * @param {number=} offset Offset to read from. Will use and advance {@link ByteBuffer#offset} by `4` if omitted.
+ * @returns {number} Value read
+ * @expose
+ */
+ ByteBufferPrototype.readInt = ByteBufferPrototype.readInt32;
+
+ /**
+ * Writes a 32bit unsigned integer.
+ * @param {number} value Value to write
+ * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by `4` if omitted.
+ * @expose
+ */
+ ByteBufferPrototype.writeUint32 = function(value, offset) {
+ var relative = typeof offset === 'undefined';
+ if (relative) offset = this.offset;
+ if (!this.noAssert) {
+ if (typeof value !== 'number' || value % 1 !== 0)
+ throw TypeError("Illegal value: "+value+" (not an integer)");
+ value >>>= 0;
+ if (typeof offset !== 'number' || offset % 1 !== 0)
+ throw TypeError("Illegal offset: "+offset+" (not an integer)");
+ offset >>>= 0;
+ if (offset < 0 || offset + 0 > this.buffer.byteLength)
+ throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength);
+ }
+ offset += 4;
+ var capacity5 = this.buffer.byteLength;
+ if (offset > capacity5)
+ this.resize((capacity5 *= 2) > offset ? capacity5 : offset);
+ offset -= 4;
+ this.view.setUint32(offset, value, this.littleEndian);
+ if (relative) this.offset += 4;
+ return this;
+ };
+
+ /**
+ * Reads a 32bit unsigned integer.
+ * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by `4` if omitted.
+ * @returns {number} Value read
+ * @expose
+ */
+ ByteBufferPrototype.readUint32 = function(offset) {
+ var relative = typeof offset === 'undefined';
+ if (relative) offset = this.offset;
+ if (!this.noAssert) {
+ if (typeof offset !== 'number' || offset % 1 !== 0)
+ throw TypeError("Illegal offset: "+offset+" (not an integer)");
+ offset >>>= 0;
+ if (offset < 0 || offset + 4 > this.buffer.byteLength)
+ throw RangeError("Illegal offset: 0 <= "+offset+" (+"+4+") <= "+this.buffer.byteLength);
+ }
+ var value = this.view.getUint32(offset, this.littleEndian);
+ if (relative) this.offset += 4;
+ return value;
+ };
+
+ // types/ints/int64
+
+ if (Long) {
+
+ /**
+ * Writes a 64bit signed integer.
+ * @param {number|!Long} value Value to write
+ * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by `8` if omitted.
+ * @returns {!ByteBuffer} this
+ * @expose
+ */
+ ByteBufferPrototype.writeInt64 = function(value, offset) {
+ var relative = typeof offset === 'undefined';
+ if (relative) offset = this.offset;
+ if (!this.noAssert) {
+ if (typeof value === 'number')
+ value = Long.fromNumber(value);
+ else if (typeof value === 'string')
+ value = Long.fromString(value);
+ else if (!(value && value instanceof Long))
+ throw TypeError("Illegal value: "+value+" (not an integer or Long)");
+ if (typeof offset !== 'number' || offset % 1 !== 0)
+ throw TypeError("Illegal offset: "+offset+" (not an integer)");
+ offset >>>= 0;
+ if (offset < 0 || offset + 0 > this.buffer.byteLength)
+ throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength);
+ }
+ if (typeof value === 'number')
+ value = Long.fromNumber(value);
+ else if (typeof value === 'string')
+ value = Long.fromString(value);
+ offset += 8;
+ var capacity6 = this.buffer.byteLength;
+ if (offset > capacity6)
+ this.resize((capacity6 *= 2) > offset ? capacity6 : offset);
+ offset -= 8;
+ if (this.littleEndian) {
+ this.view.setInt32(offset , value.low , true);
+ this.view.setInt32(offset+4, value.high, true);
+ } else {
+ this.view.setInt32(offset , value.high, false);
+ this.view.setInt32(offset+4, value.low , false);
+ }
+ if (relative) this.offset += 8;
+ return this;
+ };
+
+ /**
+ * Writes a 64bit signed integer. This is an alias of {@link ByteBuffer#writeInt64}.
+ * @param {number|!Long} value Value to write
+ * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by `8` if omitted.
+ * @returns {!ByteBuffer} this
+ * @expose
+ */
+ ByteBufferPrototype.writeLong = ByteBufferPrototype.writeInt64;
+
+ /**
+ * Reads a 64bit signed integer.
+ * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by `8` if omitted.
+ * @returns {!Long}
+ * @expose
+ */
+ ByteBufferPrototype.readInt64 = function(offset) {
+ var relative = typeof offset === 'undefined';
+ if (relative) offset = this.offset;
+ if (!this.noAssert) {
+ if (typeof offset !== 'number' || offset % 1 !== 0)
+ throw TypeError("Illegal offset: "+offset+" (not an integer)");
+ offset >>>= 0;
+ if (offset < 0 || offset + 8 > this.buffer.byteLength)
+ throw RangeError("Illegal offset: 0 <= "+offset+" (+"+8+") <= "+this.buffer.byteLength);
+ }
+ var value = this.littleEndian
+ ? new Long(this.view.getInt32(offset , true ), this.view.getInt32(offset+4, true ), false)
+ : new Long(this.view.getInt32(offset+4, false), this.view.getInt32(offset , false), false);
+ if (relative) this.offset += 8;
+ return value;
+ };
+
+ /**
+ * Reads a 64bit signed integer. This is an alias of {@link ByteBuffer#readInt64}.
+ * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by `8` if omitted.
+ * @returns {!Long}
+ * @expose
+ */
+ ByteBufferPrototype.readLong = ByteBufferPrototype.readInt64;
+
+ /**
+ * Writes a 64bit unsigned integer.
+ * @param {number|!Long} value Value to write
+ * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by `8` if omitted.
+ * @returns {!ByteBuffer} this
+ * @expose
+ */
+ ByteBufferPrototype.writeUint64 = function(value, offset) {
+ var relative = typeof offset === 'undefined';
+ if (relative) offset = this.offset;
+ if (!this.noAssert) {
+ if (typeof value === 'number')
+ value = Long.fromNumber(value);
+ else if (typeof value === 'string')
+ value = Long.fromString(value);
+ else if (!(value && value instanceof Long))
+ throw TypeError("Illegal value: "+value+" (not an integer or Long)");
+ if (typeof offset !== 'number' || offset % 1 !== 0)
+ throw TypeError("Illegal offset: "+offset+" (not an integer)");
+ offset >>>= 0;
+ if (offset < 0 || offset + 0 > this.buffer.byteLength)
+ throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength);
+ }
+ if (typeof value === 'number')
+ value = Long.fromNumber(value);
+ else if (typeof value === 'string')
+ value = Long.fromString(value);
+ offset += 8;
+ var capacity7 = this.buffer.byteLength;
+ if (offset > capacity7)
+ this.resize((capacity7 *= 2) > offset ? capacity7 : offset);
+ offset -= 8;
+ if (this.littleEndian) {
+ this.view.setInt32(offset , value.low , true);
+ this.view.setInt32(offset+4, value.high, true);
+ } else {
+ this.view.setInt32(offset , value.high, false);
+ this.view.setInt32(offset+4, value.low , false);
+ }
+ if (relative) this.offset += 8;
+ return this;
+ };
+
+ /**
+ * Reads a 64bit unsigned integer.
+ * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by `8` if omitted.
+ * @returns {!Long}
+ * @expose
+ */
+ ByteBufferPrototype.readUint64 = function(offset) {
+ var relative = typeof offset === 'undefined';
+ if (relative) offset = this.offset;
+ if (!this.noAssert) {
+ if (typeof offset !== 'number' || offset % 1 !== 0)
+ throw TypeError("Illegal offset: "+offset+" (not an integer)");
+ offset >>>= 0;
+ if (offset < 0 || offset + 8 > this.buffer.byteLength)
+ throw RangeError("Illegal offset: 0 <= "+offset+" (+"+8+") <= "+this.buffer.byteLength);
+ }
+ var value = this.littleEndian
+ ? new Long(this.view.getInt32(offset , true ), this.view.getInt32(offset+4, true ), true)
+ : new Long(this.view.getInt32(offset+4, false), this.view.getInt32(offset , false), true);
+ if (relative) this.offset += 8;
+ return value;
+ };
+
+ } // Long
+
+
+ // types/floats/float32
+
+ /**
+ * Writes a 32bit float.
+ * @param {number} value Value to write
+ * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by `4` if omitted.
+ * @returns {!ByteBuffer} this
+ * @expose
+ */
+ ByteBufferPrototype.writeFloat32 = function(value, offset) {
+ var relative = typeof offset === 'undefined';
+ if (relative) offset = this.offset;
+ if (!this.noAssert) {
+ if (typeof value !== 'number')
+ throw TypeError("Illegal value: "+value+" (not a number)");
+ if (typeof offset !== 'number' || offset % 1 !== 0)
+ throw TypeError("Illegal offset: "+offset+" (not an integer)");
+ offset >>>= 0;
+ if (offset < 0 || offset + 0 > this.buffer.byteLength)
+ throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength);
+ }
+ offset += 4;
+ var capacity8 = this.buffer.byteLength;
+ if (offset > capacity8)
+ this.resize((capacity8 *= 2) > offset ? capacity8 : offset);
+ offset -= 4;
+ this.view.setFloat32(offset, value, this.littleEndian);
+ if (relative) this.offset += 4;
+ return this;
+ };
+
+ /**
+ * Writes a 32bit 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 increase {@link ByteBuffer#offset} by `4` if omitted.
+ * @returns {!ByteBuffer} this
+ * @expose
+ */
+ ByteBufferPrototype.writeFloat = ByteBufferPrototype.writeFloat32;
+
+ /**
+ * Reads a 32bit float.
+ * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by `4` if omitted.
+ * @returns {number}
+ * @expose
+ */
+ ByteBufferPrototype.readFloat32 = function(offset) {
+ var relative = typeof offset === 'undefined';
+ if (relative) offset = this.offset;
+ if (!this.noAssert) {
+ if (typeof offset !== 'number' || offset % 1 !== 0)
+ throw TypeError("Illegal offset: "+offset+" (not an integer)");
+ offset >>>= 0;
+ if (offset < 0 || offset + 4 > this.buffer.byteLength)
+ throw RangeError("Illegal offset: 0 <= "+offset+" (+"+4+") <= "+this.buffer.byteLength);
+ }
+ var value = this.view.getFloat32(offset, this.littleEndian);
+ if (relative) this.offset += 4;
+ return value;
+ };
+
+ /**
+ * Reads a 32bit float. This is an alias of {@link ByteBuffer#readFloat32}.
+ * @function
+ * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by `4` if omitted.
+ * @returns {number}
+ * @expose
+ */
+ ByteBufferPrototype.readFloat = ByteBufferPrototype.readFloat32;
+
+ // types/floats/float64
+
+ /**
+ * Writes a 64bit float.
+ * @param {number} value Value to write
+ * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by `8` if omitted.
+ * @returns {!ByteBuffer} this
+ * @expose
+ */
+ ByteBufferPrototype.writeFloat64 = function(value, offset) {
+ var relative = typeof offset === 'undefined';
+ if (relative) offset = this.offset;
+ if (!this.noAssert) {
+ if (typeof value !== 'number')
+ throw TypeError("Illegal value: "+value+" (not a number)");
+ if (typeof offset !== 'number' || offset % 1 !== 0)
+ throw TypeError("Illegal offset: "+offset+" (not an integer)");
+ offset >>>= 0;
+ if (offset < 0 || offset + 0 > this.buffer.byteLength)
+ throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength);
+ }
+ offset += 8;
+ var capacity9 = this.buffer.byteLength;
+ if (offset > capacity9)
+ this.resize((capacity9 *= 2) > offset ? capacity9 : offset);
+ offset -= 8;
+ this.view.setFloat64(offset, value, this.littleEndian);
+ if (relative) this.offset += 8;
+ return this;
+ };
+
+ /**
+ * Writes a 64bit float. 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 increase {@link ByteBuffer#offset} by `8` if omitted.
+ * @returns {!ByteBuffer} this
+ * @expose
+ */
+ ByteBufferPrototype.writeDouble = ByteBufferPrototype.writeFloat64;
+
+ /**
+ * Reads a 64bit float.
+ * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by `8` if omitted.
+ * @returns {number}
+ * @expose
+ */
+ ByteBufferPrototype.readFloat64 = function(offset) {
+ var relative = typeof offset === 'undefined';
+ if (relative) offset = this.offset;
+ if (!this.noAssert) {
+ if (typeof offset !== 'number' || offset % 1 !== 0)
+ throw TypeError("Illegal offset: "+offset+" (not an integer)");
+ offset >>>= 0;
+ if (offset < 0 || offset + 8 > this.buffer.byteLength)
+ throw RangeError("Illegal offset: 0 <= "+offset+" (+"+8+") <= "+this.buffer.byteLength);
+ }
+ var value = this.view.getFloat64(offset, this.littleEndian);
+ if (relative) this.offset += 8;
+ return value;
+ };
+
+ /**
+ * Reads a 64bit float. This is an alias of {@link ByteBuffer#readFloat64}.
+ * @function
+ * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by `8` if omitted.
+ * @returns {number}
+ * @expose
+ */
+ ByteBufferPrototype.readDouble = ByteBufferPrototype.readFloat64;
+
+
+ // types/varints/varint32
+
+ /**
+ * Maximum number of bytes required to store a 32bit base 128 variable-length integer.
+ * @type {number}
+ * @const
+ * @expose
+ */
+ ByteBuffer.MAX_VARINT32_BYTES = 5;
+
+ /**
+ * Calculates the actual number of bytes required to store 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 < 1 << 7 ) return 1;
+ else if (value < 1 << 14) return 2;
+ else if (value < 1 << 21) return 3;
+ else if (value < 1 << 28) return 4;
+ else return 5;
+ };
+
+ /**
+ * Zigzag 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) {
+ return (((n |= 0) << 1) ^ (n >> 31)) >>> 0; // ref: src/google/protobuf/wire_format_lite.h
+ };
+
+ /**
+ * 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) {
+ return ((n >>> 1) ^ -(n & 1)) | 0; // // ref: src/google/protobuf/wire_format_lite.h
+ };
+
+ /**
+ * Writes a 32bit base 128 variable-length integer.
+ * @param {number} value Value to write
+ * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by the number of bytes
+ * written if omitted.
+ * @returns {!ByteBuffer|number} this if `offset` is omitted, else the actual number of bytes written
+ * @expose
+ */
+ ByteBufferPrototype.writeVarint32 = function(value, offset) {
+ var relative = typeof offset === 'undefined';
+ if (relative) offset = this.offset;
+ if (!this.noAssert) {
+ if (typeof value !== 'number' || value % 1 !== 0)
+ throw TypeError("Illegal value: "+value+" (not an integer)");
+ value |= 0;
+ if (typeof offset !== 'number' || offset % 1 !== 0)
+ throw TypeError("Illegal offset: "+offset+" (not an integer)");
+ offset >>>= 0;
+ if (offset < 0 || offset + 0 > this.buffer.byteLength)
+ throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength);
+ }
+ var size = ByteBuffer.calculateVarint32(value),
+ b;
+ offset += size;
+ var capacity10 = this.buffer.byteLength;
+ if (offset > capacity10)
+ this.resize((capacity10 *= 2) > offset ? capacity10 : offset);
+ offset -= size;
+ // ref: http://code.google.com/searchframe#WTeibokF6gE/trunk/src/google/protobuf/io/coded_stream.cc
+ this.view.setUint8(offset, b = value | 0x80);
+ value >>>= 0;
+ if (value >= 1 << 7) {
+ b = (value >> 7) | 0x80;
+ this.view.setUint8(offset+1, b);
+ if (value >= 1 << 14) {
+ b = (value >> 14) | 0x80;
+ this.view.setUint8(offset+2, b);
+ if (value >= 1 << 21) {
+ b = (value >> 21) | 0x80;
+ this.view.setUint8(offset+3, b);
+ if (value >= 1 << 28) {
+ this.view.setUint8(offset+4, (value >> 28) & 0x0F);
+ size = 5;
+ } else {
+ this.view.setUint8(offset+3, b & 0x7F);
+ size = 4;
+ }
+ } else {
+ this.view.setUint8(offset+2, b & 0x7F);
+ size = 3;
+ }
+ } else {
+ this.view.setUint8(offset+1, b & 0x7F);
+ size = 2;
+ }
+ } else {
+ this.view.setUint8(offset, b & 0x7F);
+ size = 1;
+ }
+ if (relative) {
+ this.offset += size;
+ return this;
+ }
+ return size;
+ };
+
+ /**
+ * Writes a zig-zag encoded 32bit base 128 variable-length integer.
+ * @param {number} value Value to write
+ * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by the number of bytes
+ * written if omitted.
+ * @returns {!ByteBuffer|number} this if `offset` is omitted, else the actual number of bytes written
+ * @expose
+ */
+ ByteBufferPrototype.writeVarint32ZigZag = function(value, offset) {
+ return this.writeVarint32(ByteBuffer.zigZagEncode32(value), offset);
+ };
+
+ /**
+ * Reads a 32bit base 128 variable-length integer.
+ * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by the number of bytes
+ * written 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. Has a property `truncated = true` if there is not enough data available
+ * to fully decode the varint.
+ * @expose
+ */
+ ByteBufferPrototype.readVarint32 = function(offset) {
+ var relative = typeof offset === 'undefined';
+ if (relative) offset = this.offset;
+ if (!this.noAssert) {
+ if (typeof offset !== 'number' || offset % 1 !== 0)
+ throw TypeError("Illegal offset: "+offset+" (not an integer)");
+ offset >>>= 0;
+ if (offset < 0 || offset + 1 > this.buffer.byteLength)
+ throw RangeError("Illegal offset: 0 <= "+offset+" (+"+1+") <= "+this.buffer.byteLength);
+ }
+ // ref: src/google/protobuf/io/coded_stream.cc
+ var size = 0,
+ value = 0 >>> 0,
+ temp,
+ ioffset;
+ do {
+ ioffset = offset+size;
+ if (!this.noAssert && ioffset > this.limit) {
+ var err = Error("Truncated");
+ err['truncated'] = true;
+ throw err;
+ }
+ temp = this.view.getUint8(ioffset);
+ if (size < 5)
+ value |= ((temp&0x7F)<<(7*size)) >>> 0;
+ ++size;
+ } while ((temp & 0x80) === 0x80);
+ value = value | 0; // Make sure to discard the higher order bits
+ if (relative) {
+ this.offset += size;
+ return value;
+ }
+ return {
+ "value": value,
+ "length": size
+ };
+ };
+
+ /**
+ * Reads a zig-zag encoded 32bit base 128 variable-length integer.
+ * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by the number of bytes
+ * written 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
+ */
+ ByteBufferPrototype.readVarint32ZigZag = function(offset) {
+ var val = this.readVarint32(offset);
+ if (typeof val === 'object')
+ val["value"] = ByteBuffer.zigZagDecode32(val["value"]);
+ else
+ val = ByteBuffer.zigZagDecode32(val);
+ return val;
+ };
+
+ // types/varints/varint64
+
+ if (Long) {
+
+ /**
+ * Maximum number of bytes required to store a 64bit base 128 variable-length integer.
+ * @type {number}
+ * @const
+ * @expose
+ */
+ ByteBuffer.MAX_VARINT64_BYTES = 10;
+
+ /**
+ * Calculates the actual number of bytes required to store 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) {
+ if (typeof value === 'number')
+ value = Long.fromNumber(value);
+ else if (typeof value === 'string')
+ value = Long.fromString(value);
+ // ref: src/google/protobuf/io/coded_stream.cc
+ 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 < 1 << 14)
+ return part0 < 1 << 7 ? 1 : 2;
+ else
+ return part0 < 1 << 21 ? 3 : 4;
+ } else {
+ if (part1 < 1 << 14)
+ return part1 < 1 << 7 ? 5 : 6;
+ else
+ return part1 < 1 << 21 ? 7 : 8;
+ }
+ } else
+ return part2 < 1 << 7 ? 9 : 10;
+ };
+
+ /**
+ * Zigzag encodes a signed 64bit integer so that it can be effectively used with varint encoding.
+ * @param {number|!Long} value Signed long
+ * @returns {!Long} Unsigned zigzag encoded long
+ * @expose
+ */
+ ByteBuffer.zigZagEncode64 = function(value) {
+ if (typeof value === 'number')
+ value = Long.fromNumber(value, false);
+ else if (typeof value === 'string')
+ value = Long.fromString(value, false);
+ else if (value.unsigned !== false) value = value.toSigned();
+ // ref: src/google/protobuf/wire_format_lite.h
+ return value.shiftLeft(1).xor(value.shiftRight(63)).toUnsigned();
+ };
+
+ /**
+ * Decodes a zigzag encoded signed 64bit integer.
+ * @param {!Long|number} value Unsigned zigzag encoded long or JavaScript number
+ * @returns {!Long} Signed long
+ * @expose
+ */
+ ByteBuffer.zigZagDecode64 = function(value) {
+ if (typeof value === 'number')
+ value = Long.fromNumber(value, false);
+ else if (typeof value === 'string')
+ value = Long.fromString(value, false);
+ else if (value.unsigned !== false) value = value.toSigned();
+ // ref: src/google/protobuf/wire_format_lite.h
+ return value.shiftRightUnsigned(1).xor(value.and(Long.ONE).toSigned().negate()).toSigned();
+ };
+
+ /**
+ * Writes a 64bit base 128 variable-length integer.
+ * @param {number|Long} value Value to write
+ * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by the number of bytes
+ * written if omitted.
+ * @returns {!ByteBuffer|number} `this` if offset is omitted, else the actual number of bytes written.
+ * @expose
+ */
+ ByteBufferPrototype.writeVarint64 = function(value, offset) {
+ var relative = typeof offset === 'undefined';
+ if (relative) offset = this.offset;
+ if (!this.noAssert) {
+ if (typeof value === 'number')
+ value = Long.fromNumber(value);
+ else if (typeof value === 'string')
+ value = Long.fromString(value);
+ else if (!(value && value instanceof Long))
+ throw TypeError("Illegal value: "+value+" (not an integer or Long)");
+ if (typeof offset !== 'number' || offset % 1 !== 0)
+ throw TypeError("Illegal offset: "+offset+" (not an integer)");
+ offset >>>= 0;
+ if (offset < 0 || offset + 0 > this.buffer.byteLength)
+ throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength);
+ }
+ if (typeof value === 'number')
+ value = Long.fromNumber(value, false);
+ else if (typeof value === 'string')
+ value = Long.fromString(value, false);
+ else if (value.unsigned !== false) value = value.toSigned();
+ var size = ByteBuffer.calculateVarint64(value),
+ part0 = value.toInt() >>> 0,
+ part1 = value.shiftRightUnsigned(28).toInt() >>> 0,
+ part2 = value.shiftRightUnsigned(56).toInt() >>> 0;
+ offset += size;
+ var capacity11 = this.buffer.byteLength;
+ if (offset > capacity11)
+ this.resize((capacity11 *= 2) > offset ? capacity11 : offset);
+ offset -= size;
+ switch (size) {
+ case 10: this.view.setUint8(offset+9, (part2 >>> 7) & 0x01);
+ case 9 : this.view.setUint8(offset+8, size !== 9 ? (part2 ) | 0x80 : (part2 ) & 0x7F);
+ case 8 : this.view.setUint8(offset+7, size !== 8 ? (part1 >>> 21) | 0x80 : (part1 >>> 21) & 0x7F);
+ case 7 : this.view.setUint8(offset+6, size !== 7 ? (part1 >>> 14) | 0x80 : (part1 >>> 14) & 0x7F);
+ case 6 : this.view.setUint8(offset+5, size !== 6 ? (part1 >>> 7) | 0x80 : (part1 >>> 7) & 0x7F);
+ case 5 : this.view.setUint8(offset+4, size !== 5 ? (part1 ) | 0x80 : (part1 ) & 0x7F);
+ case 4 : this.view.setUint8(offset+3, size !== 4 ? (part0 >>> 21) | 0x80 : (part0 >>> 21) & 0x7F);
+ case 3 : this.view.setUint8(offset+2, size !== 3 ? (part0 >>> 14) | 0x80 : (part0 >>> 14) & 0x7F);
+ case 2 : this.view.setUint8(offset+1, size !== 2 ? (part0 >>> 7) | 0x80 : (part0 >>> 7) & 0x7F);
+ case 1 : this.view.setUint8(offset , size !== 1 ? (part0 ) | 0x80 : (part0 ) & 0x7F);
+ }
+ if (relative) {
+ this.offset += size;
+ return this;
+ } else {
+ return size;
+ }
+ };
+
+ /**
+ * Writes a zig-zag encoded 64bit base 128 variable-length integer.
+ * @param {number|Long} value Value to write
+ * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by the number of bytes
+ * written if omitted.
+ * @returns {!ByteBuffer|number} `this` if offset is omitted, else the actual number of bytes written.
+ * @expose
+ */
+ ByteBufferPrototype.writeVarint64ZigZag = function(value, offset) {
+ return this.writeVarint64(ByteBuffer.zigZagEncode64(value), offset);
+ };
+
+ /**
+ * Reads a 64bit base 128 variable-length integer. Requires Long.js.
+ * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by the number of bytes
+ * read 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
+ */
+ ByteBufferPrototype.readVarint64 = function(offset) {
+ var relative = typeof offset === 'undefined';
+ if (relative) offset = this.offset;
+ if (!this.noAssert) {
+ if (typeof offset !== 'number' || offset % 1 !== 0)
+ throw TypeError("Illegal offset: "+offset+" (not an integer)");
+ offset >>>= 0;
+ if (offset < 0 || offset + 1 > this.buffer.byteLength)
+ throw RangeError("Illegal offset: 0 <= "+offset+" (+"+1+") <= "+this.buffer.byteLength);
+ }
+ // ref: src/google/protobuf/io/coded_stream.cc
+ var start = offset,
+ part0 = 0,
+ part1 = 0,
+ part2 = 0,
+ b = 0;
+ b = this.view.getUint8(offset++); part0 = (b & 0x7F) ; if (b & 0x80) {
+ b = this.view.getUint8(offset++); part0 |= (b & 0x7F) << 7; if (b & 0x80) {
+ b = this.view.getUint8(offset++); part0 |= (b & 0x7F) << 14; if (b & 0x80) {
+ b = this.view.getUint8(offset++); part0 |= (b & 0x7F) << 21; if (b & 0x80) {
+ b = this.view.getUint8(offset++); part1 = (b & 0x7F) ; if (b & 0x80) {
+ b = this.view.getUint8(offset++); part1 |= (b & 0x7F) << 7; if (b & 0x80) {
+ b = this.view.getUint8(offset++); part1 |= (b & 0x7F) << 14; if (b & 0x80) {
+ b = this.view.getUint8(offset++); part1 |= (b & 0x7F) << 21; if (b & 0x80) {
+ b = this.view.getUint8(offset++); part2 = (b & 0x7F) ; if (b & 0x80) {
+ b = this.view.getUint8(offset++); part2 |= (b & 0x7F) << 7; if (b & 0x80) {
+ throw Error("Buffer overrun"); }}}}}}}}}}
+ var value = Long.fromBits(part0 | (part1 << 28), (part1 >>> 4) | (part2) << 24, false);
+ if (relative) {
+ this.offset = offset;
+ return value;
+ } else {
+ return {
+ 'value': value,
+ 'length': offset-start
+ };
+ }
+ };
+
+ /**
+ * Reads a zig-zag encoded 64bit base 128 variable-length integer. Requires Long.js.
+ * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by the number of bytes
+ * read 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
+ */
+ ByteBufferPrototype.readVarint64ZigZag = function(offset) {
+ var val = this.readVarint64(offset);
+ if (val && val['value'] instanceof Long)
+ val["value"] = ByteBuffer.zigZagDecode64(val["value"]);
+ else
+ val = ByteBuffer.zigZagDecode64(val);
+ return val;
+ };
+
+ } // Long
+
+
+ // types/strings/cstring
+
+ /**
+ * Writes a NULL-terminated UTF8 encoded string. For this to work the specified string must not contain any NULL
+ * characters itself.
+ * @param {string} str String to write
+ * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by the number of bytes
+ * contained in `str` + 1 if omitted.
+ * @returns {!ByteBuffer|number} this if offset is omitted, else the actual number of bytes written
+ * @expose
+ */
+ ByteBufferPrototype.writeCString = function(str, offset) {
+ var relative = typeof offset === 'undefined';
+ if (relative) offset = this.offset;
+ var i,
+ k = str.length;
+ if (!this.noAssert) {
+ if (typeof str !== 'string')
+ throw TypeError("Illegal str: Not a string");
+ for (i=0; i>>= 0;
+ if (offset < 0 || offset + 0 > this.buffer.byteLength)
+ throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength);
+ }
+ // UTF8 strings do not contain zero bytes in between except for the zero character, so:
+ k = utfx.calculateUTF16asUTF8(stringSource(str))[1];
+ offset += k+1;
+ var capacity12 = this.buffer.byteLength;
+ if (offset > capacity12)
+ this.resize((capacity12 *= 2) > offset ? capacity12 : offset);
+ offset -= k+1;
+ utfx.encodeUTF16toUTF8(stringSource(str), function(b) {
+ this.view.setUint8(offset++, b);
+ }.bind(this));
+ this.view.setUint8(offset++, 0);
+ if (relative) {
+ this.offset = offset;
+ return this;
+ }
+ return k;
+ };
+
+ /**
+ * Reads a NULL-terminated UTF8 encoded string. For this to work the string read must not contain any NULL characters
+ * itself.
+ * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by the number of bytes
+ * read 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.
+ * @expose
+ */
+ ByteBufferPrototype.readCString = function(offset) {
+ var relative = typeof offset === 'undefined';
+ if (relative) offset = this.offset;
+ if (!this.noAssert) {
+ if (typeof offset !== 'number' || offset % 1 !== 0)
+ throw TypeError("Illegal offset: "+offset+" (not an integer)");
+ offset >>>= 0;
+ if (offset < 0 || offset + 1 > this.buffer.byteLength)
+ throw RangeError("Illegal offset: 0 <= "+offset+" (+"+1+") <= "+this.buffer.byteLength);
+ }
+ var start = offset,
+ temp;
+ // UTF8 strings do not contain zero bytes in between except for the zero character itself, so:
+ var sd, b = -1;
+ utfx.decodeUTF8toUTF16(function() {
+ if (b === 0) return null;
+ if (offset >= this.limit)
+ throw RangeError("Illegal range: Truncated data, "+offset+" < "+this.limit);
+ return (b = this.view.getUint8(offset++)) === 0 ? null : b;
+ }.bind(this), sd = stringDestination(), true);
+ if (relative) {
+ this.offset = offset;
+ return sd();
+ } else {
+ return {
+ "string": sd(),
+ "length": offset - start
+ };
+ }
+ };
+
+ // types/strings/istring
+
+ /**
+ * Writes a length as uint32 prefixed UTF8 encoded string.
+ * @param {string} str String to write
+ * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by the number of bytes
+ * written if omitted.
+ * @returns {!ByteBuffer|number} `this` if `offset` is omitted, else the actual number of bytes written
+ * @expose
+ * @see ByteBuffer#writeVarint32
+ */
+ ByteBufferPrototype.writeIString = function(str, offset) {
+ var relative = typeof offset === 'undefined';
+ if (relative) offset = this.offset;
+ if (!this.noAssert) {
+ if (typeof str !== 'string')
+ throw TypeError("Illegal str: Not a string");
+ if (typeof offset !== 'number' || offset % 1 !== 0)
+ throw TypeError("Illegal offset: "+offset+" (not an integer)");
+ offset >>>= 0;
+ if (offset < 0 || offset + 0 > this.buffer.byteLength)
+ throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength);
+ }
+ var start = offset,
+ k;
+ k = utfx.calculateUTF16asUTF8(stringSource(str), this.noAssert)[1];
+ offset += 4+k;
+ var capacity13 = this.buffer.byteLength;
+ if (offset > capacity13)
+ this.resize((capacity13 *= 2) > offset ? capacity13 : offset);
+ offset -= 4+k;
+ this.view.setUint32(offset, k, this.littleEndian);
+ offset += 4;
+ utfx.encodeUTF16toUTF8(stringSource(str), function(b) {
+ this.view.setUint8(offset++, b);
+ }.bind(this));
+ if (offset !== start + 4 + k)
+ throw RangeError("Illegal range: Truncated data, "+offset+" == "+(offset+4+k));
+ if (relative) {
+ this.offset = offset;
+ return this;
+ }
+ return offset - start;
+ };
+
+ /**
+ * Reads a length as uint32 prefixed UTF8 encoded string.
+ * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by the number of bytes
+ * read 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.
+ * @expose
+ * @see ByteBuffer#readVarint32
+ */
+ ByteBufferPrototype.readIString = function(offset) {
+ var relative = typeof offset === 'undefined';
+ if (relative) offset = this.offset;
+ if (!this.noAssert) {
+ if (typeof offset !== 'number' || offset % 1 !== 0)
+ throw TypeError("Illegal offset: "+offset+" (not an integer)");
+ offset >>>= 0;
+ if (offset < 0 || offset + 4 > this.buffer.byteLength)
+ throw RangeError("Illegal offset: 0 <= "+offset+" (+"+4+") <= "+this.buffer.byteLength);
+ }
+ var temp = 0,
+ start = offset,
+ str;
+ temp = this.view.getUint32(offset, this.littleEndian);
+ offset += 4;
+ var k = offset + temp,
+ sd;
+ utfx.decodeUTF8toUTF16(function() {
+ return offset < k ? this.view.getUint8(offset++) : null;
+ }.bind(this), sd = stringDestination(), this.noAssert);
+ str = sd();
+ if (relative) {
+ this.offset = offset;
+ return str;
+ } else {
+ return {
+ 'string': str,
+ 'length': offset - start
+ };
+ }
+ };
+
+ // types/strings/utf8string
+
+ /**
+ * Metrics representing number of UTF8 characters. Evaluates to `c`.
+ * @type {string}
+ * @const
+ * @expose
+ */
+ ByteBuffer.METRICS_CHARS = 'c';
+
+ /**
+ * Metrics representing number of bytes. Evaluates to `b`.
+ * @type {string}
+ * @const
+ * @expose
+ */
+ ByteBuffer.METRICS_BYTES = 'b';
+
+ /**
+ * Writes an UTF8 encoded string.
+ * @param {string} str String to write
+ * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} if omitted.
+ * @returns {!ByteBuffer|number} this if offset is omitted, else the actual number of bytes written.
+ * @expose
+ */
+ ByteBufferPrototype.writeUTF8String = function(str, offset) {
+ var relative = typeof offset === 'undefined';
+ if (relative) offset = this.offset;
+ if (!this.noAssert) {
+ if (typeof offset !== 'number' || offset % 1 !== 0)
+ throw TypeError("Illegal offset: "+offset+" (not an integer)");
+ offset >>>= 0;
+ if (offset < 0 || offset + 0 > this.buffer.byteLength)
+ throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength);
+ }
+ var k;
+ var start = offset;
+ k = utfx.calculateUTF16asUTF8(stringSource(str))[1];
+ offset += k;
+ var capacity14 = this.buffer.byteLength;
+ if (offset > capacity14)
+ this.resize((capacity14 *= 2) > offset ? capacity14 : offset);
+ offset -= k;
+ utfx.encodeUTF16toUTF8(stringSource(str), function(b) {
+ this.view.setUint8(offset++, b);
+ }.bind(this));
+ if (relative) {
+ this.offset = offset;
+ return this;
+ }
+ return offset - start;
+ };
+
+ /**
+ * Writes an UTF8 encoded string. This is an alias of {@link ByteBuffer#writeUTF8String}.
+ * @function
+ * @param {string} str String to write
+ * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} if omitted.
+ * @returns {!ByteBuffer|number} this if offset is omitted, else the actual number of bytes written.
+ * @expose
+ */
+ ByteBufferPrototype.writeString = ByteBufferPrototype.writeUTF8String;
+
+ /**
+ * Calculates the number of UTF8 characters of a string. JavaScript itself uses UTF-16, so that a string's
+ * `length` property does not reflect its actual UTF8 size if it contains code points larger than 0xFFFF.
+ * @function
+ * @param {string} str String to calculate
+ * @returns {number} Number of UTF8 characters
+ * @expose
+ */
+ ByteBuffer.calculateUTF8Chars = function(str) {
+ return utfx.calculateUTF16asUTF8(stringSource(str))[0];
+ };
+
+ /**
+ * Calculates the number of UTF8 bytes of a string.
+ * @function
+ * @param {string} str String to calculate
+ * @returns {number} Number of UTF8 bytes
+ * @expose
+ */
+ ByteBuffer.calculateUTF8Bytes = function(str) {
+ return utfx.calculateUTF16asUTF8(stringSource(str))[1];
+ };
+
+ /**
+ * Reads an UTF8 encoded string.
+ * @param {number} length Number of characters or bytes to read.
+ * @param {string=} metrics Metrics specifying what `length` is meant to count. Defaults to
+ * {@link ByteBuffer.METRICS_CHARS}.
+ * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by the number of bytes
+ * read 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.
+ * @expose
+ */
+ ByteBufferPrototype.readUTF8String = function(length, metrics, offset) {
+ if (typeof metrics === 'number') {
+ offset = metrics;
+ metrics = undefined;
+ }
+ var relative = typeof offset === 'undefined';
+ if (relative) offset = this.offset;
+ if (typeof metrics === 'undefined') metrics = ByteBuffer.METRICS_CHARS;
+ if (!this.noAssert) {
+ if (typeof length !== 'number' || length % 1 !== 0)
+ throw TypeError("Illegal length: "+length+" (not an integer)");
+ length |= 0;
+ if (typeof offset !== 'number' || offset % 1 !== 0)
+ throw TypeError("Illegal offset: "+offset+" (not an integer)");
+ offset >>>= 0;
+ if (offset < 0 || offset + 0 > this.buffer.byteLength)
+ throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength);
+ }
+ var i = 0,
+ start = offset,
+ sd;
+ if (metrics === ByteBuffer.METRICS_CHARS) { // The same for node and the browser
+ sd = stringDestination();
+ utfx.decodeUTF8(function() {
+ return i < length && offset < this.limit ? this.view.getUint8(offset++) : null;
+ }.bind(this), function(cp) {
+ ++i; utfx.UTF8toUTF16(cp, sd);
+ }.bind(this));
+ if (i !== length)
+ throw RangeError("Illegal range: Truncated data, "+i+" == "+length);
+ if (relative) {
+ this.offset = offset;
+ return sd();
+ } else {
+ return {
+ "string": sd(),
+ "length": offset - start
+ };
+ }
+ } else if (metrics === ByteBuffer.METRICS_BYTES) {
+ if (!this.noAssert) {
+ if (typeof offset !== 'number' || offset % 1 !== 0)
+ throw TypeError("Illegal offset: "+offset+" (not an integer)");
+ offset >>>= 0;
+ if (offset < 0 || offset + length > this.buffer.byteLength)
+ throw RangeError("Illegal offset: 0 <= "+offset+" (+"+length+") <= "+this.buffer.byteLength);
+ }
+ var k = offset + length;
+ utfx.decodeUTF8toUTF16(function() {
+ return offset < k ? this.view.getUint8(offset++) : null;
+ }.bind(this), sd = stringDestination(), this.noAssert);
+ if (offset !== k)
+ throw RangeError("Illegal range: Truncated data, "+offset+" == "+k);
+ if (relative) {
+ this.offset = offset;
+ return sd();
+ } else {
+ return {
+ 'string': sd(),
+ 'length': offset - start
+ };
+ }
+ } else
+ throw TypeError("Unsupported metrics: "+metrics);
+ };
+
+ /**
+ * Reads an UTF8 encoded string. This is an alias of {@link ByteBuffer#readUTF8String}.
+ * @function
+ * @param {number} length Number of characters or bytes to read
+ * @param {number=} metrics Metrics specifying what `n` is meant to count. Defaults to
+ * {@link ByteBuffer.METRICS_CHARS}.
+ * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by the number of bytes
+ * read 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.
+ * @expose
+ */
+ ByteBufferPrototype.readString = ByteBufferPrototype.readUTF8String;
+
+ // types/strings/vstring
+
+ /**
+ * Writes a length as varint32 prefixed UTF8 encoded string.
+ * @param {string} str String to write
+ * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by the number of bytes
+ * written if omitted.
+ * @returns {!ByteBuffer|number} `this` if `offset` is omitted, else the actual number of bytes written
+ * @expose
+ * @see ByteBuffer#writeVarint32
+ */
+ ByteBufferPrototype.writeVString = function(str, offset) {
+ var relative = typeof offset === 'undefined';
+ if (relative) offset = this.offset;
+ if (!this.noAssert) {
+ if (typeof str !== 'string')
+ throw TypeError("Illegal str: Not a string");
+ if (typeof offset !== 'number' || offset % 1 !== 0)
+ throw TypeError("Illegal offset: "+offset+" (not an integer)");
+ offset >>>= 0;
+ if (offset < 0 || offset + 0 > this.buffer.byteLength)
+ throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength);
+ }
+ var start = offset,
+ k, l;
+ k = utfx.calculateUTF16asUTF8(stringSource(str), this.noAssert)[1];
+ l = ByteBuffer.calculateVarint32(k);
+ offset += l+k;
+ var capacity15 = this.buffer.byteLength;
+ if (offset > capacity15)
+ this.resize((capacity15 *= 2) > offset ? capacity15 : offset);
+ offset -= l+k;
+ offset += this.writeVarint32(k, offset);
+ utfx.encodeUTF16toUTF8(stringSource(str), function(b) {
+ this.view.setUint8(offset++, b);
+ }.bind(this));
+ if (offset !== start+k+l)
+ throw RangeError("Illegal range: Truncated data, "+offset+" == "+(offset+k+l));
+ if (relative) {
+ this.offset = offset;
+ return this;
+ }
+ return offset - start;
+ };
+
+ /**
+ * Reads a length as varint32 prefixed UTF8 encoded string.
+ * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by the number of bytes
+ * read 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.
+ * @expose
+ * @see ByteBuffer#readVarint32
+ */
+ ByteBufferPrototype.readVString = function(offset) {
+ var relative = typeof offset === 'undefined';
+ if (relative) offset = this.offset;
+ if (!this.noAssert) {
+ if (typeof offset !== 'number' || offset % 1 !== 0)
+ throw TypeError("Illegal offset: "+offset+" (not an integer)");
+ offset >>>= 0;
+ if (offset < 0 || offset + 1 > this.buffer.byteLength)
+ throw RangeError("Illegal offset: 0 <= "+offset+" (+"+1+") <= "+this.buffer.byteLength);
+ }
+ var temp = this.readVarint32(offset),
+ start = offset,
+ str;
+ offset += temp['length'];
+ temp = temp['value'];
+ var k = offset + temp,
+ sd = stringDestination();
+ utfx.decodeUTF8toUTF16(function() {
+ return offset < k ? this.view.getUint8(offset++) : null;
+ }.bind(this), sd, this.noAssert);
+ str = sd();
+ if (relative) {
+ this.offset = offset;
+ return str;
+ } else {
+ return {
+ 'string': str,
+ 'length': offset - start
+ };
+ }
+ };
+
+
+ /**
+ * Appends some data to this ByteBuffer. This will overwrite any contents behind the specified offset up to the appended
+ * data's length.
+ * @param {!ByteBuffer|!ArrayBuffer|!Uint8Array|string} source Data to append. If `source` is a ByteBuffer, its offsets
+ * will be modified according to the performed read operation.
+ * @param {(string|number)=} encoding Encoding if `data` is a string ("base64", "hex", "binary", defaults to "utf8")
+ * @param {number=} offset Offset to append at. Will use and increase {@link ByteBuffer#offset} by the number of bytes
+ * read if omitted.
+ * @returns {!ByteBuffer} this
+ * @expose
+ * @example A relative `<01 02>03.append(<04 05>)` will result in `<01 02 04 05>, 04 05|`
+ * @example An absolute `<01 02>03.append(04 05>, 1)` will result in `<01 04>05, 04 05|`
+ */
+ ByteBufferPrototype.append = function(source, encoding, offset) {
+ if (typeof encoding === 'number' || typeof encoding !== 'string') {
+ offset = encoding;
+ encoding = undefined;
+ }
+ var relative = typeof offset === 'undefined';
+ if (relative) offset = this.offset;
+ if (!this.noAssert) {
+ if (typeof offset !== 'number' || offset % 1 !== 0)
+ throw TypeError("Illegal offset: "+offset+" (not an integer)");
+ offset >>>= 0;
+ if (offset < 0 || offset + 0 > this.buffer.byteLength)
+ throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength);
+ }
+ if (!(source instanceof ByteBuffer))
+ source = ByteBuffer.wrap(source, encoding);
+ var length = source.limit - source.offset;
+ if (length <= 0) return this; // Nothing to append
+ offset += length;
+ var capacity16 = this.buffer.byteLength;
+ if (offset > capacity16)
+ this.resize((capacity16 *= 2) > offset ? capacity16 : offset);
+ offset -= length;
+ new Uint8Array(this.buffer, offset).set(new Uint8Array(source.buffer).subarray(source.offset, source.limit));
+ source.offset += length;
+ if (relative) this.offset += length;
+ return this;
+ };
+
+ /**
+ * Appends this ByteBuffer's contents to another ByteBuffer. This will overwrite any contents at and after the
+ specified offset up to the length of this ByteBuffer's data.
+ * @param {!ByteBuffer} target Target ByteBuffer
+ * @param {number=} offset Offset to append to. Will use and increase {@link ByteBuffer#offset} by the number of bytes
+ * read if omitted.
+ * @returns {!ByteBuffer} this
+ * @expose
+ * @see ByteBuffer#append
+ */
+ ByteBufferPrototype.appendTo = function(target, offset) {
+ target.append(this, offset);
+ return this;
+ };
+
+ /**
+ * Enables or disables assertions of argument types and offsets. Assertions are enabled by default but you can opt to
+ * disable them if your code already makes sure that everything is valid.
+ * @param {boolean} assert `true` to enable assertions, otherwise `false`
+ * @returns {!ByteBuffer} this
+ * @expose
+ */
+ ByteBufferPrototype.assert = function(assert) {
+ this.noAssert = !assert;
+ return this;
+ };
+
+ /**
+ * Gets the capacity of this ByteBuffer's backing buffer.
+ * @returns {number} Capacity of the backing buffer
+ * @expose
+ */
+ ByteBufferPrototype.capacity = function() {
+ return this.buffer.byteLength;
+ };
+
+ /**
+ * Clears this ByteBuffer's offsets by setting {@link ByteBuffer#offset} to `0` and {@link ByteBuffer#limit} to the
+ * backing buffer's capacity. Discards {@link ByteBuffer#markedOffset}.
+ * @returns {!ByteBuffer} this
+ * @expose
+ */
+ ByteBufferPrototype.clear = function() {
+ this.offset = 0;
+ this.limit = this.buffer.byteLength;
+ this.markedOffset = -1;
+ return this;
+ };
+
+ /**
+ * Creates a cloned instance of this ByteBuffer, preset with this ByteBuffer's values for {@link ByteBuffer#offset},
+ * {@link ByteBuffer#markedOffset} and {@link ByteBuffer#limit}.
+ * @param {boolean=} copy Whether to copy the backing buffer or to return another view on the same, defaults to `false`
+ * @returns {!ByteBuffer} Cloned instance
+ * @expose
+ */
+ ByteBufferPrototype.clone = function(copy) {
+ var bb = new ByteBuffer(0, this.littleEndian, this.noAssert);
+ if (copy) {
+ var buffer = new ArrayBuffer(this.buffer.byteLength);
+ new Uint8Array(buffer).set(this.buffer);
+ bb.buffer = buffer;
+ bb.view = new DataView(buffer);
+ } else {
+ bb.buffer = this.buffer;
+ bb.view = this.view;
+ }
+ bb.offset = this.offset;
+ bb.markedOffset = this.markedOffset;
+ bb.limit = this.limit;
+ return bb;
+ };
+
+ /**
+ * Compacts this ByteBuffer to be backed by a {@link ByteBuffer#buffer} of its contents' length. Contents are the bytes
+ * between {@link ByteBuffer#offset} and {@link ByteBuffer#limit}. Will set `offset = 0` and `limit = capacity` and
+ * adapt {@link ByteBuffer#markedOffset} to the same relative position if set.
+ * @param {number=} begin Offset to start at, defaults to {@link ByteBuffer#offset}
+ * @param {number=} end Offset to end at, defaults to {@link ByteBuffer#limit}
+ * @returns {!ByteBuffer} this
+ * @expose
+ */
+ ByteBufferPrototype.compact = function(begin, end) {
+ if (typeof begin === 'undefined') begin = this.offset;
+ if (typeof end === 'undefined') end = this.limit;
+ if (!this.noAssert) {
+ if (typeof begin !== 'number' || begin % 1 !== 0)
+ throw TypeError("Illegal begin: Not an integer");
+ begin >>>= 0;
+ if (typeof end !== 'number' || end % 1 !== 0)
+ throw TypeError("Illegal end: Not an integer");
+ end >>>= 0;
+ if (begin < 0 || begin > end || end > this.buffer.byteLength)
+ throw RangeError("Illegal range: 0 <= "+begin+" <= "+end+" <= "+this.buffer.byteLength);
+ }
+ if (begin === 0 && end === this.buffer.byteLength)
+ return this; // Already compacted
+ var len = end - begin;
+ if (len === 0) {
+ this.buffer = EMPTY_BUFFER;
+ this.view = null;
+ if (this.markedOffset >= 0) this.markedOffset -= begin;
+ this.offset = 0;
+ this.limit = 0;
+ return this;
+ }
+ var buffer = new ArrayBuffer(len);
+ new Uint8Array(buffer).set(new Uint8Array(this.buffer).subarray(begin, end));
+ this.buffer = buffer;
+ this.view = new DataView(buffer);
+ if (this.markedOffset >= 0) this.markedOffset -= begin;
+ this.offset = 0;
+ this.limit = len;
+ return this;
+ };
+
+ /**
+ * Creates a copy of this ByteBuffer's contents. Contents are the bytes between {@link ByteBuffer#offset} and
+ * {@link ByteBuffer#limit}.
+ * @param {number=} begin Begin offset, defaults to {@link ByteBuffer#offset}.
+ * @param {number=} end End offset, defaults to {@link ByteBuffer#limit}.
+ * @returns {!ByteBuffer} Copy
+ * @expose
+ */
+ ByteBufferPrototype.copy = function(begin, end) {
+ if (typeof begin === 'undefined') begin = this.offset;
+ if (typeof end === 'undefined') end = this.limit;
+ if (!this.noAssert) {
+ if (typeof begin !== 'number' || begin % 1 !== 0)
+ throw TypeError("Illegal begin: Not an integer");
+ begin >>>= 0;
+ if (typeof end !== 'number' || end % 1 !== 0)
+ throw TypeError("Illegal end: Not an integer");
+ end >>>= 0;
+ if (begin < 0 || begin > end || end > this.buffer.byteLength)
+ throw RangeError("Illegal range: 0 <= "+begin+" <= "+end+" <= "+this.buffer.byteLength);
+ }
+ if (begin === end)
+ return new ByteBuffer(0, this.littleEndian, this.noAssert);
+ var capacity = end - begin,
+ bb = new ByteBuffer(capacity, this.littleEndian, this.noAssert);
+ bb.offset = 0;
+ bb.limit = capacity;
+ if (bb.markedOffset >= 0) bb.markedOffset -= begin;
+ this.copyTo(bb, 0, begin, end);
+ return bb;
+ };
+
+ /**
+ * Copies this ByteBuffer's contents to another ByteBuffer. Contents are the bytes between {@link ByteBuffer#offset} and
+ * {@link ByteBuffer#limit}.
+ * @param {!ByteBuffer} target Target ByteBuffer
+ * @param {number=} targetOffset Offset to copy to. Will use and increase the target's {@link ByteBuffer#offset}
+ * by the number of bytes copied if omitted.
+ * @param {number=} sourceOffset Offset to start copying from. Will use and increase {@link ByteBuffer#offset} by the
+ * number of bytes copied if omitted.
+ * @param {number=} sourceLimit Offset to end copying from, defaults to {@link ByteBuffer#limit}
+ * @returns {!ByteBuffer} this
+ * @expose
+ */
+ ByteBufferPrototype.copyTo = function(target, targetOffset, sourceOffset, sourceLimit) {
+ var relative,
+ targetRelative;
+ if (!this.noAssert) {
+ if (!ByteBuffer.isByteBuffer(target))
+ throw TypeError("Illegal target: Not a ByteBuffer");
+ }
+ targetOffset = (targetRelative = typeof targetOffset === 'undefined') ? target.offset : targetOffset | 0;
+ sourceOffset = (relative = typeof sourceOffset === 'undefined') ? this.offset : sourceOffset | 0;
+ sourceLimit = typeof sourceLimit === 'undefined' ? this.limit : sourceLimit | 0;
+
+ if (targetOffset < 0 || targetOffset > target.buffer.byteLength)
+ throw RangeError("Illegal target range: 0 <= "+targetOffset+" <= "+target.buffer.byteLength);
+ if (sourceOffset < 0 || sourceLimit > this.buffer.byteLength)
+ throw RangeError("Illegal source range: 0 <= "+sourceOffset+" <= "+this.buffer.byteLength);
+
+ var len = sourceLimit - sourceOffset;
+ if (len === 0)
+ return target; // Nothing to copy
+
+ target.ensureCapacity(targetOffset + len);
+
+ new Uint8Array(target.buffer).set(new Uint8Array(this.buffer).subarray(sourceOffset, sourceLimit), targetOffset);
+
+ if (relative) this.offset += len;
+ if (targetRelative) target.offset += len;
+
+ return this;
+ };
+
+ /**
+ * Makes sure that this ByteBuffer is backed by a {@link ByteBuffer#buffer} of at least the specified capacity. If the
+ * current capacity is exceeded, it will be doubled. If double the current capacity is less than the required capacity,
+ * the required capacity will be used instead.
+ * @param {number} capacity Required capacity
+ * @returns {!ByteBuffer} this
+ * @expose
+ */
+ ByteBufferPrototype.ensureCapacity = function(capacity) {
+ var current = this.buffer.byteLength;
+ if (current < capacity)
+ return this.resize((current *= 2) > capacity ? current : capacity);
+ return this;
+ };
+
+ /**
+ * Overwrites this ByteBuffer's contents with the specified value. Contents are the bytes between
+ * {@link ByteBuffer#offset} and {@link ByteBuffer#limit}.
+ * @param {number|string} value Byte value to fill with. If given as a string, the first character is used.
+ * @param {number=} begin Begin offset. Will use and increase {@link ByteBuffer#offset} by the number of bytes
+ * written if omitted. defaults to {@link ByteBuffer#offset}.
+ * @param {number=} end End offset, defaults to {@link ByteBuffer#limit}.
+ * @returns {!ByteBuffer} this
+ * @expose
+ * @example `someByteBuffer.clear().fill(0)` fills the entire backing buffer with zeroes
+ */
+ ByteBufferPrototype.fill = function(value, begin, end) {
+ var relative = typeof begin === 'undefined';
+ if (relative) begin = this.offset;
+ if (typeof value === 'string' && value.length > 0)
+ value = value.charCodeAt(0);
+ if (typeof begin === 'undefined') begin = this.offset;
+ if (typeof end === 'undefined') end = this.limit;
+ if (!this.noAssert) {
+ if (typeof value !== 'number' || value % 1 !== 0)
+ throw TypeError("Illegal value: "+value+" (not an integer)");
+ value |= 0;
+ if (typeof begin !== 'number' || begin % 1 !== 0)
+ throw TypeError("Illegal begin: Not an integer");
+ begin >>>= 0;
+ if (typeof end !== 'number' || end % 1 !== 0)
+ throw TypeError("Illegal end: Not an integer");
+ end >>>= 0;
+ if (begin < 0 || begin > end || end > this.buffer.byteLength)
+ throw RangeError("Illegal range: 0 <= "+begin+" <= "+end+" <= "+this.buffer.byteLength);
+ }
+ if (begin >= end)
+ return this; // Nothing to fill
+ while (begin < end) this.view.setUint8(begin++, value);
+ if (relative) this.offset = begin;
+ return this;
+ };
+
+ /**
+ * Makes this ByteBuffer ready for a new sequence of write or relative read operations. Sets `limit = offset` and
+ * `offset = 0`. Make sure always to flip a ByteBuffer when all relative read or write operations are complete.
+ * @returns {!ByteBuffer} this
+ * @expose
+ */
+ ByteBufferPrototype.flip = function() {
+ this.limit = this.offset;
+ this.offset = 0;
+ return this;
+ };
+ /**
+ * Marks an offset on this ByteBuffer to be used later.
+ * @param {number=} offset Offset to mark. Defaults to {@link ByteBuffer#offset}.
+ * @returns {!ByteBuffer} this
+ * @throws {TypeError} If `offset` is not a valid number
+ * @throws {RangeError} If `offset` is out of bounds
+ * @see ByteBuffer#reset
+ * @expose
+ */
+ ByteBufferPrototype.mark = function(offset) {
+ offset = typeof offset === 'undefined' ? this.offset : offset;
+ if (!this.noAssert) {
+ if (typeof offset !== 'number' || offset % 1 !== 0)
+ throw TypeError("Illegal offset: "+offset+" (not an integer)");
+ offset >>>= 0;
+ if (offset < 0 || offset + 0 > this.buffer.byteLength)
+ throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength);
+ }
+ this.markedOffset = offset;
+ return this;
+ };
+ /**
+ * Sets the byte order.
+ * @param {boolean} littleEndian `true` for little endian byte order, `false` for big endian
+ * @returns {!ByteBuffer} this
+ * @expose
+ */
+ ByteBufferPrototype.order = function(littleEndian) {
+ if (!this.noAssert) {
+ if (typeof littleEndian !== 'boolean')
+ throw TypeError("Illegal littleEndian: Not a boolean");
+ }
+ this.littleEndian = !!littleEndian;
+ return this;
+ };
+
+ /**
+ * Switches (to) little endian byte order.
+ * @param {boolean=} littleEndian Defaults to `true`, otherwise uses big endian
+ * @returns {!ByteBuffer} this
+ * @expose
+ */
+ ByteBufferPrototype.LE = function(littleEndian) {
+ this.littleEndian = typeof littleEndian !== 'undefined' ? !!littleEndian : true;
+ return this;
+ };
+
+ /**
+ * Switches (to) big endian byte order.
+ * @param {boolean=} bigEndian Defaults to `true`, otherwise uses little endian
+ * @returns {!ByteBuffer} this
+ * @expose
+ */
+ ByteBufferPrototype.BE = function(bigEndian) {
+ this.littleEndian = typeof bigEndian !== 'undefined' ? !bigEndian : false;
+ return this;
+ };
+ /**
+ * Prepends some data to this ByteBuffer. This will overwrite any contents before the specified offset up to the
+ * prepended data's length. If there is not enough space available before the specified `offset`, the backing buffer
+ * will be resized and its contents moved accordingly.
+ * @param {!ByteBuffer|string|!ArrayBuffer} source Data to prepend. If `source` is a ByteBuffer, its offset will be
+ * modified according to the performed read operation.
+ * @param {(string|number)=} encoding Encoding if `data` is a string ("base64", "hex", "binary", defaults to "utf8")
+ * @param {number=} offset Offset to prepend at. Will use and decrease {@link ByteBuffer#offset} by the number of bytes
+ * prepended if omitted.
+ * @returns {!ByteBuffer} this
+ * @expose
+ * @example A relative `00<01 02 03>.prepend(<04 05>)` results in `<04 05 01 02 03>, 04 05|`
+ * @example An absolute `00<01 02 03>.prepend(<04 05>, 2)` results in `04<05 02 03>, 04 05|`
+ */
+ ByteBufferPrototype.prepend = function(source, encoding, offset) {
+ if (typeof encoding === 'number' || typeof encoding !== 'string') {
+ offset = encoding;
+ encoding = undefined;
+ }
+ var relative = typeof offset === 'undefined';
+ if (relative) offset = this.offset;
+ if (!this.noAssert) {
+ if (typeof offset !== 'number' || offset % 1 !== 0)
+ throw TypeError("Illegal offset: "+offset+" (not an integer)");
+ offset >>>= 0;
+ if (offset < 0 || offset + 0 > this.buffer.byteLength)
+ throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength);
+ }
+ if (!(source instanceof ByteBuffer))
+ source = ByteBuffer.wrap(source, encoding);
+ var len = source.limit - source.offset;
+ if (len <= 0) return this; // Nothing to prepend
+ var diff = len - offset;
+ var arrayView;
+ if (diff > 0) { // Not enough space before offset, so resize + move
+ var buffer = new ArrayBuffer(this.buffer.byteLength + diff);
+ arrayView = new Uint8Array(buffer);
+ arrayView.set(new Uint8Array(this.buffer).subarray(offset, this.buffer.byteLength), len);
+ this.buffer = buffer;
+ this.view = new DataView(buffer);
+ this.offset += diff;
+ if (this.markedOffset >= 0) this.markedOffset += diff;
+ this.limit += diff;
+ offset += diff;
+ } else {
+ arrayView = new Uint8Array(this.buffer);
+ }
+ arrayView.set(new Uint8Array(source.buffer).subarray(source.offset, source.limit), offset - len);
+ source.offset = source.limit;
+ if (relative)
+ this.offset -= len;
+ return this;
+ };
+
+ /**
+ * Prepends this ByteBuffer to another ByteBuffer. This will overwrite any contents before the specified offset up to the
+ * prepended data's length. If there is not enough space available before the specified `offset`, the backing buffer
+ * will be resized and its contents moved accordingly.
+ * @param {!ByteBuffer} target Target ByteBuffer
+ * @param {number=} offset Offset to prepend at. Will use and decrease {@link ByteBuffer#offset} by the number of bytes
+ * prepended if omitted.
+ * @returns {!ByteBuffer} this
+ * @expose
+ * @see ByteBuffer#prepend
+ */
+ ByteBufferPrototype.prependTo = function(target, offset) {
+ target.prepend(this, offset);
+ return this;
+ };
+ /**
+ * Prints debug information about this ByteBuffer's contents.
+ * @param {function(string)=} out Output function to call, defaults to console.log
+ * @expose
+ */
+ ByteBufferPrototype.printDebug = function(out) {
+ if (typeof out !== 'function') out = console.log.bind(console);
+ out(
+ this.toString()+"\n"+
+ "-------------------------------------------------------------------\n"+
+ this.toDebug(/* columns */ true)
+ );
+ };
+
+ /**
+ * Gets the number of remaining readable bytes. Contents are the bytes between {@link ByteBuffer#offset} and
+ * {@link ByteBuffer#limit}, so this returns `limit - offset`.
+ * @returns {number} Remaining readable bytes. May be negative if `offset > limit`.
+ * @expose
+ */
+ ByteBufferPrototype.remaining = function() {
+ return this.limit - this.offset;
+ };
+ /**
+ * Resets this ByteBuffer's {@link ByteBuffer#offset}. If an offset has been marked through {@link ByteBuffer#mark}
+ * before, `offset` will be set to {@link ByteBuffer#markedOffset}, which will then be discarded. If no offset has been
+ * marked, sets `offset = 0`.
+ * @returns {!ByteBuffer} this
+ * @see ByteBuffer#mark
+ * @expose
+ */
+ ByteBufferPrototype.reset = function() {
+ if (this.markedOffset >= 0) {
+ this.offset = this.markedOffset;
+ this.markedOffset = -1;
+ } else {
+ this.offset = 0;
+ }
+ return this;
+ };
+ /**
+ * Resizes this ByteBuffer to be backed by a buffer of at least the given capacity. Will do nothing if already that
+ * large or larger.
+ * @param {number} capacity Capacity required
+ * @returns {!ByteBuffer} this
+ * @throws {TypeError} If `capacity` is not a number
+ * @throws {RangeError} If `capacity < 0`
+ * @expose
+ */
+ ByteBufferPrototype.resize = function(capacity) {
+ if (!this.noAssert) {
+ if (typeof capacity !== 'number' || capacity % 1 !== 0)
+ throw TypeError("Illegal capacity: "+capacity+" (not an integer)");
+ capacity |= 0;
+ if (capacity < 0)
+ throw RangeError("Illegal capacity: 0 <= "+capacity);
+ }
+ if (this.buffer.byteLength < capacity) {
+ var buffer = new ArrayBuffer(capacity);
+ new Uint8Array(buffer).set(new Uint8Array(this.buffer));
+ this.buffer = buffer;
+ this.view = new DataView(buffer);
+ }
+ return this;
+ };
+ /**
+ * Reverses this ByteBuffer's contents.
+ * @param {number=} begin Offset to start at, defaults to {@link ByteBuffer#offset}
+ * @param {number=} end Offset to end at, defaults to {@link ByteBuffer#limit}
+ * @returns {!ByteBuffer} this
+ * @expose
+ */
+ ByteBufferPrototype.reverse = function(begin, end) {
+ if (typeof begin === 'undefined') begin = this.offset;
+ if (typeof end === 'undefined') end = this.limit;
+ if (!this.noAssert) {
+ if (typeof begin !== 'number' || begin % 1 !== 0)
+ throw TypeError("Illegal begin: Not an integer");
+ begin >>>= 0;
+ if (typeof end !== 'number' || end % 1 !== 0)
+ throw TypeError("Illegal end: Not an integer");
+ end >>>= 0;
+ if (begin < 0 || begin > end || end > this.buffer.byteLength)
+ throw RangeError("Illegal range: 0 <= "+begin+" <= "+end+" <= "+this.buffer.byteLength);
+ }
+ if (begin === end)
+ return this; // Nothing to reverse
+ Array.prototype.reverse.call(new Uint8Array(this.buffer).subarray(begin, end));
+ this.view = new DataView(this.buffer); // FIXME: Why exactly is this necessary?
+ return this;
+ };
+ /**
+ * Skips the next `length` bytes. This will just advance
+ * @param {number} length Number of bytes to skip. May also be negative to move the offset back.
+ * @returns {!ByteBuffer} this
+ * @expose
+ */
+ ByteBufferPrototype.skip = function(length) {
+ if (!this.noAssert) {
+ if (typeof length !== 'number' || length % 1 !== 0)
+ throw TypeError("Illegal length: "+length+" (not an integer)");
+ length |= 0;
+ }
+ var offset = this.offset + length;
+ if (!this.noAssert) {
+ if (offset < 0 || offset > this.buffer.byteLength)
+ throw RangeError("Illegal length: 0 <= "+this.offset+" + "+length+" <= "+this.buffer.byteLength);
+ }
+ this.offset = offset;
+ return this;
+ };
+
+ /**
+ * Slices this ByteBuffer by creating a cloned instance with `offset = begin` and `limit = end`.
+ * @param {number=} begin Begin offset, defaults to {@link ByteBuffer#offset}.
+ * @param {number=} end End offset, defaults to {@link ByteBuffer#limit}.
+ * @returns {!ByteBuffer} Clone of this ByteBuffer with slicing applied, backed by the same {@link ByteBuffer#buffer}
+ * @expose
+ */
+ ByteBufferPrototype.slice = function(begin, end) {
+ if (typeof begin === 'undefined') begin = this.offset;
+ if (typeof end === 'undefined') end = this.limit;
+ if (!this.noAssert) {
+ if (typeof begin !== 'number' || begin % 1 !== 0)
+ throw TypeError("Illegal begin: Not an integer");
+ begin >>>= 0;
+ if (typeof end !== 'number' || end % 1 !== 0)
+ throw TypeError("Illegal end: Not an integer");
+ end >>>= 0;
+ if (begin < 0 || begin > end || end > this.buffer.byteLength)
+ throw RangeError("Illegal range: 0 <= "+begin+" <= "+end+" <= "+this.buffer.byteLength);
+ }
+ var bb = this.clone();
+ bb.offset = begin;
+ bb.limit = end;
+ return bb;
+ };
+ /**
+ * Returns a copy of the backing buffer that contains this ByteBuffer's contents. Contents are the bytes between
+ * {@link ByteBuffer#offset} and {@link ByteBuffer#limit}. Will transparently {@link ByteBuffer#flip} this
+ * ByteBuffer if `offset > limit` but the actual offsets remain untouched.
+ * @param {boolean=} forceCopy If `true` returns a copy, otherwise returns a view referencing the same memory if
+ * possible. Defaults to `false`
+ * @returns {!ArrayBuffer} Contents as an ArrayBuffer
+ * @expose
+ */
+ ByteBufferPrototype.toBuffer = function(forceCopy) {
+ var offset = this.offset,
+ limit = this.limit;
+ if (offset > limit) {
+ var t = offset;
+ offset = limit;
+ limit = t;
+ }
+ if (!this.noAssert) {
+ if (typeof offset !== 'number' || offset % 1 !== 0)
+ throw TypeError("Illegal offset: Not an integer");
+ offset >>>= 0;
+ if (typeof limit !== 'number' || limit % 1 !== 0)
+ throw TypeError("Illegal limit: Not an integer");
+ limit >>>= 0;
+ if (offset < 0 || offset > limit || limit > this.buffer.byteLength)
+ throw RangeError("Illegal range: 0 <= "+offset+" <= "+limit+" <= "+this.buffer.byteLength);
+ }
+ // NOTE: It's not possible to have another ArrayBuffer reference the same memory as the backing buffer. This is
+ // possible with Uint8Array#subarray only, but we have to return an ArrayBuffer by contract. So:
+ if (!forceCopy && offset === 0 && limit === this.buffer.byteLength) {
+ return this.buffer;
+ }
+ if (offset === limit) {
+ return EMPTY_BUFFER;
+ }
+ var buffer = new ArrayBuffer(limit - offset);
+ new Uint8Array(buffer).set(new Uint8Array(this.buffer).subarray(offset, limit), 0);
+ return buffer;
+ };
+
+ /**
+ * Returns a raw buffer compacted to contain this ByteBuffer's contents. Contents are the bytes between
+ * {@link ByteBuffer#offset} and {@link ByteBuffer#limit}. Will transparently {@link ByteBuffer#flip} this
+ * ByteBuffer if `offset > limit` but the actual offsets remain untouched. This is an alias of
+ * {@link ByteBuffer#toBuffer}.
+ * @function
+ * @param {boolean=} forceCopy If `true` returns a copy, otherwise returns a view referencing the same memory.
+ * Defaults to `false`
+ * @returns {!ArrayBuffer} Contents as an ArrayBuffer
+ * @expose
+ */
+ ByteBufferPrototype.toArrayBuffer = ByteBufferPrototype.toBuffer;
+
+
+ /**
+ * Converts the ByteBuffer's contents to a string.
+ * @param {string=} encoding Output encoding. Returns an informative string representation if omitted but also allows
+ * direct conversion to "utf8", "hex", "base64" and "binary" encoding. "debug" returns a hex representation with
+ * highlighted offsets.
+ * @param {number=} begin Offset to begin at, defaults to {@link ByteBuffer#offset}
+ * @param {number=} end Offset to end at, defaults to {@link ByteBuffer#limit}
+ * @returns {string} String representation
+ * @throws {Error} If `encoding` is invalid
+ * @expose
+ */
+ ByteBufferPrototype.toString = function(encoding, begin, end) {
+ if (typeof encoding === 'undefined')
+ return "ByteBufferAB(offset="+this.offset+",markedOffset="+this.markedOffset+",limit="+this.limit+",capacity="+this.capacity()+")";
+ if (typeof encoding === 'number')
+ encoding = "utf8",
+ begin = encoding,
+ end = begin;
+ switch (encoding) {
+ case "utf8":
+ return this.toUTF8(begin, end);
+ case "base64":
+ return this.toBase64(begin, end);
+ case "hex":
+ return this.toHex(begin, end);
+ case "binary":
+ return this.toBinary(begin, end);
+ case "debug":
+ return this.toDebug();
+ case "columns":
+ return this.toColumns();
+ default:
+ throw Error("Unsupported encoding: "+encoding);
+ }
+ };
+
+ // lxiv-embeddable
+
+ /**
+ * lxiv-embeddable (c) 2014 Daniel Wirtz
+ * Released under the Apache License, Version 2.0
+ * see: https://github.com/dcodeIO/lxiv for details
+ */
+ var lxiv = function() {
+ "use strict";
+
+ /**
+ * lxiv namespace.
+ * @type {!Object.}
+ * @exports lxiv
+ */
+ var lxiv = {};
+
+ /**
+ * Character codes for output.
+ * @type {!Array.}
+ * @inner
+ */
+ var aout = [
+ 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80,
+ 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 97, 98, 99, 100, 101, 102,
+ 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118,
+ 119, 120, 121, 122, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 43, 47
+ ];
+
+ /**
+ * Character codes for input.
+ * @type {!Array.}
+ * @inner
+ */
+ var ain = [];
+ for (var i=0, k=aout.length; i>2)&0x3f]);
+ t = (b&0x3)<<4;
+ if ((b = src()) !== null) {
+ t |= (b>>4)&0xf;
+ dst(aout[(t|((b>>4)&0xf))&0x3f]);
+ t = (b&0xf)<<2;
+ if ((b = src()) !== null)
+ dst(aout[(t|((b>>6)&0x3))&0x3f]),
+ dst(aout[b&0x3f]);
+ else
+ dst(aout[t&0x3f]),
+ dst(61);
+ } else
+ dst(aout[t&0x3f]),
+ dst(61),
+ dst(61);
+ }
+ };
+
+ /**
+ * Decodes base64 char codes to bytes.
+ * @param {!function():number|null} src Characters source as a function returning the next char code respectively
+ * `null` if there are no more characters left.
+ * @param {!function(number)} dst Bytes destination as a function successively called with the next byte.
+ * @throws {Error} If a character code is invalid
+ */
+ lxiv.decode = function(src, dst) {
+ var c, t1, t2;
+ function fail(c) {
+ throw Error("Illegal character code: "+c);
+ }
+ while ((c = src()) !== null) {
+ t1 = ain[c];
+ if (typeof t1 === 'undefined') fail(c);
+ if ((c = src()) !== null) {
+ t2 = ain[c];
+ if (typeof t2 === 'undefined') fail(c);
+ dst((t1<<2)>>>0|(t2&0x30)>>4);
+ if ((c = src()) !== null) {
+ t1 = ain[c];
+ if (typeof t1 === 'undefined')
+ if (c === 61) break; else fail(c);
+ dst(((t2&0xf)<<4)>>>0|(t1&0x3c)>>2);
+ if ((c = src()) !== null) {
+ t2 = ain[c];
+ if (typeof t2 === 'undefined')
+ if (c === 61) break; else fail(c);
+ dst(((t1&0x3)<<6)>>>0|t2);
+ }
+ }
+ }
+ }
+ };
+
+ /**
+ * Tests if a string is valid base64.
+ * @param {string} str String to test
+ * @returns {boolean} `true` if valid, otherwise `false`
+ */
+ lxiv.test = function(str) {
+ return /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(str);
+ };
+
+ return lxiv;
+ }();
+
+ // encodings/base64
+
+ /**
+ * Encodes this ByteBuffer's contents to a base64 encoded string.
+ * @param {number=} begin Offset to begin at, defaults to {@link ByteBuffer#offset}.
+ * @param {number=} end Offset to end at, defaults to {@link ByteBuffer#limit}.
+ * @returns {string} Base64 encoded string
+ * @expose
+ */
+ ByteBufferPrototype.toBase64 = function(begin, end) {
+ if (typeof begin === 'undefined')
+ begin = this.offset;
+ if (typeof end === 'undefined')
+ end = this.limit;
+ if (!this.noAssert) {
+ if (typeof begin !== 'number' || begin % 1 !== 0)
+ throw TypeError("Illegal begin: Not an integer");
+ begin >>>= 0;
+ if (typeof end !== 'number' || end % 1 !== 0)
+ throw TypeError("Illegal end: Not an integer");
+ end >>>= 0;
+ if (begin < 0 || begin > end || end > this.buffer.byteLength)
+ throw RangeError("Illegal range: 0 <= "+begin+" <= "+end+" <= "+this.buffer.byteLength);
+ }
+ var sd; lxiv.encode(function() {
+ return begin < end ? this.view.getUint8(begin++) : null;
+ }.bind(this), sd = stringDestination());
+ return sd();
+ };
+
+ /**
+ * Decodes a base64 encoded string to a ByteBuffer.
+ * @param {string} str String to decode
+ * @param {boolean=} littleEndian Whether to use little or big endian byte order. Defaults to
+ * {@link ByteBuffer.DEFAULT_ENDIAN}.
+ * @param {boolean=} noAssert Whether to skip assertions of offsets and values. Defaults to
+ * {@link ByteBuffer.DEFAULT_NOASSERT}.
+ * @returns {!ByteBuffer} ByteBuffer
+ * @expose
+ */
+ ByteBuffer.fromBase64 = function(str, littleEndian, noAssert) {
+ if (!noAssert) {
+ if (typeof str !== 'string')
+ throw TypeError("Illegal str: Not a string");
+ if (str.length % 4 !== 0)
+ throw TypeError("Illegal str: Length not a multiple of 4");
+ }
+ var bb = new ByteBuffer(str.length/4*3, littleEndian, noAssert),
+ i = 0;
+ lxiv.decode(stringSource(str), function(b) {
+ bb.view.setUint8(i++, b);
+ });
+ bb.limit = i;
+ return bb;
+ };
+
+ /**
+ * Encodes a binary string to base64 like `window.btoa` does.
+ * @param {string} str Binary string
+ * @returns {string} Base64 encoded string
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/Window.btoa
+ * @expose
+ */
+ ByteBuffer.btoa = function(str) {
+ return ByteBuffer.fromBinary(str).toBase64();
+ };
+
+ /**
+ * Decodes a base64 encoded string to binary like `window.atob` does.
+ * @param {string} b64 Base64 encoded string
+ * @returns {string} Binary string
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/Window.atob
+ * @expose
+ */
+ ByteBuffer.atob = function(b64) {
+ return ByteBuffer.fromBase64(b64).toBinary();
+ };
+
+ // encodings/binary
+
+ /**
+ * Encodes this ByteBuffer to a binary encoded string, that is using only characters 0x00-0xFF as bytes.
+ * @param {number=} begin Offset to begin at. Defaults to {@link ByteBuffer#offset}.
+ * @param {number=} end Offset to end at. Defaults to {@link ByteBuffer#limit}.
+ * @returns {string} Binary encoded string
+ * @throws {RangeError} If `offset > limit`
+ * @expose
+ */
+ ByteBufferPrototype.toBinary = function(begin, end) {
+ begin = typeof begin === 'undefined' ? this.offset : begin;
+ end = typeof end === 'undefined' ? this.limit : end;
+ if (!this.noAssert) {
+ if (typeof begin !== 'number' || begin % 1 !== 0)
+ throw TypeError("Illegal begin: Not an integer");
+ begin >>>= 0;
+ if (typeof end !== 'number' || end % 1 !== 0)
+ throw TypeError("Illegal end: Not an integer");
+ end >>>= 0;
+ if (begin < 0 || begin > end || end > this.buffer.byteLength)
+ throw RangeError("Illegal range: 0 <= "+begin+" <= "+end+" <= "+this.buffer.byteLength);
+ }
+ if (begin === end)
+ return "";
+ var cc = [], pt = [];
+ while (begin < end) {
+ cc.push(this.view.getUint8(begin++));
+ if (cc.length >= 1024)
+ pt.push(String.fromCharCode.apply(String, cc)),
+ cc = [];
+ }
+ return pt.join('') + String.fromCharCode.apply(String, cc);
+ };
+
+ /**
+ * Decodes a binary encoded string, that is using only characters 0x00-0xFF as bytes, to a ByteBuffer.
+ * @param {string} str String to decode
+ * @param {boolean=} littleEndian Whether to use little or big endian byte order. Defaults to
+ * {@link ByteBuffer.DEFAULT_ENDIAN}.
+ * @param {boolean=} noAssert Whether to skip assertions of offsets and values. Defaults to
+ * {@link ByteBuffer.DEFAULT_NOASSERT}.
+ * @returns {!ByteBuffer} ByteBuffer
+ * @expose
+ */
+ ByteBuffer.fromBinary = function(str, littleEndian, noAssert) {
+ if (!noAssert) {
+ if (typeof str !== 'string')
+ throw TypeError("Illegal str: Not a string");
+ }
+ var i = 0, k = str.length, charCode,
+ bb = new ByteBuffer(k, littleEndian, noAssert);
+ while (i 255)
+ throw RangeError("Illegal charCode at "+i+": 0 <= "+charCode+" <= 255");
+ bb.view.setUint8(i++, charCode);
+ }
+ bb.limit = k;
+ return bb;
+ };
+
+ // encodings/debug
+
+ /**
+ * Encodes this ByteBuffer to a hex encoded string with marked offsets. Offset symbols are:
+ * * `<` : offset,
+ * * `'` : markedOffset,
+ * * `>` : limit,
+ * * `|` : offset and limit,
+ * * `[` : offset and markedOffset,
+ * * `]` : markedOffset and limit,
+ * * `!` : offset, markedOffset and limit
+ * @param {boolean=} columns If `true` returns two columns hex + ascii, defaults to `false`
+ * @returns {string|!Array.} Debug string or array of lines if `asArray = true`
+ * @expose
+ * @example `>00'01 02<03` contains four bytes with `limit=0, markedOffset=1, offset=3`
+ * @example `00[01 02 03>` contains four bytes with `offset=markedOffset=1, limit=4`
+ * @example `00|01 02 03` contains four bytes with `offset=limit=1, markedOffset=-1`
+ * @example `|` contains zero bytes with `offset=limit=0, markedOffset=-1`
+ */
+ ByteBufferPrototype.toDebug = function(columns) {
+ var i = -1,
+ k = this.buffer.byteLength,
+ b,
+ hex = "",
+ asc = "",
+ out = "";
+ while (i 32 && b < 127 ? String.fromCharCode(b) : '.';
+ }
+ }
+ ++i;
+ if (columns) {
+ if (i > 0 && i % 16 === 0 && i !== k) {
+ while (hex.length < 3*16+3) hex += " ";
+ out += hex+asc+"\n";
+ hex = asc = "";
+ }
+ }
+ if (i === this.offset && i === this.limit)
+ hex += i === this.markedOffset ? "!" : "|";
+ else if (i === this.offset)
+ hex += i === this.markedOffset ? "[" : "<";
+ else if (i === this.limit)
+ hex += i === this.markedOffset ? "]" : ">";
+ else
+ hex += i === this.markedOffset ? "'" : (columns || (i !== 0 && i !== k) ? " " : "");
+ }
+ if (columns && hex !== " ") {
+ while (hex.length < 3*16+3) hex += " ";
+ out += hex+asc+"\n";
+ }
+ return columns ? out : hex;
+ };
+
+ /**
+ * Decodes a hex encoded string with marked offsets to a ByteBuffer.
+ * @param {string} str Debug string to decode (not be generated with `columns = true`)
+ * @param {boolean=} littleEndian Whether to use little or big endian byte order. Defaults to
+ * {@link ByteBuffer.DEFAULT_ENDIAN}.
+ * @param {boolean=} noAssert Whether to skip assertions of offsets and values. Defaults to
+ * {@link ByteBuffer.DEFAULT_NOASSERT}.
+ * @returns {!ByteBuffer} ByteBuffer
+ * @expose
+ * @see ByteBuffer#toDebug
+ */
+ ByteBuffer.fromDebug = function(str, littleEndian, noAssert) {
+ var k = str.length,
+ bb = new ByteBuffer(((k+1)/3)|0, littleEndian, noAssert);
+ var i = 0, j = 0, ch, b,
+ rs = false, // Require symbol next
+ ho = false, hm = false, hl = false, // Already has offset, markedOffset, limit?
+ fail = false;
+ while (i':
+ if (!noAssert) {
+ if (hl) {
+ fail = true; break;
+ }
+ hl = true;
+ }
+ bb.limit = j;
+ rs = false;
+ break;
+ case "'":
+ if (!noAssert) {
+ if (hm) {
+ fail = true; break;
+ }
+ hm = true;
+ }
+ bb.markedOffset = j;
+ rs = false;
+ break;
+ case ' ':
+ rs = false;
+ break;
+ default:
+ if (!noAssert) {
+ if (rs) {
+ fail = true; break;
+ }
+ }
+ b = parseInt(ch+str.charAt(i++), 16);
+ if (!noAssert) {
+ if (isNaN(b) || b < 0 || b > 255)
+ throw TypeError("Illegal str: Not a debug encoded string");
+ }
+ bb.view.setUint8(j++, b);
+ rs = true;
+ }
+ if (fail)
+ throw TypeError("Illegal str: Invalid symbol at "+i);
+ }
+ if (!noAssert) {
+ if (!ho || !hl)
+ throw TypeError("Illegal str: Missing offset or limit");
+ if (j>>= 0;
+ if (typeof end !== 'number' || end % 1 !== 0)
+ throw TypeError("Illegal end: Not an integer");
+ end >>>= 0;
+ if (begin < 0 || begin > end || end > this.buffer.byteLength)
+ throw RangeError("Illegal range: 0 <= "+begin+" <= "+end+" <= "+this.buffer.byteLength);
+ }
+ var out = new Array(end - begin),
+ b;
+ while (begin < end) {
+ b = this.view.getUint8(begin++);
+ if (b < 0x10)
+ out.push("0", b.toString(16));
+ else out.push(b.toString(16));
+ }
+ return out.join('');
+ };
+
+ /**
+ * Decodes a hex encoded string to a ByteBuffer.
+ * @param {string} str String to decode
+ * @param {boolean=} littleEndian Whether to use little or big endian byte order. Defaults to
+ * {@link ByteBuffer.DEFAULT_ENDIAN}.
+ * @param {boolean=} noAssert Whether to skip assertions of offsets and values. Defaults to
+ * {@link ByteBuffer.DEFAULT_NOASSERT}.
+ * @returns {!ByteBuffer} ByteBuffer
+ * @expose
+ */
+ ByteBuffer.fromHex = function(str, littleEndian, noAssert) {
+ if (!noAssert) {
+ if (typeof str !== 'string')
+ throw TypeError("Illegal str: Not a string");
+ if (str.length % 2 !== 0)
+ throw TypeError("Illegal str: Length not a multiple of 2");
+ }
+ var k = str.length,
+ bb = new ByteBuffer((k / 2) | 0, littleEndian),
+ b;
+ for (var i=0, j=0; i 255)
+ throw TypeError("Illegal str: Contains non-hex characters");
+ bb.view.setUint8(j++, b);
+ }
+ bb.limit = j;
+ return bb;
+ };
+
+ // utfx-embeddable
+
+ /**
+ * utfx-embeddable (c) 2014 Daniel Wirtz
+ * Released under the Apache License, Version 2.0
+ * see: https://github.com/dcodeIO/utfx for details
+ */
+ var utfx = function() {
+ "use strict";
+
+ /**
+ * utfx namespace.
+ * @inner
+ * @type {!Object.}
+ */
+ var utfx = {};
+
+ /**
+ * Maximum valid code point.
+ * @type {number}
+ * @const
+ */
+ utfx.MAX_CODEPOINT = 0x10FFFF;
+
+ /**
+ * Encodes UTF8 code points to UTF8 bytes.
+ * @param {(!function():number|null) | number} src Code points source, either as a function returning the next code point
+ * respectively `null` if there are no more code points left or a single numeric code point.
+ * @param {!function(number)} dst Bytes destination as a function successively called with the next byte
+ */
+ utfx.encodeUTF8 = function(src, dst) {
+ var cp = null;
+ if (typeof src === 'number')
+ cp = src,
+ src = function() { return null; };
+ while (cp !== null || (cp = src()) !== null) {
+ if (cp < 0x80)
+ dst(cp&0x7F);
+ else if (cp < 0x800)
+ dst(((cp>>6)&0x1F)|0xC0),
+ dst((cp&0x3F)|0x80);
+ else if (cp < 0x10000)
+ dst(((cp>>12)&0x0F)|0xE0),
+ dst(((cp>>6)&0x3F)|0x80),
+ dst((cp&0x3F)|0x80);
+ else
+ dst(((cp>>18)&0x07)|0xF0),
+ dst(((cp>>12)&0x3F)|0x80),
+ dst(((cp>>6)&0x3F)|0x80),
+ dst((cp&0x3F)|0x80);
+ cp = null;
+ }
+ };
+
+ /**
+ * Decodes UTF8 bytes to UTF8 code points.
+ * @param {!function():number|null} src Bytes source as a function returning the next byte respectively `null` if there
+ * are no more bytes left.
+ * @param {!function(number)} dst Code points destination as a function successively called with each decoded code point.
+ * @throws {RangeError} If a starting byte is invalid in UTF8
+ * @throws {Error} If the last sequence is truncated. Has an array property `bytes` holding the
+ * remaining bytes.
+ */
+ utfx.decodeUTF8 = function(src, dst) {
+ var a, b, c, d, fail = function(b) {
+ b = b.slice(0, b.indexOf(null));
+ var err = Error(b.toString());
+ err.name = "TruncatedError";
+ err['bytes'] = b;
+ throw err;
+ };
+ while ((a = src()) !== null) {
+ if ((a&0x80) === 0)
+ dst(a);
+ else if ((a&0xE0) === 0xC0)
+ ((b = src()) === null) && fail([a, b]),
+ dst(((a&0x1F)<<6) | (b&0x3F));
+ else if ((a&0xF0) === 0xE0)
+ ((b=src()) === null || (c=src()) === null) && fail([a, b, c]),
+ dst(((a&0x0F)<<12) | ((b&0x3F)<<6) | (c&0x3F));
+ else if ((a&0xF8) === 0xF0)
+ ((b=src()) === null || (c=src()) === null || (d=src()) === null) && fail([a, b, c ,d]),
+ dst(((a&0x07)<<18) | ((b&0x3F)<<12) | ((c&0x3F)<<6) | (d&0x3F));
+ else throw RangeError("Illegal starting byte: "+a);
+ }
+ };
+
+ /**
+ * Converts UTF16 characters to UTF8 code points.
+ * @param {!function():number|null} src Characters source as a function returning the next char code respectively
+ * `null` if there are no more characters left.
+ * @param {!function(number)} dst Code points destination as a function successively called with each converted code
+ * point.
+ */
+ utfx.UTF16toUTF8 = function(src, dst) {
+ var c1, c2 = null;
+ while (true) {
+ if ((c1 = c2 !== null ? c2 : src()) === null)
+ break;
+ if (c1 >= 0xD800 && c1 <= 0xDFFF) {
+ if ((c2 = src()) !== null) {
+ if (c2 >= 0xDC00 && c2 <= 0xDFFF) {
+ dst((c1-0xD800)*0x400+c2-0xDC00+0x10000);
+ c2 = null; continue;
+ }
+ }
+ }
+ dst(c1);
+ }
+ if (c2 !== null) dst(c2);
+ };
+
+ /**
+ * Converts UTF8 code points to UTF16 characters.
+ * @param {(!function():number|null) | number} src Code points source, either as a function returning the next code point
+ * respectively `null` if there are no more code points left or a single numeric code point.
+ * @param {!function(number)} dst Characters destination as a function successively called with each converted char code.
+ * @throws {RangeError} If a code point is out of range
+ */
+ utfx.UTF8toUTF16 = function(src, dst) {
+ var cp = null;
+ if (typeof src === 'number')
+ cp = src, src = function() { return null; };
+ while (cp !== null || (cp = src()) !== null) {
+ if (cp <= 0xFFFF)
+ dst(cp);
+ else
+ cp -= 0x10000,
+ dst((cp>>10)+0xD800),
+ dst((cp%0x400)+0xDC00);
+ cp = null;
+ }
+ };
+
+ /**
+ * Converts and encodes UTF16 characters to UTF8 bytes.
+ * @param {!function():number|null} src Characters source as a function returning the next char code respectively `null`
+ * if there are no more characters left.
+ * @param {!function(number)} dst Bytes destination as a function successively called with the next byte.
+ */
+ utfx.encodeUTF16toUTF8 = function(src, dst) {
+ utfx.UTF16toUTF8(src, function(cp) {
+ utfx.encodeUTF8(cp, dst);
+ });
+ };
+
+ /**
+ * Decodes and converts UTF8 bytes to UTF16 characters.
+ * @param {!function():number|null} src Bytes source as a function returning the next byte respectively `null` if there
+ * are no more bytes left.
+ * @param {!function(number)} dst Characters destination as a function successively called with each converted char code.
+ * @throws {RangeError} If a starting byte is invalid in UTF8
+ * @throws {Error} If the last sequence is truncated. Has an array property `bytes` holding the remaining bytes.
+ */
+ utfx.decodeUTF8toUTF16 = function(src, dst) {
+ utfx.decodeUTF8(src, function(cp) {
+ utfx.UTF8toUTF16(cp, dst);
+ });
+ };
+
+ /**
+ * Calculates the byte length of an UTF8 code point.
+ * @param {number} cp UTF8 code point
+ * @returns {number} Byte length
+ */
+ utfx.calculateCodePoint = function(cp) {
+ return (cp < 0x80) ? 1 : (cp < 0x800) ? 2 : (cp < 0x10000) ? 3 : 4;
+ };
+
+ /**
+ * Calculates the number of UTF8 bytes required to store UTF8 code points.
+ * @param {(!function():number|null)} src Code points source as a function returning the next code point respectively
+ * `null` if there are no more code points left.
+ * @returns {number} The number of UTF8 bytes required
+ */
+ utfx.calculateUTF8 = function(src) {
+ var cp, l=0;
+ while ((cp = src()) !== null)
+ l += utfx.calculateCodePoint(cp);
+ return l;
+ };
+
+ /**
+ * Calculates the number of UTF8 code points respectively UTF8 bytes required to store UTF16 char codes.
+ * @param {(!function():number|null)} src Characters source as a function returning the next char code respectively
+ * `null` if there are no more characters left.
+ * @returns {!Array.} The number of UTF8 code points at index 0 and the number of UTF8 bytes required at index 1.
+ */
+ utfx.calculateUTF16asUTF8 = function(src) {
+ var n=0, l=0;
+ utfx.UTF16toUTF8(src, function(cp) {
+ ++n; l += utfx.calculateCodePoint(cp);
+ });
+ return [n,l];
+ };
+
+ return utfx;
+ }();
+
+ // encodings/utf8
+
+ /**
+ * Encodes this ByteBuffer's contents between {@link ByteBuffer#offset} and {@link ByteBuffer#limit} to an UTF8 encoded
+ * string.
+ * @returns {string} Hex encoded string
+ * @throws {RangeError} If `offset > limit`
+ * @expose
+ */
+ ByteBufferPrototype.toUTF8 = function(begin, end) {
+ if (typeof begin === 'undefined') begin = this.offset;
+ if (typeof end === 'undefined') end = this.limit;
+ if (!this.noAssert) {
+ if (typeof begin !== 'number' || begin % 1 !== 0)
+ throw TypeError("Illegal begin: Not an integer");
+ begin >>>= 0;
+ if (typeof end !== 'number' || end % 1 !== 0)
+ throw TypeError("Illegal end: Not an integer");
+ end >>>= 0;
+ if (begin < 0 || begin > end || end > this.buffer.byteLength)
+ throw RangeError("Illegal range: 0 <= "+begin+" <= "+end+" <= "+this.buffer.byteLength);
+ }
+ var sd; try {
+ utfx.decodeUTF8toUTF16(function() {
+ return begin < end ? this.view.getUint8(begin++) : null;
+ }.bind(this), sd = stringDestination());
+ } catch (e) {
+ if (begin !== end)
+ throw RangeError("Illegal range: Truncated data, "+begin+" != "+end);
+ }
+ return sd();
+ };
+
+ /**
+ * Decodes an UTF8 encoded string to a ByteBuffer.
+ * @param {string} str String to decode
+ * @param {boolean=} littleEndian Whether to use little or big endian byte order. Defaults to
+ * {@link ByteBuffer.DEFAULT_ENDIAN}.
+ * @param {boolean=} noAssert Whether to skip assertions of offsets and values. Defaults to
+ * {@link ByteBuffer.DEFAULT_NOASSERT}.
+ * @returns {!ByteBuffer} ByteBuffer
+ * @expose
+ */
+ ByteBuffer.fromUTF8 = function(str, littleEndian, noAssert) {
+ if (!noAssert)
+ if (typeof str !== 'string')
+ throw TypeError("Illegal str: Not a string");
+ var bb = new ByteBuffer(utfx.calculateUTF16asUTF8(stringSource(str), true)[1], littleEndian, noAssert),
+ i = 0;
+ utfx.encodeUTF16toUTF8(stringSource(str), function(b) {
+ bb.view.setUint8(i++, b);
+ });
+ bb.limit = i;
+ return bb;
+ };
+
+
+ return ByteBuffer;
+ }
+
+ /* CommonJS */ if (typeof require === 'function' && typeof module === 'object' && module && typeof exports === 'object' && exports)
+ module['exports'] = (function() {
+ var Long; try { Long = require("long"); } catch (e) {}
+ return loadByteBuffer(Long);
+ })();
+ /* AMD */ else if (typeof define === 'function' && define["amd"])
+ define("ByteBuffer", ["Long"], function(Long) { return loadByteBuffer(Long); });
+ /* Global */ else
+ (global["dcodeIO"] = global["dcodeIO"] || {})["ByteBuffer"] = loadByteBuffer(global["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 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, factory) {
+
+ /* AMD */ if (typeof define === 'function' && define["amd"])
+ define(["bytebuffer"], factory);
+ /* CommonJS */ else if (typeof require === "function" && typeof module === "object" && module && module["exports"])
+ module["exports"] = factory(require("bytebuffer"), true);
+ /* Global */ else
+ (global["dcodeIO"] = global["dcodeIO"] || {})["ProtoBuf"] = factory(global["dcodeIO"]["ByteBuffer"]);
+
+})(this, function(ByteBuffer, isCommonJS) {
+ "use strict";
+
+ /**
+ * The ProtoBuf namespace.
+ * @exports ProtoBuf
+ * @namespace
+ * @expose
+ */
+ var ProtoBuf = {};
+
+ /**
+ * @type {!function(new: ByteBuffer, ...[*])}
+ * @expose
+ */
+ ProtoBuf.ByteBuffer = ByteBuffer;
+
+ /**
+ * @type {?function(new: Long, ...[*])}
+ * @expose
+ */
+ ProtoBuf.Long = ByteBuffer.Long || null;
+
+ /**
+ * ProtoBuf.js version.
+ * @type {string}
+ * @const
+ * @expose
+ */
+ ProtoBuf.VERSION = "5.0.1";
+
+ /**
+ * 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
+ * @expose
+ */
+ ProtoBuf.WIRE_TYPES.STARTGROUP = 3;
+
+ /**
+ * End group wire type.
+ * @type {number}
+ * @const
+ * @expose
+ */
+ ProtoBuf.WIRE_TYPES.ENDGROUP = 4;
+
+ /**
+ * Fixed 32 bits wire type.
+ * @type {number}
+ * @const
+ * @expose
+ */
+ ProtoBuf.WIRE_TYPES.BITS32 = 5;
+
+ /**
+ * Packable wire types.
+ * @type {!Array.}
+ * @const
+ * @expose
+ */
+ ProtoBuf.PACKABLE_WIRE_TYPES = [
+ ProtoBuf.WIRE_TYPES.VARINT,
+ ProtoBuf.WIRE_TYPES.BITS64,
+ ProtoBuf.WIRE_TYPES.BITS32
+ ];
+
+ /**
+ * Types.
+ * @dict
+ * @type {!Object.}
+ * @const
+ * @expose
+ */
+ ProtoBuf.TYPES = {
+ // According to the protobuf spec.
+ "int32": {
+ name: "int32",
+ wireType: ProtoBuf.WIRE_TYPES.VARINT,
+ defaultValue: 0
+ },
+ "uint32": {
+ name: "uint32",
+ wireType: ProtoBuf.WIRE_TYPES.VARINT,
+ defaultValue: 0
+ },
+ "sint32": {
+ name: "sint32",
+ wireType: ProtoBuf.WIRE_TYPES.VARINT,
+ defaultValue: 0
+ },
+ "int64": {
+ name: "int64",
+ wireType: ProtoBuf.WIRE_TYPES.VARINT,
+ defaultValue: ProtoBuf.Long ? ProtoBuf.Long.ZERO : undefined
+ },
+ "uint64": {
+ name: "uint64",
+ wireType: ProtoBuf.WIRE_TYPES.VARINT,
+ defaultValue: ProtoBuf.Long ? ProtoBuf.Long.UZERO : undefined
+ },
+ "sint64": {
+ name: "sint64",
+ wireType: ProtoBuf.WIRE_TYPES.VARINT,
+ defaultValue: ProtoBuf.Long ? ProtoBuf.Long.ZERO : undefined
+ },
+ "bool": {
+ name: "bool",
+ wireType: ProtoBuf.WIRE_TYPES.VARINT,
+ defaultValue: false
+ },
+ "double": {
+ name: "double",
+ wireType: ProtoBuf.WIRE_TYPES.BITS64,
+ defaultValue: 0
+ },
+ "string": {
+ name: "string",
+ wireType: ProtoBuf.WIRE_TYPES.LDELIM,
+ defaultValue: ""
+ },
+ "bytes": {
+ name: "bytes",
+ wireType: ProtoBuf.WIRE_TYPES.LDELIM,
+ defaultValue: null // overridden in the code, must be a unique instance
+ },
+ "fixed32": {
+ name: "fixed32",
+ wireType: ProtoBuf.WIRE_TYPES.BITS32,
+ defaultValue: 0
+ },
+ "sfixed32": {
+ name: "sfixed32",
+ wireType: ProtoBuf.WIRE_TYPES.BITS32,
+ defaultValue: 0
+ },
+ "fixed64": {
+ name: "fixed64",
+ wireType: ProtoBuf.WIRE_TYPES.BITS64,
+ defaultValue: ProtoBuf.Long ? ProtoBuf.Long.UZERO : undefined
+ },
+ "sfixed64": {
+ name: "sfixed64",
+ wireType: ProtoBuf.WIRE_TYPES.BITS64,
+ defaultValue: ProtoBuf.Long ? ProtoBuf.Long.ZERO : undefined
+ },
+ "float": {
+ name: "float",
+ wireType: ProtoBuf.WIRE_TYPES.BITS32,
+ defaultValue: 0
+ },
+ "enum": {
+ name: "enum",
+ wireType: ProtoBuf.WIRE_TYPES.VARINT,
+ defaultValue: 0
+ },
+ "message": {
+ name: "message",
+ wireType: ProtoBuf.WIRE_TYPES.LDELIM,
+ defaultValue: null
+ },
+ "group": {
+ name: "group",
+ wireType: ProtoBuf.WIRE_TYPES.STARTGROUP,
+ defaultValue: null
+ }
+ };
+
+ /**
+ * Valid map key types.
+ * @type {!Array.>}
+ * @const
+ * @expose
+ */
+ ProtoBuf.MAP_KEY_TYPES = [
+ ProtoBuf.TYPES["int32"],
+ ProtoBuf.TYPES["sint32"],
+ ProtoBuf.TYPES["sfixed32"],
+ ProtoBuf.TYPES["uint32"],
+ ProtoBuf.TYPES["fixed32"],
+ ProtoBuf.TYPES["int64"],
+ ProtoBuf.TYPES["sint64"],
+ ProtoBuf.TYPES["sfixed64"],
+ ProtoBuf.TYPES["uint64"],
+ ProtoBuf.TYPES["fixed64"],
+ ProtoBuf.TYPES["bool"],
+ ProtoBuf.TYPES["string"],
+ ProtoBuf.TYPES["bytes"]
+ ];
+
+ /**
+ * Minimum field id.
+ * @type {number}
+ * @const
+ * @expose
+ */
+ ProtoBuf.ID_MIN = 1;
+
+ /**
+ * Maximum field id.
+ * @type {number}
+ * @const
+ * @expose
+ */
+ ProtoBuf.ID_MAX = 0x1FFFFFFF;
+
+ /**
+ * 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;
+
+ /**
+ * By default, messages are populated with (setX, set_x) accessors for each field. This can be disabled by
+ * setting this to `false` prior to building messages.
+ * @type {boolean}
+ * @expose
+ */
+ ProtoBuf.populateAccessors = true;
+
+ /**
+ * By default, messages are populated with default values if a field is not present on the wire. To disable
+ * this behavior, set this setting to `false`.
+ * @type {boolean}
+ * @expose
+ */
+ ProtoBuf.populateDefaults = true;
+
+ /**
+ * @alias ProtoBuf.Util
+ * @expose
+ */
+ ProtoBuf.Util = (function() {
+ "use strict";
+
+ /**
+ * ProtoBuf utilities.
+ * @exports ProtoBuf.Util
+ * @namespace
+ */
+ var Util = {};
+
+ /**
+ * Flag if running in node or not.
+ * @type {boolean}
+ * @const
+ * @expose
+ */
+ Util.IS_NODE = !!(
+ typeof process === 'object' && process+'' === '[object process]' && !process['browser']
+ );
+
+ /**
+ * 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}
+ * @expose
+ */
+ ProtoBuf.Lang = {
+
+ // Characters always ending a statement
+ DELIM: /[\s\{\}=;:\[\],'"\(\)<>]/g,
+
+ // Field rules
+ RULE: /^(?:required|optional|repeated|map)$/,
+
+ // Field types
+ TYPE: /^(?:double|float|int32|uint32|sint32|int64|uint64|sint64|fixed32|sfixed32|fixed64|sfixed64|bool|string|bytes)$/,
+
+ // Names
+ NAME: /^[a-zA-Z_][a-zA-Z_0-9]*$/,
+
+ // Type definitions
+ TYPEDEF: /^[a-zA-Z][a-zA-Z_0-9]*$/,
+
+ // Type references
+ TYPEREF: /^(?:\.?[a-zA-Z_][a-zA-Z_0-9]*)+$/,
+
+ // Fully qualified type references
+ FQTYPEREF: /^(?:\.[a-zA-Z][a-zA-Z_0-9]*)+$/,
+
+ // All numbers
+ NUMBER: /^-?(?:[1-9][0-9]*|0|0[xX][0-9a-fA-F]+|0[0-7]+|([0-9]*(\.[0-9]*)?([Ee][+-]?[0-9]+)?)|inf|nan)$/,
+
+ // Decimal numbers
+ NUMBER_DEC: /^(?:[1-9][0-9]*|0)$/,
+
+ // Hexadecimal numbers
+ NUMBER_HEX: /^0[xX][0-9a-fA-F]+$/,
+
+ // Octal numbers
+ NUMBER_OCT: /^0[0-7]+$/,
+
+ // Floating point numbers
+ NUMBER_FLT: /^([0-9]*(\.[0-9]*)?([Ee][+-]?[0-9]+)?|inf|nan)$/,
+
+ // Booleans
+ BOOL: /^(?:true|false)$/i,
+
+ // Id numbers
+ ID: /^(?:[1-9][0-9]*|0|0[xX][0-9a-fA-F]+|0[0-7]+)$/,
+
+ // Negative id numbers (enum values)
+ NEGID: /^\-?(?:[1-9][0-9]*|0|0[xX][0-9a-fA-F]+|0[0-7]+)$/,
+
+ // Whitespaces
+ WHITESPACE: /\s/,
+
+ // All strings
+ STRING: /(?:"([^"\\]*(?:\\.[^"\\]*)*)")|(?:'([^'\\]*(?:\\.[^'\\]*)*)')/g,
+
+ // Double quoted strings
+ STRING_DQ: /(?:"([^"\\]*(?:\\.[^"\\]*)*)")/g,
+
+ // Single quoted strings
+ STRING_SQ: /(?:'([^'\\]*(?:\\.[^'\\]*)*)')/g
+ };
+
+ /**
+ * @alias ProtoBuf.DotProto
+ * @expose
+ */
+ ProtoBuf.DotProto = (function(ProtoBuf, Lang) {
+ "use strict";
+
+ /**
+ * Utilities to parse .proto files.
+ * @exports ProtoBuf.DotProto
+ * @namespace
+ */
+ var DotProto = {};
+
+ /**
+ * Constructs a new Tokenizer.
+ * @exports ProtoBuf.DotProto.Tokenizer
+ * @class prototype 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;
+
+ /**
+ * Token stack.
+ * @type {!Array.}
+ * @expose
+ */
+ this.stack = [];
+
+ /**
+ * Opening character of the current string read, if any.
+ * @type {?string}
+ * @private
+ */
+ this._stringOpen = null;
+ };
+
+ /**
+ * @alias ProtoBuf.DotProto.Tokenizer.prototype
+ * @inner
+ */
+ var TokenizerPrototype = Tokenizer.prototype;
+
+ /**
+ * Reads a string beginning at the current index.
+ * @return {string}
+ * @private
+ */
+ TokenizerPrototype._readString = function() {
+ var re = this._stringOpen === '"'
+ ? Lang.STRING_DQ
+ : Lang.STRING_SQ;
+ re.lastIndex = this.index - 1; // Include the open quote
+ var match = re.exec(this.source);
+ if (!match)
+ throw Error("unterminated string");
+ this.index = re.lastIndex;
+ this.stack.push(this._stringOpen);
+ this._stringOpen = null;
+ return match[1];
+ };
+
+ /**
+ * Gets the next token and advances by one.
+ * @return {?string} Token or `null` on EOF
+ * @expose
+ */
+ TokenizerPrototype.next = function() {
+ if (this.stack.length > 0)
+ return this.stack.shift();
+ if (this.index >= this.source.length)
+ return null;
+ if (this._stringOpen !== null)
+ return this._readString();
+
+ var repeat,
+ prev,
+ next;
+ do {
+ repeat = false;
+
+ // Strip white spaces
+ while (Lang.WHITESPACE.test(next = this.source.charAt(this.index))) {
+ if (next === '\n')
+ ++this.line;
+ if (++this.index === this.source.length)
+ return null;
+ }
+
+ // Strip comments
+ if (this.source.charAt(this.index) === '/') {
+ ++this.index;
+ if (this.source.charAt(this.index) === '/') { // Line
+ while (this.source.charAt(++this.index) !== '\n')
+ if (this.index == this.source.length)
+ return null;
+ ++this.index;
+ ++this.line;
+ repeat = true;
+ } else if ((next = this.source.charAt(this.index)) === '*') { /* Block */
+ do {
+ if (next === '\n')
+ ++this.line;
+ if (++this.index === this.source.length)
+ return null;
+ prev = next;
+ next = this.source.charAt(this.index);
+ } while (prev !== '*' || next !== '/');
+ ++this.index;
+ repeat = true;
+ } else
+ return '/';
+ }
+ } 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)
+ while(end < this.source.length && !Lang.DELIM.test(this.source.charAt(end)))
+ ++end;
+ var token = this.source.substring(this.index, this.index = end);
+ if (token === '"' || token === "'")
+ this._stringOpen = token;
+ return token;
+ };
+
+ /**
+ * Peeks for the next token.
+ * @return {?string} Token or `null` on EOF
+ * @expose
+ */
+ TokenizerPrototype.peek = function() {
+ if (this.stack.length === 0) {
+ var token = this.next();
+ if (token === null)
+ return null;
+ this.stack.push(token);
+ }
+ return this.stack[0];
+ };
+
+ /**
+ * Skips a specific token and throws if it differs.
+ * @param {string} expected Expected token
+ * @throws {Error} If the actual token differs
+ */
+ TokenizerPrototype.skip = function(expected) {
+ var actual = this.next();
+ if (actual !== expected)
+ throw Error("illegal '"+actual+"', '"+expected+"' expected");
+ };
+
+ /**
+ * Omits an optional token.
+ * @param {string} expected Expected optional token
+ * @returns {boolean} `true` if the token exists
+ */
+ TokenizerPrototype.omit = function(expected) {
+ if (this.peek() === expected) {
+ this.next();
+ return true;
+ }
+ return false;
+ };
+
+ /**
+ * Returns a string representation of this object.
+ * @return {string} String representation as of "Tokenizer(index/length)"
+ * @expose
+ */
+ TokenizerPrototype.toString = function() {
+ return "Tokenizer ("+this.index+"/"+this.source.length+" at line "+this.line+")";
+ };
+
+ /**
+ * @alias ProtoBuf.DotProto.Tokenizer
+ * @expose
+ */
+ DotProto.Tokenizer = Tokenizer;
+
+ /**
+ * Constructs a new Parser.
+ * @exports ProtoBuf.DotProto.Parser
+ * @class prototype parser
+ * @param {string} source Source
+ * @constructor
+ */
+ var Parser = function(source) {
+
+ /**
+ * Tokenizer.
+ * @type {!ProtoBuf.DotProto.Tokenizer}
+ * @expose
+ */
+ this.tn = new Tokenizer(source);
+
+ /**
+ * Whether parsing proto3 or not.
+ * @type {boolean}
+ */
+ this.proto3 = false;
+ };
+
+ /**
+ * @alias ProtoBuf.DotProto.Parser.prototype
+ * @inner
+ */
+ var ParserPrototype = Parser.prototype;
+
+ /**
+ * Parses the source.
+ * @returns {!Object}
+ * @throws {Error} If the source cannot be parsed
+ * @expose
+ */
+ ParserPrototype.parse = function() {
+ var topLevel = {
+ "name": "[ROOT]", // temporary
+ "package": null,
+ "messages": [],
+ "enums": [],
+ "imports": [],
+ "options": {},
+ "services": []
+ // "syntax": undefined
+ };
+ var token,
+ head = true,
+ weak;
+ try {
+ while (token = this.tn.next()) {
+ switch (token) {
+ case 'package':
+ if (!head || topLevel["package"] !== null)
+ throw Error("unexpected 'package'");
+ token = this.tn.next();
+ if (!Lang.TYPEREF.test(token))
+ throw Error("illegal package name: " + token);
+ this.tn.skip(";");
+ topLevel["package"] = token;
+ break;
+ case 'import':
+ if (!head)
+ throw Error("unexpected 'import'");
+ token = this.tn.peek();
+ if (token === "public" || (weak = token === "weak")) // token ignored
+ this.tn.next();
+ token = this._readString();
+ this.tn.skip(";");
+ if (!weak) // import ignored
+ topLevel["imports"].push(token);
+ break;
+ case 'syntax':
+ if (!head)
+ throw Error("unexpected 'syntax'");
+ this.tn.skip("=");
+ if ((topLevel["syntax"] = this._readString()) === "proto3")
+ this.proto3 = true;
+ this.tn.skip(";");
+ break;
+ case 'message':
+ this._parseMessage(topLevel, null);
+ head = false;
+ break;
+ case 'enum':
+ this._parseEnum(topLevel);
+ head = false;
+ break;
+ case 'option':
+ this._parseOption(topLevel);
+ break;
+ case 'service':
+ this._parseService(topLevel);
+ break;
+ case 'extend':
+ this._parseExtend(topLevel);
+ break;
+ default:
+ throw Error("unexpected '" + token + "'");
+ }
+ }
+ } catch (e) {
+ e.message = "Parse error at line "+this.tn.line+": " + e.message;
+ throw e;
+ }
+ delete topLevel["name"];
+ return topLevel;
+ };
+
+ /**
+ * Parses the specified source.
+ * @returns {!Object}
+ * @throws {Error} If the source cannot be parsed
+ * @expose
+ */
+ Parser.parse = function(source) {
+ return new Parser(source).parse();
+ };
+
+ // ----- Conversion ------
+
+ /**
+ * Converts a numerical string to an id.
+ * @param {string} value
+ * @param {boolean=} mayBeNegative
+ * @returns {number}
+ * @inner
+ */
+ function mkId(value, mayBeNegative) {
+ var id = -1,
+ sign = 1;
+ if (value.charAt(0) == '-') {
+ sign = -1;
+ value = value.substring(1);
+ }
+ if (Lang.NUMBER_DEC.test(value))
+ id = parseInt(value);
+ else if (Lang.NUMBER_HEX.test(value))
+ id = parseInt(value.substring(2), 16);
+ else if (Lang.NUMBER_OCT.test(value))
+ id = parseInt(value.substring(1), 8);
+ else
+ throw Error("illegal id value: " + (sign < 0 ? '-' : '') + value);
+ id = (sign*id)|0; // Force to 32bit
+ if (!mayBeNegative && id < 0)
+ throw Error("illegal id value: " + (sign < 0 ? '-' : '') + value);
+ return id;
+ }
+
+ /**
+ * Converts a numerical string to a number.
+ * @param {string} val
+ * @returns {number}
+ * @inner
+ */
+ function mkNumber(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 (val === 'inf')
+ return sign * Infinity;
+ else if (val === 'nan')
+ return NaN;
+ else if (Lang.NUMBER_FLT.test(val))
+ return sign * parseFloat(val);
+ throw Error("illegal number value: " + (sign < 0 ? '-' : '') + val);
+ }
+
+ // ----- Reading ------
+
+ /**
+ * Reads a string.
+ * @returns {string}
+ * @private
+ */
+ ParserPrototype._readString = function() {
+ var value = "",
+ token,
+ delim;
+ do {
+ delim = this.tn.next();
+ if (delim !== "'" && delim !== '"')
+ throw Error("illegal string delimiter: "+delim);
+ value += this.tn.next();
+ this.tn.skip(delim);
+ token = this.tn.peek();
+ } while (token === '"' || token === '"'); // multi line?
+ return value;
+ };
+
+ /**
+ * Reads a value.
+ * @param {boolean=} mayBeTypeRef
+ * @returns {number|boolean|string}
+ * @private
+ */
+ ParserPrototype._readValue = function(mayBeTypeRef) {
+ var token = this.tn.peek(),
+ value;
+ if (token === '"' || token === "'")
+ return this._readString();
+ this.tn.next();
+ if (Lang.NUMBER.test(token))
+ return mkNumber(token);
+ if (Lang.BOOL.test(token))
+ return (token.toLowerCase() === 'true');
+ if (mayBeTypeRef && Lang.TYPEREF.test(token))
+ return token;
+ throw Error("illegal value: "+token);
+
+ };
+
+ // ----- Parsing constructs -----
+
+ /**
+ * Parses a namespace option.
+ * @param {!Object} parent Parent definition
+ * @param {boolean=} isList
+ * @private
+ */
+ ParserPrototype._parseOption = function(parent, isList) {
+ var token = this.tn.next(),
+ custom = false;
+ if (token === '(') {
+ custom = true;
+ token = this.tn.next();
+ }
+ if (!Lang.TYPEREF.test(token))
+ // we can allow options of the form google.protobuf.* since they will just get ignored anyways
+ // if (!/google\.protobuf\./.test(token)) // FIXME: Why should that not be a valid typeref?
+ throw Error("illegal option name: "+token);
+ var name = token;
+ if (custom) { // (my_method_option).foo, (my_method_option), some_method_option, (foo.my_option).bar
+ this.tn.skip(')');
+ name = '('+name+')';
+ token = this.tn.peek();
+ if (Lang.FQTYPEREF.test(token)) {
+ name += token;
+ this.tn.next();
+ }
+ }
+ this.tn.skip('=');
+ this._parseOptionValue(parent, name);
+ if (!isList)
+ this.tn.skip(";");
+ };
+
+ /**
+ * Sets an option on the specified options object.
+ * @param {!Object.} options
+ * @param {string} name
+ * @param {string|number|boolean} value
+ * @inner
+ */
+ function setOption(options, name, value) {
+ if (typeof options[name] === 'undefined')
+ options[name] = value;
+ else {
+ if (!Array.isArray(options[name]))
+ options[name] = [ options[name] ];
+ options[name].push(value);
+ }
+ }
+
+ /**
+ * Parses an option value.
+ * @param {!Object} parent
+ * @param {string} name
+ * @private
+ */
+ ParserPrototype._parseOptionValue = function(parent, name) {
+ var token = this.tn.peek();
+ if (token !== '{') { // Plain value
+ setOption(parent["options"], name, this._readValue(true));
+ } else { // Aggregate options
+ this.tn.skip("{");
+ while ((token = this.tn.next()) !== '}') {
+ if (!Lang.NAME.test(token))
+ throw Error("illegal option name: " + name + "." + token);
+ if (this.tn.omit(":"))
+ setOption(parent["options"], name + "." + token, this._readValue(true));
+ else
+ this._parseOptionValue(parent, name + "." + token);
+ }
+ }
+ };
+
+ /**
+ * Parses a service definition.
+ * @param {!Object} parent Parent definition
+ * @private
+ */
+ ParserPrototype._parseService = function(parent) {
+ var token = this.tn.next();
+ if (!Lang.NAME.test(token))
+ throw Error("illegal service name at line "+this.tn.line+": "+token);
+ var name = token;
+ var svc = {
+ "name": name,
+ "rpc": {},
+ "options": {}
+ };
+ this.tn.skip("{");
+ while ((token = this.tn.next()) !== '}') {
+ if (token === "option")
+ this._parseOption(svc);
+ else if (token === 'rpc')
+ this._parseServiceRPC(svc);
+ else
+ throw Error("illegal service token: "+token);
+ }
+ this.tn.omit(";");
+ parent["services"].push(svc);
+ };
+
+ /**
+ * Parses a RPC service definition of the form ['rpc', name, (request), 'returns', (response)].
+ * @param {!Object} svc Service definition
+ * @private
+ */
+ ParserPrototype._parseServiceRPC = function(svc) {
+ var type = "rpc",
+ token = this.tn.next();
+ if (!Lang.NAME.test(token))
+ throw Error("illegal rpc service method name: "+token);
+ var name = token;
+ var method = {
+ "request": null,
+ "response": null,
+ "request_stream": false,
+ "response_stream": false,
+ "options": {}
+ };
+ this.tn.skip("(");
+ token = this.tn.next();
+ if (token.toLowerCase() === "stream") {
+ method["request_stream"] = true;
+ token = this.tn.next();
+ }
+ if (!Lang.TYPEREF.test(token))
+ throw Error("illegal rpc service request type: "+token);
+ method["request"] = token;
+ this.tn.skip(")");
+ token = this.tn.next();
+ if (token.toLowerCase() !== "returns")
+ throw Error("illegal rpc service request type delimiter: "+token);
+ this.tn.skip("(");
+ token = this.tn.next();
+ if (token.toLowerCase() === "stream") {
+ method["response_stream"] = true;
+ token = this.tn.next();
+ }
+ method["response"] = token;
+ this.tn.skip(")");
+ token = this.tn.peek();
+ if (token === '{') {
+ this.tn.next();
+ while ((token = this.tn.next()) !== '}') {
+ if (token === 'option')
+ this._parseOption(method);
+ else
+ throw Error("illegal rpc service token: " + token);
+ }
+ this.tn.omit(";");
+ } else
+ this.tn.skip(";");
+ if (typeof svc[type] === 'undefined')
+ svc[type] = {};
+ svc[type][name] = method;
+ };
+
+ /**
+ * Parses a message definition.
+ * @param {!Object} parent Parent definition
+ * @param {!Object=} fld Field definition if this is a group
+ * @returns {!Object}
+ * @private
+ */
+ ParserPrototype._parseMessage = function(parent, fld) {
+ var isGroup = !!fld,
+ token = this.tn.next();
+ var msg = {
+ "name": "",
+ "fields": [],
+ "enums": [],
+ "messages": [],
+ "options": {},
+ "services": [],
+ "oneofs": {}
+ // "extensions": undefined
+ };
+ if (!Lang.NAME.test(token))
+ throw Error("illegal "+(isGroup ? "group" : "message")+" name: "+token);
+ msg["name"] = token;
+ if (isGroup) {
+ this.tn.skip("=");
+ fld["id"] = mkId(this.tn.next());
+ msg["isGroup"] = true;
+ }
+ token = this.tn.peek();
+ if (token === '[' && fld)
+ this._parseFieldOptions(fld);
+ this.tn.skip("{");
+ while ((token = this.tn.next()) !== '}') {
+ if (Lang.RULE.test(token))
+ this._parseMessageField(msg, token);
+ else if (token === "oneof")
+ this._parseMessageOneOf(msg);
+ else if (token === "enum")
+ this._parseEnum(msg);
+ else if (token === "message")
+ this._parseMessage(msg);
+ else if (token === "option")
+ this._parseOption(msg);
+ else if (token === "service")
+ this._parseService(msg);
+ else if (token === "extensions")
+ msg["extensions"] = this._parseExtensionRanges();
+ else if (token === "reserved")
+ this._parseIgnored(); // TODO
+ else if (token === "extend")
+ this._parseExtend(msg);
+ else if (Lang.TYPEREF.test(token)) {
+ if (!this.proto3)
+ throw Error("illegal field rule: "+token);
+ this._parseMessageField(msg, "optional", token);
+ } else
+ throw Error("illegal message token: "+token);
+ }
+ this.tn.omit(";");
+ parent["messages"].push(msg);
+ return msg;
+ };
+
+ /**
+ * Parses an ignored statement.
+ * @private
+ */
+ ParserPrototype._parseIgnored = function() {
+ while (this.tn.peek() !== ';')
+ this.tn.next();
+ this.tn.skip(";");
+ };
+
+ /**
+ * Parses a message field.
+ * @param {!Object} msg Message definition
+ * @param {string} rule Field rule
+ * @param {string=} type Field type if already known (never known for maps)
+ * @returns {!Object} Field descriptor
+ * @private
+ */
+ ParserPrototype._parseMessageField = function(msg, rule, type) {
+ if (!Lang.RULE.test(rule))
+ throw Error("illegal message field rule: "+rule);
+ var fld = {
+ "rule": rule,
+ "type": "",
+ "name": "",
+ "options": {},
+ "id": 0
+ };
+ var token;
+ if (rule === "map") {
+
+ if (type)
+ throw Error("illegal type: " + type);
+ this.tn.skip('<');
+ token = this.tn.next();
+ if (!Lang.TYPE.test(token) && !Lang.TYPEREF.test(token))
+ throw Error("illegal message field type: " + token);
+ fld["keytype"] = token;
+ this.tn.skip(',');
+ token = this.tn.next();
+ if (!Lang.TYPE.test(token) && !Lang.TYPEREF.test(token))
+ throw Error("illegal message field: " + token);
+ fld["type"] = token;
+ this.tn.skip('>');
+ token = this.tn.next();
+ if (!Lang.NAME.test(token))
+ throw Error("illegal message field name: " + token);
+ fld["name"] = token;
+ this.tn.skip("=");
+ fld["id"] = mkId(this.tn.next());
+ token = this.tn.peek();
+ if (token === '[')
+ this._parseFieldOptions(fld);
+ this.tn.skip(";");
+
+ } else {
+
+ type = typeof type !== 'undefined' ? type : this.tn.next();
+
+ if (type === "group") {
+
+ // "A [legacy] group simply combines a nested message type and a field into a single declaration. In your
+ // code, you can treat this message just as if it had a Result type field called result (the latter name is
+ // converted to lower-case so that it does not conflict with the former)."
+ var grp = this._parseMessage(msg, fld);
+ if (!/^[A-Z]/.test(grp["name"]))
+ throw Error('illegal group name: '+grp["name"]);
+ fld["type"] = grp["name"];
+ fld["name"] = grp["name"].toLowerCase();
+ this.tn.omit(";");
+
+ } else {
+
+ if (!Lang.TYPE.test(type) && !Lang.TYPEREF.test(type))
+ throw Error("illegal message field type: " + type);
+ fld["type"] = type;
+ token = this.tn.next();
+ if (!Lang.NAME.test(token))
+ throw Error("illegal message field name: " + token);
+ fld["name"] = token;
+ this.tn.skip("=");
+ fld["id"] = mkId(this.tn.next());
+ token = this.tn.peek();
+ if (token === "[")
+ this._parseFieldOptions(fld);
+ this.tn.skip(";");
+
+ }
+ }
+ msg["fields"].push(fld);
+ return fld;
+ };
+
+ /**
+ * Parses a message oneof.
+ * @param {!Object} msg Message definition
+ * @private
+ */
+ ParserPrototype._parseMessageOneOf = function(msg) {
+ var token = this.tn.next();
+ if (!Lang.NAME.test(token))
+ throw Error("illegal oneof name: "+token);
+ var name = token,
+ fld;
+ var fields = [];
+ this.tn.skip("{");
+ while ((token = this.tn.next()) !== "}") {
+ fld = this._parseMessageField(msg, "optional", token);
+ fld["oneof"] = name;
+ fields.push(fld["id"]);
+ }
+ this.tn.omit(";");
+ msg["oneofs"][name] = fields;
+ };
+
+ /**
+ * Parses a set of field option definitions.
+ * @param {!Object} fld Field definition
+ * @private
+ */
+ ParserPrototype._parseFieldOptions = function(fld) {
+ this.tn.skip("[");
+ var token,
+ first = true;
+ while ((token = this.tn.peek()) !== ']') {
+ if (!first)
+ this.tn.skip(",");
+ this._parseOption(fld, true);
+ first = false;
+ }
+ this.tn.next();
+ };
+
+ /**
+ * Parses an enum.
+ * @param {!Object} msg Message definition
+ * @private
+ */
+ ParserPrototype._parseEnum = function(msg) {
+ var enm = {
+ "name": "",
+ "values": [],
+ "options": {}
+ };
+ var token = this.tn.next();
+ if (!Lang.NAME.test(token))
+ throw Error("illegal name: "+token);
+ enm["name"] = token;
+ this.tn.skip("{");
+ while ((token = this.tn.next()) !== '}') {
+ if (token === "option")
+ this._parseOption(enm);
+ else {
+ if (!Lang.NAME.test(token))
+ throw Error("illegal name: "+token);
+ this.tn.skip("=");
+ var val = {
+ "name": token,
+ "id": mkId(this.tn.next(), true)
+ };
+ token = this.tn.peek();
+ if (token === "[")
+ this._parseFieldOptions({ "options": {} });
+ this.tn.skip(";");
+ enm["values"].push(val);
+ }
+ }
+ this.tn.omit(";");
+ msg["enums"].push(enm);
+ };
+
+ /**
+ * Parses extension / reserved ranges.
+ * @returns {!Array.>}
+ * @private
+ */
+ ParserPrototype._parseExtensionRanges = function() {
+ var ranges = [];
+ var token,
+ range,
+ value;
+ do {
+ range = [];
+ while (true) {
+ token = this.tn.next();
+ switch (token) {
+ case "min":
+ value = ProtoBuf.ID_MIN;
+ break;
+ case "max":
+ value = ProtoBuf.ID_MAX;
+ break;
+ default:
+ value = mkNumber(token);
+ break;
+ }
+ range.push(value);
+ if (range.length === 2)
+ break;
+ if (this.tn.peek() !== "to") {
+ range.push(value);
+ break;
+ }
+ this.tn.next();
+ }
+ ranges.push(range);
+ } while (this.tn.omit(","));
+ this.tn.skip(";");
+ return ranges;
+ };
+
+ /**
+ * Parses an extend block.
+ * @param {!Object} parent Parent object
+ * @private
+ */
+ ParserPrototype._parseExtend = function(parent) {
+ var token = this.tn.next();
+ if (!Lang.TYPEREF.test(token))
+ throw Error("illegal extend reference: "+token);
+ var ext = {
+ "ref": token,
+ "fields": []
+ };
+ this.tn.skip("{");
+ while ((token = this.tn.next()) !== '}') {
+ if (Lang.RULE.test(token))
+ this._parseMessageField(ext, token);
+ else if (Lang.TYPEREF.test(token)) {
+ if (!this.proto3)
+ throw Error("illegal field rule: "+token);
+ this._parseMessageField(ext, "optional", token);
+ } else
+ throw Error("illegal extend token: "+token);
+ }
+ this.tn.omit(";");
+ parent["messages"].push(ext);
+ return ext;
+ };
+
+ // ----- General -----
+
+ /**
+ * Returns a string representation of this parser.
+ * @returns {string}
+ */
+ ParserPrototype.toString = function() {
+ return "Parser at line "+this.tn.line;
+ };
+
+ /**
+ * @alias ProtoBuf.DotProto.Parser
+ * @expose
+ */
+ DotProto.Parser = Parser;
+
+ return DotProto;
+
+ })(ProtoBuf, ProtoBuf.Lang);
+
+ /**
+ * @alias ProtoBuf.Reflect
+ * @expose
+ */
+ ProtoBuf.Reflect = (function(ProtoBuf) {
+ "use strict";
+
+ /**
+ * Reflection types.
+ * @exports ProtoBuf.Reflect
+ * @namespace
+ */
+ var Reflect = {};
+
+ /**
+ * Constructs a Reflect base class.
+ * @exports ProtoBuf.Reflect.T
+ * @constructor
+ * @abstract
+ * @param {!ProtoBuf.Builder} builder Builder reference
+ * @param {?ProtoBuf.Reflect.T} parent Parent object
+ * @param {string} name Object name
+ */
+ var T = function(builder, parent, name) {
+
+ /**
+ * Builder reference.
+ * @type {!ProtoBuf.Builder}
+ * @expose
+ */
+ this.builder = builder;
+
+ /**
+ * Parent object.
+ * @type {?ProtoBuf.Reflect.T}
+ * @expose
+ */
+ this.parent = parent;
+
+ /**
+ * Object name in namespace.
+ * @type {string}
+ * @expose
+ */
+ this.name = name;
+
+ /**
+ * Fully qualified class name
+ * @type {string}
+ * @expose
+ */
+ this.className;
+ };
+
+ /**
+ * @alias ProtoBuf.Reflect.T.prototype
+ * @inner
+ */
+ var TPrototype = T.prototype;
+
+ /**
+ * Returns the fully qualified name of this object.
+ * @returns {string} Fully qualified name as of ".PATH.TO.THIS"
+ * @expose
+ */
+ TPrototype.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
+ */
+ TPrototype.toString = function(includeClass) {
+ return (includeClass ? this.className + " " : "") + this.fqn();
+ };
+
+ /**
+ * Builds this type.
+ * @throws {Error} If this type cannot be built directly
+ * @expose
+ */
+ TPrototype.build = function() {
+ throw 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.Builder} builder Builder reference
+ * @param {?ProtoBuf.Reflect.Namespace} parent Namespace parent
+ * @param {string} name Namespace name
+ * @param {Object.=} options Namespace options
+ * @param {string?} syntax The syntax level of this definition (e.g., proto3)
+ * @constructor
+ * @extends ProtoBuf.Reflect.T
+ */
+ var Namespace = function(builder, parent, name, options, syntax) {
+ T.call(this, builder, parent, name);
+
+ /**
+ * @override
+ */
+ this.className = "Namespace";
+
+ /**
+ * Children inside the namespace.
+ * @type {!Array.}
+ */
+ this.children = [];
+
+ /**
+ * Options.
+ * @type {!Object.}
+ */
+ this.options = options || {};
+
+ /**
+ * Syntax level (e.g., proto2 or proto3).
+ * @type {!string}
+ */
+ this.syntax = syntax || "proto2";
+ };
+
+ /**
+ * @alias ProtoBuf.Reflect.Namespace.prototype
+ * @inner
+ */
+ var NamespacePrototype = 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
+ */
+ NamespacePrototype.getChildren = function(type) {
+ type = type || null;
+ if (type == null)
+ return this.children.slice();
+ var children = [];
+ for (var i=0, k=this.children.length; i} qn Qualified name to resolve
+ * @param {boolean=} excludeNonNamespace Excludes non-namespace types, defaults to `false`
+ * @return {?ProtoBuf.Reflect.Namespace} The resolved type or null if not found
+ * @expose
+ */
+ NamespacePrototype.resolve = function(qn, excludeNonNamespace) {
+ var part = typeof qn === 'string' ? qn.split(".") : qn,
+ ptr = this,
+ i = 0;
+ if (part[i] === "") { // Fully qualified name, e.g. ".My.Message'
+ while (ptr.parent !== null)
+ ptr = ptr.parent;
+ i++;
+ }
+ var child;
+ do {
+ do {
+ if (!(ptr instanceof Reflect.Namespace)) {
+ ptr = null;
+ break;
+ }
+ child = ptr.getChild(part[i]);
+ if (!child || !(child instanceof Reflect.T) || (excludeNonNamespace && !(child instanceof Reflect.Namespace))) {
+ ptr = null;
+ break;
+ }
+ ptr = child; i++;
+ } while (i < part.length);
+ if (ptr != null)
+ break; // Found
+ // Else search the parent
+ if (this.parent !== null)
+ return this.parent.resolve(qn, excludeNonNamespace);
+ } while (ptr != null);
+ return ptr;
+ };
+
+ /**
+ * Determines the shortest qualified name of the specified type, if any, relative to this namespace.
+ * @param {!ProtoBuf.Reflect.T} t Reflection type
+ * @returns {string} The shortest qualified name or, if there is none, the fqn
+ * @expose
+ */
+ NamespacePrototype.qn = function(t) {
+ var part = [], ptr = t;
+ do {
+ part.unshift(ptr.name);
+ ptr = ptr.parent;
+ } while (ptr !== null);
+ for (var len=1; len <= part.length; len++) {
+ var qn = part.slice(part.length-len);
+ if (t === this.resolve(qn, t instanceof Reflect.Namespace))
+ return qn.join(".");
+ }
+ return t.fqn();
+ };
+
+ /**
+ * Builds the namespace and returns the runtime counterpart.
+ * @return {Object.} Runtime namespace
+ * @expose
+ */
+ NamespacePrototype.build = function() {
+ /** @dict */
+ var ns = {};
+ var children = this.children;
+ for (var i=0, k=children.length, child; i}
+ */
+ NamespacePrototype.buildOpt = function() {
+ var opt = {},
+ keys = Object.keys(this.options);
+ for (var i=0, k=keys.length; i}null} Option value or NULL if there is no such option
+ */
+ NamespacePrototype.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 Element implementation that checks and converts values for a
+ * particular field type, as appropriate.
+ *
+ * An Element represents a single value: either the value of a singular field,
+ * or a value contained in one entry of a repeated field or map field. This
+ * class does not implement these higher-level concepts; it only encapsulates
+ * the low-level typechecking and conversion.
+ *
+ * @exports ProtoBuf.Reflect.Element
+ * @param {{name: string, wireType: number}} type Resolved data type
+ * @param {ProtoBuf.Reflect.T|null} resolvedType Resolved type, if relevant
+ * (e.g. submessage field).
+ * @param {boolean} isMapKey Is this element a Map key? The value will be
+ * converted to string form if so.
+ * @param {string} syntax Syntax level of defining message type, e.g.,
+ * proto2 or proto3.
+ * @constructor
+ */
+ var Element = function(type, resolvedType, isMapKey, syntax) {
+
+ /**
+ * Element type, as a string (e.g., int32).
+ * @type {{name: string, wireType: number}}
+ */
+ this.type = type;
+
+ /**
+ * Element type reference to submessage or enum definition, if needed.
+ * @type {ProtoBuf.Reflect.T|null}
+ */
+ this.resolvedType = resolvedType;
+
+ /**
+ * Element is a map key.
+ * @type {boolean}
+ */
+ this.isMapKey = isMapKey;
+
+ /**
+ * Syntax level of defining message type, e.g., proto2 or proto3.
+ * @type {string}
+ */
+ this.syntax = syntax;
+
+ if (isMapKey && ProtoBuf.MAP_KEY_TYPES.indexOf(type) < 0)
+ throw Error("Invalid map key type: " + type.name);
+ };
+
+ var ElementPrototype = Element.prototype;
+
+ /**
+ * Obtains a (new) default value for the specified type.
+ * @param type {string|{name: string, wireType: number}} Field type
+ * @returns {*} Default value
+ * @inner
+ */
+ function mkDefault(type) {
+ if (typeof type === 'string')
+ type = ProtoBuf.TYPES[type];
+ if (typeof type.defaultValue === 'undefined')
+ throw Error("default value for type "+type.name+" is not supported");
+ if (type == ProtoBuf.TYPES["bytes"])
+ return new ByteBuffer(0);
+ return type.defaultValue;
+ }
+
+ /**
+ * Returns the default value for this field in proto3.
+ * @function
+ * @param type {string|{name: string, wireType: number}} the field type
+ * @returns {*} Default value
+ */
+ Element.defaultFieldValue = mkDefault;
+
+ /**
+ * Makes a Long from a value.
+ * @param {{low: number, high: number, unsigned: boolean}|string|number} value Value
+ * @param {boolean=} unsigned Whether unsigned or not, defaults to reuse it from Long-like objects or to signed for
+ * strings and numbers
+ * @returns {!Long}
+ * @throws {Error} If the value cannot be converted to a Long
+ * @inner
+ */
+ function mkLong(value, unsigned) {
+ if (value && typeof value.low === 'number' && typeof value.high === 'number' && typeof value.unsigned === 'boolean'
+ && value.low === value.low && value.high === value.high)
+ return new ProtoBuf.Long(value.low, value.high, typeof unsigned === 'undefined' ? value.unsigned : unsigned);
+ if (typeof value === 'string')
+ return ProtoBuf.Long.fromString(value, unsigned || false, 10);
+ if (typeof value === 'number')
+ return ProtoBuf.Long.fromNumber(value, unsigned || false);
+ throw Error("not convertible to Long");
+ }
+
+ /**
+ * Checks if the given value can be set for an element of this type (singular
+ * field or one element of a repeated field or map).
+ * @param {*} value Value to check
+ * @return {*} Verified, maybe adjusted, value
+ * @throws {Error} If the value cannot be verified for this element slot
+ * @expose
+ */
+ ElementPrototype.verifyValue = function(value) {
+ var self = this;
+ function fail(val, msg) {
+ throw Error("Illegal value for "+self.toString(true)+" of type "+self.type.name+": "+val+" ("+msg+")");
+ }
+ switch (this.type) {
+ // Signed 32bit
+ case ProtoBuf.TYPES["int32"]:
+ case ProtoBuf.TYPES["sint32"]:
+ case ProtoBuf.TYPES["sfixed32"]:
+ // Account for !NaN: value === value
+ if (typeof value !== 'number' || (value === value && value % 1 !== 0))
+ fail(typeof value, "not an integer");
+ return value > 4294967295 ? value | 0 : value;
+
+ // Unsigned 32bit
+ case ProtoBuf.TYPES["uint32"]:
+ case ProtoBuf.TYPES["fixed32"]:
+ if (typeof value !== 'number' || (value === value && value % 1 !== 0))
+ fail(typeof value, "not an integer");
+ return value < 0 ? value >>> 0 : value;
+
+ // Signed 64bit
+ case ProtoBuf.TYPES["int64"]:
+ case ProtoBuf.TYPES["sint64"]:
+ case ProtoBuf.TYPES["sfixed64"]: {
+ if (ProtoBuf.Long)
+ try {
+ return mkLong(value, false);
+ } catch (e) {
+ fail(typeof value, e.message);
+ }
+ else
+ fail(typeof value, "requires Long.js");
+ }
+
+ // Unsigned 64bit
+ case ProtoBuf.TYPES["uint64"]:
+ case ProtoBuf.TYPES["fixed64"]: {
+ if (ProtoBuf.Long)
+ try {
+ return mkLong(value, true);
+ } catch (e) {
+ fail(typeof value, e.message);
+ }
+ else
+ fail(typeof value, "requires Long.js");
+ }
+
+ // Bool
+ case ProtoBuf.TYPES["bool"]:
+ if (typeof value !== 'boolean')
+ fail(typeof value, "not a boolean");
+ return value;
+
+ // Float
+ case ProtoBuf.TYPES["float"]:
+ case ProtoBuf.TYPES["double"]:
+ if (typeof value !== 'number')
+ fail(typeof value, "not a number");
+ return value;
+
+ // Length-delimited string
+ case ProtoBuf.TYPES["string"]:
+ if (typeof value !== 'string' && !(value && value instanceof String))
+ fail(typeof value, "not a string");
+ return ""+value; // Convert String object to string
+
+ // Length-delimited bytes
+ case ProtoBuf.TYPES["bytes"]:
+ if (ByteBuffer.isByteBuffer(value))
+ return value;
+ return ByteBuffer.wrap(value, "base64");
+
+ // Constant enum value
+ case ProtoBuf.TYPES["enum"]: {
+ var values = this.resolvedType.getChildren(ProtoBuf.Reflect.Enum.Value);
+ for (i=0; i 4294967295 || value < 0)
+ fail(typeof value, "not in range for uint32")
+ return value;
+ } else {
+ // proto2 requires enum values to be valid.
+ fail(value, "not a valid enum value");
+ }
+ }
+ // Embedded message
+ case ProtoBuf.TYPES["group"]:
+ case ProtoBuf.TYPES["message"]: {
+ if (!value || typeof value !== 'object')
+ fail(typeof value, "object expected");
+ if (value instanceof this.resolvedType.clazz)
+ return value;
+ if (value instanceof ProtoBuf.Builder.Message) {
+ // Mismatched type: Convert to object (see: https://github.com/dcodeIO/ProtoBuf.js/issues/180)
+ var obj = {};
+ for (var i in value)
+ if (value.hasOwnProperty(i))
+ obj[i] = value[i];
+ value = obj;
+ }
+ // Else let's try to construct one from a key-value object
+ return new (this.resolvedType.clazz)(value); // May throw for a hundred of reasons
+ }
+ }
+
+ // We should never end here
+ throw Error("[INTERNAL] Illegal value for "+this.toString(true)+": "+value+" (undefined type "+this.type+")");
+ };
+
+ /**
+ * Calculates the byte length of an element on the wire.
+ * @param {number} id Field number
+ * @param {*} value Field value
+ * @returns {number} Byte length
+ * @throws {Error} If the value cannot be calculated
+ * @expose
+ */
+ ElementPrototype.calculateLength = function(id, value) {
+ if (value === null) return 0; // Nothing to encode
+ // Tag has already been written
+ var n;
+ switch (this.type) {
+ case ProtoBuf.TYPES["int32"]:
+ return value < 0 ? ByteBuffer.calculateVarint64(value) : ByteBuffer.calculateVarint32(value);
+ case ProtoBuf.TYPES["uint32"]:
+ return ByteBuffer.calculateVarint32(value);
+ case ProtoBuf.TYPES["sint32"]:
+ return ByteBuffer.calculateVarint32(ByteBuffer.zigZagEncode32(value));
+ case ProtoBuf.TYPES["fixed32"]:
+ case ProtoBuf.TYPES["sfixed32"]:
+ case ProtoBuf.TYPES["float"]:
+ return 4;
+ case ProtoBuf.TYPES["int64"]:
+ case ProtoBuf.TYPES["uint64"]:
+ return ByteBuffer.calculateVarint64(value);
+ case ProtoBuf.TYPES["sint64"]:
+ return ByteBuffer.calculateVarint64(ByteBuffer.zigZagEncode64(value));
+ case ProtoBuf.TYPES["fixed64"]:
+ case ProtoBuf.TYPES["sfixed64"]:
+ return 8;
+ case ProtoBuf.TYPES["bool"]:
+ return 1;
+ case ProtoBuf.TYPES["enum"]:
+ return ByteBuffer.calculateVarint32(value);
+ case ProtoBuf.TYPES["double"]:
+ return 8;
+ case ProtoBuf.TYPES["string"]:
+ n = ByteBuffer.calculateUTF8Bytes(value);
+ return ByteBuffer.calculateVarint32(n) + n;
+ case ProtoBuf.TYPES["bytes"]:
+ if (value.remaining() < 0)
+ throw Error("Illegal value for "+this.toString(true)+": "+value.remaining()+" bytes remaining");
+ return ByteBuffer.calculateVarint32(value.remaining()) + value.remaining();
+ case ProtoBuf.TYPES["message"]:
+ n = this.resolvedType.calculate(value);
+ return ByteBuffer.calculateVarint32(n) + n;
+ case ProtoBuf.TYPES["group"]:
+ n = this.resolvedType.calculate(value);
+ return n + ByteBuffer.calculateVarint32((id << 3) | ProtoBuf.WIRE_TYPES.ENDGROUP);
+ }
+ // We should never end here
+ throw Error("[INTERNAL] Illegal value to encode in "+this.toString(true)+": "+value+" (unknown type)");
+ };
+
+ /**
+ * Encodes a value to the specified buffer. Does not encode the key.
+ * @param {number} id Field number
+ * @param {*} value Field value
+ * @param {ByteBuffer} buffer ByteBuffer to encode to
+ * @return {ByteBuffer} The ByteBuffer for chaining
+ * @throws {Error} If the value cannot be encoded
+ * @expose
+ */
+ ElementPrototype.encodeValue = function(id, value, buffer) {
+ if (value === null) return buffer; // Nothing to encode
+ // Tag has already been written
+
+ switch (this.type) {
+ // 32bit signed varint
+ case ProtoBuf.TYPES["int32"]:
+ // "If you use int32 or int64 as the type for a negative number, the resulting varint is always ten bytes
+ // long – it is, effectively, treated like a very large unsigned integer." (see #122)
+ if (value < 0)
+ buffer.writeVarint64(value);
+ else
+ buffer.writeVarint32(value);
+ break;
+
+ // 32bit unsigned varint
+ case ProtoBuf.TYPES["uint32"]:
+ buffer.writeVarint32(value);
+ break;
+
+ // 32bit varint zig-zag
+ case ProtoBuf.TYPES["sint32"]:
+ buffer.writeVarint32ZigZag(value);
+ break;
+
+ // Fixed unsigned 32bit
+ case ProtoBuf.TYPES["fixed32"]:
+ buffer.writeUint32(value);
+ break;
+
+ // Fixed signed 32bit
+ case ProtoBuf.TYPES["sfixed32"]:
+ buffer.writeInt32(value);
+ break;
+
+ // 64bit varint as-is
+ case ProtoBuf.TYPES["int64"]:
+ case ProtoBuf.TYPES["uint64"]:
+ buffer.writeVarint64(value); // throws
+ break;
+
+ // 64bit varint zig-zag
+ case ProtoBuf.TYPES["sint64"]:
+ buffer.writeVarint64ZigZag(value); // throws
+ break;
+
+ // Fixed unsigned 64bit
+ case ProtoBuf.TYPES["fixed64"]:
+ buffer.writeUint64(value); // throws
+ break;
+
+ // Fixed signed 64bit
+ case ProtoBuf.TYPES["sfixed64"]:
+ buffer.writeInt64(value); // throws
+ break;
+
+ // Bool
+ case ProtoBuf.TYPES["bool"]:
+ if (typeof value === 'string')
+ buffer.writeVarint32(value.toLowerCase() === 'false' ? 0 : !!value);
+ else
+ buffer.writeVarint32(value ? 1 : 0);
+ break;
+
+ // Constant enum value
+ case ProtoBuf.TYPES["enum"]:
+ buffer.writeVarint32(value);
+ break;
+
+ // 32bit float
+ case ProtoBuf.TYPES["float"]:
+ buffer.writeFloat32(value);
+ break;
+
+ // 64bit float
+ case ProtoBuf.TYPES["double"]:
+ buffer.writeFloat64(value);
+ break;
+
+ // Length-delimited string
+ case ProtoBuf.TYPES["string"]:
+ buffer.writeVString(value);
+ break;
+
+ // Length-delimited bytes
+ case ProtoBuf.TYPES["bytes"]:
+ if (value.remaining() < 0)
+ throw Error("Illegal value for "+this.toString(true)+": "+value.remaining()+" bytes remaining");
+ var prevOffset = value.offset;
+ buffer.writeVarint32(value.remaining());
+ buffer.append(value);
+ value.offset = prevOffset;
+ break;
+
+ // Embedded message
+ case ProtoBuf.TYPES["message"]:
+ var bb = new ByteBuffer().LE();
+ this.resolvedType.encode(value, bb);
+ buffer.writeVarint32(bb.offset);
+ buffer.append(bb.flip());
+ break;
+
+ // Legacy group
+ case ProtoBuf.TYPES["group"]:
+ this.resolvedType.encode(value, buffer);
+ buffer.writeVarint32((id << 3) | ProtoBuf.WIRE_TYPES.ENDGROUP);
+ break;
+
+ default:
+ // We should never end here
+ throw Error("[INTERNAL] Illegal value to encode in "+this.toString(true)+": "+value+" (unknown type)");
+ }
+ return buffer;
+ };
+
+ /**
+ * Decode one element value from the specified buffer.
+ * @param {ByteBuffer} buffer ByteBuffer to decode from
+ * @param {number} wireType The field wire type
+ * @param {number} id The field number
+ * @return {*} Decoded value
+ * @throws {Error} If the field cannot be decoded
+ * @expose
+ */
+ ElementPrototype.decode = function(buffer, wireType, id) {
+ if (wireType != this.type.wireType)
+ throw Error("Unexpected wire type for element");
+
+ var value, nBytes;
+ switch (this.type) {
+ // 32bit signed varint
+ case ProtoBuf.TYPES["int32"]:
+ return buffer.readVarint32() | 0;
+
+ // 32bit unsigned varint
+ case ProtoBuf.TYPES["uint32"]:
+ return buffer.readVarint32() >>> 0;
+
+ // 32bit signed varint zig-zag
+ case ProtoBuf.TYPES["sint32"]:
+ return buffer.readVarint32ZigZag() | 0;
+
+ // Fixed 32bit unsigned
+ case ProtoBuf.TYPES["fixed32"]:
+ return buffer.readUint32() >>> 0;
+
+ case ProtoBuf.TYPES["sfixed32"]:
+ return buffer.readInt32() | 0;
+
+ // 64bit signed varint
+ case ProtoBuf.TYPES["int64"]:
+ return buffer.readVarint64();
+
+ // 64bit unsigned varint
+ case ProtoBuf.TYPES["uint64"]:
+ return buffer.readVarint64().toUnsigned();
+
+ // 64bit signed varint zig-zag
+ case ProtoBuf.TYPES["sint64"]:
+ return buffer.readVarint64ZigZag();
+
+ // Fixed 64bit unsigned
+ case ProtoBuf.TYPES["fixed64"]:
+ return buffer.readUint64();
+
+ // Fixed 64bit signed
+ case ProtoBuf.TYPES["sfixed64"]:
+ return buffer.readInt64();
+
+ // Bool varint
+ case ProtoBuf.TYPES["bool"]:
+ return !!buffer.readVarint32();
+
+ // Constant enum value (varint)
+ case ProtoBuf.TYPES["enum"]:
+ // The following Builder.Message#set will already throw
+ return buffer.readVarint32();
+
+ // 32bit float
+ case ProtoBuf.TYPES["float"]:
+ return buffer.readFloat();
+
+ // 64bit float
+ case ProtoBuf.TYPES["double"]:
+ return buffer.readDouble();
+
+ // Length-delimited string
+ case ProtoBuf.TYPES["string"]:
+ return buffer.readVString();
+
+ // Length-delimited bytes
+ case ProtoBuf.TYPES["bytes"]: {
+ nBytes = buffer.readVarint32();
+ if (buffer.remaining() < nBytes)
+ throw Error("Illegal number of bytes for "+this.toString(true)+": "+nBytes+" required but got only "+buffer.remaining());
+ value = buffer.clone(); // Offset already set
+ value.limit = value.offset+nBytes;
+ buffer.offset += nBytes;
+ return value;
+ }
+
+ // Length-delimited embedded message
+ case ProtoBuf.TYPES["message"]: {
+ nBytes = buffer.readVarint32();
+ return this.resolvedType.decode(buffer, nBytes);
+ }
+
+ // Legacy group
+ case ProtoBuf.TYPES["group"]:
+ return this.resolvedType.decode(buffer, -1, id);
+ }
+
+ // We should never end here
+ throw Error("[INTERNAL] Illegal decode type");
+ };
+
+ /**
+ * Converts a value from a string to the canonical element type.
+ *
+ * Legal only when isMapKey is true.
+ *
+ * @param {string} str The string value
+ * @returns {*} The value
+ */
+ ElementPrototype.valueFromString = function(str) {
+ if (!this.isMapKey) {
+ throw Error("valueFromString() called on non-map-key element");
+ }
+
+ switch (this.type) {
+ case ProtoBuf.TYPES["int32"]:
+ case ProtoBuf.TYPES["sint32"]:
+ case ProtoBuf.TYPES["sfixed32"]:
+ case ProtoBuf.TYPES["uint32"]:
+ case ProtoBuf.TYPES["fixed32"]:
+ return this.verifyValue(parseInt(str));
+
+ case ProtoBuf.TYPES["int64"]:
+ case ProtoBuf.TYPES["sint64"]:
+ case ProtoBuf.TYPES["sfixed64"]:
+ case ProtoBuf.TYPES["uint64"]:
+ case ProtoBuf.TYPES["fixed64"]:
+ // Long-based fields support conversions from string already.
+ return this.verifyValue(str);
+
+ case ProtoBuf.TYPES["bool"]:
+ return str === "true";
+
+ case ProtoBuf.TYPES["string"]:
+ return this.verifyValue(str);
+
+ case ProtoBuf.TYPES["bytes"]:
+ return ByteBuffer.fromBinary(str);
+ }
+ };
+
+ /**
+ * Converts a value from the canonical element type to a string.
+ *
+ * It should be the case that `valueFromString(valueToString(val))` returns
+ * a value equivalent to `verifyValue(val)` for every legal value of `val`
+ * according to this element type.
+ *
+ * This may be used when the element must be stored or used as a string,
+ * e.g., as a map key on an Object.
+ *
+ * Legal only when isMapKey is true.
+ *
+ * @param {*} val The value
+ * @returns {string} The string form of the value.
+ */
+ ElementPrototype.valueToString = function(value) {
+ if (!this.isMapKey) {
+ throw Error("valueToString() called on non-map-key element");
+ }
+
+ if (this.type === ProtoBuf.TYPES["bytes"]) {
+ return value.toString("binary");
+ } else {
+ return value.toString();
+ }
+ };
+
+ /**
+ * @alias ProtoBuf.Reflect.Element
+ * @expose
+ */
+ Reflect.Element = Element;
+
+ /**
+ * Constructs a new Message.
+ * @exports ProtoBuf.Reflect.Message
+ * @param {!ProtoBuf.Builder} builder Builder reference
+ * @param {!ProtoBuf.Reflect.Namespace} parent Parent message or namespace
+ * @param {string} name Message name
+ * @param {Object.=} options Message options
+ * @param {boolean=} isGroup `true` if this is a legacy group
+ * @param {string?} syntax The syntax level of this definition (e.g., proto3)
+ * @constructor
+ * @extends ProtoBuf.Reflect.Namespace
+ */
+ var Message = function(builder, parent, name, options, isGroup, syntax) {
+ Namespace.call(this, builder, parent, name, options, syntax);
+
+ /**
+ * @override
+ */
+ this.className = "Message";
+
+ /**
+ * Extensions range.
+ * @type {!Array.|undefined}
+ * @expose
+ */
+ this.extensions = undefined;
+
+ /**
+ * Runtime message class.
+ * @type {?function(new:ProtoBuf.Builder.Message)}
+ * @expose
+ */
+ this.clazz = null;
+
+ /**
+ * Whether this is a legacy group or not.
+ * @type {boolean}
+ * @expose
+ */
+ this.isGroup = !!isGroup;
+
+ // The following cached collections are used to efficiently iterate over or look up fields when decoding.
+
+ /**
+ * Cached fields.
+ * @type {?Array.}
+ * @private
+ */
+ this._fields = null;
+
+ /**
+ * Cached fields by id.
+ * @type {?Object.}
+ * @private
+ */
+ this._fieldsById = null;
+
+ /**
+ * Cached fields by name.
+ * @type {?Object.}
+ * @private
+ */
+ this._fieldsByName = null;
+ };
+
+ /**
+ * @alias ProtoBuf.Reflect.Message.prototype
+ * @inner
+ */
+ var MessagePrototype = 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
+ */
+ MessagePrototype.build = function(rebuild) {
+ if (this.clazz && !rebuild)
+ return this.clazz;
+
+ // Create the runtime Message class in its own scope
+ var clazz = (function(ProtoBuf, T) {
+
+ var fields = T.getChildren(ProtoBuf.Reflect.Message.Field),
+ oneofs = T.getChildren(ProtoBuf.Reflect.Message.OneOf);
+
+ /**
+ * Constructs a new runtime Message.
+ * @name ProtoBuf.Builder.Message
+ * @class Barebone of all runtime messages.
+ * @param {!Object.|string} values Preset values
+ * @param {...string} var_args
+ * @constructor
+ * @throws {Error} If the message cannot be created
+ */
+ var Message = function(values, var_args) {
+ ProtoBuf.Builder.Message.call(this);
+
+ // Create virtual oneof properties
+ for (var i=0, k=oneofs.length; i 0) {
+ var value;
+ // Set field values from a values object
+ if (arguments.length === 1 && values !== null && typeof values === 'object' &&
+ /* not _another_ Message */ (typeof values.encode !== 'function' || values instanceof Message) &&
+ /* not a repeated field */ !Array.isArray(values) &&
+ /* not a Map */ !(values instanceof ProtoBuf.Map) &&
+ /* not a ByteBuffer */ !ByteBuffer.isByteBuffer(values) &&
+ /* not an ArrayBuffer */ !(values instanceof ArrayBuffer) &&
+ /* not a Long */ !(ProtoBuf.Long && values instanceof ProtoBuf.Long)) {
+ this.$set(values);
+ } else // Set field values from arguments, in declaration order
+ for (i=0, k=arguments.length; i} keyOrObj String key or plain object holding multiple values
+ * @param {(*|boolean)=} value Value to set if key is a string, otherwise omitted
+ * @param {boolean=} noAssert Whether to not assert for an actual field / proper value type, defaults to `false`
+ * @returns {!ProtoBuf.Builder.Message} this
+ * @throws {Error} If the value cannot be set
+ * @expose
+ */
+ MessagePrototype.set = function(keyOrObj, value, noAssert) {
+ if (keyOrObj && typeof keyOrObj === 'object') {
+ noAssert = value;
+ for (var ikey in keyOrObj)
+ if (keyOrObj.hasOwnProperty(ikey) && typeof (value = keyOrObj[ikey]) !== 'undefined')
+ this.$set(ikey, value, noAssert);
+ return this;
+ }
+ var field = T._fieldsByName[keyOrObj];
+ if (!noAssert) {
+ if (!field)
+ throw Error(this+"#"+keyOrObj+" is not a field: undefined");
+ if (!(field instanceof ProtoBuf.Reflect.Message.Field))
+ throw Error(this+"#"+keyOrObj+" is not a field: "+field.toString(true));
+ this[field.name] = (value = field.verifyValue(value)); // May throw
+ } else
+ this[keyOrObj] = value;
+ if (field && field.oneof) { // Field is part of an OneOf (not a virtual OneOf field)
+ var currentField = this[field.oneof.name]; // Virtual field references currently set field
+ if (value !== null) {
+ if (currentField !== null && currentField !== field.name)
+ this[currentField] = null; // Clear currently set field
+ this[field.oneof.name] = field.name; // Point virtual field at this field
+ } else if (/* value === null && */currentField === keyOrObj)
+ this[field.oneof.name] = null; // Clear virtual field (current field explicitly cleared)
+ }
+ return this;
+ };
+
+ /**
+ * Sets a field's value. This is an alias for [@link ProtoBuf.Builder.Message#set}.
+ * @name ProtoBuf.Builder.Message#$set
+ * @function
+ * @param {string|!Object.} keyOrObj String key or plain object holding multiple values
+ * @param {(*|boolean)=} value Value to set if key is a string, otherwise omitted
+ * @param {boolean=} noAssert Whether to not assert the value, defaults to `false`
+ * @throws {Error} If the value cannot be set
+ * @expose
+ */
+ MessagePrototype.$set = MessagePrototype.set;
+
+ /**
+ * Gets a field's value.
+ * @name ProtoBuf.Builder.Message#get
+ * @function
+ * @param {string} key Key
+ * @param {boolean=} noAssert Whether to not assert for an actual field, defaults to `false`
+ * @return {*} Value
+ * @throws {Error} If there is no such field
+ * @expose
+ */
+ MessagePrototype.get = function(key, noAssert) {
+ if (noAssert)
+ return this[key];
+ var field = T._fieldsByName[key];
+ if (!field || !(field instanceof ProtoBuf.Reflect.Message.Field))
+ throw Error(this+"#"+key+" is not a field: undefined");
+ if (!(field instanceof ProtoBuf.Reflect.Message.Field))
+ throw Error(this+"#"+key+" is not a field: "+field.toString(true));
+ return this[field.name];
+ };
+
+ /**
+ * Gets a field's value. This is an alias for {@link ProtoBuf.Builder.Message#$get}.
+ * @name ProtoBuf.Builder.Message#$get
+ * @function
+ * @param {string} key Key
+ * @return {*} Value
+ * @throws {Error} If there is no such field
+ * @expose
+ */
+ MessagePrototype.$get = MessagePrototype.get;
+
+ // Getters and setters
+
+ for (var i=0; i} data Data payload
+ * @param {(!ByteBuffer|boolean)=} buffer ByteBuffer to encode to. Will create a new one and flip it if omitted.
+ * @param {boolean=} noVerify Whether to not verify field values, defaults to `false`
+ * @return {!ByteBuffer} Encoded message as a ByteBuffer
+ * @expose
+ */
+ Message.encode = function(data, buffer, noVerify) {
+ return new Message(data).encode(buffer, noVerify);
+ };
+
+ /**
+ * Calculates the byte length of the message.
+ * @name ProtoBuf.Builder.Message#calculate
+ * @function
+ * @returns {number} Byte length
+ * @throws {Error} If the message cannot be calculated or if required fields are missing.
+ * @expose
+ */
+ MessagePrototype.calculate = function() {
+ return T.calculate(this);
+ };
+
+ /**
+ * Encodes the varint32 length-delimited message.
+ * @name ProtoBuf.Builder.Message#encodeDelimited
+ * @function
+ * @param {(!ByteBuffer|boolean)=} buffer ByteBuffer to encode to. Will create a new one and flip it if omitted.
+ * @param {boolean=} noVerify Whether to not verify field values, defaults to `false`
+ * @return {!ByteBuffer} Encoded message as a ByteBuffer
+ * @throws {Error} If the message cannot be encoded or if required fields are missing. The later still
+ * returns the encoded ByteBuffer in the `encoded` property on the error.
+ * @expose
+ */
+ MessagePrototype.encodeDelimited = function(buffer, noVerify) {
+ var isNew = false;
+ if (!buffer)
+ buffer = new ByteBuffer(),
+ isNew = true;
+ var enc = new ByteBuffer().LE();
+ T.encode(this, enc, noVerify).flip();
+ buffer.writeVarint32(enc.remaining());
+ buffer.append(enc);
+ return isNew ? buffer.flip() : buffer;
+ };
+
+ /**
+ * Directly encodes the message to an ArrayBuffer.
+ * @name ProtoBuf.Builder.Message#encodeAB
+ * @function
+ * @return {ArrayBuffer} Encoded message as ArrayBuffer
+ * @throws {Error} If the message cannot be encoded or if required fields are missing. The later still
+ * returns the encoded ArrayBuffer in the `encoded` property on the error.
+ * @expose
+ */
+ MessagePrototype.encodeAB = function() {
+ try {
+ return this.encode().toArrayBuffer();
+ } catch (e) {
+ if (e["encoded"]) e["encoded"] = e["encoded"].toArrayBuffer();
+ throw(e);
+ }
+ };
+
+ /**
+ * Returns the message as an ArrayBuffer. This is an alias for {@link ProtoBuf.Builder.Message#encodeAB}.
+ * @name ProtoBuf.Builder.Message#toArrayBuffer
+ * @function
+ * @return {ArrayBuffer} Encoded message as ArrayBuffer
+ * @throws {Error} If the message cannot be encoded or if required fields are missing. The later still
+ * returns the encoded ArrayBuffer in the `encoded` property on the error.
+ * @expose
+ */
+ MessagePrototype.toArrayBuffer = MessagePrototype.encodeAB;
+
+ /**
+ * Directly encodes the message to a node Buffer.
+ * @name ProtoBuf.Builder.Message#encodeNB
+ * @function
+ * @return {!Buffer}
+ * @throws {Error} If the message cannot be encoded, not running under node.js or if required fields are
+ * missing. The later still returns the encoded node Buffer in the `encoded` property on the error.
+ * @expose
+ */
+ MessagePrototype.encodeNB = function() {
+ try {
+ return this.encode().toBuffer();
+ } catch (e) {
+ if (e["encoded"]) e["encoded"] = e["encoded"].toBuffer();
+ throw(e);
+ }
+ };
+
+ /**
+ * Returns the message as a node Buffer. This is an alias for {@link ProtoBuf.Builder.Message#encodeNB}.
+ * @name ProtoBuf.Builder.Message#toBuffer
+ * @function
+ * @return {!Buffer}
+ * @throws {Error} If the message cannot be encoded or if required fields are missing. The later still
+ * returns the encoded node Buffer in the `encoded` property on the error.
+ * @expose
+ */
+ MessagePrototype.toBuffer = MessagePrototype.encodeNB;
+
+ /**
+ * Directly encodes the message to a base64 encoded string.
+ * @name ProtoBuf.Builder.Message#encode64
+ * @function
+ * @return {string} Base64 encoded string
+ * @throws {Error} If the underlying buffer cannot be encoded or if required fields are missing. The later
+ * still returns the encoded base64 string in the `encoded` property on the error.
+ * @expose
+ */
+ MessagePrototype.encode64 = function() {
+ try {
+ return this.encode().toBase64();
+ } catch (e) {
+ if (e["encoded"]) e["encoded"] = e["encoded"].toBase64();
+ throw(e);
+ }
+ };
+
+ /**
+ * Returns the message as a base64 encoded string. This is an alias for {@link ProtoBuf.Builder.Message#encode64}.
+ * @name ProtoBuf.Builder.Message#toBase64
+ * @function
+ * @return {string} Base64 encoded string
+ * @throws {Error} If the message cannot be encoded or if required fields are missing. The later still
+ * returns the encoded base64 string in the `encoded` property on the error.
+ * @expose
+ */
+ MessagePrototype.toBase64 = MessagePrototype.encode64;
+
+ /**
+ * Directly encodes the message to a hex encoded string.
+ * @name ProtoBuf.Builder.Message#encodeHex
+ * @function
+ * @return {string} Hex encoded string
+ * @throws {Error} If the underlying buffer cannot be encoded or if required fields are missing. The later
+ * still returns the encoded hex string in the `encoded` property on the error.
+ * @expose
+ */
+ MessagePrototype.encodeHex = function() {
+ try {
+ return this.encode().toHex();
+ } catch (e) {
+ if (e["encoded"]) e["encoded"] = e["encoded"].toHex();
+ throw(e);
+ }
+ };
+
+ /**
+ * Returns the message as a hex encoded string. This is an alias for {@link ProtoBuf.Builder.Message#encodeHex}.
+ * @name ProtoBuf.Builder.Message#toHex
+ * @function
+ * @return {string} Hex encoded string
+ * @throws {Error} If the message cannot be encoded or if required fields are missing. The later still
+ * returns the encoded hex string in the `encoded` property on the error.
+ * @expose
+ */
+ MessagePrototype.toHex = MessagePrototype.encodeHex;
+
+ /**
+ * Clones a message object or field value to a raw object.
+ * @param {*} obj Object to clone
+ * @param {boolean} binaryAsBase64 Whether to include binary data as base64 strings or as a buffer otherwise
+ * @param {boolean} longsAsStrings Whether to encode longs as strings
+ * @param {!ProtoBuf.Reflect.T=} resolvedType The resolved field type if a field
+ * @returns {*} Cloned object
+ * @inner
+ */
+ function cloneRaw(obj, binaryAsBase64, longsAsStrings, resolvedType) {
+ if (obj === null || typeof obj !== 'object') {
+ // Convert enum values to their respective names
+ if (resolvedType && resolvedType instanceof ProtoBuf.Reflect.Enum) {
+ var name = ProtoBuf.Reflect.Enum.getName(resolvedType.object, obj);
+ if (name !== null)
+ return name;
+ }
+ // Pass-through string, number, boolean, null...
+ return obj;
+ }
+ // Convert ByteBuffers to raw buffer or strings
+ if (ByteBuffer.isByteBuffer(obj))
+ return binaryAsBase64 ? obj.toBase64() : obj.toBuffer();
+ // Convert Longs to proper objects or strings
+ if (ProtoBuf.Long.isLong(obj))
+ return longsAsStrings ? obj.toString() : ProtoBuf.Long.fromValue(obj);
+ var clone;
+ // Clone arrays
+ if (Array.isArray(obj)) {
+ clone = [];
+ obj.forEach(function(v, k) {
+ clone[k] = cloneRaw(v, binaryAsBase64, longsAsStrings, resolvedType);
+ });
+ return clone;
+ }
+ clone = {};
+ // Convert maps to objects
+ if (obj instanceof ProtoBuf.Map) {
+ var it = obj.entries();
+ for (var e = it.next(); !e.done; e = it.next())
+ clone[obj.keyElem.valueToString(e.value[0])] = cloneRaw(e.value[1], binaryAsBase64, longsAsStrings, obj.valueElem.resolvedType);
+ return clone;
+ }
+ // Everything else is a non-null object
+ var type = obj.$type,
+ field = undefined;
+ for (var i in obj)
+ if (obj.hasOwnProperty(i)) {
+ if (type && (field = type.getChild(i)))
+ clone[i] = cloneRaw(obj[i], binaryAsBase64, longsAsStrings, field.resolvedType);
+ else
+ clone[i] = cloneRaw(obj[i], binaryAsBase64, longsAsStrings);
+ }
+ return clone;
+ }
+
+ /**
+ * Returns the message's raw payload.
+ * @param {boolean=} binaryAsBase64 Whether to include binary data as base64 strings instead of Buffers, defaults to `false`
+ * @param {boolean} longsAsStrings Whether to encode longs as strings
+ * @returns {Object.} Raw payload
+ * @expose
+ */
+ MessagePrototype.toRaw = function(binaryAsBase64, longsAsStrings) {
+ return cloneRaw(this, !!binaryAsBase64, !!longsAsStrings, this.$type);
+ };
+
+ /**
+ * Encodes a message to JSON.
+ * @returns {string} JSON string
+ * @expose
+ */
+ MessagePrototype.encodeJSON = function() {
+ return JSON.stringify(
+ cloneRaw(this,
+ /* binary-as-base64 */ true,
+ /* longs-as-strings */ true,
+ this.$type
+ )
+ );
+ };
+
+ /**
+ * Decodes a message from the specified buffer or string.
+ * @name ProtoBuf.Builder.Message.decode
+ * @function
+ * @param {!ByteBuffer|!ArrayBuffer|!Buffer|string} buffer Buffer to decode from
+ * @param {(number|string)=} length Message length. Defaults to decode all the remainig data.
+ * @param {string=} enc Encoding if buffer is a string: hex, utf8 (not recommended), defaults to base64
+ * @return {!ProtoBuf.Builder.Message} Decoded message
+ * @throws {Error} If the message cannot be decoded or if required fields are missing. The later still
+ * returns the decoded message with missing fields in the `decoded` property on the error.
+ * @expose
+ * @see ProtoBuf.Builder.Message.decode64
+ * @see ProtoBuf.Builder.Message.decodeHex
+ */
+ Message.decode = function(buffer, length, enc) {
+ if (typeof length === 'string')
+ enc = length,
+ length = -1;
+ if (typeof buffer === 'string')
+ buffer = ByteBuffer.wrap(buffer, enc ? enc : "base64");
+ buffer = ByteBuffer.isByteBuffer(buffer) ? buffer : ByteBuffer.wrap(buffer); // May throw
+ var le = buffer.littleEndian;
+ try {
+ var msg = T.decode(buffer.LE());
+ buffer.LE(le);
+ return msg;
+ } catch (e) {
+ buffer.LE(le);
+ throw(e);
+ }
+ };
+
+ /**
+ * Decodes a varint32 length-delimited message from the specified buffer or string.
+ * @name ProtoBuf.Builder.Message.decodeDelimited
+ * @function
+ * @param {!ByteBuffer|!ArrayBuffer|!Buffer|string} buffer Buffer to decode from
+ * @param {string=} enc Encoding if buffer is a string: hex, utf8 (not recommended), defaults to base64
+ * @return {ProtoBuf.Builder.Message} Decoded message or `null` if not enough bytes are available yet
+ * @throws {Error} If the message cannot be decoded or if required fields are missing. The later still
+ * returns the decoded message with missing fields in the `decoded` property on the error.
+ * @expose
+ */
+ Message.decodeDelimited = function(buffer, enc) {
+ if (typeof buffer === 'string')
+ buffer = ByteBuffer.wrap(buffer, enc ? enc : "base64");
+ buffer = ByteBuffer.isByteBuffer(buffer) ? buffer : ByteBuffer.wrap(buffer); // May throw
+ if (buffer.remaining() < 1)
+ return null;
+ var off = buffer.offset,
+ len = buffer.readVarint32();
+ if (buffer.remaining() < len) {
+ buffer.offset = off;
+ return null;
+ }
+ try {
+ var msg = T.decode(buffer.slice(buffer.offset, buffer.offset + len).LE());
+ buffer.offset += len;
+ return msg;
+ } catch (err) {
+ buffer.offset += len;
+ throw err;
+ }
+ };
+
+ /**
+ * Decodes the message from the specified base64 encoded string.
+ * @name ProtoBuf.Builder.Message.decode64
+ * @function
+ * @param {string} str String to decode from
+ * @return {!ProtoBuf.Builder.Message} Decoded message
+ * @throws {Error} If the message cannot be decoded or if required fields are missing. The later still
+ * returns the decoded message with missing fields in the `decoded` property on the error.
+ * @expose
+ */
+ Message.decode64 = function(str) {
+ return Message.decode(str, "base64");
+ };
+
+ /**
+ * Decodes the message from the specified hex encoded string.
+ * @name ProtoBuf.Builder.Message.decodeHex
+ * @function
+ * @param {string} str String to decode from
+ * @return {!ProtoBuf.Builder.Message} Decoded message
+ * @throws {Error} If the message cannot be decoded or if required fields are missing. The later still
+ * returns the decoded message with missing fields in the `decoded` property on the error.
+ * @expose
+ */
+ Message.decodeHex = function(str) {
+ return Message.decode(str, "hex");
+ };
+
+ /**
+ * Decodes the message from a JSON string.
+ * @name ProtoBuf.Builder.Message.decodeJSON
+ * @function
+ * @param {string} str String to decode from
+ * @return {!ProtoBuf.Builder.Message} Decoded message
+ * @throws {Error} If the message cannot be decoded or if required fields are
+ * missing.
+ * @expose
+ */
+ Message.decodeJSON = function(str) {
+ return new Message(JSON.parse(str));
+ };
+
+ // Utility
+
+ /**
+ * Returns a string representation of this Message.
+ * @name ProtoBuf.Builder.Message#toString
+ * @function
+ * @return {string} String representation as of ".Fully.Qualified.MessageName"
+ * @expose
+ */
+ MessagePrototype.toString = function() {
+ return T.toString();
+ };
+
+ // Properties
+
+ /**
+ * Message options.
+ * @name ProtoBuf.Builder.Message.$options
+ * @type {Object.}
+ * @expose
+ */
+ var $optionsS; // cc needs this
+
+ /**
+ * Message options.
+ * @name ProtoBuf.Builder.Message#$options
+ * @type {Object.}
+ * @expose
+ */
+ var $options;
+
+ /**
+ * Reflection type.
+ * @name ProtoBuf.Builder.Message.$type
+ * @type {!ProtoBuf.Reflect.Message}
+ * @expose
+ */
+ var $typeS;
+
+ /**
+ * Reflection type.
+ * @name ProtoBuf.Builder.Message#$type
+ * @type {!ProtoBuf.Reflect.Message}
+ * @expose
+ */
+ var $type;
+
+ if (Object.defineProperty)
+ Object.defineProperty(Message, '$options', { "value": T.buildOpt() }),
+ Object.defineProperty(MessagePrototype, "$options", { "value": Message["$options"] }),
+ Object.defineProperty(Message, "$type", { "value": T }),
+ Object.defineProperty(MessagePrototype, "$type", { "value": T });
+
+ return Message;
+
+ })(ProtoBuf, this);
+
+ // Static enums and prototyped sub-messages / cached collections
+ this._fields = [];
+ this._fieldsById = {};
+ this._fieldsByName = {};
+ for (var i=0, k=this.children.length, child; i>> 3;
+ switch (wireType) {
+ case ProtoBuf.WIRE_TYPES.VARINT:
+ do tag = buf.readUint8();
+ while ((tag & 0x80) === 0x80);
+ break;
+ case ProtoBuf.WIRE_TYPES.BITS64:
+ buf.offset += 8;
+ break;
+ case ProtoBuf.WIRE_TYPES.LDELIM:
+ tag = buf.readVarint32(); // reads the varint
+ buf.offset += tag; // skips n bytes
+ break;
+ case ProtoBuf.WIRE_TYPES.STARTGROUP:
+ skipTillGroupEnd(id, buf);
+ break;
+ case ProtoBuf.WIRE_TYPES.ENDGROUP:
+ if (id === expectedId)
+ return false;
+ else
+ throw Error("Illegal GROUPEND after unknown group: "+id+" ("+expectedId+" expected)");
+ case ProtoBuf.WIRE_TYPES.BITS32:
+ buf.offset += 4;
+ break;
+ default:
+ throw Error("Illegal wire type in unknown group "+expectedId+": "+wireType);
+ }
+ return true;
+ }
+
+ /**
+ * Decodes an encoded message and returns the decoded message.
+ * @param {ByteBuffer} buffer ByteBuffer to decode from
+ * @param {number=} length Message length. Defaults to decode all remaining data.
+ * @param {number=} expectedGroupEndId Expected GROUPEND id if this is a legacy group
+ * @return {ProtoBuf.Builder.Message} Decoded message
+ * @throws {Error} If the message cannot be decoded
+ * @expose
+ */
+ MessagePrototype.decode = function(buffer, length, expectedGroupEndId) {
+ length = typeof length === 'number' ? length : -1;
+ var start = buffer.offset,
+ msg = new (this.clazz)(),
+ tag, wireType, id, field;
+ while (buffer.offset < start+length || (length === -1 && buffer.remaining() > 0)) {
+ tag = buffer.readVarint32();
+ wireType = tag & 0x07;
+ id = tag >>> 3;
+ if (wireType === ProtoBuf.WIRE_TYPES.ENDGROUP) {
+ if (id !== expectedGroupEndId)
+ throw Error("Illegal group end indicator for "+this.toString(true)+": "+id+" ("+(expectedGroupEndId ? expectedGroupEndId+" expected" : "not a group")+")");
+ break;
+ }
+ if (!(field = this._fieldsById[id])) {
+ // "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;
+ case ProtoBuf.WIRE_TYPES.STARTGROUP:
+ while (skipTillGroupEnd(id, buffer)) {}
+ break;
+ default:
+ throw Error("Illegal wire type for unknown field "+id+" in "+this.toString(true)+"#decode: "+wireType);
+ }
+ continue;
+ }
+ if (field.repeated && !field.options["packed"]) {
+ msg[field.name].push(field.decode(wireType, buffer));
+ } else if (field.map) {
+ var keyval = field.decode(wireType, buffer);
+ msg[field.name].set(keyval[0], keyval[1]);
+ } else {
+ msg[field.name] = field.decode(wireType, buffer);
+ if (field.oneof) { // Field is part of an OneOf (not a virtual OneOf field)
+ var currentField = msg[field.oneof.name]; // Virtual field references currently set field
+ if (currentField !== null && currentField !== field.name)
+ msg[currentField] = null; // Clear currently set field
+ msg[field.oneof.name] = field.name; // Point virtual field at this field
+ }
+ }
+ }
+
+ // Check if all required fields are present and set default values for optional fields that are not
+ for (var i=0, k=this._fields.length; i=} options Options
+ * @param {!ProtoBuf.Reflect.Message.OneOf=} oneof Enclosing OneOf
+ * @param {string?} syntax The syntax level of this definition (e.g., proto3)
+ * @constructor
+ * @extends ProtoBuf.Reflect.T
+ */
+ var Field = function(builder, message, rule, keytype, type, name, id, options, oneof, syntax) {
+ T.call(this, builder, message, name);
+
+ /**
+ * @override
+ */
+ this.className = "Message.Field";
+
+ /**
+ * Message field required flag.
+ * @type {boolean}
+ * @expose
+ */
+ this.required = rule === "required";
+
+ /**
+ * Message field repeated flag.
+ * @type {boolean}
+ * @expose
+ */
+ this.repeated = rule === "repeated";
+
+ /**
+ * Message field map flag.
+ * @type {boolean}
+ * @expose
+ */
+ this.map = rule === "map";
+
+ /**
+ * Message field key type. Type reference string if unresolved, protobuf
+ * type if resolved. Valid only if this.map === true, null otherwise.
+ * @type {string|{name: string, wireType: number}|null}
+ * @expose
+ */
+ this.keyType = keytype || null;
+
+ /**
+ * Message field type. Type reference string if unresolved, protobuf type if
+ * resolved. In a map field, this is the value type.
+ * @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 || {};
+
+ /**
+ * Default value.
+ * @type {*}
+ * @expose
+ */
+ this.defaultValue = null;
+
+ /**
+ * Enclosing OneOf.
+ * @type {?ProtoBuf.Reflect.Message.OneOf}
+ * @expose
+ */
+ this.oneof = oneof || null;
+
+ /**
+ * Syntax level of this definition (e.g., proto3).
+ * @type {string}
+ * @expose
+ */
+ this.syntax = syntax || 'proto2';
+
+ /**
+ * Original field name.
+ * @type {string}
+ * @expose
+ */
+ this.originalName = this.name; // Used to revert camelcase transformation on naming collisions
+
+ /**
+ * Element implementation. Created in build() after types are resolved.
+ * @type {ProtoBuf.Element}
+ * @expose
+ */
+ this.element = null;
+
+ /**
+ * Key element implementation, for map fields. Created in build() after
+ * types are resolved.
+ * @type {ProtoBuf.Element}
+ * @expose
+ */
+ this.keyElement = null;
+
+ // Convert field names to camel case notation if the override is set
+ if (this.builder.options['convertFieldsToCamelCase'] && !(this instanceof Message.ExtensionField))
+ this.name = ProtoBuf.Util.toCamelCase(this.name);
+ };
+
+ /**
+ * @alias ProtoBuf.Reflect.Message.Field.prototype
+ * @inner
+ */
+ var FieldPrototype = Field.prototype = Object.create(T.prototype);
+
+ /**
+ * Builds the field.
+ * @override
+ * @expose
+ */
+ FieldPrototype.build = function() {
+ this.element = new Element(this.type, this.resolvedType, false, this.syntax);
+ if (this.map)
+ this.keyElement = new Element(this.keyType, undefined, true, this.syntax);
+
+ // In proto3, fields do not have field presence, and every field is set to
+ // its type's default value ("", 0, 0.0, or false).
+ if (this.syntax === 'proto3' && !this.repeated && !this.map)
+ this.defaultValue = Element.defaultFieldValue(this.type);
+
+ // Otherwise, default values are present when explicitly specified
+ else if (typeof this.options['default'] !== 'undefined')
+ this.defaultValue = this.verifyValue(this.options['default']);
+ };
+
+ /**
+ * 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
+ */
+ FieldPrototype.verifyValue = function(value, skipRepeated) {
+ skipRepeated = skipRepeated || false;
+ var self = this;
+ function fail(val, msg) {
+ throw Error("Illegal value for "+self.toString(true)+" of type "+self.type.name+": "+val+" ("+msg+")");
+ }
+ if (value === null) { // NULL values for optional fields
+ if (this.required)
+ fail(typeof value, "required");
+ if (this.syntax === 'proto3' && this.type !== ProtoBuf.TYPES["message"])
+ fail(typeof value, "proto3 field without field presence cannot be null");
+ return null;
+ }
+ var i;
+ if (this.repeated && !skipRepeated) { // Repeated values as arrays
+ if (!Array.isArray(value))
+ value = [value];
+ var res = [];
+ for (i=0; i 0;
+
+ case ProtoBuf.TYPES["bytes"]:
+ return value.remaining() > 0;
+
+ case ProtoBuf.TYPES["enum"]:
+ return value !== 0;
+
+ case ProtoBuf.TYPES["message"]:
+ return value !== null;
+ default:
+ return true;
+ }
+ };
+
+ /**
+ * Encodes the specified field value to the specified buffer.
+ * @param {*} value Verified field value
+ * @param {ByteBuffer} buffer ByteBuffer to encode to
+ * @param {!ProtoBuf.Builder.Message} message Runtime message
+ * @return {ByteBuffer} The ByteBuffer for chaining
+ * @throws {Error} If the field cannot be encoded
+ * @expose
+ */
+ FieldPrototype.encode = function(value, buffer, message) {
+ if (this.type === null || typeof this.type !== 'object')
+ throw Error("[INTERNAL] Unresolved type in "+this.toString(true)+": "+this.type);
+ if (value === null || (this.repeated && value.length == 0))
+ return buffer; // Optional omitted
+ try {
+ if (this.repeated) {
+ var i;
+ // "Only repeated fields of primitive numeric types (types which use the varint, 32-bit, or 64-bit wire
+ // types) can be declared 'packed'."
+ if (this.options["packed"] && ProtoBuf.PACKABLE_WIRE_TYPES.indexOf(this.type.wireType) >= 0) {
+ // "All of the elements of the field are packed into a single key-value pair with wire type 2
+ // (length-delimited). Each element is encoded the same way it would be normally, except without a
+ // tag preceding it."
+ buffer.writeVarint32((this.id << 3) | ProtoBuf.WIRE_TYPES.LDELIM);
+ buffer.ensureCapacity(buffer.offset += 1); // We do not know the length yet, so let's assume a varint of length 1
+ var start = buffer.offset; // Remember where the contents begin
+ 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= 0) {
+ n += ByteBuffer.calculateVarint32((this.id << 3) | ProtoBuf.WIRE_TYPES.LDELIM);
+ ni = 0;
+ for (i=0; i= 0) {
+ 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...
+ }
+
+ // Handle maps.
+ if (this.map) {
+ // Read one (key, value) submessage, and return [key, value]
+ var key = Element.defaultFieldValue(this.keyType);
+ value = Element.defaultFieldValue(this.type);
+
+ // Read the length
+ nBytes = buffer.readVarint32();
+ if (buffer.remaining() < nBytes)
+ throw Error("Illegal number of bytes for "+this.toString(true)+": "+nBytes+" required but got only "+buffer.remaining());
+
+ // Get a sub-buffer of this key/value submessage
+ var msgbuf = buffer.clone();
+ msgbuf.limit = msgbuf.offset + nBytes;
+ buffer.offset += nBytes;
+
+ while (msgbuf.remaining() > 0) {
+ var tag = msgbuf.readVarint32();
+ wireType = tag & 0x07;
+ var id = tag >>> 3;
+ if (id === 1) {
+ key = this.keyElement.decode(msgbuf, wireType, id);
+ } else if (id === 2) {
+ value = this.element.decode(msgbuf, wireType, id);
+ } else {
+ throw Error("Unexpected tag in map field key/value submessage");
+ }
+ }
+
+ return [key, value];
+ }
+
+ // Handle singular and non-packed repeated field values.
+ return this.element.decode(buffer, wireType, this.id);
+ };
+
+ /**
+ * @alias ProtoBuf.Reflect.Message.Field
+ * @expose
+ */
+ Reflect.Message.Field = Field;
+
+ /**
+ * Constructs a new Message ExtensionField.
+ * @exports ProtoBuf.Reflect.Message.ExtensionField
+ * @param {!ProtoBuf.Builder} builder Builder reference
+ * @param {!ProtoBuf.Reflect.Message} message Message reference
+ * @param {string} rule Rule, one of requried, optional, repeated
+ * @param {string} type Data type, e.g. int32
+ * @param {string} name Field name
+ * @param {number} id Unique field id
+ * @param {!Object.=} options Options
+ * @constructor
+ * @extends ProtoBuf.Reflect.Message.Field
+ */
+ var ExtensionField = function(builder, message, rule, type, name, id, options) {
+ Field.call(this, builder, message, rule, /* keytype = */ null, type, name, id, options);
+
+ /**
+ * Extension reference.
+ * @type {!ProtoBuf.Reflect.Extension}
+ * @expose
+ */
+ this.extension;
+ };
+
+ // Extends Field
+ ExtensionField.prototype = Object.create(Field.prototype);
+
+ /**
+ * @alias ProtoBuf.Reflect.Message.ExtensionField
+ * @expose
+ */
+ Reflect.Message.ExtensionField = ExtensionField;
+
+ /**
+ * Constructs a new Message OneOf.
+ * @exports ProtoBuf.Reflect.Message.OneOf
+ * @param {!ProtoBuf.Builder} builder Builder reference
+ * @param {!ProtoBuf.Reflect.Message} message Message reference
+ * @param {string} name OneOf name
+ * @constructor
+ * @extends ProtoBuf.Reflect.T
+ */
+ var OneOf = function(builder, message, name) {
+ T.call(this, builder, message, name);
+
+ /**
+ * Enclosed fields.
+ * @type {!Array.}
+ * @expose
+ */
+ this.fields = [];
+ };
+
+ /**
+ * @alias ProtoBuf.Reflect.Message.OneOf
+ * @expose
+ */
+ Reflect.Message.OneOf = OneOf;
+
+ /**
+ * Constructs a new Enum.
+ * @exports ProtoBuf.Reflect.Enum
+ * @param {!ProtoBuf.Builder} builder Builder reference
+ * @param {!ProtoBuf.Reflect.T} parent Parent Reflect object
+ * @param {string} name Enum name
+ * @param {Object.=} options Enum options
+ * @param {string?} syntax The syntax level (e.g., proto3)
+ * @constructor
+ * @extends ProtoBuf.Reflect.Namespace
+ */
+ var Enum = function(builder, parent, name, options, syntax) {
+ Namespace.call(this, builder, parent, name, options, syntax);
+
+ /**
+ * @override
+ */
+ this.className = "Enum";
+
+ /**
+ * Runtime enum object.
+ * @type {Object.|null}
+ * @expose
+ */
+ this.object = null;
+ };
+
+ /**
+ * Gets the string name of an enum value.
+ * @param {!ProtoBuf.Builder.Enum} enm Runtime enum
+ * @param {number} value Enum value
+ * @returns {?string} Name or `null` if not present
+ * @expose
+ */
+ Enum.getName = function(enm, value) {
+ var keys = Object.keys(enm);
+ for (var i=0, key; i}
+ * @expose
+ */
+ EnumPrototype.build = function(rebuild) {
+ if (this.object && !rebuild)
+ return this.object;
+ var enm = new ProtoBuf.Builder.Enum(),
+ values = this.getChildren(Enum.Value);
+ for (var i=0, k=values.length; i=} options Options
+ * @constructor
+ * @extends ProtoBuf.Reflect.Namespace
+ */
+ var Service = function(builder, root, name, options) {
+ Namespace.call(this, builder, root, name, options);
+
+ /**
+ * @override
+ */
+ this.className = "Service";
+
+ /**
+ * Built runtime service class.
+ * @type {?function(new:ProtoBuf.Builder.Service)}
+ */
+ this.clazz = null;
+ };
+
+ /**
+ * @alias ProtoBuf.Reflect.Service.prototype
+ * @inner
+ */
+ var ServicePrototype = 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
+ */
+ ServicePrototype.build = function(rebuild) {
+ if (this.clazz && !rebuild)
+ return this.clazz;
+
+ // Create the runtime Service class in its own scope
+ 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, Error("Not implemented, see: https://github.com/dcodeIO/ProtoBuf.js/wiki/Services")), 0); // Must be async!
+ };
+ };
+
+ /**
+ * @alias ProtoBuf.Builder.Service.prototype
+ * @inner
+ */
+ var ServicePrototype = Service.prototype = Object.create(ProtoBuf.Builder.Service.prototype);
+
+ /**
+ * 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(ProtoBuf.Reflect.Service.RPCMethod);
+ for (var i=0; i}
+ * @expose
+ */
+ var $optionsS; // cc needs this
+
+ /**
+ * Service options.
+ * @name ProtoBuf.Builder.Service#$options
+ * @type {Object.}
+ * @expose
+ */
+ var $options;
+
+ /**
+ * Reflection type.
+ * @name ProtoBuf.Builder.Service.$type
+ * @type {!ProtoBuf.Reflect.Service}
+ * @expose
+ */
+ var $typeS;
+
+ /**
+ * Reflection type.
+ * @name ProtoBuf.Builder.Service#$type
+ * @type {!ProtoBuf.Reflect.Service}
+ * @expose
+ */
+ var $type;
+
+ if (Object.defineProperty)
+ Object.defineProperty(Service, "$options", { "value": T.buildOpt() }),
+ Object.defineProperty(ServicePrototype, "$options", { "value": Service["$options"] }),
+ Object.defineProperty(Service, "$type", { "value": T }),
+ Object.defineProperty(ServicePrototype, "$type", { "value": T });
+
+ return Service;
+
+ })(ProtoBuf, this);
+ };
+
+ /**
+ * @alias ProtoBuf.Reflect.Service
+ * @expose
+ */
+ Reflect.Service = Service;
+
+ /**
+ * Abstract service method.
+ * @exports ProtoBuf.Reflect.Service.Method
+ * @param {!ProtoBuf.Builder} builder Builder reference
+ * @param {!ProtoBuf.Reflect.Service} svc Service
+ * @param {string} name Method name
+ * @param {Object.=} options Options
+ * @constructor
+ * @extends ProtoBuf.Reflect.T
+ */
+ var Method = function(builder, svc, name, options) {
+ T.call(this, builder, svc, name);
+
+ /**
+ * @override
+ */
+ this.className = "Service.Method";
+
+ /**
+ * Options.
+ * @type {Object.}
+ * @expose
+ */
+ this.options = options || {};
+ };
+
+ /**
+ * @alias ProtoBuf.Reflect.Service.Method.prototype
+ * @inner
+ */
+ var MethodPrototype = Method.prototype = Object.create(T.prototype);
+
+ /**
+ * Builds the method's '$options' property.
+ * @name ProtoBuf.Reflect.Service.Method#buildOpt
+ * @function
+ * @return {Object.}
+ */
+ MethodPrototype.buildOpt = NamespacePrototype.buildOpt;
+
+ /**
+ * @alias ProtoBuf.Reflect.Service.Method
+ * @expose
+ */
+ Reflect.Service.Method = Method;
+
+ /**
+ * RPC service method.
+ * @exports ProtoBuf.Reflect.Service.RPCMethod
+ * @param {!ProtoBuf.Builder} builder Builder reference
+ * @param {!ProtoBuf.Reflect.Service} svc Service
+ * @param {string} name Method name
+ * @param {string} request Request message name
+ * @param {string} response Response message name
+ * @param {boolean} request_stream Whether requests are streamed
+ * @param {boolean} response_stream Whether responses are streamed
+ * @param {Object.=} options Options
+ * @constructor
+ * @extends ProtoBuf.Reflect.Service.Method
+ */
+ var RPCMethod = function(builder, svc, name, request, response, request_stream, response_stream, options) {
+ Method.call(this, builder, svc, name, options);
+
+ /**
+ * @override
+ */
+ this.className = "Service.RPCMethod";
+
+ /**
+ * Request message name.
+ * @type {string}
+ * @expose
+ */
+ this.requestName = request;
+
+ /**
+ * Response message name.
+ * @type {string}
+ * @expose
+ */
+ this.responseName = response;
+
+ /**
+ * Whether requests are streamed
+ * @type {bool}
+ * @expose
+ */
+ this.requestStream = request_stream;
+
+ /**
+ * Whether responses are streamed
+ * @type {bool}
+ * @expose
+ */
+ this.responseStream = response_stream;
+
+ /**
+ * 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.
+ * @param {Object.=} options Options
+ * @constructor
+ */
+ var Builder = function(options) {
+
+ /**
+ * Namespace.
+ * @type {ProtoBuf.Reflect.Namespace}
+ * @expose
+ */
+ this.ns = new Reflect.Namespace(this, 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;
+
+ /**
+ * Options.
+ * @type {!Object.}
+ * @expose
+ */
+ this.options = options || {};
+ };
+
+ /**
+ * @alias ProtoBuf.Builder.prototype
+ * @inner
+ */
+ var BuilderPrototype = Builder.prototype;
+
+ // ----- Definition tests -----
+
+ /**
+ * Tests if a definition most likely describes a message.
+ * @param {!Object} def
+ * @returns {boolean}
+ * @expose
+ */
+ Builder.isMessage = function(def) {
+ // Messages require a string name
+ if (typeof def["name"] !== 'string')
+ return false;
+ // Messages do not contain values (enum) or rpc methods (service)
+ if (typeof def["values"] !== 'undefined' || typeof def["rpc"] !== 'undefined')
+ return false;
+ return true;
+ };
+
+ /**
+ * Tests if a definition most likely describes a message field.
+ * @param {!Object} def
+ * @returns {boolean}
+ * @expose
+ */
+ Builder.isMessageField = function(def) {
+ // Message fields require a string rule, name and type and an id
+ if (typeof def["rule"] !== 'string' || typeof def["name"] !== 'string' || typeof def["type"] !== 'string' || typeof def["id"] === 'undefined')
+ return false;
+ return true;
+ };
+
+ /**
+ * Tests if a definition most likely describes an enum.
+ * @param {!Object} def
+ * @returns {boolean}
+ * @expose
+ */
+ Builder.isEnum = function(def) {
+ // Enums require a string name
+ if (typeof def["name"] !== 'string')
+ return false;
+ // Enums require at least one value
+ if (typeof def["values"] === 'undefined' || !Array.isArray(def["values"]) || def["values"].length === 0)
+ return false;
+ return true;
+ };
+
+ /**
+ * Tests if a definition most likely describes a service.
+ * @param {!Object} def
+ * @returns {boolean}
+ * @expose
+ */
+ Builder.isService = function(def) {
+ // Services require a string name and an rpc object
+ if (typeof def["name"] !== 'string' || typeof def["rpc"] !== 'object' || !def["rpc"])
+ return false;
+ return true;
+ };
+
+ /**
+ * Tests if a definition most likely describes an extended message
+ * @param {!Object} def
+ * @returns {boolean}
+ * @expose
+ */
+ Builder.isExtend = function(def) {
+ // Extends rquire a string ref
+ if (typeof def["ref"] !== 'string')
+ return false;
+ return true;
+ };
+
+ // ----- Building -----
+
+ /**
+ * Resets the pointer to the root namespace.
+ * @returns {!ProtoBuf.Builder} this
+ * @expose
+ */
+ BuilderPrototype.reset = function() {
+ this.ptr = this.ns;
+ return this;
+ };
+
+ /**
+ * Defines a namespace on top of the current pointer position and places the pointer on it.
+ * @param {string} namespace
+ * @return {!ProtoBuf.Builder} this
+ * @expose
+ */
+ BuilderPrototype.define = function(namespace) {
+ if (typeof namespace !== 'string' || !Lang.TYPEREF.test(namespace))
+ throw Error("illegal namespace: "+namespace);
+ namespace.split(".").forEach(function(part) {
+ var ns = this.ptr.getChild(part);
+ if (ns === null) // Keep existing
+ this.ptr.addChild(ns = new Reflect.Namespace(this, this.ptr, part));
+ this.ptr = ns;
+ }, this);
+ return this;
+ };
+
+ /**
+ * Creates the specified definitions at the current pointer position.
+ * @param {!Array.} defs Messages, enums or services to create
+ * @returns {!ProtoBuf.Builder} this
+ * @throws {Error} If a message definition is invalid
+ * @expose
+ */
+ BuilderPrototype.create = function(defs) {
+ if (!defs)
+ return this; // Nothing to create
+ if (!Array.isArray(defs))
+ defs = [defs];
+ else {
+ if (defs.length === 0)
+ return this;
+ defs = defs.slice();
+ }
+
+ // It's quite hard to keep track of scopes and memory here, so let's do this iteratively.
+ var stack = [defs];
+ while (stack.length > 0) {
+ defs = stack.pop();
+
+ if (!Array.isArray(defs)) // Stack always contains entire namespaces
+ throw Error("not a valid namespace: "+JSON.stringify(defs));
+
+ while (defs.length > 0) {
+ var def = defs.shift(); // Namespaces always contain an array of messages, enums and services
+
+ if (Builder.isMessage(def)) {
+ var obj = new Reflect.Message(this, this.ptr, def["name"], def["options"], def["isGroup"], def["syntax"]);
+
+ // Create OneOfs
+ var oneofs = {};
+ if (def["oneofs"])
+ Object.keys(def["oneofs"]).forEach(function(name) {
+ obj.addChild(oneofs[name] = new Reflect.Message.OneOf(this, obj, name));
+ }, this);
+
+ // Create fields
+ if (def["fields"])
+ def["fields"].forEach(function(fld) {
+ if (obj.getChild(fld["id"]|0) !== null)
+ throw Error("duplicate or invalid field id in "+obj.name+": "+fld['id']);
+ if (fld["options"] && typeof fld["options"] !== 'object')
+ throw Error("illegal field options in "+obj.name+"#"+fld["name"]);
+ var oneof = null;
+ if (typeof fld["oneof"] === 'string' && !(oneof = oneofs[fld["oneof"]]))
+ throw Error("illegal oneof in "+obj.name+"#"+fld["name"]+": "+fld["oneof"]);
+ fld = new Reflect.Message.Field(this, obj, fld["rule"], fld["keytype"], fld["type"], fld["name"], fld["id"], fld["options"], oneof, def["syntax"]);
+ if (oneof)
+ oneof.fields.push(fld);
+ obj.addChild(fld);
+ }, this);
+
+ // Push children to stack
+ var subObj = [];
+ if (def["enums"])
+ def["enums"].forEach(function(enm) {
+ subObj.push(enm);
+ });
+ if (def["messages"])
+ def["messages"].forEach(function(msg) {
+ subObj.push(msg);
+ });
+ if (def["services"])
+ def["services"].forEach(function(svc) {
+ subObj.push(svc);
+ });
+
+ // Set extension ranges
+ if (def["extensions"]) {
+ if (typeof def["extensions"][0] === 'number') // pre 5.0.1
+ obj.extensions = [ def["extensions"] ];
+ else
+ obj.extensions = def["extensions"];
+ }
+
+ // Create on top of current namespace
+ this.ptr.addChild(obj);
+ 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;
+
+ } else if (Builder.isEnum(def)) {
+
+ obj = new Reflect.Enum(this, this.ptr, def["name"], def["options"], def["syntax"]);
+ def["values"].forEach(function(val) {
+ obj.addChild(new Reflect.Enum.Value(this, obj, val["name"], val["id"]));
+ }, this);
+ this.ptr.addChild(obj);
+
+ } else if (Builder.isService(def)) {
+
+ obj = new Reflect.Service(this, this.ptr, def["name"], def["options"]);
+ Object.keys(def["rpc"]).forEach(function(name) {
+ var mtd = def["rpc"][name];
+ obj.addChild(new Reflect.Service.RPCMethod(this, obj, name, mtd["request"], mtd["response"], !!mtd["request_stream"], !!mtd["response_stream"], mtd["options"]));
+ }, this);
+ this.ptr.addChild(obj);
+
+ } else if (Builder.isExtend(def)) {
+
+ obj = this.ptr.resolve(def["ref"], true);
+ if (obj) {
+ def["fields"].forEach(function(fld) {
+ if (obj.getChild(fld['id']|0) !== null)
+ throw Error("duplicate extended field id in "+obj.name+": "+fld['id']);
+ // Check if field id is allowed to be extended
+ if (obj.extensions) {
+ var valid = false;
+ obj.extensions.forEach(function(range) {
+ if (fld["id"] >= range[0] && fld["id"] <= range[1])
+ valid = true;
+ });
+ if (!valid)
+ throw Error("illegal extended field id in "+obj.name+": "+fld['id']+" (not within valid ranges)");
+ }
+ // Convert extension field names to camel case notation if the override is set
+ var name = fld["name"];
+ if (this.options['convertFieldsToCamelCase'])
+ name = ProtoBuf.Util.toCamelCase(name);
+ // see #161: Extensions use their fully qualified name as their runtime key and...
+ var field = new Reflect.Message.ExtensionField(this, obj, fld["rule"], fld["type"], this.ptr.fqn()+'.'+name, fld["id"], fld["options"]);
+ // ...are added on top of the current namespace as an extension which is used for
+ // resolving their type later on (the extension always keeps the original name to
+ // prevent naming collisions)
+ var ext = new Reflect.Extension(this, this.ptr, fld["name"], field);
+ field.extension = ext;
+ this.ptr.addChild(ext);
+ obj.addChild(field);
+ }, this);
+
+ } else if (!/\.?google\.protobuf\./.test(def["ref"])) // Silently skip internal extensions
+ throw Error("extended message "+def["ref"]+" is not defined");
+
+ } else
+ throw Error("not a valid definition: "+JSON.stringify(def));
+
+ def = null;
+ obj = null;
+ }
+ // Break goes here
+ defs = null;
+ this.ptr = this.ptr.parent; // Namespace done, continue at parent
+ }
+ this.resolved = false; // Require re-resolve
+ this.result = null; // Require re-build
+ return this;
+ };
+
+ /**
+ * Propagates syntax to all children.
+ * @param {!Object} parent
+ * @inner
+ */
+ function propagateSyntax(parent) {
+ if (parent['messages']) {
+ parent['messages'].forEach(function(child) {
+ child["syntax"] = parent["syntax"];
+ propagateSyntax(child);
+ });
+ }
+ if (parent['enums']) {
+ parent['enums'].forEach(function(child) {
+ child["syntax"] = parent["syntax"];
+ });
+ }
+ }
+
+ /**
+ * Imports another definition into this builder.
+ * @param {Object.} json Parsed import
+ * @param {(string|{root: string, file: string})=} filename Imported file name
+ * @returns {!ProtoBuf.Builder} this
+ * @throws {Error} If the definition or file cannot be imported
+ * @expose
+ */
+ BuilderPrototype["import"] = function(json, filename) {
+ var delim = '/';
+
+ // Make sure to skip duplicate imports
+
+ if (typeof filename === 'string') {
+
+ if (ProtoBuf.Util.IS_NODE)
+ filename = require("path")['resolve'](filename);
+ if (this.files[filename] === true)
+ return this.reset();
+ this.files[filename] = true;
+
+ } else if (typeof filename === 'object') { // Object with root, file.
+
+ var root = filename.root;
+ if (ProtoBuf.Util.IS_NODE)
+ root = require("path")['resolve'](root);
+ if (root.indexOf("\\") >= 0 || filename.file.indexOf("\\") >= 0)
+ delim = '\\';
+ var fname = root + delim + filename.file;
+ if (this.files[fname] === true)
+ return this.reset();
+ this.files[fname] = true;
+ }
+
+ // Import imports
+
+ if (json['imports'] && json['imports'].length > 0) {
+ var importRoot,
+ 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)=} path Specifies what to return. If omitted, the entire namespace will be returned.
+ * @returns {!ProtoBuf.Builder.Message|!Object.}
+ * @throws {Error} If a type could not be resolved
+ * @expose
+ */
+ BuilderPrototype.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;
+ var part = typeof path === 'string' ? path.split(".") : path,
+ ptr = this.result; // Build namespace pointer (no hasChild etc.)
+ for (var i=0; i=} contents Initial contents
+ * @constructor
+ */
+ var Map = function(field, contents) {
+ if (!field.map)
+ throw Error("field is not a map");
+
+ /**
+ * The field corresponding to this map.
+ * @type {!ProtoBuf.Reflect.Field}
+ */
+ this.field = field;
+
+ /**
+ * Element instance corresponding to key type.
+ * @type {!ProtoBuf.Reflect.Element}
+ */
+ this.keyElem = new Reflect.Element(field.keyType, null, true, field.syntax);
+
+ /**
+ * Element instance corresponding to value type.
+ * @type {!ProtoBuf.Reflect.Element}
+ */
+ this.valueElem = new Reflect.Element(field.type, field.resolvedType, false, field.syntax);
+
+ /**
+ * Internal map: stores mapping of (string form of key) -> (key, value)
+ * pair.
+ *
+ * We provide map semantics for arbitrary key types, but we build on top
+ * of an Object, which has only string keys. In order to avoid the need
+ * to convert a string key back to its native type in many situations,
+ * we store the native key value alongside the value. Thus, we only need
+ * a one-way mapping from a key type to its string form that guarantees
+ * uniqueness and equality (i.e., str(K1) === str(K2) if and only if K1
+ * === K2).
+ *
+ * @type {!Object}
+ */
+ this.map = {};
+
+ /**
+ * Returns the number of elements in the map.
+ */
+ Object.defineProperty(this, "size", {
+ get: function() { return Object.keys(this.map).length; }
+ });
+
+ // Fill initial contents from a raw object.
+ if (contents) {
+ var keys = Object.keys(contents);
+ for (var i = 0; i < keys.length; i++) {
+ var key = this.keyElem.valueFromString(keys[i]);
+ var val = this.valueElem.verifyValue(contents[keys[i]]);
+ this.map[this.keyElem.valueToString(key)] =
+ { key: key, value: val };
+ }
+ }
+ };
+
+ var MapPrototype = Map.prototype;
+
+ /**
+ * Helper: return an iterator over an array.
+ * @param {!Array<*>} arr the array
+ * @returns {!Object} an iterator
+ * @inner
+ */
+ function arrayIterator(arr) {
+ var idx = 0;
+ return {
+ next: function() {
+ if (idx < arr.length)
+ return { done: false, value: arr[idx++] };
+ return { done: true };
+ }
+ }
+ }
+
+ /**
+ * Clears the map.
+ */
+ MapPrototype.clear = function() {
+ this.map = {};
+ };
+
+ /**
+ * Deletes a particular key from the map.
+ * @returns {boolean} Whether any entry with this key was deleted.
+ */
+ MapPrototype["delete"] = function(key) {
+ var keyValue = this.keyElem.valueToString(this.keyElem.verifyValue(key));
+ var hadKey = keyValue in this.map;
+ delete this.map[keyValue];
+ return hadKey;
+ };
+
+ /**
+ * Returns an iterator over [key, value] pairs in the map.
+ * @returns {Object} The iterator
+ */
+ MapPrototype.entries = function() {
+ var entries = [];
+ var strKeys = Object.keys(this.map);
+ for (var i = 0, entry; i < strKeys.length; i++)
+ entries.push([(entry=this.map[strKeys[i]]).key, entry.value]);
+ return arrayIterator(entries);
+ };
+
+ /**
+ * Returns an iterator over keys in the map.
+ * @returns {Object} The iterator
+ */
+ MapPrototype.keys = function() {
+ var keys = [];
+ var strKeys = Object.keys(this.map);
+ for (var i = 0; i < strKeys.length; i++)
+ keys.push(this.map[strKeys[i]].key);
+ return arrayIterator(keys);
+ };
+
+ /**
+ * Returns an iterator over values in the map.
+ * @returns {!Object} The iterator
+ */
+ MapPrototype.values = function() {
+ var values = [];
+ var strKeys = Object.keys(this.map);
+ for (var i = 0; i < strKeys.length; i++)
+ values.push(this.map[strKeys[i]].value);
+ return arrayIterator(values);
+ };
+
+ /**
+ * Iterates over entries in the map, calling a function on each.
+ * @param {function(this:*, *, *, *)} cb The callback to invoke with value, key, and map arguments.
+ * @param {Object=} thisArg The `this` value for the callback
+ */
+ MapPrototype.forEach = function(cb, thisArg) {
+ var strKeys = Object.keys(this.map);
+ for (var i = 0, entry; i < strKeys.length; i++)
+ cb.call(thisArg, (entry=this.map[strKeys[i]]).value, entry.key, this);
+ };
+
+ /**
+ * Sets a key in the map to the given value.
+ * @param {*} key The key
+ * @param {*} value The value
+ * @returns {!ProtoBuf.Map} The map instance
+ */
+ MapPrototype.set = function(key, value) {
+ var keyValue = this.keyElem.verifyValue(key);
+ var valValue = this.valueElem.verifyValue(value);
+ this.map[this.keyElem.valueToString(keyValue)] =
+ { key: keyValue, value: valValue };
+ return this;
+ };
+
+ /**
+ * Gets the value corresponding to a key in the map.
+ * @param {*} key The key
+ * @returns {*|undefined} The value, or `undefined` if key not present
+ */
+ MapPrototype.get = function(key) {
+ var keyValue = this.keyElem.valueToString(this.keyElem.verifyValue(key));
+ if (!(keyValue in this.map))
+ return undefined;
+ return this.map[keyValue].value;
+ };
+
+ /**
+ * Determines whether the given key is present in the map.
+ * @param {*} key The key
+ * @returns {boolean} `true` if the key is present
+ */
+ MapPrototype.has = function(key) {
+ var keyValue = this.keyElem.valueToString(this.keyElem.verifyValue(key));
+ return (keyValue in this.map);
+ };
+
+ return Map;
+ })(ProtoBuf, ProtoBuf.Reflect);
+
+
+ /**
+ * Loads a .proto string and returns the Builder.
+ * @param {string} proto .proto file contents
+ * @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.loadProto = function(proto, builder, filename) {
+ if (typeof builder === 'string' || (builder && typeof builder["file"] === 'string' && typeof builder["root"] === 'string'))
+ filename = builder,
+ builder = undefined;
+ return ProtoBuf.loadJson(ProtoBuf.DotProto.Parser.parse(proto), builder, filename);
+ };
+
+ /**
+ * Loads a .proto string and returns the Builder. This is an alias of {@link ProtoBuf.loadProto}.
+ * @function
+ * @param {string} proto .proto file contents
+ * @param {(ProtoBuf.Builder|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.protoFromString = ProtoBuf.loadProto; // Legacy
+
+ /**
+ * Loads a .proto file and returns the Builder.
+ * @param {string|{root: string, file: string}} filename Path to proto file or an object specifying 'file' with
+ * an overridden 'root' path for all imported files.
+ * @param {function(?Error, !ProtoBuf.Builder=)=} callback Callback that will receive `null` as the first and
+ * the Builder as its second argument on success, otherwise the error as its first argument. If omitted, the
+ * file will be read synchronously and this function will return the Builder.
+ * @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.loadProtoFile = function(filename, callback, builder) {
+ if (callback && typeof callback === 'object')
+ builder = callback,
+ callback = null;
+ else if (!callback || typeof callback !== 'function')
+ callback = null;
+ if (callback)
+ return ProtoBuf.Util.fetch(typeof filename === 'string' ? filename : filename["root"]+"/"+filename["file"], function(contents) {
+ if (contents === null) {
+ callback(Error("Failed to fetch file"));
+ return;
+ }
+ try {
+ callback(null, ProtoBuf.loadProto(contents, builder, filename));
+ } catch (e) {
+ callback(e);
+ }
+ });
+ var contents = ProtoBuf.Util.fetch(typeof filename === 'object' ? filename["root"]+"/"+filename["file"] : filename);
+ return contents === null ? null : ProtoBuf.loadProto(contents, builder, filename);
+ };
+
+ /**
+ * Loads a .proto file and returns the Builder. This is an alias of {@link ProtoBuf.loadProtoFile}.
+ * @function
+ * @param {string|{root: string, file: string}} filename Path to proto file or an object specifying 'file' with
+ * an overridden 'root' path for all imported files.
+ * @param {function(?Error, !ProtoBuf.Builder=)=} callback Callback that will receive `null` as the first and
+ * the Builder as its second argument on success, otherwise the error as its first argument. If omitted, the
+ * file will be read synchronously and this function will return the Builder.
+ * @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.protoFromFile = ProtoBuf.loadProtoFile; // Legacy
+
+
+ /**
+ * Constructs a new empty Builder.
+ * @param {Object.=} options Builder options, defaults to global options set on ProtoBuf
+ * @return {!ProtoBuf.Builder} Builder
+ * @expose
+ */
+ ProtoBuf.newBuilder = function(options) {
+ options = options || {};
+ if (typeof options['convertFieldsToCamelCase'] === 'undefined')
+ options['convertFieldsToCamelCase'] = ProtoBuf.convertFieldsToCamelCase;
+ if (typeof options['populateAccessors'] === 'undefined')
+ options['populateAccessors'] = ProtoBuf.populateAccessors;
+ return new ProtoBuf.Builder(options);
+ };
+
+ /**
+ * 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();
+ 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(?Error, !ProtoBuf.Builder=)=} callback Callback that will receive `null` as the first and
+ * the Builder as its second argument on success, otherwise the error as its first argument. If omitted, the
+ * file will be read synchronously and this function will return the Builder.
+ * @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)
+ return ProtoBuf.Util.fetch(typeof filename === 'string' ? filename : filename["root"]+"/"+filename["file"], function(contents) {
+ if (contents === null) {
+ callback(Error("Failed to fetch file"));
+ return;
+ }
+ try {
+ callback(null, ProtoBuf.loadJson(JSON.parse(contents), builder, filename));
+ } catch (e) {
+ callback(e);
+ }
+ });
+ var contents = ProtoBuf.Util.fetch(typeof filename === 'object' ? filename["root"]+"/"+filename["file"] : filename);
+ return contents === null ? null : ProtoBuf.loadJson(JSON.parse(contents), builder, filename);
+ };
+
+ return ProtoBuf;
+});
+
+(function() {
+ 'use strict';
+
+ function validatePrivKey(privKey) {
+ if (privKey === undefined || !(privKey instanceof ArrayBuffer) || privKey.byteLength != 32) {
+ throw new Error("Invalid private key");
+ }
+ }
+ function validatePubKeyFormat(pubKey) {
+ if (pubKey === undefined || ((pubKey.byteLength != 33 || new Uint8Array(pubKey)[0] != 5) && pubKey.byteLength != 32)) {
+ throw new Error("Invalid public key");
+ }
+ if (pubKey.byteLength == 33) {
+ return pubKey.slice(1);
+ } else {
+ console.error("WARNING: Expected pubkey of length 33, please report the ST and client that generated the pubkey");
+ return pubKey;
+ }
+ }
+
+ function processKeys(raw_keys) {
+ // prepend version byte
+ var origPub = new Uint8Array(raw_keys.pubKey);
+ var pub = new Uint8Array(33);
+ pub.set(origPub, 1);
+ pub[0] = 5;
+
+ return { pubKey: pub.buffer, privKey: raw_keys.privKey };
+ }
+
+ function wrapCurve25519(curve25519) {
+ return {
+ // Curve 25519 crypto
+ createKeyPair: function(privKey) {
+ validatePrivKey(privKey);
+ var raw_keys = curve25519.keyPair(privKey);
+ if (raw_keys instanceof Promise) {
+ return raw_keys.then(processKeys);
+ } else {
+ return processKeys(raw_keys);
+ }
+ },
+ ECDHE: function(pubKey, privKey) {
+ pubKey = validatePubKeyFormat(pubKey);
+ validatePrivKey(privKey);
+
+ if (pubKey === undefined || pubKey.byteLength != 32) {
+ throw new Error("Invalid public key");
+ }
+
+ return curve25519.sharedSecret(pubKey, privKey);
+ },
+ Ed25519Sign: function(privKey, message) {
+ validatePrivKey(privKey);
+
+ if (message === undefined) {
+ throw new Error("Invalid message");
+ }
+
+ return curve25519.sign(privKey, message);
+ },
+ Ed25519Verify: function(pubKey, msg, sig) {
+ pubKey = validatePubKeyFormat(pubKey);
+
+ if (pubKey === undefined || pubKey.byteLength != 32) {
+ throw new Error("Invalid public key");
+ }
+
+ if (msg === undefined) {
+ throw new Error("Invalid message");
+ }
+
+ if (sig === undefined || sig.byteLength != 64) {
+ throw new Error("Invalid signature");
+ }
+
+ return curve25519.verify(pubKey, msg, sig);
+ }
+ };
+ }
+
+ Internal.Curve = wrapCurve25519(Internal.curve25519);
+ Internal.Curve.async = wrapCurve25519(Internal.curve25519_async);
+
+ function wrapCurve(curve) {
+ return {
+ generateKeyPair: function() {
+ var privKey = Internal.crypto.getRandomBytes(32);
+ return curve.createKeyPair(privKey);
+ },
+ createKeyPair: function(privKey) {
+ return curve.createKeyPair(privKey);
+ },
+ calculateAgreement: function(pubKey, privKey) {
+ return curve.ECDHE(pubKey, privKey);
+ },
+ verifySignature: function(pubKey, msg, sig) {
+ return curve.Ed25519Verify(pubKey, msg, sig);
+ },
+ calculateSignature: function(privKey, message) {
+ return curve.Ed25519Sign(privKey, message);
+ }
+ };
+ }
+
+ libsignal.Curve = wrapCurve(Internal.Curve);
+ libsignal.Curve.async = wrapCurve(Internal.Curve.async);
+
+})();
+
+/*
+ * vim: ts=4:sw=4
+ */
+
+var Internal = Internal || {};
+
+(function() {
+ 'use strict';
+
+ var crypto = window.crypto;
+
+ if (!crypto || !crypto.subtle || typeof crypto.getRandomValues !== 'function') {
+ throw new Error('WebCrypto not found');
+ }
+
+ Internal.crypto = {
+ getRandomBytes: function(size) {
+ var array = new Uint8Array(size);
+ crypto.getRandomValues(array);
+ return array.buffer;
+ },
+ encrypt: function(key, data, iv) {
+ return crypto.subtle.importKey('raw', key, {name: 'AES-CBC'}, false, ['encrypt']).then(function(key) {
+ return crypto.subtle.encrypt({name: 'AES-CBC', iv: new Uint8Array(iv)}, key, data);
+ });
+ },
+ decrypt: function(key, data, iv) {
+ return crypto.subtle.importKey('raw', key, {name: 'AES-CBC'}, false, ['decrypt']).then(function(key) {
+ return crypto.subtle.decrypt({name: 'AES-CBC', iv: new Uint8Array(iv)}, key, data);
+ });
+ },
+ sign: function(key, data) {
+ return crypto.subtle.importKey('raw', key, {name: 'HMAC', hash: {name: 'SHA-256'}}, false, ['sign']).then(function(key) {
+ return crypto.subtle.sign( {name: 'HMAC', hash: 'SHA-256'}, key, data);
+ });
+ },
+
+ hash: function(data) {
+ return crypto.subtle.digest({name: 'SHA-512'}, data);
+ },
+
+ HKDF: function(input, salt, info) {
+ // Specific implementation of RFC 5869 that only returns the first 3 32-byte chunks
+ // TODO: We dont always need the third chunk, we might skip it
+ return Internal.crypto.sign(salt, input).then(function(PRK) {
+ var infoBuffer = new ArrayBuffer(info.byteLength + 1 + 32);
+ var infoArray = new Uint8Array(infoBuffer);
+ infoArray.set(new Uint8Array(info), 32);
+ infoArray[infoArray.length - 1] = 1;
+ return Internal.crypto.sign(PRK, infoBuffer.slice(32)).then(function(T1) {
+ infoArray.set(new Uint8Array(T1));
+ infoArray[infoArray.length - 1] = 2;
+ return Internal.crypto.sign(PRK, infoBuffer).then(function(T2) {
+ infoArray.set(new Uint8Array(T2));
+ infoArray[infoArray.length - 1] = 3;
+ return Internal.crypto.sign(PRK, infoBuffer).then(function(T3) {
+ return [ T1, T2, T3 ];
+ });
+ });
+ });
+ });
+ },
+
+ // Curve 25519 crypto
+ createKeyPair: function(privKey) {
+ if (privKey === undefined) {
+ privKey = Internal.crypto.getRandomBytes(32);
+ }
+ return Internal.Curve.async.createKeyPair(privKey);
+ },
+ ECDHE: function(pubKey, privKey) {
+ return Internal.Curve.async.ECDHE(pubKey, privKey);
+ },
+ Ed25519Sign: function(privKey, message) {
+ return Internal.Curve.async.Ed25519Sign(privKey, message);
+ },
+ Ed25519Verify: function(pubKey, msg, sig) {
+ return Internal.Curve.async.Ed25519Verify(pubKey, msg, sig);
+ }
+ };
+
+
+ // HKDF for TextSecure has a bit of additional handling - salts always end up being 32 bytes
+ Internal.HKDF = function(input, salt, info) {
+ if (salt.byteLength != 32) {
+ throw new Error("Got salt of incorrect length");
+ }
+
+ return Internal.crypto.HKDF(input, salt, util.toArrayBuffer(info));
+ };
+
+ Internal.verifyMAC = function(data, key, mac, length) {
+ return Internal.crypto.sign(key, data).then(function(calculated_mac) {
+ if (mac.byteLength != length || calculated_mac.byteLength < length) {
+ throw new Error("Bad MAC length");
+ }
+ var a = new Uint8Array(calculated_mac);
+ var b = new Uint8Array(mac);
+ var result = 0;
+ for (var i=0; i < mac.byteLength; ++i) {
+ result = result | (a[i] ^ b[i]);
+ }
+ if (result !== 0) {
+ throw new Error("Bad MAC");
+ }
+ });
+ };
+
+ libsignal.HKDF = {
+ deriveSecrets: function(input, salt, info) {
+ return Internal.HKDF(input, salt, info);
+ }
+ };
+
+ libsignal.crypto = {
+ encrypt: function(key, data, iv) {
+ return Internal.crypto.encrypt(key, data, iv);
+ },
+ decrypt: function(key, data, iv) {
+ return Internal.crypto.decrypt(key, data, iv);
+ },
+ calculateMAC: function(key, data) {
+ return Internal.crypto.sign(key, data);
+ },
+ verifyMAC: function(data, key, mac, length) {
+ return Internal.verifyMAC(data, key, mac, length);
+ },
+ getRandomBytes: function(size) {
+ return Internal.crypto.getRandomBytes(size);
+ }
+ };
+
+})();
+
+/*
+ * vim: ts=4:sw=4
+ */
+
+var util = (function() {
+ 'use strict';
+
+ var StaticArrayBufferProto = new ArrayBuffer().__proto__;
+
+ return {
+ toString: function(thing) {
+ if (typeof thing == 'string') {
+ return thing;
+ }
+ return new dcodeIO.ByteBuffer.wrap(thing).toString('binary');
+ },
+ toArrayBuffer: function(thing) {
+ if (thing === undefined) {
+ return undefined;
+ }
+ if (thing === Object(thing)) {
+ if (thing.__proto__ == StaticArrayBufferProto) {
+ return thing;
+ }
+ }
+
+ var str;
+ if (typeof thing == "string") {
+ str = thing;
+ } else {
+ throw new Error("Tried to convert a non-string of type " + typeof thing + " to an array buffer");
+ }
+ return new dcodeIO.ByteBuffer.wrap(thing, 'binary').toArrayBuffer();
+ },
+ isEqual: function(a, b) {
+ // TODO: Special-case arraybuffers, etc
+ if (a === undefined || b === undefined) {
+ return false;
+ }
+ a = util.toString(a);
+ b = util.toString(b);
+ var maxLength = Math.max(a.length, b.length);
+ if (maxLength < 5) {
+ throw new Error("a/b compare too short");
+ }
+ return a.substring(0, Math.min(maxLength, a.length)) == b.substring(0, Math.min(maxLength, b.length));
+ }
+ };
+})();
+
+function isNonNegativeInteger(n) {
+ return (typeof n === 'number' && (n % 1) === 0 && n >= 0);
+}
+
+var KeyHelper = {
+ generateIdentityKeyPair: function() {
+ return Internal.crypto.createKeyPair();
+ },
+
+ generateRegistrationId: function() {
+ var registrationId = new Uint16Array(Internal.crypto.getRandomBytes(2))[0];
+ return registrationId & 0x3fff;
+ },
+
+ generateSignedPreKey: function (identityKeyPair, signedKeyId) {
+ if (!(identityKeyPair.privKey instanceof ArrayBuffer) ||
+ identityKeyPair.privKey.byteLength != 32 ||
+ !(identityKeyPair.pubKey instanceof ArrayBuffer) ||
+ identityKeyPair.pubKey.byteLength != 33) {
+ throw new TypeError('Invalid argument for identityKeyPair');
+ }
+ if (!isNonNegativeInteger(signedKeyId)) {
+ throw new TypeError(
+ 'Invalid argument for signedKeyId: ' + signedKeyId
+ );
+ }
+
+ return Internal.crypto.createKeyPair().then(function(keyPair) {
+ return Internal.crypto.Ed25519Sign(identityKeyPair.privKey, keyPair.pubKey).then(function(sig) {
+ return {
+ keyId : signedKeyId,
+ keyPair : keyPair,
+ signature : sig
+ };
+ });
+ });
+ },
+
+ generatePreKey: function(keyId) {
+ if (!isNonNegativeInteger(keyId)) {
+ throw new TypeError('Invalid argument for keyId: ' + keyId);
+ }
+
+ return Internal.crypto.createKeyPair().then(function(keyPair) {
+ return { keyId: keyId, keyPair: keyPair };
+ });
+ }
+};
+
+libsignal.KeyHelper = KeyHelper;
+
+var Internal = Internal || {};
+
+Internal.protoText = function() {
+ var protoText = {};
+
+ protoText['protos/WhisperTextProtocol.proto'] =
+ 'package textsecure;\n' +
+ 'option java_package = "org.whispersystems.libsignal.protocol";\n' +
+ 'option java_outer_classname = "WhisperProtos";\n' +
+ 'message WhisperMessage {\n' +
+ ' optional bytes ephemeralKey = 1;\n' +
+ ' optional uint32 counter = 2;\n' +
+ ' optional uint32 previousCounter = 3;\n' +
+ ' optional bytes ciphertext = 4; // PushMessageContent\n' +
+ '}\n' +
+ 'message PreKeyWhisperMessage {\n' +
+ ' optional uint32 registrationId = 5;\n' +
+ ' optional uint32 preKeyId = 1;\n' +
+ ' optional uint32 signedPreKeyId = 6;\n' +
+ ' optional bytes baseKey = 2;\n' +
+ ' optional bytes identityKey = 3;\n' +
+ ' optional bytes message = 4; // WhisperMessage\n' +
+ '}\n' +
+ 'message KeyExchangeMessage {\n' +
+ ' optional uint32 id = 1;\n' +
+ ' optional bytes baseKey = 2;\n' +
+ ' optional bytes ephemeralKey = 3;\n' +
+ ' optional bytes identityKey = 4;\n' +
+ ' optional bytes baseKeySignature = 5;\n' +
+ '}\n' +
+'' ;
+
+ return protoText;
+}();
+/* vim: ts=4:sw=4 */
+var Internal = Internal || {};
+
+Internal.protobuf = function() {
+ 'use strict';
+
+ function loadProtoBufs(filename) {
+ return dcodeIO.ProtoBuf.loadProto(Internal.protoText['protos/' + filename]).build('textsecure');
+ }
+
+ var protocolMessages = loadProtoBufs('WhisperTextProtocol.proto');
+
+ return {
+ WhisperMessage : protocolMessages.WhisperMessage,
+ PreKeyWhisperMessage : protocolMessages.PreKeyWhisperMessage
+ };
+}();
+
+/*
+ * vim: ts=4:sw=4
+ */
+
+var Internal = Internal || {};
+
+Internal.BaseKeyType = {
+ OURS: 1,
+ THEIRS: 2
+};
+Internal.ChainType = {
+ SENDING: 1,
+ RECEIVING: 2
+};
+
+Internal.SessionRecord = function() {
+ 'use strict';
+ var ARCHIVED_STATES_MAX_LENGTH = 40;
+ var OLD_RATCHETS_MAX_LENGTH = 10;
+ var SESSION_RECORD_VERSION = 'v1';
+
+ var StaticByteBufferProto = new dcodeIO.ByteBuffer().__proto__;
+ var StaticArrayBufferProto = new ArrayBuffer().__proto__;
+ var StaticUint8ArrayProto = new Uint8Array().__proto__;
+
+ function isStringable(thing) {
+ return (thing === Object(thing) &&
+ (thing.__proto__ == StaticArrayBufferProto ||
+ thing.__proto__ == StaticUint8ArrayProto ||
+ thing.__proto__ == StaticByteBufferProto));
+ }
+ function ensureStringed(thing) {
+ if (typeof thing == "string" || typeof thing == "number" || typeof thing == "boolean") {
+ return thing;
+ } else if (isStringable(thing)) {
+ return util.toString(thing);
+ } else if (thing instanceof Array) {
+ var array = [];
+ for (var i = 0; i < thing.length; i++) {
+ array[i] = ensureStringed(thing[i]);
+ }
+ return array;
+ } else if (thing === Object(thing)) {
+ var obj = {};
+ for (var key in thing) {
+ try {
+ obj[key] = ensureStringed(thing[key]);
+ } catch (ex) {
+ console.log('Error serializing key', key);
+ throw ex;
+ }
+ }
+ return obj;
+ } else if (thing === null) {
+ return null;
+ } else {
+ throw new Error("unsure of how to jsonify object of type " + typeof thing);
+ }
+ }
+
+ function jsonThing(thing) {
+ return JSON.stringify(ensureStringed(thing)); //TODO: jquery???
+ }
+
+ var migrations = [
+ {
+ version: 'v1',
+ migrate: function migrateV1(data) {
+ var sessions = data.sessions;
+ var key;
+ if (data.registrationId) {
+ for (key in sessions) {
+ if (!sessions[key].registrationId) {
+ sessions[key].registrationId = data.registrationId;
+ }
+ }
+ } else {
+ for (key in sessions) {
+ if (sessions[key].indexInfo.closed === -1) {
+ console.log('V1 session storage migration error: registrationId',
+ data.registrationId, 'for open session version',
+ data.version);
+ }
+ }
+ }
+ }
+ }
+ ];
+
+ function migrate(data) {
+ var run = (data.version === undefined);
+ for (var i=0; i < migrations.length; ++i) {
+ if (run) {
+ migrations[i].migrate(data);
+ } else if (migrations[i].version === data.version) {
+ run = true;
+ }
+ }
+ if (!run) {
+ throw new Error("Error migrating SessionRecord");
+ }
+ }
+
+ var SessionRecord = function() {
+ this.sessions = {};
+ this.version = SESSION_RECORD_VERSION;
+ };
+
+ SessionRecord.deserialize = function(serialized) {
+ var data = JSON.parse(serialized);
+ if (data.version !== SESSION_RECORD_VERSION) { migrate(data); }
+
+ var record = new SessionRecord();
+ record.sessions = data.sessions;
+ if (record.sessions === undefined || record.sessions === null || typeof record.sessions !== "object" || Array.isArray(record.sessions)) {
+ throw new Error("Error deserializing SessionRecord");
+ }
+ return record;
+ };
+
+ SessionRecord.prototype = {
+ serialize: function() {
+ return jsonThing({
+ sessions : this.sessions,
+ version : this.version
+ });
+ },
+ haveOpenSession: function() {
+ var openSession = this.getOpenSession();
+ return (!!openSession && typeof openSession.registrationId === 'number');
+ },
+
+ getSessionByBaseKey: function(baseKey) {
+ var session = this.sessions[util.toString(baseKey)];
+ if (session && session.indexInfo.baseKeyType === Internal.BaseKeyType.OURS) {
+ console.log("Tried to lookup a session using our basekey");
+ return undefined;
+ }
+ return session;
+ },
+ getSessionByRemoteEphemeralKey: function(remoteEphemeralKey) {
+ this.detectDuplicateOpenSessions();
+ var sessions = this.sessions;
+
+ var searchKey = util.toString(remoteEphemeralKey);
+
+ var openSession;
+ for (var key in sessions) {
+ if (sessions[key].indexInfo.closed == -1) {
+ openSession = sessions[key];
+ }
+ if (sessions[key][searchKey] !== undefined) {
+ return sessions[key];
+ }
+ }
+ if (openSession !== undefined) {
+ return openSession;
+ }
+
+ return undefined;
+ },
+ getOpenSession: function() {
+ var sessions = this.sessions;
+ if (sessions === undefined) {
+ return undefined;
+ }
+
+ this.detectDuplicateOpenSessions();
+
+ for (var key in sessions) {
+ if (sessions[key].indexInfo.closed == -1) {
+ return sessions[key];
+ }
+ }
+ return undefined;
+ },
+ detectDuplicateOpenSessions: function() {
+ var openSession;
+ var sessions = this.sessions;
+ for (var key in sessions) {
+ if (sessions[key].indexInfo.closed == -1) {
+ if (openSession !== undefined) {
+ throw new Error("Datastore inconsistensy: multiple open sessions");
+ }
+ openSession = sessions[key];
+ }
+ }
+ },
+ updateSessionState: function(session) {
+ var sessions = this.sessions;
+
+ this.removeOldChains(session);
+
+ sessions[util.toString(session.indexInfo.baseKey)] = session;
+
+ this.removeOldSessions();
+
+ },
+ getSessions: function() {
+ // return an array of sessions ordered by time closed,
+ // followed by the open session
+ var list = [];
+ var openSession;
+ for (var k in this.sessions) {
+ if (this.sessions[k].indexInfo.closed === -1) {
+ openSession = this.sessions[k];
+ } else {
+ list.push(this.sessions[k]);
+ }
+ }
+ list = list.sort(function(s1, s2) {
+ return s1.indexInfo.closed - s2.indexInfo.closed;
+ });
+ if (openSession) {
+ list.push(openSession);
+ }
+ return list;
+ },
+ archiveCurrentState: function() {
+ var open_session = this.getOpenSession();
+ if (open_session !== undefined) {
+ console.log('closing session');
+ open_session.indexInfo.closed = Date.now();
+ this.updateSessionState(open_session);
+ }
+ },
+ promoteState: function(session) {
+ console.log('promoting session');
+ session.indexInfo.closed = -1;
+ },
+ removeOldChains: function(session) {
+ // Sending ratchets are always removed when we step because we never need them again
+ // Receiving ratchets are added to the oldRatchetList, which we parse
+ // here and remove all but the last ten.
+ while (session.oldRatchetList.length > OLD_RATCHETS_MAX_LENGTH) {
+ var index = 0;
+ var oldest = session.oldRatchetList[0];
+ for (var i = 0; i < session.oldRatchetList.length; i++) {
+ if (session.oldRatchetList[i].added < oldest.added) {
+ oldest = session.oldRatchetList[i];
+ index = i;
+ }
+ }
+ console.log("Deleting chain closed at", oldest.added);
+ delete session[util.toString(oldest.ephemeralKey)];
+ session.oldRatchetList.splice(index, 1);
+ }
+ },
+ removeOldSessions: function() {
+ // Retain only the last 20 sessions
+ var sessions = this.sessions;
+ var oldestBaseKey, oldestSession;
+ while (Object.keys(sessions).length > ARCHIVED_STATES_MAX_LENGTH) {
+ for (var key in sessions) {
+ var session = sessions[key];
+ if (session.indexInfo.closed > -1 && // session is closed
+ (!oldestSession || session.indexInfo.closed < oldestSession.indexInfo.closed)) {
+ oldestBaseKey = key;
+ oldestSession = session;
+ }
+ }
+ console.log("Deleting session closed at", oldestSession.indexInfo.closed);
+ delete sessions[util.toString(oldestBaseKey)];
+ }
+ },
+ deleteAllSessions: function() {
+ // Used primarily in session reset scenarios, where we really delete sessions
+ this.sessions = {};
+ }
+ };
+
+ return SessionRecord;
+}();
+
+function SignalProtocolAddress(name, deviceId) {
+ this.name = name;
+ this.deviceId = deviceId;
+}
+
+SignalProtocolAddress.prototype = {
+ getName: function() {
+ return this.name;
+ },
+ getDeviceId: function() {
+ return this.deviceId;
+ },
+ toString: function() {
+ return this.name + '.' + this.deviceId;
+ },
+ equals: function(other) {
+ if (!(other instanceof SignalProtocolAddress)) { return false; }
+ return other.name === this.name && other.deviceId === this.deviceId;
+ }
+};
+
+libsignal.SignalProtocolAddress = function(name, deviceId) {
+ var address = new SignalProtocolAddress(name, deviceId);
+
+ ['getName', 'getDeviceId', 'toString', 'equals'].forEach(function(method) {
+ this[method] = address[method].bind(address);
+ }.bind(this));
+};
+
+libsignal.SignalProtocolAddress.fromString = function(encodedAddress) {
+ if (typeof encodedAddress !== 'string' || !encodedAddress.match(/.*\.\d+/)) {
+ throw new Error('Invalid SignalProtocolAddress string');
+ }
+ var parts = encodedAddress.split('.');
+ return new libsignal.SignalProtocolAddress(parts[0], parseInt(parts[1]));
+};
+
+function SessionBuilder(storage, remoteAddress) {
+ this.remoteAddress = remoteAddress;
+ this.storage = storage;
+}
+
+SessionBuilder.prototype = {
+ processPreKey: function(device) {
+ return Internal.SessionLock.queueJobForNumber(this.remoteAddress.toString(), function() {
+ return this.storage.isTrustedIdentity(
+ this.remoteAddress.getName(), device.identityKey, this.storage.Direction.SENDING
+ ).then(function(trusted) {
+ if (!trusted) {
+ throw new Error('Identity key changed');
+ }
+
+ return Internal.crypto.Ed25519Verify(
+ device.identityKey,
+ device.signedPreKey.publicKey,
+ device.signedPreKey.signature
+ );
+ }).then(function() {
+ return Internal.crypto.createKeyPair();
+ }).then(function(baseKey) {
+ var devicePreKey;
+ if (device.preKey) {
+ devicePreKey = device.preKey.publicKey;
+ }
+ return this.initSession(true, baseKey, undefined, device.identityKey,
+ devicePreKey, device.signedPreKey.publicKey, device.registrationId
+ ).then(function(session) {
+ session.pendingPreKey = {
+ signedKeyId : device.signedPreKey.keyId,
+ baseKey : baseKey.pubKey
+ };
+ if (device.preKey) {
+ session.pendingPreKey.preKeyId = device.preKey.keyId;
+ }
+ return session;
+ });
+ }.bind(this)).then(function(session) {
+ var address = this.remoteAddress.toString();
+ return this.storage.loadSession(address).then(function(serialized) {
+ var record;
+ if (serialized !== undefined) {
+ record = Internal.SessionRecord.deserialize(serialized);
+ } else {
+ record = new Internal.SessionRecord();
+ }
+
+ record.archiveCurrentState();
+ record.updateSessionState(session);
+ return Promise.all([
+ this.storage.storeSession(address, record.serialize()),
+ this.storage.saveIdentity(this.remoteAddress.toString(), session.indexInfo.remoteIdentityKey)
+ ]);
+ }.bind(this));
+ }.bind(this));
+ }.bind(this));
+ },
+ processV3: function(record, message) {
+ var preKeyPair, signedPreKeyPair, session;
+ return this.storage.isTrustedIdentity(
+ this.remoteAddress.getName(), message.identityKey.toArrayBuffer(), this.storage.Direction.RECEIVING
+ ).then(function(trusted) {
+ if (!trusted) {
+ var e = new Error('Unknown identity key');
+ e.identityKey = message.identityKey.toArrayBuffer();
+ throw e;
+ }
+ return Promise.all([
+ this.storage.loadPreKey(message.preKeyId),
+ this.storage.loadSignedPreKey(message.signedPreKeyId),
+ ]).then(function(results) {
+ preKeyPair = results[0];
+ signedPreKeyPair = results[1];
+ });
+ }.bind(this)).then(function() {
+ session = record.getSessionByBaseKey(message.baseKey);
+ if (session) {
+ console.log("Duplicate PreKeyMessage for session");
+ return;
+ }
+
+ session = record.getOpenSession();
+
+ if (signedPreKeyPair === undefined) {
+ // Session may or may not be the right one, but if its not, we
+ // can't do anything about it ...fall through and let
+ // decryptWhisperMessage handle that case
+ if (session !== undefined && session.currentRatchet !== undefined) {
+ return;
+ } else {
+ throw new Error("Missing Signed PreKey for PreKeyWhisperMessage");
+ }
+ }
+
+ if (session !== undefined) {
+ record.archiveCurrentState();
+ }
+ if (message.preKeyId && !preKeyPair) {
+ console.log('Invalid prekey id', message.preKeyId);
+ }
+ return this.initSession(false, preKeyPair, signedPreKeyPair,
+ message.identityKey.toArrayBuffer(),
+ message.baseKey.toArrayBuffer(), undefined, message.registrationId
+ ).then(function(new_session) {
+ // Note that the session is not actually saved until the very
+ // end of decryptWhisperMessage ... to ensure that the sender
+ // actually holds the private keys for all reported pubkeys
+ record.updateSessionState(new_session);
+ return this.storage.saveIdentity(this.remoteAddress.toString(), message.identityKey.toArrayBuffer()).then(function() {
+ return message.preKeyId;
+ });
+ }.bind(this));
+ }.bind(this));
+ },
+ initSession: function(isInitiator, ourEphemeralKey, ourSignedKey,
+ theirIdentityPubKey, theirEphemeralPubKey,
+ theirSignedPubKey, registrationId) {
+ return this.storage.getIdentityKeyPair().then(function(ourIdentityKey) {
+ if (isInitiator) {
+ if (ourSignedKey !== undefined) {
+ throw new Error("Invalid call to initSession");
+ }
+ ourSignedKey = ourEphemeralKey;
+ } else {
+ if (theirSignedPubKey !== undefined) {
+ throw new Error("Invalid call to initSession");
+ }
+ theirSignedPubKey = theirEphemeralPubKey;
+ }
+
+ var sharedSecret;
+ if (ourEphemeralKey === undefined || theirEphemeralPubKey === undefined) {
+ sharedSecret = new Uint8Array(32 * 4);
+ } else {
+ sharedSecret = new Uint8Array(32 * 5);
+ }
+
+ for (var i = 0; i < 32; i++) {
+ sharedSecret[i] = 0xff;
+ }
+
+ return Promise.all([
+ Internal.crypto.ECDHE(theirSignedPubKey, ourIdentityKey.privKey),
+ Internal.crypto.ECDHE(theirIdentityPubKey, ourSignedKey.privKey),
+ Internal.crypto.ECDHE(theirSignedPubKey, ourSignedKey.privKey)
+ ]).then(function(ecRes) {
+ if (isInitiator) {
+ sharedSecret.set(new Uint8Array(ecRes[0]), 32);
+ sharedSecret.set(new Uint8Array(ecRes[1]), 32 * 2);
+ } else {
+ sharedSecret.set(new Uint8Array(ecRes[0]), 32 * 2);
+ sharedSecret.set(new Uint8Array(ecRes[1]), 32);
+ }
+ sharedSecret.set(new Uint8Array(ecRes[2]), 32 * 3);
+
+ if (ourEphemeralKey !== undefined && theirEphemeralPubKey !== undefined) {
+ return Internal.crypto.ECDHE(
+ theirEphemeralPubKey, ourEphemeralKey.privKey
+ ).then(function(ecRes4) {
+ sharedSecret.set(new Uint8Array(ecRes4), 32 * 4);
+ });
+ }
+ }).then(function() {
+ return Internal.HKDF(sharedSecret.buffer, new ArrayBuffer(32), "WhisperText");
+ }).then(function(masterKey) {
+ var session = {
+ registrationId: registrationId,
+ currentRatchet: {
+ rootKey : masterKey[0],
+ lastRemoteEphemeralKey : theirSignedPubKey,
+ previousCounter : 0
+ },
+ indexInfo: {
+ remoteIdentityKey : theirIdentityPubKey,
+ closed : -1
+ },
+ oldRatchetList: []
+ };
+
+ // If we're initiating we go ahead and set our first sending ephemeral key now,
+ // otherwise we figure it out when we first maybeStepRatchet with the remote's ephemeral key
+ if (isInitiator) {
+ session.indexInfo.baseKey = ourEphemeralKey.pubKey;
+ session.indexInfo.baseKeyType = Internal.BaseKeyType.OURS;
+ return Internal.crypto.createKeyPair().then(function(ourSendingEphemeralKey) {
+ session.currentRatchet.ephemeralKeyPair = ourSendingEphemeralKey;
+ return this.calculateSendingRatchet(session, theirSignedPubKey).then(function() {
+ return session;
+ });
+ }.bind(this));
+ } else {
+ session.indexInfo.baseKey = theirEphemeralPubKey;
+ session.indexInfo.baseKeyType = Internal.BaseKeyType.THEIRS;
+ session.currentRatchet.ephemeralKeyPair = ourSignedKey;
+ return session;
+ }
+ }.bind(this));
+ }.bind(this));
+ },
+ calculateSendingRatchet: function(session, remoteKey) {
+ var ratchet = session.currentRatchet;
+
+ return Internal.crypto.ECDHE(
+ remoteKey, util.toArrayBuffer(ratchet.ephemeralKeyPair.privKey)
+ ).then(function(sharedSecret) {
+ return Internal.HKDF(
+ sharedSecret, util.toArrayBuffer(ratchet.rootKey), "WhisperRatchet"
+ );
+ }).then(function(masterKey) {
+ session[util.toString(ratchet.ephemeralKeyPair.pubKey)] = {
+ messageKeys : {},
+ chainKey : { counter : -1, key : masterKey[1] },
+ chainType : Internal.ChainType.SENDING
+ };
+ ratchet.rootKey = masterKey[0];
+ });
+ }
+
+};
+
+libsignal.SessionBuilder = function (storage, remoteAddress) {
+ var builder = new SessionBuilder(storage, remoteAddress);
+ this.processPreKey = builder.processPreKey.bind(builder);
+ this.processV3 = builder.processV3.bind(builder);
+};
+
+function SessionCipher(storage, remoteAddress) {
+ this.remoteAddress = remoteAddress;
+ this.storage = storage;
+}
+
+SessionCipher.prototype = {
+ getRecord: function(encodedNumber) {
+ return this.storage.loadSession(encodedNumber).then(function(serialized) {
+ if (serialized === undefined) {
+ return undefined;
+ }
+ return Internal.SessionRecord.deserialize(serialized);
+ });
+ },
+ encrypt: function(buffer, encoding) {
+ buffer = dcodeIO.ByteBuffer.wrap(buffer, encoding).toArrayBuffer();
+ return Internal.SessionLock.queueJobForNumber(this.remoteAddress.toString(), function() {
+ if (!(buffer instanceof ArrayBuffer)) {
+ throw new Error("Expected buffer to be an ArrayBuffer");
+ }
+
+ var address = this.remoteAddress.toString();
+ var ourIdentityKey, myRegistrationId, record, session, chain;
+
+ var msg = new Internal.protobuf.WhisperMessage();
+
+ return Promise.all([
+ this.storage.getIdentityKeyPair(),
+ this.storage.getLocalRegistrationId(),
+ this.getRecord(address)
+ ]).then(function(results) {
+ ourIdentityKey = results[0];
+ myRegistrationId = results[1];
+ record = results[2];
+ if (!record) {
+ throw new Error("No record for " + address);
+ }
+ session = record.getOpenSession();
+ if (!session) {
+ throw new Error("No session to encrypt message for " + address);
+ }
+
+ msg.ephemeralKey = util.toArrayBuffer(
+ session.currentRatchet.ephemeralKeyPair.pubKey
+ );
+ chain = session[util.toString(msg.ephemeralKey)];
+ if (chain.chainType === Internal.ChainType.RECEIVING) {
+ throw new Error("Tried to encrypt on a receiving chain");
+ }
+
+ return this.fillMessageKeys(chain, chain.chainKey.counter + 1);
+ }.bind(this)).then(function() {
+ return Internal.HKDF(
+ util.toArrayBuffer(chain.messageKeys[chain.chainKey.counter]),
+ new ArrayBuffer(32), "WhisperMessageKeys");
+ }).then(function(keys) {
+ delete chain.messageKeys[chain.chainKey.counter];
+ msg.counter = chain.chainKey.counter;
+ msg.previousCounter = session.currentRatchet.previousCounter;
+
+ return Internal.crypto.encrypt(
+ keys[0], buffer, keys[2].slice(0, 16)
+ ).then(function(ciphertext) {
+ msg.ciphertext = ciphertext;
+ var encodedMsg = msg.toArrayBuffer();
+
+ var macInput = new Uint8Array(encodedMsg.byteLength + 33*2 + 1);
+ macInput.set(new Uint8Array(util.toArrayBuffer(ourIdentityKey.pubKey)));
+ macInput.set(new Uint8Array(util.toArrayBuffer(session.indexInfo.remoteIdentityKey)), 33);
+ macInput[33*2] = (3 << 4) | 3;
+ macInput.set(new Uint8Array(encodedMsg), 33*2 + 1);
+
+ return Internal.crypto.sign(keys[1], macInput.buffer).then(function(mac) {
+ var result = new Uint8Array(encodedMsg.byteLength + 9);
+ result[0] = (3 << 4) | 3;
+ result.set(new Uint8Array(encodedMsg), 1);
+ result.set(new Uint8Array(mac, 0, 8), encodedMsg.byteLength + 1);
+
+ return this.storage.isTrustedIdentity(
+ this.remoteAddress.getName(), util.toArrayBuffer(session.indexInfo.remoteIdentityKey), this.storage.Direction.SENDING
+ ).then(function(trusted) {
+ if (!trusted) {
+ throw new Error('Identity key changed');
+ }
+ }).then(function() {
+ return this.storage.saveIdentity(this.remoteAddress.toString(), session.indexInfo.remoteIdentityKey);
+ }.bind(this)).then(function() {
+ record.updateSessionState(session);
+ return this.storage.storeSession(address, record.serialize()).then(function() {
+ return result;
+ });
+ }.bind(this));
+ }.bind(this));
+ }.bind(this));
+ }.bind(this)).then(function(message) {
+ if (session.pendingPreKey !== undefined) {
+ var preKeyMsg = new Internal.protobuf.PreKeyWhisperMessage();
+ preKeyMsg.identityKey = util.toArrayBuffer(ourIdentityKey.pubKey);
+ preKeyMsg.registrationId = myRegistrationId;
+
+ preKeyMsg.baseKey = util.toArrayBuffer(session.pendingPreKey.baseKey);
+ if (session.pendingPreKey.preKeyId) {
+ preKeyMsg.preKeyId = session.pendingPreKey.preKeyId;
+ }
+ preKeyMsg.signedPreKeyId = session.pendingPreKey.signedKeyId;
+
+ preKeyMsg.message = message;
+ var result = String.fromCharCode((3 << 4) | 3) + util.toString(preKeyMsg.encode());
+ return {
+ type : 3,
+ body : result,
+ registrationId : session.registrationId
+ };
+
+ } else {
+ return {
+ type : 1,
+ body : util.toString(message),
+ registrationId : session.registrationId
+ };
+ }
+ });
+ }.bind(this));
+ },
+ decryptWithSessionList: function(buffer, sessionList, errors) {
+ // Iterate recursively through the list, attempting to decrypt
+ // using each one at a time. Stop and return the result if we get
+ // a valid result
+ if (sessionList.length === 0) {
+ return Promise.reject(errors[0]);
+ }
+
+ var session = sessionList.pop();
+ return this.doDecryptWhisperMessage(buffer, session).then(function(plaintext) {
+ return { plaintext: plaintext, session: session };
+ }).catch(function(e) {
+ if (e.name === 'MessageCounterError') {
+ return Promise.reject(e);
+ }
+
+ errors.push(e);
+ return this.decryptWithSessionList(buffer, sessionList, errors);
+ }.bind(this));
+ },
+ decryptWhisperMessage: function(buffer, encoding) {
+ buffer = dcodeIO.ByteBuffer.wrap(buffer, encoding).toArrayBuffer();
+ return Internal.SessionLock.queueJobForNumber(this.remoteAddress.toString(), function() {
+ var address = this.remoteAddress.toString();
+ return this.getRecord(address).then(function(record) {
+ if (!record) {
+ throw new Error("No record for device " + address);
+ }
+ var errors = [];
+ return this.decryptWithSessionList(buffer, record.getSessions(), errors).then(function(result) {
+ return this.getRecord(address).then(function(record) {
+ if (result.session.indexInfo.baseKey !== record.getOpenSession().indexInfo.baseKey) {
+ record.archiveCurrentState();
+ record.promoteState(result.session);
+ }
+
+ return this.storage.isTrustedIdentity(
+ this.remoteAddress.getName(), util.toArrayBuffer(result.session.indexInfo.remoteIdentityKey), this.storage.Direction.RECEIVING
+ ).then(function(trusted) {
+ if (!trusted) {
+ throw new Error('Identity key changed');
+ }
+ }).then(function() {
+ return this.storage.saveIdentity(this.remoteAddress.toString(), result.session.indexInfo.remoteIdentityKey);
+ }.bind(this)).then(function() {
+ record.updateSessionState(result.session);
+ return this.storage.storeSession(address, record.serialize()).then(function() {
+ return result.plaintext;
+ });
+ }.bind(this));
+ }.bind(this));
+ }.bind(this));
+ }.bind(this));
+ }.bind(this));
+ },
+ decryptPreKeyWhisperMessage: function(buffer, encoding) {
+ buffer = dcodeIO.ByteBuffer.wrap(buffer, encoding);
+ var version = buffer.readUint8();
+ if ((version & 0xF) > 3 || (version >> 4) < 3) { // min version > 3 or max version < 3
+ throw new Error("Incompatible version number on PreKeyWhisperMessage");
+ }
+ return Internal.SessionLock.queueJobForNumber(this.remoteAddress.toString(), function() {
+ var address = this.remoteAddress.toString();
+ return this.getRecord(address).then(function(record) {
+ var preKeyProto = Internal.protobuf.PreKeyWhisperMessage.decode(buffer);
+ if (!record) {
+ if (preKeyProto.registrationId === undefined) {
+ throw new Error("No registrationId");
+ }
+ record = new Internal.SessionRecord(
+ preKeyProto.registrationId
+ );
+ }
+ var builder = new SessionBuilder(this.storage, this.remoteAddress);
+ // isTrustedIdentity is called within processV3, no need to call it here
+ return builder.processV3(record, preKeyProto).then(function(preKeyId) {
+ var session = record.getSessionByBaseKey(preKeyProto.baseKey);
+ return this.doDecryptWhisperMessage(
+ preKeyProto.message.toArrayBuffer(), session
+ ).then(function(plaintext) {
+ record.updateSessionState(session);
+ return this.storage.storeSession(address, record.serialize()).then(function() {
+ if (preKeyId !== undefined && preKeyId !== null) {
+ return this.storage.removePreKey(preKeyId);
+ }
+ }.bind(this)).then(function() {
+ return plaintext;
+ });
+ }.bind(this));
+ }.bind(this));
+ }.bind(this));
+ }.bind(this));
+ },
+ doDecryptWhisperMessage: function(messageBytes, session) {
+ if (!(messageBytes instanceof ArrayBuffer)) {
+ throw new Error("Expected messageBytes to be an ArrayBuffer");
+ }
+ var version = (new Uint8Array(messageBytes))[0];
+ if ((version & 0xF) > 3 || (version >> 4) < 3) { // min version > 3 or max version < 3
+ throw new Error("Incompatible version number on WhisperMessage");
+ }
+ var messageProto = messageBytes.slice(1, messageBytes.byteLength- 8);
+ var mac = messageBytes.slice(messageBytes.byteLength - 8, messageBytes.byteLength);
+
+ var message = Internal.protobuf.WhisperMessage.decode(messageProto);
+ var remoteEphemeralKey = message.ephemeralKey.toArrayBuffer();
+
+ if (session === undefined) {
+ return Promise.reject(new Error("No session found to decrypt message from " + this.remoteAddress.toString()));
+ }
+ if (session.indexInfo.closed != -1) {
+ console.log('decrypting message for closed session');
+ }
+
+ return this.maybeStepRatchet(session, remoteEphemeralKey, message.previousCounter).then(function() {
+ var chain = session[util.toString(message.ephemeralKey)];
+ if (chain.chainType === Internal.ChainType.SENDING) {
+ throw new Error("Tried to decrypt on a sending chain");
+ }
+
+ return this.fillMessageKeys(chain, message.counter).then(function() {
+ var messageKey = chain.messageKeys[message.counter];
+ if (messageKey === undefined) {
+ var e = new Error("Message key not found. The counter was repeated or the key was not filled.");
+ e.name = 'MessageCounterError';
+ throw e;
+ }
+ delete chain.messageKeys[message.counter];
+ return Internal.HKDF(util.toArrayBuffer(messageKey), new ArrayBuffer(32), "WhisperMessageKeys");
+ });
+ }.bind(this)).then(function(keys) {
+ return this.storage.getIdentityKeyPair().then(function(ourIdentityKey) {
+
+ var macInput = new Uint8Array(messageProto.byteLength + 33*2 + 1);
+ macInput.set(new Uint8Array(util.toArrayBuffer(session.indexInfo.remoteIdentityKey)));
+ macInput.set(new Uint8Array(util.toArrayBuffer(ourIdentityKey.pubKey)), 33);
+ macInput[33*2] = (3 << 4) | 3;
+ macInput.set(new Uint8Array(messageProto), 33*2 + 1);
+
+ return Internal.verifyMAC(macInput.buffer, keys[1], mac, 8);
+ }.bind(this)).then(function() {
+ return Internal.crypto.decrypt(keys[0], message.ciphertext.toArrayBuffer(), keys[2].slice(0, 16));
+ });
+ }.bind(this)).then(function(plaintext) {
+ delete session.pendingPreKey;
+ return plaintext;
+ });
+ },
+ fillMessageKeys: function(chain, counter) {
+ if (chain.chainKey.counter >= counter) {
+ return Promise.resolve(); // Already calculated
+ }
+
+ if (counter - chain.chainKey.counter > 2000) {
+ throw new Error('Over 2000 messages into the future!');
+ }
+
+ if (chain.chainKey.key === undefined) {
+ throw new Error("Got invalid request to extend chain after it was already closed");
+ }
+
+ var key = util.toArrayBuffer(chain.chainKey.key);
+ var byteArray = new Uint8Array(1);
+ byteArray[0] = 1;
+ return Internal.crypto.sign(key, byteArray.buffer).then(function(mac) {
+ byteArray[0] = 2;
+ return Internal.crypto.sign(key, byteArray.buffer).then(function(key) {
+ chain.messageKeys[chain.chainKey.counter + 1] = mac;
+ chain.chainKey.key = key;
+ chain.chainKey.counter += 1;
+ return this.fillMessageKeys(chain, counter);
+ }.bind(this));
+ }.bind(this));
+ },
+ maybeStepRatchet: function(session, remoteKey, previousCounter) {
+ if (session[util.toString(remoteKey)] !== undefined) {
+ return Promise.resolve();
+ }
+
+ console.log('New remote ephemeral key');
+ var ratchet = session.currentRatchet;
+
+ return Promise.resolve().then(function() {
+ var previousRatchet = session[util.toString(ratchet.lastRemoteEphemeralKey)];
+ if (previousRatchet !== undefined) {
+ return this.fillMessageKeys(previousRatchet, previousCounter).then(function() {
+ delete previousRatchet.chainKey.key;
+ session.oldRatchetList[session.oldRatchetList.length] = {
+ added : Date.now(),
+ ephemeralKey : ratchet.lastRemoteEphemeralKey
+ };
+ });
+ }
+ }.bind(this)).then(function() {
+ return this.calculateRatchet(session, remoteKey, false).then(function() {
+ // Now swap the ephemeral key and calculate the new sending chain
+ var previousRatchet = util.toString(ratchet.ephemeralKeyPair.pubKey);
+ if (session[previousRatchet] !== undefined) {
+ ratchet.previousCounter = session[previousRatchet].chainKey.counter;
+ delete session[previousRatchet];
+ }
+
+ return Internal.crypto.createKeyPair().then(function(keyPair) {
+ ratchet.ephemeralKeyPair = keyPair;
+ return this.calculateRatchet(session, remoteKey, true).then(function() {
+ ratchet.lastRemoteEphemeralKey = remoteKey;
+ }.bind(this));
+ }.bind(this));
+ }.bind(this));
+ }.bind(this));
+ },
+ calculateRatchet: function(session, remoteKey, sending) {
+ var ratchet = session.currentRatchet;
+
+ return Internal.crypto.ECDHE(remoteKey, util.toArrayBuffer(ratchet.ephemeralKeyPair.privKey)).then(function(sharedSecret) {
+ return Internal.HKDF(sharedSecret, util.toArrayBuffer(ratchet.rootKey), "WhisperRatchet").then(function(masterKey) {
+ var ephemeralPublicKey;
+ if (sending) {
+ ephemeralPublicKey = ratchet.ephemeralKeyPair.pubKey;
+ }
+ else {
+ ephemeralPublicKey = remoteKey;
+ }
+ session[util.toString(ephemeralPublicKey)] = {
+ messageKeys: {},
+ chainKey: { counter: -1, key: masterKey[1] },
+ chainType: sending ? Internal.ChainType.SENDING : Internal.ChainType.RECEIVING
+ };
+ ratchet.rootKey = masterKey[0];
+ });
+ });
+ },
+ getRemoteRegistrationId: function() {
+ return Internal.SessionLock.queueJobForNumber(this.remoteAddress.toString(), function() {
+ return this.getRecord(this.remoteAddress.toString()).then(function(record) {
+ if (record === undefined) {
+ return undefined;
+ }
+ var openSession = record.getOpenSession();
+ if (openSession === undefined) {
+ return null;
+ }
+ return openSession.registrationId;
+ });
+ }.bind(this));
+ },
+ hasOpenSession: function() {
+ return Internal.SessionLock.queueJobForNumber(this.remoteAddress.toString(), function() {
+ return this.getRecord(this.remoteAddress.toString()).then(function(record) {
+ if (record === undefined) {
+ return false;
+ }
+ return record.haveOpenSession();
+ });
+ }.bind(this));
+ },
+ closeOpenSessionForDevice: function() {
+ var address = this.remoteAddress.toString();
+ return Internal.SessionLock.queueJobForNumber(address, function() {
+ return this.getRecord(address).then(function(record) {
+ if (record === undefined || record.getOpenSession() === undefined) {
+ return;
+ }
+
+ record.archiveCurrentState();
+ return this.storage.storeSession(address, record.serialize());
+ }.bind(this));
+ }.bind(this));
+ },
+ deleteAllSessionsForDevice: function() {
+ // Used in session reset scenarios, where we really need to delete
+ var address = this.remoteAddress.toString();
+ return Internal.SessionLock.queueJobForNumber(address, function() {
+ return this.getRecord(address).then(function(record) {
+ if (record === undefined) {
+ return;
+ }
+
+ record.deleteAllSessions();
+ return this.storage.storeSession(address, record.serialize());
+ }.bind(this));
+ }.bind(this));
+ }
+};
+
+libsignal.SessionCipher = function(storage, remoteAddress) {
+ var cipher = new SessionCipher(storage, remoteAddress);
+
+ // returns a Promise that resolves to a ciphertext object
+ this.encrypt = cipher.encrypt.bind(cipher);
+
+ // returns a Promise that inits a session if necessary and resolves
+ // to a decrypted plaintext array buffer
+ this.decryptPreKeyWhisperMessage = cipher.decryptPreKeyWhisperMessage.bind(cipher);
+
+ // returns a Promise that resolves to decrypted plaintext array buffer
+ this.decryptWhisperMessage = cipher.decryptWhisperMessage.bind(cipher);
+
+ this.getRemoteRegistrationId = cipher.getRemoteRegistrationId.bind(cipher);
+ this.hasOpenSession = cipher.hasOpenSession.bind(cipher);
+ this.closeOpenSessionForDevice = cipher.closeOpenSessionForDevice.bind(cipher);
+ this.deleteAllSessionsForDevice = cipher.deleteAllSessionsForDevice.bind(cipher);
+};
+
+ /*
+ * jobQueue manages multiple queues indexed by device to serialize
+ * session io ops on the database.
+ */
+;(function() {
+'use strict';
+
+Internal.SessionLock = {};
+
+var jobQueue = {};
+
+Internal.SessionLock.queueJobForNumber = function queueJobForNumber(number, runJob) {
+ var runPrevious = jobQueue[number] || Promise.resolve();
+ var runCurrent = jobQueue[number] = runPrevious.then(runJob, runJob);
+ runCurrent.then(function() {
+ if (jobQueue[number] === runCurrent) {
+ delete jobQueue[number];
+ }
+ });
+ return runCurrent;
+};
+
+})();
+
+(function() {
+ var VERSION = 0;
+
+ function iterateHash(data, key, count) {
+ data = dcodeIO.ByteBuffer.concat([data, key]).toArrayBuffer();
+ return Internal.crypto.hash(data).then(function(result) {
+ if (--count === 0) {
+ return result;
+ } else {
+ return iterateHash(result, key, count);
+ }
+ });
+ }
+
+ function shortToArrayBuffer(number) {
+ return new Uint16Array([number]).buffer;
+ }
+
+ function getEncodedChunk(hash, offset) {
+ var chunk = ( hash[offset] * Math.pow(2,32) +
+ hash[offset+1] * Math.pow(2,24) +
+ hash[offset+2] * Math.pow(2,16) +
+ hash[offset+3] * Math.pow(2,8) +
+ hash[offset+4] ) % 100000;
+ var s = chunk.toString();
+ while (s.length < 5) {
+ s = '0' + s;
+ }
+ return s;
+ }
+
+ function getDisplayStringFor(identifier, key, iterations) {
+ var bytes = dcodeIO.ByteBuffer.concat([
+ shortToArrayBuffer(VERSION), key, identifier
+ ]).toArrayBuffer();
+ return iterateHash(bytes, key, iterations).then(function(output) {
+ output = new Uint8Array(output);
+ return getEncodedChunk(output, 0) +
+ getEncodedChunk(output, 5) +
+ getEncodedChunk(output, 10) +
+ getEncodedChunk(output, 15) +
+ getEncodedChunk(output, 20) +
+ getEncodedChunk(output, 25);
+ });
+ }
+
+ libsignal.FingerprintGenerator = function(iterations) {
+ this.iterations = iterations;
+ };
+ libsignal.FingerprintGenerator.prototype = {
+ createFor: function(localIdentifier, localIdentityKey,
+ remoteIdentifier, remoteIdentityKey) {
+ if (typeof localIdentifier !== 'string' ||
+ typeof remoteIdentifier !== 'string' ||
+ !(localIdentityKey instanceof ArrayBuffer) ||
+ !(remoteIdentityKey instanceof ArrayBuffer)) {
+
+ throw new Error('Invalid arguments');
+ }
+
+ return Promise.all([
+ getDisplayStringFor(localIdentifier, localIdentityKey, this.iterations),
+ getDisplayStringFor(remoteIdentifier, remoteIdentityKey, this.iterations)
+ ]).then(function(fingerprints) {
+ return fingerprints.sort().join('');
+ });
+ }
+ };
+
+})();
+
+
+})();
\ No newline at end of file