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 System.Security; 37 using Google.Protobuf.Reflection; 38 39 namespace Google.Protobuf 40 { 41 /// <summary> 42 /// Used to keep track of fields which were seen when parsing a protocol message 43 /// but whose field numbers or types are unrecognized. This most frequently 44 /// occurs when new fields are added to a message type and then messages containing 45 /// those fields are read by old software that was built before the new types were 46 /// added. 47 /// 48 /// Most users will never need to use this class directly. 49 /// </summary> 50 public sealed partial class UnknownFieldSet 51 { 52 private readonly IDictionary<int, UnknownField> fields; 53 54 /// <summary> 55 /// Creates a new UnknownFieldSet. 56 /// </summary> UnknownFieldSet()57 internal UnknownFieldSet() 58 { 59 this.fields = new Dictionary<int, UnknownField>(); 60 } 61 62 /// <summary> 63 /// Checks whether or not the given field number is present in the set. 64 /// </summary> HasField(int field)65 internal bool HasField(int field) 66 { 67 return fields.ContainsKey(field); 68 } 69 70 /// <summary> 71 /// Serializes the set and writes it to <paramref name="output"/>. 72 /// </summary> WriteTo(CodedOutputStream output)73 public void WriteTo(CodedOutputStream output) 74 { 75 WriteContext.Initialize(output, out WriteContext ctx); 76 try 77 { 78 WriteTo(ref ctx); 79 } 80 finally 81 { 82 ctx.CopyStateTo(output); 83 } 84 } 85 86 /// <summary> 87 /// Serializes the set and writes it to <paramref name="ctx"/>. 88 /// </summary> 89 [SecuritySafeCritical] WriteTo(ref WriteContext ctx)90 public void WriteTo(ref WriteContext ctx) 91 { 92 foreach (KeyValuePair<int, UnknownField> entry in fields) 93 { 94 entry.Value.WriteTo(entry.Key, ref ctx); 95 } 96 } 97 98 /// <summary> 99 /// Gets the number of bytes required to encode this set. 100 /// </summary> CalculateSize()101 public int CalculateSize() 102 { 103 int result = 0; 104 foreach (KeyValuePair<int, UnknownField> entry in fields) 105 { 106 result += entry.Value.GetSerializedSize(entry.Key); 107 } 108 return result; 109 } 110 111 /// <summary> 112 /// Checks if two unknown field sets are equal. 113 /// </summary> Equals(object other)114 public override bool Equals(object other) 115 { 116 if (ReferenceEquals(this, other)) 117 { 118 return true; 119 } 120 UnknownFieldSet otherSet = other as UnknownFieldSet; 121 IDictionary<int, UnknownField> otherFields = otherSet.fields; 122 if (fields.Count != otherFields.Count) 123 { 124 return false; 125 } 126 foreach (KeyValuePair<int, UnknownField> leftEntry in fields) 127 { 128 UnknownField rightValue; 129 if (!otherFields.TryGetValue(leftEntry.Key, out rightValue)) 130 { 131 return false; 132 } 133 if (!leftEntry.Value.Equals(rightValue)) 134 { 135 return false; 136 } 137 } 138 return true; 139 } 140 141 /// <summary> 142 /// Gets the unknown field set's hash code. 143 /// </summary> GetHashCode()144 public override int GetHashCode() 145 { 146 int ret = 1; 147 foreach (KeyValuePair<int, UnknownField> field in fields) 148 { 149 // Use ^ here to make the field order irrelevant. 150 int hash = field.Key.GetHashCode() ^ field.Value.GetHashCode(); 151 ret ^= hash; 152 } 153 return ret; 154 } 155 156 // Optimization: We keep around the last field that was 157 // modified so that we can efficiently add to it multiple times in a 158 // row (important when parsing an unknown repeated field). 159 private int lastFieldNumber; 160 private UnknownField lastField; 161 GetOrAddField(int number)162 private UnknownField GetOrAddField(int number) 163 { 164 if (lastField != null && number == lastFieldNumber) 165 { 166 return lastField; 167 } 168 if (number == 0) 169 { 170 return null; 171 } 172 173 UnknownField existing; 174 if (fields.TryGetValue(number, out existing)) 175 { 176 return existing; 177 } 178 lastField = new UnknownField(); 179 AddOrReplaceField(number, lastField); 180 lastFieldNumber = number; 181 return lastField; 182 } 183 184 /// <summary> 185 /// Adds a field to the set. If a field with the same number already exists, it 186 /// is replaced. 187 /// </summary> AddOrReplaceField(int number, UnknownField field)188 internal UnknownFieldSet AddOrReplaceField(int number, UnknownField field) 189 { 190 if (number == 0) 191 { 192 throw new ArgumentOutOfRangeException("number", "Zero is not a valid field number."); 193 } 194 fields[number] = field; 195 return this; 196 } 197 198 /// <summary> 199 /// Parse a single field from <paramref name="ctx"/> and merge it 200 /// into this set. 201 /// </summary> 202 /// <param name="ctx">The parse context from which to read the field</param> 203 /// <returns>false if the tag is an "end group" tag, true otherwise</returns> MergeFieldFrom(ref ParseContext ctx)204 private bool MergeFieldFrom(ref ParseContext ctx) 205 { 206 uint tag = ctx.LastTag; 207 int number = WireFormat.GetTagFieldNumber(tag); 208 switch (WireFormat.GetTagWireType(tag)) 209 { 210 case WireFormat.WireType.Varint: 211 { 212 ulong uint64 = ctx.ReadUInt64(); 213 GetOrAddField(number).AddVarint(uint64); 214 return true; 215 } 216 case WireFormat.WireType.Fixed32: 217 { 218 uint uint32 = ctx.ReadFixed32(); 219 GetOrAddField(number).AddFixed32(uint32); 220 return true; 221 } 222 case WireFormat.WireType.Fixed64: 223 { 224 ulong uint64 = ctx.ReadFixed64(); 225 GetOrAddField(number).AddFixed64(uint64); 226 return true; 227 } 228 case WireFormat.WireType.LengthDelimited: 229 { 230 ByteString bytes = ctx.ReadBytes(); 231 GetOrAddField(number).AddLengthDelimited(bytes); 232 return true; 233 } 234 case WireFormat.WireType.StartGroup: 235 { 236 UnknownFieldSet set = new UnknownFieldSet(); 237 ParsingPrimitivesMessages.ReadGroup(ref ctx, number, set); 238 GetOrAddField(number).AddGroup(set); 239 return true; 240 } 241 case WireFormat.WireType.EndGroup: 242 { 243 return false; 244 } 245 default: 246 throw InvalidProtocolBufferException.InvalidWireType(); 247 } 248 } 249 MergeGroupFrom(ref ParseContext ctx)250 internal void MergeGroupFrom(ref ParseContext ctx) 251 { 252 while (true) 253 { 254 uint tag = ctx.ReadTag(); 255 if (tag == 0) 256 { 257 break; 258 } 259 if (!MergeFieldFrom(ref ctx)) 260 { 261 break; 262 } 263 } 264 } 265 266 /// <summary> 267 /// Create a new UnknownFieldSet if unknownFields is null. 268 /// Parse a single field from <paramref name="input"/> and merge it 269 /// into unknownFields. If <paramref name="input"/> is configured to discard unknown fields, 270 /// <paramref name="unknownFields"/> will be returned as-is and the field will be skipped. 271 /// </summary> 272 /// <param name="unknownFields">The UnknownFieldSet which need to be merged</param> 273 /// <param name="input">The coded input stream containing the field</param> 274 /// <returns>The merged UnknownFieldSet</returns> MergeFieldFrom(UnknownFieldSet unknownFields, CodedInputStream input)275 public static UnknownFieldSet MergeFieldFrom(UnknownFieldSet unknownFields, 276 CodedInputStream input) 277 { 278 ParseContext.Initialize(input, out ParseContext ctx); 279 try 280 { 281 return MergeFieldFrom(unknownFields, ref ctx); 282 } 283 finally 284 { 285 ctx.CopyStateTo(input); 286 } 287 } 288 289 /// <summary> 290 /// Create a new UnknownFieldSet if unknownFields is null. 291 /// Parse a single field from <paramref name="ctx"/> and merge it 292 /// into unknownFields. If <paramref name="ctx"/> is configured to discard unknown fields, 293 /// <paramref name="unknownFields"/> will be returned as-is and the field will be skipped. 294 /// </summary> 295 /// <param name="unknownFields">The UnknownFieldSet which need to be merged</param> 296 /// <param name="ctx">The parse context from which to read the field</param> 297 /// <returns>The merged UnknownFieldSet</returns> 298 [SecuritySafeCritical] MergeFieldFrom(UnknownFieldSet unknownFields, ref ParseContext ctx)299 public static UnknownFieldSet MergeFieldFrom(UnknownFieldSet unknownFields, 300 ref ParseContext ctx) 301 { 302 if (ctx.DiscardUnknownFields) 303 { 304 ParsingPrimitivesMessages.SkipLastField(ref ctx.buffer, ref ctx.state); 305 return unknownFields; 306 } 307 if (unknownFields == null) 308 { 309 unknownFields = new UnknownFieldSet(); 310 } 311 if (!unknownFields.MergeFieldFrom(ref ctx)) 312 { 313 throw new InvalidProtocolBufferException("Merge an unknown field of end-group tag, indicating that the corresponding start-group was missing."); // match the old code-gen 314 } 315 return unknownFields; 316 } 317 318 /// <summary> 319 /// Merges the fields from <paramref name="other"/> into this set. 320 /// If a field number exists in both sets, the values in <paramref name="other"/> 321 /// will be appended to the values in this set. 322 /// </summary> MergeFrom(UnknownFieldSet other)323 private UnknownFieldSet MergeFrom(UnknownFieldSet other) 324 { 325 if (other != null) 326 { 327 foreach (KeyValuePair<int, UnknownField> entry in other.fields) 328 { 329 MergeField(entry.Key, entry.Value); 330 } 331 } 332 return this; 333 } 334 335 /// <summary> 336 /// Created a new UnknownFieldSet to <paramref name="unknownFields"/> if 337 /// needed and merges the fields from <paramref name="other"/> into the first set. 338 /// If a field number exists in both sets, the values in <paramref name="other"/> 339 /// will be appended to the values in this set. 340 /// </summary> MergeFrom(UnknownFieldSet unknownFields, UnknownFieldSet other)341 public static UnknownFieldSet MergeFrom(UnknownFieldSet unknownFields, 342 UnknownFieldSet other) 343 { 344 if (other == null) 345 { 346 return unknownFields; 347 } 348 if (unknownFields == null) 349 { 350 unknownFields = new UnknownFieldSet(); 351 } 352 unknownFields.MergeFrom(other); 353 return unknownFields; 354 } 355 356 357 /// <summary> 358 /// Adds a field to the unknown field set. If a field with the same 359 /// number already exists, the two are merged. 360 /// </summary> MergeField(int number, UnknownField field)361 private UnknownFieldSet MergeField(int number, UnknownField field) 362 { 363 if (number == 0) 364 { 365 throw new ArgumentOutOfRangeException("number", "Zero is not a valid field number."); 366 } 367 if (HasField(number)) 368 { 369 GetOrAddField(number).MergeFrom(field); 370 } 371 else 372 { 373 AddOrReplaceField(number, field); 374 } 375 return this; 376 } 377 378 /// <summary> 379 /// Clone an unknown field set from <paramref name="other"/>. 380 /// </summary> Clone(UnknownFieldSet other)381 public static UnknownFieldSet Clone(UnknownFieldSet other) 382 { 383 if (other == null) 384 { 385 return null; 386 } 387 UnknownFieldSet unknownFields = new UnknownFieldSet(); 388 unknownFields.MergeFrom(other); 389 return unknownFields; 390 } 391 } 392 } 393 394