From 59eeebee870332ea2b9085688ba524c69311f662 Mon Sep 17 00:00:00 2001 From: Jon Skeet Date: Fri, 17 Jul 2015 08:26:04 +0100 Subject: First pass at the big rename from ProtocolBuffers to Google.Protobuf. We'll see what I've missed when CI fails... --- csharp/src/Google.Protobuf/IMessage.cs | 144 +++++++++++++++++++++++++++++++++ 1 file changed, 144 insertions(+) create mode 100644 csharp/src/Google.Protobuf/IMessage.cs (limited to 'csharp/src/Google.Protobuf/IMessage.cs') diff --git a/csharp/src/Google.Protobuf/IMessage.cs b/csharp/src/Google.Protobuf/IMessage.cs new file mode 100644 index 00000000..8c4a4aaf --- /dev/null +++ b/csharp/src/Google.Protobuf/IMessage.cs @@ -0,0 +1,144 @@ +#region Copyright notice and license +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#endregion + +using System; +using Google.Protobuf.Reflection; + +namespace Google.Protobuf +{ + + // TODO(jonskeet): Do we want a "weak" (non-generic) version of IReflectedMessage? + // TODO(jonskeet): Split these interfaces into separate files when we're happy with them. + + /// + /// Reflection support for accessing field values. + /// + public interface IReflectedMessage : IMessage + { + FieldAccessorTable Fields { get; } + // TODO(jonskeet): Descriptor? Or a single property which has "all you need for reflection"? + } + + /// + /// Interface for a Protocol Buffers message, supporting + /// basic operations required for serialization. + /// + public interface IMessage + { + /// + /// Merges the data from the specified coded input stream with the current message. + /// + /// See the user guide for precise merge semantics. + /// + void MergeFrom(CodedInputStream input); + + /// + /// Writes the data to the given coded output stream. + /// + /// Coded output stream to write the data to. Must not be null. + void WriteTo(CodedOutputStream output); + + /// + /// Calculates the size of this message in Protocol Buffer wire format, in bytes. + /// + /// The number of bytes required to write this message + /// to a coded output stream. + int CalculateSize(); + } + + /// + /// Generic interface for a Protocol Buffers message, + /// where the type parameter is expected to be the same type as + /// the implementation class. + /// + /// The message type. + public interface IMessage : IReflectedMessage, IEquatable, IDeepCloneable, IFreezable where T : IMessage + { + /// + /// Merges the given message into this one. + /// + /// See the user guide for precise merge semantics. + /// The message to merge with this one. Must not be null. + void MergeFrom(T message); + } + + /// + /// Generic interface for a deeply cloneable type. + /// + /// + /// All generated messages implement this interface, but so do some non-message types. + /// Additionally, due to the type constraint on T in , + /// it is simpler to keep this as a separate interface. + /// + /// + /// Freezable types which implement this interface should always return a mutable clone, + /// even if the original object is frozen. + /// + /// + /// The type itself, returned by the method. + public interface IDeepCloneable + { + /// + /// Creates a deep clone of this object. + /// + /// A deep clone of this object. + T Clone(); + } + + /// + /// Provides a mechanism for freezing a message (or repeated field collection) + /// to make it immutable. + /// + /// + /// Implementations are under no obligation to make this thread-safe: if a freezable + /// type instance is shared between threads before being frozen, and one thread then + /// freezes it, it is possible for other threads to make changes during the freezing + /// operation and also to observe stale values for mutated fields. Objects should be + /// frozen before being made available to other threads. + /// + public interface IFreezable + { + /// + /// Freezes this object. + /// + /// + /// If the object is already frozen, this method has no effect. + /// + void Freeze(); + + /// + /// Returns whether or not this object is frozen (and therefore immutable). + /// + /// true if this object is frozen; false otherwise. + bool IsFrozen { get; } + } +} -- cgit v1.2.3 From 53c399a1d65df65e9f83a70b55041a01cf8d7489 Mon Sep 17 00:00:00 2001 From: Jon Skeet Date: Mon, 20 Jul 2015 19:24:31 +0100 Subject: Revamp to reflection. Changes in brief: 1. Descriptor is now the entry point for all reflection. 2. IReflectedMessage has gone; there's now a Descriptor property in IMessage, which is explicitly implemented (due to the static property). 3. FieldAccessorTable has gone away 4. IFieldAccessor and OneofFieldAccessor still exist; we *could* put the functionality straight into FieldDescriptor and OneofDescriptor... I'm unsure about that. 5. There's a temporary property MessageDescriptor.FieldAccessorsByFieldNumber to make the test changes small - we probably want this to go away 6. Discovery for delegates is now via attributes applied to properties and the Clear method of a oneof I'm happy with 1-3. 4 I'm unsure about - feedback welcome. 5 will go away 6 I'm unsure about, both in design and implementation. Should we have a ProtobufMessageAttribute too? Should we find all the relevant attributes in MessageDescriptor and pass them down, to avoid an O(N^2) scenario? Generated code changes coming in the next commit. --- .../Google.Protobuf.Test/GeneratedMessageTest.cs | 47 ++++++----- .../Reflection/DescriptorsTest.cs | 1 + .../WellKnownTypes/WrappersTest.cs | 6 +- csharp/src/Google.Protobuf/Collections/MapField.cs | 3 + csharp/src/Google.Protobuf/Google.Protobuf.csproj | 3 +- csharp/src/Google.Protobuf/IMessage.cs | 18 ++-- csharp/src/Google.Protobuf/JsonFormatter.cs | 26 +++--- .../Google.Protobuf/Reflection/DescriptorUtil.cs | 5 +- .../Google.Protobuf/Reflection/EnumDescriptor.cs | 10 ++- .../Reflection/FieldAccessorBase.cs | 7 +- .../Reflection/FieldAccessorTable.cs | 97 ---------------------- .../Google.Protobuf/Reflection/FieldDescriptor.cs | 28 +++++++ .../Google.Protobuf/Reflection/FileDescriptor.cs | 36 ++++++-- .../Google.Protobuf/Reflection/MapFieldAccessor.cs | 3 +- .../Reflection/MessageDescriptor.cs | 34 +++++++- .../Google.Protobuf/Reflection/OneofAccessor.cs | 10 +-- .../Google.Protobuf/Reflection/OneofDescriptor.cs | 34 ++++++++ .../Reflection/ProtobufFieldAttribute.cs | 58 +++++++++++++ .../Reflection/ProtobufOneofAttribute.cs | 52 ++++++++++++ .../Google.Protobuf/Reflection/ReflectionUtil.cs | 20 +++++ .../Reflection/RepeatedFieldAccessor.cs | 3 +- .../Reflection/SingleFieldAccessor.cs | 5 +- .../protobuf/compiler/csharp/csharp_field_base.cc | 2 +- .../protobuf/compiler/csharp/csharp_helpers.h | 6 +- .../protobuf/compiler/csharp/csharp_map_field.cc | 1 + .../protobuf/compiler/csharp/csharp_message.cc | 84 ++----------------- .../protobuf/compiler/csharp/csharp_message.h | 2 - .../compiler/csharp/csharp_message_field.cc | 2 + .../compiler/csharp/csharp_primitive_field.cc | 2 + .../compiler/csharp/csharp_repeated_enum_field.cc | 3 +- .../csharp/csharp_repeated_message_field.cc | 1 + .../csharp/csharp_repeated_primitive_field.cc | 1 + .../compiler/csharp/csharp_umbrella_class.cc | 37 ++++++--- .../compiler/csharp/csharp_umbrella_class.h | 1 + .../compiler/csharp/csharp_wrapper_field.cc | 2 + 35 files changed, 372 insertions(+), 278 deletions(-) delete mode 100644 csharp/src/Google.Protobuf/Reflection/FieldAccessorTable.cs create mode 100644 csharp/src/Google.Protobuf/Reflection/ProtobufFieldAttribute.cs create mode 100644 csharp/src/Google.Protobuf/Reflection/ProtobufOneofAttribute.cs (limited to 'csharp/src/Google.Protobuf/IMessage.cs') diff --git a/csharp/src/Google.Protobuf.Test/GeneratedMessageTest.cs b/csharp/src/Google.Protobuf.Test/GeneratedMessageTest.cs index acb20b15..180976d1 100644 --- a/csharp/src/Google.Protobuf.Test/GeneratedMessageTest.cs +++ b/csharp/src/Google.Protobuf.Test/GeneratedMessageTest.cs @@ -604,7 +604,7 @@ namespace Google.Protobuf public void Reflection_GetValue() { var message = SampleMessages.CreateFullTestAllTypes(); - var fields = ((IReflectedMessage) message).Fields; + var fields = TestAllTypes.Descriptor.FieldAccessorsByFieldNumber; Assert.AreEqual(message.SingleBool, fields[TestAllTypes.SingleBoolFieldNumber].GetValue(message)); Assert.AreEqual(message.SingleBytes, fields[TestAllTypes.SingleBytesFieldNumber].GetValue(message)); Assert.AreEqual(message.SingleDouble, fields[TestAllTypes.SingleDoubleFieldNumber].GetValue(message)); @@ -639,7 +639,7 @@ namespace Google.Protobuf // Just a single map field, for the same reason var mapMessage = new TestMap { MapStringString = { { "key1", "value1" }, { "key2", "value2" } } }; - fields = ((IReflectedMessage) mapMessage).Fields; + fields = TestMap.Descriptor.FieldAccessorsByFieldNumber; var dictionary = (IDictionary) fields[TestMap.MapStringStringFieldNumber].GetValue(mapMessage); Assert.AreEqual(mapMessage.MapStringString, dictionary); Assert.AreEqual("value1", dictionary["key1"]); @@ -648,8 +648,8 @@ namespace Google.Protobuf [Test] public void Reflection_Clear() { - IReflectedMessage message = SampleMessages.CreateFullTestAllTypes(); - var fields = message.Fields; + var message = SampleMessages.CreateFullTestAllTypes(); + var fields = TestAllTypes.Descriptor.FieldAccessorsByFieldNumber; fields[TestAllTypes.SingleBoolFieldNumber].Clear(message); fields[TestAllTypes.SingleInt32FieldNumber].Clear(message); fields[TestAllTypes.SingleStringFieldNumber].Clear(message); @@ -673,7 +673,7 @@ namespace Google.Protobuf // Separately, maps. var mapMessage = new TestMap { MapStringString = { { "key1", "value1" }, { "key2", "value2" } } }; - fields = ((IReflectedMessage) mapMessage).Fields; + fields = TestMap.Descriptor.FieldAccessorsByFieldNumber; fields[TestMap.MapStringStringFieldNumber].Clear(mapMessage); Assert.AreEqual(0, mapMessage.MapStringString.Count); } @@ -682,8 +682,8 @@ namespace Google.Protobuf public void Reflection_SetValue_SingleFields() { // Just a sample (primitives, messages, enums, strings, byte strings) - IReflectedMessage message = SampleMessages.CreateFullTestAllTypes(); - var fields = message.Fields; + var message = SampleMessages.CreateFullTestAllTypes(); + var fields = TestAllTypes.Descriptor.FieldAccessorsByFieldNumber; fields[TestAllTypes.SingleBoolFieldNumber].SetValue(message, false); fields[TestAllTypes.SingleInt32FieldNumber].SetValue(message, 500); fields[TestAllTypes.SingleStringFieldNumber].SetValue(message, "It's a string"); @@ -709,51 +709,52 @@ namespace Google.Protobuf [Test] public void Reflection_SetValue_SingleFields_WrongType() { - IReflectedMessage message = SampleMessages.CreateFullTestAllTypes(); - var fields = message.Fields; + IMessage message = SampleMessages.CreateFullTestAllTypes(); + var fields = message.Descriptor.FieldAccessorsByFieldNumber; Assert.Throws(() => fields[TestAllTypes.SingleBoolFieldNumber].SetValue(message, "This isn't a bool")); } [Test] public void Reflection_SetValue_MapFields() { - IReflectedMessage message = new TestMap(); - var fields = message.Fields; + IMessage message = new TestMap(); + var fields = message.Descriptor.FieldAccessorsByFieldNumber; Assert.Throws(() => fields[TestMap.MapStringStringFieldNumber].SetValue(message, new Dictionary())); } [Test] public void Reflection_SetValue_RepeatedFields() { - IReflectedMessage message = SampleMessages.CreateFullTestAllTypes(); - var fields = message.Fields; + IMessage message = SampleMessages.CreateFullTestAllTypes(); + var fields = message.Descriptor.FieldAccessorsByFieldNumber; Assert.Throws(() => fields[TestAllTypes.RepeatedDoubleFieldNumber].SetValue(message, new double[10])); } [Test] public void Reflection_GetValue_IncorrectType() { - IReflectedMessage message = SampleMessages.CreateFullTestAllTypes(); - Assert.Throws(() => message.Fields[TestAllTypes.SingleBoolFieldNumber].GetValue(new TestMap())); + IMessage message = SampleMessages.CreateFullTestAllTypes(); + var fields = message.Descriptor.FieldAccessorsByFieldNumber; + Assert.Throws(() => fields[TestAllTypes.SingleBoolFieldNumber].GetValue(new TestMap())); } [Test] public void Reflection_Oneof() { var message = new TestAllTypes(); - var fields = ((IReflectedMessage) message).Fields; - Assert.AreEqual(1, fields.Oneofs.Count); - var oneof = fields.Oneofs[0]; - Assert.AreEqual("oneof_field", oneof.Descriptor.Name); - Assert.IsNull(oneof.GetCaseFieldDescriptor(message)); + var descriptor = TestAllTypes.Descriptor; + Assert.AreEqual(1, descriptor.Oneofs.Count); + var oneof = descriptor.Oneofs[0]; + Assert.AreEqual("oneof_field", oneof.Name); + Assert.IsNull(oneof.Accessor.GetCaseFieldDescriptor(message)); message.OneofString = "foo"; - Assert.AreSame(fields[TestAllTypes.OneofStringFieldNumber].Descriptor, oneof.GetCaseFieldDescriptor(message)); + Assert.AreSame(descriptor.FieldAccessorsByFieldNumber[TestAllTypes.OneofStringFieldNumber].Descriptor, oneof.Accessor.GetCaseFieldDescriptor(message)); message.OneofUint32 = 10; - Assert.AreSame(fields[TestAllTypes.OneofUint32FieldNumber].Descriptor, oneof.GetCaseFieldDescriptor(message)); + Assert.AreSame(descriptor.FieldAccessorsByFieldNumber[TestAllTypes.OneofUint32FieldNumber].Descriptor, oneof.Accessor.GetCaseFieldDescriptor(message)); - oneof.Clear(message); + oneof.Accessor.Clear(message); Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.None, message.OneofFieldCase); } } diff --git a/csharp/src/Google.Protobuf.Test/Reflection/DescriptorsTest.cs b/csharp/src/Google.Protobuf.Test/Reflection/DescriptorsTest.cs index 0db01a5e..0aff0a6c 100644 --- a/csharp/src/Google.Protobuf.Test/Reflection/DescriptorsTest.cs +++ b/csharp/src/Google.Protobuf.Test/Reflection/DescriptorsTest.cs @@ -62,6 +62,7 @@ namespace Google.Protobuf.Reflection Assert.AreEqual(UnittestImportProto3.Descriptor, file.Dependencies[0]); MessageDescriptor messageType = TestAllTypes.Descriptor; + Assert.AreSame(typeof(TestAllTypes), messageType.GeneratedType); Assert.AreEqual(messageType, file.MessageTypes[0]); Assert.AreEqual(messageType, file.FindTypeByName("TestAllTypes")); Assert.Null(file.FindTypeByName("NoSuchType")); diff --git a/csharp/src/Google.Protobuf.Test/WellKnownTypes/WrappersTest.cs b/csharp/src/Google.Protobuf.Test/WellKnownTypes/WrappersTest.cs index ad88c4eb..c617db36 100644 --- a/csharp/src/Google.Protobuf.Test/WellKnownTypes/WrappersTest.cs +++ b/csharp/src/Google.Protobuf.Test/WellKnownTypes/WrappersTest.cs @@ -192,7 +192,7 @@ namespace Google.Protobuf.WellKnownTypes Uint32Field = 3, Uint64Field = 4 }; - var fields = ((IReflectedMessage) message).Fields; + var fields = TestWellKnownTypes.Descriptor.FieldAccessorsByFieldNumber; Assert.AreEqual("x", fields[TestWellKnownTypes.StringFieldFieldNumber].GetValue(message)); Assert.AreEqual(ByteString.CopyFrom(1, 2, 3), fields[TestWellKnownTypes.BytesFieldFieldNumber].GetValue(message)); @@ -216,7 +216,7 @@ namespace Google.Protobuf.WellKnownTypes { // Just a single example... note that we can't have a null value here var message = new RepeatedWellKnownTypes { Int32Field = { 1, 2 } }; - var fields = ((IReflectedMessage) message).Fields; + var fields = RepeatedWellKnownTypes.Descriptor.FieldAccessorsByFieldNumber; var list = (IList) fields[RepeatedWellKnownTypes.Int32FieldFieldNumber].GetValue(message); CollectionAssert.AreEqual(new[] { 1, 2 }, list); } @@ -226,7 +226,7 @@ namespace Google.Protobuf.WellKnownTypes { // Just a single example... note that we can't have a null value here var message = new MapWellKnownTypes { Int32Field = { { 1, 2 }, { 3, null } } }; - var fields = ((IReflectedMessage) message).Fields; + var fields = MapWellKnownTypes.Descriptor.FieldAccessorsByFieldNumber; var dictionary = (IDictionary) fields[MapWellKnownTypes.Int32FieldFieldNumber].GetValue(message); Assert.AreEqual(2, dictionary[1]); Assert.IsNull(dictionary[3]); diff --git a/csharp/src/Google.Protobuf/Collections/MapField.cs b/csharp/src/Google.Protobuf/Collections/MapField.cs index f5e4fd3a..9ca5104d 100644 --- a/csharp/src/Google.Protobuf/Collections/MapField.cs +++ b/csharp/src/Google.Protobuf/Collections/MapField.cs @@ -30,6 +30,7 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #endregion +using Google.Protobuf.Reflection; using System; using System.Collections; using System.Collections.Generic; @@ -559,6 +560,8 @@ namespace Google.Protobuf.Collections { return codec.keyCodec.CalculateSizeWithTag(Key) + codec.valueCodec.CalculateSizeWithTag(Value); } + + MessageDescriptor IMessage.Descriptor { get { return null; } } } } } diff --git a/csharp/src/Google.Protobuf/Google.Protobuf.csproj b/csharp/src/Google.Protobuf/Google.Protobuf.csproj index 431668fe..50756299 100644 --- a/csharp/src/Google.Protobuf/Google.Protobuf.csproj +++ b/csharp/src/Google.Protobuf/Google.Protobuf.csproj @@ -78,7 +78,6 @@ - @@ -91,6 +90,8 @@ + + diff --git a/csharp/src/Google.Protobuf/IMessage.cs b/csharp/src/Google.Protobuf/IMessage.cs index 8c4a4aaf..d179c386 100644 --- a/csharp/src/Google.Protobuf/IMessage.cs +++ b/csharp/src/Google.Protobuf/IMessage.cs @@ -39,15 +39,6 @@ namespace Google.Protobuf // TODO(jonskeet): Do we want a "weak" (non-generic) version of IReflectedMessage? // TODO(jonskeet): Split these interfaces into separate files when we're happy with them. - /// - /// Reflection support for accessing field values. - /// - public interface IReflectedMessage : IMessage - { - FieldAccessorTable Fields { get; } - // TODO(jonskeet): Descriptor? Or a single property which has "all you need for reflection"? - } - /// /// Interface for a Protocol Buffers message, supporting /// basic operations required for serialization. @@ -73,6 +64,13 @@ namespace Google.Protobuf /// The number of bytes required to write this message /// to a coded output stream. int CalculateSize(); + + /// + /// Descriptor for this message. All instances are expected to return the same descriptor, + /// and for generated types this will be an explicitly-implemented member, returning the + /// same value as the static property declared on the type. + /// + MessageDescriptor Descriptor { get; } } /// @@ -81,7 +79,7 @@ namespace Google.Protobuf /// the implementation class. /// /// The message type. - public interface IMessage : IReflectedMessage, IEquatable, IDeepCloneable, IFreezable where T : IMessage + public interface IMessage : IMessage, IEquatable, IDeepCloneable, IFreezable where T : IMessage { /// /// Merges the given message into this one. diff --git a/csharp/src/Google.Protobuf/JsonFormatter.cs b/csharp/src/Google.Protobuf/JsonFormatter.cs index a06e6545..7f13e33e 100644 --- a/csharp/src/Google.Protobuf/JsonFormatter.cs +++ b/csharp/src/Google.Protobuf/JsonFormatter.cs @@ -118,7 +118,7 @@ namespace Google.Protobuf this.settings = settings; } - public string Format(IReflectedMessage message) + public string Format(IMessage message) { ThrowHelper.ThrowIfNull(message, "message"); StringBuilder builder = new StringBuilder(); @@ -129,7 +129,7 @@ namespace Google.Protobuf return builder.ToString(); } - private void WriteMessage(StringBuilder builder, IReflectedMessage message) + private void WriteMessage(StringBuilder builder, IMessage message) { if (message == null) { @@ -137,15 +137,15 @@ namespace Google.Protobuf return; } builder.Append("{ "); - var fields = message.Fields; + var fields = message.Descriptor.Fields; bool first = true; // First non-oneof fields - foreach (var accessor in fields.Accessors) + foreach (var field in fields) { - var descriptor = accessor.Descriptor; + var accessor = field.Accessor; // Oneofs are written later // TODO: Change to write out fields in order, interleaving oneofs appropriately (as per binary format) - if (descriptor.ContainingOneof != null) + if (field.ContainingOneof != null) { continue; } @@ -156,7 +156,7 @@ namespace Google.Protobuf continue; } // Omit awkward (single) values such as unknown enum values - if (!descriptor.IsRepeated && !descriptor.IsMap && !CanWriteSingleValue(accessor.Descriptor, value)) + if (!field.IsRepeated && !field.IsMap && !CanWriteSingleValue(accessor.Descriptor, value)) { continue; } @@ -173,15 +173,15 @@ namespace Google.Protobuf } // Now oneofs - foreach (var accessor in fields.Oneofs) + foreach (var oneof in message.Descriptor.Oneofs) { + var accessor = oneof.Accessor; var fieldDescriptor = accessor.GetCaseFieldDescriptor(message); if (fieldDescriptor == null) { continue; } - var fieldAccessor = fields[fieldDescriptor.FieldNumber]; - object value = fieldAccessor.GetValue(message); + object value = fieldDescriptor.Accessor.GetValue(message); // Omit awkward (single) values such as unknown enum values if (!fieldDescriptor.IsRepeated && !fieldDescriptor.IsMap && !CanWriteSingleValue(fieldDescriptor, value)) { @@ -194,7 +194,7 @@ namespace Google.Protobuf } WriteString(builder, ToCamelCase(fieldDescriptor.Name)); builder.Append(": "); - WriteValue(builder, fieldAccessor, value); + WriteValue(builder, fieldDescriptor.Accessor, value); first = false; } builder.Append(first ? "}" : " }"); @@ -385,7 +385,7 @@ namespace Google.Protobuf } else { - WriteMessage(builder, (IReflectedMessage) value); + WriteMessage(builder, (IMessage) value); } break; default: @@ -406,7 +406,7 @@ namespace Google.Protobuf WriteSingleValue(builder, descriptor.MessageType.FindFieldByNumber(1), value); return; } - WriteMessage(builder, (IReflectedMessage) value); + WriteMessage(builder, (IMessage) value); } private void WriteList(StringBuilder builder, IFieldAccessor accessor, IList list) diff --git a/csharp/src/Google.Protobuf/Reflection/DescriptorUtil.cs b/csharp/src/Google.Protobuf/Reflection/DescriptorUtil.cs index af31dfb1..f5570fc4 100644 --- a/csharp/src/Google.Protobuf/Reflection/DescriptorUtil.cs +++ b/csharp/src/Google.Protobuf/Reflection/DescriptorUtil.cs @@ -50,9 +50,8 @@ namespace Google.Protobuf.Reflection /// Converts the given array into a read-only list, applying the specified conversion to /// each input element. /// - internal static IList ConvertAndMakeReadOnly(IList input, - IndexedConverter - converter) + internal static IList ConvertAndMakeReadOnly + (IList input, IndexedConverter converter) { TOutput[] array = new TOutput[input.Count]; for (int i = 0; i < array.Length; i++) diff --git a/csharp/src/Google.Protobuf/Reflection/EnumDescriptor.cs b/csharp/src/Google.Protobuf/Reflection/EnumDescriptor.cs index bf8f8c83..285f26f3 100644 --- a/csharp/src/Google.Protobuf/Reflection/EnumDescriptor.cs +++ b/csharp/src/Google.Protobuf/Reflection/EnumDescriptor.cs @@ -30,6 +30,7 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #endregion +using System; using System.Collections.Generic; namespace Google.Protobuf.Reflection @@ -42,11 +43,13 @@ namespace Google.Protobuf.Reflection private readonly EnumDescriptorProto proto; private readonly MessageDescriptor containingType; private readonly IList values; + private readonly Type generatedType; - internal EnumDescriptor(EnumDescriptorProto proto, FileDescriptor file, MessageDescriptor parent, int index) + internal EnumDescriptor(EnumDescriptorProto proto, FileDescriptor file, MessageDescriptor parent, int index, Type generatedType) : base(file, file.ComputeFullName(parent, proto.Name), index) { this.proto = proto; + this.generatedType = generatedType; containingType = parent; if (proto.Value.Count == 0) @@ -69,6 +72,11 @@ namespace Google.Protobuf.Reflection /// public override string Name { get { return proto.Name; } } + /// + /// The generated type for this enum, or null if the descriptor does not represent a generated type. + /// + public Type GeneratedType { get { return generatedType; } } + /// /// If this is a nested type, get the outer descriptor, otherwise null. /// diff --git a/csharp/src/Google.Protobuf/Reflection/FieldAccessorBase.cs b/csharp/src/Google.Protobuf/Reflection/FieldAccessorBase.cs index 39a63b47..0893dc3d 100644 --- a/csharp/src/Google.Protobuf/Reflection/FieldAccessorBase.cs +++ b/csharp/src/Google.Protobuf/Reflection/FieldAccessorBase.cs @@ -43,13 +43,8 @@ namespace Google.Protobuf.Reflection private readonly Func getValueDelegate; private readonly FieldDescriptor descriptor; - internal FieldAccessorBase(Type type, string propertyName, FieldDescriptor descriptor) + internal FieldAccessorBase(PropertyInfo property, FieldDescriptor descriptor) { - PropertyInfo property = type.GetProperty(propertyName); - if (property == null || !property.CanRead) - { - throw new ArgumentException("Not all required properties/methods available"); - } this.descriptor = descriptor; getValueDelegate = ReflectionUtil.CreateFuncObjectObject(property.GetGetMethod()); } diff --git a/csharp/src/Google.Protobuf/Reflection/FieldAccessorTable.cs b/csharp/src/Google.Protobuf/Reflection/FieldAccessorTable.cs deleted file mode 100644 index 24fcbc64..00000000 --- a/csharp/src/Google.Protobuf/Reflection/FieldAccessorTable.cs +++ /dev/null @@ -1,97 +0,0 @@ -#region Copyright notice and license -// Protocol Buffers - Google's data interchange format -// Copyright 2008 Google Inc. All rights reserved. -// https://developers.google.com/protocol-buffers/ -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#endregion - -using System; -using System.Collections.ObjectModel; - -namespace Google.Protobuf.Reflection -{ - /// - /// Provides access to fields in generated messages via reflection. - /// - public sealed class FieldAccessorTable - { - private readonly ReadOnlyCollection accessors; - private readonly ReadOnlyCollection oneofs; - private readonly MessageDescriptor descriptor; - - /// - /// Constructs a FieldAccessorTable for a particular message class. - /// Only one FieldAccessorTable should be constructed per class. - /// - /// The CLR type for the message. - /// The type's descriptor - /// The Pascal-case names of all the field-based properties in the message. - public FieldAccessorTable(Type type, MessageDescriptor descriptor, string[] propertyNames, string[] oneofPropertyNames) - { - this.descriptor = descriptor; - var accessorsArray = new IFieldAccessor[descriptor.Fields.Count]; - for (int i = 0; i < accessorsArray.Length; i++) - { - var field = descriptor.Fields[i]; - var name = propertyNames[i]; - accessorsArray[i] = - field.IsMap ? new MapFieldAccessor(type, name, field) - : field.IsRepeated ? new RepeatedFieldAccessor(type, name, field) - : (IFieldAccessor) new SingleFieldAccessor(type, name, field); - } - accessors = new ReadOnlyCollection(accessorsArray); - var oneofsArray = new OneofAccessor[descriptor.Oneofs.Count]; - for (int i = 0; i < oneofsArray.Length; i++) - { - var oneof = descriptor.Oneofs[i]; - oneofsArray[i] = new OneofAccessor(type, oneofPropertyNames[i], oneof); - } - oneofs = new ReadOnlyCollection(oneofsArray); - } - - // TODO: Validate the name here... should possibly make this type a more "general reflection access" type, - // bearing in mind the oneof parts to come as well. - /// - /// Returns all of the field accessors for the message type. - /// - public ReadOnlyCollection Accessors { get { return accessors; } } - - public ReadOnlyCollection Oneofs { get { return oneofs; } } - - // TODO: Review this, as it's easy to get confused between FieldNumber and Index. - // Currently only used to get an accessor related to a oneof... maybe just make that simpler? - public IFieldAccessor this[int fieldNumber] - { - get - { - FieldDescriptor field = descriptor.FindFieldByNumber(fieldNumber); - return accessors[field.Index]; - } - } - } -} \ No newline at end of file diff --git a/csharp/src/Google.Protobuf/Reflection/FieldDescriptor.cs b/csharp/src/Google.Protobuf/Reflection/FieldDescriptor.cs index 3d9d0d75..57378e4c 100644 --- a/csharp/src/Google.Protobuf/Reflection/FieldDescriptor.cs +++ b/csharp/src/Google.Protobuf/Reflection/FieldDescriptor.cs @@ -31,6 +31,7 @@ #endregion using System; +using System.Linq; namespace Google.Protobuf.Reflection { @@ -45,6 +46,7 @@ namespace Google.Protobuf.Reflection private readonly MessageDescriptor containingType; private readonly OneofDescriptor containingOneof; private FieldType fieldType; + private IFieldAccessor accessor; internal FieldDescriptor(FieldDescriptorProto proto, FileDescriptor file, MessageDescriptor parent, int index) @@ -82,6 +84,8 @@ namespace Google.Protobuf.Reflection public override string Name { get { return proto.Name; } } internal FieldDescriptorProto Proto { get { return proto; } } + + public IFieldAccessor Accessor { get { return accessor; } } /// /// Maps a field type as included in the .proto file to a FieldType. @@ -287,6 +291,30 @@ namespace Google.Protobuf.Reflection { throw new DescriptorValidationException(this, "MessageSet format is not supported."); } + + accessor = CreateAccessor(); + } + + private IFieldAccessor CreateAccessor() + { + // TODO: Check the performance of this with some large protos. Each message is O(N^2) in the number of fields, + // which isn't great... + if (containingType.GeneratedType == null) + { + return null; + } + var property = containingType + .GeneratedType + .GetProperties() + .FirstOrDefault(p => p.IsDefined(typeof(ProtobufFieldAttribute), false) && + p.GetCustomAttributes(typeof(ProtobufFieldAttribute), false).Cast().Single().Number == FieldNumber); + if (property == null) + { + return null; + } + return IsMap ? new MapFieldAccessor(property, this) + : IsRepeated ? new RepeatedFieldAccessor(property, this) + : (IFieldAccessor) new SingleFieldAccessor(property, this); } } } \ No newline at end of file diff --git a/csharp/src/Google.Protobuf/Reflection/FileDescriptor.cs b/csharp/src/Google.Protobuf/Reflection/FileDescriptor.cs index db393480..a10e617b 100644 --- a/csharp/src/Google.Protobuf/Reflection/FileDescriptor.cs +++ b/csharp/src/Google.Protobuf/Reflection/FileDescriptor.cs @@ -62,11 +62,12 @@ namespace Google.Protobuf.Reflection get { return proto.Syntax == "proto3" ? ProtoSyntax.Proto3 : ProtoSyntax.Proto2; } } - private FileDescriptor(FileDescriptorProto proto, FileDescriptor[] dependencies, DescriptorPool pool, bool allowUnknownDependencies) + private FileDescriptor(FileDescriptorProto proto, FileDescriptor[] dependencies, DescriptorPool pool, bool allowUnknownDependencies, Type[] generatedTypes) { this.pool = pool; this.proto = proto; this.dependencies = new ReadOnlyCollection((FileDescriptor[]) dependencies.Clone()); + IEnumerator generatedTypeIterator = generatedTypes == null ? null : ((IEnumerable)generatedTypes).GetEnumerator(); publicDependencies = DeterminePublicDependencies(this, proto, dependencies, allowUnknownDependencies); @@ -74,15 +75,21 @@ namespace Google.Protobuf.Reflection messageTypes = DescriptorUtil.ConvertAndMakeReadOnly(proto.MessageType, (message, index) => - new MessageDescriptor(message, this, null, index)); + new MessageDescriptor(message, this, null, index, generatedTypeIterator)); enumTypes = DescriptorUtil.ConvertAndMakeReadOnly(proto.EnumType, (enumType, index) => - new EnumDescriptor(enumType, this, null, index)); + new EnumDescriptor(enumType, this, null, index, ReflectionUtil.GetNextType(generatedTypeIterator))); services = DescriptorUtil.ConvertAndMakeReadOnly(proto.Service, (service, index) => new ServiceDescriptor(service, this, index)); + + // We should now have consumed all the generated types. + if (generatedTypeIterator != null && generatedTypeIterator.MoveNext()) + { + throw new ArgumentException("More generated types left over after consuming all expected ones", "generatedTypes"); + } } /// @@ -265,7 +272,7 @@ namespace Google.Protobuf.Reflection /// If is not /// a valid descriptor. This can occur for a number of reasons, such as a field /// having an undefined type or because two messages were defined with the same name. - private static FileDescriptor BuildFrom(FileDescriptorProto proto, FileDescriptor[] dependencies, bool allowUnknownDependencies) + private static FileDescriptor BuildFrom(FileDescriptorProto proto, FileDescriptor[] dependencies, bool allowUnknownDependencies, Type[] generatedTypes) { // Building descriptors involves two steps: translating and linking. // In the translation step (implemented by FileDescriptor's @@ -282,7 +289,7 @@ namespace Google.Protobuf.Reflection } DescriptorPool pool = new DescriptorPool(dependencies); - FileDescriptor result = new FileDescriptor(proto, dependencies, pool, allowUnknownDependencies); + FileDescriptor result = new FileDescriptor(proto, dependencies, pool, allowUnknownDependencies, generatedTypes); // TODO(jonskeet): Reinstate these checks, or get rid of them entirely. They aren't in the Java code, // and fail for the CustomOptions test right now. (We get "descriptor.proto" vs "google/protobuf/descriptor.proto".) @@ -319,8 +326,23 @@ namespace Google.Protobuf.Reflection } } + /// + /// Creates an instance for generated code. + /// + /// + /// The parameter should be null for descriptors which don't correspond to + /// generated types. Otherwise, the array should be represent all the generated types in the file: messages then + /// enums. Within each message, there can be nested messages and enums, which must be specified "inline" in the array: + /// containing message, nested messages, nested enums - and of course each nested message may contain *more* nested messages, + /// etc. All messages within the descriptor should be represented, even if they do not have a generated type - any + /// type without a corresponding generated type (such as map entries) should respond to a null element. + /// For example, a file with a messages OuterMessage and InnerMessage, and enums OuterEnum and InnerEnum (where + /// InnerMessage and InnerEnum are nested within InnerMessage) would result in an array of + /// OuterMessage, InnerMessage, InnerEnum, OuterEnum. + /// public static FileDescriptor InternalBuildGeneratedFileFrom(byte[] descriptorData, - FileDescriptor[] dependencies) + FileDescriptor[] dependencies, + Type[] generatedTypes) { FileDescriptorProto proto; try @@ -336,7 +358,7 @@ namespace Google.Protobuf.Reflection { // When building descriptors for generated code, we allow unknown // dependencies by default. - return BuildFrom(proto, dependencies, true); + return BuildFrom(proto, dependencies, true, generatedTypes); } catch (DescriptorValidationException e) { diff --git a/csharp/src/Google.Protobuf/Reflection/MapFieldAccessor.cs b/csharp/src/Google.Protobuf/Reflection/MapFieldAccessor.cs index 317fbd8d..6df4c5f0 100644 --- a/csharp/src/Google.Protobuf/Reflection/MapFieldAccessor.cs +++ b/csharp/src/Google.Protobuf/Reflection/MapFieldAccessor.cs @@ -32,6 +32,7 @@ using System; using System.Collections; +using System.Reflection; namespace Google.Protobuf.Reflection { @@ -40,7 +41,7 @@ namespace Google.Protobuf.Reflection /// internal sealed class MapFieldAccessor : FieldAccessorBase { - internal MapFieldAccessor(Type type, string propertyName, FieldDescriptor descriptor) : base(type, propertyName, descriptor) + internal MapFieldAccessor(PropertyInfo property, FieldDescriptor descriptor) : base(property, descriptor) { } diff --git a/csharp/src/Google.Protobuf/Reflection/MessageDescriptor.cs b/csharp/src/Google.Protobuf/Reflection/MessageDescriptor.cs index 1c22c460..9413cf61 100644 --- a/csharp/src/Google.Protobuf/Reflection/MessageDescriptor.cs +++ b/csharp/src/Google.Protobuf/Reflection/MessageDescriptor.cs @@ -30,8 +30,10 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #endregion +using Google.Protobuf.Collections; using System; using System.Collections.Generic; +using System.Linq; namespace Google.Protobuf.Reflection { @@ -60,11 +62,15 @@ namespace Google.Protobuf.Reflection private readonly IList enumTypes; private readonly IList fields; private readonly IList oneofs; + // CLR representation of the type described by this descriptor, if any. + private readonly Type generatedType; + private IDictionary fieldAccessorsByFieldNumber; - internal MessageDescriptor(DescriptorProto proto, FileDescriptor file, MessageDescriptor parent, int typeIndex) + internal MessageDescriptor(DescriptorProto proto, FileDescriptor file, MessageDescriptor parent, int typeIndex, IEnumerator generatedTypeIterator) : base(file, file.ComputeFullName(parent, proto.Name), typeIndex) { this.proto = proto; + generatedType = ReflectionUtil.GetNextType(generatedTypeIterator); containingType = parent; oneofs = DescriptorUtil.ConvertAndMakeReadOnly(proto.OneofDecl, @@ -73,11 +79,11 @@ namespace Google.Protobuf.Reflection nestedTypes = DescriptorUtil.ConvertAndMakeReadOnly(proto.NestedType, (type, index) => - new MessageDescriptor(type, file, this, index)); + new MessageDescriptor(type, file, this, index, generatedTypeIterator)); enumTypes = DescriptorUtil.ConvertAndMakeReadOnly(proto.EnumType, (type, index) => - new EnumDescriptor(type, file, this, index)); + new EnumDescriptor(type, file, this, index, ReflectionUtil.GetNextType(generatedTypeIterator))); // TODO(jonskeet): Sort fields first? fields = DescriptorUtil.ConvertAndMakeReadOnly(proto.Field, @@ -85,6 +91,14 @@ namespace Google.Protobuf.Reflection new FieldDescriptor(field, file, this, index)); file.DescriptorPool.AddSymbol(this); } + + /// + /// Returns the total number of nested types and enums, recursively. + /// + private int CountTotalGeneratedTypes() + { + return nestedTypes.Sum(nested => nested.CountTotalGeneratedTypes()) + enumTypes.Count; + } /// /// The brief name of the descriptor's target. @@ -93,6 +107,11 @@ namespace Google.Protobuf.Reflection internal DescriptorProto Proto { get { return proto; } } + /// + /// The generated type for this message, or null if the descriptor does not represent a generated type. + /// + public Type GeneratedType { get { return generatedType; } } + /// /// Returns whether this message is one of the "well known types" which may have runtime/protoc support. /// @@ -141,6 +160,13 @@ namespace Google.Protobuf.Reflection get { return oneofs; } } + /// + /// Returns a map from field number to accessor. + /// TODO: Revisit this. It's mostly in place to make the transition from FieldAccessorTable + /// to descriptor-based reflection simple in terms of tests. Work out what we really want. + /// + public IDictionary FieldAccessorsByFieldNumber { get { return fieldAccessorsByFieldNumber; } } + /// /// Finds a field by field name. /// @@ -192,6 +218,8 @@ namespace Google.Protobuf.Reflection { oneof.CrossLink(); } + + fieldAccessorsByFieldNumber = new ReadOnlyDictionary(fields.ToDictionary(field => field.FieldNumber, field => field.Accessor)); } } } \ No newline at end of file diff --git a/csharp/src/Google.Protobuf/Reflection/OneofAccessor.cs b/csharp/src/Google.Protobuf/Reflection/OneofAccessor.cs index 7a11d36b..20cbea92 100644 --- a/csharp/src/Google.Protobuf/Reflection/OneofAccessor.cs +++ b/csharp/src/Google.Protobuf/Reflection/OneofAccessor.cs @@ -44,18 +44,16 @@ namespace Google.Protobuf.Reflection private readonly Action clearDelegate; private OneofDescriptor descriptor; - internal OneofAccessor(Type type, string propertyName, OneofDescriptor descriptor) + internal OneofAccessor(PropertyInfo caseProperty, MethodInfo clearMethod, OneofDescriptor descriptor) { - PropertyInfo property = type.GetProperty(propertyName + "Case"); - if (property == null || !property.CanRead) + if (!caseProperty.CanRead) { - throw new ArgumentException("Not all required properties/methods available"); + throw new ArgumentException("Cannot read from property"); } this.descriptor = descriptor; - caseDelegate = ReflectionUtil.CreateFuncObjectT(property.GetGetMethod()); + caseDelegate = ReflectionUtil.CreateFuncObjectT(caseProperty.GetGetMethod()); this.descriptor = descriptor; - MethodInfo clearMethod = type.GetMethod("Clear" + propertyName); clearDelegate = ReflectionUtil.CreateActionObject(clearMethod); } diff --git a/csharp/src/Google.Protobuf/Reflection/OneofDescriptor.cs b/csharp/src/Google.Protobuf/Reflection/OneofDescriptor.cs index e92dc8bb..b4cc0791 100644 --- a/csharp/src/Google.Protobuf/Reflection/OneofDescriptor.cs +++ b/csharp/src/Google.Protobuf/Reflection/OneofDescriptor.cs @@ -32,6 +32,7 @@ using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Linq; namespace Google.Protobuf.Reflection { @@ -40,6 +41,7 @@ namespace Google.Protobuf.Reflection private readonly OneofDescriptorProto proto; private MessageDescriptor containingType; private IList fields; + private OneofAccessor accessor; internal OneofDescriptor(OneofDescriptorProto proto, FileDescriptor file, MessageDescriptor parent, int index) : base(file, file.ComputeFullName(parent, proto.Name), index) @@ -62,6 +64,8 @@ namespace Google.Protobuf.Reflection public IList Fields { get { return fields; } } + public OneofAccessor Accessor { get { return accessor; } } + internal void CrossLink() { List fieldCollection = new List(); @@ -73,6 +77,36 @@ namespace Google.Protobuf.Reflection } } fields = new ReadOnlyCollection(fieldCollection); + accessor = CreateAccessor(); + } + + private OneofAccessor CreateAccessor() + { + if (containingType.GeneratedType == null) + { + return null; + } + var caseProperty = containingType + .GeneratedType + .GetProperties() + .FirstOrDefault(p => p.IsDefined(typeof(ProtobufOneofAttribute), false) && + p.GetCustomAttributes(typeof(ProtobufOneofAttribute), false).Cast().Single().Name == Name); + if (caseProperty == null) + { + return null; + } + + var clearMethod = containingType + .GeneratedType + .GetMethods() + .FirstOrDefault(p => p.IsDefined(typeof(ProtobufOneofAttribute), false) && + p.GetCustomAttributes(typeof(ProtobufOneofAttribute), false).Cast().Single().Name == Name); + if (clearMethod == null) + { + return null; + } + + return new OneofAccessor(caseProperty, clearMethod, this); } } } diff --git a/csharp/src/Google.Protobuf/Reflection/ProtobufFieldAttribute.cs b/csharp/src/Google.Protobuf/Reflection/ProtobufFieldAttribute.cs new file mode 100644 index 00000000..a59caea7 --- /dev/null +++ b/csharp/src/Google.Protobuf/Reflection/ProtobufFieldAttribute.cs @@ -0,0 +1,58 @@ +#region Copyright notice and license +// Protocol Buffers - Google's data interchange format +// Copyright 2015 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#endregion +using System; + +namespace Google.Protobuf.Reflection +{ + /// + /// Attribute applied to a generated property corresponding to a field in a .proto file. + /// + [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] + public sealed class ProtobufFieldAttribute : Attribute + { + /// + /// The field number in the original .proto file. + /// + public int Number { get; set; } + + /// + /// The field name in the original .proto file. + /// + public string Name { get; set; } + + public ProtobufFieldAttribute(int number, string name) + { + this.Number = number; + this.Name = name; + } + } +} diff --git a/csharp/src/Google.Protobuf/Reflection/ProtobufOneofAttribute.cs b/csharp/src/Google.Protobuf/Reflection/ProtobufOneofAttribute.cs new file mode 100644 index 00000000..74ad8c53 --- /dev/null +++ b/csharp/src/Google.Protobuf/Reflection/ProtobufOneofAttribute.cs @@ -0,0 +1,52 @@ +#region Copyright notice and license +// Protocol Buffers - Google's data interchange format +// Copyright 2015 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#endregion +using System; + +namespace Google.Protobuf.Reflection +{ + /// + /// Attribute applied to the "case" property or "clear" method corresponding to a oneof in a .proto file. + /// + [AttributeUsage(AttributeTargets.Property | AttributeTargets.Method, AllowMultiple = false)] + public sealed class ProtobufOneofAttribute : Attribute + { + /// + /// The oneof name in the original .proto file. + /// + public string Name { get; set; } + + public ProtobufOneofAttribute(string name) + { + this.Name = name; + } + } +} diff --git a/csharp/src/Google.Protobuf/Reflection/ReflectionUtil.cs b/csharp/src/Google.Protobuf/Reflection/ReflectionUtil.cs index d0dc3e8b..ec222dc1 100644 --- a/csharp/src/Google.Protobuf/Reflection/ReflectionUtil.cs +++ b/csharp/src/Google.Protobuf/Reflection/ReflectionUtil.cs @@ -31,6 +31,7 @@ #endregion using System; +using System.Collections.Generic; using System.Linq.Expressions; using System.Reflection; @@ -102,5 +103,24 @@ namespace Google.Protobuf.Reflection Expression call = Expression.Call(castTarget, method); return Expression.Lambda>(call, targetParameter).Compile(); } + + /// + /// Returns the next type from an iterator of types, unless the iterator is a null reference, + /// in which case null is returned. + /// + internal static Type GetNextType(IEnumerator generatedTypeIterator) + { + if (generatedTypeIterator == null) + { + return null; + } + if (!generatedTypeIterator.MoveNext()) + { + // This parameter name corresponds to any public method supplying the generated types to start with. + throw new ArgumentException("More generated types left over after consuming all expected ones", "generatedTypes"); + } + return generatedTypeIterator.Current; + } + } } \ No newline at end of file diff --git a/csharp/src/Google.Protobuf/Reflection/RepeatedFieldAccessor.cs b/csharp/src/Google.Protobuf/Reflection/RepeatedFieldAccessor.cs index 0ada7567..acb3c8d5 100644 --- a/csharp/src/Google.Protobuf/Reflection/RepeatedFieldAccessor.cs +++ b/csharp/src/Google.Protobuf/Reflection/RepeatedFieldAccessor.cs @@ -32,6 +32,7 @@ using System; using System.Collections; +using System.Reflection; namespace Google.Protobuf.Reflection { @@ -40,7 +41,7 @@ namespace Google.Protobuf.Reflection /// internal sealed class RepeatedFieldAccessor : FieldAccessorBase { - internal RepeatedFieldAccessor(Type type, string propertyName, FieldDescriptor descriptor) : base(type, propertyName, descriptor) + internal RepeatedFieldAccessor(PropertyInfo property, FieldDescriptor descriptor) : base(property, descriptor) { } diff --git a/csharp/src/Google.Protobuf/Reflection/SingleFieldAccessor.cs b/csharp/src/Google.Protobuf/Reflection/SingleFieldAccessor.cs index 8c24e46e..f00a51ba 100644 --- a/csharp/src/Google.Protobuf/Reflection/SingleFieldAccessor.cs +++ b/csharp/src/Google.Protobuf/Reflection/SingleFieldAccessor.cs @@ -48,11 +48,8 @@ namespace Google.Protobuf.Reflection private readonly Action setValueDelegate; private readonly Action clearDelegate; - internal SingleFieldAccessor(Type type, string propertyName, FieldDescriptor descriptor) : base(type, propertyName, descriptor) + internal SingleFieldAccessor(PropertyInfo property, FieldDescriptor descriptor) : base(property, descriptor) { - PropertyInfo property = type.GetProperty(propertyName); - // We know there *is* such a property, or the base class constructor would have thrown. We should be able to write - // to it though. if (!property.CanWrite) { throw new ArgumentException("Not all required properties/methods available"); diff --git a/src/google/protobuf/compiler/csharp/csharp_field_base.cc b/src/google/protobuf/compiler/csharp/csharp_field_base.cc index 89d4eb18..d327e267 100644 --- a/src/google/protobuf/compiler/csharp/csharp_field_base.cc +++ b/src/google/protobuf/compiler/csharp/csharp_field_base.cc @@ -74,6 +74,7 @@ void FieldGeneratorBase::SetCommonFieldVariables( (*variables)["property_name"] = property_name(); (*variables)["type_name"] = type_name(); + (*variables)["original_name"] = descriptor_->name(); (*variables)["name"] = name(); (*variables)["descriptor_name"] = descriptor_->name(); (*variables)["default_value"] = default_value(); @@ -85,7 +86,6 @@ void FieldGeneratorBase::SetCommonFieldVariables( } (*variables)["capitalized_type_name"] = capitalized_type_name(); (*variables)["number"] = number(); - (*variables)["field_ordinal"] = field_ordinal(); (*variables)["has_property_check"] = (*variables)["property_name"] + " != " + (*variables)["default_value"]; (*variables)["other_has_property_check"] = "other." + diff --git a/src/google/protobuf/compiler/csharp/csharp_helpers.h b/src/google/protobuf/compiler/csharp/csharp_helpers.h index 653ff992..7737ffe2 100644 --- a/src/google/protobuf/compiler/csharp/csharp_helpers.h +++ b/src/google/protobuf/compiler/csharp/csharp_helpers.h @@ -77,6 +77,8 @@ std::string GetFullUmbrellaClassName(const FileDescriptor* descriptor); std::string GetQualifiedUmbrellaClassName(const FileDescriptor* descriptor); +std::string GetClassName(const Descriptor* descriptor); + std::string GetClassName(const EnumDescriptor* descriptor); std::string GetFieldName(const FieldDescriptor* descriptor); @@ -119,10 +121,6 @@ inline bool IsDescriptorProto(const FileDescriptor* descriptor) { return descriptor->name() == "google/protobuf/descriptor_proto_file.proto"; } -inline bool IsMapEntry(const Descriptor* descriptor) { - return descriptor->options().map_entry(); -} - inline bool IsWrapperType(const FieldDescriptor* descriptor) { return descriptor->type() == FieldDescriptor::TYPE_MESSAGE && descriptor->message_type()->file()->name() == "google/protobuf/wrappers.proto"; diff --git a/src/google/protobuf/compiler/csharp/csharp_map_field.cc b/src/google/protobuf/compiler/csharp/csharp_map_field.cc index cba24a59..bdbfd92b 100644 --- a/src/google/protobuf/compiler/csharp/csharp_map_field.cc +++ b/src/google/protobuf/compiler/csharp/csharp_map_field.cc @@ -79,6 +79,7 @@ void MapFieldGenerator::GenerateMembers(io::Printer* printer) { AddDeprecatedFlag(printer); printer->Print( variables_, + "[pbr::ProtobufField($number$, \"$original_name$\")]\n" "$access_level$ pbc::MapField<$key_type_name$, $value_type_name$> $property_name$ {\n" " get { return $name$_; }\n" "}\n"); diff --git a/src/google/protobuf/compiler/csharp/csharp_message.cc b/src/google/protobuf/compiler/csharp/csharp_message.cc index 10cf3585..42fd5065 100644 --- a/src/google/protobuf/compiler/csharp/csharp_message.cc +++ b/src/google/protobuf/compiler/csharp/csharp_message.cc @@ -96,88 +96,11 @@ const std::vector& MessageGenerator::fields_by_number() return fields_by_number_; } -/// Get an identifier that uniquely identifies this type within the file. -/// This is used to declare static variables related to this type at the -/// outermost file scope. -std::string GetUniqueFileScopeIdentifier(const Descriptor* descriptor) { - std::string result = descriptor->full_name(); - std::replace(result.begin(), result.end(), '.', '_'); - return "static_" + result; -} - -void MessageGenerator::GenerateStaticVariables(io::Printer* printer) { - // Because descriptor.proto (Google.Protobuf.DescriptorProtos) is - // used in the construction of descriptors, we have a tricky bootstrapping - // problem. To help control static initialization order, we make sure all - // descriptors and other static data that depends on them are members of - // the proto-descriptor class. This way, they will be initialized in - // a deterministic order. - - std::string identifier = GetUniqueFileScopeIdentifier(descriptor_); - - // The descriptor for this type. - printer->Print( - "internal static pbr::FieldAccessorTable internal__$identifier$__FieldAccessorTable;\n", - "identifier", GetUniqueFileScopeIdentifier(descriptor_), - "full_class_name", full_class_name()); - - for (int i = 0; i < descriptor_->nested_type_count(); i++) { - // Don't generate accessor table fields for maps... - if (!IsMapEntryMessage(descriptor_->nested_type(i))) { - MessageGenerator messageGenerator(descriptor_->nested_type(i)); - messageGenerator.GenerateStaticVariables(printer); - } - } -} - -void MessageGenerator::GenerateStaticVariableInitializers(io::Printer* printer) { - map vars; - vars["identifier"] = GetUniqueFileScopeIdentifier(descriptor_); - vars["full_class_name"] = full_class_name(); - - // Work out how to get to the message descriptor (which may be multiply nested) from the file - // descriptor. - string descriptor_chain; - const Descriptor* current_descriptor = descriptor_; - while (current_descriptor->containing_type()) { - descriptor_chain = ".NestedTypes[" + SimpleItoa(current_descriptor->index()) + "]" + descriptor_chain; - current_descriptor = current_descriptor->containing_type(); - } - descriptor_chain = "descriptor.MessageTypes[" + SimpleItoa(current_descriptor->index()) + "]" + descriptor_chain; - vars["descriptor_chain"] = descriptor_chain; - - printer->Print( - vars, - "internal__$identifier$__FieldAccessorTable = \n" - " new pbr::FieldAccessorTable(typeof($full_class_name$), $descriptor_chain$,\n"); - printer->Print(" new string[] { "); - for (int i = 0; i < descriptor_->field_count(); i++) { - printer->Print("\"$property_name$\", ", - "property_name", GetPropertyName(descriptor_->field(i))); - } - printer->Print("}, new string[] { "); - for (int i = 0; i < descriptor_->oneof_decl_count(); i++) { - printer->Print("\"$oneof_name$\", ", - "oneof_name", - UnderscoresToCamelCase(descriptor_->oneof_decl(i)->name(), true)); - } - printer->Print("});\n"); - - // Generate static member initializers for all non-map-entry nested types. - for (int i = 0; i < descriptor_->nested_type_count(); i++) { - if (!IsMapEntryMessage(descriptor_->nested_type(i))) { - MessageGenerator messageGenerator(descriptor_->nested_type(i)); - messageGenerator.GenerateStaticVariableInitializers(printer); - } - } -} - void MessageGenerator::Generate(io::Printer* printer) { map vars; vars["class_name"] = class_name(); vars["access_level"] = class_access_level(); vars["umbrella_class_name"] = GetFullUmbrellaClassName(descriptor_->file()); - vars["identifier"] = GetUniqueFileScopeIdentifier(descriptor_); printer->Print( "[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]\n"); @@ -221,8 +144,8 @@ void MessageGenerator::Generate(io::Printer* printer) { " get { return $descriptor_accessor$; }\n" "}\n" "\n" - "pbr::FieldAccessorTable pb::IReflectedMessage.Fields {\n" - " get { return $umbrella_class_name$.internal__$identifier$__FieldAccessorTable; }\n" + "pbr::MessageDescriptor pb::IMessage.Descriptor {\n" + " get { return Descriptor; }\n" "}\n" "\n" "private bool _frozen = false;\n" @@ -258,6 +181,7 @@ void MessageGenerator::Generate(io::Printer* printer) { for (int i = 0; i < descriptor_->oneof_decl_count(); i++) { vars["name"] = UnderscoresToCamelCase(descriptor_->oneof_decl(i)->name(), false); vars["property_name"] = UnderscoresToCamelCase(descriptor_->oneof_decl(i)->name(), true); + vars["original_name"] = descriptor_->oneof_decl(i)->name(); printer->Print( vars, "private object $name$_;\n" @@ -275,9 +199,11 @@ void MessageGenerator::Generate(io::Printer* printer) { printer->Print( vars, "private $property_name$OneofCase $name$Case_ = $property_name$OneofCase.None;\n" + "[pbr::ProtobufOneof(\"$original_name$\")]\n" "public $property_name$OneofCase $property_name$Case {\n" " get { return $name$Case_; }\n" "}\n\n" + "[pbr::ProtobufOneof(\"$original_name$\")]\n" "public void Clear$property_name$() {\n" " pb::Freezable.CheckMutable(this);\n" " $name$Case_ = $property_name$OneofCase.None;\n" diff --git a/src/google/protobuf/compiler/csharp/csharp_message.h b/src/google/protobuf/compiler/csharp/csharp_message.h index fbe8a3be..f0c49ac9 100644 --- a/src/google/protobuf/compiler/csharp/csharp_message.h +++ b/src/google/protobuf/compiler/csharp/csharp_message.h @@ -53,8 +53,6 @@ class MessageGenerator : public SourceGeneratorBase { void GenerateCloningCode(io::Printer* printer); void GenerateFreezingCode(io::Printer* printer); void GenerateFrameworkMethods(io::Printer* printer); - void GenerateStaticVariables(io::Printer* printer); - void GenerateStaticVariableInitializers(io::Printer* printer); void Generate(io::Printer* printer); private: diff --git a/src/google/protobuf/compiler/csharp/csharp_message_field.cc b/src/google/protobuf/compiler/csharp/csharp_message_field.cc index d2c3a88b..c2b6ff76 100644 --- a/src/google/protobuf/compiler/csharp/csharp_message_field.cc +++ b/src/google/protobuf/compiler/csharp/csharp_message_field.cc @@ -64,6 +64,7 @@ void MessageFieldGenerator::GenerateMembers(io::Printer* printer) { AddDeprecatedFlag(printer); printer->Print( variables_, + "[pbr::ProtobufField($number$, \"$original_name$\")]\n" "$access_level$ $type_name$ $property_name$ {\n" " get { return $name$_; }\n" " set {\n" @@ -158,6 +159,7 @@ void MessageOneofFieldGenerator::GenerateMembers(io::Printer* printer) { AddDeprecatedFlag(printer); printer->Print( variables_, + "[pbr::ProtobufField($number$, \"$original_name$\")]\n" "$access_level$ $type_name$ $property_name$ {\n" " get { return $has_property_check$ ? ($type_name$) $oneof_name$_ : null; }\n" " set {\n" diff --git a/src/google/protobuf/compiler/csharp/csharp_primitive_field.cc b/src/google/protobuf/compiler/csharp/csharp_primitive_field.cc index 4454ef02..2c9338bd 100644 --- a/src/google/protobuf/compiler/csharp/csharp_primitive_field.cc +++ b/src/google/protobuf/compiler/csharp/csharp_primitive_field.cc @@ -71,6 +71,7 @@ void PrimitiveFieldGenerator::GenerateMembers(io::Printer* printer) { AddDeprecatedFlag(printer); printer->Print( variables_, + "[pbr::ProtobufField($number$, \"$original_name$\")]\n" "$access_level$ $type_name$ $property_name$ {\n" " get { return $name$_; }\n" " set {\n" @@ -174,6 +175,7 @@ void PrimitiveOneofFieldGenerator::GenerateMembers(io::Printer* printer) { AddDeprecatedFlag(printer); printer->Print( variables_, + "[pbr::ProtobufField($number$, \"$original_name$\")]\n" "$access_level$ $type_name$ $property_name$ {\n" " get { return $has_property_check$ ? ($type_name$) $oneof_name$_ : $default_value$; }\n" " set {\n" diff --git a/src/google/protobuf/compiler/csharp/csharp_repeated_enum_field.cc b/src/google/protobuf/compiler/csharp/csharp_repeated_enum_field.cc index d5fc6d98..60d06154 100644 --- a/src/google/protobuf/compiler/csharp/csharp_repeated_enum_field.cc +++ b/src/google/protobuf/compiler/csharp/csharp_repeated_enum_field.cc @@ -59,12 +59,13 @@ void RepeatedEnumFieldGenerator::GenerateMembers(io::Printer* printer) { printer->Print( variables_, "private static readonly pb::FieldCodec<$type_name$> _repeated_$name$_codec\n" - " = pb::FieldCodec.ForEnum($tag$, x => (int) x, x => ($type_name$) x);"); + " = pb::FieldCodec.ForEnum($tag$, x => (int) x, x => ($type_name$) x);\n"); printer->Print(variables_, "private readonly pbc::RepeatedField<$type_name$> $name$_ = new pbc::RepeatedField<$type_name$>();\n"); AddDeprecatedFlag(printer); printer->Print( variables_, + "[pbr::ProtobufField($number$, \"$original_name$\")]\n" "$access_level$ pbc::RepeatedField<$type_name$> $property_name$ {\n" " get { return $name$_; }\n" "}\n"); diff --git a/src/google/protobuf/compiler/csharp/csharp_repeated_message_field.cc b/src/google/protobuf/compiler/csharp/csharp_repeated_message_field.cc index d939fc79..921798b0 100644 --- a/src/google/protobuf/compiler/csharp/csharp_repeated_message_field.cc +++ b/src/google/protobuf/compiler/csharp/csharp_repeated_message_field.cc @@ -78,6 +78,7 @@ void RepeatedMessageFieldGenerator::GenerateMembers(io::Printer* printer) { AddDeprecatedFlag(printer); printer->Print( variables_, + "[pbr::ProtobufField($number$, \"$original_name$\")]\n" "$access_level$ pbc::RepeatedField<$type_name$> $property_name$ {\n" " get { return $name$_; }\n" "}\n"); diff --git a/src/google/protobuf/compiler/csharp/csharp_repeated_primitive_field.cc b/src/google/protobuf/compiler/csharp/csharp_repeated_primitive_field.cc index 5b5d9b3d..bcfb9936 100644 --- a/src/google/protobuf/compiler/csharp/csharp_repeated_primitive_field.cc +++ b/src/google/protobuf/compiler/csharp/csharp_repeated_primitive_field.cc @@ -65,6 +65,7 @@ void RepeatedPrimitiveFieldGenerator::GenerateMembers(io::Printer* printer) { AddDeprecatedFlag(printer); printer->Print( variables_, + "[pbr::ProtobufField($number$, \"$original_name$\")]\n" "$access_level$ pbc::RepeatedField<$type_name$> $property_name$ {\n" " get { return $name$_; }\n" "}\n"); diff --git a/src/google/protobuf/compiler/csharp/csharp_umbrella_class.cc b/src/google/protobuf/compiler/csharp/csharp_umbrella_class.cc index 57271d17..3f39250e 100644 --- a/src/google/protobuf/compiler/csharp/csharp_umbrella_class.cc +++ b/src/google/protobuf/compiler/csharp/csharp_umbrella_class.cc @@ -63,12 +63,6 @@ UmbrellaClassGenerator::~UmbrellaClassGenerator() { void UmbrellaClassGenerator::Generate(io::Printer* printer) { WriteIntroduction(printer); - printer->Print("#region Static variables\n"); - for (int i = 0; i < file_->message_type_count(); i++) { - MessageGenerator messageGenerator(file_->message_type(i)); - messageGenerator.GenerateStaticVariables(printer); - } - printer->Print("#endregion\n"); WriteDescriptor(printer); // Close the class declaration. printer->Outdent(); @@ -183,24 +177,43 @@ void UmbrellaClassGenerator::WriteDescriptor(io::Printer* printer) { // Invoke InternalBuildGeneratedFileFrom() to build the file. printer->Print( "descriptor = pbr::FileDescriptor.InternalBuildGeneratedFileFrom(descriptorData,\n"); - printer->Print(" new pbr::FileDescriptor[] {\n"); + printer->Print(" new pbr::FileDescriptor[] { "); for (int i = 0; i < file_->dependency_count(); i++) { printer->Print( - " $full_umbrella_class_name$.Descriptor, \n", + "$full_umbrella_class_name$.Descriptor, ", "full_umbrella_class_name", GetFullUmbrellaClassName(file_->dependency(i))); } - printer->Print(" });\n"); - // Then invoke any other static variable initializers, e.g. field accessors. + // Specify all the generated types (messages and enums), recursively, as an array. + printer->Print("},\n" + " new global::System.Type[] { "); for (int i = 0; i < file_->message_type_count(); i++) { - MessageGenerator messageGenerator(file_->message_type(i)); - messageGenerator.GenerateStaticVariableInitializers(printer); + WriteTypeLiterals(file_->message_type(i), printer); } + for (int i = 0; i < file_->enum_type_count(); i++) { + printer->Print("typeof($type_name$), ", "type_name", GetClassName(file_->enum_type(i))); + } + printer->Print("});\n"); + printer->Outdent(); printer->Print("}\n"); printer->Print("#endregion\n\n"); } +void UmbrellaClassGenerator::WriteTypeLiterals(const Descriptor* descriptor, io::Printer* printer) { + if (IsMapEntryMessage(descriptor)) { + printer->Print("null, "); + return; + } + printer->Print("typeof($type_name$), ", "type_name", GetClassName(descriptor)); + for (int i = 0; i < descriptor->nested_type_count(); i++) { + WriteTypeLiterals(descriptor->nested_type(i), printer); + } + for (int i = 0; i < descriptor->enum_type_count(); i++) { + printer->Print("typeof($type_name$), ", "type_name", GetClassName(descriptor->enum_type(i))); + } +} + } // namespace csharp } // namespace compiler } // namespace protobuf diff --git a/src/google/protobuf/compiler/csharp/csharp_umbrella_class.h b/src/google/protobuf/compiler/csharp/csharp_umbrella_class.h index f7533d2d..db1092a9 100644 --- a/src/google/protobuf/compiler/csharp/csharp_umbrella_class.h +++ b/src/google/protobuf/compiler/csharp/csharp_umbrella_class.h @@ -57,6 +57,7 @@ class UmbrellaClassGenerator : public SourceGeneratorBase { void WriteIntroduction(io::Printer* printer); void WriteDescriptor(io::Printer* printer); + void WriteTypeLiterals(const Descriptor* descriptor, io::Printer* printer); GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(UmbrellaClassGenerator); }; diff --git a/src/google/protobuf/compiler/csharp/csharp_wrapper_field.cc b/src/google/protobuf/compiler/csharp/csharp_wrapper_field.cc index 75ef5e50..d6cd0a10 100644 --- a/src/google/protobuf/compiler/csharp/csharp_wrapper_field.cc +++ b/src/google/protobuf/compiler/csharp/csharp_wrapper_field.cc @@ -73,6 +73,7 @@ void WrapperFieldGenerator::GenerateMembers(io::Printer* printer) { AddDeprecatedFlag(printer); printer->Print( variables_, + "[pbr::ProtobufField($number$, \"$original_name$\")]\n" "$access_level$ $type_name$ $property_name$ {\n" " get { return $name$_; }\n" " set {\n" @@ -169,6 +170,7 @@ void WrapperOneofFieldGenerator::GenerateMembers(io::Printer* printer) { AddDeprecatedFlag(printer); printer->Print( variables_, + "[pbr::ProtobufField($number$, \"$original_name$\")]\n" "$access_level$ $type_name$ $property_name$ {\n" " get { return $has_property_check$ ? ($type_name$) $oneof_name$_ : ($type_name$) null; }\n" " set {\n" -- cgit v1.2.3 From 3783d9a8add33b240e326438fa0b16869dbcfb44 Mon Sep 17 00:00:00 2001 From: Jan Tattermusch Date: Wed, 29 Jul 2015 16:05:57 -0700 Subject: remove the freeze API --- Makefile.am | 1 - .../Collections/MapFieldTest.cs | 56 --------------------- .../Collections/RepeatedFieldTest.cs | 36 -------------- .../Google.Protobuf.Test/GeneratedMessageTest.cs | 17 ------- csharp/src/Google.Protobuf/Collections/MapField.cs | 34 +------------ .../Google.Protobuf/Collections/RepeatedField.cs | 29 ++--------- csharp/src/Google.Protobuf/Freezable.cs | 58 ---------------------- csharp/src/Google.Protobuf/Google.Protobuf.csproj | 1 - csharp/src/Google.Protobuf/IMessage.cs | 34 +------------ .../protobuf/compiler/csharp/csharp_map_field.cc | 2 - .../protobuf/compiler/csharp/csharp_message.cc | 28 ----------- .../compiler/csharp/csharp_message_field.cc | 4 -- .../compiler/csharp/csharp_primitive_field.cc | 6 +-- .../compiler/csharp/csharp_repeated_enum_field.cc | 2 - .../csharp/csharp_repeated_message_field.cc | 2 - .../csharp/csharp_repeated_primitive_field.cc | 2 - .../compiler/csharp/csharp_wrapper_field.cc | 2 - 17 files changed, 8 insertions(+), 306 deletions(-) delete mode 100644 csharp/src/Google.Protobuf/Freezable.cs (limited to 'csharp/src/Google.Protobuf/IMessage.cs') diff --git a/Makefile.am b/Makefile.am index c8e730f9..3c0cea5a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -99,7 +99,6 @@ csharp_EXTRA_DIST= \ csharp/src/Google.Protobuf/Collections/RepeatedField.cs \ csharp/src/Google.Protobuf/FieldCodec.cs \ csharp/src/Google.Protobuf/FrameworkPortability.cs \ - csharp/src/Google.Protobuf/Freezable.cs \ csharp/src/Google.Protobuf/Google.Protobuf.csproj \ csharp/src/Google.Protobuf/Google.Protobuf.nuspec \ csharp/src/Google.Protobuf/IMessage.cs \ diff --git a/csharp/src/Google.Protobuf.Test/Collections/MapFieldTest.cs b/csharp/src/Google.Protobuf.Test/Collections/MapFieldTest.cs index 46d3bd9a..c62ac046 100644 --- a/csharp/src/Google.Protobuf.Test/Collections/MapFieldTest.cs +++ b/csharp/src/Google.Protobuf.Test/Collections/MapFieldTest.cs @@ -45,53 +45,6 @@ namespace Google.Protobuf.Collections /// public class MapFieldTest { - // Protobuf-specific tests - [Test] - public void Freeze_FreezesMessages() - { - var message = new ForeignMessage { C = 20 }; - var map = new MapField { { "x", message } }; - map.Freeze(); - Assert.IsTrue(message.IsFrozen); - } - - [Test] - public void Freeze_Idempotent() - { - var message = new ForeignMessage { C = 20 }; - var map = new MapField { { "x", message } }; - Assert.IsFalse(map.IsFrozen); - map.Freeze(); - Assert.IsTrue(message.IsFrozen); - map.Freeze(); - Assert.IsTrue(message.IsFrozen); - } - - [Test] - public void Freeze_PreventsMutation() - { - var map = new MapField(); - map.Freeze(); - Assert.IsTrue(map.IsFrozen); - Assert.IsTrue(map.IsReadOnly); - ICollection> collection = map; - Assert.Throws(() => map["x"] = "y"); - Assert.Throws(() => map.Add("x", "y")); - Assert.Throws(() => map.Remove("x")); - Assert.Throws(() => map.Clear()); - Assert.Throws(() => collection.Add(NewKeyValuePair("x", "y"))); - Assert.Throws(() => collection.Remove(NewKeyValuePair("x", "y"))); - } - - [Test] - public void Clone_ReturnsNonFrozen() - { - var map = new MapField(); - map.Freeze(); - var clone = map.Clone(); - clone.Add("x", "y"); - } - [Test] public void Clone_ClonesMessages() { @@ -422,10 +375,6 @@ namespace Google.Protobuf.Collections dictionary.Remove("x"); Assert.AreEqual(0, dictionary.Count); Assert.Throws(() => dictionary.Remove(null)); - - map.Freeze(); - // Call should fail even though it clearly doesn't contain 5 as a key. - Assert.Throws(() => dictionary.Remove(5)); } [Test] @@ -449,8 +398,6 @@ namespace Google.Protobuf.Collections var map = new MapField { { "x", "y" } }; IDictionary dictionary = map; Assert.IsFalse(dictionary.IsFixedSize); - map.Freeze(); - Assert.IsTrue(dictionary.IsFixedSize); } [Test] @@ -504,9 +451,6 @@ namespace Google.Protobuf.Collections Assert.Throws(() => dictionary["x"] = 5); Assert.Throws(() => dictionary[null] = "z"); Assert.Throws(() => dictionary["x"] = null); - map.Freeze(); - // Note: Not InvalidOperationException. - Assert.Throws(() => dictionary["a"] = "c"); } [Test] diff --git a/csharp/src/Google.Protobuf.Test/Collections/RepeatedFieldTest.cs b/csharp/src/Google.Protobuf.Test/Collections/RepeatedFieldTest.cs index 25be7731..33c4e10c 100644 --- a/csharp/src/Google.Protobuf.Test/Collections/RepeatedFieldTest.cs +++ b/csharp/src/Google.Protobuf.Test/Collections/RepeatedFieldTest.cs @@ -193,44 +193,10 @@ namespace Google.Protobuf.Collections Assert.Throws(() => list[2] = "bad"); } - [Test] - public void Freeze_FreezesElements() - { - var list = new RepeatedField { new TestAllTypes() }; - Assert.IsFalse(list[0].IsFrozen); - list.Freeze(); - Assert.IsTrue(list[0].IsFrozen); - } - - [Test] - public void Freeze_PreventsMutations() - { - var list = new RepeatedField { 0 }; - list.Freeze(); - Assert.Throws(() => list.Add(1)); - Assert.Throws(() => list[0] = 1); - Assert.Throws(() => list.Clear()); - Assert.Throws(() => list.RemoveAt(0)); - Assert.Throws(() => list.Remove(0)); - Assert.Throws(() => list.Insert(0, 0)); - } - - [Test] - public void Freeze_ReportsFrozen() - { - var list = new RepeatedField { 0 }; - Assert.IsFalse(list.IsFrozen); - Assert.IsFalse(list.IsReadOnly); - list.Freeze(); - Assert.IsTrue(list.IsFrozen); - Assert.IsTrue(list.IsReadOnly); - } - [Test] public void Clone_ReturnsMutable() { var list = new RepeatedField { 0 }; - list.Freeze(); var clone = list.Clone(); clone[0] = 1; } @@ -585,8 +551,6 @@ namespace Google.Protobuf.Collections var field = new RepeatedField { "first", "second" }; IList list = field; Assert.IsFalse(list.IsFixedSize); - field.Freeze(); - Assert.IsTrue(list.IsFixedSize); } [Test] diff --git a/csharp/src/Google.Protobuf.Test/GeneratedMessageTest.cs b/csharp/src/Google.Protobuf.Test/GeneratedMessageTest.cs index 528a4023..dfaac48c 100644 --- a/csharp/src/Google.Protobuf.Test/GeneratedMessageTest.cs +++ b/csharp/src/Google.Protobuf.Test/GeneratedMessageTest.cs @@ -503,23 +503,6 @@ namespace Google.Protobuf Assert.AreNotEqual(original, clone); } - [Test] - public void Freeze() - { - var frozen = new TestAllTypes(); - frozen.Freeze(); - Assert.IsTrue(frozen.IsFrozen); - - Assert.Throws(() => frozen.ClearOneofField()); - Assert.Throws(() => frozen.SingleInt32 = 0); - Assert.Throws(() => frozen.SingleNestedMessage = null); - Assert.Throws(() => frozen.SingleNestedEnum = 0); - Assert.Throws(() => frozen.OneofString = null); - Assert.Throws(() => frozen.OneofUint32 = 0U); - Assert.Throws(() => frozen.RepeatedDouble.Add(0.0)); - Assert.Throws(() => frozen.RepeatedNestedMessage.Add(new TestAllTypes.Types.NestedMessage())); - } - [Test] public void OneofProperties() { diff --git a/csharp/src/Google.Protobuf/Collections/MapField.cs b/csharp/src/Google.Protobuf/Collections/MapField.cs index 68f2f1cc..0f7227c2 100644 --- a/csharp/src/Google.Protobuf/Collections/MapField.cs +++ b/csharp/src/Google.Protobuf/Collections/MapField.cs @@ -50,7 +50,7 @@ namespace Google.Protobuf.Collections /// /// Key type in the map. Must be a type supported by Protocol Buffer map keys. /// Value type in the map. Must be a type supported by Protocol Buffers. - public sealed class MapField : IDeepCloneable>, IFreezable, IDictionary, IEquatable>, IDictionary + public sealed class MapField : IDeepCloneable>, IDictionary, IEquatable>, IDictionary { // TODO: Don't create the map/list until we have an entry. (Assume many maps will be empty.) private readonly bool allowNullValues; @@ -119,7 +119,6 @@ namespace Google.Protobuf.Collections public bool Remove(TKey key) { - this.CheckMutable(); ThrowHelper.ThrowIfNull(key, "key"); LinkedListNode> node; if (map.TryGetValue(key, out node)) @@ -169,7 +168,6 @@ namespace Google.Protobuf.Collections { ThrowHelper.ThrowIfNull(value, "value"); } - this.CheckMutable(); LinkedListNode> node; var pair = new KeyValuePair(key, value); if (map.TryGetValue(key, out node)) @@ -214,7 +212,6 @@ namespace Google.Protobuf.Collections public void Clear() { - this.CheckMutable(); list.Clear(); map.Clear(); } @@ -233,7 +230,6 @@ namespace Google.Protobuf.Collections bool ICollection>.Remove(KeyValuePair item) { - this.CheckMutable(); if (item.Key == null) { throw new ArgumentException("Key is null", "item"); @@ -260,31 +256,6 @@ namespace Google.Protobuf.Collections public int Count { get { return list.Count; } } public bool IsReadOnly { get { return frozen; } } - public void Freeze() - { - if (IsFrozen) - { - return; - } - frozen = true; - // Only values can be frozen, as all the key types are simple. - // Everything can be done in-place, as we're just freezing objects. - if (typeof(IFreezable).IsAssignableFrom(typeof(TValue))) - { - for (var node = list.First; node != null; node = node.Next) - { - var pair = node.Value; - IFreezable freezableValue = pair.Value as IFreezable; - if (freezableValue != null) - { - freezableValue.Freeze(); - } - } - } - } - - public bool IsFrozen { get { return frozen; } } - public override bool Equals(object other) { return Equals(other as MapField); @@ -405,7 +376,6 @@ namespace Google.Protobuf.Collections void IDictionary.Remove(object key) { ThrowHelper.ThrowIfNull(key, "key"); - this.CheckMutable(); if (!(key is TKey)) { return; @@ -420,7 +390,7 @@ namespace Google.Protobuf.Collections temp.CopyTo(array, index); } - bool IDictionary.IsFixedSize { get { return IsFrozen; } } + bool IDictionary.IsFixedSize { get { return false; } } ICollection IDictionary.Keys { get { return (ICollection)Keys; } } diff --git a/csharp/src/Google.Protobuf/Collections/RepeatedField.cs b/csharp/src/Google.Protobuf/Collections/RepeatedField.cs index ccd1a9bb..e7fc0a3f 100644 --- a/csharp/src/Google.Protobuf/Collections/RepeatedField.cs +++ b/csharp/src/Google.Protobuf/Collections/RepeatedField.cs @@ -43,7 +43,7 @@ namespace Google.Protobuf.Collections /// restrictions (no null values) and capabilities (deep cloning and freezing). /// /// The element type of the repeated field. - public sealed class RepeatedField : IList, IList, IDeepCloneable>, IEquatable>, IFreezable + public sealed class RepeatedField : IList, IList, IDeepCloneable>, IEquatable> { private static readonly T[] EmptyArray = new T[0]; private const int MinArraySize = 8; @@ -190,21 +190,6 @@ namespace Google.Protobuf.Collections } } - public bool IsFrozen { get { return frozen; } } - - public void Freeze() - { - frozen = true; - IFreezable[] freezableArray = array as IFreezable[]; - if (freezableArray != null) - { - for (int i = 0; i < count; i++) - { - freezableArray[i].Freeze(); - } - } - } - private void EnsureSize(int size) { if (array.Length < size) @@ -223,14 +208,12 @@ namespace Google.Protobuf.Collections { throw new ArgumentNullException("item"); } - this.CheckMutable(); EnsureSize(count + 1); array[count++] = item; } public void Clear() { - this.CheckMutable(); array = EmptyArray; count = 0; } @@ -247,7 +230,6 @@ namespace Google.Protobuf.Collections public bool Remove(T item) { - this.CheckMutable(); int index = IndexOf(item); if (index == -1) { @@ -261,7 +243,7 @@ namespace Google.Protobuf.Collections public int Count { get { return count; } } - public bool IsReadOnly { get { return IsFrozen; } } + public bool IsReadOnly { get { return false; } } public void Add(RepeatedField values) { @@ -269,7 +251,6 @@ namespace Google.Protobuf.Collections { throw new ArgumentNullException("values"); } - this.CheckMutable(); EnsureSize(count + values.count); // We know that all the values will be valid, because it's a RepeatedField. Array.Copy(values.array, 0, array, count, values.count); @@ -282,7 +263,6 @@ namespace Google.Protobuf.Collections { throw new ArgumentNullException("values"); } - this.CheckMutable(); // TODO: Check for ICollection and get the Count? foreach (T item in values) { @@ -372,7 +352,6 @@ namespace Google.Protobuf.Collections { throw new ArgumentOutOfRangeException("index"); } - this.CheckMutable(); EnsureSize(count + 1); Array.Copy(array, index, array, index + 1, count - index); array[index] = item; @@ -385,7 +364,6 @@ namespace Google.Protobuf.Collections { throw new ArgumentOutOfRangeException("index"); } - this.CheckMutable(); Array.Copy(array, index + 1, array, index, count - index - 1); count--; array[count] = default(T); @@ -407,7 +385,6 @@ namespace Google.Protobuf.Collections { throw new ArgumentOutOfRangeException("index"); } - this.CheckMutable(); if (value == null) { throw new ArgumentNullException("value"); @@ -417,7 +394,7 @@ namespace Google.Protobuf.Collections } #region Explicit interface implementation for IList and ICollection. - bool IList.IsFixedSize { get { return IsFrozen; } } + bool IList.IsFixedSize { get { return false; } } void ICollection.CopyTo(Array array, int index) { diff --git a/csharp/src/Google.Protobuf/Freezable.cs b/csharp/src/Google.Protobuf/Freezable.cs deleted file mode 100644 index d16e1b42..00000000 --- a/csharp/src/Google.Protobuf/Freezable.cs +++ /dev/null @@ -1,58 +0,0 @@ -#region Copyright notice and license -// Protocol Buffers - Google's data interchange format -// Copyright 2015 Google Inc. All rights reserved. -// https://developers.google.com/protocol-buffers/ -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#endregion - -using System; - -namespace Google.Protobuf -{ - /// - /// Extension methods for types. - /// - public static class Freezable - { - /// - /// Throws an if - /// is frozen. - /// - /// - /// This is a convenience methods that freezable types can call before all - /// mutations, to protect frozen objects. - /// - public static void CheckMutable(this IFreezable target) - { - if (target.IsFrozen) - { - throw new InvalidOperationException("Attempt to mutate frozen object"); - } - } - } -} diff --git a/csharp/src/Google.Protobuf/Google.Protobuf.csproj b/csharp/src/Google.Protobuf/Google.Protobuf.csproj index b1f20816..b141e434 100644 --- a/csharp/src/Google.Protobuf/Google.Protobuf.csproj +++ b/csharp/src/Google.Protobuf/Google.Protobuf.csproj @@ -64,7 +64,6 @@ - diff --git a/csharp/src/Google.Protobuf/IMessage.cs b/csharp/src/Google.Protobuf/IMessage.cs index d179c386..60c6f8c3 100644 --- a/csharp/src/Google.Protobuf/IMessage.cs +++ b/csharp/src/Google.Protobuf/IMessage.cs @@ -79,7 +79,7 @@ namespace Google.Protobuf /// the implementation class. /// /// The message type. - public interface IMessage : IMessage, IEquatable, IDeepCloneable, IFreezable where T : IMessage + public interface IMessage : IMessage, IEquatable, IDeepCloneable where T : IMessage { /// /// Merges the given message into this one. @@ -97,10 +97,6 @@ namespace Google.Protobuf /// Additionally, due to the type constraint on T in , /// it is simpler to keep this as a separate interface. /// - /// - /// Freezable types which implement this interface should always return a mutable clone, - /// even if the original object is frozen. - /// /// /// The type itself, returned by the method. public interface IDeepCloneable @@ -111,32 +107,4 @@ namespace Google.Protobuf /// A deep clone of this object. T Clone(); } - - /// - /// Provides a mechanism for freezing a message (or repeated field collection) - /// to make it immutable. - /// - /// - /// Implementations are under no obligation to make this thread-safe: if a freezable - /// type instance is shared between threads before being frozen, and one thread then - /// freezes it, it is possible for other threads to make changes during the freezing - /// operation and also to observe stale values for mutated fields. Objects should be - /// frozen before being made available to other threads. - /// - public interface IFreezable - { - /// - /// Freezes this object. - /// - /// - /// If the object is already frozen, this method has no effect. - /// - void Freeze(); - - /// - /// Returns whether or not this object is frozen (and therefore immutable). - /// - /// true if this object is frozen; false otherwise. - bool IsFrozen { get; } - } } diff --git a/src/google/protobuf/compiler/csharp/csharp_map_field.cc b/src/google/protobuf/compiler/csharp/csharp_map_field.cc index cba24a59..f84ad6f7 100644 --- a/src/google/protobuf/compiler/csharp/csharp_map_field.cc +++ b/src/google/protobuf/compiler/csharp/csharp_map_field.cc @@ -129,8 +129,6 @@ void MapFieldGenerator::GenerateCloningCode(io::Printer* printer) { } void MapFieldGenerator::GenerateFreezingCode(io::Printer* printer) { - printer->Print(variables_, - "$name$_.Freeze();\n"); } } // namespace csharp diff --git a/src/google/protobuf/compiler/csharp/csharp_message.cc b/src/google/protobuf/compiler/csharp/csharp_message.cc index 81d62741..86ef0489 100644 --- a/src/google/protobuf/compiler/csharp/csharp_message.cc +++ b/src/google/protobuf/compiler/csharp/csharp_message.cc @@ -190,7 +190,6 @@ void MessageGenerator::Generate(io::Printer* printer) { " get { return $name$Case_; }\n" "}\n\n" "public void Clear$property_name$() {\n" - " pb::Freezable.CheckMutable(this);\n" " $name$Case_ = $property_name$OneofCase.None;\n" " $name$_ = null;\n" "}\n\n"); @@ -293,33 +292,6 @@ void MessageGenerator::GenerateCloningCode(io::Printer* printer) { } void MessageGenerator::GenerateFreezingCode(io::Printer* printer) { - map vars; - vars["class_name"] = class_name(); - printer->Print( - "public void Freeze() {\n" - " if (IsFrozen) {\n" - " return;\n" - " }\n" - " _frozen = true;\n"); - printer->Indent(); - // Freeze non-oneof fields first (only messages and repeated fields will actually generate any code) - for (int i = 0; i < descriptor_->field_count(); i++) { - if (!descriptor_->field(i)->containing_oneof()) { - scoped_ptr generator( - CreateFieldGeneratorInternal(descriptor_->field(i))); - generator->GenerateFreezingCode(printer); - } - } - - // For each oneof, if the value is freezable, freeze it. We don't actually need to know which type it was. - for (int i = 0; i < descriptor_->oneof_decl_count(); ++i) { - vars["name"] = UnderscoresToCamelCase(descriptor_->oneof_decl(i)->name(), false); - printer->Print(vars, - "if ($name$_ is IFreezable) ((IFreezable) $name$_).Freeze();\n"); - } - - printer->Outdent(); - printer->Print("}\n\n"); } void MessageGenerator::GenerateFrameworkMethods(io::Printer* printer) { diff --git a/src/google/protobuf/compiler/csharp/csharp_message_field.cc b/src/google/protobuf/compiler/csharp/csharp_message_field.cc index d2c3a88b..4f576cd1 100644 --- a/src/google/protobuf/compiler/csharp/csharp_message_field.cc +++ b/src/google/protobuf/compiler/csharp/csharp_message_field.cc @@ -67,7 +67,6 @@ void MessageFieldGenerator::GenerateMembers(io::Printer* printer) { "$access_level$ $type_name$ $property_name$ {\n" " get { return $name$_; }\n" " set {\n" - " pb::Freezable.CheckMutable(this);\n" " $name$_ = value;\n" " }\n" "}\n"); @@ -134,8 +133,6 @@ void MessageFieldGenerator::GenerateCloningCode(io::Printer* printer) { } void MessageFieldGenerator::GenerateFreezingCode(io::Printer* printer) { - printer->Print(variables_, - "if ($has_property_check$) $property_name$.Freeze();\n"); } void MessageFieldGenerator::GenerateCodecCode(io::Printer* printer) { @@ -161,7 +158,6 @@ void MessageOneofFieldGenerator::GenerateMembers(io::Printer* printer) { "$access_level$ $type_name$ $property_name$ {\n" " get { return $has_property_check$ ? ($type_name$) $oneof_name$_ : null; }\n" " set {\n" - " pb::Freezable.CheckMutable(this);\n" " $oneof_name$_ = value;\n" " $oneof_name$Case_ = value == null ? $oneof_property_name$OneofCase.None : $oneof_property_name$OneofCase.$property_name$;\n" " }\n" diff --git a/src/google/protobuf/compiler/csharp/csharp_primitive_field.cc b/src/google/protobuf/compiler/csharp/csharp_primitive_field.cc index 4454ef02..80f4a92d 100644 --- a/src/google/protobuf/compiler/csharp/csharp_primitive_field.cc +++ b/src/google/protobuf/compiler/csharp/csharp_primitive_field.cc @@ -73,8 +73,7 @@ void PrimitiveFieldGenerator::GenerateMembers(io::Printer* printer) { variables_, "$access_level$ $type_name$ $property_name$ {\n" " get { return $name$_; }\n" - " set {\n" - " pb::Freezable.CheckMutable(this);\n"); + " set {\n"); if (is_value_type) { printer->Print( variables_, @@ -176,8 +175,7 @@ void PrimitiveOneofFieldGenerator::GenerateMembers(io::Printer* printer) { variables_, "$access_level$ $type_name$ $property_name$ {\n" " get { return $has_property_check$ ? ($type_name$) $oneof_name$_ : $default_value$; }\n" - " set {\n" - " pb::Freezable.CheckMutable(this);\n"); + " set {\n"); if (is_value_type) { printer->Print( variables_, diff --git a/src/google/protobuf/compiler/csharp/csharp_repeated_enum_field.cc b/src/google/protobuf/compiler/csharp/csharp_repeated_enum_field.cc index c1b29e2b..625631df 100644 --- a/src/google/protobuf/compiler/csharp/csharp_repeated_enum_field.cc +++ b/src/google/protobuf/compiler/csharp/csharp_repeated_enum_field.cc @@ -117,8 +117,6 @@ void RepeatedEnumFieldGenerator::GenerateCloningCode(io::Printer* printer) { } void RepeatedEnumFieldGenerator::GenerateFreezingCode(io::Printer* printer) { - printer->Print(variables_, - "$name$_.Freeze();\n"); } } // namespace csharp diff --git a/src/google/protobuf/compiler/csharp/csharp_repeated_message_field.cc b/src/google/protobuf/compiler/csharp/csharp_repeated_message_field.cc index d939fc79..7fbab681 100644 --- a/src/google/protobuf/compiler/csharp/csharp_repeated_message_field.cc +++ b/src/google/protobuf/compiler/csharp/csharp_repeated_message_field.cc @@ -132,8 +132,6 @@ void RepeatedMessageFieldGenerator::GenerateCloningCode(io::Printer* printer) { } void RepeatedMessageFieldGenerator::GenerateFreezingCode(io::Printer* printer) { - printer->Print(variables_, - "$name$_.Freeze();\n"); } } // namespace csharp diff --git a/src/google/protobuf/compiler/csharp/csharp_repeated_primitive_field.cc b/src/google/protobuf/compiler/csharp/csharp_repeated_primitive_field.cc index 5b5d9b3d..1163ce73 100644 --- a/src/google/protobuf/compiler/csharp/csharp_repeated_primitive_field.cc +++ b/src/google/protobuf/compiler/csharp/csharp_repeated_primitive_field.cc @@ -115,8 +115,6 @@ void RepeatedPrimitiveFieldGenerator::GenerateCloningCode(io::Printer* printer) } void RepeatedPrimitiveFieldGenerator::GenerateFreezingCode(io::Printer* printer) { - printer->Print(variables_, - "$name$_.Freeze();\n"); } } // namespace csharp diff --git a/src/google/protobuf/compiler/csharp/csharp_wrapper_field.cc b/src/google/protobuf/compiler/csharp/csharp_wrapper_field.cc index 75ef5e50..44f832bf 100644 --- a/src/google/protobuf/compiler/csharp/csharp_wrapper_field.cc +++ b/src/google/protobuf/compiler/csharp/csharp_wrapper_field.cc @@ -76,7 +76,6 @@ void WrapperFieldGenerator::GenerateMembers(io::Printer* printer) { "$access_level$ $type_name$ $property_name$ {\n" " get { return $name$_; }\n" " set {\n" - " pb::Freezable.CheckMutable(this);\n" " $name$_ = value;\n" " }\n" "}\n"); @@ -172,7 +171,6 @@ void WrapperOneofFieldGenerator::GenerateMembers(io::Printer* printer) { "$access_level$ $type_name$ $property_name$ {\n" " get { return $has_property_check$ ? ($type_name$) $oneof_name$_ : ($type_name$) null; }\n" " set {\n" - " pb::Freezable.CheckMutable(this);\n" " $oneof_name$_ = value;\n" " $oneof_name$Case_ = value == null ? $oneof_property_name$OneofCase.None : $oneof_property_name$OneofCase.$property_name$;\n" " }\n" -- cgit v1.2.3 From 811fc89f0eb036d95653f5fed4b0ffea292ce791 Mon Sep 17 00:00:00 2001 From: Jon Skeet Date: Tue, 4 Aug 2015 15:58:39 +0100 Subject: Document everything, and turn on errors if we fail to document anything in the future. --- csharp/src/Google.Protobuf/ByteString.cs | 74 ++++++++++- csharp/src/Google.Protobuf/CodedInputStream.cs | 2 +- csharp/src/Google.Protobuf/CodedOutputStream.cs | 7 + csharp/src/Google.Protobuf/Collections/MapField.cs | 141 ++++++++++++++++++++- .../Google.Protobuf/Collections/RepeatedField.cs | 111 +++++++++++++++- csharp/src/Google.Protobuf/FieldCodec.cs | 107 ++++++++++++++++ csharp/src/Google.Protobuf/Google.Protobuf.csproj | 12 +- csharp/src/Google.Protobuf/IMessage.cs | 3 +- .../InvalidProtocolBufferException.cs | 7 +- csharp/src/Google.Protobuf/JsonFormatter.cs | 13 ++ csharp/src/Google.Protobuf/MessageExtensions.cs | 44 +++++++ csharp/src/Google.Protobuf/MessageParser.cs | 24 ++++ .../Google.Protobuf/Reflection/DescriptorBase.cs | 3 + .../Reflection/EnumValueDescriptor.cs | 11 +- .../Google.Protobuf/Reflection/FieldDescriptor.cs | 37 +++++- csharp/src/Google.Protobuf/Reflection/FieldType.cs | 57 ++++++++- .../Google.Protobuf/Reflection/FileDescriptor.cs | 21 ++- .../src/Google.Protobuf/Reflection/IDescriptor.cs | 11 ++ .../Google.Protobuf/Reflection/IFieldAccessor.cs | 5 +- .../Reflection/MessageDescriptor.cs | 6 +- .../Google.Protobuf/Reflection/OneofAccessor.cs | 6 + .../Google.Protobuf/Reflection/OneofDescriptor.cs | 25 +++- .../WellKnownTypes/DurationPartial.cs | 6 + .../WellKnownTypes/TimeExtensions.cs | 19 +++ .../WellKnownTypes/TimestampPartial.cs | 7 +- csharp/src/Google.Protobuf/WireFormat.cs | 89 ++++--------- 26 files changed, 741 insertions(+), 107 deletions(-) (limited to 'csharp/src/Google.Protobuf/IMessage.cs') diff --git a/csharp/src/Google.Protobuf/ByteString.cs b/csharp/src/Google.Protobuf/ByteString.cs index cf5468d2..2aa62483 100644 --- a/csharp/src/Google.Protobuf/ByteString.cs +++ b/csharp/src/Google.Protobuf/ByteString.cs @@ -105,23 +105,35 @@ namespace Google.Protobuf get { return bytes.Length; } } + /// + /// Returns true if this byte string is empty, false otherwise. + /// public bool IsEmpty { get { return Length == 0; } } + /// + /// Converts this into a byte array. + /// + /// The data is copied - changes to the returned array will not be reflected in this ByteString. + /// A byte array with the same data as this ByteString. public byte[] ToByteArray() { return (byte[]) bytes.Clone(); } + /// + /// Converts this into a standard base64 representation. + /// + /// A base64 representation of this ByteString. public string ToBase64() { return Convert.ToBase64String(bytes); } /// - /// Constructs a ByteString from the Base64 Encoded String. + /// Constructs a from the Base64 Encoded String. /// public static ByteString FromBase64(string bytes) { @@ -131,7 +143,7 @@ namespace Google.Protobuf } /// - /// Constructs a ByteString from the given array. The contents + /// Constructs a from the given array. The contents /// are copied, so further modifications to the array will not /// be reflected in the returned ByteString. /// This method can also be invoked in ByteString.CopyFrom(0xaa, 0xbb, ...) form @@ -143,7 +155,7 @@ namespace Google.Protobuf } /// - /// Constructs a ByteString from a portion of a byte array. + /// Constructs a from a portion of a byte array. /// public static ByteString CopyFrom(byte[] bytes, int offset, int count) { @@ -153,7 +165,7 @@ namespace Google.Protobuf } /// - /// Creates a new ByteString by encoding the specified text with + /// Creates a new by encoding the specified text with /// the given encoding. /// public static ByteString CopyFrom(string text, Encoding encoding) @@ -162,7 +174,7 @@ namespace Google.Protobuf } /// - /// Creates a new ByteString by encoding the specified text in UTF-8. + /// Creates a new by encoding the specified text in UTF-8. /// public static ByteString CopyFromUtf8(string text) { @@ -177,21 +189,46 @@ namespace Google.Protobuf get { return bytes[index]; } } + /// + /// Converts this into a string by applying the given encoding. + /// + /// + /// This method should only be used to convert binary data which was the result of encoding + /// text with the given encoding. + /// + /// The encoding to use to decode the binary data into text. + /// The result of decoding the binary data with the given decoding. public string ToString(Encoding encoding) { return encoding.GetString(bytes, 0, bytes.Length); } + /// + /// Converts this into a string by applying the UTF-8 encoding. + /// + /// + /// This method should only be used to convert binary data which was the result of encoding + /// text with UTF-8. + /// + /// The result of decoding the binary data with the given decoding. public string ToStringUtf8() { return ToString(Encoding.UTF8); } + /// + /// Returns an iterator over the bytes in this . + /// + /// An iterator over the bytes in this object. public IEnumerator GetEnumerator() { return ((IEnumerable) bytes).GetEnumerator(); } + /// + /// Returns an iterator over the bytes in this . + /// + /// An iterator over the bytes in this object. IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); @@ -206,6 +243,12 @@ namespace Google.Protobuf return new CodedInputStream(bytes); } + /// + /// Compares two byte strings for equality. + /// + /// The first byte string to compare. + /// The second byte string to compare. + /// true if the byte strings are equal; false otherwise. public static bool operator ==(ByteString lhs, ByteString rhs) { if (ReferenceEquals(lhs, rhs)) @@ -230,6 +273,12 @@ namespace Google.Protobuf return true; } + /// + /// Compares two byte strings for inequality. + /// + /// The first byte string to compare. + /// The second byte string to compare. + /// false if the byte strings are equal; true otherwise. public static bool operator !=(ByteString lhs, ByteString rhs) { return !(lhs == rhs); @@ -237,11 +286,21 @@ namespace Google.Protobuf // TODO(jonskeet): CopyTo if it turns out to be required + /// + /// Compares this byte string with another object. + /// + /// The object to compare this with. + /// true if refers to an equal ; false otherwise. public override bool Equals(object obj) { return this == (obj as ByteString); } + /// + /// Returns a hash code for this object. Two equal byte strings + /// will return the same hash code. + /// + /// A hash code for this object. public override int GetHashCode() { int ret = 23; @@ -252,6 +311,11 @@ namespace Google.Protobuf return ret; } + /// + /// Compares this byte string with another. + /// + /// The to compare this with. + /// true if refers to an equal byte string; false otherwise. public bool Equals(ByteString other) { return this == other; diff --git a/csharp/src/Google.Protobuf/CodedInputStream.cs b/csharp/src/Google.Protobuf/CodedInputStream.cs index f2e1d661..a1abfcb0 100644 --- a/csharp/src/Google.Protobuf/CodedInputStream.cs +++ b/csharp/src/Google.Protobuf/CodedInputStream.cs @@ -66,7 +66,7 @@ namespace Google.Protobuf internal const int DefaultRecursionLimit = 64; internal const int DefaultSizeLimit = 64 << 20; // 64MB - public const int BufferSize = 4096; + internal const int BufferSize = 4096; /// /// The total number of bytes read before the current buffer. The diff --git a/csharp/src/Google.Protobuf/CodedOutputStream.cs b/csharp/src/Google.Protobuf/CodedOutputStream.cs index b084c14f..08ea7005 100644 --- a/csharp/src/Google.Protobuf/CodedOutputStream.cs +++ b/csharp/src/Google.Protobuf/CodedOutputStream.cs @@ -297,6 +297,10 @@ namespace Google.Protobuf WriteInt32(value); } + /// + /// Writes an sfixed32 value, without a tag, to the stream. + /// + /// The value to write. public void WriteSFixed32(int value) { WriteRawLittleEndian32((uint) value); @@ -649,6 +653,9 @@ namespace Google.Protobuf } } + /// + /// Flushes any buffered data to the underlying stream (if there is one). + /// public void Flush() { if (output != null) diff --git a/csharp/src/Google.Protobuf/Collections/MapField.cs b/csharp/src/Google.Protobuf/Collections/MapField.cs index fc94fd5c..fed3d062 100644 --- a/csharp/src/Google.Protobuf/Collections/MapField.cs +++ b/csharp/src/Google.Protobuf/Collections/MapField.cs @@ -42,14 +42,14 @@ namespace Google.Protobuf.Collections /// /// Representation of a map field in a Protocol Buffer message. /// + /// Key type in the map. Must be a type supported by Protocol Buffer map keys. + /// Value type in the map. Must be a type supported by Protocol Buffers. /// /// This implementation preserves insertion order for simplicity of testing /// code using maps fields. Overwriting an existing entry does not change the /// position of that entry within the map. Equality is not order-sensitive. - /// For string keys, the equality comparison is provided by . + /// For string keys, the equality comparison is provided by . /// - /// Key type in the map. Must be a type supported by Protocol Buffer map keys. - /// Value type in the map. Must be a type supported by Protocol Buffers. public sealed class MapField : IDeepCloneable>, IDictionary, IEquatable>, IDictionary { // TODO: Don't create the map/list until we have an entry. (Assume many maps will be empty.) @@ -81,6 +81,12 @@ namespace Google.Protobuf.Collections this.allowNullValues = allowNullValues; } + /// + /// Creates a deep clone of this object. + /// + /// + /// A deep clone of this object. + /// public MapField Clone() { var clone = new MapField(allowNullValues); @@ -100,6 +106,15 @@ namespace Google.Protobuf.Collections return clone; } + /// + /// Adds the specified key/value pair to the map. + /// + /// + /// This operation fails if the key already exists in the map. To replace an existing entry, use the indexer. + /// + /// The key to add + /// The value to add. + /// The given key already exists in map. public void Add(TKey key, TValue value) { // Validation of arguments happens in ContainsKey and the indexer @@ -110,12 +125,22 @@ namespace Google.Protobuf.Collections this[key] = value; } + /// + /// Determines whether the specified key is present in the map. + /// + /// The key to check. + /// true if the map contains the given key; false otherwise. public bool ContainsKey(TKey key) { Preconditions.CheckNotNullUnconstrained(key, "key"); return map.ContainsKey(key); } + /// + /// Removes the entry identified by the given key from the map. + /// + /// The key indicating the entry to remove from the map. + /// true if the map contained the given key before the entry was removed; false otherwise. public bool Remove(TKey key) { Preconditions.CheckNotNullUnconstrained(key, "key"); @@ -132,6 +157,14 @@ namespace Google.Protobuf.Collections } } + /// + /// Gets the value associated with the specified key. + /// + /// The key whose value to get. + /// When this method returns, the value associated with the specified key, if the key is found; + /// otherwise, the default value for the type of the parameter. + /// This parameter is passed uninitialized. + /// true if the map contains an element with the specified key; otherwise, false. public bool TryGetValue(TKey key, out TValue value) { LinkedListNode> node; @@ -147,6 +180,13 @@ namespace Google.Protobuf.Collections } } + /// + /// Gets or sets the value associated with the specified key. + /// + /// The key of the value to get or set. + /// The property is retrieved and key does not exist in the collection. + /// The value associated with the specified key. If the specified key is not found, + /// a get operation throws a , and a set operation creates a new element with the specified key. public TValue this[TKey key] { get @@ -182,9 +222,21 @@ namespace Google.Protobuf.Collections } // TODO: Make these views? + + /// + /// Gets a collection containing the keys in the map. + /// public ICollection Keys { get { return list.Select(t => t.Key).ToList(); } } + + /// + /// Gets a collection containing the values in the map. + /// public ICollection Values { get { return list.Select(t => t.Value).ToList(); } } + /// + /// Adds the specified entries to the map. + /// + /// The entries to add to the map. public void Add(IDictionary entries) { Preconditions.CheckNotNull(entries, "entries"); @@ -194,27 +246,51 @@ namespace Google.Protobuf.Collections } } + /// + /// Returns an enumerator that iterates through the collection. + /// + /// + /// An enumerator that can be used to iterate through the collection. + /// public IEnumerator> GetEnumerator() { return list.GetEnumerator(); } + /// + /// Returns an enumerator that iterates through a collection. + /// + /// + /// An object that can be used to iterate through the collection. + /// IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } + /// + /// Adds the specified item to the map. + /// + /// The item to add to the map. void ICollection>.Add(KeyValuePair item) { Add(item.Key, item.Value); } + /// + /// Removes all items from the map. + /// public void Clear() { list.Clear(); map.Clear(); } + /// + /// Determines whether map contains an entry equivalent to the given key/value pair. + /// + /// The key/value pair to find. + /// bool ICollection>.Contains(KeyValuePair item) { TValue value; @@ -222,11 +298,22 @@ namespace Google.Protobuf.Collections && EqualityComparer.Default.Equals(item.Value, value); } + /// + /// Copies the key/value pairs in this map to an array. + /// + /// The array to copy the entries into. + /// The index of the array at which to start copying values. void ICollection>.CopyTo(KeyValuePair[] array, int arrayIndex) { list.CopyTo(array, arrayIndex); } + /// + /// Removes the specified key/value pair from the map. + /// + /// Both the key and the value must be found for the entry to be removed. + /// The key/value pair to remove. + /// true if the key/value pair was found and removed; false otherwise. bool ICollection>.Remove(KeyValuePair item) { if (item.Key == null) @@ -252,14 +339,34 @@ namespace Google.Protobuf.Collections /// public bool AllowsNullValues { get { return allowNullValues; } } + /// + /// Gets the number of elements contained in the map. + /// public int Count { get { return list.Count; } } + + /// + /// Gets a value indicating whether the map is read-only. + /// public bool IsReadOnly { get { return false; } } + /// + /// Determines whether the specified , is equal to this instance. + /// + /// The to compare with this instance. + /// + /// true if the specified is equal to this instance; otherwise, false. + /// public override bool Equals(object other) { return Equals(other as MapField); } + /// + /// Returns a hash code for this instance. + /// + /// + /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. + /// public override int GetHashCode() { var valueComparer = EqualityComparer.Default; @@ -271,6 +378,14 @@ namespace Google.Protobuf.Collections return hash; } + /// + /// Compares this map with another for equality. + /// + /// + /// The order of the key/value pairs in the maps is not deemed significant in this comparison. + /// + /// The map to compare this with. + /// true if refers to an equal map; false otherwise. public bool Equals(MapField other) { if (other == null) @@ -322,6 +437,12 @@ namespace Google.Protobuf.Collections } while (input.MaybeConsumeTag(codec.MapTag)); } + /// + /// Writes the contents of this map to the given coded output stream, using the specified codec + /// to encode each entry. + /// + /// The output stream to write to. + /// The codec to use for each entry. public void WriteTo(CodedOutputStream output, Codec codec) { var message = new Codec.MessageAdapter(codec); @@ -334,6 +455,11 @@ namespace Google.Protobuf.Collections } } + /// + /// Calculates the size of this map based on the given entry codec. + /// + /// The codec to use to encode each entry. + /// public int CalculateSize(Codec codec) { if (Count == 0) @@ -446,7 +572,7 @@ namespace Google.Protobuf.Collections } /// - /// A codec for a specific map field. This contains all the information required to encoded and + /// A codec for a specific map field. This contains all the information required to encode and /// decode the nested messages. /// public sealed class Codec @@ -455,6 +581,13 @@ namespace Google.Protobuf.Collections private readonly FieldCodec valueCodec; private readonly uint mapTag; + /// + /// Creates a new entry codec based on a separate key codec and value codec, + /// and the tag to use for each map entry. + /// + /// The key codec. + /// The value codec. + /// The map tag to use to introduce each map entry. public Codec(FieldCodec keyCodec, FieldCodec valueCodec, uint mapTag) { this.keyCodec = keyCodec; diff --git a/csharp/src/Google.Protobuf/Collections/RepeatedField.cs b/csharp/src/Google.Protobuf/Collections/RepeatedField.cs index 3ce19c81..c901864a 100644 --- a/csharp/src/Google.Protobuf/Collections/RepeatedField.cs +++ b/csharp/src/Google.Protobuf/Collections/RepeatedField.cs @@ -30,7 +30,6 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #endregion -using Google.Protobuf.Reflection; using System; using System.Collections; using System.Collections.Generic; @@ -81,6 +80,11 @@ namespace Google.Protobuf.Collections return clone; } + /// + /// Adds the entries from the given input stream, decoding them with the specified codec. + /// + /// The input stream to read from. + /// The codec to use in order to read each entry. public void AddEntriesFrom(CodedInputStream input, FieldCodec codec) { // TODO: Inline some of the Add code, so we can avoid checking the size on every @@ -112,6 +116,12 @@ namespace Google.Protobuf.Collections } } + /// + /// Calculates the size of this collection based on the given codec. + /// + /// The codec to use when encoding each field. + /// The number of bytes that would be written to a by , + /// using the same codec. public int CalculateSize(FieldCodec codec) { if (count == 0) @@ -157,6 +167,12 @@ namespace Google.Protobuf.Collections } } + /// + /// Writes the contents of this collection to the given , + /// encoding each value using the specified codec. + /// + /// The output stream to write to. + /// The codec to use when encoding each value. public void WriteTo(CodedOutputStream output, FieldCodec codec) { if (count == 0) @@ -200,6 +216,10 @@ namespace Google.Protobuf.Collections } } + /// + /// Adds the specified item to the collection. + /// + /// The item to add. public void Add(T item) { if (item == null) @@ -210,22 +230,40 @@ namespace Google.Protobuf.Collections array[count++] = item; } + /// + /// Removes all items from the collection. + /// public void Clear() { array = EmptyArray; count = 0; } + /// + /// Determines whether this collection contains the given item. + /// + /// The item to find. + /// true if this collection contains the given item; false otherwise. public bool Contains(T item) { return IndexOf(item) != -1; } + /// + /// Copies this collection to the given array. + /// + /// The array to copy to. + /// The first index of the array to copy to. public void CopyTo(T[] array, int arrayIndex) { Array.Copy(this.array, 0, array, arrayIndex, count); } + /// + /// Removes the specified item from the collection + /// + /// The item to remove. + /// true if the item was found and removed; false otherwise. public bool Remove(T item) { int index = IndexOf(item); @@ -239,10 +277,22 @@ namespace Google.Protobuf.Collections return true; } + /// + /// Gets the number of elements contained in the collection. + /// public int Count { get { return count; } } + /// + /// Gets a value indicating whether the collection is read-only. + /// public bool IsReadOnly { get { return false; } } + // TODO: Remove this overload and just handle it in the one below, at execution time? + + /// + /// Adds all of the specified values into this collection. + /// + /// The values to add to this collection. public void Add(RepeatedField values) { if (values == null) @@ -255,6 +305,10 @@ namespace Google.Protobuf.Collections count += values.count; } + /// + /// Adds all of the specified values into this collection. + /// + /// The values to add to this collection. public void Add(IEnumerable values) { if (values == null) @@ -268,6 +322,12 @@ namespace Google.Protobuf.Collections } } + /// + /// Returns an enumerator that iterates through the collection. + /// + /// + /// An enumerator that can be used to iterate through the collection. + /// public IEnumerator GetEnumerator() { for (int i = 0; i < count; i++) @@ -276,16 +336,35 @@ namespace Google.Protobuf.Collections } } + /// + /// Determines whether the specified , is equal to this instance. + /// + /// The to compare with this instance. + /// + /// true if the specified is equal to this instance; otherwise, false. + /// public override bool Equals(object obj) { return Equals(obj as RepeatedField); } + /// + /// Returns an enumerator that iterates through a collection. + /// + /// + /// An object that can be used to iterate through the collection. + /// IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); - } + } + /// + /// Returns a hash code for this instance. + /// + /// + /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. + /// public override int GetHashCode() { int hash = 0; @@ -296,6 +375,11 @@ namespace Google.Protobuf.Collections return hash; } + /// + /// Compares this repeated field with another for equality. + /// + /// The repeated field to compare this with. + /// true if refers to an equal repeated field; false otherwise. public bool Equals(RepeatedField other) { if (ReferenceEquals(other, null)) @@ -322,6 +406,12 @@ namespace Google.Protobuf.Collections return true; } + /// + /// Returns the index of the given item within the collection, or -1 if the item is not + /// present. + /// + /// The item to find in the collection. + /// The zero-based index of the item, or -1 if it is not found. public int IndexOf(T item) { if (item == null) @@ -340,6 +430,11 @@ namespace Google.Protobuf.Collections return -1; } + /// + /// Inserts the given item at the specified index. + /// + /// The index at which to insert the item. + /// The item to insert. public void Insert(int index, T item) { if (item == null) @@ -356,6 +451,10 @@ namespace Google.Protobuf.Collections count++; } + /// + /// Removes the item at the given index. + /// + /// The zero-based index of the item to remove. public void RemoveAt(int index) { if (index < 0 || index >= count) @@ -367,6 +466,14 @@ namespace Google.Protobuf.Collections array[count] = default(T); } + /// + /// Gets or sets the item at the specified index. + /// + /// + /// The element at the specified index. + /// + /// The zero-based index of the element to get or set. + /// The item at the specified index. public T this[int index] { get diff --git a/csharp/src/Google.Protobuf/FieldCodec.cs b/csharp/src/Google.Protobuf/FieldCodec.cs index 85462787..1076c2a9 100644 --- a/csharp/src/Google.Protobuf/FieldCodec.cs +++ b/csharp/src/Google.Protobuf/FieldCodec.cs @@ -41,76 +41,152 @@ namespace Google.Protobuf public static class FieldCodec { // TODO: Avoid the "dual hit" of lambda expressions: create open delegates instead. (At least test...) + + /// + /// Retrieves a codec suitable for a string field with the given tag. + /// + /// The tag. + /// A codec for the given tag. public static FieldCodec ForString(uint tag) { return new FieldCodec(input => input.ReadString(), (output, value) => output.WriteString(value), CodedOutputStream.ComputeStringSize, tag); } + /// + /// Retrieves a codec suitable for a bytes field with the given tag. + /// + /// The tag. + /// A codec for the given tag. public static FieldCodec ForBytes(uint tag) { return new FieldCodec(input => input.ReadBytes(), (output, value) => output.WriteBytes(value), CodedOutputStream.ComputeBytesSize, tag); } + /// + /// Retrieves a codec suitable for a bool field with the given tag. + /// + /// The tag. + /// A codec for the given tag. public static FieldCodec ForBool(uint tag) { return new FieldCodec(input => input.ReadBool(), (output, value) => output.WriteBool(value), CodedOutputStream.ComputeBoolSize, tag); } + /// + /// Retrieves a codec suitable for an int32 field with the given tag. + /// + /// The tag. + /// A codec for the given tag. public static FieldCodec ForInt32(uint tag) { return new FieldCodec(input => input.ReadInt32(), (output, value) => output.WriteInt32(value), CodedOutputStream.ComputeInt32Size, tag); } + /// + /// Retrieves a codec suitable for an sint32 field with the given tag. + /// + /// The tag. + /// A codec for the given tag. public static FieldCodec ForSInt32(uint tag) { return new FieldCodec(input => input.ReadSInt32(), (output, value) => output.WriteSInt32(value), CodedOutputStream.ComputeSInt32Size, tag); } + /// + /// Retrieves a codec suitable for a fixed32 field with the given tag. + /// + /// The tag. + /// A codec for the given tag. public static FieldCodec ForFixed32(uint tag) { return new FieldCodec(input => input.ReadFixed32(), (output, value) => output.WriteFixed32(value), 4, tag); } + /// + /// Retrieves a codec suitable for an sfixed32 field with the given tag. + /// + /// The tag. + /// A codec for the given tag. public static FieldCodec ForSFixed32(uint tag) { return new FieldCodec(input => input.ReadSFixed32(), (output, value) => output.WriteSFixed32(value), 4, tag); } + /// + /// Retrieves a codec suitable for a uint32 field with the given tag. + /// + /// The tag. + /// A codec for the given tag. public static FieldCodec ForUInt32(uint tag) { return new FieldCodec(input => input.ReadUInt32(), (output, value) => output.WriteUInt32(value), CodedOutputStream.ComputeUInt32Size, tag); } + /// + /// Retrieves a codec suitable for an int64 field with the given tag. + /// + /// The tag. + /// A codec for the given tag. public static FieldCodec ForInt64(uint tag) { return new FieldCodec(input => input.ReadInt64(), (output, value) => output.WriteInt64(value), CodedOutputStream.ComputeInt64Size, tag); } + /// + /// Retrieves a codec suitable for an sint64 field with the given tag. + /// + /// The tag. + /// A codec for the given tag. public static FieldCodec ForSInt64(uint tag) { return new FieldCodec(input => input.ReadSInt64(), (output, value) => output.WriteSInt64(value), CodedOutputStream.ComputeSInt64Size, tag); } + /// + /// Retrieves a codec suitable for a fixed64 field with the given tag. + /// + /// The tag. + /// A codec for the given tag. public static FieldCodec ForFixed64(uint tag) { return new FieldCodec(input => input.ReadFixed64(), (output, value) => output.WriteFixed64(value), 8, tag); } + /// + /// Retrieves a codec suitable for an sfixed64 field with the given tag. + /// + /// The tag. + /// A codec for the given tag. public static FieldCodec ForSFixed64(uint tag) { return new FieldCodec(input => input.ReadSFixed64(), (output, value) => output.WriteSFixed64(value), 8, tag); } + /// + /// Retrieves a codec suitable for a uint64 field with the given tag. + /// + /// The tag. + /// A codec for the given tag. public static FieldCodec ForUInt64(uint tag) { return new FieldCodec(input => input.ReadUInt64(), (output, value) => output.WriteUInt64(value), CodedOutputStream.ComputeUInt64Size, tag); } + /// + /// Retrieves a codec suitable for a float field with the given tag. + /// + /// The tag. + /// A codec for the given tag. public static FieldCodec ForFloat(uint tag) { return new FieldCodec(input => input.ReadFloat(), (output, value) => output.WriteFloat(value), CodedOutputStream.ComputeFloatSize, tag); } + /// + /// Retrieves a codec suitable for a double field with the given tag. + /// + /// The tag. + /// A codec for the given tag. public static FieldCodec ForDouble(uint tag) { return new FieldCodec(input => input.ReadDouble(), (output, value) => output.WriteDouble(value), CodedOutputStream.ComputeDoubleSize, tag); @@ -118,6 +194,14 @@ namespace Google.Protobuf // Enums are tricky. We can probably use expression trees to build these delegates automatically, // but it's easy to generate the code for it. + + /// + /// Retrieves a codec suitable for an enum field with the given tag. + /// + /// The tag. + /// A conversion function from to the enum type. + /// A conversion function from the enum type to . + /// A codec for the given tag. public static FieldCodec ForEnum(uint tag, Func toInt32, Func fromInt32) { return new FieldCodec(input => fromInt32( @@ -126,6 +210,12 @@ namespace Google.Protobuf value => CodedOutputStream.ComputeEnumSize(toInt32(value)), tag); } + /// + /// Retrieves a codec suitable for a message field with the given tag. + /// + /// The tag. + /// A parser to use for the message type. + /// A codec for the given tag. public static FieldCodec ForMessage(uint tag, MessageParser parser) where T : IMessage { return new FieldCodec(input => { T message = parser.CreateTemplate(); input.ReadMessage(message); return message; }, @@ -344,8 +434,20 @@ namespace Google.Protobuf /// internal int FixedSize { get { return fixedSize; } } + /// + /// Gets the tag of the codec. + /// + /// + /// The tag of the codec. + /// public uint Tag { get { return tag; } } + /// + /// Gets the default value of the codec's type. + /// + /// + /// The default value of the codec's type. + /// public T DefaultValue { get { return defaultValue; } } /// @@ -360,6 +462,11 @@ namespace Google.Protobuf } } + /// + /// Reads a value of the codec type from the given . + /// + /// The input stream to read from. + /// The value read from the stream. public T Read(CodedInputStream input) { return reader(input); diff --git a/csharp/src/Google.Protobuf/Google.Protobuf.csproj b/csharp/src/Google.Protobuf/Google.Protobuf.csproj index 8a19067a..033edfbb 100644 --- a/csharp/src/Google.Protobuf/Google.Protobuf.csproj +++ b/csharp/src/Google.Protobuf/Google.Protobuf.csproj @@ -24,12 +24,14 @@ bin\Debug obj\Debug\ bin\Debug\Google.Protobuf.xml - 1591, 1570, 1571, 1572, 1573, 1574 + + DEBUG;TRACE prompt 4 true Off + true pdbonly @@ -37,12 +39,14 @@ bin\Release obj\Release\ $(OutputPath)\$(AssemblyName).xml - 1591, 1570, 1571, 1572, 1573, 1574 + + TRACE prompt 4 true Off + true pdbonly @@ -50,7 +54,8 @@ bin\ReleaseSigned obj\ReleaseSigned\ $(OutputPath)\$(AssemblyName).xml - 1591, 1570, 1571, 1572, 1573, 1574 + + TRACE;SIGNED prompt 4 @@ -58,6 +63,7 @@ Off True C:\keys\Google.Protobuf.snk + true diff --git a/csharp/src/Google.Protobuf/IMessage.cs b/csharp/src/Google.Protobuf/IMessage.cs index 60c6f8c3..147c83ca 100644 --- a/csharp/src/Google.Protobuf/IMessage.cs +++ b/csharp/src/Google.Protobuf/IMessage.cs @@ -91,8 +91,9 @@ namespace Google.Protobuf /// /// Generic interface for a deeply cloneable type. - /// + /// /// + /// /// All generated messages implement this interface, but so do some non-message types. /// Additionally, due to the type constraint on T in , /// it is simpler to keep this as a separate interface. diff --git a/csharp/src/Google.Protobuf/InvalidProtocolBufferException.cs b/csharp/src/Google.Protobuf/InvalidProtocolBufferException.cs index 4f89347d..bbba9b3b 100644 --- a/csharp/src/Google.Protobuf/InvalidProtocolBufferException.cs +++ b/csharp/src/Google.Protobuf/InvalidProtocolBufferException.cs @@ -45,7 +45,7 @@ namespace Google.Protobuf { } - public static InvalidProtocolBufferException TruncatedMessage() + internal static InvalidProtocolBufferException TruncatedMessage() { return new InvalidProtocolBufferException( "While parsing a protocol message, the input ended unexpectedly " + @@ -61,12 +61,15 @@ namespace Google.Protobuf "which claimed to have negative size."); } - public static InvalidProtocolBufferException MalformedVarint() + internal static InvalidProtocolBufferException MalformedVarint() { return new InvalidProtocolBufferException( "CodedInputStream encountered a malformed varint."); } + /// + /// Creates an exception for an error condition of an invalid tag being encountered. + /// public static InvalidProtocolBufferException InvalidTag() { return new InvalidProtocolBufferException( diff --git a/csharp/src/Google.Protobuf/JsonFormatter.cs b/csharp/src/Google.Protobuf/JsonFormatter.cs index 999e106c..edc47186 100644 --- a/csharp/src/Google.Protobuf/JsonFormatter.cs +++ b/csharp/src/Google.Protobuf/JsonFormatter.cs @@ -114,11 +114,20 @@ namespace Google.Protobuf private readonly Settings settings; + /// + /// Creates a new formatted with the given settings. + /// + /// The settings. public JsonFormatter(Settings settings) { this.settings = settings; } + /// + /// Formats the specified message as JSON. + /// + /// The message to format. + /// The formatted message. public string Format(IMessage message) { Preconditions.CheckNotNull(message, "message"); @@ -768,6 +777,10 @@ namespace Google.Protobuf /// public bool FormatDefaultValues { get { return formatDefaultValues; } } + /// + /// Creates a new object with the specified formatting of default values. + /// + /// true if default values (0, empty strings etc) should be formatted; false otherwise. public Settings(bool formatDefaultValues) { this.formatDefaultValues = formatDefaultValues; diff --git a/csharp/src/Google.Protobuf/MessageExtensions.cs b/csharp/src/Google.Protobuf/MessageExtensions.cs index 6e7d47bc..ee78dc8d 100644 --- a/csharp/src/Google.Protobuf/MessageExtensions.cs +++ b/csharp/src/Google.Protobuf/MessageExtensions.cs @@ -39,6 +39,11 @@ namespace Google.Protobuf /// public static class MessageExtensions { + /// + /// Merges data from the given byte array into an existing message. + /// + /// The message to merge the data into. + /// The data to merge, which must be protobuf-encoded binary data. public static void MergeFrom(this IMessage message, byte[] data) { Preconditions.CheckNotNull(message, "message"); @@ -48,6 +53,11 @@ namespace Google.Protobuf input.CheckLastTagWas(0); } + /// + /// Merges data from the given byte string into an existing message. + /// + /// The message to merge the data into. + /// The data to merge, which must be protobuf-encoded binary data. public static void MergeFrom(this IMessage message, ByteString data) { Preconditions.CheckNotNull(message, "message"); @@ -57,6 +67,11 @@ namespace Google.Protobuf input.CheckLastTagWas(0); } + /// + /// Merges data from the given stream into an existing message. + /// + /// The message to merge the data into. + /// Stream containing the data to merge, which must be protobuf-encoded binary data. public static void MergeFrom(this IMessage message, Stream input) { Preconditions.CheckNotNull(message, "message"); @@ -66,6 +81,15 @@ namespace Google.Protobuf codedInput.CheckLastTagWas(0); } + /// + /// Merges length-delimited data from the given stream into an existing message. + /// + /// + /// The stream is expected to contain a length and then the data. Only the amount of data + /// specified by the length will be consumed. + /// + /// The message to merge the data into. + /// Stream containing the data to merge, which must be protobuf-encoded binary data. public static void MergeDelimitedFrom(this IMessage message, Stream input) { Preconditions.CheckNotNull(message, "message"); @@ -75,6 +99,11 @@ namespace Google.Protobuf message.MergeFrom(limitedStream); } + /// + /// Converts the given message into a byte array in protobuf encoding. + /// + /// The message to convert. + /// The message data as a byte array. public static byte[] ToByteArray(this IMessage message) { Preconditions.CheckNotNull(message, "message"); @@ -85,6 +114,11 @@ namespace Google.Protobuf return result; } + /// + /// Writes the given message data to the given stream in protobuf encoding. + /// + /// The message to write to the stream. + /// The stream to write to. public static void WriteTo(this IMessage message, Stream output) { Preconditions.CheckNotNull(message, "message"); @@ -94,6 +128,11 @@ namespace Google.Protobuf codedOutput.Flush(); } + /// + /// Writes the length and then data of the given message to a stream. + /// + /// The message to write. + /// The output stream to write to. public static void WriteDelimitedTo(this IMessage message, Stream output) { Preconditions.CheckNotNull(message, "message"); @@ -104,6 +143,11 @@ namespace Google.Protobuf codedOutput.Flush(); } + /// + /// Converts the given message into a byte string in protobuf encoding. + /// + /// The message to convert. + /// The message data as a byte string. public static ByteString ToByteString(this IMessage message) { Preconditions.CheckNotNull(message, "message"); diff --git a/csharp/src/Google.Protobuf/MessageParser.cs b/csharp/src/Google.Protobuf/MessageParser.cs index bfa63ae5..6a6f1017 100644 --- a/csharp/src/Google.Protobuf/MessageParser.cs +++ b/csharp/src/Google.Protobuf/MessageParser.cs @@ -90,6 +90,11 @@ namespace Google.Protobuf return message; } + /// + /// Parses a message from the given byte string. + /// + /// The data to parse. + /// The parsed message. public T ParseFrom(ByteString data) { Preconditions.CheckNotNull(data, "data"); @@ -98,6 +103,11 @@ namespace Google.Protobuf return message; } + /// + /// Parses a message from the given stream. + /// + /// The stream to parse. + /// The parsed message. public T ParseFrom(Stream input) { T message = factory(); @@ -105,6 +115,15 @@ namespace Google.Protobuf return message; } + /// + /// Parses a length-delimited message from the given stream. + /// + /// + /// The stream is expected to contain a length and then the data. Only the amount of data + /// specified by the length will be consumed. + /// + /// The stream to parse. + /// The parsed message. public T ParseDelimitedFrom(Stream input) { T message = factory(); @@ -112,6 +131,11 @@ namespace Google.Protobuf return message; } + /// + /// Parses a message from the given coded input stream. + /// + /// The stream to parse. + /// The parsed message. public T ParseFrom(CodedInputStream input) { T message = factory(); diff --git a/csharp/src/Google.Protobuf/Reflection/DescriptorBase.cs b/csharp/src/Google.Protobuf/Reflection/DescriptorBase.cs index 0300cd58..194041a8 100644 --- a/csharp/src/Google.Protobuf/Reflection/DescriptorBase.cs +++ b/csharp/src/Google.Protobuf/Reflection/DescriptorBase.cs @@ -61,6 +61,9 @@ namespace Google.Protobuf.Reflection get { return index; } } + /// + /// Returns the name of the entity (field, message etc) being described. + /// public abstract string Name { get; } /// diff --git a/csharp/src/Google.Protobuf/Reflection/EnumValueDescriptor.cs b/csharp/src/Google.Protobuf/Reflection/EnumValueDescriptor.cs index 29833c4a..b212ce96 100644 --- a/csharp/src/Google.Protobuf/Reflection/EnumValueDescriptor.cs +++ b/csharp/src/Google.Protobuf/Reflection/EnumValueDescriptor.cs @@ -51,11 +51,20 @@ namespace Google.Protobuf.Reflection } internal EnumValueDescriptorProto Proto { get { return proto; } } - + + /// + /// Returns the name of the enum value described by this object. + /// public override string Name { get { return proto.Name; } } + /// + /// Returns the number associated with this enum value. + /// public int Number { get { return Proto.Number; } } + /// + /// Returns the enum descriptor that this value is part of. + /// public EnumDescriptor EnumDescriptor { get { return enumDescriptor; } } } } \ No newline at end of file diff --git a/csharp/src/Google.Protobuf/Reflection/FieldDescriptor.cs b/csharp/src/Google.Protobuf/Reflection/FieldDescriptor.cs index 60f2bb8b..bb8e9bbb 100644 --- a/csharp/src/Google.Protobuf/Reflection/FieldDescriptor.cs +++ b/csharp/src/Google.Protobuf/Reflection/FieldDescriptor.cs @@ -93,6 +93,15 @@ namespace Google.Protobuf.Reflection internal FieldDescriptorProto Proto { get { return proto; } } + /// + /// Returns the accessor for this field, or null if this descriptor does + /// not support reflective access. + /// + /// + /// While a describes the field, it does not provide + /// any way of obtaining or changing the value of the field within a specific message; + /// that is the responsibility of the accessor. + /// public IFieldAccessor Accessor { get { return accessor; } } /// @@ -141,43 +150,61 @@ namespace Google.Protobuf.Reflection default: throw new ArgumentException("Invalid type specified"); } - } + } + /// + /// Returns true if this field is a repeated field; false otherwise. + /// public bool IsRepeated { get { return Proto.Label == FieldDescriptorProto.Types.Label.LABEL_REPEATED; } } + /// + /// Returns true if this field is a map field; false otherwise. + /// public bool IsMap { get { return fieldType == FieldType.Message && messageType.Proto.Options != null && messageType.Proto.Options.MapEntry; } } + // TODO(jonskeet): Check whether this is correct with proto3, where we default to packed... + + /// + /// Returns true if this field is a packed, repeated field; false otherwise. + /// public bool IsPacked { get { return Proto.Options != null && Proto.Options.Packed; } } /// - /// Get the field's containing type. For extensions, this is the type being - /// extended, not the location where the extension was defined. See - /// . + /// Get the field's containing message type. /// public MessageDescriptor ContainingType { get { return containingType; } } + /// + /// Returns the oneof containing this field, or null if it is not part of a oneof. + /// public OneofDescriptor ContainingOneof { get { return containingOneof; } - } + } + /// + /// Returns the type of the field. + /// public FieldType FieldType { get { return fieldType; } } + /// + /// Returns the field number declared in the proto file. + /// public int FieldNumber { get { return Proto.Number; } diff --git a/csharp/src/Google.Protobuf/Reflection/FieldType.cs b/csharp/src/Google.Protobuf/Reflection/FieldType.cs index 41fa702d..1658e34c 100644 --- a/csharp/src/Google.Protobuf/Reflection/FieldType.cs +++ b/csharp/src/Google.Protobuf/Reflection/FieldType.cs @@ -33,28 +33,81 @@ namespace Google.Protobuf.Reflection { /// - /// Enumeration of all the possible field types. The odd formatting is to make it very clear - /// which attribute applies to which value, while maintaining a compact format. + /// Enumeration of all the possible field types. /// public enum FieldType { + /// + /// The double field type. + /// Double, + /// + /// The float field type. + /// Float, + /// + /// The int64 field type. + /// Int64, + /// + /// The uint64 field type. + /// UInt64, + /// + /// The int32 field type. + /// Int32, + /// + /// The fixed64 field type. + /// Fixed64, + /// + /// The fixed32 field type. + /// Fixed32, + /// + /// The bool field type. + /// Bool, + /// + /// The string field type. + /// String, + /// + /// The field type used for groups (not supported in this implementation). + /// Group, + /// + /// The field type used for message fields. + /// Message, + /// + /// The bytes field type. + /// Bytes, + /// + /// The uint32 field type. + /// UInt32, + /// + /// The sfixed32 field type. + /// SFixed32, + /// + /// The sfixed64 field type. + /// SFixed64, + /// + /// The sint32 field type. + /// SInt32, + /// + /// The sint64 field type. + /// SInt64, + /// + /// The field type used for enum fields. + /// Enum } } \ No newline at end of file diff --git a/csharp/src/Google.Protobuf/Reflection/FileDescriptor.cs b/csharp/src/Google.Protobuf/Reflection/FileDescriptor.cs index c17c4cc4..72927702 100644 --- a/csharp/src/Google.Protobuf/Reflection/FileDescriptor.cs +++ b/csharp/src/Google.Protobuf/Reflection/FileDescriptor.cs @@ -51,18 +51,7 @@ namespace Google.Protobuf.Reflection private readonly IList dependencies; private readonly IList publicDependencies; private readonly DescriptorPool pool; - - public enum ProtoSyntax - { - Proto2, - Proto3 - } - - public ProtoSyntax Syntax - { - get { return proto.Syntax == "proto3" ? ProtoSyntax.Proto3 : ProtoSyntax.Proto2; } - } - + private FileDescriptor(ByteString descriptorData, FileDescriptorProto proto, FileDescriptor[] dependencies, DescriptorPool pool, bool allowUnknownDependencies, GeneratedCodeInfo generatedCodeInfo) { this.descriptorData = descriptorData; @@ -368,7 +357,13 @@ namespace Google.Protobuf.Reflection throw new ArgumentException("Invalid embedded descriptor for \"" + proto.Name + "\".", e); } } - + + /// + /// Returns a that represents this instance. + /// + /// + /// A that represents this instance. + /// public override string ToString() { return "FileDescriptor for " + proto.Name; diff --git a/csharp/src/Google.Protobuf/Reflection/IDescriptor.cs b/csharp/src/Google.Protobuf/Reflection/IDescriptor.cs index 6506db1b..318d58c9 100644 --- a/csharp/src/Google.Protobuf/Reflection/IDescriptor.cs +++ b/csharp/src/Google.Protobuf/Reflection/IDescriptor.cs @@ -37,8 +37,19 @@ namespace Google.Protobuf.Reflection /// public interface IDescriptor { + /// + /// Returns the name of the entity (message, field etc) being described. + /// string Name { get; } + + /// + /// Returns the fully-qualified name of the entity being described. + /// string FullName { get; } + + /// + /// Returns the descriptor for the .proto file that this entity is part of. + /// FileDescriptor File { get; } } } \ No newline at end of file diff --git a/csharp/src/Google.Protobuf/Reflection/IFieldAccessor.cs b/csharp/src/Google.Protobuf/Reflection/IFieldAccessor.cs index 3f4f05f4..f97d73ee 100644 --- a/csharp/src/Google.Protobuf/Reflection/IFieldAccessor.cs +++ b/csharp/src/Google.Protobuf/Reflection/IFieldAccessor.cs @@ -30,6 +30,9 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #endregion +using System; +using System.Collections; + namespace Google.Protobuf.Reflection { /// @@ -64,7 +67,7 @@ namespace Google.Protobuf.Reflection /// Repeated fields are mutated by fetching the value and manipulating it as a list. /// Map fields are mutated by fetching the value and manipulating it as a dictionary. /// - /// The field is not a "simple" field, or the message is frozen. + /// The field is not a "simple" field. void SetValue(object message, object value); } } \ No newline at end of file diff --git a/csharp/src/Google.Protobuf/Reflection/MessageDescriptor.cs b/csharp/src/Google.Protobuf/Reflection/MessageDescriptor.cs index 0b562de1..82901f1b 100644 --- a/csharp/src/Google.Protobuf/Reflection/MessageDescriptor.cs +++ b/csharp/src/Google.Protobuf/Reflection/MessageDescriptor.cs @@ -30,7 +30,6 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #endregion -using Google.Protobuf.Collections; using System; using System.Collections.Generic; using System.Collections.ObjectModel; @@ -163,6 +162,9 @@ namespace Google.Protobuf.Reflection get { return enumTypes; } } + /// + /// An unmodifiable list of the "oneof" field collections in this message type. + /// public IList Oneofs { get { return oneofs; } @@ -276,7 +278,7 @@ namespace Google.Protobuf.Reflection /// /// Retrieves the descriptor for the field with the given name. /// - /// Number of the field to retrieve the descriptor for + /// Name of the field to retrieve the descriptor for /// The descriptor for the given field /// The message descriptor does not contain a field /// with the given name diff --git a/csharp/src/Google.Protobuf/Reflection/OneofAccessor.cs b/csharp/src/Google.Protobuf/Reflection/OneofAccessor.cs index 8631a1c5..ff51291b 100644 --- a/csharp/src/Google.Protobuf/Reflection/OneofAccessor.cs +++ b/csharp/src/Google.Protobuf/Reflection/OneofAccessor.cs @@ -58,6 +58,12 @@ namespace Google.Protobuf.Reflection clearDelegate = ReflectionUtil.CreateActionObject(clearMethod); } + /// + /// Gets the descriptor for this oneof. + /// + /// + /// The descriptor of the oneof. + /// public OneofDescriptor Descriptor { get { return descriptor; } } /// diff --git a/csharp/src/Google.Protobuf/Reflection/OneofDescriptor.cs b/csharp/src/Google.Protobuf/Reflection/OneofDescriptor.cs index 8571a5eb..d51ee526 100644 --- a/csharp/src/Google.Protobuf/Reflection/OneofDescriptor.cs +++ b/csharp/src/Google.Protobuf/Reflection/OneofDescriptor.cs @@ -32,11 +32,14 @@ using System.Collections.Generic; using System.Collections.ObjectModel; -using System.Linq; using Google.Protobuf.Compatibility; namespace Google.Protobuf.Reflection { + /// + /// Describes a "oneof" field collection in a message type: a set of + /// fields of which at most one can be set in any particular message. + /// public sealed class OneofDescriptor : DescriptorBase { private readonly OneofDescriptorProto proto; @@ -59,13 +62,33 @@ namespace Google.Protobuf.Reflection /// public override string Name { get { return proto.Name; } } + /// + /// Gets the message type containing this oneof. + /// + /// + /// The message type containing this oneof. + /// public MessageDescriptor ContainingType { get { return containingType; } } + /// + /// Gets the fields within this oneof, in declaration order. + /// + /// + /// The fields within this oneof, in declaration order. + /// public IList Fields { get { return fields; } } + /// + /// Gets an accessor for reflective access to the values associated with the oneof + /// in a particular message. + /// + /// + /// The accessor used for reflective access, or null if reflection is not + /// supported by this descriptor. + /// public OneofAccessor Accessor { get { return accessor; } } internal void CrossLink() diff --git a/csharp/src/Google.Protobuf/WellKnownTypes/DurationPartial.cs b/csharp/src/Google.Protobuf/WellKnownTypes/DurationPartial.cs index c69d1b23..18ebefd2 100644 --- a/csharp/src/Google.Protobuf/WellKnownTypes/DurationPartial.cs +++ b/csharp/src/Google.Protobuf/WellKnownTypes/DurationPartial.cs @@ -38,7 +38,13 @@ namespace Google.Protobuf.WellKnownTypes // providing a conversion to TimeSpan and convenience operators. public partial class Duration { + /// + /// The number of nanoseconds in a second. + /// public const int NanosecondsPerSecond = 1000000000; + /// + /// The number of nanoseconds in a BCL tick (as used by and ). + /// public const int NanosecondsPerTick = 100; /// diff --git a/csharp/src/Google.Protobuf/WellKnownTypes/TimeExtensions.cs b/csharp/src/Google.Protobuf/WellKnownTypes/TimeExtensions.cs index a3806b5c..dd485d32 100644 --- a/csharp/src/Google.Protobuf/WellKnownTypes/TimeExtensions.cs +++ b/csharp/src/Google.Protobuf/WellKnownTypes/TimeExtensions.cs @@ -43,16 +43,35 @@ namespace Google.Protobuf.WellKnownTypes /// public static class TimeExtensions { + /// + /// Converts the given to a . + /// + /// The date and time to convert to a timestamp. + /// The value has a other than Utc. + /// The converted timestamp. public static Timestamp ToTimestamp(this DateTime dateTime) { return Timestamp.FromDateTime(dateTime); } + /// + /// Converts the given to a + /// + /// The offset is taken into consideration when converting the value (so the same instant in time + /// is represented) but is not a separate part of the resulting value. In other words, there is no + /// roundtrip operation to retrieve the original DateTimeOffset. + /// The date and time (with UTC offset) to convert to a timestamp. + /// The converted timestamp. public static Timestamp ToTimestamp(this DateTimeOffset dateTimeOffset) { return Timestamp.FromDateTimeOffset(dateTimeOffset); } + /// + /// Converts the given to a . + /// + /// The time span to convert. + /// The converted duration. public static Duration ToDuration(this TimeSpan timeSpan) { return Duration.FromTimeSpan(timeSpan); diff --git a/csharp/src/Google.Protobuf/WellKnownTypes/TimestampPartial.cs b/csharp/src/Google.Protobuf/WellKnownTypes/TimestampPartial.cs index 1aa392c7..68584358 100644 --- a/csharp/src/Google.Protobuf/WellKnownTypes/TimestampPartial.cs +++ b/csharp/src/Google.Protobuf/WellKnownTypes/TimestampPartial.cs @@ -136,9 +136,12 @@ namespace Google.Protobuf.WellKnownTypes } /// - /// Converts the specified to a . + /// Converts the given to a /// - /// + /// The offset is taken into consideration when converting the value (so the same instant in time + /// is represented) but is not a separate part of the resulting value. In other words, there is no + /// roundtrip operation to retrieve the original DateTimeOffset. + /// The date and time (with UTC offset) to convert to a timestamp. /// The converted timestamp. public static Timestamp FromDateTimeOffset(DateTimeOffset dateTimeOffset) { diff --git a/csharp/src/Google.Protobuf/WireFormat.cs b/csharp/src/Google.Protobuf/WireFormat.cs index c1712daa..bbd7e4f9 100644 --- a/csharp/src/Google.Protobuf/WireFormat.cs +++ b/csharp/src/Google.Protobuf/WireFormat.cs @@ -30,9 +30,6 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #endregion -using System; -using Google.Protobuf.Reflection; - namespace Google.Protobuf { /// @@ -59,13 +56,34 @@ namespace Google.Protobuf #endregion + /// + /// Wire types within protobuf encoding. + /// public enum WireType : uint { + /// + /// Variable-length integer. + /// Varint = 0, + /// + /// A fixed-length 64-bit value. + /// Fixed64 = 1, + /// + /// A length-delimited value, i.e. a length followed by that many bytes of data. + /// LengthDelimited = 2, + /// + /// A "start group" value - not supported by this implementation. + /// StartGroup = 3, + /// + /// An "end group" value - not supported by this implementation. + /// EndGroup = 4, + /// + /// A fixed-length 32-bit value. + /// Fixed32 = 5 } @@ -80,6 +98,11 @@ namespace Google.Protobuf return (WireType) (tag & TagTypeMask); } + /// + /// Determines whether the given tag is an end group tag. + /// + /// The tag to check. + /// true if the given tag is an end group tag; false otherwise. public static bool IsEndGroupTag(uint tag) { return (WireType) (tag & TagTypeMask) == WireType.EndGroup; @@ -99,64 +122,6 @@ namespace Google.Protobuf public static uint MakeTag(int fieldNumber, WireType wireType) { return (uint) (fieldNumber << TagTypeBits) | (uint) wireType; - } - - public static uint MakeTag(FieldDescriptor field) - { - return MakeTag(field.FieldNumber, GetWireType(field)); - } - - /// - /// Returns the wire type for the given field descriptor. This differs - /// from GetWireType(FieldType) for packed repeated fields. - /// - internal static WireType GetWireType(FieldDescriptor descriptor) - { - return descriptor.IsPacked ? WireType.LengthDelimited : GetWireType(descriptor.FieldType); - } - - /// - /// Converts a field type to its wire type. Done with a switch for the sake - /// of speed - this is significantly faster than a dictionary lookup. - /// - public static WireType GetWireType(FieldType fieldType) - { - switch (fieldType) - { - case FieldType.Double: - return WireType.Fixed64; - case FieldType.Float: - return WireType.Fixed32; - case FieldType.Int64: - case FieldType.UInt64: - case FieldType.Int32: - return WireType.Varint; - case FieldType.Fixed64: - return WireType.Fixed64; - case FieldType.Fixed32: - return WireType.Fixed32; - case FieldType.Bool: - return WireType.Varint; - case FieldType.String: - return WireType.LengthDelimited; - case FieldType.Group: - return WireType.StartGroup; - case FieldType.Message: - case FieldType.Bytes: - return WireType.LengthDelimited; - case FieldType.UInt32: - return WireType.Varint; - case FieldType.SFixed32: - return WireType.Fixed32; - case FieldType.SFixed64: - return WireType.Fixed64; - case FieldType.SInt32: - case FieldType.SInt64: - case FieldType.Enum: - return WireType.Varint; - default: - throw new ArgumentOutOfRangeException("fieldType", "No such field type"); - } - } + } } } \ No newline at end of file -- cgit v1.2.3 From 6f300442bc0e5eced5f48820bcd5f24fce9e3867 Mon Sep 17 00:00:00 2001 From: Jon Skeet Date: Thu, 6 Aug 2015 14:29:34 +0100 Subject: Tidying up - fix a bunch of TODOs and remove outdated ones. --- .../Google.Protobuf.Test/CodedInputStreamTest.cs | 1 - csharp/src/Google.Protobuf/ByteString.cs | 3 -- csharp/src/Google.Protobuf/CodedInputStream.cs | 25 ++++++++-------- csharp/src/Google.Protobuf/CodedOutputStream.cs | 22 +++++++++----- .../Google.Protobuf/Collections/RepeatedField.cs | 6 ++-- csharp/src/Google.Protobuf/FrameworkPortability.cs | 3 +- csharp/src/Google.Protobuf/IMessage.cs | 2 -- csharp/src/Google.Protobuf/JsonFormatter.cs | 3 +- .../Reflection/FieldAccessorBase.cs | 10 +++---- .../Google.Protobuf/Reflection/FileDescriptor.cs | 34 +++++++++++----------- .../Google.Protobuf/Reflection/IFieldAccessor.cs | 8 ++--- .../Google.Protobuf/Reflection/MapFieldAccessor.cs | 4 +-- .../Google.Protobuf/Reflection/OneofAccessor.cs | 12 ++++---- .../Google.Protobuf/Reflection/ReflectionUtil.cs | 24 +++++++-------- .../Reflection/RepeatedFieldAccessor.cs | 4 +-- .../Reflection/SingleFieldAccessor.cs | 12 ++++---- csharp/src/Google.Protobuf/WireFormat.cs | 13 --------- 17 files changed, 84 insertions(+), 102 deletions(-) (limited to 'csharp/src/Google.Protobuf/IMessage.cs') diff --git a/csharp/src/Google.Protobuf.Test/CodedInputStreamTest.cs b/csharp/src/Google.Protobuf.Test/CodedInputStreamTest.cs index 42c740ac..c8326d80 100644 --- a/csharp/src/Google.Protobuf.Test/CodedInputStreamTest.cs +++ b/csharp/src/Google.Protobuf.Test/CodedInputStreamTest.cs @@ -281,7 +281,6 @@ namespace Google.Protobuf CodedInputStream input = new CodedInputStream(ms); Assert.AreEqual(tag, input.ReadTag()); - // TODO(jonskeet): Should this be ArgumentNullException instead? Assert.Throws(() => input.ReadBytes()); } diff --git a/csharp/src/Google.Protobuf/ByteString.cs b/csharp/src/Google.Protobuf/ByteString.cs index 2aa62483..3d55f02d 100644 --- a/csharp/src/Google.Protobuf/ByteString.cs +++ b/csharp/src/Google.Protobuf/ByteString.cs @@ -40,7 +40,6 @@ namespace Google.Protobuf { /// /// Immutable array of bytes. - /// TODO(jonskeet): Implement the common collection interfaces? /// public sealed class ByteString : IEnumerable, IEquatable { @@ -284,8 +283,6 @@ namespace Google.Protobuf return !(lhs == rhs); } - // TODO(jonskeet): CopyTo if it turns out to be required - /// /// Compares this byte string with another object. /// diff --git a/csharp/src/Google.Protobuf/CodedInputStream.cs b/csharp/src/Google.Protobuf/CodedInputStream.cs index dcd19e48..c8b33b33 100644 --- a/csharp/src/Google.Protobuf/CodedInputStream.cs +++ b/csharp/src/Google.Protobuf/CodedInputStream.cs @@ -30,6 +30,7 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #endregion +using Google.Protobuf.Collections; using System; using System.Collections.Generic; using System.IO; @@ -40,20 +41,21 @@ namespace Google.Protobuf /// Readings and decodes protocol message fields. /// /// - /// This class contains two kinds of methods: methods that read specific - /// protocol message constructs and field types (e.g. ReadTag and - /// ReadInt32) and methods that read low-level values (e.g. - /// ReadRawVarint32 and ReadRawBytes). If you are reading encoded protocol - /// messages, you should use the former methods, but if you are reading some - /// other format of your own design, use the latter. The names of the former - /// methods are taken from the protocol buffer type names, not .NET types. - /// (Hence ReadFloat instead of ReadSingle, and ReadBool instead of ReadBoolean.) - /// - /// TODO(jonskeet): Consider whether recursion and size limits shouldn't be readonly, - /// set at construction time. + /// + /// This class is generally used by generated code to read appropriate + /// primitives from the stream. It effectively encapsulates the lowest + /// levels of protocol buffer format. + /// + /// + /// Repeated fields and map fields are not handled by this class; use + /// and to serialize such fields. + /// /// public sealed class CodedInputStream { + // TODO(jonskeet): Consider whether recursion and size limits shouldn't be readonly, + // set at construction time. + /// /// Buffer of data read from the stream or provided at construction time. /// @@ -1022,7 +1024,6 @@ namespace Google.Protobuf if (totalBytesRetired + bufferPos + size > currentLimit) { // Read to the end of the stream (up to the current limit) anyway. - // TODO(jonskeet): This is the only usage of SkipRawBytes. Do we really need to do it? SkipRawBytes(currentLimit - totalBytesRetired - bufferPos); // Then fail. throw InvalidProtocolBufferException.TruncatedMessage(); diff --git a/csharp/src/Google.Protobuf/CodedOutputStream.cs b/csharp/src/Google.Protobuf/CodedOutputStream.cs index 08ea7005..7f41cb7a 100644 --- a/csharp/src/Google.Protobuf/CodedOutputStream.cs +++ b/csharp/src/Google.Protobuf/CodedOutputStream.cs @@ -30,6 +30,7 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #endregion +using Google.Protobuf.Collections; using System; using System.IO; using System.Text; @@ -40,14 +41,19 @@ namespace Google.Protobuf /// Encodes and writes protocol message fields. /// /// - /// This class contains two kinds of methods: methods that write specific - /// protocol message constructs and field types (e.g. WriteTag and - /// WriteInt32) and methods that write low-level values (e.g. - /// WriteRawVarint32 and WriteRawBytes). If you are writing encoded protocol - /// messages, you should use the former methods, but if you are writing some - /// other format of your own design, use the latter. The names of the former - /// methods are taken from the protocol buffer type names, not .NET types. - /// (Hence WriteFloat instead of WriteSingle, and WriteBool instead of WriteBoolean.) + /// + /// This class is generally used by generated code to write appropriate + /// primitives to the stream. It effectively encapsulates the lowest + /// levels of protocol buffer format. Unlike some other implementations, + /// this does not include combined "write tag and value" methods. Generated + /// code knows the exact byte representations of the tags they're going to write, + /// so there's no need to re-encode them each time. Manually-written code calling + /// this class should just call one of the WriteTag overloads before each value. + /// + /// + /// Repeated fields and map fields are not handled by this class; use + /// and to serialize such fields. + /// /// public sealed partial class CodedOutputStream { diff --git a/csharp/src/Google.Protobuf/Collections/RepeatedField.cs b/csharp/src/Google.Protobuf/Collections/RepeatedField.cs index c901864a..d9ced6ec 100644 --- a/csharp/src/Google.Protobuf/Collections/RepeatedField.cs +++ b/csharp/src/Google.Protobuf/Collections/RepeatedField.cs @@ -88,7 +88,7 @@ namespace Google.Protobuf.Collections public void AddEntriesFrom(CodedInputStream input, FieldCodec codec) { // TODO: Inline some of the Add code, so we can avoid checking the size on every - // iteration and the mutability. + // iteration. uint tag = input.LastTag; var reader = codec.ValueReader; // Value types can be packed or not. @@ -315,7 +315,7 @@ namespace Google.Protobuf.Collections { throw new ArgumentNullException("values"); } - // TODO: Check for ICollection and get the Count? + // TODO: Check for ICollection and get the Count, to optimize? foreach (T item in values) { Add(item); @@ -394,7 +394,6 @@ namespace Google.Protobuf.Collections { return false; } - // TODO(jonskeet): Does this box for enums? EqualityComparer comparer = EqualityComparer.Default; for (int i = 0; i < count; i++) { @@ -418,7 +417,6 @@ namespace Google.Protobuf.Collections { throw new ArgumentNullException("item"); } - // TODO(jonskeet): Does this box for enums? EqualityComparer comparer = EqualityComparer.Default; for (int i = 0; i < count; i++) { diff --git a/csharp/src/Google.Protobuf/FrameworkPortability.cs b/csharp/src/Google.Protobuf/FrameworkPortability.cs index 082eb2e1..9498dbe4 100644 --- a/csharp/src/Google.Protobuf/FrameworkPortability.cs +++ b/csharp/src/Google.Protobuf/FrameworkPortability.cs @@ -40,10 +40,9 @@ namespace Google.Protobuf /// internal static class FrameworkPortability { - // TODO(jtattermusch): is this still a thing? // The value of RegexOptions.Compiled is 8. We can test for the presence at // execution time using Enum.IsDefined, so a single build will do the right thing - // on each platform. + // on each platform. (RegexOptions.Compiled isn't supported by PCLs.) internal static readonly RegexOptions CompiledRegexWhereAvailable = Enum.IsDefined(typeof(RegexOptions), 8) ? (RegexOptions)8 : RegexOptions.None; } diff --git a/csharp/src/Google.Protobuf/IMessage.cs b/csharp/src/Google.Protobuf/IMessage.cs index 147c83ca..773845ff 100644 --- a/csharp/src/Google.Protobuf/IMessage.cs +++ b/csharp/src/Google.Protobuf/IMessage.cs @@ -35,8 +35,6 @@ using Google.Protobuf.Reflection; namespace Google.Protobuf { - - // TODO(jonskeet): Do we want a "weak" (non-generic) version of IReflectedMessage? // TODO(jonskeet): Split these interfaces into separate files when we're happy with them. /// diff --git a/csharp/src/Google.Protobuf/JsonFormatter.cs b/csharp/src/Google.Protobuf/JsonFormatter.cs index 7c4894be..12bbdfdd 100644 --- a/csharp/src/Google.Protobuf/JsonFormatter.cs +++ b/csharp/src/Google.Protobuf/JsonFormatter.cs @@ -158,7 +158,6 @@ namespace Google.Protobuf { var accessor = field.Accessor; // Oneofs are written later - // TODO: Change to write out fields in order, interleaving oneofs appropriately (as per binary format) if (field.ContainingOneof != null && field.ContainingOneof.Accessor.GetCaseFieldDescriptor(message) != field) { continue; @@ -425,7 +424,7 @@ namespace Google.Protobuf if (descriptor.FullName == ListValue.Descriptor.FullName) { var fieldAccessor = descriptor.Fields[ListValue.ValuesFieldNumber].Accessor; - WriteList(builder, fieldAccessor, (IList) fieldAccessor.GetValue(value)); + WriteList(builder, fieldAccessor, (IList) fieldAccessor.GetValue((IMessage) value)); return; } if (descriptor.FullName == Value.Descriptor.FullName) diff --git a/csharp/src/Google.Protobuf/Reflection/FieldAccessorBase.cs b/csharp/src/Google.Protobuf/Reflection/FieldAccessorBase.cs index 3fccf884..82ce5051 100644 --- a/csharp/src/Google.Protobuf/Reflection/FieldAccessorBase.cs +++ b/csharp/src/Google.Protobuf/Reflection/FieldAccessorBase.cs @@ -41,23 +41,23 @@ namespace Google.Protobuf.Reflection /// internal abstract class FieldAccessorBase : IFieldAccessor { - private readonly Func getValueDelegate; + private readonly Func getValueDelegate; private readonly FieldDescriptor descriptor; internal FieldAccessorBase(PropertyInfo property, FieldDescriptor descriptor) { this.descriptor = descriptor; - getValueDelegate = ReflectionUtil.CreateFuncObjectObject(property.GetGetMethod()); + getValueDelegate = ReflectionUtil.CreateFuncIMessageObject(property.GetGetMethod()); } public FieldDescriptor Descriptor { get { return descriptor; } } - public object GetValue(object message) + public object GetValue(IMessage message) { return getValueDelegate(message); } - public abstract void Clear(object message); - public abstract void SetValue(object message, object value); + public abstract void Clear(IMessage message); + public abstract void SetValue(IMessage message, object value); } } diff --git a/csharp/src/Google.Protobuf/Reflection/FileDescriptor.cs b/csharp/src/Google.Protobuf/Reflection/FileDescriptor.cs index 72927702..500e467c 100644 --- a/csharp/src/Google.Protobuf/Reflection/FileDescriptor.cs +++ b/csharp/src/Google.Protobuf/Reflection/FileDescriptor.cs @@ -287,23 +287,23 @@ namespace Google.Protobuf.Reflection DescriptorPool pool = new DescriptorPool(dependencies); FileDescriptor result = new FileDescriptor(descriptorData, proto, dependencies, pool, allowUnknownDependencies, generatedCodeInfo); - // TODO(jonskeet): Reinstate these checks, or get rid of them entirely. They aren't in the Java code, - // and fail for the CustomOptions test right now. (We get "descriptor.proto" vs "google/protobuf/descriptor.proto".) - //if (dependencies.Length != proto.DependencyCount) - //{ - // throw new DescriptorValidationException(result, - // "Dependencies passed to FileDescriptor.BuildFrom() don't match " + - // "those listed in the FileDescriptorProto."); - //} - //for (int i = 0; i < proto.DependencyCount; i++) - //{ - // if (dependencies[i].Name != proto.DependencyList[i]) - // { - // throw new DescriptorValidationException(result, - // "Dependencies passed to FileDescriptor.BuildFrom() don't match " + - // "those listed in the FileDescriptorProto."); - // } - //} + // Validate that the dependencies we've been passed (as FileDescriptors) are actually the ones we + // need. + if (dependencies.Length != proto.Dependency.Count) + { + throw new DescriptorValidationException(result, + "Dependencies passed to FileDescriptor.BuildFrom() don't match " + + "those listed in the FileDescriptorProto."); + } + for (int i = 0; i < proto.Dependency.Count; i++) + { + if (dependencies[i].Name != proto.Dependency[i]) + { + throw new DescriptorValidationException(result, + "Dependencies passed to FileDescriptor.BuildFrom() don't match " + + "those listed in the FileDescriptorProto."); + } + } result.CrossLink(); return result; diff --git a/csharp/src/Google.Protobuf/Reflection/IFieldAccessor.cs b/csharp/src/Google.Protobuf/Reflection/IFieldAccessor.cs index f97d73ee..c9e28f50 100644 --- a/csharp/src/Google.Protobuf/Reflection/IFieldAccessor.cs +++ b/csharp/src/Google.Protobuf/Reflection/IFieldAccessor.cs @@ -45,20 +45,20 @@ namespace Google.Protobuf.Reflection /// FieldDescriptor Descriptor { get; } - // TODO: Should the argument type for these messages be IReflectedMessage? + // TODO: Should the argument type for these messages be IMessage? /// /// Clears the field in the specified message. (For repeated fields, /// this clears the list.) /// - void Clear(object message); + void Clear(IMessage message); /// /// Fetches the field value. For repeated values, this will be an /// implementation. For map values, this will be an /// implementation. /// - object GetValue(object message); + object GetValue(IMessage message); /// /// Mutator for single "simple" fields only. @@ -68,6 +68,6 @@ namespace Google.Protobuf.Reflection /// Map fields are mutated by fetching the value and manipulating it as a dictionary. /// /// The field is not a "simple" field. - void SetValue(object message, object value); + void SetValue(IMessage message, object value); } } \ No newline at end of file diff --git a/csharp/src/Google.Protobuf/Reflection/MapFieldAccessor.cs b/csharp/src/Google.Protobuf/Reflection/MapFieldAccessor.cs index 6df4c5f0..9ed7f8c4 100644 --- a/csharp/src/Google.Protobuf/Reflection/MapFieldAccessor.cs +++ b/csharp/src/Google.Protobuf/Reflection/MapFieldAccessor.cs @@ -45,13 +45,13 @@ namespace Google.Protobuf.Reflection { } - public override void Clear(object message) + public override void Clear(IMessage message) { IDictionary list = (IDictionary) GetValue(message); list.Clear(); } - public override void SetValue(object message, object value) + public override void SetValue(IMessage message, object value) { throw new InvalidOperationException("SetValue is not implemented for map fields"); } diff --git a/csharp/src/Google.Protobuf/Reflection/OneofAccessor.cs b/csharp/src/Google.Protobuf/Reflection/OneofAccessor.cs index ff51291b..8714ab18 100644 --- a/csharp/src/Google.Protobuf/Reflection/OneofAccessor.cs +++ b/csharp/src/Google.Protobuf/Reflection/OneofAccessor.cs @@ -41,8 +41,8 @@ namespace Google.Protobuf.Reflection /// public sealed class OneofAccessor { - private readonly Func caseDelegate; - private readonly Action clearDelegate; + private readonly Func caseDelegate; + private readonly Action clearDelegate; private OneofDescriptor descriptor; internal OneofAccessor(PropertyInfo caseProperty, MethodInfo clearMethod, OneofDescriptor descriptor) @@ -52,10 +52,10 @@ namespace Google.Protobuf.Reflection throw new ArgumentException("Cannot read from property"); } this.descriptor = descriptor; - caseDelegate = ReflectionUtil.CreateFuncObjectT(caseProperty.GetGetMethod()); + caseDelegate = ReflectionUtil.CreateFuncIMessageT(caseProperty.GetGetMethod()); this.descriptor = descriptor; - clearDelegate = ReflectionUtil.CreateActionObject(clearMethod); + clearDelegate = ReflectionUtil.CreateActionIMessage(clearMethod); } /// @@ -69,7 +69,7 @@ namespace Google.Protobuf.Reflection /// /// Clears the oneof in the specified message. /// - public void Clear(object message) + public void Clear(IMessage message) { clearDelegate(message); } @@ -77,7 +77,7 @@ namespace Google.Protobuf.Reflection /// /// Indicates which field in the oneof is set for specified message /// - public FieldDescriptor GetCaseFieldDescriptor(object message) + public FieldDescriptor GetCaseFieldDescriptor(IMessage message) { int fieldNumber = caseDelegate(message); if (fieldNumber > 0) diff --git a/csharp/src/Google.Protobuf/Reflection/ReflectionUtil.cs b/csharp/src/Google.Protobuf/Reflection/ReflectionUtil.cs index 5b3cbb36..df820ca3 100644 --- a/csharp/src/Google.Protobuf/Reflection/ReflectionUtil.cs +++ b/csharp/src/Google.Protobuf/Reflection/ReflectionUtil.cs @@ -56,52 +56,52 @@ namespace Google.Protobuf.Reflection /// Creates a delegate which will cast the argument to the appropriate method target type, /// call the method on it, then convert the result to object. /// - internal static Func CreateFuncObjectObject(MethodInfo method) + internal static Func CreateFuncIMessageObject(MethodInfo method) { - ParameterExpression parameter = Expression.Parameter(typeof(object), "p"); + ParameterExpression parameter = Expression.Parameter(typeof(IMessage), "p"); Expression downcast = Expression.Convert(parameter, method.DeclaringType); Expression call = Expression.Call(downcast, method); Expression upcast = Expression.Convert(call, typeof(object)); - return Expression.Lambda>(upcast, parameter).Compile(); + return Expression.Lambda>(upcast, parameter).Compile(); } /// /// Creates a delegate which will cast the argument to the appropriate method target type, /// call the method on it, then convert the result to the specified type. /// - internal static Func CreateFuncObjectT(MethodInfo method) + internal static Func CreateFuncIMessageT(MethodInfo method) { - ParameterExpression parameter = Expression.Parameter(typeof(object), "p"); + ParameterExpression parameter = Expression.Parameter(typeof(IMessage), "p"); Expression downcast = Expression.Convert(parameter, method.DeclaringType); Expression call = Expression.Call(downcast, method); Expression upcast = Expression.Convert(call, typeof(T)); - return Expression.Lambda>(upcast, parameter).Compile(); + return Expression.Lambda>(upcast, parameter).Compile(); } /// /// Creates a delegate which will execute the given method after casting the first argument to /// the target type of the method, and the second argument to the first parameter type of the method. /// - internal static Action CreateActionObjectObject(MethodInfo method) + internal static Action CreateActionIMessageObject(MethodInfo method) { - ParameterExpression targetParameter = Expression.Parameter(typeof(object), "target"); + ParameterExpression targetParameter = Expression.Parameter(typeof(IMessage), "target"); ParameterExpression argParameter = Expression.Parameter(typeof(object), "arg"); Expression castTarget = Expression.Convert(targetParameter, method.DeclaringType); Expression castArgument = Expression.Convert(argParameter, method.GetParameters()[0].ParameterType); Expression call = Expression.Call(castTarget, method, castArgument); - return Expression.Lambda>(call, targetParameter, argParameter).Compile(); + return Expression.Lambda>(call, targetParameter, argParameter).Compile(); } /// /// Creates a delegate which will execute the given method after casting the first argument to /// the target type of the method. /// - internal static Action CreateActionObject(MethodInfo method) + internal static Action CreateActionIMessage(MethodInfo method) { - ParameterExpression targetParameter = Expression.Parameter(typeof(object), "target"); + ParameterExpression targetParameter = Expression.Parameter(typeof(IMessage), "target"); Expression castTarget = Expression.Convert(targetParameter, method.DeclaringType); Expression call = Expression.Call(castTarget, method); - return Expression.Lambda>(call, targetParameter).Compile(); + return Expression.Lambda>(call, targetParameter).Compile(); } } } \ No newline at end of file diff --git a/csharp/src/Google.Protobuf/Reflection/RepeatedFieldAccessor.cs b/csharp/src/Google.Protobuf/Reflection/RepeatedFieldAccessor.cs index acb3c8d5..bd408470 100644 --- a/csharp/src/Google.Protobuf/Reflection/RepeatedFieldAccessor.cs +++ b/csharp/src/Google.Protobuf/Reflection/RepeatedFieldAccessor.cs @@ -45,13 +45,13 @@ namespace Google.Protobuf.Reflection { } - public override void Clear(object message) + public override void Clear(IMessage message) { IList list = (IList) GetValue(message); list.Clear(); } - public override void SetValue(object message, object value) + public override void SetValue(IMessage message, object value) { throw new InvalidOperationException("SetValue is not implemented for repeated fields"); } diff --git a/csharp/src/Google.Protobuf/Reflection/SingleFieldAccessor.cs b/csharp/src/Google.Protobuf/Reflection/SingleFieldAccessor.cs index 851efc26..de92fbc1 100644 --- a/csharp/src/Google.Protobuf/Reflection/SingleFieldAccessor.cs +++ b/csharp/src/Google.Protobuf/Reflection/SingleFieldAccessor.cs @@ -46,8 +46,8 @@ namespace Google.Protobuf.Reflection // and proto2 vs proto3 for non-message types, as proto3 doesn't support "full" presence detection or default // values. - private readonly Action setValueDelegate; - private readonly Action clearDelegate; + private readonly Action setValueDelegate; + private readonly Action clearDelegate; internal SingleFieldAccessor(PropertyInfo property, FieldDescriptor descriptor) : base(property, descriptor) { @@ -55,12 +55,10 @@ namespace Google.Protobuf.Reflection { throw new ArgumentException("Not all required properties/methods available"); } - setValueDelegate = ReflectionUtil.CreateActionObjectObject(property.GetSetMethod()); + setValueDelegate = ReflectionUtil.CreateActionIMessageObject(property.GetSetMethod()); var clrType = property.PropertyType; - // TODO: What should clear on a oneof member do? Clear the oneof? - // TODO: Validate that this is a reasonable single field? (Should be a value type, a message type, or string/ByteString.) object defaultValue = typeof(IMessage).IsAssignableFrom(clrType) ? null @@ -70,12 +68,12 @@ namespace Google.Protobuf.Reflection clearDelegate = message => SetValue(message, defaultValue); } - public override void Clear(object message) + public override void Clear(IMessage message) { clearDelegate(message); } - public override void SetValue(object message, object value) + public override void SetValue(IMessage message, object value) { setValueDelegate(message, value); } diff --git a/csharp/src/Google.Protobuf/WireFormat.cs b/csharp/src/Google.Protobuf/WireFormat.cs index b0e4a41f..faf1e715 100644 --- a/csharp/src/Google.Protobuf/WireFormat.cs +++ b/csharp/src/Google.Protobuf/WireFormat.cs @@ -43,19 +43,6 @@ namespace Google.Protobuf /// public static class WireFormat { - #region Fixed sizes. - - // TODO(jonskeet): Move these somewhere else. They're messy. Consider making FieldType a smarter kind of enum - internal const int Fixed32Size = 4; - internal const int Fixed64Size = 8; - internal const int SFixed32Size = 4; - internal const int SFixed64Size = 8; - internal const int FloatSize = 4; - internal const int DoubleSize = 8; - internal const int BoolSize = 1; - - #endregion - /// /// Wire types within protobuf encoding. /// -- cgit v1.2.3 From f2732c7af13110a72ded2667684f52a86a23de2f Mon Sep 17 00:00:00 2001 From: Jon Skeet Date: Mon, 10 Aug 2015 18:32:18 +0100 Subject: More TODOs done. - Removed a TODO without change in DescriptorPool.LookupSymbol - the TODOs were around performance, and this is only used during descriptor initialization - Make the CodedInputStream limits read-only, adding a static factory method for the rare cases when this is useful - Extracted IDeepCloneable into its own file. --- .../Google.Protobuf.Test/CodedInputStreamTest.cs | 48 +++---- .../Google.Protobuf.Test/CodedOutputStreamTest.cs | 2 +- csharp/src/Google.Protobuf/CodedInputStream.cs | 160 +++++++++++---------- csharp/src/Google.Protobuf/Google.Protobuf.csproj | 1 + csharp/src/Google.Protobuf/IDeepCloneable.cs | 54 +++++++ csharp/src/Google.Protobuf/IMessage.cs | 22 --- .../Google.Protobuf/Reflection/DescriptorPool.cs | 7 +- 7 files changed, 168 insertions(+), 126 deletions(-) create mode 100644 csharp/src/Google.Protobuf/IDeepCloneable.cs (limited to 'csharp/src/Google.Protobuf/IMessage.cs') diff --git a/csharp/src/Google.Protobuf.Test/CodedInputStreamTest.cs b/csharp/src/Google.Protobuf.Test/CodedInputStreamTest.cs index c8326d80..54c44e47 100644 --- a/csharp/src/Google.Protobuf.Test/CodedInputStreamTest.cs +++ b/csharp/src/Google.Protobuf.Test/CodedInputStreamTest.cs @@ -320,38 +320,18 @@ namespace Google.Protobuf Assert.Throws(() => TestRecursiveMessage.Parser.ParseFrom(data65)); - CodedInputStream input = data64.CreateCodedInput(); - input.SetRecursionLimit(8); + CodedInputStream input = CodedInputStream.CreateWithLimits(new MemoryStream(data64.ToByteArray()), 1000000, 63); Assert.Throws(() => TestRecursiveMessage.Parser.ParseFrom(input)); } - /* [Test] public void SizeLimit() { // Have to use a Stream rather than ByteString.CreateCodedInput as SizeLimit doesn't // apply to the latter case. - MemoryStream ms = new MemoryStream(TestUtil.GetAllSet().ToByteString().ToByteArray()); - CodedInputStream input = new CodedInputStream(ms); - input.SetSizeLimit(16); - - Assert.Throws(() => TestAllTypes.ParseFrom(input)); - }*/ - - [Test] - public void ResetSizeCounter() - { - CodedInputStream input = new CodedInputStream( - new SmallBlockInputStream(new byte[256], 8)); - input.SetSizeLimit(16); - input.ReadRawBytes(16); - - Assert.Throws(() => input.ReadRawByte()); - - input.ResetSizeCounter(); - input.ReadRawByte(); // No exception thrown. - - Assert.Throws(() => input.ReadRawBytes(16)); + MemoryStream ms = new MemoryStream(SampleMessages.CreateFullTestAllTypes().ToByteArray()); + CodedInputStream input = CodedInputStream.CreateWithLimits(ms, 16, 100); + Assert.Throws(() => TestAllTypes.Parser.ParseFrom(input)); } /// @@ -423,7 +403,7 @@ namespace Google.Protobuf output.Flush(); ms.Position = 0; - CodedInputStream input = new CodedInputStream(ms, new byte[ms.Length / 2]); + CodedInputStream input = new CodedInputStream(ms, new byte[ms.Length / 2], 0, 0); uint tag = input.ReadTag(); Assert.AreEqual(1, WireFormat.GetTagFieldNumber(tag)); @@ -528,5 +508,23 @@ namespace Google.Protobuf Assert.AreEqual(WireFormat.MakeTag(1, WireFormat.WireType.StartGroup), input.ReadTag()); Assert.Throws(() => input.SkipLastField()); } + + [Test] + public void Construction_Invalid() + { + Assert.Throws(() => new CodedInputStream((byte[]) null)); + Assert.Throws(() => new CodedInputStream(null, 0, 0)); + Assert.Throws(() => new CodedInputStream((Stream) null)); + Assert.Throws(() => new CodedInputStream(new byte[10], 100, 0)); + Assert.Throws(() => new CodedInputStream(new byte[10], 5, 10)); + } + + [Test] + public void CreateWithLimits_InvalidLimits() + { + var stream = new MemoryStream(); + Assert.Throws(() => CodedInputStream.CreateWithLimits(stream, 0, 1)); + Assert.Throws(() => CodedInputStream.CreateWithLimits(stream, 1, 0)); + } } } \ No newline at end of file diff --git a/csharp/src/Google.Protobuf.Test/CodedOutputStreamTest.cs b/csharp/src/Google.Protobuf.Test/CodedOutputStreamTest.cs index c1bf7bd6..3297fe87 100644 --- a/csharp/src/Google.Protobuf.Test/CodedOutputStreamTest.cs +++ b/csharp/src/Google.Protobuf.Test/CodedOutputStreamTest.cs @@ -334,7 +334,7 @@ namespace Google.Protobuf } // Now test Input stream: { - CodedInputStream cin = new CodedInputStream(new MemoryStream(bytes), new byte[50]); + CodedInputStream cin = new CodedInputStream(new MemoryStream(bytes), new byte[50], 0, 0); Assert.AreEqual(0, cin.Position); // Field 1: uint tag = cin.ReadTag(); diff --git a/csharp/src/Google.Protobuf/CodedInputStream.cs b/csharp/src/Google.Protobuf/CodedInputStream.cs index c8b33b33..82c6ceab 100644 --- a/csharp/src/Google.Protobuf/CodedInputStream.cs +++ b/csharp/src/Google.Protobuf/CodedInputStream.cs @@ -53,16 +53,13 @@ namespace Google.Protobuf /// public sealed class CodedInputStream { - // TODO(jonskeet): Consider whether recursion and size limits shouldn't be readonly, - // set at construction time. - /// /// Buffer of data read from the stream or provided at construction time. /// private readonly byte[] buffer; /// - /// The number of valid bytes in the buffer. + /// The index of the buffer at which we need to refill from the stream (if there is one). /// private int bufferSize; @@ -106,61 +103,103 @@ namespace Google.Protobuf /// private int currentLimit = int.MaxValue; - /// - /// - /// private int recursionDepth = 0; - private int recursionLimit = DefaultRecursionLimit; + private readonly int recursionLimit; + private readonly int sizeLimit; + + #region Construction + // Note that the checks are performed such that we don't end up checking obviously-valid things + // like non-null references for arrays we've just created. /// - /// + /// Creates a new CodedInputStream reading data from the given byte array. /// - private int sizeLimit = DefaultSizeLimit; + public CodedInputStream(byte[] buffer) : this(null, Preconditions.CheckNotNull(buffer, "buffer"), 0, buffer.Length) + { + } - #region Construction /// - /// Creates a new CodedInputStream reading data from the given - /// byte array. + /// Creates a new CodedInputStream that reads from the given byte array slice. /// - public CodedInputStream(byte[] buf) : this(buf, 0, buf.Length) - { + public CodedInputStream(byte[] buffer, int offset, int length) + : this(null, Preconditions.CheckNotNull(buffer, "buffer"), offset, offset + length) + { + if (offset < 0 || offset > buffer.Length) + { + throw new ArgumentOutOfRangeException("offset", "Offset must be within the buffer"); + } + if (length < 0 || offset + length > buffer.Length) + { + throw new ArgumentOutOfRangeException("length", "Length must be non-negative and within the buffer"); + } } /// - /// Creates a new CodedInputStream that reads from the given - /// byte array slice. + /// Creates a new CodedInputStream reading data from the given stream. /// - public CodedInputStream(byte[] buffer, int offset, int length) + public CodedInputStream(Stream input) : this(input, new byte[BufferSize], 0, 0) { - this.buffer = buffer; - this.bufferPos = offset; - this.bufferSize = offset + length; - this.input = null; + Preconditions.CheckNotNull(input, "input"); } /// - /// Creates a new CodedInputStream reading data from the given stream. + /// Creates a new CodedInputStream reading data from the given + /// stream and buffer, using the default limits. /// - public CodedInputStream(Stream input) + internal CodedInputStream(Stream input, byte[] buffer, int bufferPos, int bufferSize) { - this.buffer = new byte[BufferSize]; - this.bufferSize = 0; this.input = input; + this.buffer = buffer; + this.bufferPos = bufferPos; + this.bufferSize = bufferSize; + this.sizeLimit = DefaultSizeLimit; + this.recursionLimit = DefaultRecursionLimit; } /// /// Creates a new CodedInputStream reading data from the given - /// stream, with a pre-allocated buffer. + /// stream and buffer, using the specified limits. /// - internal CodedInputStream(Stream input, byte[] buffer) + /// + /// This chains to the version with the default limits instead of vice versa to avoid + /// having to check that the default values are valid every time. + /// + internal CodedInputStream(Stream input, byte[] buffer, int bufferPos, int bufferSize, int sizeLimit, int recursionLimit) + : this(input, buffer, bufferPos, bufferSize) { - this.buffer = buffer; - this.bufferSize = 0; - this.input = input; + if (sizeLimit <= 0) + { + throw new ArgumentOutOfRangeException("sizeLimit", "Size limit must be positive"); + } + if (recursionLimit <= 0) + { + throw new ArgumentOutOfRangeException("recursionLimit!", "Recursion limit must be positive"); + } + this.sizeLimit = sizeLimit; + this.recursionLimit = recursionLimit; } #endregion + /// + /// Creates a with the specified size and recursion limits, reading + /// from an input stream. + /// + /// + /// This method exists separately from the constructor to reduce the number of constructor overloads. + /// It is likely to be used considerably less frequently than the constructors, as the default limits + /// are suitable for most use cases. + /// + /// The input stream to read from + /// The total limit of data to read from the stream. + /// The maximum recursion depth to allow while reading. + /// A CodedInputStream reading from with the specified size + /// and recursion limits. + public static CodedInputStream CreateWithLimits(Stream input, int sizeLimit, int recursionLimit) + { + return new CodedInputStream(input, new byte[BufferSize], 0, 0, sizeLimit, recursionLimit); + } + /// /// Returns the current position in the input stream, or the position in the input buffer /// @@ -182,59 +221,30 @@ namespace Google.Protobuf /// internal uint LastTag { get { return lastTag; } } - #region Limits for recursion and length /// - /// Set the maximum message recursion depth. + /// Returns the size limit for this stream. /// /// - /// In order to prevent malicious - /// messages from causing stack overflows, CodedInputStream limits - /// how deeply messages may be nested. The default limit is 64. + /// This limit is applied when reading from the underlying stream, as a sanity check. It is + /// not applied when reading from a byte array data source without an underlying stream. + /// The default value is 64MB. /// - public int SetRecursionLimit(int limit) - { - if (limit < 0) - { - throw new ArgumentOutOfRangeException("Recursion limit cannot be negative: " + limit); - } - int oldLimit = recursionLimit; - recursionLimit = limit; - return oldLimit; - } + /// + /// The size limit. + /// + public int SizeLimit { get { return sizeLimit; } } /// - /// Set the maximum message size. + /// Returns the recursion limit for this stream. This limit is applied whilst reading messages, + /// to avoid maliciously-recursive data. /// /// - /// In order to prevent malicious messages from exhausting memory or - /// causing integer overflows, CodedInputStream limits how large a message may be. - /// The default limit is 64MB. You should set this limit as small - /// as you can without harming your app's functionality. Note that - /// size limits only apply when reading from an InputStream, not - /// when constructed around a raw byte array (nor with ByteString.NewCodedInput). - /// If you want to read several messages from a single CodedInputStream, you - /// can call ResetSizeCounter() after each message to avoid hitting the - /// size limit. + /// The default limit is 64. /// - public int SetSizeLimit(int limit) - { - if (limit < 0) - { - throw new ArgumentOutOfRangeException("Size limit cannot be negative: " + limit); - } - int oldLimit = sizeLimit; - sizeLimit = limit; - return oldLimit; - } - - /// - /// Resets the current size counter to zero (see ). - /// - public void ResetSizeCounter() - { - totalBytesRetired = 0; - } - #endregion + /// + /// The recursion limit for this stream. + /// + public int RecursionLimit { get { return recursionLimit; } } #region Validation /// diff --git a/csharp/src/Google.Protobuf/Google.Protobuf.csproj b/csharp/src/Google.Protobuf/Google.Protobuf.csproj index 033edfbb..a17bf81c 100644 --- a/csharp/src/Google.Protobuf/Google.Protobuf.csproj +++ b/csharp/src/Google.Protobuf/Google.Protobuf.csproj @@ -83,6 +83,7 @@ + diff --git a/csharp/src/Google.Protobuf/IDeepCloneable.cs b/csharp/src/Google.Protobuf/IDeepCloneable.cs new file mode 100644 index 00000000..c9c71bbe --- /dev/null +++ b/csharp/src/Google.Protobuf/IDeepCloneable.cs @@ -0,0 +1,54 @@ +#region Copyright notice and license +// Protocol Buffers - Google's data interchange format +// Copyright 2015 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#endregion + +namespace Google.Protobuf +{ + /// + /// Generic interface for a deeply cloneable type. + /// + /// + /// + /// All generated messages implement this interface, but so do some non-message types. + /// Additionally, due to the type constraint on T in , + /// it is simpler to keep this as a separate interface. + /// + /// + /// The type itself, returned by the method. + public interface IDeepCloneable + { + /// + /// Creates a deep clone of this object. + /// + /// A deep clone of this object. + T Clone(); + } +} diff --git a/csharp/src/Google.Protobuf/IMessage.cs b/csharp/src/Google.Protobuf/IMessage.cs index 773845ff..d089f946 100644 --- a/csharp/src/Google.Protobuf/IMessage.cs +++ b/csharp/src/Google.Protobuf/IMessage.cs @@ -35,8 +35,6 @@ using Google.Protobuf.Reflection; namespace Google.Protobuf { - // TODO(jonskeet): Split these interfaces into separate files when we're happy with them. - /// /// Interface for a Protocol Buffers message, supporting /// basic operations required for serialization. @@ -86,24 +84,4 @@ namespace Google.Protobuf /// The message to merge with this one. Must not be null. void MergeFrom(T message); } - - /// - /// Generic interface for a deeply cloneable type. - /// - /// - /// - /// All generated messages implement this interface, but so do some non-message types. - /// Additionally, due to the type constraint on T in , - /// it is simpler to keep this as a separate interface. - /// - /// - /// The type itself, returned by the method. - public interface IDeepCloneable - { - /// - /// Creates a deep clone of this object. - /// - /// A deep clone of this object. - T Clone(); - } } diff --git a/csharp/src/Google.Protobuf/Reflection/DescriptorPool.cs b/csharp/src/Google.Protobuf/Reflection/DescriptorPool.cs index 157ea400..759955e6 100644 --- a/csharp/src/Google.Protobuf/Reflection/DescriptorPool.cs +++ b/csharp/src/Google.Protobuf/Reflection/DescriptorPool.cs @@ -257,10 +257,12 @@ namespace Google.Protobuf.Reflection /// or unqualified. C++-like name lookup semantics are used to search for the /// matching descriptor. /// + /// + /// This isn't heavily optimized, but it's only used during cross linking anyway. + /// If it starts being used more widely, we should look at performance more carefully. + /// internal IDescriptor LookupSymbol(string name, IDescriptor relativeTo) { - // TODO(jonskeet): This could be optimized in a number of ways. - IDescriptor result; if (name.StartsWith(".")) { @@ -282,7 +284,6 @@ namespace Google.Protobuf.Reflection { // Chop off the last component of the scope. - // TODO(jonskeet): Make this more efficient. May not be worth using StringBuilder at all int dotpos = scopeToTry.ToString().LastIndexOf("."); if (dotpos == -1) { -- cgit v1.2.3