diff options
author | Jon Skeet <skeet@pobox.com> | 2008-08-14 20:33:33 +0100 |
---|---|---|
committer | Jon Skeet <skeet@pobox.com> | 2008-08-14 20:33:33 +0100 |
commit | 621bb698e54a7e0d2fdf74ab1f380183461ce7fa (patch) | |
tree | f1750773eee92541401126c66c811a382b0b22a7 /csharp | |
parent | a7246897ab87bc52951cc208f3775c7dfc1d6e09 (diff) | |
download | protobuf-621bb698e54a7e0d2fdf74ab1f380183461ce7fa.tar.gz protobuf-621bb698e54a7e0d2fdf74ab1f380183461ce7fa.tar.bz2 protobuf-621bb698e54a7e0d2fdf74ab1f380183461ce7fa.zip |
Some work on AbstractBuilder, and complete implementation of UnknownField and UnknownFieldSet
Diffstat (limited to 'csharp')
25 files changed, 1553 insertions, 65 deletions
diff --git a/csharp/ProtocolBuffers/AbstractBuilder.cs b/csharp/ProtocolBuffers/AbstractBuilder.cs new file mode 100644 index 00000000..a76e3479 --- /dev/null +++ b/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; + } + } +} diff --git a/csharp/ProtocolBuffers/AbstractMessage.cs b/csharp/ProtocolBuffers/AbstractMessage.cs index 562214a0..e1d5f883 100644 --- a/csharp/ProtocolBuffers/AbstractMessage.cs +++ b/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 } } diff --git a/csharp/ProtocolBuffers/ByteString.cs b/csharp/ProtocolBuffers/ByteString.cs index 34aea2df..94eb45e3 100644 --- a/csharp/ProtocolBuffers/ByteString.cs +++ b/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> diff --git a/csharp/ProtocolBuffers/CodedInputStream.cs b/csharp/ProtocolBuffers/CodedInputStream.cs index cdad2440..d2a3657b 100644 --- a/csharp/ProtocolBuffers/CodedInputStream.cs +++ b/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(); diff --git a/csharp/ProtocolBuffers/CodedOutputStream.cs b/csharp/ProtocolBuffers/CodedOutputStream.cs index bb160319..5fec5274 100644 --- a/csharp/ProtocolBuffers/CodedOutputStream.cs +++ b/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); } diff --git a/csharp/ProtocolBuffers/Collections/Lists.cs b/csharp/ProtocolBuffers/Collections/Lists.cs new file mode 100644 index 00000000..6d27142f --- /dev/null +++ b/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; } + } + } +} diff --git a/csharp/ProtocolBuffers/Descriptors/FieldDescriptor.cs b/csharp/ProtocolBuffers/Descriptors/FieldDescriptor.cs index c5c9bbe4..8eb19d53 100644 --- a/csharp/ProtocolBuffers/Descriptors/FieldDescriptor.cs +++ b/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; } } } diff --git a/csharp/ProtocolBuffers/Descriptors/MappedType.cs b/csharp/ProtocolBuffers/Descriptors/MappedType.cs index 4ee2d341..4d2d8e51 100644 --- a/csharp/ProtocolBuffers/Descriptors/MappedType.cs +++ b/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 } } diff --git a/csharp/ProtocolBuffers/Descriptors/MessageDescriptor.cs b/csharp/ProtocolBuffers/Descriptors/MessageDescriptor.cs index 1475d711..4e0834f0 100644 --- a/csharp/ProtocolBuffers/Descriptors/MessageDescriptor.cs +++ b/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; } } diff --git a/csharp/ProtocolBuffers/ExtensionInfo.cs b/csharp/ProtocolBuffers/ExtensionInfo.cs new file mode 100644 index 00000000..a4936104 --- /dev/null +++ b/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; + } + } +}
\ No newline at end of file diff --git a/csharp/ProtocolBuffers/ExtensionRegistry.cs b/csharp/ProtocolBuffers/ExtensionRegistry.cs index a8ed661d..6c36aff4 100644 --- a/csharp/ProtocolBuffers/ExtensionRegistry.cs +++ b/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; + } + } } } diff --git a/csharp/ProtocolBuffers/FieldAccess/Delegates.cs b/csharp/ProtocolBuffers/FieldAccess/Delegates.cs new file mode 100644 index 00000000..3dc0445d --- /dev/null +++ b/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); + +} diff --git a/csharp/ProtocolBuffers/FieldAccess/FieldAccessorTable.cs b/csharp/ProtocolBuffers/FieldAccess/FieldAccessorTable.cs new file mode 100644 index 00000000..d7b239c1 --- /dev/null +++ b/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; } + } + } +} diff --git a/csharp/ProtocolBuffers/FieldAccess/IFieldAccessor.cs b/csharp/ProtocolBuffers/FieldAccess/IFieldAccessor.cs new file mode 100644 index 00000000..b667fd7f --- /dev/null +++ b/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; } + } +} diff --git a/csharp/ProtocolBuffers/FieldAccess/SingularFieldAccessor.cs b/csharp/ProtocolBuffers/FieldAccess/SingularFieldAccessor.cs new file mode 100644 index 00000000..62b3c48d --- /dev/null +++ b/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 + } +} diff --git a/csharp/ProtocolBuffers/FieldSet.cs b/csharp/ProtocolBuffers/FieldSet.cs new file mode 100644 index 00000000..0efde4b1 --- /dev/null +++ b/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(); + } + } +} diff --git a/csharp/ProtocolBuffers/GeneratedExtension.cs b/csharp/ProtocolBuffers/GeneratedExtension.cs new file mode 100644 index 00000000..52d3349b --- /dev/null +++ b/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<MyProto.Foo,int>. + /// <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; + } +} diff --git a/csharp/ProtocolBuffers/GeneratedMessage.cs b/csharp/ProtocolBuffers/GeneratedMessage.cs new file mode 100644 index 00000000..515eadc0 --- /dev/null +++ b/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; } + } + } +} diff --git a/csharp/ProtocolBuffers/IBuilder.cs b/csharp/ProtocolBuffers/IBuilder.cs index 977001c7..50279039 100644 --- a/csharp/ProtocolBuffers/IBuilder.cs +++ b/csharp/ProtocolBuffers/IBuilder.cs @@ -29,7 +29,6 @@ namespace Google.ProtocolBuffers { /// how IEnumerable and IEnumerable<T> 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 } } diff --git a/csharp/ProtocolBuffers/IMessage.cs b/csharp/ProtocolBuffers/IMessage.cs index cb0e7623..9aea0ea7 100644 --- a/csharp/ProtocolBuffers/IMessage.cs +++ b/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<T> 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. diff --git a/csharp/ProtocolBuffers/ProtocolBuffers.csproj b/csharp/ProtocolBuffers/ProtocolBuffers.csproj index e3c22ac5..c22720f4 100644 --- a/csharp/ProtocolBuffers/ProtocolBuffers.csproj +++ b/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> diff --git a/csharp/ProtocolBuffers/TextFormat.cs b/csharp/ProtocolBuffers/TextFormat.cs index b574c56d..e789d0d7 100644 --- a/csharp/ProtocolBuffers/TextFormat.cs +++ b/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(); + } } } diff --git a/csharp/ProtocolBuffers/UnknownField.cs b/csharp/ProtocolBuffers/UnknownField.cs new file mode 100644 index 00000000..1866cbba --- /dev/null +++ b/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; + } + } + } +} diff --git a/csharp/ProtocolBuffers/UnknownFieldSet.cs b/csharp/ProtocolBuffers/UnknownFieldSet.cs index 787126ad..a0d84d40 100644 --- a/csharp/ProtocolBuffers/UnknownFieldSet.cs +++ b/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(); } } } diff --git a/csharp/ProtocolBuffers/WireFormat.cs b/csharp/ProtocolBuffers/WireFormat.cs index ade1ebb1..0045e369 100644 --- a/csharp/ProtocolBuffers/WireFormat.cs +++ b/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> |