blob: 49c1fa32cea8b09b1823f8640fee46f53ab16d48 [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 Skeetb49d3c72009-11-03 16:51:01 +0000246 /// Mapping from number to field. Note that by using a SortedList we ensure
Jon Skeet68036862008-10-22 13:30:34 +0100247 /// that the fields will be serialized in ascending order.
248 /// </summary>
Jon Skeetb49d3c72009-11-03 16:51:01 +0000249 private IDictionary<int, UnknownField> fields = new SortedList<int, UnknownField>();
Jon Skeet68036862008-10-22 13:30:34 +0100250 // Optimization: We keep around a builder for the last field that was
251 // modified so that we can efficiently add to it multiple times in a
252 // row (important when parsing an unknown repeated field).
Jon Skeet43da7ae2009-05-28 21:45:43 +0100253 private int lastFieldNumber;
254 private UnknownField.Builder lastField;
Jon Skeet68036862008-10-22 13:30:34 +0100255
256 internal Builder() {
257 }
258
259 /// <summary>
260 /// Returns a field builder for the specified field number, including any values
261 /// which already exist.
262 /// </summary>
263 private UnknownField.Builder GetFieldBuilder(int number) {
264 if (lastField != null) {
265 if (number == lastFieldNumber) {
266 return lastField;
267 }
268 // Note: AddField() will reset lastField and lastFieldNumber.
269 AddField(lastFieldNumber, lastField.Build());
270 }
271 if (number == 0) {
272 return null;
273 }
274
275 lastField = UnknownField.CreateBuilder();
276 UnknownField existing;
277 if (fields.TryGetValue(number, out existing)) {
278 lastField.MergeFrom(existing);
279 }
280 lastFieldNumber = number;
281 return lastField;
282 }
283
284 /// <summary>
285 /// Build the UnknownFieldSet and return it. Once this method has been called,
286 /// this instance will no longer be usable. Calling any method after this
287 /// will throw a NullReferenceException.
288 /// </summary>
289 public UnknownFieldSet Build() {
290 GetFieldBuilder(0); // Force lastField to be built.
291 UnknownFieldSet result = fields.Count == 0 ? DefaultInstance : new UnknownFieldSet(fields);
292 fields = null;
293 return result;
294 }
295
296 /// <summary>
297 /// Adds a field to the set. If a field with the same number already exists, it
298 /// is replaced.
299 /// </summary>
300 public Builder AddField(int number, UnknownField field) {
301 if (number == 0) {
302 throw new ArgumentOutOfRangeException("number", "Zero is not a valid field number.");
303 }
304 if (lastField != null && lastFieldNumber == number) {
305 // Discard this.
306 lastField = null;
307 lastFieldNumber = 0;
308 }
309 fields[number] = field;
310 return this;
311 }
312
313 /// <summary>
314 /// Resets the builder to an empty set.
315 /// </summary>
316 public Builder Clear() {
317 fields.Clear();
318 lastFieldNumber = 0;
319 lastField = null;
320 return this;
321 }
322
323 /// <summary>
324 /// Parse an entire message from <paramref name="input"/> and merge
325 /// its fields into this set.
326 /// </summary>
327 public Builder MergeFrom(CodedInputStream input) {
328 while (true) {
329 uint tag = input.ReadTag();
330 if (tag == 0 || !MergeFieldFrom(tag, input)) {
331 break;
332 }
333 }
334 return this;
335 }
336
337 /// <summary>
338 /// Parse a single field from <paramref name="input"/> and merge it
339 /// into this set.
340 /// </summary>
341 /// <param name="tag">The field's tag number, which was already parsed.</param>
342 /// <param name="input">The coded input stream containing the field</param>
343 /// <returns>false if the tag is an "end group" tag, true otherwise</returns>
Jon Skeetd6dd0a42009-06-05 22:00:05 +0100344 [CLSCompliant(false)]
Jon Skeet68036862008-10-22 13:30:34 +0100345 public bool MergeFieldFrom(uint tag, CodedInputStream input) {
346 int number = WireFormat.GetTagFieldNumber(tag);
347 switch (WireFormat.GetTagWireType(tag)) {
348 case WireFormat.WireType.Varint:
349 GetFieldBuilder(number).AddVarint(input.ReadUInt64());
350 return true;
351 case WireFormat.WireType.Fixed64:
352 GetFieldBuilder(number).AddFixed64(input.ReadFixed64());
353 return true;
354 case WireFormat.WireType.LengthDelimited:
355 GetFieldBuilder(number).AddLengthDelimited(input.ReadBytes());
356 return true;
357 case WireFormat.WireType.StartGroup: {
358 Builder subBuilder = CreateBuilder();
359 input.ReadUnknownGroup(number, subBuilder);
360 GetFieldBuilder(number).AddGroup(subBuilder.Build());
361 return true;
362 }
363 case WireFormat.WireType.EndGroup:
364 return false;
365 case WireFormat.WireType.Fixed32:
366 GetFieldBuilder(number).AddFixed32(input.ReadFixed32());
367 return true;
368 default:
369 throw InvalidProtocolBufferException.InvalidWireType();
370 }
371 }
372
373 /// <summary>
374 /// Parses <paramref name="input"/> as an UnknownFieldSet and merge it
375 /// with the set being built. This is just a small wrapper around
376 /// MergeFrom(CodedInputStream).
377 /// </summary>
378 public Builder MergeFrom(Stream input) {
379 CodedInputStream codedInput = CodedInputStream.CreateInstance(input);
380 MergeFrom(codedInput);
381 codedInput.CheckLastTagWas(0);
382 return this;
383 }
384
385 /// <summary>
386 /// Parses <paramref name="data"/> as an UnknownFieldSet and merge it
387 /// with the set being built. This is just a small wrapper around
388 /// MergeFrom(CodedInputStream).
389 /// </summary>
390 public Builder MergeFrom(ByteString data) {
391 CodedInputStream input = data.CreateCodedInput();
392 MergeFrom(input);
393 input.CheckLastTagWas(0);
394 return this;
395 }
396
397 /// <summary>
398 /// Parses <paramref name="data"/> as an UnknownFieldSet and merge it
399 /// with the set being built. This is just a small wrapper around
400 /// MergeFrom(CodedInputStream).
401 /// </summary>
402 public Builder MergeFrom(byte[] data) {
403 CodedInputStream input = CodedInputStream.CreateInstance(data);
404 MergeFrom(input);
405 input.CheckLastTagWas(0);
406 return this;
407 }
408
409 /// <summary>
410 /// Convenience method for merging a new field containing a single varint
411 /// value. This is used in particular when an unknown enum value is
412 /// encountered.
413 /// </summary>
Jon Skeetd6dd0a42009-06-05 22:00:05 +0100414 [CLSCompliant(false)]
Jon Skeet68036862008-10-22 13:30:34 +0100415 public Builder MergeVarintField(int number, ulong value) {
416 if (number == 0) {
417 throw new ArgumentOutOfRangeException("number", "Zero is not a valid field number.");
418 }
419 GetFieldBuilder(number).AddVarint(value);
420 return this;
421 }
422
423 /// <summary>
424 /// Merges the fields from <paramref name="other"/> into this set.
425 /// If a field number exists in both sets, the values in <paramref name="other"/>
426 /// will be appended to the values in this set.
427 /// </summary>
428 public Builder MergeFrom(UnknownFieldSet other) {
429 if (other != DefaultInstance) {
430 foreach(KeyValuePair<int, UnknownField> entry in other.fields) {
431 MergeField(entry.Key, entry.Value);
432 }
433 }
434 return this;
435 }
436
437 /// <summary>
438 /// Checks if the given field number is present in the set.
439 /// </summary>
440 public bool HasField(int number) {
441 if (number == 0) {
442 throw new ArgumentOutOfRangeException("number", "Zero is not a valid field number.");
443 }
444 return number == lastFieldNumber || fields.ContainsKey(number);
445 }
446
447 /// <summary>
448 /// Adds a field to the unknown field set. If a field with the same
449 /// number already exists, the two are merged.
450 /// </summary>
451 public Builder MergeField(int number, UnknownField field) {
452 if (number == 0) {
453 throw new ArgumentOutOfRangeException("number", "Zero is not a valid field number.");
454 }
455 if (HasField(number)) {
456 GetFieldBuilder(number).MergeFrom(field);
457 } else {
458 // Optimization: We could call getFieldBuilder(number).mergeFrom(field)
459 // in this case, but that would create a copy of the Field object.
460 // We'd rather reuse the one passed to us, so call AddField() instead.
461 AddField(number, field);
462 }
463 return this;
464 }
465
466 internal void MergeFrom(CodedInputStream input, ExtensionRegistry extensionRegistry, IBuilder builder) {
467 while (true) {
468 uint tag = input.ReadTag();
469 if (tag == 0) {
470 break;
471 }
472 if (!MergeFieldFrom(input, extensionRegistry, builder, tag)) {
473 // end group tag
474 break;
475 }
476 }
477 }
478
479 /// <summary>
480 /// Like <see cref="MergeFrom(CodedInputStream, ExtensionRegistry, IBuilder)" />
481 /// but parses a single field.
482 /// </summary>
483 /// <param name="input">The input to read the field from</param>
484 /// <param name="extensionRegistry">Registry to use when an extension field is encountered</param>
485 /// <param name="builder">Builder to merge field into, if it's a known field</param>
486 /// <param name="tag">The tag, which should already have been read from the input</param>
487 /// <returns>true unless the tag is an end-group tag</returns>
488 internal bool MergeFieldFrom(CodedInputStream input,
489 ExtensionRegistry extensionRegistry, IBuilder builder, uint tag) {
490
Jon Skeet68036862008-10-22 13:30:34 +0100491 MessageDescriptor type = builder.DescriptorForType;
492 if (type.Options.MessageSetWireFormat && tag == WireFormat.MessageSetTag.ItemStart) {
493 MergeMessageSetExtensionFromCodedStream(input, extensionRegistry, builder);
494 return true;
495 }
496
497 WireFormat.WireType wireType = WireFormat.GetTagWireType(tag);
498 int fieldNumber = WireFormat.GetTagFieldNumber(tag);
499
500 FieldDescriptor field;
501 IMessage defaultFieldInstance = null;
502
503 if (type.IsExtensionNumber(fieldNumber)) {
504 ExtensionInfo extension = extensionRegistry[type, fieldNumber];
505 if (extension == null) {
506 field = null;
507 } else {
508 field = extension.Descriptor;
509 defaultFieldInstance = extension.DefaultInstance;
510 }
511 } else {
512 field = type.FindFieldByNumber(fieldNumber);
513 }
514
515 // Unknown field or wrong wire type. Skip.
Jon Skeet25a28582009-02-18 16:06:22 +0000516 if (field == null || wireType != WireFormat.GetWireType(field)) {
Jon Skeet68036862008-10-22 13:30:34 +0100517 return MergeFieldFrom(tag, input);
518 }
519
Jon Skeet25a28582009-02-18 16:06:22 +0000520 if (field.IsPacked) {
521 int length = (int)input.ReadRawVarint32();
522 int limit = input.PushLimit(length);
523 if (field.FieldType == FieldType.Enum) {
524 while (!input.ReachedLimit) {
Jon Skeet68036862008-10-22 13:30:34 +0100525 int rawValue = input.ReadEnum();
Jon Skeet25a28582009-02-18 16:06:22 +0000526 object value = field.EnumType.FindValueByNumber(rawValue);
Jon Skeet68036862008-10-22 13:30:34 +0100527 if (value == null) {
Jon Skeet25a28582009-02-18 16:06:22 +0000528 // If the number isn't recognized as a valid value for this
529 // enum, drop it (don't even add it to unknownFields).
Jon Skeet68036862008-10-22 13:30:34 +0100530 return true;
531 }
Jon Skeet25a28582009-02-18 16:06:22 +0000532 builder.WeakAddRepeatedField(field, value);
Jon Skeet68036862008-10-22 13:30:34 +0100533 }
Jon Skeet25a28582009-02-18 16:06:22 +0000534 } else {
535 while (!input.ReachedLimit) {
536 Object value = input.ReadPrimitiveField(field.FieldType);
537 builder.WeakAddRepeatedField(field, value);
538 }
539 }
540 input.PopLimit(limit);
Jon Skeet68036862008-10-22 13:30:34 +0100541 } else {
Jon Skeet25a28582009-02-18 16:06:22 +0000542 object value;
543 switch (field.FieldType) {
544 case FieldType.Group:
545 case FieldType.Message: {
546 IBuilder subBuilder;
547 if (defaultFieldInstance != null) {
548 subBuilder = defaultFieldInstance.WeakCreateBuilderForType();
549 } else {
550 subBuilder = builder.CreateBuilderForField(field);
551 }
552 if (!field.IsRepeated) {
553 subBuilder.WeakMergeFrom((IMessage)builder[field]);
554 }
555 if (field.FieldType == FieldType.Group) {
556 input.ReadGroup(field.FieldNumber, subBuilder, extensionRegistry);
557 } else {
558 input.ReadMessage(subBuilder, extensionRegistry);
559 }
560 value = subBuilder.WeakBuild();
561 break;
562 }
563 case FieldType.Enum: {
564 int rawValue = input.ReadEnum();
565 value = field.EnumType.FindValueByNumber(rawValue);
566 // If the number isn't recognized as a valid value for this enum,
567 // drop it.
568 if (value == null) {
569 MergeVarintField(fieldNumber, (ulong)rawValue);
570 return true;
571 }
572 break;
573 }
574 default:
575 value = input.ReadPrimitiveField(field.FieldType);
576 break;
577 }
578 if (field.IsRepeated) {
579 builder.WeakAddRepeatedField(field, value);
580 } else {
581 builder[field] = value;
582 }
Jon Skeet68036862008-10-22 13:30:34 +0100583 }
584 return true;
585 }
586
587 /// <summary>
588 /// Called by MergeFieldFrom to parse a MessageSet extension.
589 /// </summary>
590 private void MergeMessageSetExtensionFromCodedStream(CodedInputStream input,
591 ExtensionRegistry extensionRegistry, IBuilder builder) {
592 MessageDescriptor type = builder.DescriptorForType;
593
594 // The wire format for MessageSet is:
595 // message MessageSet {
596 // repeated group Item = 1 {
597 // required int32 typeId = 2;
598 // required bytes message = 3;
599 // }
600 // }
601 // "typeId" is the extension's field number. The extension can only be
602 // a message type, where "message" contains the encoded bytes of that
603 // message.
604 //
605 // In practice, we will probably never see a MessageSet item in which
606 // the message appears before the type ID, or where either field does not
607 // appear exactly once. However, in theory such cases are valid, so we
608 // should be prepared to accept them.
609
610 int typeId = 0;
611 ByteString rawBytes = null; // If we encounter "message" before "typeId"
612 IBuilder subBuilder = null;
613 FieldDescriptor field = null;
614
615 while (true) {
616 uint tag = input.ReadTag();
617 if (tag == 0) {
618 break;
619 }
620
621 if (tag == WireFormat.MessageSetTag.TypeID) {
622 typeId = input.ReadInt32();
623 // Zero is not a valid type ID.
624 if (typeId != 0) {
625 ExtensionInfo extension = extensionRegistry[type, typeId];
626 if (extension != null) {
627 field = extension.Descriptor;
628 subBuilder = extension.DefaultInstance.WeakCreateBuilderForType();
629 IMessage originalMessage = (IMessage)builder[field];
630 if (originalMessage != null) {
631 subBuilder.WeakMergeFrom(originalMessage);
632 }
633 if (rawBytes != null) {
634 // We already encountered the message. Parse it now.
635 // TODO(jonskeet): Check this is okay. It's subtly different from the Java, as it doesn't create an input stream from rawBytes.
636 // In fact, why don't we just call MergeFrom(rawBytes)? And what about the extension registry?
637 subBuilder.WeakMergeFrom(rawBytes.CreateCodedInput());
638 rawBytes = null;
639 }
640 } else {
641 // Unknown extension number. If we already saw data, put it
642 // in rawBytes.
643 if (rawBytes != null) {
644 MergeField(typeId, UnknownField.CreateBuilder().AddLengthDelimited(rawBytes).Build());
645 rawBytes = null;
646 }
647 }
648 }
649 } else if (tag == WireFormat.MessageSetTag.Message) {
650 if (typeId == 0) {
651 // We haven't seen a type ID yet, so we have to store the raw bytes for now.
652 rawBytes = input.ReadBytes();
653 } else if (subBuilder == null) {
654 // We don't know how to parse this. Ignore it.
655 MergeField(typeId, UnknownField.CreateBuilder().AddLengthDelimited(input.ReadBytes()).Build());
656 } else {
657 // We already know the type, so we can parse directly from the input
658 // with no copying. Hooray!
659 input.ReadMessage(subBuilder, extensionRegistry);
660 }
661 } else {
662 // Unknown tag. Skip it.
663 if (!input.SkipField(tag)) {
664 break; // end of group
665 }
666 }
667 }
668
669 input.CheckLastTagWas(WireFormat.MessageSetTag.ItemEnd);
670
671 if (subBuilder != null) {
672 builder[field] = subBuilder.WeakBuild();
673 }
674 }
675 }
676 }
677}