diff options
Diffstat (limited to 'src/ProtocolBuffers')
44 files changed, 2990 insertions, 530 deletions
diff --git a/src/ProtocolBuffers/AbstractBuilder.cs b/src/ProtocolBuffers/AbstractBuilder.cs index a60ff0aa..0d1279c7 100644 --- a/src/ProtocolBuffers/AbstractBuilder.cs +++ b/src/ProtocolBuffers/AbstractBuilder.cs @@ -42,93 +42,57 @@ namespace Google.ProtocolBuffers { /// <summary> /// Implementation of the non-generic IMessage interface as far as possible. /// </summary> - public abstract class AbstractBuilder<TMessage, TBuilder> : IBuilder<TMessage, TBuilder> + public abstract class AbstractBuilder<TMessage, TBuilder> : AbstractBuilderLite<TMessage, TBuilder>, IBuilder<TMessage, TBuilder> where TMessage : AbstractMessage<TMessage, TBuilder> where TBuilder : AbstractBuilder<TMessage, TBuilder> { - protected abstract TBuilder ThisBuilder { get; } - #region Unimplemented members of IBuilder public abstract UnknownFieldSet UnknownFields { get; set; } - public abstract TBuilder MergeFrom(TMessage other); - public abstract bool IsInitialized { get; } public abstract IDictionary<FieldDescriptor, object> AllFields { get; } public abstract object this[FieldDescriptor field] { get; set; } public abstract MessageDescriptor DescriptorForType { get; } public abstract int GetRepeatedFieldCount(FieldDescriptor field); public abstract object this[FieldDescriptor field, int index] { get; set; } public abstract bool HasField(FieldDescriptor field); - public abstract TMessage Build(); - public abstract TMessage BuildPartial(); - public abstract TBuilder Clone(); - public abstract TMessage DefaultInstanceForType { get; } public abstract IBuilder CreateBuilderForField(FieldDescriptor field); public abstract TBuilder ClearField(FieldDescriptor field); public abstract TBuilder AddRepeatedField(FieldDescriptor field, object value); #endregion - #region Implementation of methods which don't require type parameter information - public IMessage WeakBuild() { - return Build(); - } - - public IBuilder WeakAddRepeatedField(FieldDescriptor field, object value) { - return AddRepeatedField(field, value); - } - - public IBuilder WeakClear() { - return Clear(); - } - - public IBuilder WeakMergeFrom(IMessage message) { - return MergeFrom(message); - } - - public IBuilder WeakMergeFrom(CodedInputStream input) { - return MergeFrom(input); - } - - public IBuilder WeakMergeFrom(CodedInputStream input, ExtensionRegistry registry) { - return MergeFrom(input, registry); - } - - public IBuilder WeakMergeFrom(ByteString data) { - return MergeFrom(data); - } - - public IBuilder WeakMergeFrom(ByteString data, ExtensionRegistry registry) { - return MergeFrom(data, registry); - } - - public IMessage WeakBuildPartial() { - return BuildPartial(); - } - - public IBuilder WeakClone() { - return Clone(); - } - - public IMessage WeakDefaultInstanceForType { - get { return DefaultInstanceForType; } - } - - public IBuilder WeakClearField(FieldDescriptor field) { - return ClearField(field); - } - #endregion - public TBuilder SetUnknownFields(UnknownFieldSet fields) { UnknownFields = fields; return ThisBuilder; } - public virtual TBuilder Clear() { + public override TBuilder Clear() { foreach(FieldDescriptor field in AllFields.Keys) { ClearField(field); } return ThisBuilder; } + public sealed override TBuilder MergeFrom(IMessageLite other) { + if (other is IMessage) { + return MergeFrom((IMessage) other); + } + throw new ArgumentException("MergeFrom(Message) can only merge messages of the same type."); + } + + /// <summary> + /// Merge the specified other message into the message being + /// built. Merging occurs as follows. For each field: + /// For singular primitive fields, if the field is set in <paramref name="other"/>, + /// then <paramref name="other"/>'s value overwrites the value in this message. + /// For singular message fields, if the field is set in <paramref name="other"/>, + /// it is merged into the corresponding sub-message of this message using the same + /// merging rules. + /// For repeated fields, the elements in <paramref name="other"/> are concatenated + /// with the elements in this message. + /// </summary> + /// <param name="other"></param> + /// <returns></returns> + public abstract TBuilder MergeFrom(TMessage other); + public virtual TBuilder MergeFrom(IMessage other) { if (other.DescriptorForType != DescriptorForType) { throw new ArgumentException("MergeFrom(IMessage) can only merge messages of the same type."); @@ -151,13 +115,13 @@ namespace Google.ProtocolBuffers { } } else if (field.MappedType == MappedType.Message) { // Merge singular messages - IMessage existingValue = (IMessage) this[field]; + IMessageLite existingValue = (IMessageLite)this[field]; if (existingValue == existingValue.WeakDefaultInstanceForType) { this[field] = entry.Value; } else { this[field] = existingValue.WeakCreateBuilderForType() .WeakMergeFrom(existingValue) - .WeakMergeFrom((IMessage) entry.Value) + .WeakMergeFrom((IMessageLite)entry.Value) .WeakBuild(); } } else { @@ -165,14 +129,14 @@ namespace Google.ProtocolBuffers { this[field] = entry.Value; } } - return ThisBuilder; - } - public virtual TBuilder MergeFrom(CodedInputStream input) { - return MergeFrom(input, ExtensionRegistry.Empty); + //Fix for unknown fields not merging, see java's AbstractMessage.Builder<T> line 236 + MergeUnknownFields(other.UnknownFields); + + return ThisBuilder; } - public virtual TBuilder MergeFrom(CodedInputStream input, ExtensionRegistry extensionRegistry) { + public override TBuilder MergeFrom(CodedInputStream input, ExtensionRegistry extensionRegistry) { UnknownFieldSet.Builder unknownFields = UnknownFieldSet.CreateBuilder(UnknownFields); unknownFields.MergeFrom(input, extensionRegistry, this); UnknownFields = unknownFields.Build(); @@ -186,131 +150,65 @@ namespace Google.ProtocolBuffers { return ThisBuilder; } - public virtual TBuilder MergeFrom(ByteString data) { - CodedInputStream input = data.CreateCodedInput(); - MergeFrom(input); - input.CheckLastTagWas(0); + public virtual IBuilder SetField(FieldDescriptor field, object value) { + this[field] = value; return ThisBuilder; } - public virtual TBuilder MergeFrom(ByteString data, ExtensionRegistry extensionRegistry) { - CodedInputStream input = data.CreateCodedInput(); - MergeFrom(input, extensionRegistry); - input.CheckLastTagWas(0); + public virtual IBuilder SetRepeatedField(FieldDescriptor field, int index, object value) { + this[field, index] = value; return ThisBuilder; - } + } - public virtual TBuilder MergeFrom(byte[] data) { - CodedInputStream input = CodedInputStream.CreateInstance(data); - MergeFrom(input); - input.CheckLastTagWas(0); - return ThisBuilder; - } + #region Explicit Implementations - public virtual TBuilder MergeFrom(byte[] data, ExtensionRegistry extensionRegistry) { - CodedInputStream input = CodedInputStream.CreateInstance(data); - MergeFrom(input, extensionRegistry); - input.CheckLastTagWas(0); - return ThisBuilder; + IMessage IBuilder.WeakBuild() { + return Build(); } - public virtual TBuilder MergeFrom(Stream input) { - CodedInputStream codedInput = CodedInputStream.CreateInstance(input); - MergeFrom(codedInput); - codedInput.CheckLastTagWas(0); - return ThisBuilder; + IBuilder IBuilder.WeakAddRepeatedField(FieldDescriptor field, object value) { + return AddRepeatedField(field, value); } - public virtual TBuilder MergeFrom(Stream input, ExtensionRegistry extensionRegistry) { - CodedInputStream codedInput = CodedInputStream.CreateInstance(input); - MergeFrom(codedInput, extensionRegistry); - codedInput.CheckLastTagWas(0); - return ThisBuilder; + IBuilder IBuilder.WeakClear() { + return Clear(); } - public TBuilder MergeDelimitedFrom(Stream input, ExtensionRegistry extensionRegistry) { - int size = (int) CodedInputStream.ReadRawVarint32(input); - Stream limitedStream = new LimitedInputStream(input, size); - return MergeFrom(limitedStream, extensionRegistry); + IBuilder IBuilder.WeakMergeFrom(IMessage message) { + return MergeFrom(message); } - public TBuilder MergeDelimitedFrom(Stream input) { - return MergeDelimitedFrom(input, ExtensionRegistry.Empty); + IBuilder IBuilder.WeakMergeFrom(CodedInputStream input) { + return MergeFrom(input); } - public virtual IBuilder SetField(FieldDescriptor field, object value) { - this[field] = value; - return ThisBuilder; + IBuilder IBuilder.WeakMergeFrom(CodedInputStream input, ExtensionRegistry registry) { + return MergeFrom(input, registry); } - public virtual IBuilder SetRepeatedField(FieldDescriptor field, int index, object value) { - this[field, index] = value; - return ThisBuilder; + IBuilder IBuilder.WeakMergeFrom(ByteString data) { + return MergeFrom(data); } - /// <summary> - /// Stream implementation which proxies another stream, only allowing a certain amount - /// of data to be read. Note that this is only used to read delimited streams, so it - /// doesn't attempt to implement everything. - /// </summary> - private class LimitedInputStream : Stream { - - private readonly Stream proxied; - private int bytesLeft; - - internal LimitedInputStream(Stream proxied, int size) { - this.proxied = proxied; - bytesLeft = size; - } - - public override bool CanRead { - get { return true; } - } - - public override bool CanSeek { - get { return false; } - } - - public override bool CanWrite { - get { return false; } - } - - public override void Flush() { - } - - public override long Length { - get { throw new NotImplementedException(); } - } - - public override long Position { - get { - throw new NotImplementedException(); - } - set { - throw new NotImplementedException(); - } - } + IBuilder IBuilder.WeakMergeFrom(ByteString data, ExtensionRegistry registry) { + return MergeFrom(data, registry); + } - public override int Read(byte[] buffer, int offset, int count) { - if (bytesLeft > 0) { - int bytesRead = proxied.Read(buffer, offset, Math.Min(bytesLeft, count)); - bytesLeft -= bytesRead; - return bytesRead; - } - return 0; - } + IMessage IBuilder.WeakBuildPartial() { + return BuildPartial(); + } - public override long Seek(long offset, SeekOrigin origin) { - throw new NotImplementedException(); - } + IBuilder IBuilder.WeakClone() { + return Clone(); + } - public override void SetLength(long value) { - throw new NotImplementedException(); - } + IMessage IBuilder.WeakDefaultInstanceForType { + get { return DefaultInstanceForType; } + } - public override void Write(byte[] buffer, int offset, int count) { - throw new NotImplementedException(); - } + IBuilder IBuilder.WeakClearField(FieldDescriptor field) { + return ClearField(field); } + #endregion } } diff --git a/src/ProtocolBuffers/AbstractBuilderLite.cs b/src/ProtocolBuffers/AbstractBuilderLite.cs new file mode 100644 index 00000000..c5fbf0ce --- /dev/null +++ b/src/ProtocolBuffers/AbstractBuilderLite.cs @@ -0,0 +1,232 @@ +#region Copyright notice and license +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://github.com/jskeet/dotnet-protobufs/ +// Original C++/Java/Python code: +// http://code.google.com/p/protobuf/ +// +// 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; +using System.Collections.Generic; +using System.IO; + +namespace Google.ProtocolBuffers { + /// <summary> + /// Implementation of the non-generic IMessage interface as far as possible. + /// </summary> + public abstract class AbstractBuilderLite<TMessage, TBuilder> : IBuilderLite<TMessage, TBuilder> + where TMessage : AbstractMessageLite<TMessage, TBuilder> + where TBuilder : AbstractBuilderLite<TMessage, TBuilder> { + + protected abstract TBuilder ThisBuilder { get; } + + public abstract bool IsInitialized { get; } + + public abstract TBuilder Clear(); + + public abstract TBuilder Clone(); + + public abstract TMessage Build(); + + public abstract TMessage BuildPartial(); + + public abstract TBuilder MergeFrom(IMessageLite other); + + public abstract TBuilder MergeFrom(CodedInputStream input, ExtensionRegistry extensionRegistry); + + public abstract TMessage DefaultInstanceForType { get; } + + #region IBuilderLite<TMessage,TBuilder> Members + + public virtual TBuilder MergeFrom(CodedInputStream input) { + return MergeFrom(input, ExtensionRegistry.CreateInstance()); + } + + public TBuilder MergeDelimitedFrom(Stream input) { + return MergeDelimitedFrom(input, ExtensionRegistry.CreateInstance()); + } + + public TBuilder MergeDelimitedFrom(Stream input, ExtensionRegistry extensionRegistry) { + int size = (int)CodedInputStream.ReadRawVarint32(input); + Stream limitedStream = new LimitedInputStream(input, size); + return MergeFrom(limitedStream, extensionRegistry); + } + + public TBuilder MergeFrom(ByteString data) { + return MergeFrom(data, ExtensionRegistry.CreateInstance()); + } + + public TBuilder MergeFrom(ByteString data, ExtensionRegistry extensionRegistry) { + CodedInputStream input = data.CreateCodedInput(); + MergeFrom(input, extensionRegistry); + input.CheckLastTagWas(0); + return ThisBuilder; + } + + public TBuilder MergeFrom(byte[] data) { + CodedInputStream input = CodedInputStream.CreateInstance(data); + MergeFrom(input); + input.CheckLastTagWas(0); + return ThisBuilder; + } + + public TBuilder MergeFrom(byte[] data, ExtensionRegistry extensionRegistry) { + CodedInputStream input = CodedInputStream.CreateInstance(data); + MergeFrom(input, extensionRegistry); + input.CheckLastTagWas(0); + return ThisBuilder; + } + + public TBuilder MergeFrom(Stream input) { + CodedInputStream codedInput = CodedInputStream.CreateInstance(input); + MergeFrom(codedInput); + codedInput.CheckLastTagWas(0); + return ThisBuilder; + } + + public TBuilder MergeFrom(Stream input, ExtensionRegistry extensionRegistry) { + CodedInputStream codedInput = CodedInputStream.CreateInstance(input); + MergeFrom(codedInput, extensionRegistry); + codedInput.CheckLastTagWas(0); + return ThisBuilder; + } + + #endregion + #region Explicit definitions + + IBuilderLite IBuilderLite.WeakClear() { + return Clear(); + } + + IBuilderLite IBuilderLite.WeakMergeFrom(IMessageLite message) { + return MergeFrom(message); + } + + IBuilderLite IBuilderLite.WeakMergeFrom(ByteString data) { + return MergeFrom(data); + } + + IBuilderLite IBuilderLite.WeakMergeFrom(ByteString data, ExtensionRegistry registry) { + return MergeFrom(data, registry); + } + + IBuilderLite IBuilderLite.WeakMergeFrom(CodedInputStream input) { + return MergeFrom(input); + } + + IBuilderLite IBuilderLite.WeakMergeFrom(CodedInputStream input, ExtensionRegistry registry) { + return MergeFrom(input, registry); + } + + IMessageLite IBuilderLite.WeakBuild() { + return Build(); + } + + IMessageLite IBuilderLite.WeakBuildPartial() { + return BuildPartial(); + } + + IBuilderLite IBuilderLite.WeakClone() { + return Clone(); + } + + IMessageLite IBuilderLite.WeakDefaultInstanceForType { + get { return DefaultInstanceForType; } + } + + #endregion + #region LimitedInputStream + /// <summary> + /// Stream implementation which proxies another stream, only allowing a certain amount + /// of data to be read. Note that this is only used to read delimited streams, so it + /// doesn't attempt to implement everything. + /// </summary> + private class LimitedInputStream : Stream { + + private readonly Stream proxied; + private int bytesLeft; + + internal LimitedInputStream(Stream proxied, int size) { + this.proxied = proxied; + bytesLeft = size; + } + + public override bool CanRead { + get { return true; } + } + + public override bool CanSeek { + get { return false; } + } + + public override bool CanWrite { + get { return false; } + } + + public override void Flush() { + } + + public override long Length { + get { throw new NotSupportedException(); } + } + + public override long Position { + get { + throw new NotSupportedException(); + } + set { + throw new NotSupportedException(); + } + } + + public override int Read(byte[] buffer, int offset, int count) { + if (bytesLeft > 0) { + int bytesRead = proxied.Read(buffer, offset, Math.Min(bytesLeft, count)); + bytesLeft -= bytesRead; + return bytesRead; + } + return 0; + } + + public override long Seek(long offset, SeekOrigin origin) { + throw new NotSupportedException(); + } + + public override void SetLength(long value) { + throw new NotSupportedException(); + } + + public override void Write(byte[] buffer, int offset, int count) { + throw new NotSupportedException(); + } + } + #endregion + } +} diff --git a/src/ProtocolBuffers/AbstractMessage.cs b/src/ProtocolBuffers/AbstractMessage.cs index 086aac82..203b71a4 100644 --- a/src/ProtocolBuffers/AbstractMessage.cs +++ b/src/ProtocolBuffers/AbstractMessage.cs @@ -42,7 +42,7 @@ namespace Google.ProtocolBuffers { /// <summary> /// Implementation of the non-generic IMessage interface as far as possible. /// </summary> - public abstract class AbstractMessage<TMessage, TBuilder> : IMessage<TMessage, TBuilder> + public abstract class AbstractMessage<TMessage, TBuilder> : AbstractMessageLite<TMessage, TBuilder>, IMessage<TMessage, TBuilder> where TMessage : AbstractMessage<TMessage, TBuilder> where TBuilder : AbstractBuilder<TMessage, TBuilder> { /// <summary> @@ -59,24 +59,13 @@ namespace Google.ProtocolBuffers { public abstract int GetRepeatedFieldCount(FieldDescriptor field); public abstract object this[FieldDescriptor field, int index] { get; } public abstract UnknownFieldSet UnknownFields { get; } - public abstract TMessage DefaultInstanceForType { get; } - public abstract TBuilder CreateBuilderForType(); - public abstract TBuilder ToBuilder(); #endregion - - public IBuilder WeakCreateBuilderForType() { - return CreateBuilderForType(); - } - - public IBuilder WeakToBuilder() { - return ToBuilder(); - } - public IMessage WeakDefaultInstanceForType { - get { return DefaultInstanceForType; } - } - - public virtual bool IsInitialized { + /// <summary> + /// Returns true iff all required fields in the message and all embedded + /// messages are set. + /// </summary> + public override bool IsInitialized { get { // Check that all required fields are present. foreach (FieldDescriptor field in DescriptorForType.Fields) { @@ -92,13 +81,13 @@ namespace Google.ProtocolBuffers { if (field.IsRepeated) { // We know it's an IList<T>, but not the exact type - so // IEnumerable is the best we can do. (C# generics aren't covariant yet.) - foreach (IMessage element in (IEnumerable) entry.Value) { + foreach (IMessageLite element in (IEnumerable)entry.Value) { if (!element.IsInitialized) { return false; } } } else { - if (!((IMessage)entry.Value).IsInitialized) { + if (!((IMessageLite)entry.Value).IsInitialized) { return false; } } @@ -112,7 +101,23 @@ namespace Google.ProtocolBuffers { return TextFormat.PrintToString(this); } - public virtual void WriteTo(CodedOutputStream output) { + public sealed override void PrintTo(TextWriter writer) { + TextFormat.Print(this, writer); + } + + /// <summary> + /// Serializes the message and writes it to the given output stream. + /// This does not flush or close the stream. + /// </summary> + /// <remarks> + /// Protocol Buffers are not self-delimiting. Therefore, if you write + /// any more data to the stream after the message, you must somehow ensure + /// that the parser on the receiving end does not interpret this as being + /// part of the protocol message. One way of doing this is by writing the size + /// of the message before the data, then making sure you limit the input to + /// that size when receiving the data. Alternatively, use WriteDelimitedTo(Stream). + /// </remarks> + public override void WriteTo(CodedOutputStream output) { foreach (KeyValuePair<FieldDescriptor, object> entry in AllFields) { FieldDescriptor field = entry.Key; if (field.IsRepeated) { @@ -147,7 +152,11 @@ namespace Google.ProtocolBuffers { } } - public virtual int SerializedSize { + /// <summary> + /// Returns the number of bytes required to encode this message. + /// The result is only computed on the first call and memoized after that. + /// </summary> + public override int SerializedSize { get { if (memoizedSize != null) { return memoizedSize.Value; @@ -188,33 +197,12 @@ namespace Google.ProtocolBuffers { } } - public ByteString ToByteString() { - ByteString.CodedBuilder output = new ByteString.CodedBuilder(SerializedSize); - WriteTo(output.CodedOutput); - return output.Build(); - } - - public byte[] ToByteArray() { - byte[] result = new byte[SerializedSize]; - CodedOutputStream output = CodedOutputStream.CreateInstance(result); - WriteTo(output); - output.CheckNoSpaceLeft(); - return result; - } - - public void WriteTo(Stream output) { - CodedOutputStream codedOutput = CodedOutputStream.CreateInstance(output); - WriteTo(codedOutput); - codedOutput.Flush(); - } - - public void WriteDelimitedTo(Stream output) { - CodedOutputStream codedOutput = CodedOutputStream.CreateInstance(output); - codedOutput.WriteRawVarint32((uint) SerializedSize); - WriteTo(codedOutput); - codedOutput.Flush(); - } - + /// <summary> + /// Compares the specified object with this message for equality. + /// Returns true iff the given object is a message of the same type + /// (as defined by DescriptorForType) and has identical values + /// for all its fields. + /// </summary> public override bool Equals(object other) { if (other == this) { return true; @@ -226,6 +214,10 @@ namespace Google.ProtocolBuffers { return Dictionaries.Equals(AllFields, otherMessage.AllFields) && UnknownFields.Equals(otherMessage.UnknownFields); } + /// <summary> + /// Returns the hash code value for this message. + /// TODO(jonskeet): Specify the hash algorithm, but better than the Java one! + /// </summary> public override int GetHashCode() { int hash = 41; hash = (19 * hash) + DescriptorForType.GetHashCode(); @@ -233,5 +225,21 @@ namespace Google.ProtocolBuffers { hash = (29 * hash) + UnknownFields.GetHashCode(); return hash; } + + #region Explicit Members + + IBuilder IMessage.WeakCreateBuilderForType() { + return CreateBuilderForType(); + } + + IBuilder IMessage.WeakToBuilder() { + return ToBuilder(); + } + + IMessage IMessage.WeakDefaultInstanceForType { + get { return DefaultInstanceForType; } + } + + #endregion } } diff --git a/src/ProtocolBuffers/AbstractMessageLite.cs b/src/ProtocolBuffers/AbstractMessageLite.cs new file mode 100644 index 00000000..ee75f3dd --- /dev/null +++ b/src/ProtocolBuffers/AbstractMessageLite.cs @@ -0,0 +1,133 @@ +#region Copyright notice and license +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://github.com/jskeet/dotnet-protobufs/ +// Original C++/Java/Python code: +// http://code.google.com/p/protobuf/ +// +// 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.Collections; +using System.Collections.Generic; +using System.IO; + +namespace Google.ProtocolBuffers { + /// <summary> + /// Implementation of the non-generic IMessage interface as far as possible. + /// </summary> + public abstract class AbstractMessageLite<TMessage, TBuilder> : IMessageLite<TMessage, TBuilder> + where TMessage : AbstractMessageLite<TMessage, TBuilder> + where TBuilder : AbstractBuilderLite<TMessage, TBuilder> { + + + public abstract TBuilder CreateBuilderForType(); + + public abstract TBuilder ToBuilder(); + + public abstract TMessage DefaultInstanceForType { get; } + + public abstract bool IsInitialized { get; } + + public abstract void WriteTo(CodedOutputStream output); + + public abstract int SerializedSize { get; } + + //public override bool Equals(object other) { + //} + + //public override int GetHashCode() { + //} + + public abstract void PrintTo(TextWriter writer); + + #region IMessageLite<TMessage,TBuilder> Members + + /// <summary> + /// Serializes the message to a ByteString. This is a trivial wrapper + /// around WriteTo(CodedOutputStream). + /// </summary> + public ByteString ToByteString() { + ByteString.CodedBuilder output = new ByteString.CodedBuilder(SerializedSize); + WriteTo(output.CodedOutput); + return output.Build(); + } + + /// <summary> + /// Serializes the message to a byte array. This is a trivial wrapper + /// around WriteTo(CodedOutputStream). + /// </summary> + public byte[] ToByteArray() { + byte[] result = new byte[SerializedSize]; + CodedOutputStream output = CodedOutputStream.CreateInstance(result); + WriteTo(output); + output.CheckNoSpaceLeft(); + return result; + } + + /// <summary> + /// Serializes the message and writes it to the given stream. + /// This is just a wrapper around WriteTo(CodedOutputStream). This + /// does not flush or close the stream. + /// </summary> + /// <param name="output"></param> + public void WriteTo(Stream output) { + CodedOutputStream codedOutput = CodedOutputStream.CreateInstance(output); + WriteTo(codedOutput); + codedOutput.Flush(); + } + + /// <summary> + /// Like WriteTo(Stream) but writes the size of the message as a varint before + /// writing the data. This allows more data to be written to the stream after the + /// message without the need to delimit the message data yourself. Use + /// IBuilder.MergeDelimitedFrom(Stream) or the static method + /// YourMessageType.ParseDelimitedFrom(Stream) to parse messages written by this method. + /// </summary> + /// <param name="output"></param> + public void WriteDelimitedTo(Stream output) { + CodedOutputStream codedOutput = CodedOutputStream.CreateInstance(output); + codedOutput.WriteRawVarint32((uint)SerializedSize); + WriteTo(codedOutput); + codedOutput.Flush(); + } + + IBuilderLite IMessageLite.WeakCreateBuilderForType() { + return CreateBuilderForType(); + } + + IBuilderLite IMessageLite.WeakToBuilder() { + return ToBuilder(); + } + + IMessageLite IMessageLite.WeakDefaultInstanceForType { + get { return DefaultInstanceForType; } + } + + #endregion + } +} diff --git a/src/ProtocolBuffers/ByteString.cs b/src/ProtocolBuffers/ByteString.cs index 78b946bb..5add171e 100644 --- a/src/ProtocolBuffers/ByteString.cs +++ b/src/ProtocolBuffers/ByteString.cs @@ -79,6 +79,13 @@ namespace Google.ProtocolBuffers { } /// <summary> + /// Constructs a ByteString from the Base64 Encoded String. + /// </summary> + public static ByteString FromBase64(string bytes) { + return new ByteString(System.Convert.FromBase64String(bytes)); + } + + /// <summary> /// Constructs a ByteString from the given array. The contents /// are copied, so further modifications to the array will not /// be reflected in the returned ByteString. diff --git a/src/ProtocolBuffers/CodedInputStream.cs b/src/ProtocolBuffers/CodedInputStream.cs index 3306f309..922957f2 100644 --- a/src/ProtocolBuffers/CodedInputStream.cs +++ b/src/ProtocolBuffers/CodedInputStream.cs @@ -258,7 +258,7 @@ namespace Google.ProtocolBuffers { /// <summary> /// Reads a group field value from the stream. /// </summary> - public void ReadGroup(int fieldNumber, IBuilder builder, + public void ReadGroup(int fieldNumber, IBuilderLite builder, ExtensionRegistry extensionRegistry) { if (recursionDepth >= recursionLimit) { throw InvalidProtocolBufferException.RecursionLimitExceeded(); @@ -273,12 +273,14 @@ namespace Google.ProtocolBuffers { /// Reads a group field value from the stream and merges it into the given /// UnknownFieldSet. /// </summary> - public void ReadUnknownGroup(int fieldNumber, UnknownFieldSet.Builder builder) { + [Obsolete] + public void ReadUnknownGroup(int fieldNumber, IBuilderLite builder) + { if (recursionDepth >= recursionLimit) { throw InvalidProtocolBufferException.RecursionLimitExceeded(); } ++recursionDepth; - builder.MergeFrom(this); + builder.WeakMergeFrom(this); CheckLastTagWas(WireFormat.MakeTag(fieldNumber, WireFormat.WireType.EndGroup)); --recursionDepth; } @@ -286,7 +288,7 @@ namespace Google.ProtocolBuffers { /// <summary> /// Reads an embedded message field value from the stream. /// </summary> - public void ReadMessage(IBuilder builder, ExtensionRegistry extensionRegistry) { + public void ReadMessage(IBuilderLite builder, ExtensionRegistry extensionRegistry) { int length = (int) ReadRawVarint32(); if (recursionDepth >= recursionLimit) { throw InvalidProtocolBufferException.RecursionLimitExceeded(); @@ -393,7 +395,6 @@ namespace Google.ProtocolBuffers { throw new ArgumentOutOfRangeException("Invalid field type " + fieldType); } } - #endregion #region Underlying reading primitives diff --git a/src/ProtocolBuffers/CodedOutputStream.cs b/src/ProtocolBuffers/CodedOutputStream.cs index a7e1eca6..264ca6ef 100644 --- a/src/ProtocolBuffers/CodedOutputStream.cs +++ b/src/ProtocolBuffers/CodedOutputStream.cs @@ -206,19 +206,20 @@ namespace Google.ProtocolBuffers { /// <summary> /// Writes a group field value, including tag, to the stream. /// </summary> - public void WriteGroup(int fieldNumber, IMessage value) { + public void WriteGroup(int fieldNumber, IMessageLite value) { WriteTag(fieldNumber, WireFormat.WireType.StartGroup); value.WriteTo(this); WriteTag(fieldNumber, WireFormat.WireType.EndGroup); } - public void WriteUnknownGroup(int fieldNumber, UnknownFieldSet value) { + [Obsolete] + public void WriteUnknownGroup(int fieldNumber, IMessageLite value) { WriteTag(fieldNumber, WireFormat.WireType.StartGroup); value.WriteTo(this); WriteTag(fieldNumber, WireFormat.WireType.EndGroup); } - public void WriteMessage(int fieldNumber, IMessage value) { + public void WriteMessage(int fieldNumber, IMessageLite value) { WriteTag(fieldNumber, WireFormat.WireType.LengthDelimited); WriteRawVarint32((uint)value.SerializedSize); value.WriteTo(this); @@ -263,7 +264,7 @@ namespace Google.ProtocolBuffers { WriteRawVarint64(EncodeZigZag64(value)); } - public void WriteMessageSetExtension(int fieldNumber, IMessage value) { + public void WriteMessageSetExtension(int fieldNumber, IMessageLite value) { WriteTag(WireFormat.MessageSetField.Item, WireFormat.WireType.StartGroup); WriteUInt32(WireFormat.MessageSetField.TypeID, (uint)fieldNumber); WriteMessage(WireFormat.MessageSetField.Message, value); @@ -288,15 +289,15 @@ namespace Google.ProtocolBuffers { case FieldType.Fixed32: WriteFixed32(fieldNumber, (uint)value); break; case FieldType.Bool: WriteBool(fieldNumber, (bool)value); break; case FieldType.String: WriteString(fieldNumber, (string)value); break; - case FieldType.Group: WriteGroup(fieldNumber, (IMessage)value); break; - case FieldType.Message: WriteMessage(fieldNumber, (IMessage)value); break; + case FieldType.Group: WriteGroup(fieldNumber, (IMessageLite)value); break; + case FieldType.Message: WriteMessage(fieldNumber, (IMessageLite)value); break; case FieldType.Bytes: WriteBytes(fieldNumber, (ByteString)value); break; case FieldType.UInt32: WriteUInt32(fieldNumber, (uint)value); break; case FieldType.SFixed32: WriteSFixed32(fieldNumber, (int)value); break; case FieldType.SFixed64: WriteSFixed64(fieldNumber, (long)value); break; case FieldType.SInt32: WriteSInt32(fieldNumber, (int)value); break; case FieldType.SInt64: WriteSInt64(fieldNumber, (long)value); break; - case FieldType.Enum: WriteEnum(fieldNumber, ((EnumValueDescriptor)value).Number); + case FieldType.Enum: WriteEnum(fieldNumber, ((IEnumLite)value).Number); break; } } @@ -312,15 +313,15 @@ namespace Google.ProtocolBuffers { case FieldType.Fixed32: WriteFixed32NoTag((uint)value); break; case FieldType.Bool: WriteBoolNoTag((bool)value); break; case FieldType.String: WriteStringNoTag((string)value); break; - case FieldType.Group: WriteGroupNoTag((IMessage)value); break; - case FieldType.Message: WriteMessageNoTag((IMessage)value); break; + case FieldType.Group: WriteGroupNoTag((IMessageLite)value); break; + case FieldType.Message: WriteMessageNoTag((IMessageLite)value); break; case FieldType.Bytes: WriteBytesNoTag((ByteString)value); break; case FieldType.UInt32: WriteUInt32NoTag((uint)value); break; case FieldType.SFixed32: WriteSFixed32NoTag((int)value); break; case FieldType.SFixed64: WriteSFixed64NoTag((long)value); break; case FieldType.SInt32: WriteSInt32NoTag((int)value); break; case FieldType.SInt64: WriteSInt64NoTag((long)value); break; - case FieldType.Enum: WriteEnumNoTag(((EnumValueDescriptor)value).Number); + case FieldType.Enum: WriteEnumNoTag(((IEnumLite)value).Number); break; } } @@ -420,11 +421,11 @@ namespace Google.ProtocolBuffers { /// <summary> /// Writes a group field value, without a tag, to the stream. /// </summary> - public void WriteGroupNoTag(IMessage value) { + public void WriteGroupNoTag(IMessageLite value) { value.WriteTo(this); } - public void WriteMessageNoTag(IMessage value) { + public void WriteMessageNoTag(IMessageLite value) { WriteRawVarint32((uint)value.SerializedSize); value.WriteTo(this); } @@ -685,7 +686,7 @@ namespace Google.ProtocolBuffers { /// Compute the number of bytes that would be needed to encode a /// group field, including the tag. /// </summary> - public static int ComputeGroupSize(int fieldNumber, IMessage value) { + public static int ComputeGroupSize(int fieldNumber, IMessageLite value) { return ComputeTagSize(fieldNumber) * 2 + value.SerializedSize; } @@ -693,8 +694,9 @@ namespace Google.ProtocolBuffers { /// Compute the number of bytes that would be needed to encode a /// group field represented by an UnknownFieldSet, including the tag. /// </summary> + [Obsolete] public static int ComputeUnknownGroupSize(int fieldNumber, - UnknownFieldSet value) { + IMessageLite value) { return ComputeTagSize(fieldNumber) * 2 + value.SerializedSize; } @@ -702,7 +704,7 @@ namespace Google.ProtocolBuffers { /// Compute the number of bytes that would be needed to encode an /// embedded message field, including the tag. /// </summary> - public static int ComputeMessageSize(int fieldNumber, IMessage value) { + public static int ComputeMessageSize(int fieldNumber, IMessageLite value) { int size = value.SerializedSize; return ComputeTagSize(fieldNumber) + ComputeRawVarint32Size((uint)size) + size; } @@ -853,7 +855,7 @@ namespace Google.ProtocolBuffers { /// Compute the number of bytes that would be needed to encode a /// group field, including the tag. /// </summary> - public static int ComputeGroupSizeNoTag(IMessage value) { + public static int ComputeGroupSizeNoTag(IMessageLite value) { return value.SerializedSize; } @@ -861,7 +863,8 @@ namespace Google.ProtocolBuffers { /// Compute the number of bytes that would be needed to encode a /// group field represented by an UnknownFieldSet, including the tag. /// </summary> - public static int ComputeUnknownGroupSizeNoTag(UnknownFieldSet value) { + [Obsolete] + public static int ComputeUnknownGroupSizeNoTag(IMessageLite value) { return value.SerializedSize; } @@ -869,7 +872,7 @@ namespace Google.ProtocolBuffers { /// Compute the number of bytes that would be needed to encode an /// embedded message field, including the tag. /// </summary> - public static int ComputeMessageSizeNoTag(IMessage value) { + public static int ComputeMessageSizeNoTag(IMessageLite value) { int size = value.SerializedSize; return ComputeRawVarint32Size((uint)size) + size; } @@ -943,7 +946,7 @@ namespace Google.ProtocolBuffers { /// MessageSet extension to the stream. For historical reasons, /// the wire format differs from normal fields. /// </summary> - public static int ComputeMessageSetExtensionSize(int fieldNumber, IMessage value) { + public static int ComputeMessageSetExtensionSize(int fieldNumber, IMessageLite value) { return ComputeTagSize(WireFormat.MessageSetField.Item) * 2 + ComputeUInt32Size(WireFormat.MessageSetField.TypeID, (uint) fieldNumber) + ComputeMessageSize(WireFormat.MessageSetField.Message, value); @@ -1004,15 +1007,15 @@ namespace Google.ProtocolBuffers { case FieldType.Fixed32: return ComputeFixed32Size(fieldNumber, (uint)value); case FieldType.Bool: return ComputeBoolSize(fieldNumber, (bool)value); case FieldType.String: return ComputeStringSize(fieldNumber, (string)value); - case FieldType.Group: return ComputeGroupSize(fieldNumber, (IMessage)value); - case FieldType.Message: return ComputeMessageSize(fieldNumber, (IMessage)value); + case FieldType.Group: return ComputeGroupSize(fieldNumber, (IMessageLite)value); + case FieldType.Message: return ComputeMessageSize(fieldNumber, (IMessageLite)value); case FieldType.Bytes: return ComputeBytesSize(fieldNumber, (ByteString)value); case FieldType.UInt32: return ComputeUInt32Size(fieldNumber, (uint)value); case FieldType.SFixed32: return ComputeSFixed32Size(fieldNumber, (int)value); case FieldType.SFixed64: return ComputeSFixed64Size(fieldNumber, (long)value); case FieldType.SInt32: return ComputeSInt32Size(fieldNumber, (int)value); case FieldType.SInt64: return ComputeSInt64Size(fieldNumber, (long)value); - case FieldType.Enum: return ComputeEnumSize(fieldNumber, ((EnumValueDescriptor)value).Number); + case FieldType.Enum: return ComputeEnumSize(fieldNumber, ((IEnumLite)value).Number); default: throw new ArgumentOutOfRangeException("Invalid field type " + fieldType); } @@ -1033,15 +1036,15 @@ namespace Google.ProtocolBuffers { case FieldType.Fixed32: return ComputeFixed32SizeNoTag((uint)value); case FieldType.Bool: return ComputeBoolSizeNoTag((bool)value); case FieldType.String: return ComputeStringSizeNoTag((string)value); - case FieldType.Group: return ComputeGroupSizeNoTag((IMessage)value); - case FieldType.Message: return ComputeMessageSizeNoTag((IMessage)value); + case FieldType.Group: return ComputeGroupSizeNoTag((IMessageLite)value); + case FieldType.Message: return ComputeMessageSizeNoTag((IMessageLite)value); case FieldType.Bytes: return ComputeBytesSizeNoTag((ByteString)value); case FieldType.UInt32: return ComputeUInt32SizeNoTag((uint)value); case FieldType.SFixed32: return ComputeSFixed32SizeNoTag((int)value); case FieldType.SFixed64: return ComputeSFixed64SizeNoTag((long)value); case FieldType.SInt32: return ComputeSInt32SizeNoTag((int)value); case FieldType.SInt64: return ComputeSInt64SizeNoTag((long)value); - case FieldType.Enum: return ComputeEnumSizeNoTag(((EnumValueDescriptor)value).Number); + case FieldType.Enum: return ComputeEnumSizeNoTag(((IEnumLite)value).Number); default: throw new ArgumentOutOfRangeException("Invalid field type " + fieldType); } diff --git a/src/ProtocolBuffers/DescriptorProtos/CSharpOptions.cs b/src/ProtocolBuffers/DescriptorProtos/CSharpOptions.cs index ae13b5ac..61d52fdc 100644 --- a/src/ProtocolBuffers/DescriptorProtos/CSharpOptions.cs +++ b/src/ProtocolBuffers/DescriptorProtos/CSharpOptions.cs @@ -1,4 +1,4 @@ -// Generated by the protocol buffer compiler. DO NOT EDIT! +// Generated by ProtoGen, Version=0.9.0.0, Culture=neutral, PublicKeyToken=8fd7408b07f8d2cd. DO NOT EDIT! using pb = global::Google.ProtocolBuffers; using pbc = global::Google.ProtocolBuffers.Collections; diff --git a/src/ProtocolBuffers/DescriptorProtos/DescriptorProtoFile.cs b/src/ProtocolBuffers/DescriptorProtos/DescriptorProtoFile.cs index 0c6be671..09a00c97 100644 --- a/src/ProtocolBuffers/DescriptorProtos/DescriptorProtoFile.cs +++ b/src/ProtocolBuffers/DescriptorProtos/DescriptorProtoFile.cs @@ -1,4 +1,4 @@ -// Generated by the protocol buffer compiler. DO NOT EDIT! +// Generated by ProtoGen, Version=0.9.0.0, Culture=neutral, PublicKeyToken=8fd7408b07f8d2cd. DO NOT EDIT! using pb = global::Google.ProtocolBuffers; using pbc = global::Google.ProtocolBuffers.Collections; @@ -104,38 +104,42 @@ namespace Google.ProtocolBuffers.DescriptorProtos { "Lmdvb2dsZS5wcm90b2J1Zi5TZXJ2aWNlT3B0aW9ucyJ/ChVNZXRob2REZXNj" + "cmlwdG9yUHJvdG8SDAoEbmFtZRgBIAEoCRISCgppbnB1dF90eXBlGAIgASgJ" + "EhMKC291dHB1dF90eXBlGAMgASgJEi8KB29wdGlvbnMYBCABKAsyHi5nb29n" + - "bGUucHJvdG9idWYuTWV0aG9kT3B0aW9ucyKnAgoLRmlsZU9wdGlvbnMSFAoM" + + "bGUucHJvdG9idWYuTWV0aG9kT3B0aW9ucyKkAwoLRmlsZU9wdGlvbnMSFAoM" + "amF2YV9wYWNrYWdlGAEgASgJEhwKFGphdmFfb3V0ZXJfY2xhc3NuYW1lGAgg" + "ASgJEiIKE2phdmFfbXVsdGlwbGVfZmlsZXMYCiABKAg6BWZhbHNlEkYKDG9w" + "dGltaXplX2ZvchgJIAEoDjIpLmdvb2dsZS5wcm90b2J1Zi5GaWxlT3B0aW9u" + - "cy5PcHRpbWl6ZU1vZGU6BVNQRUVEEkMKFHVuaW50ZXJwcmV0ZWRfb3B0aW9u" + - "GOcHIAMoCzIkLmdvb2dsZS5wcm90b2J1Zi5VbmludGVycHJldGVkT3B0aW9u" + - "IigKDE9wdGltaXplTW9kZRIJCgVTUEVFRBABEg0KCUNPREVfU0laRRACKgkI" + - "6AcQgICAgAIiiAEKDk1lc3NhZ2VPcHRpb25zEiYKF21lc3NhZ2Vfc2V0X3dp" + - "cmVfZm9ybWF0GAEgASgIOgVmYWxzZRJDChR1bmludGVycHJldGVkX29wdGlv" + - "bhjnByADKAsyJC5nb29nbGUucHJvdG9idWYuVW5pbnRlcnByZXRlZE9wdGlv" + - "bioJCOgHEICAgIACIoACCgxGaWVsZE9wdGlvbnMSMgoFY3R5cGUYASABKA4y" + - "Iy5nb29nbGUucHJvdG9idWYuRmllbGRPcHRpb25zLkNUeXBlEg4KBnBhY2tl" + - "ZBgCIAEoCBIZCgpkZXByZWNhdGVkGAMgASgIOgVmYWxzZRIcChRleHBlcmlt" + - "ZW50YWxfbWFwX2tleRgJIAEoCRJDChR1bmludGVycHJldGVkX29wdGlvbhjn" + - "ByADKAsyJC5nb29nbGUucHJvdG9idWYuVW5pbnRlcnByZXRlZE9wdGlvbiIj" + - "CgVDVHlwZRIICgRDT1JEEAESEAoMU1RSSU5HX1BJRUNFEAIqCQjoBxCAgICA" + - "AiJdCgtFbnVtT3B0aW9ucxJDChR1bmludGVycHJldGVkX29wdGlvbhjnByAD" + - "KAsyJC5nb29nbGUucHJvdG9idWYuVW5pbnRlcnByZXRlZE9wdGlvbioJCOgH" + - "EICAgIACImIKEEVudW1WYWx1ZU9wdGlvbnMSQwoUdW5pbnRlcnByZXRlZF9v" + - "cHRpb24Y5wcgAygLMiQuZ29vZ2xlLnByb3RvYnVmLlVuaW50ZXJwcmV0ZWRP" + - "cHRpb24qCQjoBxCAgICAAiJgCg5TZXJ2aWNlT3B0aW9ucxJDChR1bmludGVy" + - "cHJldGVkX29wdGlvbhjnByADKAsyJC5nb29nbGUucHJvdG9idWYuVW5pbnRl" + - "cnByZXRlZE9wdGlvbioJCOgHEICAgIACIl8KDU1ldGhvZE9wdGlvbnMSQwoU" + - "dW5pbnRlcnByZXRlZF9vcHRpb24Y5wcgAygLMiQuZ29vZ2xlLnByb3RvYnVm" + - "LlVuaW50ZXJwcmV0ZWRPcHRpb24qCQjoBxCAgICAAiKFAgoTVW5pbnRlcnBy" + - "ZXRlZE9wdGlvbhI7CgRuYW1lGAIgAygLMi0uZ29vZ2xlLnByb3RvYnVmLlVu" + - "aW50ZXJwcmV0ZWRPcHRpb24uTmFtZVBhcnQSGAoQaWRlbnRpZmllcl92YWx1" + - "ZRgDIAEoCRIaChJwb3NpdGl2ZV9pbnRfdmFsdWUYBCABKAQSGgoSbmVnYXRp" + - "dmVfaW50X3ZhbHVlGAUgASgDEhQKDGRvdWJsZV92YWx1ZRgGIAEoARIUCgxz" + - "dHJpbmdfdmFsdWUYByABKAwaMwoITmFtZVBhcnQSEQoJbmFtZV9wYXJ0GAEg" + - "AigJEhQKDGlzX2V4dGVuc2lvbhgCIAIoCEIpChNjb20uZ29vZ2xlLnByb3Rv" + - "YnVmQhBEZXNjcmlwdG9yUHJvdG9zSAE="); + "cy5PcHRpbWl6ZU1vZGU6BVNQRUVEEiEKE2NjX2dlbmVyaWNfc2VydmljZXMY" + + "ECABKAg6BHRydWUSIwoVamF2YV9nZW5lcmljX3NlcnZpY2VzGBEgASgIOgR0" + + "cnVlEiEKE3B5X2dlbmVyaWNfc2VydmljZXMYEiABKAg6BHRydWUSQwoUdW5p" + + "bnRlcnByZXRlZF9vcHRpb24Y5wcgAygLMiQuZ29vZ2xlLnByb3RvYnVmLlVu" + + "aW50ZXJwcmV0ZWRPcHRpb24iOgoMT3B0aW1pemVNb2RlEgkKBVNQRUVEEAES" + + "DQoJQ09ERV9TSVpFEAISEAoMTElURV9SVU5USU1FEAMqCQjoBxCAgICAAiK4" + + "AQoOTWVzc2FnZU9wdGlvbnMSJgoXbWVzc2FnZV9zZXRfd2lyZV9mb3JtYXQY" + + "ASABKAg6BWZhbHNlEi4KH25vX3N0YW5kYXJkX2Rlc2NyaXB0b3JfYWNjZXNz" + + "b3IYAiABKAg6BWZhbHNlEkMKFHVuaW50ZXJwcmV0ZWRfb3B0aW9uGOcHIAMo" + + "CzIkLmdvb2dsZS5wcm90b2J1Zi5VbmludGVycHJldGVkT3B0aW9uKgkI6AcQ" + + "gICAgAIilAIKDEZpZWxkT3B0aW9ucxI6CgVjdHlwZRgBIAEoDjIjLmdvb2ds" + + "ZS5wcm90b2J1Zi5GaWVsZE9wdGlvbnMuQ1R5cGU6BlNUUklORxIOCgZwYWNr" + + "ZWQYAiABKAgSGQoKZGVwcmVjYXRlZBgDIAEoCDoFZmFsc2USHAoUZXhwZXJp" + + "bWVudGFsX21hcF9rZXkYCSABKAkSQwoUdW5pbnRlcnByZXRlZF9vcHRpb24Y" + + "5wcgAygLMiQuZ29vZ2xlLnByb3RvYnVmLlVuaW50ZXJwcmV0ZWRPcHRpb24i" + + "LwoFQ1R5cGUSCgoGU1RSSU5HEAASCAoEQ09SRBABEhAKDFNUUklOR19QSUVD" + + "RRACKgkI6AcQgICAgAIiXQoLRW51bU9wdGlvbnMSQwoUdW5pbnRlcnByZXRl" + + "ZF9vcHRpb24Y5wcgAygLMiQuZ29vZ2xlLnByb3RvYnVmLlVuaW50ZXJwcmV0" + + "ZWRPcHRpb24qCQjoBxCAgICAAiJiChBFbnVtVmFsdWVPcHRpb25zEkMKFHVu" + + "aW50ZXJwcmV0ZWRfb3B0aW9uGOcHIAMoCzIkLmdvb2dsZS5wcm90b2J1Zi5V" + + "bmludGVycHJldGVkT3B0aW9uKgkI6AcQgICAgAIiYAoOU2VydmljZU9wdGlv" + + "bnMSQwoUdW5pbnRlcnByZXRlZF9vcHRpb24Y5wcgAygLMiQuZ29vZ2xlLnBy" + + "b3RvYnVmLlVuaW50ZXJwcmV0ZWRPcHRpb24qCQjoBxCAgICAAiJfCg1NZXRo" + + "b2RPcHRpb25zEkMKFHVuaW50ZXJwcmV0ZWRfb3B0aW9uGOcHIAMoCzIkLmdv" + + "b2dsZS5wcm90b2J1Zi5VbmludGVycHJldGVkT3B0aW9uKgkI6AcQgICAgAIi" + + "hQIKE1VuaW50ZXJwcmV0ZWRPcHRpb24SOwoEbmFtZRgCIAMoCzItLmdvb2ds" + + "ZS5wcm90b2J1Zi5VbmludGVycHJldGVkT3B0aW9uLk5hbWVQYXJ0EhgKEGlk" + + "ZW50aWZpZXJfdmFsdWUYAyABKAkSGgoScG9zaXRpdmVfaW50X3ZhbHVlGAQg" + + "ASgEEhoKEm5lZ2F0aXZlX2ludF92YWx1ZRgFIAEoAxIUCgxkb3VibGVfdmFs" + + "dWUYBiABKAESFAoMc3RyaW5nX3ZhbHVlGAcgASgMGjMKCE5hbWVQYXJ0EhEK" + + "CW5hbWVfcGFydBgBIAIoCRIUCgxpc19leHRlbnNpb24YAiACKAhCKQoTY29t" + + "Lmdvb2dsZS5wcm90b2J1ZkIQRGVzY3JpcHRvclByb3Rvc0gB"); pbd::FileDescriptor.InternalDescriptorAssigner assigner = delegate(pbd::FileDescriptor root) { descriptor = root; internal__static_google_protobuf_FileDescriptorSet__Descriptor = Descriptor.MessageTypes[0]; @@ -177,11 +181,11 @@ namespace Google.ProtocolBuffers.DescriptorProtos { internal__static_google_protobuf_FileOptions__Descriptor = Descriptor.MessageTypes[8]; internal__static_google_protobuf_FileOptions__FieldAccessorTable = new pb::FieldAccess.FieldAccessorTable<global::Google.ProtocolBuffers.DescriptorProtos.FileOptions, global::Google.ProtocolBuffers.DescriptorProtos.FileOptions.Builder>(internal__static_google_protobuf_FileOptions__Descriptor, - new string[] { "JavaPackage", "JavaOuterClassname", "JavaMultipleFiles", "OptimizeFor", "UninterpretedOption", }); + new string[] { "JavaPackage", "JavaOuterClassname", "JavaMultipleFiles", "OptimizeFor", "CcGenericServices", "JavaGenericServices", "PyGenericServices", "UninterpretedOption", }); internal__static_google_protobuf_MessageOptions__Descriptor = Descriptor.MessageTypes[9]; internal__static_google_protobuf_MessageOptions__FieldAccessorTable = new pb::FieldAccess.FieldAccessorTable<global::Google.ProtocolBuffers.DescriptorProtos.MessageOptions, global::Google.ProtocolBuffers.DescriptorProtos.MessageOptions.Builder>(internal__static_google_protobuf_MessageOptions__Descriptor, - new string[] { "MessageSetWireFormat", "UninterpretedOption", }); + new string[] { "MessageSetWireFormat", "NoStandardDescriptorAccessor", "UninterpretedOption", }); internal__static_google_protobuf_FieldOptions__Descriptor = Descriptor.MessageTypes[10]; internal__static_google_protobuf_FieldOptions__FieldAccessorTable = new pb::FieldAccess.FieldAccessorTable<global::Google.ProtocolBuffers.DescriptorProtos.FieldOptions, global::Google.ProtocolBuffers.DescriptorProtos.FieldOptions.Builder>(internal__static_google_protobuf_FieldOptions__Descriptor, @@ -4013,6 +4017,7 @@ namespace Google.ProtocolBuffers.DescriptorProtos { public enum OptimizeMode { SPEED = 1, CODE_SIZE = 2, + LITE_RUNTIME = 3, } } @@ -4058,6 +4063,36 @@ namespace Google.ProtocolBuffers.DescriptorProtos { get { return optimizeFor_; } } + public const int CcGenericServicesFieldNumber = 16; + private bool hasCcGenericServices; + private bool ccGenericServices_ = true; + public bool HasCcGenericServices { + get { return hasCcGenericServices; } + } + public bool CcGenericServices { + get { return ccGenericServices_; } + } + + public const int JavaGenericServicesFieldNumber = 17; + private bool hasJavaGenericServices; + private bool javaGenericServices_ = true; + public bool HasJavaGenericServices { + get { return hasJavaGenericServices; } + } + public bool JavaGenericServices { + get { return javaGenericServices_; } + } + + public const int PyGenericServicesFieldNumber = 18; + private bool hasPyGenericServices; + private bool pyGenericServices_ = true; + public bool HasPyGenericServices { + get { return hasPyGenericServices; } + } + public bool PyGenericServices { + get { return pyGenericServices_; } + } + public const int UninterpretedOptionFieldNumber = 999; private pbc::PopsicleList<global::Google.ProtocolBuffers.DescriptorProtos.UninterpretedOption> uninterpretedOption_ = new pbc::PopsicleList<global::Google.ProtocolBuffers.DescriptorProtos.UninterpretedOption>(); public scg::IList<global::Google.ProtocolBuffers.DescriptorProtos.UninterpretedOption> UninterpretedOptionList { @@ -4095,6 +4130,15 @@ namespace Google.ProtocolBuffers.DescriptorProtos { if (HasJavaMultipleFiles) { output.WriteBool(10, JavaMultipleFiles); } + if (HasCcGenericServices) { + output.WriteBool(16, CcGenericServices); + } + if (HasJavaGenericServices) { + output.WriteBool(17, JavaGenericServices); + } + if (HasPyGenericServices) { + output.WriteBool(18, PyGenericServices); + } foreach (global::Google.ProtocolBuffers.DescriptorProtos.UninterpretedOption element in UninterpretedOptionList) { output.WriteMessage(999, element); } @@ -4121,6 +4165,15 @@ namespace Google.ProtocolBuffers.DescriptorProtos { if (HasOptimizeFor) { size += pb::CodedOutputStream.ComputeEnumSize(9, (int) OptimizeFor); } + if (HasCcGenericServices) { + size += pb::CodedOutputStream.ComputeBoolSize(16, CcGenericServices); + } + if (HasJavaGenericServices) { + size += pb::CodedOutputStream.ComputeBoolSize(17, JavaGenericServices); + } + if (HasPyGenericServices) { + size += pb::CodedOutputStream.ComputeBoolSize(18, PyGenericServices); + } foreach (global::Google.ProtocolBuffers.DescriptorProtos.UninterpretedOption element in UninterpretedOptionList) { size += pb::CodedOutputStream.ComputeMessageSize(999, element); } @@ -4230,6 +4283,15 @@ namespace Google.ProtocolBuffers.DescriptorProtos { if (other.HasOptimizeFor) { OptimizeFor = other.OptimizeFor; } + if (other.HasCcGenericServices) { + CcGenericServices = other.CcGenericServices; + } + if (other.HasJavaGenericServices) { + JavaGenericServices = other.JavaGenericServices; + } + if (other.HasPyGenericServices) { + PyGenericServices = other.PyGenericServices; + } if (other.uninterpretedOption_.Count != 0) { base.AddRange(other.uninterpretedOption_, result.uninterpretedOption_); } @@ -4290,6 +4352,18 @@ namespace Google.ProtocolBuffers.DescriptorProtos { JavaMultipleFiles = input.ReadBool(); break; } + case 128: { + CcGenericServices = input.ReadBool(); + break; + } + case 136: { + JavaGenericServices = input.ReadBool(); + break; + } + case 144: { + PyGenericServices = input.ReadBool(); + break; + } case 7994: { global::Google.ProtocolBuffers.DescriptorProtos.UninterpretedOption.Builder subBuilder = global::Google.ProtocolBuffers.DescriptorProtos.UninterpretedOption.CreateBuilder(); input.ReadMessage(subBuilder, extensionRegistry); @@ -4375,6 +4449,60 @@ namespace Google.ProtocolBuffers.DescriptorProtos { return this; } + public bool HasCcGenericServices { + get { return result.HasCcGenericServices; } + } + public bool CcGenericServices { + get { return result.CcGenericServices; } + set { SetCcGenericServices(value); } + } + public Builder SetCcGenericServices(bool value) { + result.hasCcGenericServices = true; + result.ccGenericServices_ = value; + return this; + } + public Builder ClearCcGenericServices() { + result.hasCcGenericServices = false; + result.ccGenericServices_ = true; + return this; + } + + public bool HasJavaGenericServices { + get { return result.HasJavaGenericServices; } + } + public bool JavaGenericServices { + get { return result.JavaGenericServices; } + set { SetJavaGenericServices(value); } + } + public Builder SetJavaGenericServices(bool value) { + result.hasJavaGenericServices = true; + result.javaGenericServices_ = value; + return this; + } + public Builder ClearJavaGenericServices() { + result.hasJavaGenericServices = false; + result.javaGenericServices_ = true; + return this; + } + + public bool HasPyGenericServices { + get { return result.HasPyGenericServices; } + } + public bool PyGenericServices { + get { return result.PyGenericServices; } + set { SetPyGenericServices(value); } + } + public Builder SetPyGenericServices(bool value) { + result.hasPyGenericServices = true; + result.pyGenericServices_ = value; + return this; + } + public Builder ClearPyGenericServices() { + result.hasPyGenericServices = false; + result.pyGenericServices_ = true; + return this; + } + public pbc::IPopsicleList<global::Google.ProtocolBuffers.DescriptorProtos.UninterpretedOption> UninterpretedOptionList { get { return result.uninterpretedOption_; } } @@ -4450,6 +4578,16 @@ namespace Google.ProtocolBuffers.DescriptorProtos { get { return messageSetWireFormat_; } } + public const int NoStandardDescriptorAccessorFieldNumber = 2; + private bool hasNoStandardDescriptorAccessor; + private bool noStandardDescriptorAccessor_ = false; + public bool HasNoStandardDescriptorAccessor { + get { return hasNoStandardDescriptorAccessor; } + } + public bool NoStandardDescriptorAccessor { + get { return noStandardDescriptorAccessor_; } + } + public const int UninterpretedOptionFieldNumber = 999; private pbc::PopsicleList<global::Google.ProtocolBuffers.DescriptorProtos.UninterpretedOption> uninterpretedOption_ = new pbc::PopsicleList<global::Google.ProtocolBuffers.DescriptorProtos.UninterpretedOption>(); public scg::IList<global::Google.ProtocolBuffers.DescriptorProtos.UninterpretedOption> UninterpretedOptionList { @@ -4478,6 +4616,9 @@ namespace Google.ProtocolBuffers.DescriptorProtos { if (HasMessageSetWireFormat) { output.WriteBool(1, MessageSetWireFormat); } + if (HasNoStandardDescriptorAccessor) { + output.WriteBool(2, NoStandardDescriptorAccessor); + } foreach (global::Google.ProtocolBuffers.DescriptorProtos.UninterpretedOption element in UninterpretedOptionList) { output.WriteMessage(999, element); } @@ -4495,6 +4636,9 @@ namespace Google.ProtocolBuffers.DescriptorProtos { if (HasMessageSetWireFormat) { size += pb::CodedOutputStream.ComputeBoolSize(1, MessageSetWireFormat); } + if (HasNoStandardDescriptorAccessor) { + size += pb::CodedOutputStream.ComputeBoolSize(2, NoStandardDescriptorAccessor); + } foreach (global::Google.ProtocolBuffers.DescriptorProtos.UninterpretedOption element in UninterpretedOptionList) { size += pb::CodedOutputStream.ComputeMessageSize(999, element); } @@ -4595,6 +4739,9 @@ namespace Google.ProtocolBuffers.DescriptorProtos { if (other.HasMessageSetWireFormat) { MessageSetWireFormat = other.MessageSetWireFormat; } + if (other.HasNoStandardDescriptorAccessor) { + NoStandardDescriptorAccessor = other.NoStandardDescriptorAccessor; + } if (other.uninterpretedOption_.Count != 0) { base.AddRange(other.uninterpretedOption_, result.uninterpretedOption_); } @@ -4635,6 +4782,10 @@ namespace Google.ProtocolBuffers.DescriptorProtos { MessageSetWireFormat = input.ReadBool(); break; } + case 16: { + NoStandardDescriptorAccessor = input.ReadBool(); + break; + } case 7994: { global::Google.ProtocolBuffers.DescriptorProtos.UninterpretedOption.Builder subBuilder = global::Google.ProtocolBuffers.DescriptorProtos.UninterpretedOption.CreateBuilder(); input.ReadMessage(subBuilder, extensionRegistry); @@ -4664,6 +4815,24 @@ namespace Google.ProtocolBuffers.DescriptorProtos { return this; } + public bool HasNoStandardDescriptorAccessor { + get { return result.HasNoStandardDescriptorAccessor; } + } + public bool NoStandardDescriptorAccessor { + get { return result.NoStandardDescriptorAccessor; } + set { SetNoStandardDescriptorAccessor(value); } + } + public Builder SetNoStandardDescriptorAccessor(bool value) { + result.hasNoStandardDescriptorAccessor = true; + result.noStandardDescriptorAccessor_ = value; + return this; + } + public Builder ClearNoStandardDescriptorAccessor() { + result.hasNoStandardDescriptorAccessor = false; + result.noStandardDescriptorAccessor_ = false; + return this; + } + public pbc::IPopsicleList<global::Google.ProtocolBuffers.DescriptorProtos.UninterpretedOption> UninterpretedOptionList { get { return result.uninterpretedOption_; } } @@ -4732,6 +4901,7 @@ namespace Google.ProtocolBuffers.DescriptorProtos { #region Nested types public static class Types { public enum CType { + STRING = 0, CORD = 1, STRING_PIECE = 2, } @@ -4741,7 +4911,7 @@ namespace Google.ProtocolBuffers.DescriptorProtos { public const int CtypeFieldNumber = 1; private bool hasCtype; - private global::Google.ProtocolBuffers.DescriptorProtos.FieldOptions.Types.CType ctype_ = global::Google.ProtocolBuffers.DescriptorProtos.FieldOptions.Types.CType.CORD; + private global::Google.ProtocolBuffers.DescriptorProtos.FieldOptions.Types.CType ctype_ = global::Google.ProtocolBuffers.DescriptorProtos.FieldOptions.Types.CType.STRING; public bool HasCtype { get { return hasCtype; } } @@ -5036,7 +5206,7 @@ namespace Google.ProtocolBuffers.DescriptorProtos { } public Builder ClearCtype() { result.hasCtype = false; - result.ctype_ = global::Google.ProtocolBuffers.DescriptorProtos.FieldOptions.Types.CType.CORD; + result.ctype_ = global::Google.ProtocolBuffers.DescriptorProtos.FieldOptions.Types.CType.STRING; return this; } diff --git a/src/ProtocolBuffers/Descriptors/EnumDescriptor.cs b/src/ProtocolBuffers/Descriptors/EnumDescriptor.cs index 2fa18d9a..d6ef4e17 100644 --- a/src/ProtocolBuffers/Descriptors/EnumDescriptor.cs +++ b/src/ProtocolBuffers/Descriptors/EnumDescriptor.cs @@ -37,7 +37,7 @@ namespace Google.ProtocolBuffers.Descriptors { /// <summary> /// Descriptor for an enum type in a .proto file. /// </summary> - public sealed class EnumDescriptor : IndexedDescriptorBase<EnumDescriptorProto, EnumOptions> { + public sealed class EnumDescriptor : IndexedDescriptorBase<EnumDescriptorProto, EnumOptions>, IEnumLiteMap<EnumValueDescriptor> { private readonly MessageDescriptor containingType; private readonly IList<EnumValueDescriptor> values; @@ -73,13 +73,23 @@ namespace Google.ProtocolBuffers.Descriptors { } /// <summary> + /// Logic moved from FieldSet to continue current behavior + /// </summary> + public bool IsValidValue(IEnumLite value) { + return value is EnumValueDescriptor && ((EnumValueDescriptor)value).EnumDescriptor == this; + } + + /// <summary> /// Finds an enum value by number. If multiple enum values have the /// same number, this returns the first defined value with that number. /// </summary> - internal EnumValueDescriptor FindValueByNumber(int number) { + public EnumValueDescriptor FindValueByNumber(int number) { return File.DescriptorPool.FindEnumValueByNumber(this, number); } + IEnumLite IEnumLiteMap.FindValueByNumber(int number) { + return FindValueByNumber(number); + } /// <summary> /// Finds an enum value by name. /// </summary> diff --git a/src/ProtocolBuffers/Descriptors/EnumValueDescriptor.cs b/src/ProtocolBuffers/Descriptors/EnumValueDescriptor.cs index 4125814d..732dec06 100644 --- a/src/ProtocolBuffers/Descriptors/EnumValueDescriptor.cs +++ b/src/ProtocolBuffers/Descriptors/EnumValueDescriptor.cs @@ -36,7 +36,7 @@ namespace Google.ProtocolBuffers.Descriptors { /// <summary> /// Descriptor for a single enum value within an enum in a .proto file. /// </summary> - public sealed class EnumValueDescriptor : IndexedDescriptorBase<EnumValueDescriptorProto, EnumValueOptions> { + public sealed class EnumValueDescriptor : IndexedDescriptorBase<EnumValueDescriptorProto, EnumValueOptions>, IEnumLite { private readonly EnumDescriptor enumDescriptor; diff --git a/src/ProtocolBuffers/Descriptors/FieldDescriptor.cs b/src/ProtocolBuffers/Descriptors/FieldDescriptor.cs index 7d99ed21..854b3a89 100644 --- a/src/ProtocolBuffers/Descriptors/FieldDescriptor.cs +++ b/src/ProtocolBuffers/Descriptors/FieldDescriptor.cs @@ -40,7 +40,7 @@ namespace Google.ProtocolBuffers.Descriptors { /// <summary> /// Descriptor for a field or extension within a message in a .proto file. /// </summary> - public sealed class FieldDescriptor : IndexedDescriptorBase<FieldDescriptorProto, FieldOptions>, IComparable<FieldDescriptor> { + public sealed class FieldDescriptor : IndexedDescriptorBase<FieldDescriptorProto, FieldOptions>, IComparable<FieldDescriptor>, IFieldDescriptorLite { private readonly MessageDescriptor extensionScope; private EnumDescriptor enumType; @@ -299,9 +299,26 @@ namespace Google.ProtocolBuffers.Descriptors { } return FieldNumber - other.FieldNumber; } - /// <summary> + /// Compares this descriptor with another one, ordering in "canonical" order + /// which simply means ascending order by field number. <paramref name="other"/> + /// must be a field of the same type, i.e. the <see cref="ContainingType"/> of + /// both fields must be the same. + /// </summary> + public int CompareTo(IFieldDescriptorLite other) { + return FieldNumber - other.FieldNumber; + } + + IEnumLiteMap IFieldDescriptorLite.EnumType { + get { return EnumType; } + } + + bool IFieldDescriptorLite.MessageSetWireFormat { + get { return ContainingType.Options.MessageSetWireFormat; } + } + + /// <summary> /// For enum fields, returns the field's type. /// </summary> public EnumDescriptor EnumType { diff --git a/src/ProtocolBuffers/Descriptors/FieldMappingAttribute.cs b/src/ProtocolBuffers/Descriptors/FieldMappingAttribute.cs index e62360fc..b0bf5de1 100644 --- a/src/ProtocolBuffers/Descriptors/FieldMappingAttribute.cs +++ b/src/ProtocolBuffers/Descriptors/FieldMappingAttribute.cs @@ -30,6 +30,8 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. using System; +using System.Collections.Generic; +using Google.ProtocolBuffers.Collections; namespace Google.ProtocolBuffers.Descriptors { @@ -46,5 +48,29 @@ namespace Google.ProtocolBuffers.Descriptors { internal MappedType MappedType { get; private set; } internal WireFormat.WireType WireType { get; private set; } + + + /// <summary> + /// Immutable mapping from field type to mapped type. Built using the attributes on + /// FieldType values. + /// </summary> + static readonly IDictionary<FieldType, FieldMappingAttribute> FieldTypeToMappedTypeMap = MapFieldTypes(); + + private static IDictionary<FieldType, FieldMappingAttribute> MapFieldTypes() { + var map = new Dictionary<FieldType, FieldMappingAttribute>(); + foreach (System.Reflection.FieldInfo field in typeof(FieldType).GetFields(System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public)) { + FieldType fieldType = (FieldType)field.GetValue(null); + FieldMappingAttribute mapping = (FieldMappingAttribute)field.GetCustomAttributes(typeof(FieldMappingAttribute), false)[0]; + map[fieldType] = mapping; + } + return Dictionaries.AsReadOnly(map); + } + + internal static MappedType MappedTypeFromFieldType(FieldType type) { + return FieldTypeToMappedTypeMap[type].MappedType; + } + internal static WireFormat.WireType WireTypeFromFieldType(FieldType type, bool packed) { + return packed ? WireFormat.WireType.LengthDelimited : FieldTypeToMappedTypeMap[type].WireType; + } } } diff --git a/src/ProtocolBuffers/DynamicMessage.cs b/src/ProtocolBuffers/DynamicMessage.cs index 12f2186c..44fe0f39 100644 --- a/src/ProtocolBuffers/DynamicMessage.cs +++ b/src/ProtocolBuffers/DynamicMessage.cs @@ -180,7 +180,7 @@ namespace Google.ProtocolBuffers { } public override IDictionary<FieldDescriptor, object> AllFields { - get { return fields.AllFields; } + get { return fields.AllFieldDescriptors; } } public override bool HasField(FieldDescriptor field) { @@ -216,7 +216,7 @@ namespace Google.ProtocolBuffers { } public bool Initialized { - get { return fields.IsInitializedWithRespectTo(type); } + get { return fields.IsInitializedWithRespectTo(type.Fields); } } public override void WriteTo(CodedOutputStream output) { @@ -295,7 +295,8 @@ namespace Google.ProtocolBuffers { } public override Builder MergeFrom(DynamicMessage other) { - return MergeFrom((IMessage)other); + IMessage downcast = other; + return MergeFrom(downcast); } public override DynamicMessage Build() { @@ -335,7 +336,7 @@ namespace Google.ProtocolBuffers { } public override bool IsInitialized { - get { return fields.IsInitializedWithRespectTo(type); } + get { return fields.IsInitializedWithRespectTo(type.Fields); } } public override Builder MergeFrom(CodedInputStream input, ExtensionRegistry extensionRegistry) { @@ -354,7 +355,7 @@ namespace Google.ProtocolBuffers { } public override IDictionary<FieldDescriptor, object> AllFields { - get { return fields.AllFields; } + get { return fields.AllFieldDescriptors; } } public override IBuilder CreateBuilderForField(FieldDescriptor field) { diff --git a/src/ProtocolBuffers/EnumLite.cs b/src/ProtocolBuffers/EnumLite.cs new file mode 100644 index 00000000..12497d8e --- /dev/null +++ b/src/ProtocolBuffers/EnumLite.cs @@ -0,0 +1,106 @@ +#region Copyright notice and license +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://github.com/jskeet/dotnet-protobufs/ +// Original C++/Java/Python code: +// http://code.google.com/p/protobuf/ +// +// 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.Generic; +using System.Globalization; +using System.Text; + +namespace Google.ProtocolBuffers { + + ///<summary> + ///Interface for an enum value or value descriptor, to be used in FieldSet. + ///The lite library stores enum values directly in FieldSets but the full + ///library stores EnumValueDescriptors in order to better support reflection. + ///</summary> + public interface IEnumLite { + int Number { get; } + string Name { get; } + } + + ///<summary> + ///Interface for an object which maps integers to {@link EnumLite}s. + ///{@link Descriptors.EnumDescriptor} implements this interface by mapping + ///numbers to {@link Descriptors.EnumValueDescriptor}s. Additionally, + ///every generated enum type has a static method internalGetValueMap() which + ///returns an implementation of this type that maps numbers to enum values. + ///</summary> + public interface IEnumLiteMap<T> : IEnumLiteMap + where T : IEnumLite { + new T FindValueByNumber(int number); + } + + public interface IEnumLiteMap { + bool IsValidValue(IEnumLite value); + IEnumLite FindValueByNumber(int number); + } + + public class EnumLiteMap<TEnum> : IEnumLiteMap<IEnumLite> + where TEnum : struct, IComparable, IFormattable { + + struct EnumValue : IEnumLite { + readonly TEnum value; + public EnumValue(TEnum value) { + this.value = value; + } + int IEnumLite.Number { + get { return Convert.ToInt32(value); } + } + string IEnumLite.Name { + get { return value.ToString(); } + } + } + + private readonly SortedList<int, IEnumLite> items; + + public EnumLiteMap() { + items = new SortedList<int, IEnumLite>(); + foreach (TEnum evalue in Enum.GetValues(typeof(TEnum))) + items.Add(Convert.ToInt32(evalue), new EnumValue(evalue)); + } + + IEnumLite IEnumLiteMap.FindValueByNumber(int number) { + return FindValueByNumber(number); + } + + public IEnumLite FindValueByNumber(int number) { + IEnumLite val; + return items.TryGetValue(number, out val) ? val : null; + } + + public bool IsValidValue(IEnumLite value) { + return items.ContainsKey(value.Number); + } + } +} diff --git a/src/ProtocolBuffers/ExtendableBuilder.cs b/src/ProtocolBuffers/ExtendableBuilder.cs index 803ceb1e..ad97cdc4 100644 --- a/src/ProtocolBuffers/ExtendableBuilder.cs +++ b/src/ProtocolBuffers/ExtendableBuilder.cs @@ -94,21 +94,21 @@ namespace Google.ProtocolBuffers { /// <summary> /// Appends a value to a repeated extension. /// </summary> - public ExtendableBuilder<TMessage, TBuilder> AddExtension<TExtension>(GeneratedExtensionBase<IList<TExtension>> extension, TExtension value) { + public TBuilder AddExtension<TExtension>(GeneratedExtensionBase<IList<TExtension>> extension, TExtension value) { ExtendableMessage<TMessage, TBuilder> message = MessageBeingBuilt; message.VerifyExtensionContainingType(extension); message.Extensions.AddRepeatedField(extension.Descriptor, extension.SingularToReflectionType(value)); - return this; + return ThisBuilder; } /// <summary> /// Clears an extension. /// </summary> - public ExtendableBuilder<TMessage, TBuilder> ClearExtension<TExtension>(GeneratedExtensionBase<TExtension> extension) { + public TBuilder ClearExtension<TExtension>(GeneratedExtensionBase<TExtension> extension) { ExtendableMessage<TMessage, TBuilder> message = MessageBeingBuilt; message.VerifyExtensionContainingType(extension); message.Extensions.ClearField(extension.Descriptor); - return this; + return ThisBuilder; } /// <summary> diff --git a/src/ProtocolBuffers/ExtendableBuilderLite.cs b/src/ProtocolBuffers/ExtendableBuilderLite.cs new file mode 100644 index 00000000..b3d37eda --- /dev/null +++ b/src/ProtocolBuffers/ExtendableBuilderLite.cs @@ -0,0 +1,262 @@ +#region Copyright notice and license +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://github.com/jskeet/dotnet-protobufs/ +// Original C++/Java/Python code: +// http://code.google.com/p/protobuf/ +// +// 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.Generic; +using Google.ProtocolBuffers.Descriptors; + +namespace Google.ProtocolBuffers { + public abstract class ExtendableBuilderLite<TMessage, TBuilder> : GeneratedBuilderLite<TMessage, TBuilder> + where TMessage : ExtendableMessageLite<TMessage, TBuilder> + where TBuilder : GeneratedBuilderLite<TMessage, TBuilder> { + + protected ExtendableBuilderLite() { } + + /// <summary> + /// Checks if a singular extension is present + /// </summary> + public bool HasExtension<TExtension>(GeneratedExtensionLite<TMessage, TExtension> extension) { + return MessageBeingBuilt.HasExtension(extension); + } + + /// <summary> + /// Returns the number of elements in a repeated extension. + /// </summary> + public int GetExtensionCount<TExtension>(GeneratedExtensionLite<TMessage, IList<TExtension>> extension) { + return MessageBeingBuilt.GetExtensionCount(extension); + } + + /// <summary> + /// Returns the value of an extension. + /// </summary> + public TExtension GetExtension<TExtension>(GeneratedExtensionLite<TMessage, TExtension> extension) { + return MessageBeingBuilt.GetExtension(extension); + } + + /// <summary> + /// Returns one element of a repeated extension. + /// </summary> + public TExtension GetExtension<TExtension>(GeneratedExtensionLite<TMessage, IList<TExtension>> extension, int index) { + return MessageBeingBuilt.GetExtension(extension, index); + } + + /// <summary> + /// Sets the value of an extension. + /// </summary> + public TBuilder SetExtension<TExtension>(GeneratedExtensionLite<TMessage, TExtension> extension, TExtension value) { + ExtendableMessageLite<TMessage, TBuilder> message = MessageBeingBuilt; + message.VerifyExtensionContainingType(extension); + message.Extensions[extension.Descriptor] = extension.ToReflectionType(value); + return ThisBuilder; + } + + /// <summary> + /// Sets the value of one element of a repeated extension. + /// </summary> + public TBuilder SetExtension<TExtension>(GeneratedExtensionLite<TMessage, IList<TExtension>> extension, int index, TExtension value) { + ExtendableMessageLite<TMessage, TBuilder> message = MessageBeingBuilt; + message.VerifyExtensionContainingType(extension); + message.Extensions[extension.Descriptor, index] = extension.SingularToReflectionType(value); + return ThisBuilder; + } + + /// <summary> + /// Appends a value to a repeated extension. + /// </summary> + public TBuilder AddExtension<TExtension>(GeneratedExtensionLite<TMessage, IList<TExtension>> extension, TExtension value) { + ExtendableMessageLite<TMessage, TBuilder> message = MessageBeingBuilt; + message.VerifyExtensionContainingType(extension); + message.Extensions.AddRepeatedField(extension.Descriptor, extension.SingularToReflectionType(value)); + return ThisBuilder; + } + + /// <summary> + /// Clears an extension. + /// </summary> + public TBuilder ClearExtension<TExtension>(GeneratedExtensionLite<TMessage, TExtension> extension) { + ExtendableMessageLite<TMessage, TBuilder> message = MessageBeingBuilt; + message.VerifyExtensionContainingType(extension); + message.Extensions.ClearField(extension.Descriptor); + return ThisBuilder; + } + + /// <summary> + /// Called by subclasses to parse an unknown field or an extension. + /// </summary> + /// <returns>true unless the tag is an end-group tag</returns> + [CLSCompliant(false)] + protected override bool ParseUnknownField(CodedInputStream input, + ExtensionRegistry extensionRegistry, uint tag) { + FieldSet extensions = MessageBeingBuilt.Extensions; + + WireFormat.WireType wireType = WireFormat.GetTagWireType(tag); + int fieldNumber = WireFormat.GetTagFieldNumber(tag); + IGeneratedExtensionLite extension = extensionRegistry[DefaultInstanceForType, fieldNumber]; + + bool unknown = false; + bool packed = false; + if (extension == null) { + unknown = true; // Unknown field. + } else if (wireType == FieldMappingAttribute.WireTypeFromFieldType(extension.Descriptor.FieldType, false /* isPacked */)) { + packed = false; // Normal, unpacked value. + } else if (extension.Descriptor.IsRepeated && + //?? just returns true ?? extension.Descriptor.type.isPackable() && + wireType == FieldMappingAttribute.WireTypeFromFieldType(extension.Descriptor.FieldType, true /* isPacked */)) { + packed = true; // Packed value. + } else { + unknown = true; // Wrong wire type. + } + + if (unknown) { // Unknown field or wrong wire type. Skip. + return input.SkipField(tag); + } + + if (packed) { + int length = (int)Math.Min(int.MaxValue, input.ReadRawVarint32()); + int limit = input.PushLimit(length); + if (extension.Descriptor.FieldType == FieldType.Enum) { + while (!input.ReachedLimit) { + int rawValue = input.ReadEnum(); + Object value = + extension.Descriptor.EnumType.FindValueByNumber(rawValue); + if (value == null) { + // If the number isn't recognized as a valid value for this + // enum, drop it (don't even add it to unknownFields). + return true; + } + extensions.AddRepeatedField(extension.Descriptor, value); + } + } else { + while (!input.ReachedLimit) { + Object value = input.ReadPrimitiveField(extension.Descriptor.FieldType); + extensions.AddRepeatedField(extension.Descriptor, value); + } + } + input.PopLimit(limit); + } else { + Object value; + switch (extension.Descriptor.MappedType) { + case MappedType.Message: { + IBuilderLite subBuilder = null; + if (!extension.Descriptor.IsRepeated) { + IMessageLite existingValue = extensions[extension.Descriptor] as IMessageLite; + if (existingValue != null) { + subBuilder = existingValue.WeakToBuilder(); + } + } + if (subBuilder == null) { + subBuilder = extension.MessageDefaultInstance.WeakCreateBuilderForType(); + } + if (extension.Descriptor.FieldType == FieldType.Group) { + input.ReadGroup(extension.Number, subBuilder, extensionRegistry); + } else { + input.ReadMessage(subBuilder, extensionRegistry); + } + value = subBuilder.WeakBuild(); + break; + } + case MappedType.Enum: + int rawValue = input.ReadEnum(); + value = extension.Descriptor.EnumType.FindValueByNumber(rawValue); + // If the number isn't recognized as a valid value for this enum, + // drop it. + if (value == null) { + return true; + } + break; + default: + value = input.ReadPrimitiveField(extension.Descriptor.FieldType); + break; + } + + if (extension.Descriptor.IsRepeated) { + extensions.AddRepeatedField(extension.Descriptor, value); + } else { + extensions[extension.Descriptor] = value; + } + } + + return true; + } + + #region Reflection + + public object this[IFieldDescriptorLite field, int index] { + set { + if (field.IsExtension) { + ExtendableMessageLite<TMessage, TBuilder> message = MessageBeingBuilt; + message.Extensions[field, index] = value; + } else { + throw new NotSupportedException("Not supported in the lite runtime."); + } + } + } + + public object this[IFieldDescriptorLite field] { + set { + if (field.IsExtension) { + ExtendableMessageLite<TMessage, TBuilder> message = MessageBeingBuilt; + message.Extensions[field] = value; + } else { + throw new NotSupportedException("Not supported in the lite runtime."); + } + } + } + + public TBuilder ClearField(IFieldDescriptorLite field) { + if (field.IsExtension) { + ExtendableMessageLite<TMessage, TBuilder> message = MessageBeingBuilt; + message.Extensions.ClearField(field); + return ThisBuilder; + } else { + throw new NotSupportedException("Not supported in the lite runtime."); + } + } + + public TBuilder AddRepeatedField(IFieldDescriptorLite field, object value) { + if (field.IsExtension) { + ExtendableMessageLite<TMessage, TBuilder> message = MessageBeingBuilt; + message.Extensions.AddRepeatedField(field, value); + return ThisBuilder; + } else { + throw new NotSupportedException("Not supported in the lite runtime."); + } + } + + protected void MergeExtensionFields(ExtendableMessageLite<TMessage, TBuilder> other) { + MessageBeingBuilt.Extensions.MergeFrom(other.Extensions); + } + #endregion + } +} diff --git a/src/ProtocolBuffers/ExtendableMessage.cs b/src/ProtocolBuffers/ExtendableMessage.cs index 4450f4f9..c67b5a1d 100644 --- a/src/ProtocolBuffers/ExtendableMessage.cs +++ b/src/ProtocolBuffers/ExtendableMessage.cs @@ -102,8 +102,8 @@ namespace Google.ProtocolBuffers { public override IDictionary<FieldDescriptor, object> AllFields { get { IDictionary<FieldDescriptor, object> result = GetMutableFieldMap(); - foreach(KeyValuePair<FieldDescriptor, object> entry in extensions.AllFields) { - result[entry.Key] = entry.Value; + foreach(KeyValuePair<IFieldDescriptorLite, object> entry in extensions.AllFields) { + result[(FieldDescriptor)entry.Key] = entry.Value; } return Dictionaries.AsReadOnly(result); } @@ -173,9 +173,9 @@ namespace Google.ProtocolBuffers { /// TODO(jonskeet): See if we can improve this in terms of readability. /// </summary> protected class ExtensionWriter { - readonly IEnumerator<KeyValuePair<FieldDescriptor, object>> iterator; + readonly IEnumerator<KeyValuePair<IFieldDescriptorLite, object>> iterator; readonly FieldSet extensions; - KeyValuePair<FieldDescriptor, object>? next = null; + KeyValuePair<IFieldDescriptorLite, object>? next = null; internal ExtensionWriter(ExtendableMessage<TMessage, TBuilder> message) { extensions = message.extensions; diff --git a/src/ProtocolBuffers/ExtendableMessageLite.cs b/src/ProtocolBuffers/ExtendableMessageLite.cs new file mode 100644 index 00000000..aed8545d --- /dev/null +++ b/src/ProtocolBuffers/ExtendableMessageLite.cs @@ -0,0 +1,184 @@ +#region Copyright notice and license +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://github.com/jskeet/dotnet-protobufs/ +// Original C++/Java/Python code: +// http://code.google.com/p/protobuf/ +// +// 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; +using System.Collections.Generic; +using Google.ProtocolBuffers.Collections; + +namespace Google.ProtocolBuffers { + public abstract class ExtendableMessageLite<TMessage, TBuilder> : GeneratedMessageLite<TMessage, TBuilder> + where TMessage : GeneratedMessageLite<TMessage, TBuilder> + where TBuilder : GeneratedBuilderLite<TMessage, TBuilder> { + + protected ExtendableMessageLite() { } + private readonly FieldSet extensions = FieldSet.CreateInstance(); + + /// <summary> + /// Access for the builder. + /// </summary> + internal FieldSet Extensions { + get { return extensions; } + } + + public override bool Equals(object obj) { + ExtendableMessageLite<TMessage, TBuilder> other = obj as ExtendableMessageLite<TMessage, TBuilder>; + return !ReferenceEquals(null, other) && + Dictionaries.Equals(extensions.AllFields, other.extensions.AllFields); + } + + public override int GetHashCode() { + return Dictionaries.GetHashCode(extensions.AllFields); + } + + /// <summary> + /// writes the extensions to the text stream + /// </summary> + public override void PrintTo(System.IO.TextWriter writer) { + foreach (KeyValuePair<IFieldDescriptorLite, object> entry in extensions.AllFields) { + string fn = string.Format("[{0}]", entry.Key.FullName); + if (entry.Key.IsRepeated) { + foreach (object o in ((IEnumerable)entry.Value)) + PrintField(fn, true, o, writer); + } else { + PrintField(fn, true, entry.Value, writer); + } + } + } + + /// <summary> + /// Checks if a singular extension is present. + /// </summary> + public bool HasExtension<TExtension>(GeneratedExtensionLite<TMessage, TExtension> extension) { + VerifyExtensionContainingType(extension); + return extensions.HasField(extension.Descriptor); + } + + /// <summary> + /// Returns the number of elements in a repeated extension. + /// </summary> + public int GetExtensionCount<TExtension>(GeneratedExtensionLite<TMessage, IList<TExtension>> extension) { + VerifyExtensionContainingType(extension); + return extensions.GetRepeatedFieldCount(extension.Descriptor); + } + + /// <summary> + /// Returns the value of an extension. + /// </summary> + public TExtension GetExtension<TExtension>(GeneratedExtensionLite<TMessage, TExtension> extension) { + VerifyExtensionContainingType(extension); + object value = extensions[extension.Descriptor]; + if (value == null) { + return extension.DefaultValue; + } else { + return (TExtension)extension.FromReflectionType(value); + } + } + + /// <summary> + /// Returns one element of a repeated extension. + /// </summary> + public TExtension GetExtension<TExtension>(GeneratedExtensionLite<TMessage, IList<TExtension>> extension, int index) { + VerifyExtensionContainingType(extension); + return (TExtension)extension.SingularFromReflectionType(extensions[extension.Descriptor, index]); + } + + /// <summary> + /// Called to check if all extensions are initialized. + /// </summary> + protected bool ExtensionsAreInitialized { + get { return extensions.IsInitialized; } + } + + public override bool IsInitialized { + get { + return ExtensionsAreInitialized; + } + } + + /// <summary> + /// Used by subclasses to serialize extensions. Extension ranges may be + /// interleaves with field numbers, but we must write them in canonical + /// (sorted by field number) order. This class helps us to write individual + /// ranges of extensions at once. + /// + /// TODO(jonskeet): See if we can improve this in terms of readability. + /// </summary> + protected class ExtensionWriter { + readonly IEnumerator<KeyValuePair<IFieldDescriptorLite, object>> iterator; + readonly FieldSet extensions; + KeyValuePair<IFieldDescriptorLite, object>? next = null; + + internal ExtensionWriter(ExtendableMessageLite<TMessage, TBuilder> message) { + extensions = message.extensions; + iterator = message.extensions.GetEnumerator(); + if (iterator.MoveNext()) { + next = iterator.Current; + } + } + + public void WriteUntil(int end, CodedOutputStream output) { + while (next != null && next.Value.Key.FieldNumber < end) { + extensions.WriteField(next.Value.Key, next.Value.Value, output); + if (iterator.MoveNext()) { + next = iterator.Current; + } else { + next = null; + } + } + } + } + + protected ExtensionWriter CreateExtensionWriter(ExtendableMessageLite<TMessage, TBuilder> message) { + return new ExtensionWriter(message); + } + + /// <summary> + /// Called by subclasses to compute the size of extensions. + /// </summary> + protected int ExtensionsSerializedSize { + get { return extensions.SerializedSize; } + } + + internal void VerifyExtensionContainingType<TExtension>(GeneratedExtensionLite<TMessage, TExtension> extension) { + if (!ReferenceEquals(extension.ContainingTypeDefaultInstance, DefaultInstanceForType)) { + // This can only happen if someone uses unchecked operations. + throw new ArgumentException( + String.Format("Extension is for type \"{0}\" which does not match message type \"{1}\".", + extension.ContainingTypeDefaultInstance, DefaultInstanceForType + )); + } + } + } +} diff --git a/src/ProtocolBuffers/ExtensionInfo.cs b/src/ProtocolBuffers/ExtensionInfo.cs index 4c877ab8..5d99b8ff 100644 --- a/src/ProtocolBuffers/ExtensionInfo.cs +++ b/src/ProtocolBuffers/ExtensionInfo.cs @@ -36,24 +36,42 @@ using Google.ProtocolBuffers.Descriptors; namespace Google.ProtocolBuffers { - public sealed class ExtensionInfo { + public sealed class ExtensionInfo : IGeneratedExtensionLite { /// <summary> /// The extension's descriptor /// </summary> public FieldDescriptor Descriptor { get; private set; } - /// <summary> + IFieldDescriptorLite IGeneratedExtensionLite.Descriptor { get { return Descriptor; } } + + /// <summary> /// A default instance of the extensions's type, if it has a message type, /// or null otherwise. /// </summary> - public IMessage DefaultInstance { get; private set; } + public IMessageLite DefaultInstance { get; private set; } internal ExtensionInfo(FieldDescriptor descriptor) : this(descriptor, null) { } - internal ExtensionInfo(FieldDescriptor descriptor, IMessage defaultInstance) { + internal ExtensionInfo(FieldDescriptor descriptor, IMessageLite defaultInstance) { Descriptor = descriptor; DefaultInstance = defaultInstance; } + + #region IGeneratedExtensionLite Members + + int IGeneratedExtensionLite.Number { + get { return Descriptor.FieldNumber; } + } + + object IGeneratedExtensionLite.ContainingType { + get { return Descriptor; } + } + + IMessageLite IGeneratedExtensionLite.MessageDefaultInstance { + get { return DefaultInstance; } + } + + #endregion } }
\ No newline at end of file diff --git a/src/ProtocolBuffers/ExtensionRegistry.cs b/src/ProtocolBuffers/ExtensionRegistry.cs index 154b6902..b7690731 100644 --- a/src/ProtocolBuffers/ExtensionRegistry.cs +++ b/src/ProtocolBuffers/ExtensionRegistry.cs @@ -88,23 +88,20 @@ namespace Google.ProtocolBuffers { /// could take advantage of this to inject a mutable object into a message /// belonging to privileged code and create mischief.</para> /// </remarks> - public sealed class ExtensionRegistry { - + public sealed partial class ExtensionRegistry { +#if !LITE private static readonly ExtensionRegistry empty = new ExtensionRegistry( new Dictionary<string, ExtensionInfo>(), - new Dictionary<DescriptorIntPair, ExtensionInfo>(), + new Dictionary<ExtensionIntPair, IGeneratedExtensionLite>(), true); private readonly IDictionary<string, ExtensionInfo> extensionsByName; - private readonly IDictionary<DescriptorIntPair, ExtensionInfo> extensionsByNumber; - private readonly bool readOnly; private ExtensionRegistry(IDictionary<String, ExtensionInfo> extensionsByName, - IDictionary<DescriptorIntPair, ExtensionInfo> extensionsByNumber, - bool readOnly) { + IDictionary<ExtensionIntPair, IGeneratedExtensionLite> extensionsByNumber, + bool readOnly) + : this(extensionsByNumber, readOnly) { this.extensionsByName = extensionsByName; - this.extensionsByNumber = extensionsByNumber; - this.readOnly = readOnly; } /// <summary> @@ -112,19 +109,13 @@ namespace Google.ProtocolBuffers { /// </summary> public static ExtensionRegistry CreateInstance() { return new ExtensionRegistry(new Dictionary<string, ExtensionInfo>(), - new Dictionary<DescriptorIntPair, ExtensionInfo>(), false); - } - - /// <summary> - /// Get the unmodifiable singleton empty instance. - /// </summary> - public static ExtensionRegistry Empty { - get { return empty; } + new Dictionary<ExtensionIntPair, IGeneratedExtensionLite>(), false); } public ExtensionRegistry AsReadOnly() { return new ExtensionRegistry(extensionsByName, extensionsByNumber, true); } +#endif /// <summary> /// Finds an extension by fully-qualified field name, in the @@ -146,9 +137,9 @@ namespace Google.ProtocolBuffers { /// </summary> public ExtensionInfo this[MessageDescriptor containingType, int fieldNumber] { get { - ExtensionInfo ret; - extensionsByNumber.TryGetValue(new DescriptorIntPair(containingType, fieldNumber), out ret); - return ret; + IGeneratedExtensionLite ret; + extensionsByNumber.TryGetValue(new ExtensionIntPair(containingType, fieldNumber), out ret); + return ret as ExtensionInfo; } } @@ -198,7 +189,7 @@ namespace Google.ProtocolBuffers { } extensionsByName[extension.Descriptor.FullName] = extension; - extensionsByNumber[new DescriptorIntPair(extension.Descriptor.ContainingType, + extensionsByNumber[new ExtensionIntPair(extension.Descriptor.ContainingType, extension.Descriptor.FieldNumber)] = extension; FieldDescriptor field = extension.Descriptor; @@ -212,34 +203,5 @@ namespace Google.ProtocolBuffers { extensionsByName[field.MessageType.FullName] = extension; } } - - /// <summary> - /// Nested type just used to represent a pair of MessageDescriptor and int, as - /// the key into the "by number" map. - /// </summary> - private struct DescriptorIntPair : IEquatable<DescriptorIntPair> { - readonly MessageDescriptor descriptor; - readonly int number; - - internal DescriptorIntPair(MessageDescriptor descriptor, int number) { - this.descriptor = descriptor; - this.number = number; - } - - public override int GetHashCode() { - return descriptor.GetHashCode() * ((1 << 16) - 1) + number; - } - - public override bool Equals(object obj) { - if (!(obj is DescriptorIntPair)) { - return false; - } - return Equals((DescriptorIntPair)obj); - } - - public bool Equals(DescriptorIntPair other) { - return descriptor == other.descriptor && number == other.number; - } - } } } diff --git a/src/ProtocolBuffers/ExtensionRegistryLite.cs b/src/ProtocolBuffers/ExtensionRegistryLite.cs new file mode 100644 index 00000000..8e420a81 --- /dev/null +++ b/src/ProtocolBuffers/ExtensionRegistryLite.cs @@ -0,0 +1,179 @@ +#region Copyright notice and license +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://github.com/jskeet/dotnet-protobufs/ +// Original C++/Java/Python code: +// http://code.google.com/p/protobuf/ +// +// 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.Collections.Generic; +using System; + +namespace Google.ProtocolBuffers { + + /// <summary> + /// A table of known extensions, searchable by name or field number. When + /// parsing a protocol message that might have extensions, you must provide + /// an <see cref="ExtensionRegistry"/> in which you have registered any extensions + /// that you want to be able to parse. Otherwise, those extensions will just + /// be treated like unknown fields. + /// </summary> + /// <example> + /// For example, if you had the <c>.proto</c> file: + /// <code> + /// option java_class = "MyProto"; + /// + /// message Foo { + /// extensions 1000 to max; + /// } + /// + /// extend Foo { + /// optional int32 bar; + /// } + /// </code> + /// + /// Then you might write code like: + /// + /// <code> + /// extensionRegistry registry = extensionRegistry.CreateInstance(); + /// registry.Add(MyProto.Bar); + /// MyProto.Foo message = MyProto.Foo.ParseFrom(input, registry); + /// </code> + /// </example> + /// + /// <remarks> + /// <para>You might wonder why this is necessary. Two alternatives might come to + /// mind. First, you might imagine a system where generated extensions are + /// automatically registered when their containing classes are loaded. This + /// is a popular technique, but is bad design; among other things, it creates a + /// situation where behavior can change depending on what classes happen to be + /// loaded. It also introduces a security vulnerability, because an + /// unprivileged class could cause its code to be called unexpectedly from a + /// privileged class by registering itself as an extension of the right type. + /// </para> + /// <para>Another option you might consider is lazy parsing: do not parse an + /// extension until it is first requested, at which point the caller must + /// provide a type to use. This introduces a different set of problems. First, + /// it would require a mutex lock any time an extension was accessed, which + /// would be slow. Second, corrupt data would not be detected until first + /// access, at which point it would be much harder to deal with it. Third, it + /// could violate the expectation that message objects are immutable, since the + /// type provided could be any arbitrary message class. An unprivileged user + /// could take advantage of this to inject a mutable object into a message + /// belonging to privileged code and create mischief.</para> + /// </remarks> + public sealed partial class ExtensionRegistry { + private readonly IDictionary<ExtensionIntPair, IGeneratedExtensionLite> extensionsByNumber; + private readonly bool readOnly; + + private ExtensionRegistry(IDictionary<ExtensionIntPair, IGeneratedExtensionLite> extensionsByNumber, + bool readOnly) { + this.extensionsByNumber = extensionsByNumber; + this.readOnly = readOnly; + } + +#if LITE + private static readonly ExtensionRegistry empty = new ExtensionRegistry( + new Dictionary<ExtensionIntPair, IGeneratedExtensionLite>(), + true); + + /// <summary> + /// Construct a new, empty instance. + /// </summary> + public static ExtensionRegistry CreateInstance() { + return new ExtensionRegistry( + new Dictionary<ExtensionIntPair, IGeneratedExtensionLite>(), false); + } + public ExtensionRegistry AsReadOnly() { + return new ExtensionRegistry(extensionsByNumber, true); + } + +#endif + + /// <summary> + /// Get the unmodifiable singleton empty instance. + /// </summary> + public static ExtensionRegistry Empty { + get { return empty; } + } + + /// <summary> + /// Finds an extension by containing type and field number. + /// A null reference is returned if the extension can't be found. + /// </summary> + public IGeneratedExtensionLite this[IMessageLite containingType, int fieldNumber] { + get { + IGeneratedExtensionLite ret; + extensionsByNumber.TryGetValue(new ExtensionIntPair(containingType, fieldNumber), out ret); + return ret; + } + } + + /// <summary> + /// Add an extension from a generated file to the registry. + /// </summary> + public void Add(IGeneratedExtensionLite extension) { + if (readOnly) { + throw new InvalidOperationException("Cannot add entries to a read-only extension registry"); + } + extensionsByNumber.Add( + new ExtensionIntPair(extension.ContainingType, extension.Number), + extension); + } + + /// <summary> + /// Nested type just used to represent a pair of MessageDescriptor and int, as + /// the key into the "by number" map. + /// </summary> + private struct ExtensionIntPair : IEquatable<ExtensionIntPair> { + readonly object msgType; + readonly int number; + + internal ExtensionIntPair(object msgType, int number) { + this.msgType = msgType; + this.number = number; + } + + public override int GetHashCode() { + return msgType.GetHashCode() * ((1 << 16) - 1) + number; + } + + public override bool Equals(object obj) { + if (!(obj is ExtensionIntPair)) { + return false; + } + return Equals((ExtensionIntPair)obj); + } + + public bool Equals(ExtensionIntPair other) { + return msgType.Equals(other.msgType) && number == other.number; + } + } + } +} diff --git a/src/ProtocolBuffers/FieldAccess/RepeatedMessageAccessor.cs b/src/ProtocolBuffers/FieldAccess/RepeatedMessageAccessor.cs index 893b0285..df6680cb 100644 --- a/src/ProtocolBuffers/FieldAccess/RepeatedMessageAccessor.cs +++ b/src/ProtocolBuffers/FieldAccess/RepeatedMessageAccessor.cs @@ -71,7 +71,7 @@ namespace Google.ProtocolBuffers.FieldAccess { } // No... so let's create a builder of the right type, and merge the value in. - IMessage message = (IMessage) value; + IMessageLite message = (IMessageLite) value; return CreateBuilder().WeakMergeFrom(message).WeakBuild(); } diff --git a/src/ProtocolBuffers/FieldAccess/SingleMessageAccessor.cs b/src/ProtocolBuffers/FieldAccess/SingleMessageAccessor.cs index ea422c94..838bc5a2 100644 --- a/src/ProtocolBuffers/FieldAccess/SingleMessageAccessor.cs +++ b/src/ProtocolBuffers/FieldAccess/SingleMessageAccessor.cs @@ -67,7 +67,7 @@ namespace Google.ProtocolBuffers.FieldAccess { } // No... so let's create a builder of the right type, and merge the value in. - IMessage message = (IMessage) value; + IMessageLite message = (IMessageLite) value; return CreateBuilder().WeakMergeFrom(message).WeakBuild(); } diff --git a/src/ProtocolBuffers/FieldSet.cs b/src/ProtocolBuffers/FieldSet.cs index 7c373b95..c3e3d740 100644 --- a/src/ProtocolBuffers/FieldSet.cs +++ b/src/ProtocolBuffers/FieldSet.cs @@ -39,6 +39,21 @@ using Google.ProtocolBuffers.Collections; using Google.ProtocolBuffers.Descriptors; namespace Google.ProtocolBuffers { + + public interface IFieldDescriptorLite : IComparable<IFieldDescriptorLite> { + bool IsRepeated { get; } + bool IsRequired { get; } + bool IsPacked { get; } + bool IsExtension { get; } + bool MessageSetWireFormat { get; } //field.ContainingType.Options.MessageSetWireFormat + int FieldNumber { get; } + string FullName { get; } + IEnumLiteMap EnumType { get; } + FieldType FieldType { get; } + MappedType MappedType { get; } + object DefaultValue { get; } + } + /// <summary> /// A class which represents an arbitrary set of fields of some message type. /// This is used to implement DynamicMessage, and also to represent extensions @@ -56,17 +71,17 @@ namespace Google.ProtocolBuffers { /// </summary> internal sealed class FieldSet { - private static readonly FieldSet defaultInstance = new FieldSet(new Dictionary<FieldDescriptor, object>()).MakeImmutable(); + private static readonly FieldSet defaultInstance = new FieldSet(new Dictionary<IFieldDescriptorLite, object>()).MakeImmutable(); - private IDictionary<FieldDescriptor, object> fields; + private IDictionary<IFieldDescriptorLite, object> fields; - private FieldSet(IDictionary<FieldDescriptor, object> fields) { + private FieldSet(IDictionary<IFieldDescriptorLite, object> fields) { this.fields = fields; } public static FieldSet CreateInstance() { // Use SortedList to keep fields in the canonical order - return new FieldSet(new SortedList<FieldDescriptor, object>()); + return new FieldSet(new SortedList<IFieldDescriptorLite, object>()); } /// <summary> @@ -85,8 +100,8 @@ namespace Google.ProtocolBuffers { } if (hasRepeats) { - var tmp = new SortedList<FieldDescriptor, object>(); - foreach (KeyValuePair<FieldDescriptor, object> entry in fields) { + var tmp = new SortedList<IFieldDescriptorLite, object>(); + foreach (KeyValuePair<IFieldDescriptorLite, object> entry in fields) { IList<object> list = entry.Value as IList<object>; tmp[entry.Key] = list == null ? entry.Value : Lists.AsReadOnly(list); } @@ -110,14 +125,26 @@ namespace Google.ProtocolBuffers { /// is immutable, the entries may not be (i.e. any repeated values are represented by /// mutable lists). The behaviour is not specified if the contents are mutated. /// </summary> - internal IDictionary<FieldDescriptor, object> AllFields { + internal IDictionary<IFieldDescriptorLite, object> AllFields { get { return Dictionaries.AsReadOnly(fields); } } - +#if !LITE + /// <summary> + /// Force coercion to full descriptor dictionary. + /// </summary> + internal IDictionary<Descriptors.FieldDescriptor, object> AllFieldDescriptors { + get { + SortedList<Descriptors.FieldDescriptor, object> copy = new SortedList<Google.ProtocolBuffers.Descriptors.FieldDescriptor, object>(); + foreach (KeyValuePair<IFieldDescriptorLite, object> fd in fields) + copy.Add((Descriptors.FieldDescriptor)fd.Key, fd.Value); + return Dictionaries.AsReadOnly(copy); + } + } +#endif /// <summary> - /// See <see cref="IMessage.HasField"/>. + /// See <see cref="IMessageLite.HasField"/>. /// </summary> - public bool HasField(FieldDescriptor field) { + public bool HasField(IFieldDescriptorLite field) { if (field.IsRepeated) { throw new ArgumentException("HasField() can only be called on non-repeated fields."); } @@ -133,7 +160,7 @@ namespace Google.ProtocolBuffers { } /// <summary> - /// See <see cref="IMessage.Item(FieldDescriptor)"/> + /// See <see cref="IMessageLite.Item(IFieldDescriptorLite)"/> /// </summary> /// <remarks> /// If the field is not set, the behaviour when fetching this property varies by field type: @@ -153,7 +180,7 @@ namespace Google.ProtocolBuffers { /// to ensure it is of an appropriate type. /// </remarks> /// - internal object this[FieldDescriptor field] { + internal object this[IFieldDescriptorLite field] { get { object result; if (fields.TryGetValue(field, out result)) { @@ -191,9 +218,9 @@ namespace Google.ProtocolBuffers { } /// <summary> - /// See <see cref="IMessage.Item(FieldDescriptor,int)" /> + /// See <see cref="IMessageLite.Item(IFieldDescriptorLite,int)" /> /// </summary> - internal object this[FieldDescriptor field, int index] { + internal object this[IFieldDescriptorLite field, int index] { get { if (!field.IsRepeated) { throw new ArgumentException("Indexer specifying field and index can only be called on repeated fields."); @@ -217,7 +244,7 @@ namespace Google.ProtocolBuffers { /// <summary> /// See <see cref="IBuilder{TMessage, TBuilder}.AddRepeatedField" /> /// </summary> - internal void AddRepeatedField(FieldDescriptor field, object value) { + internal void AddRepeatedField(IFieldDescriptorLite field, object value) { if (!field.IsRepeated) { throw new ArgumentException("AddRepeatedField can only be called on repeated fields."); } @@ -233,12 +260,12 @@ namespace Google.ProtocolBuffers { /// <summary> /// Returns an enumerator for the field map. Used to write the fields out. /// </summary> - internal IEnumerator<KeyValuePair<FieldDescriptor, object>> GetEnumerator() { + internal IEnumerator<KeyValuePair<IFieldDescriptorLite, object>> GetEnumerator() { return fields.GetEnumerator(); } /// <summary> - /// See <see cref="IMessage.IsInitialized" /> + /// See <see cref="IMessageLite.IsInitialized" /> /// </summary> /// <remarks> /// Since FieldSet itself does not have any way of knowing about @@ -248,17 +275,17 @@ namespace Google.ProtocolBuffers { /// </remarks> internal bool IsInitialized { get { - foreach (KeyValuePair<FieldDescriptor, object> entry in fields) { - FieldDescriptor field = entry.Key; + foreach (KeyValuePair<IFieldDescriptorLite, object> entry in fields) { + IFieldDescriptorLite field = entry.Key; if (field.MappedType == MappedType.Message) { if (field.IsRepeated) { - foreach(IMessage message in (IEnumerable) entry.Value) { + foreach(IMessageLite message in (IEnumerable) entry.Value) { if (!message.IsInitialized) { return false; } } } else { - if (!((IMessage) entry.Value).IsInitialized) { + if (!((IMessageLite)entry.Value).IsInitialized) { return false; } } @@ -273,8 +300,8 @@ namespace Google.ProtocolBuffers { /// descriptor are present in this field set, as well as whether /// all the embedded messages are themselves initialized. /// </summary> - internal bool IsInitializedWithRespectTo(MessageDescriptor type) { - foreach (FieldDescriptor field in type.Fields) { + internal bool IsInitializedWithRespectTo(IEnumerable typeFields) { + foreach (IFieldDescriptorLite field in typeFields) { if (field.IsRequired && !HasField(field)) { return false; } @@ -285,14 +312,14 @@ namespace Google.ProtocolBuffers { /// <summary> /// See <see cref="IBuilder{TMessage, TBuilder}.ClearField" /> /// </summary> - public void ClearField(FieldDescriptor field) { + public void ClearField(IFieldDescriptorLite field) { fields.Remove(field); } /// <summary> - /// See <see cref="IMessage.GetRepeatedFieldCount" /> + /// See <see cref="IMessageLite.GetRepeatedFieldCount" /> /// </summary> - public int GetRepeatedFieldCount(FieldDescriptor field) { + public int GetRepeatedFieldCount(IFieldDescriptorLite field) { if (!field.IsRepeated) { throw new ArgumentException("GetRepeatedFieldCount() can only be called on repeated fields."); } @@ -300,64 +327,63 @@ namespace Google.ProtocolBuffers { return ((IList<object>) this[field]).Count; } +#if !LITE + /// <summary> + /// See <see cref="IBuilder{TMessage, TBuilder}.MergeFrom(IMessageLite)" /> + /// </summary> + public void MergeFrom(IMessage other) { + foreach (KeyValuePair<Descriptors.FieldDescriptor, object> fd in other.AllFields) + MergeField(fd.Key, fd.Value); + } +#endif + /// <summary> /// Implementation of both <c>MergeFrom</c> methods. /// </summary> /// <param name="otherFields"></param> - private void MergeFields(IEnumerable<KeyValuePair<FieldDescriptor, object>> otherFields) { + public void MergeFrom(FieldSet other) { // Note: We don't attempt to verify that other's fields have valid // types. Doing so would be a losing battle. We'd have to verify // all sub-messages as well, and we'd have to make copies of all of // them to insure that they don't change after verification (since - // the IMessage interface itself cannot enforce immutability of + // the IMessageLite interface itself cannot enforce immutability of // implementations). // TODO(jonskeet): Provide a function somewhere called MakeDeepCopy() // which allows people to make secure deep copies of messages. - foreach (KeyValuePair<FieldDescriptor, object> entry in otherFields) { - FieldDescriptor field = entry.Key; - object existingValue; - fields.TryGetValue(field, out existingValue); - if (field.IsRepeated) { - if (existingValue == null) { - existingValue = new List<object>(); - fields[field] = existingValue; - } - IList<object> list = (IList<object>) existingValue; - foreach (object otherValue in (IEnumerable) entry.Value) { - list.Add(otherValue); - } - } else if (field.MappedType == MappedType.Message && existingValue != null) { - IMessage existingMessage = (IMessage)existingValue; - IMessage merged = existingMessage.WeakToBuilder() - .WeakMergeFrom((IMessage) entry.Value) - .WeakBuild(); - this[field] = merged; - } else { - this[field] = entry.Value; - } + foreach (KeyValuePair<IFieldDescriptorLite, object> entry in other.fields) { + MergeField(entry.Key, entry.Value); } } - /// <summary> - /// See <see cref="IBuilder{TMessage, TBuilder}.MergeFrom(IMessage)" /> - /// </summary> - public void MergeFrom(IMessage other) { - MergeFields(other.AllFields); - } - - /// <summary> - /// Like <see cref="MergeFrom(IMessage)"/>, but merges from another <c>FieldSet</c>. - /// </summary> - public void MergeFrom(FieldSet other) { - MergeFields(other.fields); + private void MergeField(IFieldDescriptorLite field, object mergeValue) { + object existingValue; + fields.TryGetValue(field, out existingValue); + if (field.IsRepeated) { + if (existingValue == null) { + existingValue = new List<object>(); + fields[field] = existingValue; + } + IList<object> list = (IList<object>) existingValue; + foreach (object otherValue in (IEnumerable)mergeValue) { + list.Add(otherValue); + } + } else if (field.MappedType == MappedType.Message && existingValue != null) { + IMessageLite existingMessage = (IMessageLite)existingValue; + IMessageLite merged = existingMessage.WeakToBuilder() + .WeakMergeFrom((IMessageLite)mergeValue) + .WeakBuild(); + this[field] = merged; + } else { + this[field] = mergeValue; + } } /// <summary> - /// See <see cref="IMessage.WriteTo(CodedOutputStream)" />. + /// See <see cref="IMessageLite.WriteTo(CodedOutputStream)" />. /// </summary> public void WriteTo(CodedOutputStream output) { - foreach (KeyValuePair<FieldDescriptor, object> entry in fields) { + foreach (KeyValuePair<IFieldDescriptorLite, object> entry in fields) { WriteField(entry.Key, entry.Value, output); } } @@ -365,9 +391,9 @@ namespace Google.ProtocolBuffers { /// <summary> /// Writes a single field to a CodedOutputStream. /// </summary> - public void WriteField(FieldDescriptor field, Object value, CodedOutputStream output) { - if (field.IsExtension && field.ContainingType.Options.MessageSetWireFormat) { - output.WriteMessageSetExtension(field.FieldNumber, (IMessage) value); + public void WriteField(IFieldDescriptorLite field, Object value, CodedOutputStream output) { + if (field.IsExtension && field.MessageSetWireFormat) { + output.WriteMessageSetExtension(field.FieldNumber, (IMessageLite) value); } else { if (field.IsRepeated) { IEnumerable valueList = (IEnumerable) value; @@ -395,18 +421,18 @@ namespace Google.ProtocolBuffers { } /// <summary> - /// See <see cref="IMessage.SerializedSize" />. It's up to the caller to + /// See <see cref="IMessageLite.SerializedSize" />. It's up to the caller to /// cache the resulting size if desired. /// </summary> public int SerializedSize { get { int size = 0; - foreach (KeyValuePair<FieldDescriptor, object> entry in fields) { - FieldDescriptor field = entry.Key; + foreach (KeyValuePair<IFieldDescriptorLite, object> entry in fields) { + IFieldDescriptorLite field = entry.Key; object value = entry.Value; - if (field.IsExtension && field.ContainingType.Options.MessageSetWireFormat) { - size += CodedOutputStream.ComputeMessageSetExtensionSize(field.FieldNumber, (IMessage)value); + if (field.IsExtension && field.MessageSetWireFormat) { + size += CodedOutputStream.ComputeMessageSetExtensionSize(field.FieldNumber, (IMessageLite)value); } else { if (field.IsRepeated) { IEnumerable valueList = (IEnumerable)value; @@ -440,7 +466,7 @@ namespace Google.ProtocolBuffers { /// </remarks> /// <exception cref="ArgumentException">The value is not of the right type.</exception> /// <exception cref="ArgumentNullException">The value is null.</exception> - private static void VerifyType(FieldDescriptor field, object value) { + private static void VerifyType(IFieldDescriptorLite field, object value) { ThrowHelper.ThrowIfNull(value, "value"); bool isValid = false; switch (field.MappedType) { @@ -454,12 +480,17 @@ namespace Google.ProtocolBuffers { case MappedType.String: isValid = value is string; break; case MappedType.ByteString: isValid = value is ByteString; break; case MappedType.Enum: - EnumValueDescriptor enumValue = value as EnumValueDescriptor; - isValid = enumValue != null && enumValue.EnumDescriptor == field.EnumType; + IEnumLite enumValue = value as IEnumLite; + isValid = enumValue != null && field.EnumType.IsValidValue(enumValue); break; case MappedType.Message: - IMessage messageValue = value as IMessage; - isValid = messageValue != null && messageValue.DescriptorForType == field.MessageType; + IMessageLite messageValue = value as IMessageLite; + isValid = messageValue != null; +#if !LITE + if (isValid && messageValue is IMessage && field is FieldDescriptor) { + isValid = ((IMessage) messageValue).DescriptorForType == ((FieldDescriptor) field).MessageType; + } +#endif break; } @@ -468,10 +499,16 @@ namespace Google.ProtocolBuffers { // the stack trace which exact call failed, since the whole chain is // considered one line of code. So, let's make sure to include the // field name and other useful info in the exception. - throw new ArgumentException("Wrong object type used with protocol message reflection. " - + "Message type \"" + field.ContainingType.FullName - + "\", field \"" + (field.IsExtension ? field.FullName : field.Name) - + "\", value was type \"" + value.GetType().Name + "\"."); + string message = "Wrong object type used with protocol message reflection."; +#if !LITE + Google.ProtocolBuffers.Descriptors.FieldDescriptor fieldinfo = field as Google.ProtocolBuffers.Descriptors.FieldDescriptor; + if (fieldinfo != null) { + message += "Message type \"" + fieldinfo.ContainingType.FullName; + message += "\", field \"" + (fieldinfo.IsExtension ? fieldinfo.FullName : fieldinfo.Name); + message += "\", value was type \"" + value.GetType().Name + "\"."; + } +#endif + throw new ArgumentException(message); } } } diff --git a/src/ProtocolBuffers/GeneratedBuilder.cs b/src/ProtocolBuffers/GeneratedBuilder.cs index 1124fc86..2ec2762f 100644 --- a/src/ProtocolBuffers/GeneratedBuilder.cs +++ b/src/ProtocolBuffers/GeneratedBuilder.cs @@ -148,16 +148,20 @@ namespace Google.ProtocolBuffers { } } else if (field.MappedType == MappedType.Message && HasField(field)) { // Merge singular embedded messages - IMessage oldValue = (IMessage)this[field]; + IMessageLite oldValue = (IMessageLite)this[field]; this[field] = oldValue.WeakCreateBuilderForType() .WeakMergeFrom(oldValue) - .WeakMergeFrom((IMessage)entry.Value) + .WeakMergeFrom((IMessageLite)entry.Value) .WeakBuildPartial(); } else { // Just overwrite this[field] = entry.Value; } } + + //Fix for unknown fields not merging, see java's AbstractMessage.Builder<T> line 236 + MergeUnknownFields(other.UnknownFields); + return ThisBuilder; } diff --git a/src/ProtocolBuffers/GeneratedBuilderLite.cs b/src/ProtocolBuffers/GeneratedBuilderLite.cs new file mode 100644 index 00000000..d721fddd --- /dev/null +++ b/src/ProtocolBuffers/GeneratedBuilderLite.cs @@ -0,0 +1,117 @@ +#region Copyright notice and license +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://github.com/jskeet/dotnet-protobufs/ +// Original C++/Java/Python code: +// http://code.google.com/p/protobuf/ +// +// 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; +using System.Collections.Generic; + +namespace Google.ProtocolBuffers { + /// <summary> + /// All generated protocol message builder classes extend this class. It implements + /// most of the IBuilder interface using reflection. Users can ignore this class + /// as an implementation detail. + /// </summary> + public abstract class GeneratedBuilderLite<TMessage, TBuilder> : AbstractBuilderLite<TMessage, TBuilder> + where TMessage : GeneratedMessageLite<TMessage, TBuilder> + where TBuilder : GeneratedBuilderLite<TMessage, TBuilder> { + + /// <summary> + /// Returns the message being built at the moment. + /// </summary> + protected abstract TMessage MessageBeingBuilt { get; } + + public override TBuilder MergeFrom(IMessageLite other) { + //do nothing, Lite runtime does not support cross-message merges + return ThisBuilder; + } + + public abstract TBuilder MergeFrom(TMessage other); + + public override bool IsInitialized { + get { return MessageBeingBuilt.IsInitialized; } + } + + /// <summary> + /// Adds all of the specified values to the given collection. + /// </summary> + /// <exception cref="ArgumentNullException">Any element of the list is null</exception> + protected void AddRange<T>(IEnumerable<T> source, IList<T> destination) { + ThrowHelper.ThrowIfNull(source); + // We only need to check this for nullable types. + if (default(T) == null) { + ThrowHelper.ThrowIfAnyNull(source); + } + List<T> list = destination as List<T>; + if (list != null) { + list.AddRange(source); + } else { + foreach (T element in source) { + destination.Add(element); + } + } + } + + /// <summary> + /// Called by derived classes to parse an unknown field. + /// </summary> + /// <returns>true unless the tag is an end-group tag</returns> + [CLSCompliant(false)] + protected virtual bool ParseUnknownField(CodedInputStream input, + ExtensionRegistry extensionRegistry, uint tag) { + return input.SkipField(tag); + } + + /// <summary> + /// Like Build(), but will wrap UninitializedMessageException in + /// InvalidProtocolBufferException. + /// </summary> + public TMessage BuildParsed() { + if (!IsInitialized) { + throw new UninitializedMessageException(MessageBeingBuilt).AsInvalidProtocolBufferException(); + } + return BuildPartial(); + } + + /// <summary> + /// Implementation of <see cref="IBuilder{TMessage, TBuilder}.Build" />. + /// </summary> + public override TMessage Build() { + // If the message is null, we'll throw a more appropriate exception in BuildPartial. + if (MessageBeingBuilt != null && !IsInitialized) { + throw new UninitializedMessageException(MessageBeingBuilt); + } + return BuildPartial(); + } + } +} diff --git a/src/ProtocolBuffers/GeneratedExtensionBase.cs b/src/ProtocolBuffers/GeneratedExtensionBase.cs index 813f69cf..aacc0655 100644 --- a/src/ProtocolBuffers/GeneratedExtensionBase.cs +++ b/src/ProtocolBuffers/GeneratedExtensionBase.cs @@ -39,6 +39,7 @@ using System.Reflection; using Google.ProtocolBuffers.Descriptors; namespace Google.ProtocolBuffers { + /// <summary> /// Base type for all generated extensions. /// </summary> @@ -65,7 +66,7 @@ namespace Google.ProtocolBuffers { public abstract class GeneratedExtensionBase<TExtension> { private readonly FieldDescriptor descriptor; - private readonly IMessage messageDefaultInstance; + private readonly IMessageLite messageDefaultInstance; protected GeneratedExtensionBase(FieldDescriptor descriptor, Type singularExtensionType) { if (!descriptor.IsExtension) { @@ -79,7 +80,8 @@ namespace Google.ProtocolBuffers { if (defaultInstanceProperty == null) { throw new ArgumentException("No public static DefaultInstance property for type " + typeof(TExtension).Name); } - messageDefaultInstance = (IMessage)defaultInstanceProperty.GetValue(null, null); + + messageDefaultInstance = (IMessageLite)defaultInstanceProperty.GetValue(null, null); } } @@ -87,10 +89,14 @@ namespace Google.ProtocolBuffers { get { return descriptor; } } + public int Number { + get { return Descriptor.FieldNumber; } + } + /// <summary> /// Returns the default message instance for extensions which are message types. /// </summary> - public IMessage MessageDefaultInstance { + public IMessageLite MessageDefaultInstance { get { return messageDefaultInstance; } } @@ -107,7 +113,7 @@ namespace Google.ProtocolBuffers { // This should not happen in normal use. But, to be nice, we'll // copy the message to whatever type the caller was expecting. return MessageDefaultInstance.WeakCreateBuilderForType() - .WeakMergeFrom((IMessage)value).WeakBuild(); + .WeakMergeFrom((IMessageLite)value).WeakBuild(); } case MappedType.Enum: // Just return a boxed int - that can be unboxed to the enum diff --git a/src/ProtocolBuffers/GeneratedExtensionLite.cs b/src/ProtocolBuffers/GeneratedExtensionLite.cs new file mode 100644 index 00000000..33969f4b --- /dev/null +++ b/src/ProtocolBuffers/GeneratedExtensionLite.cs @@ -0,0 +1,294 @@ +#region Copyright notice and license +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://github.com/jskeet/dotnet-protobufs/ +// Original C++/Java/Python code: +// http://code.google.com/p/protobuf/ +// +// 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; +using System.Collections.Generic; +using Google.ProtocolBuffers.Collections; +using Google.ProtocolBuffers.Descriptors; + +namespace Google.ProtocolBuffers { + + public interface IGeneratedExtensionLite { + int Number { get; } + object ContainingType { get; } + IMessageLite MessageDefaultInstance { get; } + IFieldDescriptorLite Descriptor { get; } + } + + public class ExtensionDescriptorLite : IFieldDescriptorLite { + private readonly string fullName; + private readonly IEnumLiteMap enumTypeMap; + private readonly int number; + private readonly FieldType type; + private readonly bool isRepeated; + private readonly bool isPacked; + private readonly MappedType mapType; + private readonly object defaultValue; + + public ExtensionDescriptorLite(string fullName, IEnumLiteMap enumTypeMap, int number, FieldType type, object defaultValue, bool isRepeated, bool isPacked) { + this.fullName = fullName; + this.enumTypeMap = enumTypeMap; + this.number = number; + this.type = type; + this.mapType = FieldMappingAttribute.MappedTypeFromFieldType(type); + this.isRepeated = isRepeated; + this.isPacked = isPacked; + this.defaultValue = defaultValue; + } + + public string FullName { get { return fullName; } } + + public bool IsRepeated { + get { return isRepeated; } + } + + public bool IsRequired { + get { return false; } + } + + public bool IsPacked { + get { return isPacked; } + } + + public bool IsExtension { + get { return true; } + } + + /// <summary> + /// This is not supported and assertions are made to ensure this does not exist on extensions of Lite types + /// </summary> + public bool MessageSetWireFormat { + get { return false; } + } + + public int FieldNumber { + get { return number; } + } + + public IEnumLiteMap EnumType { + get { return enumTypeMap; } + } + + public FieldType FieldType { + get { return type; } + } + + public MappedType MappedType { + get { return mapType; } + } + + public object DefaultValue { + get { return defaultValue; } + } + + public int CompareTo(IFieldDescriptorLite other) { + return FieldNumber.CompareTo(other.FieldNumber); + } + } + + public class GeneratedRepeatExtensionLite<TContainingType, TExtensionType> : GeneratedExtensionLite<TContainingType, IList<TExtensionType>> + where TContainingType : IMessageLite { + public GeneratedRepeatExtensionLite(string fullName, TContainingType containingTypeDefaultInstance, + IMessageLite messageDefaultInstance, IEnumLiteMap enumTypeMap, int number, FieldType type, bool isPacked) : + base(fullName, containingTypeDefaultInstance, new List<TExtensionType>(), messageDefaultInstance, enumTypeMap, number, type, isPacked) { + } + + public override object ToReflectionType(object value) { + IList<object> result = new List<object>(); + foreach (object element in (IEnumerable) value) { + result.Add(SingularToReflectionType(element)); + } + return result; + } + + public override object FromReflectionType(object value) { + // Must convert the whole list. + List<TExtensionType> result = new List<TExtensionType>(); + foreach (object element in (IEnumerable)value) { + result.Add((TExtensionType)SingularFromReflectionType(element)); + } + return result; + } + } + + public class GeneratedExtensionLite<TContainingType, TExtensionType> : IGeneratedExtensionLite + where TContainingType : IMessageLite { + + private readonly TContainingType containingTypeDefaultInstance; + private readonly TExtensionType defaultValue; + private readonly IMessageLite messageDefaultInstance; + private readonly ExtensionDescriptorLite descriptor; + + // We can't always initialize a GeneratedExtension when we first construct + // it due to initialization order difficulties (namely, the default + // instances may not have been constructed yet). So, we construct an + // uninitialized GeneratedExtension once, then call internalInit() on it + // later. Generated code will always call internalInit() on all extensions + // as part of the static initialization code, and internalInit() throws an + // exception if called more than once, so this method is useless to users. + protected GeneratedExtensionLite( + TContainingType containingTypeDefaultInstance, + TExtensionType defaultValue, + IMessageLite messageDefaultInstance, + ExtensionDescriptorLite descriptor) { + this.containingTypeDefaultInstance = containingTypeDefaultInstance; + this.messageDefaultInstance = messageDefaultInstance; + this.defaultValue = defaultValue; + this.descriptor = descriptor; + } + + /** For use by generated code only. */ + public GeneratedExtensionLite( + string fullName, + TContainingType containingTypeDefaultInstance, + TExtensionType defaultValue, + IMessageLite messageDefaultInstance, + IEnumLiteMap enumTypeMap, + int number, + FieldType type) + : this(containingTypeDefaultInstance, defaultValue, messageDefaultInstance, + new ExtensionDescriptorLite(fullName, enumTypeMap, number, type, defaultValue, + false /* isRepeated */, false /* isPacked */)) { + } + + private static readonly IList<object> Empty = new object[0]; + /** Repeating fields: For use by generated code only. */ + protected GeneratedExtensionLite( + string fullName, + TContainingType containingTypeDefaultInstance, + TExtensionType defaultValue, + IMessageLite messageDefaultInstance, + IEnumLiteMap enumTypeMap, + int number, + FieldType type, + bool isPacked) + : this(containingTypeDefaultInstance, defaultValue, messageDefaultInstance, + new ExtensionDescriptorLite(fullName, enumTypeMap, number, type, Empty, + true /* isRepeated */, isPacked)) { + } + + /// <summary> + /// Returns information about this extension + /// </summary> + public IFieldDescriptorLite Descriptor { + get { return descriptor; } + } + + /// <summary> + /// Returns the default value for this extension + /// </summary> + public TExtensionType DefaultValue { + get { return defaultValue; } + } + + /// <summary> + /// used for the extension registry + /// </summary> + object IGeneratedExtensionLite.ContainingType { + get { return ContainingTypeDefaultInstance; } + } + /** + * Default instance of the type being extended, used to identify that type. + */ + public TContainingType ContainingTypeDefaultInstance { + get { + return containingTypeDefaultInstance; + } + } + + /** Get the field number. */ + public int Number { + get { + return descriptor.FieldNumber; + } + } + /** + * If the extension is an embedded message, this is the default instance of + * that type. + */ + public IMessageLite MessageDefaultInstance { + get { + return messageDefaultInstance; + } + } + + /// <summary> + /// Converts from the type used by the native accessors to the type + /// used by reflection accessors. For example, the reflection accessors + /// for enums use EnumValueDescriptors but the native accessors use + /// the generated enum type. + /// </summary> + public virtual object ToReflectionType(object value) { + return SingularToReflectionType(value); + } + + /// <summary> + /// Like ToReflectionType(object) but for a single element. + /// </summary> + public object SingularToReflectionType(object value) { + return descriptor.MappedType == MappedType.Enum + ? descriptor.EnumType.FindValueByNumber((int)value) + : value; + } + + public virtual object FromReflectionType(object value) { + return SingularFromReflectionType(value); + } + + public object SingularFromReflectionType(object value) { + switch (Descriptor.MappedType) { + case MappedType.Message: + if (value is TExtensionType) { + return value; + } else { + // It seems the copy of the embedded message stored inside the + // extended message is not of the exact type the user was + // expecting. This can happen if a user defines a + // GeneratedExtension manually and gives it a different type. + // This should not happen in normal use. But, to be nice, we'll + // copy the message to whatever type the caller was expecting. + return MessageDefaultInstance.WeakCreateBuilderForType() + .WeakMergeFrom((IMessageLite)value).WeakBuild(); + } + case MappedType.Enum: + // Just return a boxed int - that can be unboxed to the enum + IEnumLite enumValue = (IEnumLite)value; + return enumValue.Number; + default: + return value; + } + } + } +}
\ No newline at end of file diff --git a/src/ProtocolBuffers/GeneratedMessage.cs b/src/ProtocolBuffers/GeneratedMessage.cs index d60f2b9c..fe1188b6 100644 --- a/src/ProtocolBuffers/GeneratedMessage.cs +++ b/src/ProtocolBuffers/GeneratedMessage.cs @@ -99,13 +99,13 @@ namespace Google.ProtocolBuffers { if (field.IsRepeated) { // We know it's an IList<T>, but not the exact type - so // IEnumerable is the best we can do. (C# generics aren't covariant yet.) - foreach (IMessage element in (IEnumerable) this[field]) { + foreach (IMessageLite element in (IEnumerable) this[field]) { if (!element.IsInitialized) { return false; } } } else { - if (HasField(field) && !((IMessage) this[field]).IsInitialized) { + if (HasField(field) && !((IMessageLite) this[field]).IsInitialized) { return false; } } diff --git a/src/ProtocolBuffers/GeneratedMessageLite.cs b/src/ProtocolBuffers/GeneratedMessageLite.cs new file mode 100644 index 00000000..b2a009c4 --- /dev/null +++ b/src/ProtocolBuffers/GeneratedMessageLite.cs @@ -0,0 +1,132 @@ +#region Copyright notice and license +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://github.com/jskeet/dotnet-protobufs/ +// Original C++/Java/Python code: +// http://code.google.com/p/protobuf/ +// +// 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.Generic; +using System.Collections; +using System.Globalization; +using Google.ProtocolBuffers.Descriptors; + +namespace Google.ProtocolBuffers { + + /// <summary> + /// All generated protocol message classes extend this class. It implements + /// most of the IMessage interface using reflection. Users + /// can ignore this class as an implementation detail. + /// </summary> + public abstract class GeneratedMessageLite<TMessage, TBuilder> : AbstractMessageLite<TMessage, TBuilder> + where TMessage : GeneratedMessageLite<TMessage, TBuilder> + where TBuilder : GeneratedBuilderLite<TMessage, TBuilder> { + + protected abstract TMessage ThisMessage { get; } + + public sealed override string ToString() { + using (System.IO.StringWriter wtr = new System.IO.StringWriter()) { + PrintTo(wtr); + return wtr.ToString(); + } + } + + /// <summary> + /// PrintTo() helper methods for Lite Runtime + /// </summary> + protected static void PrintField<T>(string name, IList<T> value, System.IO.TextWriter writer) { + foreach (T item in value) + PrintField(name, true, (object)item, writer); + } + /// <summary> + /// PrintTo() helper methods for Lite Runtime + /// </summary> + protected static void PrintField(string name, bool hasValue, object value, System.IO.TextWriter writer) { + if (!hasValue) return; + if (value is IMessageLite) { + writer.WriteLine("{0} {{", name); + ((IMessageLite)value).PrintTo(writer); + writer.WriteLine("}"); + } else if (value is ByteString || value is String) { + writer.Write("{0}: \"", name); + if(value is String) + EscapeBytes( System.Text.Encoding.UTF8.GetBytes((string)value), writer); + else + EscapeBytes(((ByteString)value), writer); + writer.WriteLine("\""); + } else if (value is bool) { + writer.WriteLine("{0}: {1}", name, (bool)value ? "true" : "false"); + } else if (value is IEnumLite) { + writer.WriteLine("{0}: {1}", name, ((IEnumLite)value).Name); + } + else { + writer.WriteLine("{0}: {1}", name, ((IConvertible)value).ToString(CultureInfo.InvariantCulture)); + } + } + + /// <summary> + /// COPIED from TextFormat + /// Escapes bytes in the format used in protocol buffer text format, which + /// is the same as the format used for C string literals. All bytes + /// that are not printable 7-bit ASCII characters are escaped, as well as + /// backslash, single-quote, and double-quote characters. Characters for + /// which no defined short-hand escape sequence is defined will be escaped + /// using 3-digit octal sequences. + /// The returned value is guaranteed to be entirely ASCII. + /// </summary> + private static void EscapeBytes(IEnumerable<byte> input, System.IO.TextWriter writer) { + foreach (byte b in input) { + switch (b) { + // C# does not use \a or \v + case 0x07: writer.Write("\\a"); break; + case (byte)'\b': writer.Write("\\b"); break; + case (byte)'\f': writer.Write("\\f"); break; + case (byte)'\n': writer.Write("\\n"); break; + case (byte)'\r': writer.Write("\\r"); break; + case (byte)'\t': writer.Write("\\t"); break; + case 0x0b: writer.Write("\\v"); break; + case (byte)'\\': writer.Write("\\\\"); break; + case (byte)'\'': writer.Write("\\\'"); break; + case (byte)'"': writer.Write("\\\""); break; + default: + if (b >= 0x20 && b < 128) { + writer.Write((char)b); + } else { + writer.Write('\\'); + writer.Write((char)('0' + ((b >> 6) & 3))); + writer.Write((char)('0' + ((b >> 3) & 7))); + writer.Write((char)('0' + (b & 7))); + } + break; + } + } + } + } +} diff --git a/src/ProtocolBuffers/IBuilder.cs b/src/ProtocolBuffers/IBuilder.cs index b1aa4fb1..c4a1c609 100644 --- a/src/ProtocolBuffers/IBuilder.cs +++ b/src/ProtocolBuffers/IBuilder.cs @@ -47,12 +47,12 @@ namespace Google.ProtocolBuffers { /// use explicit interface implemenation for the non-generic form. This mirrors /// how IEnumerable and IEnumerable<T> work. /// </summary> - public interface IBuilder { + public interface IBuilder : IBuilderLite { /// <summary> /// Returns true iff all required fields in the message and all /// embedded messages are set. /// </summary> - bool IsInitialized { get; } + new bool IsInitialized { get; } /// <summary> /// Only present in the nongeneric interface - useful for tests, but @@ -119,17 +119,17 @@ namespace Google.ProtocolBuffers { #region Methods which are like those of the generic form, but without any knowledge of the type parameters IBuilder WeakAddRepeatedField(FieldDescriptor field, object value); - IBuilder WeakClear(); + new IBuilder WeakClear(); IBuilder WeakClearField(FieldDescriptor field); IBuilder WeakMergeFrom(IMessage message); - IBuilder WeakMergeFrom(ByteString data); - IBuilder WeakMergeFrom(ByteString data, ExtensionRegistry registry); - IBuilder WeakMergeFrom(CodedInputStream input); - IBuilder WeakMergeFrom(CodedInputStream input, ExtensionRegistry registry); - IMessage WeakBuild(); - IMessage WeakBuildPartial(); - IBuilder WeakClone(); - IMessage WeakDefaultInstanceForType { get; } + new IBuilder WeakMergeFrom(ByteString data); + new IBuilder WeakMergeFrom(ByteString data, ExtensionRegistry registry); + new IBuilder WeakMergeFrom(CodedInputStream input); + new IBuilder WeakMergeFrom(CodedInputStream input, ExtensionRegistry registry); + new IMessage WeakBuild(); + new IMessage WeakBuildPartial(); + new IBuilder WeakClone(); + new IMessage WeakDefaultInstanceForType { get; } #endregion } @@ -139,7 +139,7 @@ namespace Google.ProtocolBuffers { /// </summary> /// <typeparam name="TMessage">Type of message</typeparam> /// <typeparam name="TBuilder">Type of builder</typeparam> - public interface IBuilder<TMessage, TBuilder> : IBuilder + public interface IBuilder<TMessage, TBuilder> : IBuilder, IBuilderLite<TMessage, TBuilder> where TMessage : IMessage<TMessage, TBuilder> where TBuilder : IBuilder<TMessage, TBuilder> { @@ -148,22 +148,7 @@ namespace Google.ProtocolBuffers { /// <summary> /// Resets all fields to their default values. /// </summary> - TBuilder Clear(); - - /// <summary> - /// Merge the specified other message into the message being - /// built. Merging occurs as follows. For each field: - /// For singular primitive fields, if the field is set in <paramref name="other"/>, - /// then <paramref name="other"/>'s value overwrites the value in this message. - /// For singular message fields, if the field is set in <paramref name="other"/>, - /// it is merged into the corresponding sub-message of this message using the same - /// merging rules. - /// For repeated fields, the elements in <paramref name="other"/> are concatenated - /// with the elements in this message. - /// </summary> - /// <param name="other"></param> - /// <returns></returns> - TBuilder MergeFrom(TMessage other); + new TBuilder Clear(); /// <summary> /// Merge the specified other message which may be a different implementation of @@ -180,19 +165,19 @@ namespace Google.ProtocolBuffers { /// <exception cref="UninitializedMessageException">the message /// is missing one or more required fields; use BuildPartial to bypass /// this check</exception> - TMessage Build(); + new TMessage Build(); /// <summary> /// Like Build(), but does not throw an exception if the message is missing /// required fields. Instead, a partial message is returned. /// </summary> - TMessage BuildPartial(); + new TMessage BuildPartial(); /// <summary> /// Clones this builder. /// TODO(jonskeet): Explain depth of clone. /// </summary> - TBuilder Clone(); + new TBuilder Clone(); /// <summary> /// Parses a message of this type from the input and merges it with this @@ -213,7 +198,7 @@ namespace Google.ProtocolBuffers { /// Use BuildPartial to build, which ignores missing required fields. /// </list> /// </remarks> - TBuilder MergeFrom(CodedInputStream input); + new TBuilder MergeFrom(CodedInputStream input); /// <summary> /// Like MergeFrom(CodedInputStream), but also parses extensions. @@ -221,13 +206,13 @@ namespace Google.ProtocolBuffers { /// in <paramref name="extensionRegistry"/>. Extensions not in the registry /// will be treated as unknown fields. /// </summary> - TBuilder MergeFrom(CodedInputStream input, ExtensionRegistry extensionRegistry); + new TBuilder MergeFrom(CodedInputStream input, ExtensionRegistry extensionRegistry); /// <summary> /// Get's the message's type's default instance. /// <see cref="IMessage{TMessage}.DefaultInstanceForType" /> /// </summary> - TMessage DefaultInstanceForType { get; } + new TMessage DefaultInstanceForType { get; } /// <summary> /// Clears the field. This is exactly equivalent to calling the generated @@ -258,12 +243,12 @@ namespace Google.ProtocolBuffers { /// write messages in this format. /// </summary> /// <param name="input"></param> - TBuilder MergeDelimitedFrom(Stream input); + new TBuilder MergeDelimitedFrom(Stream input); /// <summary> /// Like MergeDelimitedFrom(Stream) but supporting extensions. /// </summary> - TBuilder MergeDelimitedFrom(Stream input, ExtensionRegistry extensionRegistry); + new TBuilder MergeDelimitedFrom(Stream input, ExtensionRegistry extensionRegistry); #region Convenience methods /// <summary> @@ -271,28 +256,28 @@ namespace Google.ProtocolBuffers { /// it with the message being built. This is just a small wrapper around /// MergeFrom(CodedInputStream). /// </summary> - TBuilder MergeFrom(ByteString data); + new TBuilder MergeFrom(ByteString data); /// <summary> /// Parse <paramref name="data"/> as a message of this type and merge /// it with the message being built. This is just a small wrapper around - /// MergeFrom(CodedInputStream, ExtensionRegistry). + /// MergeFrom(CodedInputStream, extensionRegistry). /// </summary> - TBuilder MergeFrom(ByteString data, ExtensionRegistry extensionRegistry); + new TBuilder MergeFrom(ByteString data, ExtensionRegistry extensionRegistry); /// <summary> /// Parse <paramref name="data"/> as a message of this type and merge /// it with the message being built. This is just a small wrapper around /// MergeFrom(CodedInputStream). /// </summary> - TBuilder MergeFrom(byte[] data); + new TBuilder MergeFrom(byte[] data); /// <summary> /// Parse <paramref name="data"/> as a message of this type and merge /// it with the message being built. This is just a small wrapper around - /// MergeFrom(CodedInputStream, ExtensionRegistry). + /// MergeFrom(CodedInputStream, extensionRegistry). /// </summary> - TBuilder MergeFrom(byte[] data, ExtensionRegistry extensionRegistry); + new TBuilder MergeFrom(byte[] data, ExtensionRegistry extensionRegistry); /// <summary> /// Parse <paramref name="input"/> as a message of this type and merge @@ -304,14 +289,14 @@ namespace Google.ProtocolBuffers { /// to write your message and MmergeDelimitedFrom(Stream) to read it. /// Despite usually reading the entire stream, this method never closes the stream. /// </summary> - TBuilder MergeFrom(Stream input); + new TBuilder MergeFrom(Stream input); /// <summary> /// Parse <paramref name="input"/> as a message of this type and merge /// it with the message being built. This is just a small wrapper around - /// MergeFrom(CodedInputStream, ExtensionRegistry). + /// MergeFrom(CodedInputStream, extensionRegistry). /// </summary> - TBuilder MergeFrom(Stream input, ExtensionRegistry extensionRegistry); + new TBuilder MergeFrom(Stream input, ExtensionRegistry extensionRegistry); #endregion } } diff --git a/src/ProtocolBuffers/IBuilderLite.cs b/src/ProtocolBuffers/IBuilderLite.cs new file mode 100644 index 00000000..e91d44bc --- /dev/null +++ b/src/ProtocolBuffers/IBuilderLite.cs @@ -0,0 +1,210 @@ +#region Copyright notice and license +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://github.com/jskeet/dotnet-protobufs/ +// Original C++/Java/Python code: +// http://code.google.com/p/protobuf/ +// +// 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.Generic; +using System.IO; + +namespace Google.ProtocolBuffers { + + /// <summary> + /// Non-generic interface for all members whose signatures don't require knowledge of + /// the type being built. The generic interface extends this one. Some methods return + /// either an IBuilder or an IMessage; in these cases the generic interface redeclares + /// the same method with a type-specific signature. Implementations are encouraged to + /// use explicit interface implemenation for the non-generic form. This mirrors + /// how IEnumerable and IEnumerable<T> work. + /// </summary> + public interface IBuilderLite { + /// <summary> + /// Returns true iff all required fields in the message and all + /// embedded messages are set. + /// </summary> + bool IsInitialized { get; } + + IBuilderLite WeakClear(); + IBuilderLite WeakMergeFrom(IMessageLite message); + IBuilderLite WeakMergeFrom(ByteString data); + IBuilderLite WeakMergeFrom(ByteString data, ExtensionRegistry registry); + IBuilderLite WeakMergeFrom(CodedInputStream input); + IBuilderLite WeakMergeFrom(CodedInputStream input, ExtensionRegistry registry); + IMessageLite WeakBuild(); + IMessageLite WeakBuildPartial(); + IBuilderLite WeakClone(); + IMessageLite WeakDefaultInstanceForType { get; } + } + + /// <summary> + /// Interface implemented by Protocol Message builders. + /// TODO(jonskeet): Consider "SetXXX" methods returning the builder, as well as the properties. + /// </summary> + /// <typeparam name="TMessage">Type of message</typeparam> + /// <typeparam name="TBuilder">Type of builder</typeparam> + public interface IBuilderLite<TMessage, TBuilder> : IBuilderLite + where TMessage : IMessageLite<TMessage, TBuilder> + where TBuilder : IBuilderLite<TMessage, TBuilder> { + + /// <summary> + /// Resets all fields to their default values. + /// </summary> + TBuilder Clear(); + + /// <summary> + /// Merge the specified other message which may be a different implementation of + /// the same message descriptor. + /// </summary> + TBuilder MergeFrom(IMessageLite other); + + /// <summary> + /// Constructs the final message. Once this is called, this Builder instance + /// is no longer valid, and calling any other method may throw a + /// NullReferenceException. If you need to continue working with the builder + /// after calling Build, call Clone first. + /// </summary> + /// <exception cref="UninitializedMessageException">the message + /// is missing one or more required fields; use BuildPartial to bypass + /// this check</exception> + TMessage Build(); + + /// <summary> + /// Like Build(), but does not throw an exception if the message is missing + /// required fields. Instead, a partial message is returned. + /// </summary> + TMessage BuildPartial(); + + /// <summary> + /// Clones this builder. + /// TODO(jonskeet): Explain depth of clone. + /// </summary> + TBuilder Clone(); + + /// <summary> + /// Parses a message of this type from the input and merges it with this + /// message, as if using MergeFrom(IMessage<T>). + /// </summary> + /// <remarks> + /// Warning: This does not verify that all required fields are present + /// in the input message. If you call Build() without setting all + /// required fields, it will throw an UninitializedMessageException. + /// There are a few good ways to deal with this: + /// <list> + /// <item>Call IsInitialized to verify to verify that all required fields are + /// set before building.</item> + /// <item>Parse the message separately using one of the static ParseFrom + /// methods, then use MergeFrom(IMessage<T>) to merge it with + /// this one. ParseFrom will throw an InvalidProtocolBufferException + /// (an IOException) if some required fields are missing. + /// Use BuildPartial to build, which ignores missing required fields. + /// </list> + /// </remarks> + TBuilder MergeFrom(CodedInputStream input); + + /// <summary> + /// Like MergeFrom(CodedInputStream), but also parses extensions. + /// The extensions that you want to be able to parse must be registered + /// in <paramref name="extensionRegistry"/>. Extensions not in the registry + /// will be treated as unknown fields. + /// </summary> + TBuilder MergeFrom(CodedInputStream input, ExtensionRegistry extensionRegistry); + + /// <summary> + /// Get's the message's type's default instance. + /// <see cref="IMessageLite{TMessage}.DefaultInstanceForType" /> + /// </summary> + TMessage DefaultInstanceForType { get; } + + /// <summary> + /// Like MergeFrom(Stream), but does not read until the end of the file. + /// Instead, the size of the message (encoded as a varint) is read first, + /// then the message data. Use Message.WriteDelimitedTo(Stream) to + /// write messages in this format. + /// </summary> + /// <param name="input"></param> + TBuilder MergeDelimitedFrom(Stream input); + + /// <summary> + /// Like MergeDelimitedFrom(Stream) but supporting extensions. + /// </summary> + TBuilder MergeDelimitedFrom(Stream input, ExtensionRegistry extensionRegistry); + + #region Convenience methods + /// <summary> + /// Parse <paramref name="data"/> as a message of this type and merge + /// it with the message being built. This is just a small wrapper around + /// MergeFrom(CodedInputStream). + /// </summary> + TBuilder MergeFrom(ByteString data); + + /// <summary> + /// Parse <paramref name="data"/> as a message of this type and merge + /// it with the message being built. This is just a small wrapper around + /// MergeFrom(CodedInputStream, ExtensionRegistry). + /// </summary> + TBuilder MergeFrom(ByteString data, ExtensionRegistry extensionRegistry); + + /// <summary> + /// Parse <paramref name="data"/> as a message of this type and merge + /// it with the message being built. This is just a small wrapper around + /// MergeFrom(CodedInputStream). + /// </summary> + TBuilder MergeFrom(byte[] data); + + /// <summary> + /// Parse <paramref name="data"/> as a message of this type and merge + /// it with the message being built. This is just a small wrapper around + /// MergeFrom(CodedInputStream, ExtensionRegistry). + /// </summary> + TBuilder MergeFrom(byte[] data, ExtensionRegistry extensionRegistry); + + /// <summary> + /// Parse <paramref name="input"/> as a message of this type and merge + /// it with the message being built. This is just a small wrapper around + /// MergeFrom(CodedInputStream). Note that this method always reads + /// the entire input (unless it throws an exception). If you want it to + /// stop earlier, you will need to wrap the input in a wrapper + /// stream which limits reading. Or, use IMessage.WriteDelimitedTo(Stream) + /// to write your message and MmergeDelimitedFrom(Stream) to read it. + /// Despite usually reading the entire stream, this method never closes the stream. + /// </summary> + TBuilder MergeFrom(Stream input); + + /// <summary> + /// Parse <paramref name="input"/> as a message of this type and merge + /// it with the message being built. This is just a small wrapper around + /// MergeFrom(CodedInputStream, ExtensionRegistry). + /// </summary> + TBuilder MergeFrom(Stream input, ExtensionRegistry extensionRegistry); + #endregion + } +} diff --git a/src/ProtocolBuffers/IMessage.cs b/src/ProtocolBuffers/IMessage.cs index ec955c6d..4d37a8d2 100644 --- a/src/ProtocolBuffers/IMessage.cs +++ b/src/ProtocolBuffers/IMessage.cs @@ -43,7 +43,7 @@ namespace Google.ProtocolBuffers { /// Non-generic interface used for all parts of the API which don't require /// any type knowledge. /// </summary> - public interface IMessage { + public interface IMessage : IMessageLite { /// <summary> /// Returns the message's type's descriptor. This differs from the /// Descriptor property of each generated message class in that this @@ -110,7 +110,7 @@ namespace Google.ProtocolBuffers { /// Returns true iff all required fields in the message and all embedded /// messages are set. /// </summary> - bool IsInitialized { get; } + new bool IsInitialized { get; } /// <summary> /// Serializes the message and writes it to the given output stream. @@ -124,7 +124,7 @@ namespace Google.ProtocolBuffers { /// of the message before the data, then making sure you limit the input to /// that size when receiving the data. Alternatively, use WriteDelimitedTo(Stream). /// </remarks> - void WriteTo(CodedOutputStream output); + new void WriteTo(CodedOutputStream output); /// <summary> /// Like WriteTo(Stream) but writes the size of the message as a varint before @@ -134,13 +134,13 @@ namespace Google.ProtocolBuffers { /// YourMessageType.ParseDelimitedFrom(Stream) to parse messages written by this method. /// </summary> /// <param name="output"></param> - void WriteDelimitedTo(Stream output); + new void WriteDelimitedTo(Stream output); /// <summary> /// Returns the number of bytes required to encode this message. /// The result is only computed on the first call and memoized after that. /// </summary> - int SerializedSize { get; } + new int SerializedSize { get; } #region Comparison and hashing /// <summary> @@ -149,13 +149,13 @@ namespace Google.ProtocolBuffers { /// (as defined by DescriptorForType) and has identical values /// for all its fields. /// </summary> - bool Equals(object other); + new bool Equals(object other); /// <summary> /// Returns the hash code value for this message. /// TODO(jonskeet): Specify the hash algorithm, but better than the Java one! /// </summary> - int GetHashCode(); + new int GetHashCode(); #endregion #region Convenience methods @@ -163,19 +163,19 @@ namespace Google.ProtocolBuffers { /// Converts the message to a string in protocol buffer text format. /// This is just a trivial wrapper around TextFormat.PrintToString. /// </summary> - string ToString(); + new string ToString(); /// <summary> /// Serializes the message to a ByteString. This is a trivial wrapper /// around WriteTo(CodedOutputStream). /// </summary> - ByteString ToByteString(); + new ByteString ToByteString(); /// <summary> /// Serializes the message to a byte array. This is a trivial wrapper /// around WriteTo(CodedOutputStream). /// </summary> - byte[] ToByteArray(); + new byte[] ToByteArray(); /// <summary> /// Serializes the message and writes it to the given stream. @@ -183,7 +183,7 @@ namespace Google.ProtocolBuffers { /// does not flush or close the stream. /// </summary> /// <param name="output"></param> - void WriteTo(Stream output); + new void WriteTo(Stream output); #endregion /// <summary> @@ -191,19 +191,19 @@ namespace Google.ProtocolBuffers { /// is typically implemented by strongly typed messages by just returning /// the result of CreateBuilderForType. /// </summary> - IBuilder WeakCreateBuilderForType(); + new IBuilder WeakCreateBuilderForType(); /// <summary> /// Creates a builder with the same contents as this message. This /// is typically implemented by strongly typed messages by just returning /// the result of ToBuilder. /// </summary> - IBuilder WeakToBuilder(); + new IBuilder WeakToBuilder(); - IMessage WeakDefaultInstanceForType { get; } + new IMessage WeakDefaultInstanceForType { get; } } - public interface IMessage<TMessage> : IMessage { + public interface IMessage<TMessage> : IMessage, IMessageLite<TMessage> { /// <summary> /// Returns an instance of this message type with all fields set to /// their default values. This may or may not be a singleton. This differs @@ -211,26 +211,26 @@ namespace Google.ProtocolBuffers { /// method is an abstract method of IMessage whereas DefaultInstance is /// a static property of a specific class. They return the same thing. /// </summary> - TMessage DefaultInstanceForType { get; } + new TMessage DefaultInstanceForType { get; } } /// <summary> /// Type-safe interface for all generated messages to implement. /// </summary> - public interface IMessage<TMessage, TBuilder> : IMessage<TMessage> + public interface IMessage<TMessage, TBuilder> : IMessage<TMessage>, IMessageLite<TMessage, TBuilder> where TMessage : IMessage<TMessage, TBuilder> where TBuilder : IBuilder<TMessage, TBuilder> { #region Builders /// <summary> /// Constructs a new builder for a message of the same type as this message. /// </summary> - TBuilder CreateBuilderForType(); + new TBuilder CreateBuilderForType(); /// <summary> /// Creates a builder with the same contents as this current instance. /// This is typically implemented by strongly typed messages by just /// returning the result of ToBuilder(). /// </summary> - TBuilder ToBuilder(); + new TBuilder ToBuilder(); #endregion } } diff --git a/src/ProtocolBuffers/IMessageLite.cs b/src/ProtocolBuffers/IMessageLite.cs new file mode 100644 index 00000000..48660882 --- /dev/null +++ b/src/ProtocolBuffers/IMessageLite.cs @@ -0,0 +1,179 @@ +#region Copyright notice and license +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://github.com/jskeet/dotnet-protobufs/ +// Original C++/Java/Python code: +// http://code.google.com/p/protobuf/ +// +// 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.Generic; +using System.IO; + +namespace Google.ProtocolBuffers { + + /// <summary> + /// Non-generic interface used for all parts of the API which don't require + /// any type knowledge. + /// </summary> + public interface IMessageLite { + + /// <summary> + /// Returns true iff all required fields in the message and all embedded + /// messages are set. + /// </summary> + bool IsInitialized { get; } + + /// <summary> + /// Serializes the message and writes it to the given output stream. + /// This does not flush or close the stream. + /// </summary> + /// <remarks> + /// Protocol Buffers are not self-delimiting. Therefore, if you write + /// any more data to the stream after the message, you must somehow ensure + /// that the parser on the receiving end does not interpret this as being + /// part of the protocol message. One way of doing this is by writing the size + /// of the message before the data, then making sure you limit the input to + /// that size when receiving the data. Alternatively, use WriteDelimitedTo(Stream). + /// </remarks> + void WriteTo(CodedOutputStream output); + + /// <summary> + /// Like WriteTo(Stream) but writes the size of the message as a varint before + /// writing the data. This allows more data to be written to the stream after the + /// message without the need to delimit the message data yourself. Use + /// IBuilder.MergeDelimitedFrom(Stream) or the static method + /// YourMessageType.ParseDelimitedFrom(Stream) to parse messages written by this method. + /// </summary> + /// <param name="output"></param> + void WriteDelimitedTo(Stream output); + + /// <summary> + /// Returns the number of bytes required to encode this message. + /// The result is only computed on the first call and memoized after that. + /// </summary> + int SerializedSize { get; } + + #region Comparison and hashing + /// <summary> + /// Compares the specified object with this message for equality. + /// Returns true iff the given object is a message of the same type + /// (as defined by DescriptorForType) and has identical values + /// for all its fields. + /// </summary> + bool Equals(object other); + + /// <summary> + /// Returns the hash code value for this message. + /// TODO(jonskeet): Specify the hash algorithm, but better than the Java one! + /// </summary> + int GetHashCode(); + #endregion + + #region Convenience methods + /// <summary> + /// Converts the message to a string in protocol buffer text format. + /// This is just a trivial wrapper around TextFormat.PrintToString. + /// </summary> + string ToString(); + + /// <summary> + /// Converts the message to a string. + /// </summary> + void PrintTo(TextWriter writer); + + /// <summary> + /// Serializes the message to a ByteString. This is a trivial wrapper + /// around WriteTo(CodedOutputStream). + /// </summary> + ByteString ToByteString(); + + /// <summary> + /// Serializes the message to a byte array. This is a trivial wrapper + /// around WriteTo(CodedOutputStream). + /// </summary> + byte[] ToByteArray(); + + /// <summary> + /// Serializes the message and writes it to the given stream. + /// This is just a wrapper around WriteTo(CodedOutputStream). This + /// does not flush or close the stream. + /// </summary> + /// <param name="output"></param> + void WriteTo(Stream output); + #endregion + + /// <summary> + /// Creates a builder for the type, but in a weakly typed manner. This + /// is typically implemented by strongly typed messages by just returning + /// the result of CreateBuilderForType. + /// </summary> + IBuilderLite WeakCreateBuilderForType(); + + /// <summary> + /// Creates a builder with the same contents as this message. This + /// is typically implemented by strongly typed messages by just returning + /// the result of ToBuilder. + /// </summary> + IBuilderLite WeakToBuilder(); + + IMessageLite WeakDefaultInstanceForType { get; } + } + + public interface IMessageLite<TMessage> : IMessageLite { + /// <summary> + /// Returns an instance of this message type with all fields set to + /// their default values. This may or may not be a singleton. This differs + /// from the DefaultInstance property of each generated message class in that this + /// method is an abstract method of IMessage whereas DefaultInstance is + /// a static property of a specific class. They return the same thing. + /// </summary> + TMessage DefaultInstanceForType { get; } + } + + /// <summary> + /// Type-safe interface for all generated messages to implement. + /// </summary> + public interface IMessageLite<TMessage, TBuilder> : IMessageLite<TMessage> + where TMessage : IMessageLite<TMessage, TBuilder> + where TBuilder : IBuilderLite<TMessage, TBuilder> { + #region Builders + /// <summary> + /// Constructs a new builder for a message of the same type as this message. + /// </summary> + TBuilder CreateBuilderForType(); + /// <summary> + /// Creates a builder with the same contents as this current instance. + /// This is typically implemented by strongly typed messages by just + /// returning the result of ToBuilder(). + /// </summary> + TBuilder ToBuilder(); + #endregion + } +} diff --git a/src/ProtocolBuffers/Properties/AssemblyInfo.cs b/src/ProtocolBuffers/Properties/AssemblyInfo.cs index a1047370..e86a2f15 100644 --- a/src/ProtocolBuffers/Properties/AssemblyInfo.cs +++ b/src/ProtocolBuffers/Properties/AssemblyInfo.cs @@ -74,6 +74,12 @@ using System.Runtime.CompilerServices; "72f738140072bb69990bc4f98a21365de2c105e848974a3d210e938b0a56103c0662901efd6b78"+ "0ee6dbe977923d46a8fda18fb25c65dd73b149a5cd9f3100668b56649932dadd8cf5be52eb1dce"+ "ad5cedbf")] +[assembly: InternalsVisibleTo("Google.ProtocolBuffersLite.Test,PublicKey=" + +"00240000048000009400000006020000002400005253413100040000010001008179f2dd31a648" + +"2a2359dbe33e53701167a888e7c369a9ae3210b64f93861d8a7d286447e58bc167e3d99483beda" + +"72f738140072bb69990bc4f98a21365de2c105e848974a3d210e938b0a56103c0662901efd6b78" + +"0ee6dbe977923d46a8fda18fb25c65dd73b149a5cd9f3100668b56649932dadd8cf5be52eb1dce" + +"ad5cedbf")] [assembly: InternalsVisibleTo("ProtoGen,PublicKey=" + "00240000048000009400000006020000002400005253413100040000010001006d739020e13bdc" + "038e86fa8aa5e1b13aae65d3ae79d622816c6067ab5b6955be50cc887130117582349208c13a55" + diff --git a/src/ProtocolBuffers/ProtocolBuffers.csproj b/src/ProtocolBuffers/ProtocolBuffers.csproj index c8a0445e..a5759668 100644 --- a/src/ProtocolBuffers/ProtocolBuffers.csproj +++ b/src/ProtocolBuffers/ProtocolBuffers.csproj @@ -80,10 +80,18 @@ <ItemGroup> <Reference Include="mscorlib" /> <Reference Include="System" /> + <Reference Include="System.Data" /> + <Reference Include="System.Xml" /> </ItemGroup> <ItemGroup> <Compile Include="AbstractBuilder.cs" /> + <Compile Include="AbstractBuilderLite.cs"> + <SubType>Code</SubType> + </Compile> <Compile Include="AbstractMessage.cs" /> + <Compile Include="AbstractMessageLite.cs"> + <SubType>Code</SubType> + </Compile> <Compile Include="ByteString.cs" /> <Compile Include="Collections\Enumerables.cs" /> <Compile Include="Collections\IPopsicleList.cs" /> @@ -116,10 +124,16 @@ <Compile Include="Descriptors\PackageDescriptor.cs" /> <Compile Include="Descriptors\ServiceDescriptor.cs" /> <Compile Include="DynamicMessage.cs" /> + <Compile Include="EnumLite.cs" /> <Compile Include="ExtendableBuilder.cs" /> + <Compile Include="ExtendableBuilderLite.cs" /> <Compile Include="ExtendableMessage.cs" /> - <Compile Include="ExtensionInfo.cs" /> + <Compile Include="ExtendableMessageLite.cs" /> + <Compile Include="ExtensionInfo.cs"> + <SubType>Code</SubType> + </Compile> <Compile Include="ExtensionRegistry.cs" /> + <Compile Include="ExtensionRegistryLite.cs" /> <Compile Include="FieldAccess\ReflectionUtil.cs" /> <Compile Include="FieldAccess\SingleEnumAccessor.cs" /> <Compile Include="FieldAccess\SingleMessageAccessor.cs" /> @@ -131,12 +145,19 @@ <Compile Include="FieldAccess\RepeatedMessageAccessor.cs" /> <Compile Include="FieldSet.cs" /> <Compile Include="GeneratedBuilder.cs" /> + <Compile Include="GeneratedBuilderLite.cs" /> + <Compile Include="GeneratedExtensionLite.cs" /> + <Compile Include="GeneratedMessageLite.cs" /> <Compile Include="GeneratedRepeatExtension.cs" /> <Compile Include="GeneratedSingleExtension.cs" /> <Compile Include="GeneratedMessage.cs" /> <Compile Include="IBuilder.cs" /> <Compile Include="GeneratedExtensionBase.cs" /> + <Compile Include="IBuilderLite.cs"> + <SubType>Code</SubType> + </Compile> <Compile Include="IMessage.cs" /> + <Compile Include="IMessageLite.cs" /> <Compile Include="InvalidProtocolBufferException.cs" /> <Compile Include="IRpcChannel.cs" /> <Compile Include="IRpcController.cs" /> diff --git a/src/ProtocolBuffers/ProtocolBuffersLite.csproj b/src/ProtocolBuffers/ProtocolBuffersLite.csproj new file mode 100644 index 00000000..d4413d83 --- /dev/null +++ b/src/ProtocolBuffers/ProtocolBuffersLite.csproj @@ -0,0 +1,115 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <ProductVersion>9.0.30729</ProductVersion> + <SchemaVersion>2.0</SchemaVersion> + <ProjectGuid>{6969BDCE-D925-43F3-94AC-A531E6DF2591}</ProjectGuid> + <OutputType>Library</OutputType> + <AppDesignerFolder>Properties</AppDesignerFolder> + <RootNamespace>Google.ProtocolBuffers</RootNamespace> + <AssemblyName>Google.ProtocolBuffersLite</AssemblyName> + <TargetFrameworkVersion>v2.0</TargetFrameworkVersion> + <FileAlignment>512</FileAlignment> + <SignAssembly>true</SignAssembly> + <AssemblyOriginatorKeyFile>Properties\Google.ProtocolBuffers.snk</AssemblyOriginatorKeyFile> + <FileUpgradeFlags> + </FileUpgradeFlags> + <OldToolsVersion>3.5</OldToolsVersion> + <UpgradeBackupLocation /> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> + <DebugSymbols>true</DebugSymbols> + <DebugType>full</DebugType> + <Optimize>false</Optimize> + <OutputPath>bin\Debug\</OutputPath> + <DefineConstants>TRACE;DEBUG;LITE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <NoStdLib>true</NoStdLib> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> + <DebugType>pdbonly</DebugType> + <Optimize>true</Optimize> + <OutputPath>bin\Release\</OutputPath> + <DefineConstants>TRACE;LITE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <NoStdLib>true</NoStdLib> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug_Silverlight2|AnyCPU'"> + <DebugSymbols>true</DebugSymbols> + <DebugType>full</DebugType> + <Optimize>false</Optimize> + <OutputPath>bin\Debug_Silverlight2\</OutputPath> + <DefineConstants>DEBUG;TRACE;SILVERLIGHT2;LITE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <NoStdLib>true</NoStdLib> + <CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release_Silverlight2|AnyCPU'"> + <DebugType>pdbonly</DebugType> + <Optimize>true</Optimize> + <OutputPath>bin\Release_Silverlight2\</OutputPath> + <DefineConstants>TRACE;SILVERLIGHT2;LITE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <NoStdLib>true</NoStdLib> + <CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet> + </PropertyGroup> + <ItemGroup> + <Reference Include="mscorlib" /> + <Reference Include="System" /> + <Reference Include="System.Data" /> + <Reference Include="System.Xml" /> + </ItemGroup> + <ItemGroup> + </ItemGroup> + <ItemGroup> + <None Include="Properties\Google.ProtocolBuffers.snk" /> + </ItemGroup> + <ItemGroup> + <Compile Include="AbstractBuilderLite.cs" /> + <Compile Include="AbstractMessageLite.cs" /> + <Compile Include="Collections\Dictionaries.cs" /> + <Compile Include="Collections\Enumerables.cs" /> + <Compile Include="Collections\IPopsicleList.cs" /> + <Compile Include="Collections\Lists.cs" /> + <Compile Include="Collections\PopsicleList.cs" /> + <Compile Include="Collections\ReadOnlyDictionary.cs" /> + <Compile Include="Descriptors\FieldMappingAttribute.cs" /> + <Compile Include="Descriptors\FieldType.cs" /> + <Compile Include="Descriptors\MappedType.cs" /> + <Compile Include="EnumLite.cs" /> + <Compile Include="ExtendableBuilderLite.cs" /> + <Compile Include="ExtendableMessageLite.cs" /> + <Compile Include="FieldSet.cs" /> + <Compile Include="GeneratedBuilderLite.cs" /> + <Compile Include="GeneratedExtensionLite.cs" /> + <Compile Include="GeneratedMessageLite.cs" /> + <Compile Include="Properties\AssemblyInfo.cs" /> + <Compile Include="ByteString.cs" /> + <Compile Include="CodedInputStream.cs" /> + <Compile Include="CodedOutputStream.cs" /> + <Compile Include="ExtensionRegistryLite.cs" /> + <Compile Include="IBuilderLite.cs" /> + <Compile Include="IMessageLite.cs"> + <SubType>Code</SubType> + </Compile> + <Compile Include="InvalidProtocolBufferException.cs" /> + <Compile Include="ThrowHelper.cs" /> + <Compile Include="UninitializedMessageException.cs" /> + <Compile Include="WireFormat.cs" /> + </ItemGroup> + <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" Condition=" '$(Configuration)' != 'Silverlight2' " /> + <Import Project="$(MSBuildExtensionsPath)\Microsoft\Silverlight\v2.0\Microsoft.Silverlight.CSharp.targets" Condition=" '$(Configuration)' == 'Silverlight2' " /> + <!-- To modify your build process, add your task inside one of the targets below and uncomment it. + Other similar extension points exist, see Microsoft.Common.targets. + <Target Name="BeforeBuild"> + </Target> + <Target Name="AfterBuild"> + </Target> + --> +</Project>
\ No newline at end of file diff --git a/src/ProtocolBuffers/SortedList.cs b/src/ProtocolBuffers/SortedList.cs index e9f4458e..14f14e30 100644 --- a/src/ProtocolBuffers/SortedList.cs +++ b/src/ProtocolBuffers/SortedList.cs @@ -50,6 +50,18 @@ namespace Google.ProtocolBuffers { private readonly IDictionary<TKey, TValue> wrapped = new Dictionary<TKey, TValue>(); + public SortedList() + { + } + + public SortedList(IDictionary<TKey, TValue> dictionary) + { + foreach (KeyValuePair<TKey, TValue> entry in dictionary) + { + Add(entry.Key, entry.Value); + } + } + public void Add(TKey key, TValue value) { wrapped.Add(key, value); diff --git a/src/ProtocolBuffers/TextFormat.cs b/src/ProtocolBuffers/TextFormat.cs index 5f07e693..81b3447c 100644 --- a/src/ProtocolBuffers/TextFormat.cs +++ b/src/ProtocolBuffers/TextFormat.cs @@ -170,13 +170,19 @@ namespace Google.ProtocolBuffers { } case FieldType.Enum: { - generator.Print(((EnumValueDescriptor) value).Name); + if (value is IEnumLite && !(value is EnumValueDescriptor)) { + throw new NotSupportedException("Lite enumerations are not supported."); + } + generator.Print(((EnumValueDescriptor)value).Name); break; } case FieldType.Message: case FieldType.Group: - Print((IMessage) value, generator); + if (value is IMessageLite && !(value is IMessage)) { + throw new NotSupportedException("Lite messages are not supported."); + } + Print((IMessage)value, generator); break; } } @@ -576,7 +582,9 @@ namespace Google.ProtocolBuffers { if (extension == null) { subBuilder = builder.CreateBuilderForField(field); } else { - subBuilder = extension.DefaultInstance.WeakCreateBuilderForType(); + subBuilder = extension.DefaultInstance.WeakCreateBuilderForType() as IBuilder; + if (subBuilder == null) + throw new NotSupportedException("Lite messages are not supported."); } while (!tokenizer.TryConsume(endToken)) { diff --git a/src/ProtocolBuffers/UninitializedMessageException.cs b/src/ProtocolBuffers/UninitializedMessageException.cs index 2fa5ef1d..d7f58197 100644 --- a/src/ProtocolBuffers/UninitializedMessageException.cs +++ b/src/ProtocolBuffers/UninitializedMessageException.cs @@ -36,8 +36,10 @@ using System; using System.Collections; using System.Collections.Generic; using System.Text; +#if !LITE using Google.ProtocolBuffers.Collections; using Google.ProtocolBuffers.Descriptors; +#endif namespace Google.ProtocolBuffers { /// <summary> @@ -47,16 +49,10 @@ namespace Google.ProtocolBuffers { private readonly IList<string> missingFields; - public UninitializedMessageException(IMessage message) - : this(FindMissingFields(message)) { - } - private UninitializedMessageException(IList<string> missingFields) : base(BuildDescription(missingFields)) { - this.missingFields = Lists.AsReadOnly(missingFields); + this.missingFields = new List<string>(missingFields); } - - /// <summary> /// Returns a read-only list of human-readable names of /// required fields missing from this message. Each name @@ -93,6 +89,19 @@ namespace Google.ProtocolBuffers { } /// <summary> + /// For Lite exceptions that do not known how to enumerate missing fields + /// </summary> + public UninitializedMessageException(IMessageLite message) + : base(String.Format("Message {0} is missing required fields", message.GetType())) { + missingFields = new List<string>(); + } + +#if !LITE + public UninitializedMessageException(IMessage message) + : this(FindMissingFields(message)) { + } + + /// <summary> /// Returns a list of the full "paths" of missing required /// fields in the specified message. /// </summary> @@ -120,11 +129,19 @@ namespace Google.ProtocolBuffers { if (field.IsRepeated) { int i = 0; foreach (object element in (IEnumerable) value) { - FindMissingFields((IMessage) element, SubMessagePrefix(prefix, field, i++), results); + if (element is IMessage) { + FindMissingFields((IMessage)element, SubMessagePrefix(prefix, field, i++), results); + } else { + results.Add(prefix + field.Name); + } } } else { if (message.HasField(field)) { - FindMissingFields((IMessage) value, SubMessagePrefix(prefix, field, -1), results); + if (value is IMessage) { + FindMissingFields((IMessage)value, SubMessagePrefix(prefix, field, -1), results); + } else { + results.Add(prefix + field.Name); + } } } } @@ -148,5 +165,6 @@ namespace Google.ProtocolBuffers { result.Append('.'); return result.ToString(); } +#endif } } diff --git a/src/ProtocolBuffers/UnknownField.cs b/src/ProtocolBuffers/UnknownField.cs index fdc00650..ad1b59b4 100644 --- a/src/ProtocolBuffers/UnknownField.cs +++ b/src/ProtocolBuffers/UnknownField.cs @@ -172,7 +172,9 @@ namespace Google.ProtocolBuffers { output.WriteBytes(fieldNumber, value); } foreach (UnknownFieldSet value in groupList) { +#pragma warning disable 0612 output.WriteUnknownGroup(fieldNumber, value); +#pragma warning restore 0612 } } @@ -195,7 +197,9 @@ namespace Google.ProtocolBuffers { result += CodedOutputStream.ComputeBytesSize(fieldNumber, value); } foreach (UnknownFieldSet value in groupList) { +#pragma warning disable 0612 result += CodedOutputStream.ComputeUnknownGroupSize(fieldNumber, value); +#pragma warning restore 0612 } return result; } diff --git a/src/ProtocolBuffers/UnknownFieldSet.cs b/src/ProtocolBuffers/UnknownFieldSet.cs index 49c1fa32..594ae8bd 100644 --- a/src/ProtocolBuffers/UnknownFieldSet.cs +++ b/src/ProtocolBuffers/UnknownFieldSet.cs @@ -51,7 +51,7 @@ namespace Google.ProtocolBuffers { /// /// Most users will never need to use this class directly. /// </summary> - public sealed class UnknownFieldSet { + public sealed class UnknownFieldSet : IMessageLite { private static readonly UnknownFieldSet defaultInstance = new UnknownFieldSet(new Dictionary<int, UnknownField>()); @@ -139,6 +139,14 @@ namespace Google.ProtocolBuffers { } /// <summary> + /// Converts the set to a string in protocol buffer text format. This + /// is just a trivial wrapper around TextFormat.PrintToString. + /// </summary> + public void PrintTo(TextWriter writer) { + TextFormat.Print(this, writer); + } + + /// <summary> /// Serializes the message to a ByteString and returns it. This is /// just a trivial wrapper around WriteTo(CodedOutputStream). /// </summary> @@ -237,16 +245,43 @@ namespace Google.ProtocolBuffers { return CreateBuilder().MergeFrom(input).Build(); } + #region IMessageLite Members + + public bool IsInitialized { + get { return fields != null; } + } + + public void WriteDelimitedTo(Stream output) { + CodedOutputStream codedOutput = CodedOutputStream.CreateInstance(output); + codedOutput.WriteRawVarint32((uint) SerializedSize); + WriteTo(codedOutput); + codedOutput.Flush(); + } + + public IBuilderLite WeakCreateBuilderForType() { + return new Builder(); + } + + public IBuilderLite WeakToBuilder() { + return new Builder(fields); + } + + public IMessageLite WeakDefaultInstanceForType { + get { return defaultInstance; } + } + + #endregion + /// <summary> /// Builder for UnknownFieldSets. /// </summary> - public sealed class Builder + public sealed class Builder : IBuilderLite { /// <summary> /// Mapping from number to field. Note that by using a SortedList we ensure /// that the fields will be serialized in ascending order. /// </summary> - private IDictionary<int, UnknownField> fields = new SortedList<int, UnknownField>(); + private IDictionary<int, UnknownField> fields; // Optimization: We keep around a builder for the last field that was // modified so that we can efficiently add to it multiple times in a // row (important when parsing an unknown repeated field). @@ -254,6 +289,11 @@ namespace Google.ProtocolBuffers { private UnknownField.Builder lastField; internal Builder() { + fields = new SortedList<int, UnknownField>(); + } + + internal Builder(IDictionary<int, UnknownField> dictionary) { + fields = new SortedList<int, UnknownField>(dictionary); } /// <summary> @@ -356,7 +396,9 @@ namespace Google.ProtocolBuffers { return true; case WireFormat.WireType.StartGroup: { Builder subBuilder = CreateBuilder(); +#pragma warning disable 0612 input.ReadUnknownGroup(number, subBuilder); +#pragma warning restore 0612 GetFieldBuilder(number).AddGroup(subBuilder.Build()); return true; } @@ -469,6 +511,7 @@ namespace Google.ProtocolBuffers { if (tag == 0) { break; } + if (!MergeFieldFrom(input, extensionRegistry, builder, tag)) { // end group tag break; @@ -485,7 +528,7 @@ namespace Google.ProtocolBuffers { /// <param name="builder">Builder to merge field into, if it's a known field</param> /// <param name="tag">The tag, which should already have been read from the input</param> /// <returns>true unless the tag is an end-group tag</returns> - internal bool MergeFieldFrom(CodedInputStream input, + internal bool MergeFieldFrom(CodedInputStream input, ExtensionRegistry extensionRegistry, IBuilder builder, uint tag) { MessageDescriptor type = builder.DescriptorForType; @@ -498,7 +541,7 @@ namespace Google.ProtocolBuffers { int fieldNumber = WireFormat.GetTagFieldNumber(tag); FieldDescriptor field; - IMessage defaultFieldInstance = null; + IMessageLite defaultFieldInstance = null; if (type.IsExtensionNumber(fieldNumber)) { ExtensionInfo extension = extensionRegistry[type, fieldNumber]; @@ -543,14 +586,14 @@ namespace Google.ProtocolBuffers { switch (field.FieldType) { case FieldType.Group: case FieldType.Message: { - IBuilder subBuilder; + IBuilderLite subBuilder; if (defaultFieldInstance != null) { subBuilder = defaultFieldInstance.WeakCreateBuilderForType(); } else { subBuilder = builder.CreateBuilderForField(field); } if (!field.IsRepeated) { - subBuilder.WeakMergeFrom((IMessage)builder[field]); + subBuilder.WeakMergeFrom((IMessageLite)builder[field]); } if (field.FieldType == FieldType.Group) { input.ReadGroup(field.FieldNumber, subBuilder, extensionRegistry); @@ -609,7 +652,7 @@ namespace Google.ProtocolBuffers { int typeId = 0; ByteString rawBytes = null; // If we encounter "message" before "typeId" - IBuilder subBuilder = null; + IBuilderLite subBuilder = null; FieldDescriptor field = null; while (true) { @@ -626,7 +669,7 @@ namespace Google.ProtocolBuffers { if (extension != null) { field = extension.Descriptor; subBuilder = extension.DefaultInstance.WeakCreateBuilderForType(); - IMessage originalMessage = (IMessage)builder[field]; + IMessageLite originalMessage = (IMessageLite)builder[field]; if (originalMessage != null) { subBuilder.WeakMergeFrom(originalMessage); } @@ -672,6 +715,54 @@ namespace Google.ProtocolBuffers { builder[field] = subBuilder.WeakBuild(); } } + + #region IBuilderLite Members + + bool IBuilderLite.IsInitialized { + get { return fields != null; } + } + + IBuilderLite IBuilderLite.WeakClear() { + return Clear(); + } + + IBuilderLite IBuilderLite.WeakMergeFrom(IMessageLite message) { + return MergeFrom((UnknownFieldSet)message); + } + + IBuilderLite IBuilderLite.WeakMergeFrom(ByteString data) { + return MergeFrom(data); + } + + IBuilderLite IBuilderLite.WeakMergeFrom(ByteString data, ExtensionRegistry registry) { + return MergeFrom(data); + } + + IBuilderLite IBuilderLite.WeakMergeFrom(CodedInputStream input) { + return MergeFrom(input); + } + + IBuilderLite IBuilderLite.WeakMergeFrom(CodedInputStream input, ExtensionRegistry registry) { + return MergeFrom(input); + } + + IMessageLite IBuilderLite.WeakBuild() { + return Build(); + } + + IMessageLite IBuilderLite.WeakBuildPartial() { + return Build(); + } + + IBuilderLite IBuilderLite.WeakClone() { + return Build().WeakToBuilder(); + } + + IMessageLite IBuilderLite.WeakDefaultInstanceForType { + get { return DefaultInstance; } + } + + #endregion } } } diff --git a/src/ProtocolBuffers/WireFormat.cs b/src/ProtocolBuffers/WireFormat.cs index 22346460..508f1b48 100644 --- a/src/ProtocolBuffers/WireFormat.cs +++ b/src/ProtocolBuffers/WireFormat.cs @@ -33,7 +33,9 @@ #endregion using System; +#if !LITE using Google.ProtocolBuffers.Descriptors; +#endif namespace Google.ProtocolBuffers { @@ -115,6 +117,7 @@ namespace Google.ProtocolBuffers { return (uint) (fieldNumber << TagTypeBits) | (uint) wireType; } +#if !LITE [CLSCompliant(false)] public static uint MakeTag(FieldDescriptor field) { return MakeTag(field.FieldNumber, GetWireType(field)); @@ -170,5 +173,6 @@ namespace Google.ProtocolBuffers { throw new ArgumentOutOfRangeException("No such field type"); } } +#endif } } |