|  | #region Copyright notice and license | 
|  | // Protocol Buffers - Google's data interchange format | 
|  | // Copyright 2015 Google Inc.  All rights reserved. | 
|  | // https://developers.google.com/protocol-buffers/ | 
|  | // | 
|  | // 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 System.Security; | 
|  | using Google.Protobuf.Reflection; | 
|  |  | 
|  | namespace Google.Protobuf | 
|  | { | 
|  | /// <summary> | 
|  | /// 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. | 
|  | /// | 
|  | /// Most users will never need to use this class directly. | 
|  | /// </summary> | 
|  | public sealed partial class UnknownFieldSet | 
|  | { | 
|  | private readonly IDictionary<int, UnknownField> fields; | 
|  |  | 
|  | /// <summary> | 
|  | /// Creates a new UnknownFieldSet. | 
|  | /// </summary> | 
|  | internal UnknownFieldSet() | 
|  | { | 
|  | this.fields = new Dictionary<int, UnknownField>(); | 
|  | } | 
|  |  | 
|  | /// <summary> | 
|  | /// Checks whether or not the given field number is present in the set. | 
|  | /// </summary> | 
|  | internal bool HasField(int field) | 
|  | { | 
|  | return fields.ContainsKey(field); | 
|  | } | 
|  |  | 
|  | /// <summary> | 
|  | /// Serializes the set and writes it to <paramref name="output"/>. | 
|  | /// </summary> | 
|  | public void WriteTo(CodedOutputStream output) | 
|  | { | 
|  | WriteContext.Initialize(output, out WriteContext ctx); | 
|  | try | 
|  | { | 
|  | WriteTo(ref ctx); | 
|  | } | 
|  | finally | 
|  | { | 
|  | ctx.CopyStateTo(output); | 
|  | } | 
|  | } | 
|  |  | 
|  | /// <summary> | 
|  | /// Serializes the set and writes it to <paramref name="ctx"/>. | 
|  | /// </summary> | 
|  | [SecuritySafeCritical] | 
|  | public void WriteTo(ref WriteContext ctx) | 
|  | { | 
|  | foreach (KeyValuePair<int, UnknownField> entry in fields) | 
|  | { | 
|  | entry.Value.WriteTo(entry.Key, ref ctx); | 
|  | } | 
|  | } | 
|  |  | 
|  | /// <summary> | 
|  | /// Gets the number of bytes required to encode this set. | 
|  | /// </summary> | 
|  | public int CalculateSize() | 
|  | { | 
|  | int result = 0; | 
|  | foreach (KeyValuePair<int, UnknownField> entry in fields) | 
|  | { | 
|  | result += entry.Value.GetSerializedSize(entry.Key); | 
|  | } | 
|  | return result; | 
|  | } | 
|  |  | 
|  | /// <summary> | 
|  | /// Checks if two unknown field sets are equal. | 
|  | /// </summary> | 
|  | public override bool Equals(object other) | 
|  | { | 
|  | if (ReferenceEquals(this, other)) | 
|  | { | 
|  | return true; | 
|  | } | 
|  | UnknownFieldSet otherSet = other as UnknownFieldSet; | 
|  | IDictionary<int, UnknownField> otherFields = otherSet.fields; | 
|  | if (fields.Count  != otherFields.Count) | 
|  | { | 
|  | return false; | 
|  | } | 
|  | foreach (KeyValuePair<int, UnknownField> leftEntry in fields) | 
|  | { | 
|  | UnknownField rightValue; | 
|  | if (!otherFields.TryGetValue(leftEntry.Key, out rightValue)) | 
|  | { | 
|  | return false; | 
|  | } | 
|  | if (!leftEntry.Value.Equals(rightValue)) | 
|  | { | 
|  | return false; | 
|  | } | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | /// <summary> | 
|  | /// Gets the unknown field set's hash code. | 
|  | /// </summary> | 
|  | public override int GetHashCode() | 
|  | { | 
|  | int ret = 1; | 
|  | foreach (KeyValuePair<int, UnknownField> field in fields) | 
|  | { | 
|  | // Use ^ here to make the field order irrelevant. | 
|  | int hash = field.Key.GetHashCode() ^ field.Value.GetHashCode(); | 
|  | ret ^= hash; | 
|  | } | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | // Optimization:  We keep around 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 lastField; | 
|  |  | 
|  | private UnknownField GetOrAddField(int number) | 
|  | { | 
|  | if (lastField != null && number == lastFieldNumber) | 
|  | { | 
|  | return lastField; | 
|  | } | 
|  | if (number == 0) | 
|  | { | 
|  | return null; | 
|  | } | 
|  |  | 
|  | UnknownField existing; | 
|  | if (fields.TryGetValue(number, out existing)) | 
|  | { | 
|  | return existing; | 
|  | } | 
|  | lastField = new UnknownField(); | 
|  | AddOrReplaceField(number, lastField); | 
|  | lastFieldNumber = number; | 
|  | return lastField; | 
|  | } | 
|  |  | 
|  | /// <summary> | 
|  | /// Adds a field to the set. If a field with the same number already exists, it | 
|  | /// is replaced. | 
|  | /// </summary> | 
|  | internal UnknownFieldSet AddOrReplaceField(int number, UnknownField field) | 
|  | { | 
|  | if (number == 0) | 
|  | { | 
|  | throw new ArgumentOutOfRangeException("number", "Zero is not a valid field number."); | 
|  | } | 
|  | fields[number] = field; | 
|  | return this; | 
|  | } | 
|  |  | 
|  | /// <summary> | 
|  | /// Parse a single field from <paramref name="ctx"/> and merge it | 
|  | /// into this set. | 
|  | /// </summary> | 
|  | /// <param name="ctx">The parse context from which to read the field</param> | 
|  | /// <returns>false if the tag is an "end group" tag, true otherwise</returns> | 
|  | private bool MergeFieldFrom(ref ParseContext ctx) | 
|  | { | 
|  | uint tag = ctx.LastTag; | 
|  | int number = WireFormat.GetTagFieldNumber(tag); | 
|  | switch (WireFormat.GetTagWireType(tag)) | 
|  | { | 
|  | case WireFormat.WireType.Varint: | 
|  | { | 
|  | ulong uint64 = ctx.ReadUInt64(); | 
|  | GetOrAddField(number).AddVarint(uint64); | 
|  | return true; | 
|  | } | 
|  | case WireFormat.WireType.Fixed32: | 
|  | { | 
|  | uint uint32 = ctx.ReadFixed32(); | 
|  | GetOrAddField(number).AddFixed32(uint32); | 
|  | return true; | 
|  | } | 
|  | case WireFormat.WireType.Fixed64: | 
|  | { | 
|  | ulong uint64 = ctx.ReadFixed64(); | 
|  | GetOrAddField(number).AddFixed64(uint64); | 
|  | return true; | 
|  | } | 
|  | case WireFormat.WireType.LengthDelimited: | 
|  | { | 
|  | ByteString bytes = ctx.ReadBytes(); | 
|  | GetOrAddField(number).AddLengthDelimited(bytes); | 
|  | return true; | 
|  | } | 
|  | case WireFormat.WireType.StartGroup: | 
|  | { | 
|  | UnknownFieldSet set = new UnknownFieldSet(); | 
|  | ParsingPrimitivesMessages.ReadGroup(ref ctx, number, set); | 
|  | GetOrAddField(number).AddGroup(set); | 
|  | return true; | 
|  | } | 
|  | case WireFormat.WireType.EndGroup: | 
|  | { | 
|  | return false; | 
|  | } | 
|  | default: | 
|  | throw InvalidProtocolBufferException.InvalidWireType(); | 
|  | } | 
|  | } | 
|  |  | 
|  | internal void MergeGroupFrom(ref ParseContext ctx) | 
|  | { | 
|  | while (true) | 
|  | { | 
|  | uint tag = ctx.ReadTag(); | 
|  | if (tag == 0) | 
|  | { | 
|  | break; | 
|  | } | 
|  | if (!MergeFieldFrom(ref ctx)) | 
|  | { | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /// <summary> | 
|  | /// Create a new UnknownFieldSet if unknownFields is null. | 
|  | /// Parse a single field from <paramref name="input"/> and merge it | 
|  | /// into unknownFields. If <paramref name="input"/> is configured to discard unknown fields, | 
|  | /// <paramref name="unknownFields"/> will be returned as-is and the field will be skipped. | 
|  | /// </summary> | 
|  | /// <param name="unknownFields">The UnknownFieldSet which need to be merged</param> | 
|  | /// <param name="input">The coded input stream containing the field</param> | 
|  | /// <returns>The merged UnknownFieldSet</returns> | 
|  | public static UnknownFieldSet MergeFieldFrom(UnknownFieldSet unknownFields, | 
|  | CodedInputStream input) | 
|  | { | 
|  | ParseContext.Initialize(input, out ParseContext ctx); | 
|  | try | 
|  | { | 
|  | return MergeFieldFrom(unknownFields, ref ctx); | 
|  | } | 
|  | finally | 
|  | { | 
|  | ctx.CopyStateTo(input); | 
|  | } | 
|  | } | 
|  |  | 
|  | /// <summary> | 
|  | /// Create a new UnknownFieldSet if unknownFields is null. | 
|  | /// Parse a single field from <paramref name="ctx"/> and merge it | 
|  | /// into unknownFields. If <paramref name="ctx"/> is configured to discard unknown fields, | 
|  | /// <paramref name="unknownFields"/> will be returned as-is and the field will be skipped. | 
|  | /// </summary> | 
|  | /// <param name="unknownFields">The UnknownFieldSet which need to be merged</param> | 
|  | /// <param name="ctx">The parse context from which to read the field</param> | 
|  | /// <returns>The merged UnknownFieldSet</returns> | 
|  | [SecuritySafeCritical] | 
|  | public static UnknownFieldSet MergeFieldFrom(UnknownFieldSet unknownFields, | 
|  | ref ParseContext ctx) | 
|  | { | 
|  | if (ctx.DiscardUnknownFields) | 
|  | { | 
|  | ParsingPrimitivesMessages.SkipLastField(ref ctx.buffer, ref ctx.state); | 
|  | return unknownFields; | 
|  | } | 
|  | if (unknownFields == null) | 
|  | { | 
|  | unknownFields = new UnknownFieldSet(); | 
|  | } | 
|  | if (!unknownFields.MergeFieldFrom(ref ctx)) | 
|  | { | 
|  | throw new InvalidProtocolBufferException("Merge an unknown field of end-group tag, indicating that the corresponding start-group was missing."); // match the old code-gen | 
|  | } | 
|  | return unknownFields; | 
|  | } | 
|  |  | 
|  | /// <summary> | 
|  | /// Merges the fields from <paramref name="other"/> into this set. | 
|  | /// If a field number exists in both sets, the values in <paramref name="other"/> | 
|  | /// will be appended to the values in this set. | 
|  | /// </summary> | 
|  | private UnknownFieldSet MergeFrom(UnknownFieldSet other) | 
|  | { | 
|  | if (other != null) | 
|  | { | 
|  | foreach (KeyValuePair<int, UnknownField> entry in other.fields) | 
|  | { | 
|  | MergeField(entry.Key, entry.Value); | 
|  | } | 
|  | } | 
|  | return this; | 
|  | } | 
|  |  | 
|  | /// <summary> | 
|  | /// Created a new UnknownFieldSet to <paramref name="unknownFields"/> if | 
|  | /// needed and merges the fields from <paramref name="other"/> into the first set. | 
|  | /// If a field number exists in both sets, the values in <paramref name="other"/> | 
|  | /// will be appended to the values in this set. | 
|  | /// </summary> | 
|  | public static UnknownFieldSet MergeFrom(UnknownFieldSet unknownFields, | 
|  | UnknownFieldSet other) | 
|  | { | 
|  | if (other == null) | 
|  | { | 
|  | return unknownFields; | 
|  | } | 
|  | if (unknownFields == null) | 
|  | { | 
|  | unknownFields = new UnknownFieldSet(); | 
|  | } | 
|  | unknownFields.MergeFrom(other); | 
|  | return unknownFields; | 
|  | } | 
|  |  | 
|  |  | 
|  | /// <summary> | 
|  | /// Adds a field to the unknown field set. If a field with the same | 
|  | /// number already exists, the two are merged. | 
|  | /// </summary> | 
|  | private UnknownFieldSet MergeField(int number, UnknownField field) | 
|  | { | 
|  | if (number == 0) | 
|  | { | 
|  | throw new ArgumentOutOfRangeException("number", "Zero is not a valid field number."); | 
|  | } | 
|  | if (HasField(number)) | 
|  | { | 
|  | GetOrAddField(number).MergeFrom(field); | 
|  | } | 
|  | else | 
|  | { | 
|  | AddOrReplaceField(number, field); | 
|  | } | 
|  | return this; | 
|  | } | 
|  |  | 
|  | /// <summary> | 
|  | /// Clone an unknown field set from <paramref name="other"/>. | 
|  | /// </summary> | 
|  | public static UnknownFieldSet Clone(UnknownFieldSet other) | 
|  | { | 
|  | if (other == null) | 
|  | { | 
|  | return null; | 
|  | } | 
|  | UnknownFieldSet unknownFields = new UnknownFieldSet(); | 
|  | unknownFields.MergeFrom(other); | 
|  | return unknownFields; | 
|  | } | 
|  | } | 
|  | } | 
|  |  |