2 * Constructs a new Message.
\r
3 * @exports ProtoBuf.Reflect.Message
\r
4 * @param {!ProtoBuf.Builder} builder Builder reference
\r
5 * @param {!ProtoBuf.Reflect.Namespace} parent Parent message or namespace
\r
6 * @param {string} name Message name
\r
7 * @param {Object.<string,*>=} options Message options
\r
8 * @param {boolean=} isGroup `true` if this is a legacy group
\r
9 * @param {string?} syntax The syntax level of this definition (e.g., proto3)
\r
11 * @extends ProtoBuf.Reflect.Namespace
\r
13 var Message = function(builder, parent, name, options, isGroup, syntax) {
\r
14 Namespace.call(this, builder, parent, name, options, syntax);
\r
19 this.className = "Message";
\r
23 * @type {!Array.<number>|undefined}
\r
26 this.extensions = undefined;
\r
29 * Runtime message class.
\r
30 * @type {?function(new:ProtoBuf.Builder.Message)}
\r
36 * Whether this is a legacy group or not.
\r
40 this.isGroup = !!isGroup;
\r
42 // The following cached collections are used to efficiently iterate over or look up fields when decoding.
\r
46 * @type {?Array.<!ProtoBuf.Reflect.Message.Field>}
\r
49 this._fields = null;
\r
52 * Cached fields by id.
\r
53 * @type {?Object.<number,!ProtoBuf.Reflect.Message.Field>}
\r
56 this._fieldsById = null;
\r
59 * Cached fields by name.
\r
60 * @type {?Object.<string,!ProtoBuf.Reflect.Message.Field>}
\r
63 this._fieldsByName = null;
\r
67 * @alias ProtoBuf.Reflect.Message.prototype
\r
70 var MessagePrototype = Message.prototype = Object.create(Namespace.prototype);
\r
73 * Builds the message and returns the runtime counterpart, which is a fully functional class.
\r
74 * @see ProtoBuf.Builder.Message
\r
75 * @param {boolean=} rebuild Whether to rebuild or not, defaults to false
\r
76 * @return {ProtoBuf.Reflect.Message} Message class
\r
77 * @throws {Error} If the message cannot be built
\r
80 MessagePrototype.build = function(rebuild) {
\r
81 if (this.clazz && !rebuild)
\r
84 // Create the runtime Message class in its own scope
\r
85 var clazz = (function(ProtoBuf, T) {
\r
87 //? include("../Builder/Message.js");
\r
93 // Static enums and prototyped sub-messages / cached collections
\r
95 this._fieldsById = {};
\r
96 this._fieldsByName = {};
\r
97 this._oneofsByName = {};
\r
98 for (var i=0, k=this.children.length, child; i<k; i++) {
\r
99 child = this.children[i];
\r
100 if (child instanceof Enum || child instanceof Message || child instanceof Service) {
\r
101 if (clazz.hasOwnProperty(child.name))
\r
102 throw Error("Illegal reflect child of "+this.toString(true)+": "+child.toString(true)+" cannot override static property '"+child.name+"'");
\r
103 clazz[child.name] = child.build();
\r
104 } else if (child instanceof Message.Field)
\r
106 this._fields.push(child),
\r
107 this._fieldsById[child.id] = child,
\r
108 this._fieldsByName[child.name] = child;
\r
109 else if (child instanceof Message.OneOf) {
\r
110 this._oneofsByName[child.name] = child;
\r
112 else if (!(child instanceof Message.OneOf) && !(child instanceof Extension)) // Not built
\r
113 throw Error("Illegal reflect child of "+this.toString(true)+": "+this.children[i].toString(true));
\r
116 return this.clazz = clazz;
\r
120 * Encodes a runtime message's contents to the specified buffer.
\r
121 * @param {!ProtoBuf.Builder.Message} message Runtime message to encode
\r
122 * @param {ByteBuffer} buffer ByteBuffer to write to
\r
123 * @param {boolean=} noVerify Whether to not verify field values, defaults to `false`
\r
124 * @return {ByteBuffer} The ByteBuffer for chaining
\r
125 * @throws {Error} If required fields are missing or the message cannot be encoded for another reason
\r
128 MessagePrototype.encode = function(message, buffer, noVerify) {
\r
129 var fieldMissing = null,
\r
131 for (var i=0, k=this._fields.length, val; i<k; ++i) {
\r
132 field = this._fields[i];
\r
133 val = message[field.name];
\r
134 if (field.required && val === null) {
\r
135 if (fieldMissing === null)
\r
136 fieldMissing = field;
\r
138 field.encode(noVerify ? val : field.verifyValue(val), buffer, message);
\r
140 if (fieldMissing !== null) {
\r
141 var err = Error("Missing at least one required field for "+this.toString(true)+": "+fieldMissing);
\r
142 err["encoded"] = buffer; // Still expose what we got
\r
149 * Calculates a runtime message's byte length.
\r
150 * @param {!ProtoBuf.Builder.Message} message Runtime message to encode
\r
151 * @returns {number} Byte length
\r
152 * @throws {Error} If required fields are missing or the message cannot be calculated for another reason
\r
155 MessagePrototype.calculate = function(message) {
\r
156 for (var n=0, i=0, k=this._fields.length, field, val; i<k; ++i) {
\r
157 field = this._fields[i];
\r
158 val = message[field.name];
\r
159 if (field.required && val === null)
\r
160 throw Error("Missing at least one required field for "+this.toString(true)+": "+field);
\r
162 n += field.calculate(val, message);
\r
168 * Skips all data until the end of the specified group has been reached.
\r
169 * @param {number} expectedId Expected GROUPEND id
\r
170 * @param {!ByteBuffer} buf ByteBuffer
\r
171 * @returns {boolean} `true` if a value as been skipped, `false` if the end has been reached
\r
172 * @throws {Error} If it wasn't possible to find the end of the group (buffer overrun or end tag mismatch)
\r
175 function skipTillGroupEnd(expectedId, buf) {
\r
176 var tag = buf.readVarint32(), // Throws on OOB
\r
177 wireType = tag & 0x07,
\r
179 switch (wireType) {
\r
180 case ProtoBuf.WIRE_TYPES.VARINT:
\r
181 do tag = buf.readUint8();
\r
182 while ((tag & 0x80) === 0x80);
\r
184 case ProtoBuf.WIRE_TYPES.BITS64:
\r
187 case ProtoBuf.WIRE_TYPES.LDELIM:
\r
188 tag = buf.readVarint32(); // reads the varint
\r
189 buf.offset += tag; // skips n bytes
\r
191 case ProtoBuf.WIRE_TYPES.STARTGROUP:
\r
192 skipTillGroupEnd(id, buf);
\r
194 case ProtoBuf.WIRE_TYPES.ENDGROUP:
\r
195 if (id === expectedId)
\r
198 throw Error("Illegal GROUPEND after unknown group: "+id+" ("+expectedId+" expected)");
\r
199 case ProtoBuf.WIRE_TYPES.BITS32:
\r
203 throw Error("Illegal wire type in unknown group "+expectedId+": "+wireType);
\r
209 * Decodes an encoded message and returns the decoded message.
\r
210 * @param {ByteBuffer} buffer ByteBuffer to decode from
\r
211 * @param {number=} length Message length. Defaults to decode all remaining data.
\r
212 * @param {number=} expectedGroupEndId Expected GROUPEND id if this is a legacy group
\r
213 * @return {ProtoBuf.Builder.Message} Decoded message
\r
214 * @throws {Error} If the message cannot be decoded
\r
217 MessagePrototype.decode = function(buffer, length, expectedGroupEndId) {
\r
218 if (typeof length !== 'number')
\r
220 var start = buffer.offset,
\r
221 msg = new (this.clazz)(),
\r
222 tag, wireType, id, field;
\r
223 while (buffer.offset < start+length || (length === -1 && buffer.remaining() > 0)) {
\r
224 tag = buffer.readVarint32();
\r
225 wireType = tag & 0x07;
\r
227 if (wireType === ProtoBuf.WIRE_TYPES.ENDGROUP) {
\r
228 if (id !== expectedGroupEndId)
\r
229 throw Error("Illegal group end indicator for "+this.toString(true)+": "+id+" ("+(expectedGroupEndId ? expectedGroupEndId+" expected" : "not a group")+")");
\r
232 if (!(field = this._fieldsById[id])) {
\r
233 // "messages created by your new code can be parsed by your old code: old binaries simply ignore the new field when parsing."
\r
234 switch (wireType) {
\r
235 case ProtoBuf.WIRE_TYPES.VARINT:
\r
236 buffer.readVarint32();
\r
238 case ProtoBuf.WIRE_TYPES.BITS32:
\r
239 buffer.offset += 4;
\r
241 case ProtoBuf.WIRE_TYPES.BITS64:
\r
242 buffer.offset += 8;
\r
244 case ProtoBuf.WIRE_TYPES.LDELIM:
\r
245 var len = buffer.readVarint32();
\r
246 buffer.offset += len;
\r
248 case ProtoBuf.WIRE_TYPES.STARTGROUP:
\r
249 while (skipTillGroupEnd(id, buffer)) {}
\r
252 throw Error("Illegal wire type for unknown field "+id+" in "+this.toString(true)+"#decode: "+wireType);
\r
256 if (field.repeated && !field.options["packed"]) {
\r
257 msg[field.name].push(field.decode(wireType, buffer));
\r
258 } else if (field.map) {
\r
259 var keyval = field.decode(wireType, buffer);
\r
260 msg[field.name].set(keyval[0], keyval[1]);
\r
262 msg[field.name] = field.decode(wireType, buffer);
\r
263 if (field.oneof) { // Field is part of an OneOf (not a virtual OneOf field)
\r
264 var currentField = msg[field.oneof.name]; // Virtual field references currently set field
\r
265 if (currentField !== null && currentField !== field.name)
\r
266 msg[currentField] = null; // Clear currently set field
\r
267 msg[field.oneof.name] = field.name; // Point virtual field at this field
\r
272 // Check if all required fields are present and set default values for optional fields that are not
\r
273 for (var i=0, k=this._fields.length; i<k; ++i) {
\r
274 field = this._fields[i];
\r
275 if (msg[field.name] === null) {
\r
276 if (this.syntax === "proto3") { // Proto3 sets default values by specification
\r
277 msg[field.name] = field.defaultValue;
\r
278 } else if (field.required) {
\r
279 var err = Error("Missing at least one required field for " + this.toString(true) + ": " + field.name);
\r
280 err["decoded"] = msg; // Still expose what we got
\r
282 } else if (ProtoBuf.populateDefaults && field.defaultValue !== null)
\r
283 msg[field.name] = field.defaultValue;
\r