diff --git a/src/ProtoGen/Helpers.cs b/src/ProtoGen/Helpers.cs index 7fb7d8fd43..6b6e316381 100644 --- a/src/ProtoGen/Helpers.cs +++ b/src/ProtoGen/Helpers.cs @@ -38,11 +38,5 @@ namespace Google.ProtocolBuffers.ProtoGen { /// Helpers to resolve class names etc. /// internal static class Helpers { - internal static void WriteNamespaces(TextGenerator writer) { - writer.WriteLine("using pb = global::Google.ProtocolBuffers;"); - writer.WriteLine("using pbc = global::Google.ProtocolBuffers.Collections;"); - writer.WriteLine("using pbd = global::Google.ProtocolBuffers.Descriptors;"); - writer.WriteLine("using scg = global::System.Collections.Generic;"); - } } } diff --git a/src/ProtoGen/MessageGenerator.cs b/src/ProtoGen/MessageGenerator.cs index 3488cdc943..907a1c0e1f 100644 --- a/src/ProtoGen/MessageGenerator.cs +++ b/src/ProtoGen/MessageGenerator.cs @@ -69,12 +69,13 @@ namespace Google.ProtocolBuffers.ProtoGen { string identifier = GetUniqueFileScopeIdentifier(Descriptor); - // The descriptor for this type. - string access = Descriptor.File.CSharpOptions.NestClasses ? "private" : "internal"; - writer.WriteLine("{0} static pbd::MessageDescriptor internal__{1}__Descriptor;", access, identifier); - writer.WriteLine("{0} static pb::FieldAccess.FieldAccessorTable<{1}, {1}.Builder> internal__{2}__FieldAccessorTable;", - access, FullClassName, identifier); - + if (!UseLiteRuntime) { + // The descriptor for this type. + string access = Descriptor.File.CSharpOptions.NestClasses ? "private" : "internal"; + writer.WriteLine("{0} static pbd::MessageDescriptor internal__{1}__Descriptor;", access, identifier); + writer.WriteLine("{0} static pb::FieldAccess.FieldAccessorTable<{1}, {1}.Builder> internal__{2}__FieldAccessorTable;", + access, FullClassName, identifier); + } // Generate static members for all nested types. foreach (MessageDescriptor nestedMessage in Descriptor.NestedTypes) { new MessageGenerator(nestedMessage).GenerateStaticVariables(writer); @@ -84,21 +85,23 @@ namespace Google.ProtocolBuffers.ProtoGen { internal void GenerateStaticVariableInitializers(TextGenerator writer) { string identifier = GetUniqueFileScopeIdentifier(Descriptor); - writer.Write("internal__{0}__Descriptor = ", identifier); - if (Descriptor.ContainingType == null) { - writer.WriteLine("Descriptor.MessageTypes[{0}];", Descriptor.Index); - } else { - writer.WriteLine("internal__{0}__Descriptor.NestedTypes[{1}];", GetUniqueFileScopeIdentifier(Descriptor.ContainingType), Descriptor.Index); - } + if (!UseLiteRuntime) { + writer.Write("internal__{0}__Descriptor = ", identifier); + if (Descriptor.ContainingType == null) { + writer.WriteLine("Descriptor.MessageTypes[{0}];", Descriptor.Index); + } else { + writer.WriteLine("internal__{0}__Descriptor.NestedTypes[{1}];", GetUniqueFileScopeIdentifier(Descriptor.ContainingType), Descriptor.Index); + } - writer.WriteLine("internal__{0}__FieldAccessorTable = ", identifier); - writer.WriteLine(" new pb::FieldAccess.FieldAccessorTable<{1}, {1}.Builder>(internal__{0}__Descriptor,", - identifier, FullClassName); - writer.Print(" new string[] { "); - foreach (FieldDescriptor field in Descriptor.Fields) { - writer.Write("\"{0}\", ", field.CSharpOptions.PropertyName); + writer.WriteLine("internal__{0}__FieldAccessorTable = ", identifier); + writer.WriteLine(" new pb::FieldAccess.FieldAccessorTable<{1}, {1}.Builder>(internal__{0}__Descriptor,", + identifier, FullClassName); + writer.Print(" new string[] { "); + foreach (FieldDescriptor field in Descriptor.Fields) { + writer.Write("\"{0}\", ", field.CSharpOptions.PropertyName); + } + writer.WriteLine("});"); } - writer.WriteLine("});"); // Generate static member initializers for all nested types. foreach (MessageDescriptor nestedMessage in Descriptor.NestedTypes) { @@ -111,8 +114,10 @@ namespace Google.ProtocolBuffers.ProtoGen { } public void Generate(TextGenerator writer) { - writer.WriteLine("{0} sealed partial class {1} : pb::{2}Message<{1}, {1}.Builder> {{", - ClassAccessLevel, ClassName, Descriptor.Proto.ExtensionRangeCount > 0 ? "Extendable" : "Generated"); + writer.WriteLine("{0} sealed partial class {1} : pb::{2}Message{3}<{1}, {1}.Builder> {{", + ClassAccessLevel, ClassName, + Descriptor.Proto.ExtensionRangeCount > 0 ? "Extendable" : "Generated", + UseLiteRuntime ? "Lite" : ""); writer.Indent(); // Must call BuildPartial() to make sure all lists are made read-only writer.WriteLine("private static readonly {0} defaultInstance = new Builder().BuildPartial();", ClassName); @@ -128,16 +133,18 @@ namespace Google.ProtocolBuffers.ProtoGen { writer.WriteLine(" get { return this; }"); writer.WriteLine("}"); writer.WriteLine(); - writer.WriteLine("public static pbd::MessageDescriptor Descriptor {"); - writer.WriteLine(" get {{ return {0}.internal__{1}__Descriptor; }}", DescriptorUtil.GetFullUmbrellaClassName(Descriptor), - GetUniqueFileScopeIdentifier(Descriptor)); - writer.WriteLine("}"); - writer.WriteLine(); - writer.WriteLine("protected override pb::FieldAccess.FieldAccessorTable<{0}, {0}.Builder> InternalFieldAccessors {{", ClassName); - writer.WriteLine(" get {{ return {0}.internal__{1}__FieldAccessorTable; }}", DescriptorUtil.GetFullUmbrellaClassName(Descriptor), - GetUniqueFileScopeIdentifier(Descriptor)); - writer.WriteLine("}"); - writer.WriteLine(); + if (!UseLiteRuntime) { + writer.WriteLine("public static pbd::MessageDescriptor Descriptor {"); + writer.WriteLine(" get {{ return {0}.internal__{1}__Descriptor; }}", DescriptorUtil.GetFullUmbrellaClassName(Descriptor), + GetUniqueFileScopeIdentifier(Descriptor)); + writer.WriteLine("}"); + writer.WriteLine(); + writer.WriteLine("protected override pb::FieldAccess.FieldAccessorTable<{0}, {0}.Builder> InternalFieldAccessors {{", ClassName); + writer.WriteLine(" get {{ return {0}.internal__{1}__FieldAccessorTable; }}", DescriptorUtil.GetFullUmbrellaClassName(Descriptor), + GetUniqueFileScopeIdentifier(Descriptor)); + writer.WriteLine("}"); + writer.WriteLine(); + } // Extensions don't need to go in an extra nested type WriteChildren(writer, null, Descriptor.Extensions); diff --git a/src/ProtoGen/SourceGeneratorBase.cs b/src/ProtoGen/SourceGeneratorBase.cs index 02f8457729..5fa420fc21 100644 --- a/src/ProtoGen/SourceGeneratorBase.cs +++ b/src/ProtoGen/SourceGeneratorBase.cs @@ -40,8 +40,16 @@ namespace Google.ProtocolBuffers.ProtoGen { private readonly T descriptor; + protected readonly bool OptimizeSpeed; + protected readonly bool OptimizeSize; + protected readonly bool UseLiteRuntime; + protected SourceGeneratorBase(T descriptor) { this.descriptor = descriptor; + + OptimizeSize = descriptor.File.Options.OptimizeFor == Google.ProtocolBuffers.DescriptorProtos.FileOptions.Types.OptimizeMode.CODE_SIZE; + OptimizeSpeed = descriptor.File.Options.OptimizeFor == Google.ProtocolBuffers.DescriptorProtos.FileOptions.Types.OptimizeMode.SPEED; + UseLiteRuntime = descriptor.File.Options.OptimizeFor == Google.ProtocolBuffers.DescriptorProtos.FileOptions.Types.OptimizeMode.LITE_RUNTIME; } protected T Descriptor { diff --git a/src/ProtoGen/UmbrellaClassGenerator.cs b/src/ProtoGen/UmbrellaClassGenerator.cs index 6f987048e7..3c643c3236 100644 --- a/src/ProtoGen/UmbrellaClassGenerator.cs +++ b/src/ProtoGen/UmbrellaClassGenerator.cs @@ -83,7 +83,11 @@ namespace Google.ProtocolBuffers.ProtoGen { new MessageGenerator(message).GenerateStaticVariables(writer); } writer.WriteLine("#endregion"); - WriteDescriptor(writer); + if (!UseLiteRuntime) { + WriteDescriptor(writer); + } else { + WriteLiteExtensions(writer); + } // The class declaration either gets closed before or after the children are written. if (!Descriptor.CSharpOptions.NestClasses) { writer.Outdent(); @@ -111,7 +115,12 @@ namespace Google.ProtocolBuffers.ProtoGen { private void WriteIntroduction(TextGenerator writer) { writer.WriteLine("// Generated by the protocol buffer compiler. DO NOT EDIT!"); writer.WriteLine(); - Helpers.WriteNamespaces(writer); + writer.WriteLine("using pb = global::Google.ProtocolBuffers;"); + if (!UseLiteRuntime) { + writer.WriteLine("using pbc = global::Google.ProtocolBuffers.Collections;"); + writer.WriteLine("using pbd = global::Google.ProtocolBuffers.Descriptors;"); + } + writer.WriteLine("using scg = global::System.Collections.Generic;"); if (Descriptor.CSharpOptions.Namespace != "") { writer.WriteLine("namespace {0} {{", Descriptor.CSharpOptions.Namespace); @@ -211,5 +220,23 @@ namespace Google.ProtocolBuffers.ProtoGen { writer.WriteLine("#endregion"); writer.WriteLine(); } + + private void WriteLiteExtensions(TextGenerator writer) { + writer.WriteLine("#region Extensions"); + + writer.WriteLine("static {0}() {{", Descriptor.CSharpOptions.UmbrellaClassname); + writer.Indent(); + + foreach (MessageDescriptor message in Descriptor.MessageTypes) { + new MessageGenerator(message).GenerateStaticVariableInitializers(writer); + } + foreach (FieldDescriptor extension in Descriptor.Extensions) { + new ExtensionGenerator(extension).GenerateStaticVariableInitializers(writer); + } + writer.Outdent(); + writer.WriteLine("}"); + writer.WriteLine("#endregion"); + writer.WriteLine(); + } } } diff --git a/src/ProtocolBuffers/CodedInputStream.cs b/src/ProtocolBuffers/CodedInputStream.cs index df177161f6..922957f20c 100644 --- a/src/ProtocolBuffers/CodedInputStream.cs +++ b/src/ProtocolBuffers/CodedInputStream.cs @@ -36,9 +36,7 @@ using System; using System.Collections.Generic; using System.IO; using System.Text; -#if !LITE using Google.ProtocolBuffers.Descriptors; -#endif namespace Google.ProtocolBuffers { @@ -363,7 +361,7 @@ namespace Google.ProtocolBuffers { public long ReadSInt64() { return DecodeZigZag64(ReadRawVarint64()); } -#if !LITE + /// /// Reads a field of any primitive type. Enums, groups and embedded /// messages are not handled by this method. @@ -397,7 +395,6 @@ namespace Google.ProtocolBuffers { throw new ArgumentOutOfRangeException("Invalid field type " + fieldType); } } -#endif #endregion #region Underlying reading primitives diff --git a/src/ProtocolBuffers/CodedOutputStream.cs b/src/ProtocolBuffers/CodedOutputStream.cs index b261375c19..264ca6ef4f 100644 --- a/src/ProtocolBuffers/CodedOutputStream.cs +++ b/src/ProtocolBuffers/CodedOutputStream.cs @@ -35,9 +35,8 @@ using System; using System.IO; using System.Text; -#if !LITE using Google.ProtocolBuffers.Descriptors; -#endif + namespace Google.ProtocolBuffers { /// @@ -279,7 +278,6 @@ namespace Google.ProtocolBuffers { WriteTag(WireFormat.MessageSetField.Item, WireFormat.WireType.EndGroup); } -#if !LITE public void WriteField(FieldType fieldType, int fieldNumber, object value) { switch (fieldType) { case FieldType.Double: WriteDouble(fieldNumber, (double)value); break; @@ -291,15 +289,15 @@ namespace Google.ProtocolBuffers { case FieldType.Fixed32: WriteFixed32(fieldNumber, (uint)value); break; case FieldType.Bool: WriteBool(fieldNumber, (bool)value); break; case FieldType.String: WriteString(fieldNumber, (string)value); break; - case FieldType.Group: WriteGroup(fieldNumber, (IMessage)value); break; - case FieldType.Message: WriteMessage(fieldNumber, (IMessage)value); break; + case FieldType.Group: WriteGroup(fieldNumber, (IMessageLite)value); break; + case FieldType.Message: WriteMessage(fieldNumber, (IMessageLite)value); break; case FieldType.Bytes: WriteBytes(fieldNumber, (ByteString)value); break; case FieldType.UInt32: WriteUInt32(fieldNumber, (uint)value); break; case FieldType.SFixed32: WriteSFixed32(fieldNumber, (int)value); break; 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, ((EnumValueDescriptor)value).Number); + case FieldType.Enum: WriteEnum(fieldNumber, ((IEnumLite)value).Number); break; } } @@ -315,19 +313,18 @@ namespace Google.ProtocolBuffers { case FieldType.Fixed32: WriteFixed32NoTag((uint)value); break; case FieldType.Bool: WriteBoolNoTag((bool)value); break; case FieldType.String: WriteStringNoTag((string)value); break; - case FieldType.Group: WriteGroupNoTag((IMessage)value); break; - case FieldType.Message: WriteMessageNoTag((IMessage)value); break; + case FieldType.Group: WriteGroupNoTag((IMessageLite)value); break; + case FieldType.Message: WriteMessageNoTag((IMessageLite)value); break; case FieldType.Bytes: WriteBytesNoTag((ByteString)value); break; case FieldType.UInt32: WriteUInt32NoTag((uint)value); break; case FieldType.SFixed32: WriteSFixed32NoTag((int)value); break; case FieldType.SFixed64: WriteSFixed64NoTag((long)value); break; case FieldType.SInt32: WriteSInt32NoTag((int)value); break; case FieldType.SInt64: WriteSInt64NoTag((long)value); break; - case FieldType.Enum: WriteEnumNoTag(((EnumValueDescriptor)value).Number); + case FieldType.Enum: WriteEnumNoTag(((IEnumLite)value).Number); break; } } -#endif #endregion #region Writing of values without tags @@ -995,7 +992,6 @@ namespace Google.ProtocolBuffers { return 10; } -#if !LITE /// /// Compute the number of bytes that would be needed to encode a /// field of arbitrary type, including the tag, to the stream. @@ -1011,15 +1007,15 @@ namespace Google.ProtocolBuffers { case FieldType.Fixed32: return ComputeFixed32Size(fieldNumber, (uint)value); case FieldType.Bool: return ComputeBoolSize(fieldNumber, (bool)value); case FieldType.String: return ComputeStringSize(fieldNumber, (string)value); - case FieldType.Group: return ComputeGroupSize(fieldNumber, (IMessage)value); - case FieldType.Message: return ComputeMessageSize(fieldNumber, (IMessage)value); + case FieldType.Group: return ComputeGroupSize(fieldNumber, (IMessageLite)value); + case FieldType.Message: return ComputeMessageSize(fieldNumber, (IMessageLite)value); case FieldType.Bytes: return ComputeBytesSize(fieldNumber, (ByteString)value); case FieldType.UInt32: return ComputeUInt32Size(fieldNumber, (uint)value); case FieldType.SFixed32: return ComputeSFixed32Size(fieldNumber, (int)value); 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, ((EnumValueDescriptor)value).Number); + case FieldType.Enum: return ComputeEnumSize(fieldNumber, ((IEnumLite)value).Number); default: throw new ArgumentOutOfRangeException("Invalid field type " + fieldType); } @@ -1040,20 +1036,19 @@ namespace Google.ProtocolBuffers { case FieldType.Fixed32: return ComputeFixed32SizeNoTag((uint)value); case FieldType.Bool: return ComputeBoolSizeNoTag((bool)value); case FieldType.String: return ComputeStringSizeNoTag((string)value); - case FieldType.Group: return ComputeGroupSizeNoTag((IMessage)value); - case FieldType.Message: return ComputeMessageSizeNoTag((IMessage)value); + case FieldType.Group: return ComputeGroupSizeNoTag((IMessageLite)value); + case FieldType.Message: return ComputeMessageSizeNoTag((IMessageLite)value); case FieldType.Bytes: return ComputeBytesSizeNoTag((ByteString)value); case FieldType.UInt32: return ComputeUInt32SizeNoTag((uint)value); case FieldType.SFixed32: return ComputeSFixed32SizeNoTag((int)value); case FieldType.SFixed64: return ComputeSFixed64SizeNoTag((long)value); case FieldType.SInt32: return ComputeSInt32SizeNoTag((int)value); case FieldType.SInt64: return ComputeSInt64SizeNoTag((long)value); - case FieldType.Enum: return ComputeEnumSizeNoTag(((EnumValueDescriptor)value).Number); + case FieldType.Enum: return ComputeEnumSizeNoTag(((IEnumLite)value).Number); default: throw new ArgumentOutOfRangeException("Invalid field type " + fieldType); } } -#endif /// /// Compute the number of bytes that would be needed to encode a tag. diff --git a/src/ProtocolBuffers/Descriptors/EnumDescriptor.cs b/src/ProtocolBuffers/Descriptors/EnumDescriptor.cs index 2fa18d9ab1..c121c7e61b 100644 --- a/src/ProtocolBuffers/Descriptors/EnumDescriptor.cs +++ b/src/ProtocolBuffers/Descriptors/EnumDescriptor.cs @@ -37,7 +37,7 @@ namespace Google.ProtocolBuffers.Descriptors { /// /// Descriptor for an enum type in a .proto file. /// - public sealed class EnumDescriptor : IndexedDescriptorBase { + public sealed class EnumDescriptor : IndexedDescriptorBase, IEnumLiteMap { private readonly MessageDescriptor containingType; private readonly IList values; @@ -72,11 +72,18 @@ namespace Google.ProtocolBuffers.Descriptors { get { return values; } } + /// + /// Logic moved from FieldSet to continue current behavior + /// + public bool IsValidValue(IEnumLite value) { + return value is EnumValueDescriptor && ((EnumValueDescriptor)value).EnumDescriptor == this; + } + /// /// Finds an enum value by number. If multiple enum values have the /// same number, this returns the first defined value with that number. /// - internal EnumValueDescriptor FindValueByNumber(int number) { + public EnumValueDescriptor FindValueByNumber(int number) { return File.DescriptorPool.FindEnumValueByNumber(this, number); } diff --git a/src/ProtocolBuffers/Descriptors/EnumValueDescriptor.cs b/src/ProtocolBuffers/Descriptors/EnumValueDescriptor.cs index 4125814d12..732dec069f 100644 --- a/src/ProtocolBuffers/Descriptors/EnumValueDescriptor.cs +++ b/src/ProtocolBuffers/Descriptors/EnumValueDescriptor.cs @@ -36,7 +36,7 @@ namespace Google.ProtocolBuffers.Descriptors { /// /// Descriptor for a single enum value within an enum in a .proto file. /// - public sealed class EnumValueDescriptor : IndexedDescriptorBase { + public sealed class EnumValueDescriptor : IndexedDescriptorBase, IEnumLite { private readonly EnumDescriptor enumDescriptor; diff --git a/src/ProtocolBuffers/Descriptors/FieldDescriptor.cs b/src/ProtocolBuffers/Descriptors/FieldDescriptor.cs index 7d99ed2168..854b3a89eb 100644 --- a/src/ProtocolBuffers/Descriptors/FieldDescriptor.cs +++ b/src/ProtocolBuffers/Descriptors/FieldDescriptor.cs @@ -40,7 +40,7 @@ namespace Google.ProtocolBuffers.Descriptors { /// /// Descriptor for a field or extension within a message in a .proto file. /// - public sealed class FieldDescriptor : IndexedDescriptorBase, IComparable { + public sealed class FieldDescriptor : IndexedDescriptorBase, IComparable, IFieldDescriptorLite { private readonly MessageDescriptor extensionScope; private EnumDescriptor enumType; @@ -299,9 +299,26 @@ namespace Google.ProtocolBuffers.Descriptors { } return FieldNumber - other.FieldNumber; } - /// + /// Compares this descriptor with another one, ordering in "canonical" order + /// which simply means ascending order by field number. + /// must be a field of the same type, i.e. the of + /// both fields must be the same. + /// + public int CompareTo(IFieldDescriptorLite other) { + return FieldNumber - other.FieldNumber; + } + + IEnumLiteMap IFieldDescriptorLite.EnumType { + get { return EnumType; } + } + + bool IFieldDescriptorLite.MessageSetWireFormat { + get { return ContainingType.Options.MessageSetWireFormat; } + } + + /// /// For enum fields, returns the field's type. /// public EnumDescriptor EnumType { diff --git a/src/ProtocolBuffers/DynamicMessage.cs b/src/ProtocolBuffers/DynamicMessage.cs index 12f2186cf4..34a50c5b2d 100644 --- a/src/ProtocolBuffers/DynamicMessage.cs +++ b/src/ProtocolBuffers/DynamicMessage.cs @@ -180,7 +180,7 @@ namespace Google.ProtocolBuffers { } public override IDictionary AllFields { - get { return fields.AllFields; } + get { return fields.AllFieldDescriptors; } } public override bool HasField(FieldDescriptor field) { @@ -216,7 +216,7 @@ namespace Google.ProtocolBuffers { } public bool Initialized { - get { return fields.IsInitializedWithRespectTo(type); } + get { return fields.IsInitializedWithRespectTo(type.Fields); } } public override void WriteTo(CodedOutputStream output) { @@ -335,7 +335,7 @@ namespace Google.ProtocolBuffers { } public override bool IsInitialized { - get { return fields.IsInitializedWithRespectTo(type); } + get { return fields.IsInitializedWithRespectTo(type.Fields); } } public override Builder MergeFrom(CodedInputStream input, ExtensionRegistry extensionRegistry) { @@ -354,7 +354,7 @@ namespace Google.ProtocolBuffers { } public override IDictionary AllFields { - get { return fields.AllFields; } + get { return fields.AllFieldDescriptors; } } public override IBuilder CreateBuilderForField(FieldDescriptor field) { diff --git a/src/ProtocolBuffers/EnumLite.cs b/src/ProtocolBuffers/EnumLite.cs new file mode 100644 index 0000000000..0c6c9afb9d --- /dev/null +++ b/src/ProtocolBuffers/EnumLite.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Google.ProtocolBuffers { + + /// + ///Interface for an enum value or value descriptor, to be used in FieldSet. + ///The lite library stores enum values directly in FieldSets but the full + ///library stores EnumValueDescriptors in order to better support reflection. + /// + public interface IEnumLite { + int Number { get; } + } + + /// + ///Interface for an object which maps integers to {@link EnumLite}s. + ///{@link Descriptors.EnumDescriptor} implements this interface by mapping + ///numbers to {@link Descriptors.EnumValueDescriptor}s. Additionally, + ///every generated enum type has a static method internalGetValueMap() which + ///returns an implementation of this type that maps numbers to enum values. + /// + public interface IEnumLiteMap : IEnumLiteMap + where T : IEnumLite { + T FindValueByNumber(int number); + } + + public interface IEnumLiteMap { + bool IsValidValue(IEnumLite value); + } +} diff --git a/src/ProtocolBuffers/ExtendableMessage.cs b/src/ProtocolBuffers/ExtendableMessage.cs index 4450f4f9c0..c67b5a1d04 100644 --- a/src/ProtocolBuffers/ExtendableMessage.cs +++ b/src/ProtocolBuffers/ExtendableMessage.cs @@ -102,8 +102,8 @@ namespace Google.ProtocolBuffers { public override IDictionary AllFields { get { IDictionary result = GetMutableFieldMap(); - foreach(KeyValuePair entry in extensions.AllFields) { - result[entry.Key] = entry.Value; + foreach(KeyValuePair entry in extensions.AllFields) { + result[(FieldDescriptor)entry.Key] = entry.Value; } return Dictionaries.AsReadOnly(result); } @@ -173,9 +173,9 @@ namespace Google.ProtocolBuffers { /// TODO(jonskeet): See if we can improve this in terms of readability. /// protected class ExtensionWriter { - readonly IEnumerator> iterator; + readonly IEnumerator> iterator; readonly FieldSet extensions; - KeyValuePair? next = null; + KeyValuePair? next = null; internal ExtensionWriter(ExtendableMessage message) { extensions = message.extensions; diff --git a/src/ProtocolBuffers/FieldSet.cs b/src/ProtocolBuffers/FieldSet.cs index 7c373b95ca..3ac3e3d303 100644 --- a/src/ProtocolBuffers/FieldSet.cs +++ b/src/ProtocolBuffers/FieldSet.cs @@ -39,6 +39,20 @@ using Google.ProtocolBuffers.Collections; using Google.ProtocolBuffers.Descriptors; namespace Google.ProtocolBuffers { + + public interface IFieldDescriptorLite : IComparable { + bool IsRepeated { get; } + bool IsRequired { get; } + bool IsPacked { get; } + bool IsExtension { get; } + bool MessageSetWireFormat { get; } //field.ContainingType.Options.MessageSetWireFormat + int FieldNumber { get; } + IEnumLiteMap EnumType { get; } + FieldType FieldType { get; } + MappedType MappedType { get; } + object DefaultValue { get; } + } + /// /// A class which represents an arbitrary set of fields of some message type. /// This is used to implement DynamicMessage, and also to represent extensions @@ -56,17 +70,17 @@ namespace Google.ProtocolBuffers { /// internal sealed class FieldSet { - private static readonly FieldSet defaultInstance = new FieldSet(new Dictionary()).MakeImmutable(); + private static readonly FieldSet defaultInstance = new FieldSet(new Dictionary()).MakeImmutable(); - private IDictionary fields; + private IDictionary fields; - private FieldSet(IDictionary fields) { + private FieldSet(IDictionary fields) { this.fields = fields; } public static FieldSet CreateInstance() { // Use SortedList to keep fields in the canonical order - return new FieldSet(new SortedList()); + return new FieldSet(new SortedList()); } /// @@ -85,8 +99,8 @@ namespace Google.ProtocolBuffers { } if (hasRepeats) { - var tmp = new SortedList(); - foreach (KeyValuePair entry in fields) { + var tmp = new SortedList(); + foreach (KeyValuePair entry in fields) { IList list = entry.Value as IList; tmp[entry.Key] = list == null ? entry.Value : Lists.AsReadOnly(list); } @@ -110,14 +124,26 @@ namespace Google.ProtocolBuffers { /// is immutable, the entries may not be (i.e. any repeated values are represented by /// mutable lists). The behaviour is not specified if the contents are mutated. /// - internal IDictionary AllFields { + internal IDictionary AllFields { get { return Dictionaries.AsReadOnly(fields); } } - +#if !LITE + /// + /// Force coercion to full descriptor dictionary. + /// + internal IDictionary AllFieldDescriptors { + get { + SortedList copy = new SortedList(); + foreach (KeyValuePair fd in fields) + copy.Add((Descriptors.FieldDescriptor)fd.Key, fd.Value); + return Dictionaries.AsReadOnly(copy); + } + } +#endif /// - /// See . + /// See . /// - public bool HasField(FieldDescriptor field) { + public bool HasField(IFieldDescriptorLite field) { if (field.IsRepeated) { throw new ArgumentException("HasField() can only be called on non-repeated fields."); } @@ -133,7 +159,7 @@ namespace Google.ProtocolBuffers { } /// - /// See + /// See /// /// /// If the field is not set, the behaviour when fetching this property varies by field type: @@ -153,7 +179,7 @@ namespace Google.ProtocolBuffers { /// to ensure it is of an appropriate type. /// /// - internal object this[FieldDescriptor field] { + internal object this[IFieldDescriptorLite field] { get { object result; if (fields.TryGetValue(field, out result)) { @@ -191,9 +217,9 @@ namespace Google.ProtocolBuffers { } /// - /// See + /// See /// - internal object this[FieldDescriptor field, int index] { + internal object this[IFieldDescriptorLite field, int index] { get { if (!field.IsRepeated) { throw new ArgumentException("Indexer specifying field and index can only be called on repeated fields."); @@ -217,7 +243,7 @@ namespace Google.ProtocolBuffers { /// /// See /// - internal void AddRepeatedField(FieldDescriptor field, object value) { + internal void AddRepeatedField(IFieldDescriptorLite field, object value) { if (!field.IsRepeated) { throw new ArgumentException("AddRepeatedField can only be called on repeated fields."); } @@ -233,12 +259,12 @@ namespace Google.ProtocolBuffers { /// /// Returns an enumerator for the field map. Used to write the fields out. /// - internal IEnumerator> GetEnumerator() { + internal IEnumerator> GetEnumerator() { return fields.GetEnumerator(); } /// - /// See + /// See /// /// /// Since FieldSet itself does not have any way of knowing about @@ -248,17 +274,17 @@ namespace Google.ProtocolBuffers { /// internal bool IsInitialized { get { - foreach (KeyValuePair entry in fields) { - FieldDescriptor field = entry.Key; + foreach (KeyValuePair entry in fields) { + IFieldDescriptorLite field = entry.Key; if (field.MappedType == MappedType.Message) { if (field.IsRepeated) { - foreach(IMessage message in (IEnumerable) entry.Value) { + foreach(IMessageLite message in (IEnumerable) entry.Value) { if (!message.IsInitialized) { return false; } } } else { - if (!((IMessage) entry.Value).IsInitialized) { + if (!((IMessageLite)entry.Value).IsInitialized) { return false; } } @@ -273,8 +299,8 @@ namespace Google.ProtocolBuffers { /// descriptor are present in this field set, as well as whether /// all the embedded messages are themselves initialized. /// - internal bool IsInitializedWithRespectTo(MessageDescriptor type) { - foreach (FieldDescriptor field in type.Fields) { + internal bool IsInitializedWithRespectTo(IEnumerable typeFields) { + foreach (IFieldDescriptorLite field in typeFields) { if (field.IsRequired && !HasField(field)) { return false; } @@ -285,14 +311,14 @@ namespace Google.ProtocolBuffers { /// /// See /// - public void ClearField(FieldDescriptor field) { + public void ClearField(IFieldDescriptorLite field) { fields.Remove(field); } /// - /// See + /// See /// - public int GetRepeatedFieldCount(FieldDescriptor field) { + public int GetRepeatedFieldCount(IFieldDescriptorLite field) { if (!field.IsRepeated) { throw new ArgumentException("GetRepeatedFieldCount() can only be called on repeated fields."); } @@ -300,64 +326,63 @@ namespace Google.ProtocolBuffers { return ((IList) this[field]).Count; } +#if !LITE + /// + /// See + /// + public void MergeFrom(IMessage other) { + foreach (KeyValuePair fd in other.AllFields) + MergeField(fd.Key, fd.Value); + } +#endif + /// /// Implementation of both MergeFrom methods. /// /// - private void MergeFields(IEnumerable> otherFields) { + public void MergeFrom(FieldSet other) { // 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 IMessage interface itself cannot enforce immutability of + // the IMessageLite 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 entry in otherFields) { - FieldDescriptor field = entry.Key; - object existingValue; - fields.TryGetValue(field, out existingValue); - if (field.IsRepeated) { - if (existingValue == null) { - existingValue = new List(); - fields[field] = existingValue; - } - IList list = (IList) existingValue; - foreach (object otherValue in (IEnumerable) entry.Value) { - list.Add(otherValue); - } - } else if (field.MappedType == MappedType.Message && existingValue != null) { - IMessage existingMessage = (IMessage)existingValue; - IMessage merged = existingMessage.WeakToBuilder() - .WeakMergeFrom((IMessage) entry.Value) - .WeakBuild(); - this[field] = merged; - } else { - this[field] = entry.Value; - } + foreach (KeyValuePair entry in other.fields) { + MergeField(entry.Key, entry.Value); } } - /// - /// See - /// - public void MergeFrom(IMessage other) { - MergeFields(other.AllFields); - } - - /// - /// Like , but merges from another FieldSet. - /// - public void MergeFrom(FieldSet other) { - MergeFields(other.fields); + private void MergeField(IFieldDescriptorLite field, object mergeValue) { + object existingValue; + fields.TryGetValue(field, out existingValue); + if (field.IsRepeated) { + if (existingValue == null) { + existingValue = new List(); + fields[field] = existingValue; + } + IList list = (IList) existingValue; + foreach (object otherValue in (IEnumerable)mergeValue) { + list.Add(otherValue); + } + } else if (field.MappedType == MappedType.Message && existingValue != null) { + IMessageLite existingMessage = (IMessageLite)existingValue; + IMessageLite merged = existingMessage.WeakToBuilder() + .WeakMergeFrom((IMessageLite)mergeValue) + .WeakBuild(); + this[field] = merged; + } else { + this[field] = mergeValue; + } } /// - /// See . + /// See . /// public void WriteTo(CodedOutputStream output) { - foreach (KeyValuePair entry in fields) { + foreach (KeyValuePair entry in fields) { WriteField(entry.Key, entry.Value, output); } } @@ -365,9 +390,9 @@ namespace Google.ProtocolBuffers { /// /// Writes a single field to a CodedOutputStream. /// - public void WriteField(FieldDescriptor field, Object value, CodedOutputStream output) { - if (field.IsExtension && field.ContainingType.Options.MessageSetWireFormat) { - output.WriteMessageSetExtension(field.FieldNumber, (IMessage) value); + public void WriteField(IFieldDescriptorLite field, Object value, CodedOutputStream output) { + if (field.IsExtension && field.MessageSetWireFormat) { + output.WriteMessageSetExtension(field.FieldNumber, (IMessageLite) value); } else { if (field.IsRepeated) { IEnumerable valueList = (IEnumerable) value; @@ -395,18 +420,18 @@ namespace Google.ProtocolBuffers { } /// - /// See . It's up to the caller to + /// See . It's up to the caller to /// cache the resulting size if desired. /// public int SerializedSize { get { int size = 0; - foreach (KeyValuePair entry in fields) { - FieldDescriptor field = entry.Key; + foreach (KeyValuePair entry in fields) { + IFieldDescriptorLite field = entry.Key; object value = entry.Value; - if (field.IsExtension && field.ContainingType.Options.MessageSetWireFormat) { - size += CodedOutputStream.ComputeMessageSetExtensionSize(field.FieldNumber, (IMessage)value); + if (field.IsExtension && field.MessageSetWireFormat) { + size += CodedOutputStream.ComputeMessageSetExtensionSize(field.FieldNumber, (IMessageLite)value); } else { if (field.IsRepeated) { IEnumerable valueList = (IEnumerable)value; @@ -440,7 +465,7 @@ namespace Google.ProtocolBuffers { /// /// The value is not of the right type. /// The value is null. - private static void VerifyType(FieldDescriptor field, object value) { + private static void VerifyType(IFieldDescriptorLite field, object value) { ThrowHelper.ThrowIfNull(value, "value"); bool isValid = false; switch (field.MappedType) { @@ -454,12 +479,15 @@ namespace Google.ProtocolBuffers { case MappedType.String: isValid = value is string; break; case MappedType.ByteString: isValid = value is ByteString; break; case MappedType.Enum: - EnumValueDescriptor enumValue = value as EnumValueDescriptor; - isValid = enumValue != null && enumValue.EnumDescriptor == field.EnumType; + IEnumLite enumValue = value as IEnumLite; + isValid = enumValue != null && field.EnumType.IsValidValue(enumValue); break; case MappedType.Message: - IMessage messageValue = value as IMessage; - isValid = messageValue != null && messageValue.DescriptorForType == field.MessageType; + IMessageLite messageValue = value as IMessageLite; + isValid = messageValue != null; +#if !LITE + isValid = isValid && ((IMessage)messageValue).DescriptorForType == ((Google.ProtocolBuffers.Descriptors.FieldDescriptor)field).MessageType; +#endif break; } @@ -468,10 +496,16 @@ namespace Google.ProtocolBuffers { // the stack trace which exact call failed, since the whole chain is // considered one line of code. So, let's make sure to include the // field name and other useful info in the exception. - throw new ArgumentException("Wrong object type used with protocol message reflection. " - + "Message type \"" + field.ContainingType.FullName - + "\", field \"" + (field.IsExtension ? field.FullName : field.Name) - + "\", value was type \"" + value.GetType().Name + "\"."); + string message = "Wrong object type used with protocol message reflection."; +#if !LITE + Google.ProtocolBuffers.Descriptors.FieldDescriptor fieldinfo = field as Google.ProtocolBuffers.Descriptors.FieldDescriptor; + if (fieldinfo != null) { + message += "Message type \"" + fieldinfo.ContainingType.FullName; + message += "\", field \"" + (fieldinfo.IsExtension ? fieldinfo.FullName : fieldinfo.Name); + message += "\", value was type \"" + value.GetType().Name + "\"."; + } +#endif + throw new ArgumentException(message); } } } diff --git a/src/ProtocolBuffers/GeneratedExtensionLite.cs b/src/ProtocolBuffers/GeneratedExtensionLite.cs index 9b3cf504fe..da07fb8cb3 100644 --- a/src/ProtocolBuffers/GeneratedExtensionLite.cs +++ b/src/ProtocolBuffers/GeneratedExtensionLite.cs @@ -1,4 +1,5 @@ using System; +using Google.ProtocolBuffers.Descriptors; namespace Google.ProtocolBuffers { @@ -7,4 +8,111 @@ namespace Google.ProtocolBuffers { object ContainingType { get; } IMessageLite MessageDefaultInstance { get; } } + + public class ExtensionDescriptorLite { + private readonly EnumLiteMap enumTypeMap; + private readonly int number; + private readonly FieldType type; + private readonly bool isRepeated; + private readonly bool isPacked; + + public ExtensionDescriptorLite(EnumLiteMap enumTypeMap, int number, FieldType type, bool isRepeated, bool isPacked) { + this.enumTypeMap = enumTypeMap; + this.number = number; + this.type = type; + this.isRepeated = isRepeated; + this.isPacked = isPacked; + } + + public int Number { + get { return number; } + } + } + + public class EnumLiteMap { } + + public class GeneratedExtensionLite : IGeneratedExtensionLite + where TContainingType : IMessageLite { + + private readonly TContainingType containingTypeDefaultInstance; + private readonly TExtensionType defaultValue; + private readonly IMessageLite messageDefaultInstance; + private readonly ExtensionDescriptorLite descriptor; + + // We can't always initialize a GeneratedExtension when we first construct + // it due to initialization order difficulties (namely, the default + // instances may not have been constructed yet). So, we construct an + // uninitialized GeneratedExtension once, then call internalInit() on it + // later. Generated code will always call internalInit() on all extensions + // as part of the static initialization code, and internalInit() throws an + // exception if called more than once, so this method is useless to users. + protected GeneratedExtensionLite( + TContainingType containingTypeDefaultInstance, + TExtensionType defaultValue, + IMessageLite messageDefaultInstance, + ExtensionDescriptorLite descriptor) { + this.containingTypeDefaultInstance = containingTypeDefaultInstance; + this.messageDefaultInstance = messageDefaultInstance; + this.defaultValue = defaultValue; + this.descriptor = descriptor; + } + + /** For use by generated code only. */ + public GeneratedExtensionLite( + TContainingType containingTypeDefaultInstance, + TExtensionType defaultValue, + IMessageLite messageDefaultInstance, + EnumLiteMap enumTypeMap, + int number, + FieldType type) + : this(containingTypeDefaultInstance, defaultValue, messageDefaultInstance, + new ExtensionDescriptorLite(enumTypeMap, number, type, + false /* isRepeated */, false /* isPacked */)) { + } + + /** For use by generated code only. */ + public GeneratedExtensionLite( + TContainingType containingTypeDefaultInstance, + TExtensionType defaultValue, + IMessageLite messageDefaultInstance, + EnumLiteMap enumTypeMap, + int number, + FieldType type, + bool isPacked) + : this(containingTypeDefaultInstance, defaultValue, messageDefaultInstance, + new ExtensionDescriptorLite(enumTypeMap, number, type, + true /* isRepeated */, isPacked)) { + } + + /// + /// used for the extension registry + /// + object IGeneratedExtensionLite.ContainingType { + get { return ContainingTypeDefaultInstance; } + } + /** + * Default instance of the type being extended, used to identify that type. + */ + public TContainingType ContainingTypeDefaultInstance { + get { + return containingTypeDefaultInstance; + } + } + + /** Get the field number. */ + public int Number { + get { + return descriptor.Number; + } + } + /** + * If the extension is an embedded message, this is the default instance of + * that type. + */ + public IMessageLite MessageDefaultInstance { + get { + return messageDefaultInstance; + } + } + } } \ No newline at end of file diff --git a/src/ProtocolBuffers/ProtocolBuffers.csproj b/src/ProtocolBuffers/ProtocolBuffers.csproj index 59dfaa08aa..dece65aff4 100644 --- a/src/ProtocolBuffers/ProtocolBuffers.csproj +++ b/src/ProtocolBuffers/ProtocolBuffers.csproj @@ -90,6 +90,7 @@ + diff --git a/src/ProtocolBuffers/ProtocolBuffersLite.csproj b/src/ProtocolBuffers/ProtocolBuffersLite.csproj index a08b024b79..d3d60b242d 100644 --- a/src/ProtocolBuffers/ProtocolBuffersLite.csproj +++ b/src/ProtocolBuffers/ProtocolBuffersLite.csproj @@ -46,6 +46,8 @@ + + @@ -55,7 +57,17 @@ + + + + + + + + + + @@ -66,6 +78,7 @@ Code + diff --git a/src/ProtocolBuffersLite.Test/ProtocolBuffersLite.Test.csproj b/src/ProtocolBuffersLite.Test/ProtocolBuffersLite.Test.csproj index 19bd4b6cbb..794b3fddc2 100644 --- a/src/ProtocolBuffersLite.Test/ProtocolBuffersLite.Test.csproj +++ b/src/ProtocolBuffersLite.Test/ProtocolBuffersLite.Test.csproj @@ -54,9 +54,13 @@ + + UnitTestLiteProtoFile.cs + Properties\AssemblyInfo.cs +