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