UNPKG

protobufjs

Version:

Protocol Buffers for JavaScript & TypeScript.

205 lines (188 loc) 7.55 kB
"use strict"; module.exports = decoder; var Enum = require("./enum"), types = require("./types"), util = require("./util"); function missing(field) { return "missing required '" + field.name + "'"; } /** * Generates a decoder specific to the specified message type. * @param {Type} mtype Message type * @returns {Codegen} Codegen instance */ function decoder(mtype) { /* eslint-disable no-unexpected-multiline */ var hasMapField = false, hasImplicitPresenceField = false, i = 0; for (; i < mtype.fieldsArray.length; ++i) { var pfield = mtype._fieldsArray[i]; if (pfield.map) hasMapField = true; if (!pfield.repeated && !pfield.map && !pfield.hasPresence) hasImplicitPresenceField = true; } var gen = util.codegen(["r", "l", "z", "q", "g"], mtype.name + "$decode") ("if(!(r instanceof Reader))") ("r=Reader.create(r)") ("if(q===undefined)q=0") ("if(q>Reader.recursionLimit)") ("throw Error(\"max depth exceeded\")") ("var c=l===undefined?r.len:r.pos+l,m=g||new C" + (hasMapField ? ",k,v" : hasImplicitPresenceField ? ",v" : "")) ("while(r.pos<c){") ("var s=r.pos") ("var t=r.tag()") ("if(t===z){") ("z=undefined") ("break") ("}"); if (mtype.fieldsArray.length) gen ("var u=t&7") ("switch(t>>>=3){"); for (i = 0; i < /* initializes */ mtype.fieldsArray.length; ++i) { var field = mtype._fieldsArray[i].resolve(), type = field.resolvedType instanceof Enum ? "int32" : field.type, ref = "m" + util.safeProp(field.name); // Map fields if (field.map) { gen ("case %i:{", field.id) ("if(u!==2)") ("break") ("if(%s===util.emptyObject)", ref) ("%s={}", ref) ("var c2=r.uint32()+r.pos"); if (types.defaults[field.keyType] !== undefined) gen ("k=%j", types.defaults[field.keyType]); else gen ("k=null"); if (types.defaults[type] !== undefined) gen ("v=%j", types.defaults[type]); else gen ("v=null"); gen ("while(r.pos<c2){") ("var t2=r.tag()") ("u=t2&7") ("switch(t2>>>=3){") ("case 1:") ("if(u!==%i)", types.mapKey[field.keyType]) ("break") ("k=r.%s()", field.keyType) ("continue") ("case 2:") ("if(u!==%i)", types.basic[type] === undefined ? 2 : types.basic[type]) ("break"); if (types.basic[type] === undefined) gen ("v=types[%i].decode(r,r.uint32(),undefined,q+1)", i); // can't be groups else gen ("v=r.%s()", type); gen ("continue") ("}") ("r.skipType(u,q,t2)") ("}"); var val = types.basic[type] === undefined ? "v||new types[" + i + "].ctor" : "v"; if (types.long[field.keyType] !== undefined) gen ("%s[typeof k===\"object\"?util.longToHash(k):k]=%s", ref, val); else { if (field.keyType === "string") gen ("if(k===\"__proto__\")") ("util.makeProp(%s,k)", ref); gen ("%s[k]=%s", ref, val); } // Repeated fields } else if (field.repeated) { gen ("case %i:", field.id) ("{"); // Packable (always check for forward and backward compatiblity) if (types.packed[type] !== undefined) gen ("if(u===2){") ("if(!(%s&&%s.length))", ref, ref) ("%s=[]", ref) ("var c2=r.uint32()+r.pos") ("while(r.pos<c2)") ("%s.push(r.%s())", ref, type) ("continue") ("}"); // Non-packed gen ("if(u!==%i)", types.basic[type] === undefined ? field.delimited ? 3 : 2 : types.basic[type]) ("break") ("if(!(%s&&%s.length))", ref, ref) ("%s=[]", ref); if (types.basic[type] === undefined) { if (field.delimited) gen ("%s.push(types[%i].decode(r,undefined,%i,q+1))", ref, i, field.id * 8 + 4); else gen ("%s.push(types[%i].decode(r,r.uint32(),undefined,q+1))", ref, i); } else gen ("%s.push(r.%s())", ref, type); // Non-repeated } else if (types.basic[type] === undefined) { gen ("case %i:{", field.id) ("if(u!==%i)", field.delimited ? 3 : 2) ("break"); if (field.delimited) gen ("%s=types[%i].decode(r,undefined,%i,q+1,%s)", ref, i, field.id * 8 + 4, ref); else gen ("%s=types[%i].decode(r,r.uint32(),undefined,q+1,%s)", ref, i, ref); } else if (field.hasPresence) { gen ("case %i:{", field.id) ("if(u!==%i)", types.basic[type]) ("break") ("%s=r.%s()", ref, type); } else { gen ("case %i:{", field.id) ("if(u!==%i)", types.basic[type]) ("break"); if (field.resolvedType instanceof Enum && field.typeDefault !== 0) gen // TODO: Protoc rejects open enums whose first value is not zero. // We should do the same, but for v8 this would be a regression. ("if((v=r.%s())!==%j)", type, field.typeDefault); else if (type === "string" || type === "bytes") gen ("if((v=r.%s()).length)", type); else if (types.long[type] !== undefined) gen ("if(typeof(v=r.%s())===\"object\"?v.low||v.high:v!==0)", type); else if (type === "double" || type === "float") gen ("if((v=r.%s())!==0)", type); else gen ("if(v=r.%s())", type); gen ("%s=v", ref) ("else") ("delete %s", ref); // rare/odd case: later default clears earlier non-default } if (field.partOf) gen ("m%s=%j", util.safeProp(field.partOf.name), field.name); gen ("continue") ("}"); } if (i) gen ("}"); // Unknown fields gen ("r.skipType(%s,q,t)", i ? "u" : "t&7") ("util.makeProp(m,\"$unknowns\",false);") ("(m.$unknowns||(m.$unknowns=[])).push(r.raw(s,r.pos))") ("}") ("if(z!==undefined)") ("throw Error(\"missing end group\")"); // Field presence for (i = 0; i < mtype._fieldsArray.length; ++i) { var rfield = mtype._fieldsArray[i]; if (rfield.required) gen ("if(!m.hasOwnProperty(%j))", rfield.name) ("throw util.ProtocolError(%j,{instance:m})", missing(rfield)); } return gen ("return m"); /* eslint-enable no-unexpected-multiline */ }