aboutsummaryrefslogblamecommitdiff
path: root/csharp/ProtocolBuffers/UnknownFieldSet.cs
blob: 3d3e38ea953f2b122edb501ecf3dca62012f9274 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16















                                                                           

                                 
                                         
                                         

                                  











                                                                                     
 



























                                                                                                                       
                                                                             

                                                           
                                                     

























                                                                           
                                                   


                                                                 

     















































































                                                                                         


























                                                                     
 



                                     
     




                                                                                          


































































































                                                                                                   

















































































































                                                                                               
       
 
















































                                                                                                              
                                                                                   






































































































































                                                                                                                                                 
     

   
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc.
// http://code.google.com/p/protobuf/
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using System;
using System.Collections.Generic;
using System.IO;
using Google.ProtocolBuffers.Collections;
using Google.ProtocolBuffers.Descriptors;

namespace Google.ProtocolBuffers {
  /// <summary>
  /// Used to keep track of fields which were seen when parsing a protocol message
  /// but whose field numbers or types are unrecognized. This most frequently
  /// occurs when new fields are added to a message type and then messages containing
  /// those fields are read by old software that was built before the new types were
  /// added.
  /// 
  /// Every message contains an UnknownFieldSet.
  /// 
  /// Most users will never need to use this class directly.
  /// </summary>
  public sealed class UnknownFieldSet {

    private static readonly UnknownFieldSet defaultInstance = new UnknownFieldSet(new Dictionary<int, UnknownField>());

    private readonly IDictionary<int, UnknownField> fields;

    private UnknownFieldSet(IDictionary<int, UnknownField> fields) {
      this.fields = fields;
    }

    /// <summary>
    /// Creates a new unknown field set builder.
    /// </summary>
    public static Builder CreateBuilder() {
      return new Builder();
    }

    /// <summary>
    /// Creates a new unknown field set builder 
    /// and initialize it from <paramref name="original"/>.
    /// </summary>
    public static Builder CreateBuilder(UnknownFieldSet original) {
      return new Builder().MergeFrom(original);
    }

    public static UnknownFieldSet DefaultInstance {
      get { return defaultInstance; }
    }

    /// <summary>
    /// Returns a read-only view of the mapping from field numbers to values.
    /// </summary>
    public IDictionary<int, UnknownField> FieldDictionary {
      get { return Dictionaries.AsReadOnly(fields); }
    }

    /// <summary>
    /// Checks whether or not the given field number is present in the set.
    /// </summary>
    public bool HasField(int field) {
      return fields.ContainsKey(field);
    }

    /// <summary>
    /// Fetches a field by number, returning an empty field if not present.
    /// Never returns null.
    /// </summary>
    public UnknownField this[int number] {
      get {
        UnknownField ret;
        if (!fields.TryGetValue(number, out ret)) {
          ret = UnknownField.DefaultInstance;
        }
        return ret;
      }
    }

    /// <summary>
    /// Serializes the set and writes it to <paramref name="output"/>.
    /// </summary>
    public void WriteTo(CodedOutputStream output) {
      foreach (KeyValuePair<int, UnknownField> entry in fields) {
        entry.Value.WriteTo(entry.Key, output);
      }
    }

    /// <summary>
    /// Gets the number of bytes required to encode this set.
    /// </summary>
    public int SerializedSize { 
      get {
        int result = 0;
        foreach (KeyValuePair<int, UnknownField> entry in fields) {
          result += entry.Value.GetSerializedSize(entry.Key);
        }
        return result;
      } 
    }

    /// <summary>
    /// Converts the set to a string in protocol buffer text format. This
    /// is just a trivial wrapper around TextFormat.PrintToString.
    /// </summary>
    public override String ToString() {
      return TextFormat.PrintToString(this);
    }

    /// <summary>
    /// Serializes the message to a ByteString and returns it. This is
    /// just a trivial wrapper around WriteTo(CodedOutputStream).
    /// </summary>
    /// <returns></returns>
    public ByteString ToByteString() {
      ByteString.CodedBuilder codedBuilder = new ByteString.CodedBuilder(SerializedSize);
      WriteTo(codedBuilder.CodedOutput);
      return codedBuilder.Build();
    }

    /// <summary>
    /// Serializes the message to a byte array and returns it. This is
    /// just a trivial wrapper around WriteTo(CodedOutputStream).
    /// </summary>
    /// <returns></returns>
    public byte[] ToByteArray() {
      byte[] data = new byte[SerializedSize];
      CodedOutputStream output = CodedOutputStream.CreateInstance(data);
      WriteTo(output);
      output.CheckNoSpaceLeft();
      return data;
    }

    /// <summary>
    /// Serializes the message and writes it to <paramref name="output"/>. This is
    /// just a trivial wrapper around WriteTo(CodedOutputStream).
    /// </summary>
    /// <param name="output"></param>
    public void WriteTo(Stream output) {
      CodedOutputStream codedOutput = CodedOutputStream.CreateInstance(output);
      WriteTo(codedOutput);
      codedOutput.Flush();
    }

    /// <summary>
    /// Serializes the set and writes it to <paramref name="output"/> using
    /// the MessageSet wire format.
    /// </summary>
    public void WriteAsMessageSetTo(CodedOutputStream output) {
      foreach (KeyValuePair<int, UnknownField> entry in fields) {
        entry.Value.WriteAsMessageSetExtensionTo(entry.Key, output);
      }
    }

    /// <summary>
    /// Gets the number of bytes required to encode this set using the MessageSet
    /// wire format.
    /// </summary>
    public int SerializedSizeAsMessageSet {
      get {
        int result = 0;
        foreach (KeyValuePair<int, UnknownField> entry in fields) {
          result += entry.Value.GetSerializedSizeAsMessageSetExtension(entry.Key);
        }
        return result;
      }
    }

    /// <summary>
    /// Parses an UnknownFieldSet from the given input.
    /// </summary>
    public static UnknownFieldSet ParseFrom(CodedInputStream input) {
      return CreateBuilder().MergeFrom(input).Build();
    }

    /// <summary>
    /// Parses an UnknownFieldSet from the given data.
    /// </summary>
    public static UnknownFieldSet ParseFrom(ByteString data) {
      return CreateBuilder().MergeFrom(data).Build();
    }

    /// <summary>
    /// Parses an UnknownFieldSet from the given data.
    /// </summary>
    public static UnknownFieldSet ParseFrom(byte[] data) {
      return CreateBuilder().MergeFrom(data).Build();
    }

    /// <summary>
    /// Parses an UnknownFieldSet from the given input.
    /// </summary>
    public static UnknownFieldSet ParseFrom(Stream input) {
      return CreateBuilder().MergeFrom(input).Build();
    }

    /// <summary>
    /// Builder for UnknownFieldSets.
    /// </summary>
    public sealed class Builder
    {
      /// <summary>
      /// Mapping from number to field. Note that by using a SortedList we ensure
      /// that the fields will be serialized in ascending order.
      /// </summary>
      private IDictionary<int, UnknownField> fields = new SortedList<int, UnknownField>();

      // Optimization:  We keep around a builder for the last field that was
      // modified so that we can efficiently add to it multiple times in a
      // row (important when parsing an unknown repeated field).
      int lastFieldNumber;
      UnknownField.Builder lastField;

      internal Builder() {
      }

      /// <summary>
      /// Returns a field builder for the specified field number, including any values
      /// which already exist.
      /// </summary>
      private UnknownField.Builder GetFieldBuilder(int number) {
        if (lastField != null) {
          if (number == lastFieldNumber) {
            return lastField;
          }
          // Note: AddField() will reset lastField and lastFieldNumber.
          AddField(lastFieldNumber, lastField.Build());
        }
        if (number == 0) {
          return null;
        }

        lastField = UnknownField.CreateBuilder();
        UnknownField existing;
        if (fields.TryGetValue(number, out existing)) {
          lastField.MergeFrom(existing);
        }
        lastFieldNumber = number;
        return lastField;
      }

      /// <summary>
      /// Build the UnknownFieldSet and return it. Once this method has been called,
      /// this instance will no longer be usable. Calling any method after this
      /// will throw a NullReferenceException.
      /// </summary>
      public UnknownFieldSet Build() {
        GetFieldBuilder(0);  // Force lastField to be built.
        UnknownFieldSet result = fields.Count == 0 ? DefaultInstance : new UnknownFieldSet(fields);
        fields = null;
        return result;
      }

      /// <summary>
      /// Adds a field to the set. If a field with the same number already exists, it
      /// is replaced.
      /// </summary>
      public Builder AddField(int number, UnknownField field) {
        if (number == 0) {
          throw new ArgumentOutOfRangeException("number", "Zero is not a valid field number.");
        }
        if (lastField != null && lastFieldNumber == number) {
          // Discard this.
          lastField = null;
          lastFieldNumber = 0;
        }
        fields[number] = field;
        return this;
      }

      /// <summary>
      /// Resets the builder to an empty set.
      /// </summary>
      public Builder Clear() {
        fields.Clear();
        lastFieldNumber = 0;
        lastField = null;
        return this;
      }
      
      /// <summary>
      /// Parse an entire message from <paramref name="input"/> and merge
      /// its fields into this set.
      /// </summary>
      public Builder MergeFrom(CodedInputStream input) {
        while (true) {
          uint tag = input.ReadTag();
          if (tag == 0 || !MergeFieldFrom(tag, input)) {
            break;
          }
        }
        return this;
      }

        /// <summary>
        /// Parse a single field from <paramref name="input"/> and merge it
        /// into this set.
        /// </summary>
        /// <param name="tag">The field's tag number, which was already parsed.</param>
        /// <param name="input">The coded input stream containing the field</param>
        /// <returns>false if the tag is an "end group" tag, true otherwise</returns>
      public bool MergeFieldFrom(uint tag, CodedInputStream input) {
        int number = WireFormat.GetTagFieldNumber(tag);
        switch (WireFormat.GetTagWireType(tag)) {
          case WireFormat.WireType.Varint:
            GetFieldBuilder(number).AddVarint(input.ReadUInt64());
            return true;
          case WireFormat.WireType.Fixed64:
            GetFieldBuilder(number).AddFixed64(input.ReadFixed64());
            return true;
          case WireFormat.WireType.LengthDelimited:
            GetFieldBuilder(number).AddLengthDelimited(input.ReadBytes());
            return true;
          case WireFormat.WireType.StartGroup: {
            Builder subBuilder = CreateBuilder();
            input.ReadUnknownGroup(number, subBuilder);
            GetFieldBuilder(number).AddGroup(subBuilder.Build());
            return true;
          }
          case WireFormat.WireType.EndGroup:
            return false;
          case WireFormat.WireType.Fixed32:
            GetFieldBuilder(number).AddFixed32(input.ReadFixed32());
            return true;
          default:
            throw InvalidProtocolBufferException.InvalidWireType();
        }
      }

      /// <summary>
      /// Parses <paramref name="input"/> as an UnknownFieldSet and merge it
      /// with the set being built. This is just a small wrapper around
      /// MergeFrom(CodedInputStream).
      /// </summary>
      public Builder MergeFrom(Stream input) {
        CodedInputStream codedInput = CodedInputStream.CreateInstance(input);
        MergeFrom(codedInput);
        codedInput.CheckLastTagWas(0);
        return this;
      }

      /// <summary>
      /// Parses <paramref name="data"/> as an UnknownFieldSet and merge it
      /// with the set being built. This is just a small wrapper around
      /// MergeFrom(CodedInputStream).
      /// </summary>
      public Builder MergeFrom(ByteString data) {
        CodedInputStream input = data.CreateCodedInput();
        MergeFrom(input);
        input.CheckLastTagWas(0);
        return this;
      }

      /// <summary>
      /// Parses <paramref name="data"/> as an UnknownFieldSet and merge it
      /// with the set being built. This is just a small wrapper around
      /// MergeFrom(CodedInputStream).
      /// </summary>
      public Builder MergeFrom(byte[] data) {
        CodedInputStream input = CodedInputStream.CreateInstance(data);
        MergeFrom(input);
        input.CheckLastTagWas(0);
        return this;
      }

      /// <summary>
      /// Convenience method for merging a new field containing a single varint
      /// value.  This is used in particular when an unknown enum value is
      /// encountered.
      /// </summary>
      public Builder MergeVarintField(int number, ulong value) {
        if (number == 0) {
          throw new ArgumentOutOfRangeException("number", "Zero is not a valid field number.");
        }
        GetFieldBuilder(number).AddVarint(value);
        return this;
      }

      /// <summary>
      /// Merges the fields from <paramref name="other"/> into this set.
      /// If a field number exists in both sets, the values in <paramref name="other"/>
      /// will be appended to the values in this set.
      /// </summary>
      public Builder MergeFrom(UnknownFieldSet other) {
        if (other != DefaultInstance) {
          foreach(KeyValuePair<int, UnknownField> entry in other.fields) {
            MergeField(entry.Key, entry.Value);
          }
        }
        return this;
      }

      /// <summary>
      /// Checks if the given field number is present in the set.
      /// </summary>
      public bool HasField(int number) {
        if (number == 0) {
          throw new ArgumentOutOfRangeException("number", "Zero is not a valid field number.");
        }
        return number == lastFieldNumber || fields.ContainsKey(number);
      }

      /// <summary>
      /// Adds a field to the unknown field set. If a field with the same
      /// number already exists, the two are merged.
      /// </summary>
      public Builder MergeField(int number, UnknownField field) {
        if (number == 0) {
          throw new ArgumentOutOfRangeException("number", "Zero is not a valid field number.");
        }
        if (HasField(number)) {
          GetFieldBuilder(number).MergeFrom(field);
        } else {
          // Optimization:  We could call getFieldBuilder(number).mergeFrom(field)
          // in this case, but that would create a copy of the Field object.
          // We'd rather reuse the one passed to us, so call AddField() instead.
          AddField(number, field);
        }
        return this;
      }

      internal void MergeFrom(CodedInputStream input, ExtensionRegistry extensionRegistry, IBuilder builder) {
        while (true) {
          uint tag = input.ReadTag();
          if (tag == 0) {
            break;
          }
          if (!MergeFieldFrom(input, extensionRegistry, builder, tag)) {
            // end group tag
            break;
          }
        }
      }

      /// <summary>
      /// Like <see cref="MergeFrom(CodedInputStream, ExtensionRegistry, IBuilder)" />
      /// but parses a single field.
      /// </summary>
      /// <param name="input">The input to read the field from</param>
      /// <param name="extensionRegistry">Registry to use when an extension field is encountered</param>
      /// <param name="builder">Builder to merge field into, if it's a known field</param>
      /// <param name="tag">The tag, which should already have been read from the input</param>
      /// <returns>true unless the tag is an end-group tag</returns>
      internal bool MergeFieldFrom(CodedInputStream input, 
          ExtensionRegistry extensionRegistry, IBuilder builder, uint tag) {
        MessageDescriptor type = builder.DescriptorForType;
        if (type.Options.MessageSetWireFormat && tag == WireFormat.MessageSetTag.ItemStart) {
          MergeMessageSetExtensionFromCodedStream(input, extensionRegistry, builder);
          return true;
        }

        WireFormat.WireType wireType = WireFormat.GetTagWireType(tag);
        int fieldNumber = WireFormat.GetTagFieldNumber(tag);

        FieldDescriptor field;
        IMessage defaultFieldInstance = null;

        if (type.IsExtensionNumber(fieldNumber)) {
          ExtensionInfo extension = extensionRegistry[type, fieldNumber];
          if (extension == null) {
            field = null;
          } else {
            field = extension.Descriptor;
            defaultFieldInstance = extension.DefaultInstance;
          }
        } else {
          field = type.FindFieldByNumber(fieldNumber);
        }

        // Unknown field or wrong wire type. Skip.
        if (field == null || wireType != WireFormat.GetWireType(field.FieldType)) {
          return MergeFieldFrom(tag, input);
        }

        object value;
        switch (field.FieldType) {
          case FieldType.Group:
          case FieldType.Message: {
              IBuilder subBuilder;
              if (defaultFieldInstance != null) {
                subBuilder = defaultFieldInstance.WeakCreateBuilderForType();
              } else {
                subBuilder = builder.CreateBuilderForField(field);
              }
              if (!field.IsRepeated) {
                subBuilder.WeakMergeFrom((IMessage)builder[field]);
              }
              if (field.FieldType == FieldType.Group) {
                input.ReadGroup(field.FieldNumber, subBuilder, extensionRegistry);
              } else {
                input.ReadMessage(subBuilder, extensionRegistry);
              }
              value = subBuilder.WeakBuild();
              break;
            }
          case FieldType.Enum: {
              int rawValue = input.ReadEnum();
              value = field.EnumType.FindValueByNumber(rawValue);
              // If the number isn't recognized as a valid value for this enum,
              // drop it.
              if (value == null) {
                MergeVarintField(fieldNumber, (ulong)rawValue);
                return true;
              }
              break;
            }
          default:
            value = input.ReadPrimitiveField(field.FieldType);
            break;
        }
        if (field.IsRepeated) {
          builder.WeakAddRepeatedField(field, value);
        } else {
          builder[field] = value;
        }
        return true;
      }

      /// <summary>
      /// Called by MergeFieldFrom to parse a MessageSet extension.
      /// </summary>
      private void MergeMessageSetExtensionFromCodedStream(CodedInputStream input,
          ExtensionRegistry extensionRegistry, IBuilder builder) {
        MessageDescriptor type = builder.DescriptorForType;

        // The wire format for MessageSet is:
        //   message MessageSet {
        //     repeated group Item = 1 {
        //       required int32 typeId = 2;
        //       required bytes message = 3;
        //     }
        //   }
        // "typeId" is the extension's field number.  The extension can only be
        // a message type, where "message" contains the encoded bytes of that
        // message.
        //
        // In practice, we will probably never see a MessageSet item in which
        // the message appears before the type ID, or where either field does not
        // appear exactly once.  However, in theory such cases are valid, so we
        // should be prepared to accept them.

        int typeId = 0;
        ByteString rawBytes = null;  // If we encounter "message" before "typeId"
        IBuilder subBuilder = null;
        FieldDescriptor field = null;

        while (true) {
          uint tag = input.ReadTag();
          if (tag == 0) {
            break;
          }

          if (tag == WireFormat.MessageSetTag.TypeID) {
            typeId = input.ReadInt32();
            // Zero is not a valid type ID.
            if (typeId != 0) {
              ExtensionInfo extension = extensionRegistry[type, typeId];
              if (extension != null) {
                field = extension.Descriptor;
                subBuilder = extension.DefaultInstance.WeakCreateBuilderForType();
                IMessage originalMessage = (IMessage)builder[field];
                if (originalMessage != null) {
                  subBuilder.WeakMergeFrom(originalMessage);
                }
                if (rawBytes != null) {
                  // We already encountered the message.  Parse it now.
                  // TODO(jonskeet): Check this is okay. It's subtly different from the Java, as it doesn't create an input stream from rawBytes.
                  // In fact, why don't we just call MergeFrom(rawBytes)? And what about the extension registry?
                  subBuilder.WeakMergeFrom(rawBytes.CreateCodedInput());
                  rawBytes = null;
                }
              } else {
                // Unknown extension number.  If we already saw data, put it
                // in rawBytes.
                if (rawBytes != null) {
                  MergeField(typeId, UnknownField.CreateBuilder().AddLengthDelimited(rawBytes).Build());
                  rawBytes = null;
                }
              }
            }
          } else if (tag == WireFormat.MessageSetTag.Message) {
            if (typeId == 0) {
              // We haven't seen a type ID yet, so we have to store the raw bytes for now.
              rawBytes = input.ReadBytes();
            } else if (subBuilder == null) {
              // We don't know how to parse this.  Ignore it.
              MergeField(typeId, UnknownField.CreateBuilder().AddLengthDelimited(input.ReadBytes()).Build());
            } else {
              // We already know the type, so we can parse directly from the input
              // with no copying.  Hooray!
              input.ReadMessage(subBuilder, extensionRegistry);
            }
          } else {
            // Unknown tag.  Skip it.
            if (!input.SkipField(tag)) {
              break;  // end of group
            }
          }
        }

        input.CheckLastTagWas(WireFormat.MessageSetTag.ItemEnd);

        if (subBuilder != null) {
          builder[field] = subBuilder.WeakBuild();
        }
      }
    }
  }
}