• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #region Copyright notice and license
2 // Protocol Buffers - Google's data interchange format
3 // Copyright 2015 Google Inc.  All rights reserved.
4 // https://developers.google.com/protocol-buffers/
5 //
6 // Redistribution and use in source and binary forms, with or without
7 // modification, are permitted provided that the following conditions are
8 // met:
9 //
10 //     * Redistributions of source code must retain the above copyright
11 // notice, this list of conditions and the following disclaimer.
12 //     * Redistributions in binary form must reproduce the above
13 // copyright notice, this list of conditions and the following disclaimer
14 // in the documentation and/or other materials provided with the
15 // distribution.
16 //     * Neither the name of Google Inc. nor the names of its
17 // contributors may be used to endorse or promote products derived from
18 // this software without specific prior written permission.
19 //
20 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 #endregion
32 
33 using System;
34 using System.Collections.Generic;
35 using System.IO;
36 using Google.Protobuf.Reflection;
37 
38 namespace Google.Protobuf
39 {
40     /// <summary>
41     /// Used to keep track of fields which were seen when parsing a protocol message
42     /// but whose field numbers or types are unrecognized. This most frequently
43     /// occurs when new fields are added to a message type and then messages containing
44     /// those fields are read by old software that was built before the new types were
45     /// added.
46     ///
47     /// Most users will never need to use this class directly.
48     /// </summary>
49     public sealed partial class UnknownFieldSet
50     {
51         private readonly IDictionary<int, UnknownField> fields;
52 
53         /// <summary>
54         /// Creates a new UnknownFieldSet.
55         /// </summary>
UnknownFieldSet()56         internal UnknownFieldSet()
57         {
58             this.fields = new Dictionary<int, UnknownField>();
59         }
60 
61         /// <summary>
62         /// Checks whether or not the given field number is present in the set.
63         /// </summary>
HasField(int field)64         internal bool HasField(int field)
65         {
66             return fields.ContainsKey(field);
67         }
68 
69         /// <summary>
70         /// Serializes the set and writes it to <paramref name="output"/>.
71         /// </summary>
WriteTo(CodedOutputStream output)72         public void WriteTo(CodedOutputStream output)
73         {
74             foreach (KeyValuePair<int, UnknownField> entry in fields)
75             {
76                 entry.Value.WriteTo(entry.Key, output);
77             }
78         }
79 
80         /// <summary>
81         /// Gets the number of bytes required to encode this set.
82         /// </summary>
CalculateSize()83         public int CalculateSize()
84         {
85             int result = 0;
86             foreach (KeyValuePair<int, UnknownField> entry in fields)
87             {
88                 result += entry.Value.GetSerializedSize(entry.Key);
89             }
90             return result;
91         }
92 
93         /// <summary>
94         /// Checks if two unknown field sets are equal.
95         /// </summary>
Equals(object other)96         public override bool Equals(object other)
97         {
98             if (ReferenceEquals(this, other))
99             {
100                 return true;
101             }
102             UnknownFieldSet otherSet = other as UnknownFieldSet;
103             IDictionary<int, UnknownField> otherFields = otherSet.fields;
104             if (fields.Count  != otherFields.Count)
105             {
106                 return false;
107             }
108             foreach (KeyValuePair<int, UnknownField> leftEntry in fields)
109             {
110                 UnknownField rightValue;
111                 if (!otherFields.TryGetValue(leftEntry.Key, out rightValue))
112                 {
113                     return false;
114                 }
115                 if (!leftEntry.Value.Equals(rightValue))
116                 {
117                     return false;
118                 }
119             }
120             return true;
121         }
122 
123         /// <summary>
124         /// Gets the unknown field set's hash code.
125         /// </summary>
GetHashCode()126         public override int GetHashCode()
127         {
128             int ret = 1;
129             foreach (KeyValuePair<int, UnknownField> field in fields)
130             {
131                 // Use ^ here to make the field order irrelevant.
132                 int hash = field.Key.GetHashCode() ^ field.Value.GetHashCode();
133                 ret ^= hash;
134             }
135             return ret;
136         }
137 
138         // Optimization:  We keep around the last field that was
139         // modified so that we can efficiently add to it multiple times in a
140         // row (important when parsing an unknown repeated field).
141         private int lastFieldNumber;
142         private UnknownField lastField;
143 
GetOrAddField(int number)144         private UnknownField GetOrAddField(int number)
145         {
146             if (lastField != null && number == lastFieldNumber)
147             {
148                 return lastField;
149             }
150             if (number == 0)
151             {
152                 return null;
153             }
154 
155             UnknownField existing;
156             if (fields.TryGetValue(number, out existing))
157             {
158                 return existing;
159             }
160             lastField = new UnknownField();
161             AddOrReplaceField(number, lastField);
162             lastFieldNumber = number;
163             return lastField;
164         }
165 
166         /// <summary>
167         /// Adds a field to the set. If a field with the same number already exists, it
168         /// is replaced.
169         /// </summary>
AddOrReplaceField(int number, UnknownField field)170         internal UnknownFieldSet AddOrReplaceField(int number, UnknownField field)
171         {
172             if (number == 0)
173             {
174                 throw new ArgumentOutOfRangeException("number", "Zero is not a valid field number.");
175             }
176             fields[number] = field;
177             return this;
178         }
179 
180         /// <summary>
181         /// Parse a single field from <paramref name="input"/> and merge it
182         /// into this set.
183         /// </summary>
184         /// <param name="input">The coded input stream containing the field</param>
185         /// <returns>false if the tag is an "end group" tag, true otherwise</returns>
MergeFieldFrom(CodedInputStream input)186         private bool MergeFieldFrom(CodedInputStream input)
187         {
188             uint tag = input.LastTag;
189             int number = WireFormat.GetTagFieldNumber(tag);
190             switch (WireFormat.GetTagWireType(tag))
191             {
192                 case WireFormat.WireType.Varint:
193                     {
194                         ulong uint64 = input.ReadUInt64();
195                         GetOrAddField(number).AddVarint(uint64);
196                         return true;
197                     }
198                 case WireFormat.WireType.Fixed32:
199                     {
200                         uint uint32 = input.ReadFixed32();
201                         GetOrAddField(number).AddFixed32(uint32);
202                         return true;
203                     }
204                 case WireFormat.WireType.Fixed64:
205                     {
206                         ulong uint64 = input.ReadFixed64();
207                         GetOrAddField(number).AddFixed64(uint64);
208                         return true;
209                     }
210                 case WireFormat.WireType.LengthDelimited:
211                     {
212                         ByteString bytes = input.ReadBytes();
213                         GetOrAddField(number).AddLengthDelimited(bytes);
214                         return true;
215                     }
216                 case WireFormat.WireType.StartGroup:
217                     {
218                         uint endTag = WireFormat.MakeTag(number, WireFormat.WireType.EndGroup);
219                         UnknownFieldSet set = new UnknownFieldSet();
220                         while (input.ReadTag() != endTag)
221                         {
222                             set.MergeFieldFrom(input);
223                         }
224                         GetOrAddField(number).AddGroup(set);
225                         return true;
226                     }
227                 case WireFormat.WireType.EndGroup:
228                     {
229                         return false;
230                     }
231                 default:
232                     throw InvalidProtocolBufferException.InvalidWireType();
233             }
234         }
235 
236         /// <summary>
237         /// Create a new UnknownFieldSet if unknownFields is null.
238         /// Parse a single field from <paramref name="input"/> and merge it
239         /// into unknownFields. If <paramref name="input"/> is configured to discard unknown fields,
240         /// <paramref name="unknownFields"/> will be returned as-is and the field will be skipped.
241         /// </summary>
242         /// <param name="unknownFields">The UnknownFieldSet which need to be merged</param>
243         /// <param name="input">The coded input stream containing the field</param>
244         /// <returns>The merged UnknownFieldSet</returns>
MergeFieldFrom(UnknownFieldSet unknownFields, CodedInputStream input)245         public static UnknownFieldSet MergeFieldFrom(UnknownFieldSet unknownFields,
246                                                      CodedInputStream input)
247         {
248             if (input.DiscardUnknownFields)
249             {
250                 input.SkipLastField();
251                 return unknownFields;
252             }
253             if (unknownFields == null)
254             {
255                 unknownFields = new UnknownFieldSet();
256             }
257             if (!unknownFields.MergeFieldFrom(input))
258             {
259                 throw new InvalidProtocolBufferException("Merge an unknown field of end-group tag, indicating that the corresponding start-group was missing."); // match the old code-gen
260             }
261             return unknownFields;
262         }
263 
264         /// <summary>
265         /// Merges the fields from <paramref name="other"/> into this set.
266         /// If a field number exists in both sets, the values in <paramref name="other"/>
267         /// will be appended to the values in this set.
268         /// </summary>
MergeFrom(UnknownFieldSet other)269         private UnknownFieldSet MergeFrom(UnknownFieldSet other)
270         {
271             if (other != null)
272             {
273                 foreach (KeyValuePair<int, UnknownField> entry in other.fields)
274                 {
275                     MergeField(entry.Key, entry.Value);
276                 }
277             }
278             return this;
279         }
280 
281         /// <summary>
282         /// Created a new UnknownFieldSet to <paramref name="unknownFields"/> if
283         /// needed and merges the fields from <paramref name="other"/> into the first set.
284         /// If a field number exists in both sets, the values in <paramref name="other"/>
285         /// will be appended to the values in this set.
286         /// </summary>
MergeFrom(UnknownFieldSet unknownFields, UnknownFieldSet other)287         public static UnknownFieldSet MergeFrom(UnknownFieldSet unknownFields,
288                                                 UnknownFieldSet other)
289         {
290             if (other == null)
291             {
292                 return unknownFields;
293             }
294             if (unknownFields == null)
295             {
296                 unknownFields = new UnknownFieldSet();
297             }
298             unknownFields.MergeFrom(other);
299             return unknownFields;
300         }
301 
302 
303         /// <summary>
304         /// Adds a field to the unknown field set. If a field with the same
305         /// number already exists, the two are merged.
306         /// </summary>
MergeField(int number, UnknownField field)307         private UnknownFieldSet MergeField(int number, UnknownField field)
308         {
309             if (number == 0)
310             {
311                 throw new ArgumentOutOfRangeException("number", "Zero is not a valid field number.");
312             }
313             if (HasField(number))
314             {
315                 GetOrAddField(number).MergeFrom(field);
316             }
317             else
318             {
319                 AddOrReplaceField(number, field);
320             }
321             return this;
322         }
323 
324         /// <summary>
325         /// Clone an unknown field set from <paramref name="other"/>.
326         /// </summary>
Clone(UnknownFieldSet other)327         public static UnknownFieldSet Clone(UnknownFieldSet other)
328         {
329             if (other == null)
330             {
331                 return null;
332             }
333             UnknownFieldSet unknownFields = new UnknownFieldSet();
334             unknownFields.MergeFrom(other);
335             return unknownFields;
336         }
337     }
338 }
339 
340