blob: 18eacbe81dfad9a8d815e8e5f82b091f31cd55e7 [file] [log] [blame]
Jon Skeet68036862008-10-22 13:30:34 +01001// Protocol Buffers - Google's data interchange format
2// Copyright 2008 Google Inc.
3// http://code.google.com/p/protobuf/
4//
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16using System;
17using System.Collections.Generic;
18using System.IO;
19using Google.ProtocolBuffers.Collections;
20using Google.ProtocolBuffers.Descriptors;
21using Google.ProtocolBuffers.DescriptorProtos;
22
23namespace Google.ProtocolBuffers {
24 /// <summary>
25 /// Used to keep track of fields which were seen when parsing a protocol message
26 /// but whose field numbers or types are unrecognized. This most frequently
27 /// occurs when new fields are added to a message type and then messages containing
28 /// those fields are read by old software that was built before the new types were
29 /// added.
30 ///
31 /// Every message contains an UnknownFieldSet.
32 ///
33 /// Most users will never need to use this class directly.
34 /// </summary>
35 public sealed class UnknownFieldSet {
36
37 private static readonly UnknownFieldSet defaultInstance = new UnknownFieldSet(new Dictionary<int, UnknownField>());
38
39 private readonly IDictionary<int, UnknownField> fields;
40
41 private UnknownFieldSet(IDictionary<int, UnknownField> fields) {
42 this.fields = fields;
43 }
44
45 /// <summary>
46 /// Creates a new unknown field set builder.
47 /// </summary>
48 public static Builder CreateBuilder() {
49 return new Builder();
50 }
51
52 /// <summary>
53 /// Creates a new unknown field set builder
54 /// and initialize it from <paramref name="original"/>.
55 /// </summary>
56 public static Builder CreateBuilder(UnknownFieldSet original) {
57 return new Builder().MergeFrom(original);
58 }
59
60 public static UnknownFieldSet DefaultInstance {
61 get { return defaultInstance; }
62 }
63
64 /// <summary>
65 /// Returns a read-only view of the mapping from field numbers to values.
66 /// </summary>
67 public IDictionary<int, UnknownField> FieldDictionary {
68 get { return Dictionaries.AsReadOnly(fields); }
69 }
70
71 /// <summary>
72 /// Checks whether or not the given field number is present in the set.
73 /// </summary>
74 public bool HasField(int field) {
75 return fields.ContainsKey(field);
76 }
77
78 /// <summary>
79 /// Fetches a field by number, returning an empty field if not present.
80 /// Never returns null.
81 /// </summary>
82 public UnknownField this[int number] {
83 get {
84 UnknownField ret;
85 if (!fields.TryGetValue(number, out ret)) {
86 ret = UnknownField.DefaultInstance;
87 }
88 return ret;
89 }
90 }
91
92 /// <summary>
93 /// Serializes the set and writes it to <paramref name="output"/>.
94 /// </summary>
95 public void WriteTo(CodedOutputStream output) {
96 foreach (KeyValuePair<int, UnknownField> entry in fields) {
97 entry.Value.WriteTo(entry.Key, output);
98 }
99 }
100
101 /// <summary>
102 /// Gets the number of bytes required to encode this set.
103 /// </summary>
104 public int SerializedSize {
105 get {
106 int result = 0;
107 foreach (KeyValuePair<int, UnknownField> entry in fields) {
108 result += entry.Value.GetSerializedSize(entry.Key);
109 }
110 return result;
111 }
112 }
113
114 /// <summary>
115 /// Converts the set to a string in protocol buffer text format. This
116 /// is just a trivial wrapper around TextFormat.PrintToString.
117 /// </summary>
118 public override String ToString() {
119 return TextFormat.PrintToString(this);
120 }
121
122 /// <summary>
123 /// Serializes the message to a ByteString and returns it. This is
124 /// just a trivial wrapper around WriteTo(CodedOutputStream).
125 /// </summary>
126 /// <returns></returns>
127 public ByteString ToByteString() {
128 ByteString.CodedBuilder codedBuilder = new ByteString.CodedBuilder(SerializedSize);
129 WriteTo(codedBuilder.CodedOutput);
130 return codedBuilder.Build();
131 }
132
133 /// <summary>
134 /// Serializes the message to a byte array and returns it. This is
135 /// just a trivial wrapper around WriteTo(CodedOutputStream).
136 /// </summary>
137 /// <returns></returns>
138 public byte[] ToByteArray() {
139 byte[] data = new byte[SerializedSize];
140 CodedOutputStream output = CodedOutputStream.CreateInstance(data);
141 WriteTo(output);
142 output.CheckNoSpaceLeft();
143 return data;
144 }
145
146 /// <summary>
147 /// Serializes the message and writes it to <paramref name="output"/>. This is
148 /// just a trivial wrapper around WriteTo(CodedOutputStream).
149 /// </summary>
150 /// <param name="output"></param>
151 public void WriteTo(Stream output) {
152 CodedOutputStream codedOutput = CodedOutputStream.CreateInstance(output);
153 WriteTo(codedOutput);
154 codedOutput.Flush();
155 }
156
157 /// <summary>
158 /// Serializes the set and writes it to <paramref name="output"/> using
159 /// the MessageSet wire format.
160 /// </summary>
161 public void WriteAsMessageSetTo(CodedOutputStream output) {
162 foreach (KeyValuePair<int, UnknownField> entry in fields) {
163 entry.Value.WriteAsMessageSetExtensionTo(entry.Key, output);
164 }
165 }
166
167 /// <summary>
168 /// Gets the number of bytes required to encode this set using the MessageSet
169 /// wire format.
170 /// </summary>
171 public int SerializedSizeAsMessageSet {
172 get {
173 int result = 0;
174 foreach (KeyValuePair<int, UnknownField> entry in fields) {
175 result += entry.Value.GetSerializedSizeAsMessageSetExtension(entry.Key);
176 }
177 return result;
178 }
179 }
180
181 /// <summary>
182 /// Parses an UnknownFieldSet from the given input.
183 /// </summary>
184 public static UnknownFieldSet ParseFrom(CodedInputStream input) {
185 return CreateBuilder().MergeFrom(input).Build();
186 }
187
188 /// <summary>
189 /// Parses an UnknownFieldSet from the given data.
190 /// </summary>
191 public static UnknownFieldSet ParseFrom(ByteString data) {
192 return CreateBuilder().MergeFrom(data).Build();
193 }
194
195 /// <summary>
196 /// Parses an UnknownFieldSet from the given data.
197 /// </summary>
198 public static UnknownFieldSet ParseFrom(byte[] data) {
199 return CreateBuilder().MergeFrom(data).Build();
200 }
201
202 /// <summary>
203 /// Parses an UnknownFieldSet from the given input.
204 /// </summary>
205 public static UnknownFieldSet ParseFrom(Stream input) {
206 return CreateBuilder().MergeFrom(input).Build();
207 }
208
209 /// <summary>
210 /// Builder for UnknownFieldSets.
211 /// </summary>
212 public sealed class Builder
213 {
214 /// <summary>
215 /// Mapping from number to field. Note that by using a SortedList we ensure
216 /// that the fields will be serialized in ascending order.
217 /// </summary>
218 private IDictionary<int, UnknownField> fields = new SortedList<int, UnknownField>();
219
220 // Optimization: We keep around a builder for the last field that was
221 // modified so that we can efficiently add to it multiple times in a
222 // row (important when parsing an unknown repeated field).
223 int lastFieldNumber;
224 UnknownField.Builder lastField;
225
226 internal Builder() {
227 }
228
229 /// <summary>
230 /// Returns a field builder for the specified field number, including any values
231 /// which already exist.
232 /// </summary>
233 private UnknownField.Builder GetFieldBuilder(int number) {
234 if (lastField != null) {
235 if (number == lastFieldNumber) {
236 return lastField;
237 }
238 // Note: AddField() will reset lastField and lastFieldNumber.
239 AddField(lastFieldNumber, lastField.Build());
240 }
241 if (number == 0) {
242 return null;
243 }
244
245 lastField = UnknownField.CreateBuilder();
246 UnknownField existing;
247 if (fields.TryGetValue(number, out existing)) {
248 lastField.MergeFrom(existing);
249 }
250 lastFieldNumber = number;
251 return lastField;
252 }
253
254 /// <summary>
255 /// Build the UnknownFieldSet and return it. Once this method has been called,
256 /// this instance will no longer be usable. Calling any method after this
257 /// will throw a NullReferenceException.
258 /// </summary>
259 public UnknownFieldSet Build() {
260 GetFieldBuilder(0); // Force lastField to be built.
261 UnknownFieldSet result = fields.Count == 0 ? DefaultInstance : new UnknownFieldSet(fields);
262 fields = null;
263 return result;
264 }
265
266 /// <summary>
267 /// Adds a field to the set. If a field with the same number already exists, it
268 /// is replaced.
269 /// </summary>
270 public Builder AddField(int number, UnknownField field) {
271 if (number == 0) {
272 throw new ArgumentOutOfRangeException("number", "Zero is not a valid field number.");
273 }
274 if (lastField != null && lastFieldNumber == number) {
275 // Discard this.
276 lastField = null;
277 lastFieldNumber = 0;
278 }
279 fields[number] = field;
280 return this;
281 }
282
283 /// <summary>
284 /// Resets the builder to an empty set.
285 /// </summary>
286 public Builder Clear() {
287 fields.Clear();
288 lastFieldNumber = 0;
289 lastField = null;
290 return this;
291 }
292
293 /// <summary>
294 /// Parse an entire message from <paramref name="input"/> and merge
295 /// its fields into this set.
296 /// </summary>
297 public Builder MergeFrom(CodedInputStream input) {
298 while (true) {
299 uint tag = input.ReadTag();
300 if (tag == 0 || !MergeFieldFrom(tag, input)) {
301 break;
302 }
303 }
304 return this;
305 }
306
307 /// <summary>
308 /// Parse a single field from <paramref name="input"/> and merge it
309 /// into this set.
310 /// </summary>
311 /// <param name="tag">The field's tag number, which was already parsed.</param>
312 /// <param name="input">The coded input stream containing the field</param>
313 /// <returns>false if the tag is an "end group" tag, true otherwise</returns>
314 public bool MergeFieldFrom(uint tag, CodedInputStream input) {
315 int number = WireFormat.GetTagFieldNumber(tag);
316 switch (WireFormat.GetTagWireType(tag)) {
317 case WireFormat.WireType.Varint:
318 GetFieldBuilder(number).AddVarint(input.ReadUInt64());
319 return true;
320 case WireFormat.WireType.Fixed64:
321 GetFieldBuilder(number).AddFixed64(input.ReadFixed64());
322 return true;
323 case WireFormat.WireType.LengthDelimited:
324 GetFieldBuilder(number).AddLengthDelimited(input.ReadBytes());
325 return true;
326 case WireFormat.WireType.StartGroup: {
327 Builder subBuilder = CreateBuilder();
328 input.ReadUnknownGroup(number, subBuilder);
329 GetFieldBuilder(number).AddGroup(subBuilder.Build());
330 return true;
331 }
332 case WireFormat.WireType.EndGroup:
333 return false;
334 case WireFormat.WireType.Fixed32:
335 GetFieldBuilder(number).AddFixed32(input.ReadFixed32());
336 return true;
337 default:
338 throw InvalidProtocolBufferException.InvalidWireType();
339 }
340 }
341
342 /// <summary>
343 /// Parses <paramref name="input"/> as an UnknownFieldSet and merge it
344 /// with the set being built. This is just a small wrapper around
345 /// MergeFrom(CodedInputStream).
346 /// </summary>
347 public Builder MergeFrom(Stream input) {
348 CodedInputStream codedInput = CodedInputStream.CreateInstance(input);
349 MergeFrom(codedInput);
350 codedInput.CheckLastTagWas(0);
351 return this;
352 }
353
354 /// <summary>
355 /// Parses <paramref name="data"/> as an UnknownFieldSet and merge it
356 /// with the set being built. This is just a small wrapper around
357 /// MergeFrom(CodedInputStream).
358 /// </summary>
359 public Builder MergeFrom(ByteString data) {
360 CodedInputStream input = data.CreateCodedInput();
361 MergeFrom(input);
362 input.CheckLastTagWas(0);
363 return this;
364 }
365
366 /// <summary>
367 /// Parses <paramref name="data"/> as an UnknownFieldSet and merge it
368 /// with the set being built. This is just a small wrapper around
369 /// MergeFrom(CodedInputStream).
370 /// </summary>
371 public Builder MergeFrom(byte[] data) {
372 CodedInputStream input = CodedInputStream.CreateInstance(data);
373 MergeFrom(input);
374 input.CheckLastTagWas(0);
375 return this;
376 }
377
378 /// <summary>
379 /// Convenience method for merging a new field containing a single varint
380 /// value. This is used in particular when an unknown enum value is
381 /// encountered.
382 /// </summary>
383 public Builder MergeVarintField(int number, ulong value) {
384 if (number == 0) {
385 throw new ArgumentOutOfRangeException("number", "Zero is not a valid field number.");
386 }
387 GetFieldBuilder(number).AddVarint(value);
388 return this;
389 }
390
391 /// <summary>
392 /// Merges the fields from <paramref name="other"/> into this set.
393 /// If a field number exists in both sets, the values in <paramref name="other"/>
394 /// will be appended to the values in this set.
395 /// </summary>
396 public Builder MergeFrom(UnknownFieldSet other) {
397 if (other != DefaultInstance) {
398 foreach(KeyValuePair<int, UnknownField> entry in other.fields) {
399 MergeField(entry.Key, entry.Value);
400 }
401 }
402 return this;
403 }
404
405 /// <summary>
406 /// Checks if the given field number is present in the set.
407 /// </summary>
408 public bool HasField(int number) {
409 if (number == 0) {
410 throw new ArgumentOutOfRangeException("number", "Zero is not a valid field number.");
411 }
412 return number == lastFieldNumber || fields.ContainsKey(number);
413 }
414
415 /// <summary>
416 /// Adds a field to the unknown field set. If a field with the same
417 /// number already exists, the two are merged.
418 /// </summary>
419 public Builder MergeField(int number, UnknownField field) {
420 if (number == 0) {
421 throw new ArgumentOutOfRangeException("number", "Zero is not a valid field number.");
422 }
423 if (HasField(number)) {
424 GetFieldBuilder(number).MergeFrom(field);
425 } else {
426 // Optimization: We could call getFieldBuilder(number).mergeFrom(field)
427 // in this case, but that would create a copy of the Field object.
428 // We'd rather reuse the one passed to us, so call AddField() instead.
429 AddField(number, field);
430 }
431 return this;
432 }
433
434 internal void MergeFrom(CodedInputStream input, ExtensionRegistry extensionRegistry, IBuilder builder) {
435 while (true) {
436 uint tag = input.ReadTag();
437 if (tag == 0) {
438 break;
439 }
440 if (!MergeFieldFrom(input, extensionRegistry, builder, tag)) {
441 // end group tag
442 break;
443 }
444 }
445 }
446
447 /// <summary>
448 /// Like <see cref="MergeFrom(CodedInputStream, ExtensionRegistry, IBuilder)" />
449 /// but parses a single field.
450 /// </summary>
451 /// <param name="input">The input to read the field from</param>
452 /// <param name="extensionRegistry">Registry to use when an extension field is encountered</param>
453 /// <param name="builder">Builder to merge field into, if it's a known field</param>
454 /// <param name="tag">The tag, which should already have been read from the input</param>
455 /// <returns>true unless the tag is an end-group tag</returns>
456 internal bool MergeFieldFrom(CodedInputStream input,
457 ExtensionRegistry extensionRegistry, IBuilder builder, uint tag) {
458
459 if (DescriptorProtoFile.Bootstrapping) {
460 return MergeFieldFrom(tag, input);
461 }
462
463 MessageDescriptor type = builder.DescriptorForType;
464 if (type.Options.MessageSetWireFormat && tag == WireFormat.MessageSetTag.ItemStart) {
465 MergeMessageSetExtensionFromCodedStream(input, extensionRegistry, builder);
466 return true;
467 }
468
469 WireFormat.WireType wireType = WireFormat.GetTagWireType(tag);
470 int fieldNumber = WireFormat.GetTagFieldNumber(tag);
471
472 FieldDescriptor field;
473 IMessage defaultFieldInstance = null;
474
475 if (type.IsExtensionNumber(fieldNumber)) {
476 ExtensionInfo extension = extensionRegistry[type, fieldNumber];
477 if (extension == null) {
478 field = null;
479 } else {
480 field = extension.Descriptor;
481 defaultFieldInstance = extension.DefaultInstance;
482 }
483 } else {
484 field = type.FindFieldByNumber(fieldNumber);
485 }
486
487 // Unknown field or wrong wire type. Skip.
488 if (field == null || wireType != WireFormat.GetWireType(field.FieldType)) {
489 return MergeFieldFrom(tag, input);
490 }
491
492 object value;
493 switch (field.FieldType) {
494 case FieldType.Group:
495 case FieldType.Message: {
496 IBuilder subBuilder;
497 if (defaultFieldInstance != null) {
498 subBuilder = defaultFieldInstance.WeakCreateBuilderForType();
499 } else {
500 subBuilder = builder.CreateBuilderForField(field);
501 }
502 if (!field.IsRepeated) {
503 subBuilder.WeakMergeFrom((IMessage)builder[field]);
504 }
505 if (field.FieldType == FieldType.Group) {
506 input.ReadGroup(field.FieldNumber, subBuilder, extensionRegistry);
507 } else {
508 input.ReadMessage(subBuilder, extensionRegistry);
509 }
510 value = subBuilder.WeakBuild();
511 break;
512 }
513 case FieldType.Enum: {
514 int rawValue = input.ReadEnum();
515 value = field.EnumType.FindValueByNumber(rawValue);
516 // If the number isn't recognized as a valid value for this enum,
517 // drop it.
518 if (value == null) {
519 MergeVarintField(fieldNumber, (ulong)rawValue);
520 return true;
521 }
522 break;
523 }
524 default:
525 value = input.ReadPrimitiveField(field.FieldType);
526 break;
527 }
528 if (field.IsRepeated) {
529 builder.WeakAddRepeatedField(field, value);
530 } else {
531 builder[field] = value;
532 }
533 return true;
534 }
535
536 /// <summary>
537 /// Called by MergeFieldFrom to parse a MessageSet extension.
538 /// </summary>
539 private void MergeMessageSetExtensionFromCodedStream(CodedInputStream input,
540 ExtensionRegistry extensionRegistry, IBuilder builder) {
541 MessageDescriptor type = builder.DescriptorForType;
542
543 // The wire format for MessageSet is:
544 // message MessageSet {
545 // repeated group Item = 1 {
546 // required int32 typeId = 2;
547 // required bytes message = 3;
548 // }
549 // }
550 // "typeId" is the extension's field number. The extension can only be
551 // a message type, where "message" contains the encoded bytes of that
552 // message.
553 //
554 // In practice, we will probably never see a MessageSet item in which
555 // the message appears before the type ID, or where either field does not
556 // appear exactly once. However, in theory such cases are valid, so we
557 // should be prepared to accept them.
558
559 int typeId = 0;
560 ByteString rawBytes = null; // If we encounter "message" before "typeId"
561 IBuilder subBuilder = null;
562 FieldDescriptor field = null;
563
564 while (true) {
565 uint tag = input.ReadTag();
566 if (tag == 0) {
567 break;
568 }
569
570 if (tag == WireFormat.MessageSetTag.TypeID) {
571 typeId = input.ReadInt32();
572 // Zero is not a valid type ID.
573 if (typeId != 0) {
574 ExtensionInfo extension = extensionRegistry[type, typeId];
575 if (extension != null) {
576 field = extension.Descriptor;
577 subBuilder = extension.DefaultInstance.WeakCreateBuilderForType();
578 IMessage originalMessage = (IMessage)builder[field];
579 if (originalMessage != null) {
580 subBuilder.WeakMergeFrom(originalMessage);
581 }
582 if (rawBytes != null) {
583 // We already encountered the message. Parse it now.
584 // TODO(jonskeet): Check this is okay. It's subtly different from the Java, as it doesn't create an input stream from rawBytes.
585 // In fact, why don't we just call MergeFrom(rawBytes)? And what about the extension registry?
586 subBuilder.WeakMergeFrom(rawBytes.CreateCodedInput());
587 rawBytes = null;
588 }
589 } else {
590 // Unknown extension number. If we already saw data, put it
591 // in rawBytes.
592 if (rawBytes != null) {
593 MergeField(typeId, UnknownField.CreateBuilder().AddLengthDelimited(rawBytes).Build());
594 rawBytes = null;
595 }
596 }
597 }
598 } else if (tag == WireFormat.MessageSetTag.Message) {
599 if (typeId == 0) {
600 // We haven't seen a type ID yet, so we have to store the raw bytes for now.
601 rawBytes = input.ReadBytes();
602 } else if (subBuilder == null) {
603 // We don't know how to parse this. Ignore it.
604 MergeField(typeId, UnknownField.CreateBuilder().AddLengthDelimited(input.ReadBytes()).Build());
605 } else {
606 // We already know the type, so we can parse directly from the input
607 // with no copying. Hooray!
608 input.ReadMessage(subBuilder, extensionRegistry);
609 }
610 } else {
611 // Unknown tag. Skip it.
612 if (!input.SkipField(tag)) {
613 break; // end of group
614 }
615 }
616 }
617
618 input.CheckLastTagWas(WireFormat.MessageSetTag.ItemEnd);
619
620 if (subBuilder != null) {
621 builder[field] = subBuilder.WeakBuild();
622 }
623 }
624 }
625 }
626}