#region Copyright notice and license // Protocol Buffers - Google's data interchange format // Copyright 2008 Google Inc. All rights reserved. // http://github.com/jskeet/dotnet-protobufs/ // Original C++/Java/Python code: // http://code.google.com/p/protobuf/ // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #endregion using System; using System.Collections.Generic; using System.IO; using Google.ProtocolBuffers.Collections; using Google.ProtocolBuffers.Descriptors; namespace Google.ProtocolBuffers { /// /// Used to keep track of fields which were seen when parsing a protocol message /// but whose field numbers or types are unrecognized. This most frequently /// occurs when new fields are added to a message type and then messages containing /// those fields are read by old software that was built before the new types were /// added. /// /// Every message contains an UnknownFieldSet. /// /// Most users will never need to use this class directly. /// public sealed partial class UnknownFieldSet : IMessageLite { private static readonly UnknownFieldSet defaultInstance = new UnknownFieldSet(new Dictionary()); private readonly IDictionary fields; private UnknownFieldSet(IDictionary fields) { this.fields = fields; } /// /// Creates a new unknown field set builder. /// public static Builder CreateBuilder() { return new Builder(); } /// /// Creates a new unknown field set builder /// and initialize it from . /// public static Builder CreateBuilder(UnknownFieldSet original) { return new Builder().MergeFrom(original); } public static UnknownFieldSet DefaultInstance { get { return defaultInstance; } } /// /// Returns a read-only view of the mapping from field numbers to values. /// public IDictionary FieldDictionary { get { return Dictionaries.AsReadOnly(fields); } } /// /// Checks whether or not the given field number is present in the set. /// public bool HasField(int field) { return fields.ContainsKey(field); } /// /// Fetches a field by number, returning an empty field if not present. /// Never returns null. /// public UnknownField this[int number] { get { UnknownField ret; if (!fields.TryGetValue(number, out ret)) { ret = UnknownField.DefaultInstance; } return ret; } } /// /// Serializes the set and writes it to . /// public void WriteTo(ICodedOutputStream output) { // Avoid creating enumerator for the most common code path. if (fields.Count > 0) { foreach (KeyValuePair entry in fields) { entry.Value.WriteTo(entry.Key, output); } } } /// /// Gets the number of bytes required to encode this set. /// public int SerializedSize { get { // Avoid creating enumerator for the most common code path. if (fields.Count == 0) { return 0; } int result = 0; foreach (KeyValuePair entry in fields) { result += entry.Value.GetSerializedSize(entry.Key); } return result; } } /// /// Converts the set to a string in protocol buffer text format. This /// is just a trivial wrapper around TextFormat.PrintToString. /// public override String ToString() { return TextFormat.PrintToString(this); } /// /// Converts the set to a string in protocol buffer text format. This /// is just a trivial wrapper around TextFormat.PrintToString. /// public void PrintTo(TextWriter writer) { TextFormat.Print(this, writer); } /// /// Serializes the message to a ByteString and returns it. This is /// just a trivial wrapper around WriteTo(ICodedOutputStream). /// /// public ByteString ToByteString() { ByteString.CodedBuilder codedBuilder = new ByteString.CodedBuilder(SerializedSize); WriteTo(codedBuilder.CodedOutput); return codedBuilder.Build(); } /// /// Serializes the message to a byte array and returns it. This is /// just a trivial wrapper around WriteTo(ICodedOutputStream). /// /// public byte[] ToByteArray() { byte[] data = new byte[SerializedSize]; CodedOutputStream output = CodedOutputStream.CreateInstance(data); WriteTo(output); output.CheckNoSpaceLeft(); return data; } /// /// Serializes the message and writes it to . This is /// just a trivial wrapper around WriteTo(ICodedOutputStream). /// /// public void WriteTo(Stream output) { CodedOutputStream codedOutput = CodedOutputStream.CreateInstance(output); WriteTo(codedOutput); codedOutput.Flush(); } /// /// Serializes the set and writes it to using /// the MessageSet wire format. /// public void WriteAsMessageSetTo(ICodedOutputStream output) { // Avoid creating enumerator for the most common code path. if (fields.Count > 0) { foreach (KeyValuePair entry in fields) { entry.Value.WriteAsMessageSetExtensionTo(entry.Key, output); } } } /// /// Gets the number of bytes required to encode this set using the MessageSet /// wire format. /// public int SerializedSizeAsMessageSet { get { // Avoid creating enumerator for the most common code path. if (fields.Count == 0) { return 0; } int result = 0; foreach (KeyValuePair entry in fields) { result += entry.Value.GetSerializedSizeAsMessageSetExtension(entry.Key); } return result; } } public override bool Equals(object other) { if (ReferenceEquals(this, other)) { return true; } UnknownFieldSet otherSet = other as UnknownFieldSet; return otherSet != null && Dictionaries.Equals(fields, otherSet.fields); } public override int GetHashCode() { return Dictionaries.GetHashCode(fields); } /// /// Parses an UnknownFieldSet from the given input. /// public static UnknownFieldSet ParseFrom(ICodedInputStream input) { return CreateBuilder().MergeFrom(input).Build(); } /// /// Parses an UnknownFieldSet from the given data. /// public static UnknownFieldSet ParseFrom(ByteString data) { return CreateBuilder().MergeFrom(data).Build(); } /// /// Parses an UnknownFieldSet from the given data. /// public static UnknownFieldSet ParseFrom(byte[] data) { return CreateBuilder().MergeFrom(data).Build(); } /// /// Parses an UnknownFieldSet from the given input. /// public static UnknownFieldSet ParseFrom(Stream input) { return CreateBuilder().MergeFrom(input).Build(); } #region IMessageLite Members public bool IsInitialized { get { return fields != null; } } public void WriteDelimitedTo(Stream output) { CodedOutputStream codedOutput = CodedOutputStream.CreateInstance(output); codedOutput.WriteRawVarint32((uint) SerializedSize); WriteTo(codedOutput); codedOutput.Flush(); } public IBuilderLite WeakCreateBuilderForType() { return new Builder(); } public IBuilderLite WeakToBuilder() { return new Builder(fields); } public IMessageLite WeakDefaultInstanceForType { get { return defaultInstance; } } #endregion /// /// Builder for UnknownFieldSets. /// public sealed partial class Builder : IBuilderLite { /// /// Mapping from number to field. Note that by using a SortedList we ensure /// that the fields will be serialized in ascending order. /// private IDictionary fields; // Optimization: We keep around a builder for the last field that was // modified so that we can efficiently add to it multiple times in a // row (important when parsing an unknown repeated field). private int lastFieldNumber; private UnknownField.Builder lastField; internal Builder() { fields = new SortedDictionary(); } internal Builder(IDictionary dictionary) { fields = new SortedDictionary(dictionary); } /// /// Returns a field builder for the specified field number, including any values /// which already exist. /// private UnknownField.Builder GetFieldBuilder(int number) { if (lastField != null) { if (number == lastFieldNumber) { return lastField; } // Note: AddField() will reset lastField and lastFieldNumber. AddField(lastFieldNumber, lastField.Build()); } if (number == 0) { return null; } lastField = UnknownField.CreateBuilder(); UnknownField existing; if (fields.TryGetValue(number, out existing)) { lastField.MergeFrom(existing); } lastFieldNumber = number; return lastField; } /// /// Build the UnknownFieldSet and return it. Once this method has been called, /// this instance will no longer be usable. Calling any method after this /// will throw a NullReferenceException. /// public UnknownFieldSet Build() { GetFieldBuilder(0); // Force lastField to be built. UnknownFieldSet result = fields.Count == 0 ? DefaultInstance : new UnknownFieldSet(fields); fields = null; return result; } /// /// Adds a field to the set. If a field with the same number already exists, it /// is replaced. /// public Builder AddField(int number, UnknownField field) { if (number == 0) { throw new ArgumentOutOfRangeException("number", "Zero is not a valid field number."); } if (lastField != null && lastFieldNumber == number) { // Discard this. lastField = null; lastFieldNumber = 0; } fields[number] = field; return this; } /// /// Resets the builder to an empty set. /// public Builder Clear() { fields.Clear(); lastFieldNumber = 0; lastField = null; return this; } /// /// Parse an entire message from and merge /// its fields into this set. /// public Builder MergeFrom(ICodedInputStream input) { uint tag; string name; while (input.ReadTag(out tag, out name)) { if (tag == 0) { if (input.SkipField()) { continue; //can't merge unknown without field tag } break; } if (!MergeFieldFrom(tag, input)) { break; } } return this; } /// /// Parse a single field from and merge it /// into this set. /// /// The field's tag number, which was already parsed. /// The coded input stream containing the field /// false if the tag is an "end group" tag, true otherwise public bool MergeFieldFrom(uint tag, ICodedInputStream input) { if (tag == 0) { input.SkipField(); return true; } int number = WireFormat.GetTagFieldNumber(tag); switch (WireFormat.GetTagWireType(tag)) { case WireFormat.WireType.Varint: { ulong uint64 = 0; if (input.ReadUInt64(ref uint64)) { GetFieldBuilder(number).AddVarint(uint64); } return true; } case WireFormat.WireType.Fixed32: { uint uint32 = 0; if (input.ReadFixed32(ref uint32)) { GetFieldBuilder(number).AddFixed32(uint32); } return true; } case WireFormat.WireType.Fixed64: { ulong uint64 = 0; if (input.ReadFixed64(ref uint64)) { GetFieldBuilder(number).AddFixed64(uint64); } return true; } case WireFormat.WireType.LengthDelimited: { ByteString bytes = null; if (input.ReadBytes(ref bytes)) { GetFieldBuilder(number).AddLengthDelimited(bytes); } return true; } case WireFormat.WireType.StartGroup: { Builder subBuilder = CreateBuilder(); #pragma warning disable 0612 input.ReadUnknownGroup(number, subBuilder); #pragma warning restore 0612 GetFieldBuilder(number).AddGroup(subBuilder.Build()); return true; } case WireFormat.WireType.EndGroup: return false; default: throw InvalidProtocolBufferException.InvalidWireType(); } } /// /// Parses as an UnknownFieldSet and merge it /// with the set being built. This is just a small wrapper around /// MergeFrom(ICodedInputStream). /// public Builder MergeFrom(Stream input) { CodedInputStream codedInput = CodedInputStream.CreateInstance(input); MergeFrom(codedInput); codedInput.CheckLastTagWas(0); return this; } /// /// Parses as an UnknownFieldSet and merge it /// with the set being built. This is just a small wrapper around /// MergeFrom(ICodedInputStream). /// public Builder MergeFrom(ByteString data) { CodedInputStream input = data.CreateCodedInput(); MergeFrom(input); input.CheckLastTagWas(0); return this; } /// /// Parses as an UnknownFieldSet and merge it /// with the set being built. This is just a small wrapper around /// MergeFrom(ICodedInputStream). /// public Builder MergeFrom(byte[] data) { CodedInputStream input = CodedInputStream.CreateInstance(data); MergeFrom(input); input.CheckLastTagWas(0); return this; } /// /// Convenience method for merging a new field containing a single varint /// value. This is used in particular when an unknown enum value is /// encountered. /// public Builder MergeVarintField(int number, ulong value) { if (number == 0) { throw new ArgumentOutOfRangeException("number", "Zero is not a valid field number."); } GetFieldBuilder(number).AddVarint(value); return this; } /// /// Merges the fields from into this set. /// If a field number exists in both sets, the values in /// will be appended to the values in this set. /// public Builder MergeFrom(UnknownFieldSet other) { if (other != DefaultInstance) { foreach (KeyValuePair entry in other.fields) { MergeField(entry.Key, entry.Value); } } return this; } /// /// Checks if the given field number is present in the set. /// public bool HasField(int number) { if (number == 0) { throw new ArgumentOutOfRangeException("number", "Zero is not a valid field number."); } return number == lastFieldNumber || fields.ContainsKey(number); } /// /// Adds a field to the unknown field set. If a field with the same /// number already exists, the two are merged. /// public Builder MergeField(int number, UnknownField field) { if (number == 0) { throw new ArgumentOutOfRangeException("number", "Zero is not a valid field number."); } if (HasField(number)) { GetFieldBuilder(number).MergeFrom(field); } else { // Optimization: We could call getFieldBuilder(number).mergeFrom(field) // in this case, but that would create a copy of the Field object. // We'd rather reuse the one passed to us, so call AddField() instead. AddField(number, field); } return this; } internal void MergeFrom(ICodedInputStream input, ExtensionRegistry extensionRegistry, IBuilder builder) { uint tag; string name; while (input.ReadTag(out tag, out name)) { if (tag == 0 && name != null) { FieldDescriptor fieldByName = builder.DescriptorForType.FindFieldByName(name); if (fieldByName != null) { tag = WireFormat.MakeTag(fieldByName); } else { ExtensionInfo extension = extensionRegistry.FindByName(builder.DescriptorForType, name); if (extension != null) { tag = WireFormat.MakeTag(extension.Descriptor); } } } if (tag == 0) { if (input.SkipField()) { continue; //can't merge unknown without field tag } break; } if (!MergeFieldFrom(input, extensionRegistry, builder, tag, name)) { // end group tag break; } } } /// /// Like /// but parses a single field. /// /// The input to read the field from /// Registry to use when an extension field is encountered /// Builder to merge field into, if it's a known field /// The tag, which should already have been read from the input /// true unless the tag is an end-group tag internal bool MergeFieldFrom(ICodedInputStream input, ExtensionRegistry extensionRegistry, IBuilder builder, uint tag, string fieldName) { if (tag == 0 && fieldName != null) { FieldDescriptor fieldByName = builder.DescriptorForType.FindFieldByName(fieldName); if (fieldByName != null) { tag = WireFormat.MakeTag(fieldByName); } else { ExtensionInfo extension = extensionRegistry.FindByName(builder.DescriptorForType, fieldName); if (extension != null) { tag = WireFormat.MakeTag(extension.Descriptor); } } } MessageDescriptor type = builder.DescriptorForType; if (type.Options.MessageSetWireFormat && tag == WireFormat.MessageSetTag.ItemStart) { MergeMessageSetExtensionFromCodedStream(input, extensionRegistry, builder); return true; } WireFormat.WireType wireType = WireFormat.GetTagWireType(tag); int fieldNumber = WireFormat.GetTagFieldNumber(tag); FieldDescriptor field; IMessageLite defaultFieldInstance = null; if (type.IsExtensionNumber(fieldNumber)) { ExtensionInfo extension = extensionRegistry[type, fieldNumber]; if (extension == null) { field = null; } else { field = extension.Descriptor; defaultFieldInstance = extension.DefaultInstance; } } else { field = type.FindFieldByNumber(fieldNumber); } // Unknown field or wrong wire type. Skip. if (field == null) { return MergeFieldFrom(tag, input); } if (wireType != WireFormat.GetWireType(field)) { WireFormat.WireType expectedType = WireFormat.GetWireType(field.FieldType); if (wireType == expectedType) { //Allowed as of 2.3, this is unpacked data for a packed array } else if (field.IsRepeated && wireType == WireFormat.WireType.LengthDelimited && (expectedType == WireFormat.WireType.Varint || expectedType == WireFormat.WireType.Fixed32 || expectedType == WireFormat.WireType.Fixed64)) { //Allowed as of 2.3, this is packed data for an unpacked array } else { return MergeFieldFrom(tag, input); } } switch (field.FieldType) { case FieldType.Group: case FieldType.Message: { IBuilderLite subBuilder = (defaultFieldInstance != null) ? defaultFieldInstance.WeakCreateBuilderForType() : builder.CreateBuilderForField(field); if (!field.IsRepeated) { subBuilder.WeakMergeFrom((IMessageLite) builder[field]); if (field.FieldType == FieldType.Group) { input.ReadGroup(field.FieldNumber, subBuilder, extensionRegistry); } else { input.ReadMessage(subBuilder, extensionRegistry); } builder[field] = subBuilder.WeakBuild(); } else { List list = new List(); if (field.FieldType == FieldType.Group) { input.ReadGroupArray(tag, fieldName, list, subBuilder.WeakDefaultInstanceForType, extensionRegistry); } else { input.ReadMessageArray(tag, fieldName, list, subBuilder.WeakDefaultInstanceForType, extensionRegistry); } foreach (IMessageLite m in list) { builder.WeakAddRepeatedField(field, m); } return true; } break; } case FieldType.Enum: { if (!field.IsRepeated) { object unknown; IEnumLite value = null; if (input.ReadEnum(ref value, out unknown, field.EnumType)) { builder[field] = value; } else if (unknown is int) { MergeVarintField(fieldNumber, (ulong) (int) unknown); } } else { ICollection unknown; List list = new List(); input.ReadEnumArray(tag, fieldName, list, out unknown, field.EnumType); foreach (IEnumLite en in list) { builder.WeakAddRepeatedField(field, en); } if (unknown != null) { foreach (object oval in unknown) { if (oval is int) { MergeVarintField(fieldNumber, (ulong) (int) oval); } } } } break; } default: { if (!field.IsRepeated) { object value = null; if (input.ReadPrimitiveField(field.FieldType, ref value)) { builder[field] = value; } } else { List list = new List(); input.ReadPrimitiveArray(field.FieldType, tag, fieldName, list); foreach (object oval in list) { builder.WeakAddRepeatedField(field, oval); } } break; } } return true; } /// /// Called by MergeFieldFrom to parse a MessageSet extension. /// private void MergeMessageSetExtensionFromCodedStream(ICodedInputStream input, ExtensionRegistry extensionRegistry, IBuilder builder) { MessageDescriptor type = builder.DescriptorForType; // The wire format for MessageSet is: // message MessageSet { // repeated group Item = 1 { // required int32 typeId = 2; // required bytes message = 3; // } // } // "typeId" is the extension's field number. The extension can only be // a message type, where "message" contains the encoded bytes of that // message. // // In practice, we will probably never see a MessageSet item in which // the message appears before the type ID, or where either field does not // appear exactly once. However, in theory such cases are valid, so we // should be prepared to accept them. int typeId = 0; ByteString rawBytes = null; // If we encounter "message" before "typeId" IBuilderLite subBuilder = null; FieldDescriptor field = null; uint lastTag = WireFormat.MessageSetTag.ItemStart; uint tag; string name; while (input.ReadTag(out tag, out name)) { if (tag == 0 && name != null) { if (name == "type_id") { tag = WireFormat.MessageSetTag.TypeID; } else if (name == "message") { tag = WireFormat.MessageSetTag.Message; } } if (tag == 0) { if (input.SkipField()) { continue; //can't merge unknown without field tag } break; } lastTag = tag; if (tag == WireFormat.MessageSetTag.TypeID) { typeId = 0; // Zero is not a valid type ID. if (input.ReadInt32(ref typeId) && typeId != 0) { ExtensionInfo extension = extensionRegistry[type, typeId]; if (extension != null) { field = extension.Descriptor; subBuilder = extension.DefaultInstance.WeakCreateBuilderForType(); IMessageLite originalMessage = (IMessageLite) builder[field]; if (originalMessage != null) { subBuilder.WeakMergeFrom(originalMessage); } if (rawBytes != null) { // We already encountered the message. Parse it now. // TODO(jonskeet): Check this is okay. It's subtly different from the Java, as it doesn't create an input stream from rawBytes. // In fact, why don't we just call MergeFrom(rawBytes)? And what about the extension registry? subBuilder.WeakMergeFrom(rawBytes.CreateCodedInput()); rawBytes = null; } } else { // Unknown extension number. If we already saw data, put it // in rawBytes. if (rawBytes != null) { MergeField(typeId, UnknownField.CreateBuilder().AddLengthDelimited(rawBytes).Build()); rawBytes = null; } } } } else if (tag == WireFormat.MessageSetTag.Message) { if (subBuilder != null) { // We already know the type, so we can parse directly from the input // with no copying. Hooray! input.ReadMessage(subBuilder, extensionRegistry); } else if (input.ReadBytes(ref rawBytes)) { if (typeId != 0) { // We don't know how to parse this. Ignore it. MergeField(typeId, UnknownField.CreateBuilder().AddLengthDelimited(rawBytes).Build()); } } } else { // Unknown tag. Skip it. if (!input.SkipField()) { break; // end of group } } } if (lastTag != WireFormat.MessageSetTag.ItemEnd) { throw InvalidProtocolBufferException.InvalidEndTag(); } if (subBuilder != null) { builder[field] = subBuilder.WeakBuild(); } } #region IBuilderLite Members bool IBuilderLite.IsInitialized { get { return fields != null; } } IBuilderLite IBuilderLite.WeakClear() { return Clear(); } IBuilderLite IBuilderLite.WeakMergeFrom(IMessageLite message) { return MergeFrom((UnknownFieldSet) message); } IBuilderLite IBuilderLite.WeakMergeFrom(ByteString data) { return MergeFrom(data); } IBuilderLite IBuilderLite.WeakMergeFrom(ByteString data, ExtensionRegistry registry) { return MergeFrom(data); } IBuilderLite IBuilderLite.WeakMergeFrom(ICodedInputStream input) { return MergeFrom(input); } IBuilderLite IBuilderLite.WeakMergeFrom(ICodedInputStream input, ExtensionRegistry registry) { return MergeFrom(input); } IMessageLite IBuilderLite.WeakBuild() { return Build(); } IMessageLite IBuilderLite.WeakBuildPartial() { return Build(); } IBuilderLite IBuilderLite.WeakClone() { return Build().WeakToBuilder(); } IMessageLite IBuilderLite.WeakDefaultInstanceForType { get { return DefaultInstance; } } #endregion } } }