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