protobufjs
Version:
Protocol Buffers for JavaScript & TypeScript.
205 lines (188 loc) • 7.55 kB
JavaScript
;
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 */
}