blob: 2a955fc6bb83d126feb4a2ff7324c2b6f7b6fd94 [file] [log] [blame]
Jon Skeet0aac0e42009-09-09 18:48:02 +01001#region Copyright notice and license
Jon Skeet60c059b2008-10-23 21:17:56 +01002// Protocol Buffers - Google's data interchange format
3// Copyright 2008 Google Inc. All rights reserved.
4// http://github.com/jskeet/dotnet-protobufs/
5// Original C++/Java/Python code:
Jon Skeet68036862008-10-22 13:30:34 +01006// http://code.google.com/p/protobuf/
7//
Jon Skeet60c059b2008-10-23 21:17:56 +01008// Redistribution and use in source and binary forms, with or without
9// modification, are permitted provided that the following conditions are
10// met:
Jon Skeet68036862008-10-22 13:30:34 +010011//
Jon Skeet60c059b2008-10-23 21:17:56 +010012// * Redistributions of source code must retain the above copyright
13// notice, this list of conditions and the following disclaimer.
14// * Redistributions in binary form must reproduce the above
15// copyright notice, this list of conditions and the following disclaimer
16// in the documentation and/or other materials provided with the
17// distribution.
18// * Neither the name of Google Inc. nor the names of its
19// contributors may be used to endorse or promote products derived from
20// this software without specific prior written permission.
Jon Skeet68036862008-10-22 13:30:34 +010021//
Jon Skeet60c059b2008-10-23 21:17:56 +010022// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
25// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
26// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
28// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
32// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Jon Skeet0aac0e42009-09-09 18:48:02 +010033#endregion
34
Jon Skeet68036862008-10-22 13:30:34 +010035using System;
36using System.Collections.Generic;
37using System.IO;
38using Google.ProtocolBuffers.Collections;
39using Google.ProtocolBuffers.Descriptors;
40using Google.ProtocolBuffers.DescriptorProtos;
41
42namespace Google.ProtocolBuffers {
43 /// <summary>
44 /// Used to keep track of fields which were seen when parsing a protocol message
45 /// but whose field numbers or types are unrecognized. This most frequently
46 /// occurs when new fields are added to a message type and then messages containing
47 /// those fields are read by old software that was built before the new types were
48 /// added.
49 ///
50 /// Every message contains an UnknownFieldSet.
51 ///
52 /// Most users will never need to use this class directly.
53 /// </summary>
54 public sealed class UnknownFieldSet {
55
56 private static readonly UnknownFieldSet defaultInstance = new UnknownFieldSet(new Dictionary<int, UnknownField>());
57
58 private readonly IDictionary<int, UnknownField> fields;
59
60 private UnknownFieldSet(IDictionary<int, UnknownField> fields) {
61 this.fields = fields;
62 }
63
64 /// <summary>
65 /// Creates a new unknown field set builder.
66 /// </summary>
67 public static Builder CreateBuilder() {
68 return new Builder();
69 }
70
71 /// <summary>
72 /// Creates a new unknown field set builder
73 /// and initialize it from <paramref name="original"/>.
74 /// </summary>
75 public static Builder CreateBuilder(UnknownFieldSet original) {
76 return new Builder().MergeFrom(original);
77 }
78
79 public static UnknownFieldSet DefaultInstance {
80 get { return defaultInstance; }
81 }
82
83 /// <summary>
84 /// Returns a read-only view of the mapping from field numbers to values.
85 /// </summary>
86 public IDictionary<int, UnknownField> FieldDictionary {
87 get { return Dictionaries.AsReadOnly(fields); }
88 }
89
90 /// <summary>
91 /// Checks whether or not the given field number is present in the set.
92 /// </summary>
93 public bool HasField(int field) {
94 return fields.ContainsKey(field);
95 }
96
97 /// <summary>
98 /// Fetches a field by number, returning an empty field if not present.
99 /// Never returns null.
100 /// </summary>
101 public UnknownField this[int number] {
102 get {
103 UnknownField ret;
104 if (!fields.TryGetValue(number, out ret)) {
105 ret = UnknownField.DefaultInstance;
106 }
107 return ret;
108 }
109 }
110
111 /// <summary>
112 /// Serializes the set and writes it to <paramref name="output"/>.
113 /// </summary>
114 public void WriteTo(CodedOutputStream output) {
115 foreach (KeyValuePair<int, UnknownField> entry in fields) {
116 entry.Value.WriteTo(entry.Key, output);
117 }
118 }
119
120 /// <summary>
121 /// Gets the number of bytes required to encode this set.
122 /// </summary>
123 public int SerializedSize {
124 get {
125 int result = 0;
126 foreach (KeyValuePair<int, UnknownField> entry in fields) {
127 result += entry.Value.GetSerializedSize(entry.Key);
128 }
129 return result;
130 }
131 }
132
133 /// <summary>
134 /// Converts the set to a string in protocol buffer text format. This
135 /// is just a trivial wrapper around TextFormat.PrintToString.
136 /// </summary>
137 public override String ToString() {
138 return TextFormat.PrintToString(this);
139 }
140
141 /// <summary>
142 /// Serializes the message to a ByteString and returns it. This is
143 /// just a trivial wrapper around WriteTo(CodedOutputStream).
144 /// </summary>
145 /// <returns></returns>
146 public ByteString ToByteString() {
147 ByteString.CodedBuilder codedBuilder = new ByteString.CodedBuilder(SerializedSize);
148 WriteTo(codedBuilder.CodedOutput);
149 return codedBuilder.Build();
150 }
151
152 /// <summary>
153 /// Serializes the message to a byte array and returns it. This is
154 /// just a trivial wrapper around WriteTo(CodedOutputStream).
155 /// </summary>
156 /// <returns></returns>
157 public byte[] ToByteArray() {
158 byte[] data = new byte[SerializedSize];
159 CodedOutputStream output = CodedOutputStream.CreateInstance(data);
160 WriteTo(output);
161 output.CheckNoSpaceLeft();
162 return data;
163 }
164
165 /// <summary>
166 /// Serializes the message and writes it to <paramref name="output"/>. This is
167 /// just a trivial wrapper around WriteTo(CodedOutputStream).
168 /// </summary>
169 /// <param name="output"></param>
170 public void WriteTo(Stream output) {
171 CodedOutputStream codedOutput = CodedOutputStream.CreateInstance(output);
172 WriteTo(codedOutput);
173 codedOutput.Flush();
174 }
175
176 /// <summary>
177 /// Serializes the set and writes it to <paramref name="output"/> using
178 /// the MessageSet wire format.
179 /// </summary>
180 public void WriteAsMessageSetTo(CodedOutputStream output) {
181 foreach (KeyValuePair<int, UnknownField> entry in fields) {
182 entry.Value.WriteAsMessageSetExtensionTo(entry.Key, output);
183 }
184 }
185
186 /// <summary>
187 /// Gets the number of bytes required to encode this set using the MessageSet
188 /// wire format.
189 /// </summary>
190 public int SerializedSizeAsMessageSet {
191 get {
192 int result = 0;
193 foreach (KeyValuePair<int, UnknownField> entry in fields) {
194 result += entry.Value.GetSerializedSizeAsMessageSetExtension(entry.Key);
195 }
196 return result;
197 }
198 }
199
Jon Skeet43da7ae2009-05-28 21:45:43 +0100200 public override bool Equals(object other) {
201 if (ReferenceEquals(this, other)) {
202 return true;
203 }
204 UnknownFieldSet otherSet = other as UnknownFieldSet;
205 return otherSet != null && Dictionaries.Equals(fields, otherSet.fields);
206 }
207
208 public override int GetHashCode() {
209 return Dictionaries.GetHashCode(fields);
210 }
211
Jon Skeet68036862008-10-22 13:30:34 +0100212 /// <summary>
213 /// Parses an UnknownFieldSet from the given input.
214 /// </summary>
215 public static UnknownFieldSet ParseFrom(CodedInputStream input) {
216 return CreateBuilder().MergeFrom(input).Build();
217 }
218
219 /// <summary>
220 /// Parses an UnknownFieldSet from the given data.
221 /// </summary>
222 public static UnknownFieldSet ParseFrom(ByteString data) {
223 return CreateBuilder().MergeFrom(data).Build();
224 }
225
226 /// <summary>
227 /// Parses an UnknownFieldSet from the given data.
228 /// </summary>
229 public static UnknownFieldSet ParseFrom(byte[] data) {
230 return CreateBuilder().MergeFrom(data).Build();
231 }
232
233 /// <summary>
234 /// Parses an UnknownFieldSet from the given input.
235 /// </summary>
236 public static UnknownFieldSet ParseFrom(Stream input) {
237 return CreateBuilder().MergeFrom(input).Build();
238 }
239
240 /// <summary>
241 /// Builder for UnknownFieldSets.
242 /// </summary>
243 public sealed class Builder
244 {
245 /// <summary>
Jon Skeet60fb63e2009-06-20 20:46:28 +0100246 /// Mapping from number to field. Note that by using a SortedDictionary we ensure
Jon Skeet68036862008-10-22 13:30:34 +0100247 /// that the fields will be serialized in ascending order.
248 /// </summary>
Jon Skeet60fb63e2009-06-20 20:46:28 +0100249 private IDictionary<int, UnknownField> fields = new SortedDictionary<int, UnknownField>();
Jon Skeet68036862008-10-22 13:30:34 +0100250
251 // Optimization: We keep around a builder for the last field that was
252 // modified so that we can efficiently add to it multiple times in a
253 // row (important when parsing an unknown repeated field).
Jon Skeet43da7ae2009-05-28 21:45:43 +0100254 private int lastFieldNumber;
255 private UnknownField.Builder lastField;
Jon Skeet68036862008-10-22 13:30:34 +0100256
257 internal Builder() {
258 }
259
260 /// <summary>
261 /// Returns a field builder for the specified field number, including any values
262 /// which already exist.
263 /// </summary>
264 private UnknownField.Builder GetFieldBuilder(int number) {
265 if (lastField != null) {
266 if (number == lastFieldNumber) {
267 return lastField;
268 }
269 // Note: AddField() will reset lastField and lastFieldNumber.
270 AddField(lastFieldNumber, lastField.Build());
271 }
272 if (number == 0) {
273 return null;
274 }
275
276 lastField = UnknownField.CreateBuilder();
277 UnknownField existing;
278 if (fields.TryGetValue(number, out existing)) {
279 lastField.MergeFrom(existing);
280 }
281 lastFieldNumber = number;
282 return lastField;
283 }
284
285 /// <summary>
286 /// Build the UnknownFieldSet and return it. Once this method has been called,
287 /// this instance will no longer be usable. Calling any method after this
288 /// will throw a NullReferenceException.
289 /// </summary>
290 public UnknownFieldSet Build() {
291 GetFieldBuilder(0); // Force lastField to be built.
292 UnknownFieldSet result = fields.Count == 0 ? DefaultInstance : new UnknownFieldSet(fields);
293 fields = null;
294 return result;
295 }
296
297 /// <summary>
298 /// Adds a field to the set. If a field with the same number already exists, it
299 /// is replaced.
300 /// </summary>
301 public Builder AddField(int number, UnknownField field) {
302 if (number == 0) {
303 throw new ArgumentOutOfRangeException("number", "Zero is not a valid field number.");
304 }
305 if (lastField != null && lastFieldNumber == number) {
306 // Discard this.
307 lastField = null;
308 lastFieldNumber = 0;
309 }
310 fields[number] = field;
311 return this;
312 }
313
314 /// <summary>
315 /// Resets the builder to an empty set.
316 /// </summary>
317 public Builder Clear() {
318 fields.Clear();
319 lastFieldNumber = 0;
320 lastField = null;
321 return this;
322 }
323
324 /// <summary>
325 /// Parse an entire message from <paramref name="input"/> and merge
326 /// its fields into this set.
327 /// </summary>
328 public Builder MergeFrom(CodedInputStream input) {
329 while (true) {
330 uint tag = input.ReadTag();
331 if (tag == 0 || !MergeFieldFrom(tag, input)) {
332 break;
333 }
334 }
335 return this;
336 }
337
338 /// <summary>
339 /// Parse a single field from <paramref name="input"/> and merge it
340 /// into this set.
341 /// </summary>
342 /// <param name="tag">The field's tag number, which was already parsed.</param>
343 /// <param name="input">The coded input stream containing the field</param>
344 /// <returns>false if the tag is an "end group" tag, true otherwise</returns>
Jon Skeetd6dd0a42009-06-05 22:00:05 +0100345 [CLSCompliant(false)]
Jon Skeet68036862008-10-22 13:30:34 +0100346 public bool MergeFieldFrom(uint tag, CodedInputStream input) {
347 int number = WireFormat.GetTagFieldNumber(tag);
348 switch (WireFormat.GetTagWireType(tag)) {
349 case WireFormat.WireType.Varint:
350 GetFieldBuilder(number).AddVarint(input.ReadUInt64());
351 return true;
352 case WireFormat.WireType.Fixed64:
353 GetFieldBuilder(number).AddFixed64(input.ReadFixed64());
354 return true;
355 case WireFormat.WireType.LengthDelimited:
356 GetFieldBuilder(number).AddLengthDelimited(input.ReadBytes());
357 return true;
358 case WireFormat.WireType.StartGroup: {
359 Builder subBuilder = CreateBuilder();
360 input.ReadUnknownGroup(number, subBuilder);
361 GetFieldBuilder(number).AddGroup(subBuilder.Build());
362 return true;
363 }
364 case WireFormat.WireType.EndGroup:
365 return false;
366 case WireFormat.WireType.Fixed32:
367 GetFieldBuilder(number).AddFixed32(input.ReadFixed32());
368 return true;
369 default:
370 throw InvalidProtocolBufferException.InvalidWireType();
371 }
372 }
373
374 /// <summary>
375 /// Parses <paramref name="input"/> as an UnknownFieldSet and merge it
376 /// with the set being built. This is just a small wrapper around
377 /// MergeFrom(CodedInputStream).
378 /// </summary>
379 public Builder MergeFrom(Stream input) {
380 CodedInputStream codedInput = CodedInputStream.CreateInstance(input);
381 MergeFrom(codedInput);
382 codedInput.CheckLastTagWas(0);
383 return this;
384 }
385
386 /// <summary>
387 /// Parses <paramref name="data"/> as an UnknownFieldSet and merge it
388 /// with the set being built. This is just a small wrapper around
389 /// MergeFrom(CodedInputStream).
390 /// </summary>
391 public Builder MergeFrom(ByteString data) {
392 CodedInputStream input = data.CreateCodedInput();
393 MergeFrom(input);
394 input.CheckLastTagWas(0);
395 return this;
396 }
397
398 /// <summary>
399 /// Parses <paramref name="data"/> as an UnknownFieldSet and merge it
400 /// with the set being built. This is just a small wrapper around
401 /// MergeFrom(CodedInputStream).
402 /// </summary>
403 public Builder MergeFrom(byte[] data) {
404 CodedInputStream input = CodedInputStream.CreateInstance(data);
405 MergeFrom(input);
406 input.CheckLastTagWas(0);
407 return this;
408 }
409
410 /// <summary>
411 /// Convenience method for merging a new field containing a single varint
412 /// value. This is used in particular when an unknown enum value is
413 /// encountered.
414 /// </summary>
Jon Skeetd6dd0a42009-06-05 22:00:05 +0100415 [CLSCompliant(false)]
Jon Skeet68036862008-10-22 13:30:34 +0100416 public Builder MergeVarintField(int number, ulong value) {
417 if (number == 0) {
418 throw new ArgumentOutOfRangeException("number", "Zero is not a valid field number.");
419 }
420 GetFieldBuilder(number).AddVarint(value);
421 return this;
422 }
423
424 /// <summary>
425 /// Merges the fields from <paramref name="other"/> into this set.
426 /// If a field number exists in both sets, the values in <paramref name="other"/>
427 /// will be appended to the values in this set.
428 /// </summary>
429 public Builder MergeFrom(UnknownFieldSet other) {
430 if (other != DefaultInstance) {
431 foreach(KeyValuePair<int, UnknownField> entry in other.fields) {
432 MergeField(entry.Key, entry.Value);
433 }
434 }
435 return this;
436 }
437
438 /// <summary>
439 /// Checks if the given field number is present in the set.
440 /// </summary>
441 public bool HasField(int number) {
442 if (number == 0) {
443 throw new ArgumentOutOfRangeException("number", "Zero is not a valid field number.");
444 }
445 return number == lastFieldNumber || fields.ContainsKey(number);
446 }
447
448 /// <summary>
449 /// Adds a field to the unknown field set. If a field with the same
450 /// number already exists, the two are merged.
451 /// </summary>
452 public Builder MergeField(int number, UnknownField field) {
453 if (number == 0) {
454 throw new ArgumentOutOfRangeException("number", "Zero is not a valid field number.");
455 }
456 if (HasField(number)) {
457 GetFieldBuilder(number).MergeFrom(field);
458 } else {
459 // Optimization: We could call getFieldBuilder(number).mergeFrom(field)
460 // in this case, but that would create a copy of the Field object.
461 // We'd rather reuse the one passed to us, so call AddField() instead.
462 AddField(number, field);
463 }
464 return this;
465 }
466
467 internal void MergeFrom(CodedInputStream input, ExtensionRegistry extensionRegistry, IBuilder builder) {
468 while (true) {
469 uint tag = input.ReadTag();
470 if (tag == 0) {
471 break;
472 }
473 if (!MergeFieldFrom(input, extensionRegistry, builder, tag)) {
474 // end group tag
475 break;
476 }
477 }
478 }
479
480 /// <summary>
481 /// Like <see cref="MergeFrom(CodedInputStream, ExtensionRegistry, IBuilder)" />
482 /// but parses a single field.
483 /// </summary>
484 /// <param name="input">The input to read the field from</param>
485 /// <param name="extensionRegistry">Registry to use when an extension field is encountered</param>
486 /// <param name="builder">Builder to merge field into, if it's a known field</param>
487 /// <param name="tag">The tag, which should already have been read from the input</param>
488 /// <returns>true unless the tag is an end-group tag</returns>
489 internal bool MergeFieldFrom(CodedInputStream input,
490 ExtensionRegistry extensionRegistry, IBuilder builder, uint tag) {
491
Jon Skeet68036862008-10-22 13:30:34 +0100492 MessageDescriptor type = builder.DescriptorForType;
493 if (type.Options.MessageSetWireFormat && tag == WireFormat.MessageSetTag.ItemStart) {
494 MergeMessageSetExtensionFromCodedStream(input, extensionRegistry, builder);
495 return true;
496 }
497
498 WireFormat.WireType wireType = WireFormat.GetTagWireType(tag);
499 int fieldNumber = WireFormat.GetTagFieldNumber(tag);
500
501 FieldDescriptor field;
502 IMessage defaultFieldInstance = null;
503
504 if (type.IsExtensionNumber(fieldNumber)) {
505 ExtensionInfo extension = extensionRegistry[type, fieldNumber];
506 if (extension == null) {
507 field = null;
508 } else {
509 field = extension.Descriptor;
510 defaultFieldInstance = extension.DefaultInstance;
511 }
512 } else {
513 field = type.FindFieldByNumber(fieldNumber);
514 }
515
516 // Unknown field or wrong wire type. Skip.
Jon Skeet25a28582009-02-18 16:06:22 +0000517 if (field == null || wireType != WireFormat.GetWireType(field)) {
Jon Skeet68036862008-10-22 13:30:34 +0100518 return MergeFieldFrom(tag, input);
519 }
520
Jon Skeet25a28582009-02-18 16:06:22 +0000521 if (field.IsPacked) {
522 int length = (int)input.ReadRawVarint32();
523 int limit = input.PushLimit(length);
524 if (field.FieldType == FieldType.Enum) {
525 while (!input.ReachedLimit) {
Jon Skeet68036862008-10-22 13:30:34 +0100526 int rawValue = input.ReadEnum();
Jon Skeet25a28582009-02-18 16:06:22 +0000527 object value = field.EnumType.FindValueByNumber(rawValue);
Jon Skeet68036862008-10-22 13:30:34 +0100528 if (value == null) {
Jon Skeet25a28582009-02-18 16:06:22 +0000529 // If the number isn't recognized as a valid value for this
530 // enum, drop it (don't even add it to unknownFields).
Jon Skeet68036862008-10-22 13:30:34 +0100531 return true;
532 }
Jon Skeet25a28582009-02-18 16:06:22 +0000533 builder.WeakAddRepeatedField(field, value);
Jon Skeet68036862008-10-22 13:30:34 +0100534 }
Jon Skeet25a28582009-02-18 16:06:22 +0000535 } else {
536 while (!input.ReachedLimit) {
537 Object value = input.ReadPrimitiveField(field.FieldType);
538 builder.WeakAddRepeatedField(field, value);
539 }
540 }
541 input.PopLimit(limit);
Jon Skeet68036862008-10-22 13:30:34 +0100542 } else {
Jon Skeet25a28582009-02-18 16:06:22 +0000543 object value;
544 switch (field.FieldType) {
545 case FieldType.Group:
546 case FieldType.Message: {
547 IBuilder subBuilder;
548 if (defaultFieldInstance != null) {
549 subBuilder = defaultFieldInstance.WeakCreateBuilderForType();
550 } else {
551 subBuilder = builder.CreateBuilderForField(field);
552 }
553 if (!field.IsRepeated) {
554 subBuilder.WeakMergeFrom((IMessage)builder[field]);
555 }
556 if (field.FieldType == FieldType.Group) {
557 input.ReadGroup(field.FieldNumber, subBuilder, extensionRegistry);
558 } else {
559 input.ReadMessage(subBuilder, extensionRegistry);
560 }
561 value = subBuilder.WeakBuild();
562 break;
563 }
564 case FieldType.Enum: {
565 int rawValue = input.ReadEnum();
566 value = field.EnumType.FindValueByNumber(rawValue);
567 // If the number isn't recognized as a valid value for this enum,
568 // drop it.
569 if (value == null) {
570 MergeVarintField(fieldNumber, (ulong)rawValue);
571 return true;
572 }
573 break;
574 }
575 default:
576 value = input.ReadPrimitiveField(field.FieldType);
577 break;
578 }
579 if (field.IsRepeated) {
580 builder.WeakAddRepeatedField(field, value);
581 } else {
582 builder[field] = value;
583 }
Jon Skeet68036862008-10-22 13:30:34 +0100584 }
585 return true;
586 }
587
588 /// <summary>
589 /// Called by MergeFieldFrom to parse a MessageSet extension.
590 /// </summary>
591 private void MergeMessageSetExtensionFromCodedStream(CodedInputStream input,
592 ExtensionRegistry extensionRegistry, IBuilder builder) {
593 MessageDescriptor type = builder.DescriptorForType;
594
595 // The wire format for MessageSet is:
596 // message MessageSet {
597 // repeated group Item = 1 {
598 // required int32 typeId = 2;
599 // required bytes message = 3;
600 // }
601 // }
602 // "typeId" is the extension's field number. The extension can only be
603 // a message type, where "message" contains the encoded bytes of that
604 // message.
605 //
606 // In practice, we will probably never see a MessageSet item in which
607 // the message appears before the type ID, or where either field does not
608 // appear exactly once. However, in theory such cases are valid, so we
609 // should be prepared to accept them.
610
611 int typeId = 0;
612 ByteString rawBytes = null; // If we encounter "message" before "typeId"
613 IBuilder subBuilder = null;
614 FieldDescriptor field = null;
615
616 while (true) {
617 uint tag = input.ReadTag();
618 if (tag == 0) {
619 break;
620 }
621
622 if (tag == WireFormat.MessageSetTag.TypeID) {
623 typeId = input.ReadInt32();
624 // Zero is not a valid type ID.
625 if (typeId != 0) {
626 ExtensionInfo extension = extensionRegistry[type, typeId];
627 if (extension != null) {
628 field = extension.Descriptor;
629 subBuilder = extension.DefaultInstance.WeakCreateBuilderForType();
630 IMessage originalMessage = (IMessage)builder[field];
631 if (originalMessage != null) {
632 subBuilder.WeakMergeFrom(originalMessage);
633 }
634 if (rawBytes != null) {
635 // We already encountered the message. Parse it now.
636 // TODO(jonskeet): Check this is okay. It's subtly different from the Java, as it doesn't create an input stream from rawBytes.
637 // In fact, why don't we just call MergeFrom(rawBytes)? And what about the extension registry?
638 subBuilder.WeakMergeFrom(rawBytes.CreateCodedInput());
639 rawBytes = null;
640 }
641 } else {
642 // Unknown extension number. If we already saw data, put it
643 // in rawBytes.
644 if (rawBytes != null) {
645 MergeField(typeId, UnknownField.CreateBuilder().AddLengthDelimited(rawBytes).Build());
646 rawBytes = null;
647 }
648 }
649 }
650 } else if (tag == WireFormat.MessageSetTag.Message) {
651 if (typeId == 0) {
652 // We haven't seen a type ID yet, so we have to store the raw bytes for now.
653 rawBytes = input.ReadBytes();
654 } else if (subBuilder == null) {
655 // We don't know how to parse this. Ignore it.
656 MergeField(typeId, UnknownField.CreateBuilder().AddLengthDelimited(input.ReadBytes()).Build());
657 } else {
658 // We already know the type, so we can parse directly from the input
659 // with no copying. Hooray!
660 input.ReadMessage(subBuilder, extensionRegistry);
661 }
662 } else {
663 // Unknown tag. Skip it.
664 if (!input.SkipField(tag)) {
665 break; // end of group
666 }
667 }
668 }
669
670 input.CheckLastTagWas(WireFormat.MessageSetTag.ItemEnd);
671
672 if (subBuilder != null) {
673 builder[field] = subBuilder.WeakBuild();
674 }
675 }
676 }
677 }
678}