Bladeren bron

Some work on AbstractBuilder, and complete implementation of UnknownField and UnknownFieldSet

Jon Skeet 17 jaren geleden
bovenliggende
commit
621bb698e5

+ 195 - 0
csharp/ProtocolBuffers/AbstractBuilder.cs

@@ -0,0 +1,195 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Google.ProtocolBuffers.Descriptors;
+using System.Collections;
+using System.IO;
+
+namespace Google.ProtocolBuffers {
+  /// <summary>
+  /// Implementation of the non-generic IMessage interface as far as possible.
+  /// </summary>
+  public abstract class AbstractBuilder : IBuilder {
+
+    public bool Initialized {
+      get { throw new NotImplementedException(); }
+    }
+
+    public IDictionary<FieldDescriptor, object> AllFields {
+      get { throw new NotImplementedException(); }
+    }
+
+    public object this[FieldDescriptor field] {
+      get {
+        throw new NotImplementedException();
+      }
+      set {
+        throw new NotImplementedException();
+      }
+    }
+
+    public MessageDescriptor DescriptorForType {
+      get { throw new NotImplementedException(); }
+    }
+
+    public int GetRepeatedFieldCount(FieldDescriptor field) {
+      throw new NotImplementedException();
+    }
+
+    public object this[FieldDescriptor field, int index] {
+      get {
+        throw new NotImplementedException();
+      }
+      set {
+        throw new NotImplementedException();
+      }
+    }
+
+    public bool HasField(FieldDescriptor field) {
+      throw new NotImplementedException();
+    }
+
+    public IBuilder Clear() {
+      foreach(FieldDescriptor field in AllFields.Keys) {
+        ClearField(field);
+      }
+      return this;
+    }
+
+    public IBuilder MergeFrom(IMessage other) {
+      if (other.DescriptorForType != DescriptorForType) {
+        throw new ArgumentException("MergeFrom(Message) can only merge messages of the same type.");
+      }
+
+      // Note:  We don't attempt to verify that other's fields have valid
+      //   types.  Doing so would be a losing battle.  We'd have to verify
+      //   all sub-messages as well, and we'd have to make copies of all of
+      //   them to insure that they don't change after verification (since
+      //   the Message interface itself cannot enforce immutability of
+      //   implementations).
+      // TODO(jonskeet):  Provide a function somewhere called makeDeepCopy()
+      //   which allows people to make secure deep copies of messages.
+      foreach (KeyValuePair<FieldDescriptor, object> entry in AllFields) {
+        FieldDescriptor field = entry.Key;
+        if (field.IsRepeated) {
+          // Concatenate repeated fields
+          foreach (object element in (IEnumerable) entry.Value) {
+            AddRepeatedField(field, element);
+          }
+        } else if (field.MappedType == MappedType.Message) {
+          // Merge singular messages
+          IMessage existingValue = (IMessage) this[field];
+          if (existingValue == existingValue.DefaultInstanceForType) {
+            this[field] = entry.Value;
+          } else {
+            this[field] = existingValue.CreateBuilderForType()
+                                       .MergeFrom(existingValue)
+                                       .MergeFrom((IMessage) entry.Value)
+                                       .Build();
+          }
+        } else {
+          // Overwrite simple values
+          this[field] = entry.Value;
+        }
+      }
+      return this;
+    }
+
+    public IMessage Build() {
+      throw new NotImplementedException();
+    }
+
+    public IMessage BuildPartial() {
+      throw new NotImplementedException();
+    }
+
+    public IBuilder Clone() {
+      throw new NotImplementedException();
+    }
+
+    public IBuilder MergeFrom(CodedInputStream input) {
+      return MergeFrom(input, ExtensionRegistry.Empty);
+    }
+
+    public IBuilder MergeFrom(CodedInputStream input, ExtensionRegistry extensionRegistry) {
+      UnknownFieldSet.Builder unknownFields = UnknownFieldSet.CreateBuilder(UnknownFields);
+      FieldSet.MergeFrom(input, unknownFields, extensionRegistry, this);
+      UnknownFields = unknownFields.Build();
+      return this;
+    }
+
+    public IMessage DefaultInstanceForType {
+      get { throw new NotImplementedException(); }
+    }
+
+    public IBuilder NewBuilderForField<TField>(FieldDescriptor field) {
+      throw new NotImplementedException();
+    }
+
+    public IBuilder ClearField(FieldDescriptor field) {
+      throw new NotImplementedException();
+    }
+
+    public IBuilder AddRepeatedField(FieldDescriptor field, object value) {
+      throw new NotImplementedException();
+    }
+
+    public IBuilder MergeUnknownFields(UnknownFieldSet unknownFields) {
+      UnknownFields = UnknownFieldSet.CreateBuilder(UnknownFields)
+          .MergeFrom(unknownFields)
+          .Build();
+      return this;
+    }
+
+    public UnknownFieldSet UnknownFields {
+      get {
+        throw new NotImplementedException();
+      }
+      set {
+        throw new NotImplementedException();
+      }
+    }
+
+    public IBuilder MergeFrom(ByteString data) {
+      CodedInputStream input = data.CreateCodedInput();
+      MergeFrom(input);
+      input.CheckLastTagWas(0);
+      return this;
+    }
+
+    public IBuilder MergeFrom(ByteString data, ExtensionRegistry extensionRegistry) {
+      CodedInputStream input = data.CreateCodedInput();
+      MergeFrom(input, extensionRegistry);
+      input.CheckLastTagWas(0);
+      return this;
+    }
+
+    public IBuilder MergeFrom(byte[] data) {
+      CodedInputStream input = CodedInputStream.CreateInstance(data);
+      MergeFrom(input);
+      input.CheckLastTagWas(0);
+      return this;
+    }
+
+    public IBuilder MergeFrom(byte[] data, ExtensionRegistry extensionRegistry) {
+      CodedInputStream input = CodedInputStream.CreateInstance(data);
+      MergeFrom(input, extensionRegistry);
+      input.CheckLastTagWas(0);
+      return this;
+    }
+
+    public IBuilder MergeFrom(Stream input) {
+      CodedInputStream codedInput = CodedInputStream.CreateInstance(input);
+      MergeFrom(codedInput);
+      codedInput.CheckLastTagWas(0);
+      return this;
+    }
+
+    public IBuilder MergeFrom(Stream input, ExtensionRegistry extensionRegistry) {
+      CodedInputStream codedInput = CodedInputStream.CreateInstance(input);
+      MergeFrom(codedInput, extensionRegistry);
+      codedInput.CheckLastTagWas(0);
+      return this;
+    }
+  }
+}

+ 76 - 8
csharp/ProtocolBuffers/AbstractMessage.cs

@@ -34,13 +34,15 @@ namespace Google.ProtocolBuffers {
 
     #region Unimplemented members of IMessage
     public abstract MessageDescriptor DescriptorForType { get; }
-    public abstract IDictionary<Descriptors.FieldDescriptor, object> AllFields { get; }
-    public abstract bool HasField(Descriptors.FieldDescriptor field);
-    public abstract object this[Descriptors.FieldDescriptor field] { get; }
-    public abstract int GetRepeatedFieldCount(Descriptors.FieldDescriptor field);
-    public abstract object this[Descriptors.FieldDescriptor field, int index] { get; }
+    public abstract IDictionary<FieldDescriptor, object> AllFields { get; }
+    public abstract bool HasField(FieldDescriptor field);
+    public abstract object this[FieldDescriptor field] { get; }
+    public abstract int GetRepeatedFieldCount(FieldDescriptor field);
+    public abstract object this[FieldDescriptor field, int index] { get; }
     public abstract UnknownFieldSet UnknownFields { get; }
-    public abstract IMessage DefaultInstanceForType { get; }
+    // FIXME
+    IMessage IMessage.DefaultInstanceForType { get { return null; } }
+    IBuilder IMessage.CreateBuilderForType() { return null; }
     #endregion
 
     public bool IsInitialized {
@@ -152,8 +154,6 @@ namespace Google.ProtocolBuffers {
       codedOutput.Flush();
     }
 
-    public abstract IBuilder CreateBuilderForType();
-
     public override bool Equals(object other) {
       if (other == this) {
         return true;
@@ -173,5 +173,73 @@ namespace Google.ProtocolBuffers {
       hash = (53 * hash) + AllFields.GetHashCode();
       return hash;
     }
+
+    #region IMessage Members
+
+    MessageDescriptor IMessage.DescriptorForType {
+      get { throw new NotImplementedException(); }
+    }
+
+    IDictionary<FieldDescriptor, object> IMessage.AllFields {
+      get { throw new NotImplementedException(); }
+    }
+
+    bool IMessage.HasField(FieldDescriptor field) {
+      throw new NotImplementedException();
+    }
+
+    object IMessage.this[FieldDescriptor field] {
+      get { throw new NotImplementedException(); }
+    }
+
+    int IMessage.GetRepeatedFieldCount(FieldDescriptor field) {
+      throw new NotImplementedException();
+    }
+
+    object IMessage.this[FieldDescriptor field, int index] {
+      get { throw new NotImplementedException(); }
+    }
+
+    UnknownFieldSet IMessage.UnknownFields {
+      get { throw new NotImplementedException(); }
+    }
+
+    bool IMessage.IsInitialized {
+      get { throw new NotImplementedException(); }
+    }
+
+    void IMessage.WriteTo(CodedOutputStream output) {
+      throw new NotImplementedException();
+    }
+
+    int IMessage.SerializedSize {
+      get { throw new NotImplementedException(); }
+    }
+
+    bool IMessage.Equals(object other) {
+      throw new NotImplementedException();
+    }
+
+    int IMessage.GetHashCode() {
+      throw new NotImplementedException();
+    }
+
+    string IMessage.ToString() {
+      throw new NotImplementedException();
+    }
+
+    ByteString IMessage.ToByteString() {
+      throw new NotImplementedException();
+    }
+
+    byte[] IMessage.ToByteArray() {
+      throw new NotImplementedException();
+    }
+
+    void IMessage.WriteTo(Stream output) {
+      throw new NotImplementedException();
+    }
+
+    #endregion
   }
 }

+ 9 - 0
csharp/ProtocolBuffers/ByteString.cs

@@ -105,6 +105,15 @@ namespace Google.ProtocolBuffers {
       return ToString(Encoding.UTF8);
     }
 
+    /// <summary>
+    /// Creates a CodedInputStream from this ByteString's data.
+    /// </summary>
+    public CodedInputStream CreateCodedInput() {
+      
+      // We trust CodedInputStream not to reveal the provided byte array or modify it
+      return CodedInputStream.CreateInstance(bytes);
+    }
+
     // TODO(jonskeet): CopyTo, Equals, GetHashCode if they turn out to be required
 
     /// <summary>

+ 1 - 1
csharp/ProtocolBuffers/CodedInputStream.cs

@@ -321,7 +321,7 @@ namespace Google.ProtocolBuffers {
     /// Reads a field of any primitive type. Enums, groups and embedded
     /// messages are not handled by this method.
     /// </summary>
-    public object readPrimitiveField(FieldType fieldType) {
+    public object ReadPrimitiveField(FieldType fieldType) {
       switch (fieldType) {
         case FieldType.Double:   return ReadDouble();
         case FieldType.Float:    return ReadFloat();

+ 3 - 3
csharp/ProtocolBuffers/CodedOutputStream.cs

@@ -271,7 +271,7 @@ namespace Google.ProtocolBuffers {
         case FieldType.SFixed64: WriteSFixed64(fieldNumber, (long)value); break;
         case FieldType.SInt32: WriteSInt32(fieldNumber, (int)value); break;
         case FieldType.SInt64: WriteSInt64(fieldNumber, (long)value); break;
-        case FieldType.Enum: WriteEnum(fieldNumber, ((Descriptors.EnumValueDescriptor)value).Number);
+        case FieldType.Enum: WriteEnum(fieldNumber, ((EnumValueDescriptor)value).Number);
           break;
       }
     }
@@ -615,7 +615,7 @@ namespace Google.ProtocolBuffers {
      * @param number The field's number.
      * @param value  Object representing the field's value.  Must be of the exact
      *               type which would be returned by
-     *               {@link Message#getField(Descriptors.FieldDescriptor)} for
+     *               {@link Message#getField(FieldDescriptor)} for
      *               this field.
      */
     public static int ComputeFieldSize(FieldType fieldType, int fieldNumber, Object value) {
@@ -637,7 +637,7 @@ namespace Google.ProtocolBuffers {
         case FieldType.SFixed64: return ComputeSFixed64Size(fieldNumber, (long)value);
         case FieldType.SInt32: return ComputeSInt32Size(fieldNumber, (int)value);
         case FieldType.SInt64: return ComputeSInt64Size(fieldNumber, (long)value);
-        case FieldType.Enum: return ComputeEnumSize(fieldNumber, ((Descriptors.EnumValueDescriptor)value).Number);
+        case FieldType.Enum: return ComputeEnumSize(fieldNumber, ((EnumValueDescriptor)value).Number);
         default:
           throw new ArgumentOutOfRangeException("Invalid field type " + fieldType);
       }

+ 21 - 0
csharp/ProtocolBuffers/Collections/Lists.cs

@@ -0,0 +1,21 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Text;
+
+namespace Google.ProtocolBuffers.Collections {
+  /// <summary>
+  /// Utilities class for dealing with lists.
+  /// </summary>
+  static class Lists<T> {
+
+    static readonly ReadOnlyCollection<T> empty = new ReadOnlyCollection<T>(new T[0]);
+
+    /// <summary>
+    /// Returns an immutable empty list.
+    /// </summary>
+    internal static ReadOnlyCollection<T> Empty {
+      get { return empty; }
+    }
+  }
+}

+ 12 - 0
csharp/ProtocolBuffers/Descriptors/FieldDescriptor.cs

@@ -13,5 +13,17 @@ namespace Google.ProtocolBuffers.Descriptors {
 
     public FieldType FieldType { get; set; }
     public int FieldNumber { get; set; }
+
+    public bool IsExtension { get; set; }
+
+    public MessageDescriptor ContainingType { get; set; }
+
+    public string FullName { get; set; }
+
+    public bool IsOptional { get; set; }
+
+    public MessageDescriptor MessageType { get; set; }
+
+    public MessageDescriptor ExtensionScope { get; set; }
   }
 }

+ 9 - 1
csharp/ProtocolBuffers/Descriptors/MappedType.cs

@@ -7,6 +7,14 @@ namespace Google.ProtocolBuffers.Descriptors {
   /// Type as it's mapped onto a .NET type.
   /// </summary>
   public enum MappedType {
-    Message
+    Int32,
+    Int64,
+    Single,
+    Double,
+    Boolean,
+    String,
+    ByteString,
+    Message,
+    Enum
   }
 }

+ 1 - 0
csharp/ProtocolBuffers/Descriptors/MessageDescriptor.cs

@@ -5,5 +5,6 @@ namespace Google.ProtocolBuffers.Descriptors {
   public class MessageDescriptor {
     public IList<FieldDescriptor> Fields;
     public DescriptorProtos.MessageOptions Options;
+    public string FullName;
   }
 }

+ 25 - 0
csharp/ProtocolBuffers/ExtensionInfo.cs

@@ -0,0 +1,25 @@
+using Google.ProtocolBuffers.Descriptors;
+
+namespace Google.ProtocolBuffers
+{
+  public sealed class ExtensionInfo {
+    /// <summary>
+    /// The extension's descriptor
+    /// </summary>
+    public FieldDescriptor Descriptor { get; private set; }
+
+    /// <summary>
+    /// A default instance of the extensions's type, if it has a message type,
+    /// or null otherwise.
+    /// </summary>
+    public IMessage DefaultInstance { get; private set; }
+
+    internal ExtensionInfo(FieldDescriptor descriptor) : this(descriptor, null) {
+    }
+
+    internal ExtensionInfo(FieldDescriptor descriptor, IMessage defaultInstance) {
+      Descriptor = descriptor;
+      DefaultInstance = defaultInstance;
+    }
+  }
+}

+ 161 - 1
csharp/ProtocolBuffers/ExtensionRegistry.cs

@@ -13,7 +13,167 @@
 // 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.Collections.Generic;
+using Google.ProtocolBuffers.Descriptors;
+using System;
+
 namespace Google.ProtocolBuffers {
-  public class ExtensionRegistry {
+  /// <summary>
+  /// TODO(jonskeet): Copy docs from Java
+  /// </summary>
+  public sealed class ExtensionRegistry {
+
+    private static readonly ExtensionRegistry empty = new ExtensionRegistry(
+        new Dictionary<string, ExtensionInfo>(),
+        new Dictionary<DescriptorIntPair, ExtensionInfo>(),
+        true);
+
+    private readonly IDictionary<string, ExtensionInfo> extensionsByName;
+    private readonly IDictionary<DescriptorIntPair, ExtensionInfo> extensionsByNumber;
+    private readonly bool readOnly;
+
+    private ExtensionRegistry(IDictionary<String, ExtensionInfo> extensionsByName,
+        IDictionary<DescriptorIntPair, ExtensionInfo> extensionsByNumber,
+        bool readOnly) {
+      this.extensionsByName = extensionsByName;
+      this.extensionsByNumber = extensionsByNumber;
+      this.readOnly = readOnly;
+    }
+
+    /// <summary>
+    /// Construct a new, empty instance.
+    /// </summary>
+    public static ExtensionRegistry CreateInstance() {
+      return new ExtensionRegistry(new Dictionary<string, ExtensionInfo>(),
+        new Dictionary<DescriptorIntPair, ExtensionInfo>(), false);
+    }
+
+    /// <summary>
+    /// Get the unmodifiable singleton empty instance.
+    /// </summary>
+    public static ExtensionRegistry Empty {
+      get { return empty; }
+    }
+
+    public ExtensionRegistry AsReadOnly() {
+      return new ExtensionRegistry(extensionsByName, extensionsByNumber, true);
+    }
+
+    /// <summary>
+    /// Finds an extension by fully-qualified field name, in the
+    /// proto namespace, i.e. result.Descriptor.FullName will match
+    /// <paramref name="fullName"/> if a match is found. A null
+    /// reference is returned if the extension can't be found.
+    /// </summary>
+    public ExtensionInfo this[string fullName] {
+      get {
+        ExtensionInfo ret;
+        extensionsByName.TryGetValue(fullName, out ret);
+        return ret;
+      }
+    }
+
+    /// <summary>
+    /// Finds an extension by containing type and field number.
+    /// A null reference is returned if the extension can't be found.
+    /// </summary>
+    public ExtensionInfo this[MessageDescriptor containingType, int fieldNumber] {
+      get {
+        ExtensionInfo ret;
+        extensionsByNumber.TryGetValue(new DescriptorIntPair(containingType, fieldNumber), out ret);
+        return ret;
+      }
+    }
+
+    /// <summary>
+    /// Add an extension from a generated file to the registry.
+    /// </summary>
+    public void Add<TContainer, TExtension> (GeneratedExtension<TContainer, TExtension> extension) 
+        where TContainer : IMessage<TContainer> {
+      if (extension.Descriptor.MappedType == MappedType.Message) {
+        Add(new ExtensionInfo(extension.Descriptor, extension.MessageDefaultInstance));
+      } else {
+        Add(new ExtensionInfo(extension.Descriptor, null));
+      }
+    }
+
+    /// <summary>
+    /// Adds a non-message-type extension to the registry by descriptor.
+    /// </summary>
+    /// <param name="type"></param>
+    public void Add(FieldDescriptor type) {
+      if (type.MappedType == MappedType.Message) {
+        throw new ArgumentException("ExtensionRegistry.Add() must be provided a default instance "
+            + "when adding an embedded message extension.");
+      }
+      Add(new ExtensionInfo(type, null));
+    }
+
+    /// <summary>
+    /// Adds a message-type-extension to the registry by descriptor.
+    /// </summary>
+    /// <param name="type"></param>
+    /// <param name="defaultInstance"></param>
+    public void Add(FieldDescriptor type, IMessage defaultInstance) {
+      if (type.MappedType != MappedType.Message) {
+        throw new ArgumentException("ExtensionRegistry.Add() provided a default instance for a "
+            + "non-message extension.");
+      }
+      Add(new ExtensionInfo(type, defaultInstance));
+    }
+
+    private void Add(ExtensionInfo extension) {
+      if (readOnly) {
+        throw new InvalidOperationException("Cannot add entries to a read-only extension registry");
+      }
+      if (!extension.Descriptor.IsExtension) {
+        throw new ArgumentException("ExtensionRegistry.add() was given a FieldDescriptor for a "
+            + "regular (non-extension) field.");
+      }
+
+      extensionsByName[extension.Descriptor.FullName] = extension;
+      extensionsByNumber[new DescriptorIntPair(extension.Descriptor.ContainingType,
+          extension.Descriptor.FieldNumber)] = extension;
+
+      FieldDescriptor field = extension.Descriptor;
+      if (field.ContainingType.Options.IsMessageSetWireFormat
+          && field.FieldType == FieldType.Message
+          && field.IsOptional
+          && field.ExtensionScope == field.MessageType) {
+        // This is an extension of a MessageSet type defined within the extension
+        // type's own scope.  For backwards-compatibility, allow it to be looked
+        // up by type name.
+        extensionsByName[field.MessageType.FullName] = extension;
+      }
+    }
+
+    /// <summary>
+    /// Nested type just used to represent a pair of MessageDescriptor and int, as
+    /// the key into the "by number" map.
+    /// </summary>
+    private struct DescriptorIntPair : IEquatable<DescriptorIntPair> {
+      readonly MessageDescriptor descriptor;
+      readonly int number;
+
+      internal DescriptorIntPair(MessageDescriptor descriptor, int number) {
+        this.descriptor = descriptor;
+        this.number = number;
+      }
+
+      public override int GetHashCode() {
+        return descriptor.GetHashCode() * ((1 << 16) - 1) + number;
+      }
+
+      public override bool Equals(object obj) {
+        if (!(obj is DescriptorIntPair)) {
+          return false;
+        }
+        return Equals((DescriptorIntPair)obj);
+      }
+
+      public bool Equals(DescriptorIntPair other) {
+        return descriptor == other.descriptor && number == other.number;
+      }
+    }
   }
 }

+ 12 - 0
csharp/ProtocolBuffers/FieldAccess/Delegates.cs

@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Google.ProtocolBuffers.FieldAccess {
+  /// <summary>
+  /// Declarations of delegate types used for field access. Can't
+  /// use Func and Action (other than one parameter) as we can't guarantee .NET 3.5.
+  /// </summary>
+  delegate bool HasFunction<TMessage>(TMessage message);
+  
+}

+ 25 - 0
csharp/ProtocolBuffers/FieldAccess/FieldAccessorTable.cs

@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Google.ProtocolBuffers.Descriptors;
+
+namespace Google.ProtocolBuffers.FieldAccess {
+  public class FieldAccessorTable<TMessage, TBuilder> 
+      where TMessage : IMessage<TMessage> 
+      where TBuilder : IBuilder<TMessage> {
+
+    readonly MessageDescriptor descriptor;
+
+    public MessageDescriptor Descriptor { 
+      get { return descriptor; }
+    }
+
+    public FieldAccessorTable(MessageDescriptor descriptor, String[] pascalCaseNames) {
+      this.descriptor = descriptor;
+    }
+
+    internal IFieldAccessor<TMessage, TBuilder> this[FieldDescriptor field] {
+      get { return null; }
+    }
+  }
+}

+ 34 - 0
csharp/ProtocolBuffers/FieldAccess/IFieldAccessor.cs

@@ -0,0 +1,34 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Google.ProtocolBuffers.FieldAccess {
+  internal interface IFieldAccessor<TMessage, TBuilder> 
+      where TMessage : IMessage<TMessage> 
+      where TBuilder : IBuilder<TMessage> {
+
+    void AddRepeated(IBuilder<TMessage> builder, object value);
+    bool Has(IMessage<TMessage> message);
+    int GetRepeatedCount(IMessage<TMessage> message);
+    void Clear(TBuilder builder);
+    TBuilder CreateBuilder();
+
+    /// <summary>
+    /// Accessor for single fields
+    /// </summary>
+    object this[IMessage<TMessage> message] { get; }
+    /// <summary>
+    /// Mutator for single fields
+    /// </summary>
+    object this[IBuilder<TMessage> builder] { set; }
+
+    /// <summary>
+    /// Accessor for repeated fields
+    /// </summary>
+    object this[IMessage<TMessage> message, int index] { get; }
+    /// <summary>
+    /// Mutator for repeated fields
+    /// </summary>
+    object this[IBuilder<TMessage> builder, int index] { set; }
+  }
+}

+ 65 - 0
csharp/ProtocolBuffers/FieldAccess/SingularFieldAccessor.cs

@@ -0,0 +1,65 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Google.ProtocolBuffers.Descriptors;
+
+namespace Google.ProtocolBuffers.FieldAccess {
+  internal class SingularFieldAccessor<TMessage, TBuilder> : IFieldAccessor<TMessage, TBuilder>
+    where TMessage : IMessage<TMessage>
+    where TBuilder : IBuilder<TMessage> {
+
+    readonly HasFunction<TMessage> hasProxy;
+    readonly Action<TBuilder> clearProxy;
+
+    internal SingularFieldAccessor(FieldDescriptor descriptor, String pascalCaseName) {
+
+      /*          Class<? extends GeneratedMessage> messageClass,
+                Class<? extends GeneratedMessage.Builder> builderClass) {
+              getMethod = getMethodOrDie(messageClass, "get" + camelCaseName);
+              type = getMethod.getReturnType();
+              setMethod = getMethodOrDie(builderClass, "set" + camelCaseName, type);
+              hasMethod =
+                getMethodOrDie(messageClass, "has" + camelCaseName);
+              clearMethod = getMethodOrDie(builderClass, "clear" + camelCaseName); */
+    }
+
+    public bool Has(IMessage<TMessage> message) {
+      return false;// hasProxy(message);
+    }
+
+    public void Clear(TBuilder builder) {
+//      clearProxy(builder);
+    }
+
+    public TBuilder CreateBuilder() {
+//      return createBuilderProxy(builder);
+      return default(TBuilder);
+    }
+
+    public object this[IMessage<TMessage> message] {
+      get { return null;/* getProxy(message);*/ }
+    }
+
+    public object this[IBuilder<TMessage> builder] {
+      set { /*setProxy(builder, value);*/ }
+    }
+
+    #region Repeated operations (which just throw an exception)
+    public object this[IMessage<TMessage> message, int index] {
+      get { throw new InvalidOperationException("Repeated operation called on singular field"); }
+    }
+
+    public object this[IBuilder<TMessage> builder, int index] {
+      set { throw new InvalidOperationException("Repeated operation called on singular field"); }
+    }
+
+    public int GetRepeatedCount(IMessage<TMessage> message) {
+      throw new InvalidOperationException("Repeated operation called on singular field");
+    }
+
+    public void AddRepeated(IBuilder<TMessage> builder, object value) {
+      throw new InvalidOperationException("Repeated operation called on singular field");
+    }
+    #endregion
+  }
+}

+ 33 - 0
csharp/ProtocolBuffers/FieldSet.cs

@@ -0,0 +1,33 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Google.ProtocolBuffers {
+  public class FieldSet {
+    public static void MergeFrom(CodedInputStream input,
+         UnknownFieldSet.Builder unknownFields,
+         ExtensionRegistry extensionRegistry,
+         IBuilder builder) {
+
+      while (true) {
+        uint tag = input.ReadTag();
+        if (tag == 0) {
+          break;
+        }
+        if (!MergeFieldFrom(input, unknownFields, extensionRegistry,
+                        builder, tag)) {
+          // end group tag
+          break;
+        }
+      }
+    }
+
+    public static bool MergeFieldFrom(CodedInputStream input,
+      UnknownFieldSet.Builder unknownFields,
+      ExtensionRegistry extensionRegistry,
+      IBuilder builder,
+      uint tag) {
+      throw new NotImplementedException();
+    }
+  }
+}

+ 34 - 0
csharp/ProtocolBuffers/GeneratedExtension.cs

@@ -0,0 +1,34 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Google.ProtocolBuffers.Descriptors;
+
+namespace Google.ProtocolBuffers {
+
+  /// <summary>
+  /// Base class for all generated extensions.
+  /// </summary>
+  /// <remarks>
+  /// The protocol compiler generates a static singleton instance of this
+  /// class for each extension. For exmaple, imagine a .proto file with:
+  /// <code>
+  /// message Foo {
+  ///   extensions 1000 to max
+  /// }
+  /// 
+  /// extend Foo {
+  ///   optional int32 bar;
+  /// }
+  /// </code>
+  /// Then MyProto.Foo.Bar has type GeneratedExtension&lt;MyProto.Foo,int&gt;.
+  /// <para />
+  /// In general, users should ignore the details of this type, and
+  /// simply use the static singletons as parmaeters to the extension accessors
+  /// in ExtendableMessage and ExtendableBuilder.
+  /// </remarks>
+  public class GeneratedExtension<TContainer, TExtension> where TContainer : IMessage<TContainer> {
+    public FieldDescriptor Descriptor;
+
+    public IMessage MessageDefaultInstance;
+  }
+}

+ 71 - 0
csharp/ProtocolBuffers/GeneratedMessage.cs

@@ -0,0 +1,71 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Google.ProtocolBuffers.Descriptors;
+using Google.ProtocolBuffers.FieldAccess;
+
+namespace Google.ProtocolBuffers {
+  
+  /// <summary>
+  /// All generated protocol message classes extend this class. It implements
+  /// most of the IMessage and IBuilder interfaces using reflection. Users
+  /// can ignore this class as an implementation detail.
+  /// </summary>
+  public abstract class GeneratedMessage<TMessage, TBuilder> : AbstractMessage, IMessage<TMessage>
+      where TMessage : GeneratedMessage<TMessage, TBuilder> where TBuilder : IBuilder<TMessage> {
+
+    private readonly UnknownFieldSet unknownFields = UnknownFieldSet.DefaultInstance;
+
+    protected abstract FieldAccessorTable<TMessage, TBuilder> InternalFieldAccessors { get; }
+
+    public override MessageDescriptor DescriptorForType {
+      get { return InternalFieldAccessors.Descriptor; }
+    }
+
+    public IMessage<TMessage> DefaultInstanceForType {
+      get { throw new System.NotImplementedException(); }
+    }
+
+    public IBuilder<TMessage> CreateBuilderForType() {
+      throw new System.NotImplementedException();
+    }
+
+    private IDictionary<FieldDescriptor, Object> GetMutableFieldMap() {
+      var ret = new Dictionary<FieldDescriptor, object>();
+      MessageDescriptor descriptor = DescriptorForType;
+      foreach (FieldDescriptor field in descriptor.Fields) {
+        IFieldAccessor<TMessage, TBuilder> accessor = InternalFieldAccessors[field];
+        if ((field.IsRepeated && accessor.GetRepeatedCount(this) != 0)
+            || accessor.Has(this)) {
+          ret[field] = accessor[this];
+        }
+      }
+      return ret;
+    }
+
+    public override IDictionary<FieldDescriptor, object> AllFields {
+      // FIXME: Make it immutable
+      get { return GetMutableFieldMap(); }
+    }
+
+    public override bool HasField(FieldDescriptor field) {
+      return InternalFieldAccessors[field].Has(this);
+    }
+
+    public override int GetRepeatedFieldCount(FieldDescriptor field) {
+      return InternalFieldAccessors[field].GetRepeatedCount(this);
+    }
+
+    public override object this[FieldDescriptor field, int index] {
+      get { return InternalFieldAccessors[field][this, index]; }
+    }
+
+    public override object this[FieldDescriptor field] {
+      get { return InternalFieldAccessors[field][this]; }
+    }
+
+    public override UnknownFieldSet UnknownFields {
+      get { return unknownFields; }
+    }
+  }
+}

+ 51 - 35
csharp/ProtocolBuffers/IBuilder.cs

@@ -29,7 +29,6 @@ namespace Google.ProtocolBuffers {
   /// how IEnumerable and IEnumerable&lt;T&gt; work.
   /// </summary>
   public interface IBuilder {
-    IBuilder MergeFrom(CodedInputStream codedInputStream, ExtensionRegistry extensionRegistry);
     /// <summary>
     /// Returns true iff all required fields in the message and all
     /// embedded messages are set.
@@ -41,15 +40,15 @@ namespace Google.ProtocolBuffers {
     /// The returned map may or may not reflect future changes to the builder.
     /// Either way, the returned map is unmodifiable.
     /// </summary>
-    IDictionary<ProtocolBuffers.Descriptors.FieldDescriptor, object> AllFields { get; }
+    IDictionary<FieldDescriptor, object> AllFields { get; }
 
     /// <summary>
     /// Allows getting and setting of a field.
-    /// <see cref="IMessage{T}.Item(Descriptors.FieldDescriptor)"/>
+    /// <see cref="IMessage{T}.Item(FieldDescriptor)"/>
     /// </summary>
     /// <param name="field"></param>
     /// <returns></returns>
-    object this[Descriptors.FieldDescriptor field] { get; set; }
+    object this[FieldDescriptor field] { get; set; }
 
     /// <summary>
     /// Get the message's type's descriptor.
@@ -62,18 +61,44 @@ namespace Google.ProtocolBuffers {
     /// </summary>
     /// <param name="field"></param>
     /// <returns></returns>
-    int GetRepeatedFieldCount(Descriptors.FieldDescriptor field);
+    int GetRepeatedFieldCount(FieldDescriptor field);
 
     /// <summary>
     /// Allows getting and setting of a repeated field value.
-    /// <see cref="IMessage{T}.Item(Descriptors.FieldDescriptor, int)"/>
+    /// <see cref="IMessage{T}.Item(FieldDescriptor, int)"/>
     /// </summary>
-    object this[Descriptors.FieldDescriptor field, int index] { get; set; }
+    object this[FieldDescriptor field, int index] { get; set; }
 
     /// <summary>
     /// <see cref="IMessage{T}.HasField"/>
     /// </summary>
-    bool HasField(Descriptors.FieldDescriptor field);
+    bool HasField(FieldDescriptor field);
+
+    /// <summary>
+    /// <see cref="IMessage{T}.UnknownFields"/>
+    /// </summary>
+    UnknownFieldSet UnknownFields { get; set; }
+
+    #region Non-generic versions of generic methods in IBuilder<T>
+    IBuilder Clear();
+    IBuilder MergeFrom(IMessage other);
+    IMessage Build();
+    IMessage BuildPartial();
+    IBuilder Clone();
+    IBuilder MergeFrom(CodedInputStream input);
+    IBuilder MergeFrom(CodedInputStream codedInputStream, ExtensionRegistry extensionRegistry);
+    IMessage DefaultInstanceForType { get; }
+    IBuilder NewBuilderForField<TField>(FieldDescriptor field);
+    IBuilder ClearField(FieldDescriptor field);
+    IBuilder AddRepeatedField(FieldDescriptor field, object value);
+    IBuilder MergeUnknownFields(UnknownFieldSet unknownFields);
+    IBuilder MergeFrom(ByteString data);
+    IBuilder MergeFrom(ByteString data, ExtensionRegistry extensionRegistry);
+    IBuilder MergeFrom(byte[] data);
+    IBuilder MergeFrom(byte[] data, ExtensionRegistry extensionRegistry);
+    IBuilder MergeFrom(Stream input);
+    IBuilder MergeFrom(Stream input, ExtensionRegistry extensionRegistry);
+    #endregion
   }
 
   /// <summary>
@@ -85,7 +110,7 @@ namespace Google.ProtocolBuffers {
     /// <summary>
     /// Resets all fields to their default values.
     /// </summary>
-    IBuilder<T> Clear();
+    new IBuilder<T> Clear();
 
     /// <summary>
     /// Merge the specified other message into the message being
@@ -111,20 +136,20 @@ namespace Google.ProtocolBuffers {
     /// <exception cref="UninitializedMessageException">the message
     /// is missing one or more required fields; use BuildPartial to bypass
     /// this check</exception>
-    IMessage<T> Build();
+    new IMessage<T> Build();
 
     /// <summary>
     /// Like Build(), but does not throw an exception if the message is missing
     /// required fields. Instead, a partial message is returned.
     /// </summary>
     /// <returns></returns>
-    IMessage<T> BuildPartial();
+    new IMessage<T> BuildPartial();
 
     /// <summary>
     /// Clones this builder.
     /// TODO(jonskeet): Explain depth of clone.
     /// </summary>
-    IBuilder<T> Clone();
+    new IBuilder<T> Clone();
 
     /// <summary>
     /// Parses a message of this type from the input and merges it with this
@@ -145,7 +170,7 @@ namespace Google.ProtocolBuffers {
     /// Use BuildPartial to build, which ignores missing required fields.
     /// </list>
     /// </remarks>
-    IBuilder<T> MergeFrom(CodedInputStream input);
+    new IBuilder<T> MergeFrom(CodedInputStream input);
 
     /// <summary>
     /// Like MergeFrom(CodedInputStream), but also parses extensions.
@@ -159,17 +184,14 @@ namespace Google.ProtocolBuffers {
     /// Get's the message's type's default instance.
     /// <see cref="IMessage{T}.DefaultInstanceForType" />
     /// </summary>
-    IMessage<T> DefaultInstanceForType { get; }
+    new IMessage<T> DefaultInstanceForType { get; }
 
     /// <summary>
     /// Create a builder for messages of the appropriate type for the given field.
     /// Messages built with this can then be passed to the various mutation properties
     /// and methods.
     /// </summary>
-    /// <typeparam name="TField"></typeparam>
-    /// <param name="field"></param>
-    /// <returns></returns>
-    IBuilder<TField> NewBuilderForField<TField>(Descriptors.FieldDescriptor field) where TField : IMessage<TField>;
+    new IBuilder<TField> NewBuilderForField<TField>(FieldDescriptor field) where TField : IMessage<TField>;
 
     /// <summary>
     /// Clears the field. This is exactly equivalent to calling the generated
@@ -177,7 +199,7 @@ namespace Google.ProtocolBuffers {
     /// </summary>
     /// <param name="field"></param>
     /// <returns></returns>
-    IBuilder<T> ClearField(Descriptors.FieldDescriptor field);
+    new IBuilder<T> ClearField(FieldDescriptor field);
 
     /// <summary>
     /// Appends the given value as a new element for the specified repeated field.
@@ -186,51 +208,45 @@ namespace Google.ProtocolBuffers {
     /// the field does not belong to this builder's type, or the value is
     /// of the incorrect type
     /// </exception>
-    IBuilder<T> AddRepeatedField(Descriptors.FieldDescriptor field, object value);
-
-    /// <summary>
-    /// <see cref="IMessage{T}.UnknownFields"/>
-    /// </summary>
-    UnknownFieldSet UnknownFields { get; set; }
+    new IBuilder<T> AddRepeatedField(FieldDescriptor field, object value);
 
     /// <summary>
     /// Merge some unknown fields into the set for this message.
     /// </summary>
-    IBuilder<T> MergeUnknownFields(UnknownFieldSet unknownFields);
+    new IBuilder<T> MergeUnknownFields(UnknownFieldSet unknownFields);
 
     #region Convenience methods
     // TODO(jonskeet): Implement these as extension methods?
-
     /// <summary>
     /// Parse <paramref name="data"/> as a message of this type and merge
     /// it with the message being built. This is just a small wrapper around
     /// MergeFrom(CodedInputStream).
     /// </summary>
-    IBuilder<T> MergeFrom(ByteString data);
+    new IBuilder<T> MergeFrom(ByteString data);
 
     /// <summary>
     /// Parse <paramref name="data"/> as a message of this type and merge
     /// it with the message being built. This is just a small wrapper around
     /// MergeFrom(CodedInputStream, ExtensionRegistry).
     /// </summary>
-    IBuilder<T> MergeFrom(ByteString data, ExtensionRegistry extensionRegistry);
+    new IBuilder<T> MergeFrom(ByteString data, ExtensionRegistry extensionRegistry);
 
     /// <summary>
     /// Parse <paramref name="data"/> as a message of this type and merge
     /// it with the message being built. This is just a small wrapper around
     /// MergeFrom(CodedInputStream).
     /// </summary>
-    IBuilder<T> MergeFrom(byte[] data);
+    new IBuilder<T> MergeFrom(byte[] data);
 
     /// <summary>
     /// Parse <paramref name="data"/> as a message of this type and merge
     /// it with the message being built. This is just a small wrapper around
     /// MergeFrom(CodedInputStream, ExtensionRegistry).
     /// </summary>
-    IBuilder<T> MergeFrom(byte[] data, ExtensionRegistry extensionRegistry);
+    new IBuilder<T> MergeFrom(byte[] data, ExtensionRegistry extensionRegistry);
 
     /// <summary>
-    /// Parse <paramref name="data"/> as a message of this type and merge
+    /// Parse <paramref name="input"/> as a message of this type and merge
     /// it with the message being built. This is just a small wrapper around
     /// MergeFrom(CodedInputStream). Note that this method always reads
     /// the entire input (unless it throws an exception). If you want it to
@@ -238,14 +254,14 @@ namespace Google.ProtocolBuffers {
     /// stream which limits reading. Despite usually reading the entire
     /// stream, this method never closes the stream.
     /// </summary>
-    IBuilder<T> MergeFrom(Stream input);
+    new IBuilder<T> MergeFrom(Stream input);
 
     /// <summary>
-    /// Parse <paramref name="data"/> as a message of this type and merge
+    /// Parse <paramref name="input"/> as a message of this type and merge
     /// it with the message being built. This is just a small wrapper around
     /// MergeFrom(CodedInputStream, ExtensionRegistry).
     /// </summary>
-    IBuilder<T> MergeFrom(Stream input, ExtensionRegistry extensionRegistry);
+    new IBuilder<T> MergeFrom(Stream input, ExtensionRegistry extensionRegistry);
     #endregion
   }
 }

+ 6 - 6
csharp/ProtocolBuffers/IMessage.cs

@@ -41,11 +41,11 @@ namespace Google.ProtocolBuffers {
     /// field is set iff HasField() returns true for that field.  A "repeated"
     /// field is set iff GetRepeatedFieldSize() is greater than zero.  The
     /// values are exactly what would be returned by calling
-    /// GetField(Descriptors.FieldDescriptor) for each field.  The map
+    /// GetField(FieldDescriptor) for each field.  The map
     /// is guaranteed to be a sorted map, so iterating over it will return fields
     /// in order by field number. 
     /// </summary>
-    IDictionary<Descriptors.FieldDescriptor, object> AllFields { get; }
+    IDictionary<FieldDescriptor, object> AllFields { get; }
 
     /// <summary>
     /// Returns true if the given field is set. This is exactly equivalent
@@ -53,7 +53,7 @@ namespace Google.ProtocolBuffers {
     /// </summary>
     /// <exception cref="ArgumentException">the field is a repeated field,
     /// or it's not a field of this type</exception>
-    bool HasField(Descriptors.FieldDescriptor field);
+    bool HasField(FieldDescriptor field);
 
     /// <summary>
     /// Obtains the value of the given field, or the default value if
@@ -61,7 +61,7 @@ namespace Google.ProtocolBuffers {
     /// value is returned. For embedded message fields, the sub-message
     /// is returned. For repeated fields, an IList&lt;T&gt; is returned.
     /// </summary>
-    object this[Descriptors.FieldDescriptor field] { get; }
+    object this[FieldDescriptor field] { get; }
 
     /// <summary>
     /// Returns the number of elements of a repeated field. This is
@@ -70,7 +70,7 @@ namespace Google.ProtocolBuffers {
     /// </summary>
     /// <exception cref="ArgumentException">the field is not a repeated field,
     /// or it's not a field of this type</exception>
-    int GetRepeatedFieldCount(Descriptors.FieldDescriptor field);
+    int GetRepeatedFieldCount(FieldDescriptor field);
 
     /// <summary>
     /// Gets an element of a repeated field. For value type fields 
@@ -81,7 +81,7 @@ namespace Google.ProtocolBuffers {
     /// or it's not a field of this type</exception>
     /// <exception cref="ArgumentOutOfRangeException">the index is out of
     /// range for the repeated field's value</exception>
-    object this[Descriptors.FieldDescriptor field, int index] { get; }
+    object this[FieldDescriptor field, int index] { get; }
 
     /// <summary>
     /// Returns the unknown fields for this message.

+ 11 - 0
csharp/ProtocolBuffers/ProtocolBuffers.csproj

@@ -36,23 +36,34 @@
     <Reference Include="System.Xml" />
   </ItemGroup>
   <ItemGroup>
+    <Compile Include="AbstractBuilder.cs" />
     <Compile Include="AbstractMessage.cs" />
     <Compile Include="Autogenerated.cs" />
     <Compile Include="ByteString.cs" />
     <Compile Include="CodedInputStream.cs" />
     <Compile Include="CodedOutputStream.cs" />
+    <Compile Include="Collections\Lists.cs" />
     <Compile Include="Descriptors\EnumValueDescriptor.cs" />
     <Compile Include="Descriptors\FieldDescriptor.cs" />
     <Compile Include="Descriptors\FieldType.cs" />
     <Compile Include="Descriptors\MappedType.cs" />
     <Compile Include="Descriptors\MessageDescriptor.cs" />
+    <Compile Include="ExtensionInfo.cs" />
     <Compile Include="ExtensionRegistry.cs" />
+    <Compile Include="FieldAccess\Delegates.cs" />
+    <Compile Include="FieldAccess\IFieldAccessor.cs" />
+    <Compile Include="FieldAccess\FieldAccessorTable.cs" />
+    <Compile Include="FieldAccess\SingularFieldAccessor.cs" />
+    <Compile Include="FieldSet.cs" />
+    <Compile Include="GeneratedExtension.cs" />
+    <Compile Include="GeneratedMessage.cs" />
     <Compile Include="IBuilder.cs" />
     <Compile Include="IMessage.cs" />
     <Compile Include="InvalidProtocolBufferException.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
     <Compile Include="TextFormat.cs" />
     <Compile Include="UninitializedMessageException.cs" />
+    <Compile Include="UnknownField.cs" />
     <Compile Include="UnknownFieldSet.cs" />
     <Compile Include="WireFormat.cs" />
   </ItemGroup>

+ 4 - 0
csharp/ProtocolBuffers/TextFormat.cs

@@ -7,5 +7,9 @@ namespace Google.ProtocolBuffers {
     public static string PrintToString(IMessage message) {
       throw new NotImplementedException();
     }
+
+    internal static string PrintToString(UnknownFieldSet unknownFieldSet) {
+      throw new NotImplementedException();
+    }
   }
 }

+ 306 - 0
csharp/ProtocolBuffers/UnknownField.cs

@@ -0,0 +1,306 @@
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using Google.ProtocolBuffers.Collections;
+
+namespace Google.ProtocolBuffers {
+  /// <summary>
+  /// Represents a single field in an UnknownFieldSet.
+  /// 
+  /// An UnknownField consists of five lists of values. The lists correspond
+   /// to the five "wire types" used in the protocol buffer binary format.
+   /// The wire type of each field can be determined from the encoded form alone,
+   /// without knowing the field's declared type. So, we are able to parse
+   /// unknown values at least this far and separate them. Normally, only one
+   /// of the five lists will contain any values, since it is impossible to
+   /// define a valid message type that declares two different types for the
+   /// same field number. However, the code is designed to allow for the case
+   /// where the same unknown field number is encountered using multiple different
+   /// wire types.
+   /// 
+   /// UnknownField is an immutable class. To construct one, you must use an
+   /// UnknownField.Builder.
+  /// </summary>
+  public sealed class UnknownField {
+
+    private static readonly UnknownField defaultInstance = CreateBuilder().Build();
+    private readonly ReadOnlyCollection<ulong> varintList;
+    private readonly ReadOnlyCollection<int> fixed32List;
+    private readonly ReadOnlyCollection<long> fixed64List;
+    private readonly ReadOnlyCollection<ByteString> lengthDelimitedList;
+    private readonly ReadOnlyCollection<UnknownFieldSet> groupList;
+
+    private UnknownField(ReadOnlyCollection<ulong> varintList,
+        ReadOnlyCollection<int> fixed32List, 
+        ReadOnlyCollection<long> fixed64List, 
+        ReadOnlyCollection<ByteString> lengthDelimitedList, 
+        ReadOnlyCollection<UnknownFieldSet> groupList) {
+      this.varintList = varintList;
+      this.fixed32List = fixed32List;
+      this.fixed64List = fixed64List;
+      this.lengthDelimitedList = lengthDelimitedList;
+      this.groupList = groupList;
+    }
+
+    public static UnknownField DefaultInstance { 
+      get { return defaultInstance; } 
+    }
+
+    /// <summary>
+    /// The list of varint values for this field.
+    /// </summary>
+    public IList<ulong> VarintList {
+      get { return varintList; }
+    }
+
+    /// <summary>
+    /// The list of fixed32 values for this field.
+    /// </summary>
+    public IList<int> Fixed32List {
+      get { return fixed32List; }
+    }
+
+    /// <summary>
+    /// The list of fixed64 values for this field.
+    /// </summary>
+    public IList<long> Fixed64List {
+      get { return fixed64List; }
+    }
+
+    /// <summary>
+    /// The list of length-delimited values for this field.
+    /// </summary>
+    public IList<ByteString> LengthDelimitedList {
+      get { return lengthDelimitedList; }
+    }
+
+    /// <summary>
+    /// The list of embedded group values for this field. These
+    /// are represented using UnknownFieldSets rather than Messages
+    /// since the group's type is presumably unknown.
+    /// </summary>
+    public IList<UnknownFieldSet> GroupList {
+      get { return groupList; }
+    }
+
+    /// <summary>
+    /// Constructs a new Builder.
+    /// </summary>
+    public static Builder CreateBuilder() {
+      return new Builder();
+    }
+
+    /// <summary>
+    /// Constructs a new Builder and initializes it to a copy of <paramref name="copyFrom"/>.
+    /// </summary>
+    public static Builder CreateBuilder(UnknownField copyFrom) {
+      return new Builder().MergeFrom(copyFrom);
+    }
+   
+    /// <summary>
+    /// Serializes the field, including the field number, and writes it to
+    /// <paramref name="output"/>.
+    /// </summary>
+    public void WriteTo(int fieldNumber, CodedOutputStream output) {
+      foreach (ulong value in varintList) {
+        output.WriteUInt64(fieldNumber, value);
+      }
+      foreach (int value in fixed32List) {
+        output.WriteFixed32(fieldNumber, value);
+      }
+      foreach (long value in fixed64List) {
+        output.WriteFixed64(fieldNumber, value);
+      }
+      foreach (ByteString value in lengthDelimitedList) {
+        output.WriteBytes(fieldNumber, value);
+      }
+      foreach (UnknownFieldSet value in groupList) {
+        output.WriteUnknownGroup(fieldNumber, value);
+      }
+    }
+
+    /// <summary>
+    /// Computes the number of bytes required to encode this field, including field
+    /// number.
+    /// </summary>
+    public int GetSerializedSize(int fieldNumber) {
+      int result = 0;
+      foreach (ulong value in varintList) {
+        result += CodedOutputStream.ComputeUInt64Size(fieldNumber, value);
+      }
+      foreach (int value in fixed32List) {
+        result += CodedOutputStream.ComputeFixed32Size(fieldNumber, value);
+      }
+      foreach (long value in fixed64List) {
+        result += CodedOutputStream.ComputeFixed64Size(fieldNumber, value);
+      }
+      foreach (ByteString value in lengthDelimitedList) {
+        result += CodedOutputStream.ComputeBytesSize(fieldNumber, value);
+      }
+      foreach (UnknownFieldSet value in groupList) {
+        result += CodedOutputStream.ComputeUnknownGroupSize(fieldNumber, value);
+      }
+      return result;
+    }
+
+    /// <summary>
+    /// Serializes the length-delimited values of the field, including field
+    /// number, and writes them to <paramref name="output"/> using the MessageSet wire format.
+    /// </summary>
+    /// <param name="fieldNumber"></param>
+    /// <param name="output"></param>
+    public void WriteAsMessageSetExtensionTo(int fieldNumber, CodedOutputStream output) {
+      foreach (ByteString value in lengthDelimitedList) {
+        output.WriteRawMessageSetExtension(fieldNumber, value);
+      }
+    }
+
+    /// <summary>
+    /// Get the number of bytes required to encode this field, incuding field number,
+    /// using the MessageSet wire format.
+    /// </summary>
+    public int GetSerializedSizeAsMessageSetExtension(int fieldNumber) {
+      int result = 0;
+      foreach (ByteString value in lengthDelimitedList) {
+        result += CodedOutputStream.ComputeRawMessageSetExtensionSize(fieldNumber, value);
+      }
+      return result;
+    }
+
+    /// <summary>
+    /// Used to build instances of UnknownField.
+    /// </summary>
+    public class Builder {
+
+      private List<ulong> varintList;
+      private List<int> fixed32List;
+      private List<long> fixed64List;
+      private List<ByteString> lengthDelimitedList;
+      private List<UnknownFieldSet> groupList;
+
+      /// <summary>
+      /// Builds the field. After building, the builder is reset to an empty
+      /// state. (This is actually easier than making it unusable.)
+      /// </summary>
+      public UnknownField Build() {
+        return new UnknownField(MakeReadOnly(ref varintList),
+            MakeReadOnly(ref fixed32List),
+            MakeReadOnly(ref fixed64List),
+            MakeReadOnly(ref lengthDelimitedList),
+            MakeReadOnly(ref groupList));
+      }
+
+      /// <summary>
+      /// Merge the values in <paramref name="other" /> into this field.  For each list
+      /// of values, <paramref name="other"/>'s values are append to the ones in this
+      /// field.
+      /// </summary>
+      public Builder MergeFrom(UnknownField other) {
+        varintList = AddAll(varintList, other.VarintList);
+        fixed32List = AddAll(fixed32List, other.Fixed32List);
+        fixed64List = AddAll(fixed64List, other.Fixed64List);
+        lengthDelimitedList = AddAll(lengthDelimitedList, other.LengthDelimitedList);
+        groupList = AddAll(groupList, other.GroupList);
+        return this;
+      }
+
+      /// <summary>
+      /// Returns a new list containing all of the given specified values from
+      /// both the <paramref name="current"/> and <paramref name="extras"/> lists.
+      /// If <paramref name="current" /> is null and <paramref name="extras"/> is empty,
+      /// null is returned. Otherwise, either a new list is created (if <paramref name="current" />
+      /// is null) or the elements of <paramref name="extras"/> are added to <paramref name="current" />.
+      /// </summary>
+      private static List<T> AddAll<T>(List<T> current, IList<T> extras)
+      {
+        if (extras.Count == 0) {
+          return current;
+        }
+ 	      if (current == null) {
+          current = new List<T>(extras);
+        } else {
+          current.AddRange(extras);
+        }
+        return current;
+      }
+
+      /// <summary>
+      /// Clears the contents of this builder.
+      /// </summary>
+      public Builder Clear() {
+        varintList = null;
+        fixed32List = null;
+        fixed64List = null;
+        lengthDelimitedList = null;
+        groupList = null;
+        return this;
+      }
+
+      /// <summary>
+      /// Adds a varint value.
+      /// </summary>
+      public Builder AddVarint(ulong value) {
+        varintList = Add(varintList, value);
+        return this;
+      }
+
+      /// <summary>
+      /// Adds a fixed32 value.
+      /// </summary>
+      public Builder AddFixed32(int value) {
+        fixed32List = Add(fixed32List, value);
+        return this;
+      }
+
+      /// <summary>
+      /// Adds a fixed64 value.
+      /// </summary>
+      public Builder AddFixed64(long value) {
+        fixed64List = Add(fixed64List, value);
+        return this;
+      }
+
+      /// <summary>
+      /// Adds a length-delimited value.
+      /// </summary>
+      public Builder AddLengthDelimited(ByteString value) {
+        lengthDelimitedList = Add(lengthDelimitedList, value);
+        return this;
+      }
+
+      /// <summary>
+      /// Adds an embedded group.
+      /// </summary>
+      /// <param name="value"></param>
+      /// <returns></returns>
+      public Builder AddGroup(UnknownFieldSet value) {
+        groupList = Add(groupList, value);
+        return this;
+      }
+
+      /// <summary>
+      /// Adds <paramref name="value"/> to the <paramref name="list"/>, creating
+      /// a new list if <paramref name="list"/> is null. The list is returned - either
+      /// the original reference or the new list.
+      /// </summary>
+      private static List<T> Add<T>(List<T> list, T value) {
+        if (list == null) {
+          list = new List<T>();
+        }
+        list.Add(value);
+        return list;
+      }
+
+      /// <summary>
+      /// Returns a read-only version of the given IList, and clears
+      /// the field used for <paramref name="list"/>. If the value
+      /// is null, an empty list is produced using Lists.Empty.
+      /// </summary>
+      /// <returns></returns>
+      private static ReadOnlyCollection<T> MakeReadOnly<T>(ref List<T> list) {
+        ReadOnlyCollection<T> ret = list == null ? Lists<T>.Empty : new ReadOnlyCollection<T>(list);
+ 	      list = null;
+        return ret;
+      }
+    }
+  }
+}

+ 386 - 8
csharp/ProtocolBuffers/UnknownFieldSet.cs

@@ -14,26 +14,404 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 using System;
+using System.Collections.Generic;
+using System.IO;
 
 namespace Google.ProtocolBuffers {
   public class UnknownFieldSet {
-    public int SerializedSizeAsMessageSet;
 
+    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>
+    /// Creates and returns a copy of the mapping from field numbers to values.
+    /// </summary>
+    public IDictionary<int, UnknownField> FieldDictionary {
+      get { return new Dictionary<int, UnknownField>(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) {
-      throw new NotImplementedException();
+      foreach (KeyValuePair<int, UnknownField> entry in fields) {
+        entry.Value.WriteTo(entry.Key, output);
+      }
     }
 
-    public int SerializedSize { get { return 0; } }
+    /// <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();
+    }
 
     public class Builder
     {
-      internal void MergeFrom(CodedInputStream codedInputStream) {
-        throw new NotImplementedException();
+      private Dictionary<int, UnknownField> fields = new Dictionary<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:
+            // TODO(jonskeet): Check this is correct (different to Java)
+            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 WriteAsMessageSetTo(CodedOutputStream output) {
-      throw new NotImplementedException();
     }
   }
 }

+ 2 - 2
csharp/ProtocolBuffers/WireFormat.cs

@@ -43,8 +43,8 @@ namespace Google.ProtocolBuffers {
     /// <summary>
     /// Given a tag value, determines the field number (the upper 29 bits).
     /// </summary>
-    public static uint GetTagFieldNumber(uint tag) {
-      return tag >> TagTypeBits;
+    public static int GetTagFieldNumber(uint tag) {
+      return (int) tag >> TagTypeBits;
     }
 
     /// <summary>