// Copyright 2008 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. /** * @fileoverview Protocol Buffer 2 Serializer which serializes messages * into PB-Lite ("JsPbLite") format. * * PB-Lite format is an array where each index corresponds to the associated tag * number. For example, a message like so: * * message Foo { * optional int bar = 1; * optional int baz = 2; * optional int bop = 4; * } * * would be represented as such: * * [, (bar data), (baz data), (nothing), (bop data)] * * Note that since the array index is used to represent the tag number, sparsely * populated messages with tag numbers that are not continuous (and/or are very * large) will have many (empty) spots and thus, are inefficient. * * */ goog.provide('goog.proto2.PbLiteSerializer'); goog.require('goog.asserts'); goog.require('goog.proto2.FieldDescriptor'); goog.require('goog.proto2.LazyDeserializer'); goog.require('goog.proto2.Serializer'); /** * PB-Lite serializer. * * @constructor * @extends {goog.proto2.LazyDeserializer} */ goog.proto2.PbLiteSerializer = function() {}; goog.inherits(goog.proto2.PbLiteSerializer, goog.proto2.LazyDeserializer); /** * If true, fields will be serialized with 0-indexed tags (i.e., the proto * field with tag id 1 will have index 0 in the array). * @type {boolean} * @private */ goog.proto2.PbLiteSerializer.prototype.zeroIndexing_ = false; /** * By default, the proto tag with id 1 will have index 1 in the serialized * array. * * If the serializer is set to use zero-indexing, the tag with id 1 will have * index 0. * * @param {boolean} zeroIndexing Whether this serializer should deal with * 0-indexed protos. */ goog.proto2.PbLiteSerializer.prototype.setZeroIndexed = function(zeroIndexing) { this.zeroIndexing_ = zeroIndexing; }; /** * Serializes a message to a PB-Lite object. * * @param {goog.proto2.Message} message The message to be serialized. * @return {!Array} The serialized form of the message. * @override */ goog.proto2.PbLiteSerializer.prototype.serialize = function(message) { var descriptor = message.getDescriptor(); var fields = descriptor.getFields(); var serialized = []; var zeroIndexing = this.zeroIndexing_; // Add the known fields. for (var i = 0; i < fields.length; i++) { var field = fields[i]; if (!message.has(field)) { continue; } var tag = field.getTag(); var index = zeroIndexing ? tag - 1 : tag; if (field.isRepeated()) { serialized[index] = []; for (var j = 0; j < message.countOf(field); j++) { serialized[index][j] = this.getSerializedValue(field, message.get(field, j)); } } else { serialized[index] = this.getSerializedValue(field, message.get(field)); } } // Add any unknown fields. message.forEachUnknown(function(tag, value) { var index = zeroIndexing ? tag - 1 : tag; serialized[index] = value; }); return serialized; }; /** @override */ goog.proto2.PbLiteSerializer.prototype.deserializeField = function( message, field, value) { if (value == null) { // Since value double-equals null, it may be either null or undefined. // Ensure we return the same one, since they have different meanings. // TODO(user): If the field is repeated, this method should probably // return [] instead of null. return value; } if (field.isRepeated()) { var data = []; goog.asserts.assert(goog.isArray(value), 'Value must be array: %s', value); for (var i = 0; i < value.length; i++) { data[i] = this.getDeserializedValue(field, value[i]); } return data; } else { return this.getDeserializedValue(field, value); } }; /** @override */ goog.proto2.PbLiteSerializer.prototype.getSerializedValue = function( field, value) { if (field.getFieldType() == goog.proto2.FieldDescriptor.FieldType.BOOL) { // Booleans are serialized in numeric form. return value ? 1 : 0; } return goog.proto2.Serializer.prototype.getSerializedValue.apply( this, arguments); }; /** @override */ goog.proto2.PbLiteSerializer.prototype.getDeserializedValue = function( field, value) { if (field.getFieldType() == goog.proto2.FieldDescriptor.FieldType.BOOL) { goog.asserts.assert( goog.isNumber(value) || goog.isBoolean(value), 'Value is expected to be a number or boolean'); return !!value; } return goog.proto2.Serializer.prototype.getDeserializedValue.apply( this, arguments); }; /** @override */ goog.proto2.PbLiteSerializer.prototype.deserialize = function( descriptor, data) { var toConvert = data; if (this.zeroIndexing_) { // Make the data align with tag-IDs (1-indexed) by shifting everything // up one. toConvert = []; for (var key in data) { toConvert[parseInt(key, 10) + 1] = data[key]; } } return goog.proto2.PbLiteSerializer.base( this, 'deserialize', descriptor, toConvert); };