diff options
Diffstat (limited to 'src/ProtocolBuffers')
-rw-r--r-- | src/ProtocolBuffers/RpcUtil.cs | 142 | ||||
-rw-r--r-- | src/ProtocolBuffers/SilverlightCompatibility.cs | 104 | ||||
-rw-r--r-- | src/ProtocolBuffers/SortedList.cs | 342 | ||||
-rw-r--r-- | src/ProtocolBuffers/TextFormat.cs | 1366 | ||||
-rw-r--r-- | src/ProtocolBuffers/TextGenerator.cs | 286 | ||||
-rw-r--r-- | src/ProtocolBuffers/TextTokenizer.cs | 824 | ||||
-rw-r--r-- | src/ProtocolBuffers/ThrowHelper.cs | 146 | ||||
-rw-r--r-- | src/ProtocolBuffers/UninitializedMessageException.cs | 340 | ||||
-rw-r--r-- | src/ProtocolBuffers/UnknownField.cs | 742 | ||||
-rw-r--r-- | src/ProtocolBuffers/UnknownFieldSet.cs | 1536 | ||||
-rw-r--r-- | src/ProtocolBuffers/WireFormat.cs | 342 |
11 files changed, 3085 insertions, 3085 deletions
diff --git a/src/ProtocolBuffers/RpcUtil.cs b/src/ProtocolBuffers/RpcUtil.cs index 48d389d0..d0614a37 100644 --- a/src/ProtocolBuffers/RpcUtil.cs +++ b/src/ProtocolBuffers/RpcUtil.cs @@ -1,71 +1,71 @@ -#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; - -namespace Google.ProtocolBuffers { - /// <summary> - /// Grab-bag of utility functions useful when dealing with RPCs. - /// </summary> - public static class RpcUtil { - - /// <summary> - /// Converts an Action[IMessage] to an Action[T]. - /// </summary> - public static Action<T> SpecializeCallback<T>(Action<IMessage> action) - where T : IMessage<T> { - return message => action(message); - } - - /// <summary> - /// Converts an Action[T] to an Action[IMessage]. - /// The generalized action will accept any message object which has - /// the same descriptor, and will convert it to the correct class - /// before calling the original action. However, if the generalized - /// callback is given a message with a different descriptor, an - /// exception will be thrown. - /// </summary> - public static Action<IMessage> GeneralizeCallback<TMessage, TBuilder>(Action<TMessage> action, TMessage defaultInstance) - where TMessage : class, IMessage<TMessage, TBuilder> - where TBuilder : IBuilder<TMessage, TBuilder> { - return message => { - TMessage castMessage = message as TMessage; - if (castMessage == null) { - castMessage = defaultInstance.CreateBuilderForType().MergeFrom(message).Build(); - } - action(castMessage); - }; - } - } -} +#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;
+
+namespace Google.ProtocolBuffers {
+ /// <summary>
+ /// Grab-bag of utility functions useful when dealing with RPCs.
+ /// </summary>
+ public static class RpcUtil {
+
+ /// <summary>
+ /// Converts an Action[IMessage] to an Action[T].
+ /// </summary>
+ public static Action<T> SpecializeCallback<T>(Action<IMessage> action)
+ where T : IMessage<T> {
+ return message => action(message);
+ }
+
+ /// <summary>
+ /// Converts an Action[T] to an Action[IMessage].
+ /// The generalized action will accept any message object which has
+ /// the same descriptor, and will convert it to the correct class
+ /// before calling the original action. However, if the generalized
+ /// callback is given a message with a different descriptor, an
+ /// exception will be thrown.
+ /// </summary>
+ public static Action<IMessage> GeneralizeCallback<TMessage, TBuilder>(Action<TMessage> action, TMessage defaultInstance)
+ where TMessage : class, IMessage<TMessage, TBuilder>
+ where TBuilder : IBuilder<TMessage, TBuilder> {
+ return message => {
+ TMessage castMessage = message as TMessage;
+ if (castMessage == null) {
+ castMessage = defaultInstance.CreateBuilderForType().MergeFrom(message).Build();
+ }
+ action(castMessage);
+ };
+ }
+ }
+}
diff --git a/src/ProtocolBuffers/SilverlightCompatibility.cs b/src/ProtocolBuffers/SilverlightCompatibility.cs index 37b45828..5f2e12b1 100644 --- a/src/ProtocolBuffers/SilverlightCompatibility.cs +++ b/src/ProtocolBuffers/SilverlightCompatibility.cs @@ -1,52 +1,52 @@ -#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.Text.RegularExpressions; - -namespace Google.ProtocolBuffers -{ - /// <summary> - /// Class containing helpful workarounds for Silverlight compatibility - /// </summary> - internal static class SilverlightCompatibility - { - -#if SILVERLIGHT2 - internal const RegexOptions CompiledRegexWhereAvailable = RegexOptions.None; -#else - internal const RegexOptions CompiledRegexWhereAvailable = RegexOptions.Compiled; -#endif - - } -} +#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.Text.RegularExpressions;
+
+namespace Google.ProtocolBuffers
+{
+ /// <summary>
+ /// Class containing helpful workarounds for Silverlight compatibility
+ /// </summary>
+ internal static class SilverlightCompatibility
+ {
+
+#if SILVERLIGHT2
+ internal const RegexOptions CompiledRegexWhereAvailable = RegexOptions.None;
+#else
+ internal const RegexOptions CompiledRegexWhereAvailable = RegexOptions.Compiled;
+#endif
+
+ }
+}
diff --git a/src/ProtocolBuffers/SortedList.cs b/src/ProtocolBuffers/SortedList.cs index 14f14e30..da186ee5 100644 --- a/src/ProtocolBuffers/SortedList.cs +++ b/src/ProtocolBuffers/SortedList.cs @@ -1,172 +1,172 @@ -#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 - -#if SILVERLIGHT2 -using System.Collections; -using System.Collections.Generic; - -namespace Google.ProtocolBuffers -{ - /// <summary> - /// Dictionary implementation which always yields keys in sorted order. - /// This is not particularly efficient: it wraps a normal dictionary - /// for most operations, but sorts by key when either iterating or - /// fetching the Keys/Values properties. - /// This is only used for Silverlight, which doesn't have the normal - /// sorted collections. - /// </summary> - internal sealed class SortedList<TKey, TValue> : IDictionary<TKey, TValue> - { - 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); - } - - public bool ContainsKey(TKey key) - { - return wrapped.ContainsKey(key); - } - - public ICollection<TKey> Keys - { - get - { - List<TKey> keys = new List<TKey>(wrapped.Count); - foreach (var pair in this) - { - keys.Add(pair.Key); - } - return keys; - } - } - - public bool Remove(TKey key) - { - return wrapped.Remove(key); - } - - public bool TryGetValue(TKey key, out TValue value) - { - return wrapped.TryGetValue(key, out value); - } - - public ICollection<TValue> Values - { - get - { - List<TValue> values = new List<TValue>(wrapped.Count); - foreach (var pair in this) - { - values.Add(pair.Value); - } - return values; - } - } - - public TValue this[TKey key] - { - get - { - return wrapped[key]; - } - set - { - wrapped[key] = value; - } - } - - public void Add(KeyValuePair<TKey, TValue> item) - { - wrapped.Add(item); - } - - public void Clear() - { - wrapped.Clear(); - } - - public bool Contains(KeyValuePair<TKey, TValue> item) - { - return wrapped.Contains(item); - } - - public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) - { - wrapped.CopyTo(array, arrayIndex); - } - - public int Count - { - get { return wrapped.Count; } - } - - public bool IsReadOnly - { - get { return wrapped.IsReadOnly; } - } - - public bool Remove(KeyValuePair<TKey, TValue> item) - { - return wrapped.Remove(item); - } - - public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() - { - IComparer<TKey> comparer = Comparer<TKey>.Default; - var list = new List<KeyValuePair<TKey, TValue>>(wrapped); - list.Sort((x, y) => comparer.Compare(x.Key, y.Key)); - return list.GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - } -} +#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
+
+#if SILVERLIGHT2
+using System.Collections;
+using System.Collections.Generic;
+
+namespace Google.ProtocolBuffers
+{
+ /// <summary>
+ /// Dictionary implementation which always yields keys in sorted order.
+ /// This is not particularly efficient: it wraps a normal dictionary
+ /// for most operations, but sorts by key when either iterating or
+ /// fetching the Keys/Values properties.
+ /// This is only used for Silverlight, which doesn't have the normal
+ /// sorted collections.
+ /// </summary>
+ internal sealed class SortedList<TKey, TValue> : IDictionary<TKey, TValue>
+ {
+ 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);
+ }
+
+ public bool ContainsKey(TKey key)
+ {
+ return wrapped.ContainsKey(key);
+ }
+
+ public ICollection<TKey> Keys
+ {
+ get
+ {
+ List<TKey> keys = new List<TKey>(wrapped.Count);
+ foreach (var pair in this)
+ {
+ keys.Add(pair.Key);
+ }
+ return keys;
+ }
+ }
+
+ public bool Remove(TKey key)
+ {
+ return wrapped.Remove(key);
+ }
+
+ public bool TryGetValue(TKey key, out TValue value)
+ {
+ return wrapped.TryGetValue(key, out value);
+ }
+
+ public ICollection<TValue> Values
+ {
+ get
+ {
+ List<TValue> values = new List<TValue>(wrapped.Count);
+ foreach (var pair in this)
+ {
+ values.Add(pair.Value);
+ }
+ return values;
+ }
+ }
+
+ public TValue this[TKey key]
+ {
+ get
+ {
+ return wrapped[key];
+ }
+ set
+ {
+ wrapped[key] = value;
+ }
+ }
+
+ public void Add(KeyValuePair<TKey, TValue> item)
+ {
+ wrapped.Add(item);
+ }
+
+ public void Clear()
+ {
+ wrapped.Clear();
+ }
+
+ public bool Contains(KeyValuePair<TKey, TValue> item)
+ {
+ return wrapped.Contains(item);
+ }
+
+ public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
+ {
+ wrapped.CopyTo(array, arrayIndex);
+ }
+
+ public int Count
+ {
+ get { return wrapped.Count; }
+ }
+
+ public bool IsReadOnly
+ {
+ get { return wrapped.IsReadOnly; }
+ }
+
+ public bool Remove(KeyValuePair<TKey, TValue> item)
+ {
+ return wrapped.Remove(item);
+ }
+
+ public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
+ {
+ IComparer<TKey> comparer = Comparer<TKey>.Default;
+ var list = new List<KeyValuePair<TKey, TValue>>(wrapped);
+ list.Sort((x, y) => comparer.Compare(x.Key, y.Key));
+ return list.GetEnumerator();
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+ }
+}
#endif
\ No newline at end of file diff --git a/src/ProtocolBuffers/TextFormat.cs b/src/ProtocolBuffers/TextFormat.cs index 885152db..cf352273 100644 --- a/src/ProtocolBuffers/TextFormat.cs +++ b/src/ProtocolBuffers/TextFormat.cs @@ -1,685 +1,685 @@ -#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.IO; -using System.Text; -using Google.ProtocolBuffers.Descriptors; -using System.Collections; - -namespace Google.ProtocolBuffers { - /// <summary> - /// Provides ASCII text formatting support for messages. - /// TODO(jonskeet): Support for alternative line endings. - /// (Easy to print, via TextGenerator. Not sure about parsing.) - /// </summary> - public static class TextFormat { - - /// <summary> - /// Outputs a textual representation of the Protocol Message supplied into - /// the parameter output. - /// </summary> - public static void Print(IMessage message, TextWriter output) { - TextGenerator generator = new TextGenerator(output, "\n"); - Print(message, generator); - } - - /// <summary> - /// Outputs a textual representation of <paramref name="fields" /> to <paramref name="output"/>. - /// </summary> - public static void Print(UnknownFieldSet fields, TextWriter output) { - TextGenerator generator = new TextGenerator(output, "\n"); - PrintUnknownFields(fields, generator); - } - - public static string PrintToString(IMessage message) { - StringWriter text = new StringWriter(); - Print(message, text); - return text.ToString(); - } - - public static string PrintToString(UnknownFieldSet fields) { - StringWriter text = new StringWriter(); - Print(fields, text); - return text.ToString(); - } - - private static void Print(IMessage message, TextGenerator generator) { - foreach (KeyValuePair<FieldDescriptor, object> entry in message.AllFields) { - PrintField(entry.Key, entry.Value, generator); - } - PrintUnknownFields(message.UnknownFields, generator); - } - - internal static void PrintField(FieldDescriptor field, object value, TextGenerator generator) { - if (field.IsRepeated) { - // Repeated field. Print each element. - foreach (object element in (IEnumerable) value) { - PrintSingleField(field, element, generator); - } - } else { - PrintSingleField(field, value, generator); - } - } - - private static void PrintSingleField(FieldDescriptor field, Object value, TextGenerator generator) { - if (field.IsExtension) { - generator.Print("["); - // We special-case MessageSet elements for compatibility with proto1. - if (field.ContainingType.Options.MessageSetWireFormat - && field.FieldType == FieldType.Message - && field.IsOptional - // object equality (TODO(jonskeet): Work out what this comment means!) - && field.ExtensionScope == field.MessageType) { - generator.Print(field.MessageType.FullName); - } else { - generator.Print(field.FullName); - } - generator.Print("]"); - } else { - if (field.FieldType == FieldType.Group) { - // Groups must be serialized with their original capitalization. - generator.Print(field.MessageType.Name); - } else { - generator.Print(field.Name); - } - } - - if (field.MappedType == MappedType.Message) { - generator.Print(" {\n"); - generator.Indent(); - } else { - generator.Print(": "); - } - - PrintFieldValue(field, value, generator); - - if (field.MappedType == MappedType.Message) { - generator.Outdent(); - generator.Print("}"); - } - generator.Print("\n"); - } - - private static void PrintFieldValue(FieldDescriptor field, object value, TextGenerator generator) { - switch (field.FieldType) { - case FieldType.Int32: - case FieldType.Int64: - case FieldType.SInt32: - case FieldType.SInt64: - case FieldType.SFixed32: - case FieldType.SFixed64: - case FieldType.Float: - case FieldType.Double: - case FieldType.UInt32: - case FieldType.UInt64: - case FieldType.Fixed32: - case FieldType.Fixed64: - // The simple Object.ToString converts using the current culture. - // We want to always use the invariant culture so it's predictable. - generator.Print(((IConvertible) value).ToString(CultureInfo.InvariantCulture)); - break; - case FieldType.Bool: - // Explicitly use the Java true/false - generator.Print((bool) value ? "true" : "false"); - break; - - case FieldType.String: - generator.Print("\""); - generator.Print(EscapeText((string) value)); - generator.Print("\""); - break; - - case FieldType.Bytes: { - generator.Print("\""); - generator.Print(EscapeBytes((ByteString) value)); - generator.Print("\""); - break; - } - - case FieldType.Enum: { - 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: - if (value is IMessageLite && !(value is IMessage)) { - throw new NotSupportedException("Lite messages are not supported."); - } - Print((IMessage)value, generator); - break; - } - } - - private static void PrintUnknownFields(UnknownFieldSet unknownFields, TextGenerator generator) { - foreach (KeyValuePair<int, UnknownField> entry in unknownFields.FieldDictionary) { - String prefix = entry.Key.ToString() + ": "; - UnknownField field = entry.Value; - - foreach (ulong value in field.VarintList) { - generator.Print(prefix); - generator.Print(value.ToString()); - generator.Print("\n"); - } - foreach (uint value in field.Fixed32List) { - generator.Print(prefix); - generator.Print(string.Format("0x{0:x8}", value)); - generator.Print("\n"); - } - foreach (ulong value in field.Fixed64List) { - generator.Print(prefix); - generator.Print(string.Format("0x{0:x16}", value)); - generator.Print("\n"); - } - foreach (ByteString value in field.LengthDelimitedList) { - generator.Print(entry.Key.ToString()); - generator.Print(": \""); - generator.Print(EscapeBytes(value)); - generator.Print("\"\n"); - } - foreach (UnknownFieldSet value in field.GroupList) { - generator.Print(entry.Key.ToString()); - generator.Print(" {\n"); - generator.Indent(); - PrintUnknownFields(value, generator); - generator.Outdent(); - generator.Print("}\n"); - } - } - } - - [CLSCompliant(false)] - public static ulong ParseUInt64(string text) { - return (ulong) ParseInteger(text, false, true); - } - - public static long ParseInt64(string text) { - return ParseInteger(text, true, true); +#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.IO;
+using System.Text;
+using Google.ProtocolBuffers.Descriptors;
+using System.Collections;
+
+namespace Google.ProtocolBuffers {
+ /// <summary>
+ /// Provides ASCII text formatting support for messages.
+ /// TODO(jonskeet): Support for alternative line endings.
+ /// (Easy to print, via TextGenerator. Not sure about parsing.)
+ /// </summary>
+ public static class TextFormat {
+
+ /// <summary>
+ /// Outputs a textual representation of the Protocol Message supplied into
+ /// the parameter output.
+ /// </summary>
+ public static void Print(IMessage message, TextWriter output) {
+ TextGenerator generator = new TextGenerator(output, "\n");
+ Print(message, generator);
+ }
+
+ /// <summary>
+ /// Outputs a textual representation of <paramref name="fields" /> to <paramref name="output"/>.
+ /// </summary>
+ public static void Print(UnknownFieldSet fields, TextWriter output) {
+ TextGenerator generator = new TextGenerator(output, "\n");
+ PrintUnknownFields(fields, generator);
+ }
+
+ public static string PrintToString(IMessage message) {
+ StringWriter text = new StringWriter();
+ Print(message, text);
+ return text.ToString();
+ }
+
+ public static string PrintToString(UnknownFieldSet fields) {
+ StringWriter text = new StringWriter();
+ Print(fields, text);
+ return text.ToString();
+ }
+
+ private static void Print(IMessage message, TextGenerator generator) {
+ foreach (KeyValuePair<FieldDescriptor, object> entry in message.AllFields) {
+ PrintField(entry.Key, entry.Value, generator);
+ }
+ PrintUnknownFields(message.UnknownFields, generator);
+ }
+
+ internal static void PrintField(FieldDescriptor field, object value, TextGenerator generator) {
+ if (field.IsRepeated) {
+ // Repeated field. Print each element.
+ foreach (object element in (IEnumerable) value) {
+ PrintSingleField(field, element, generator);
+ }
+ } else {
+ PrintSingleField(field, value, generator);
+ }
+ }
+
+ private static void PrintSingleField(FieldDescriptor field, Object value, TextGenerator generator) {
+ if (field.IsExtension) {
+ generator.Print("[");
+ // We special-case MessageSet elements for compatibility with proto1.
+ if (field.ContainingType.Options.MessageSetWireFormat
+ && field.FieldType == FieldType.Message
+ && field.IsOptional
+ // object equality (TODO(jonskeet): Work out what this comment means!)
+ && field.ExtensionScope == field.MessageType) {
+ generator.Print(field.MessageType.FullName);
+ } else {
+ generator.Print(field.FullName);
+ }
+ generator.Print("]");
+ } else {
+ if (field.FieldType == FieldType.Group) {
+ // Groups must be serialized with their original capitalization.
+ generator.Print(field.MessageType.Name);
+ } else {
+ generator.Print(field.Name);
+ }
+ }
+
+ if (field.MappedType == MappedType.Message) {
+ generator.Print(" {\n");
+ generator.Indent();
+ } else {
+ generator.Print(": ");
+ }
+
+ PrintFieldValue(field, value, generator);
+
+ if (field.MappedType == MappedType.Message) {
+ generator.Outdent();
+ generator.Print("}");
+ }
+ generator.Print("\n");
+ }
+
+ private static void PrintFieldValue(FieldDescriptor field, object value, TextGenerator generator) {
+ switch (field.FieldType) {
+ case FieldType.Int32:
+ case FieldType.Int64:
+ case FieldType.SInt32:
+ case FieldType.SInt64:
+ case FieldType.SFixed32:
+ case FieldType.SFixed64:
+ case FieldType.Float:
+ case FieldType.Double:
+ case FieldType.UInt32:
+ case FieldType.UInt64:
+ case FieldType.Fixed32:
+ case FieldType.Fixed64:
+ // The simple Object.ToString converts using the current culture.
+ // We want to always use the invariant culture so it's predictable.
+ generator.Print(((IConvertible) value).ToString(CultureInfo.InvariantCulture));
+ break;
+ case FieldType.Bool:
+ // Explicitly use the Java true/false
+ generator.Print((bool) value ? "true" : "false");
+ break;
+
+ case FieldType.String:
+ generator.Print("\"");
+ generator.Print(EscapeText((string) value));
+ generator.Print("\"");
+ break;
+
+ case FieldType.Bytes: {
+ generator.Print("\"");
+ generator.Print(EscapeBytes((ByteString) value));
+ generator.Print("\"");
+ break;
+ }
+
+ case FieldType.Enum: {
+ 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:
+ if (value is IMessageLite && !(value is IMessage)) {
+ throw new NotSupportedException("Lite messages are not supported.");
+ }
+ Print((IMessage)value, generator);
+ break;
+ }
+ }
+
+ private static void PrintUnknownFields(UnknownFieldSet unknownFields, TextGenerator generator) {
+ foreach (KeyValuePair<int, UnknownField> entry in unknownFields.FieldDictionary) {
+ String prefix = entry.Key.ToString() + ": ";
+ UnknownField field = entry.Value;
+
+ foreach (ulong value in field.VarintList) {
+ generator.Print(prefix);
+ generator.Print(value.ToString());
+ generator.Print("\n");
+ }
+ foreach (uint value in field.Fixed32List) {
+ generator.Print(prefix);
+ generator.Print(string.Format("0x{0:x8}", value));
+ generator.Print("\n");
+ }
+ foreach (ulong value in field.Fixed64List) {
+ generator.Print(prefix);
+ generator.Print(string.Format("0x{0:x16}", value));
+ generator.Print("\n");
+ }
+ foreach (ByteString value in field.LengthDelimitedList) {
+ generator.Print(entry.Key.ToString());
+ generator.Print(": \"");
+ generator.Print(EscapeBytes(value));
+ generator.Print("\"\n");
+ }
+ foreach (UnknownFieldSet value in field.GroupList) {
+ generator.Print(entry.Key.ToString());
+ generator.Print(" {\n");
+ generator.Indent();
+ PrintUnknownFields(value, generator);
+ generator.Outdent();
+ generator.Print("}\n");
+ }
+ }
+ }
+
+ [CLSCompliant(false)]
+ public static ulong ParseUInt64(string text) {
+ return (ulong) ParseInteger(text, false, true);
+ }
+
+ public static long ParseInt64(string text) {
+ return ParseInteger(text, true, true);
+ }
+
+ [CLSCompliant(false)]
+ public static uint ParseUInt32(string text) {
+ return (uint) ParseInteger(text, false, false);
+ }
+
+ public static int ParseInt32(string text) {
+ return (int) ParseInteger(text, true, false);
+ }
+
+ public static float ParseFloat(string text) {
+ switch (text) {
+ case "-inf":
+ case "-infinity":
+ case "-inff":
+ case "-infinityf":
+ return float.NegativeInfinity;
+ case "inf":
+ case "infinity":
+ case "inff":
+ case "infinityf":
+ return float.PositiveInfinity;
+ case "nan":
+ case "nanf":
+ return float.NaN;
+ default:
+ return float.Parse(text, CultureInfo.InvariantCulture);
+ }
+ }
+
+ public static double ParseDouble(string text) {
+ switch (text) {
+ case "-inf":
+ case "-infinity":
+ return double.NegativeInfinity;
+ case "inf":
+ case "infinity":
+ return double.PositiveInfinity;
+ case "nan":
+ return double.NaN;
+ default:
+ return double.Parse(text, CultureInfo.InvariantCulture);
+ }
+ }
+
+ /// <summary>
+ /// Parses an integer in hex (leading 0x), decimal (no prefix) or octal (leading 0).
+ /// Only a negative sign is permitted, and it must come before the radix indicator.
+ /// </summary>
+ private static long ParseInteger(string text, bool isSigned, bool isLong) {
+ string original = text;
+ bool negative = false;
+ if (text.StartsWith("-")) {
+ if (!isSigned) {
+ throw new FormatException("Number must be positive: " + original);
+ }
+ negative = true;
+ text = text.Substring(1);
+ }
+
+ int radix = 10;
+ if (text.StartsWith("0x")) {
+ radix = 16;
+ text = text.Substring(2);
+ } else if (text.StartsWith("0")) {
+ radix = 8;
+ }
+
+ ulong result;
+ try {
+ // Workaround for https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=278448
+ // We should be able to use Convert.ToUInt64 for all cases.
+ result = radix == 10 ? ulong.Parse(text) : Convert.ToUInt64(text, radix);
+ } catch (OverflowException) {
+ // Convert OverflowException to FormatException so there's a single exception type this method can throw.
+ string numberDescription = string.Format("{0}-bit {1}signed integer", isLong ? 64 : 32, isSigned ? "" : "un");
+ throw new FormatException("Number out of range for " + numberDescription + ": " + original);
+ }
+
+ if (negative) {
+ ulong max = isLong ? 0x8000000000000000UL : 0x80000000L;
+ if (result > max) {
+ string numberDescription = string.Format("{0}-bit signed integer", isLong ? 64 : 32);
+ throw new FormatException("Number out of range for " + numberDescription + ": " + original);
+ }
+ return -((long) result);
+ } else {
+ ulong max = isSigned
+ ? (isLong ? (ulong) long.MaxValue : int.MaxValue)
+ : (isLong ? ulong.MaxValue : uint.MaxValue);
+ if (result > max) {
+ string numberDescription = string.Format("{0}-bit {1}signed integer", isLong ? 64 : 32, isSigned ? "" : "un");
+ throw new FormatException("Number out of range for " + numberDescription + ": " + original);
+ }
+ return (long) result;
+ }
+ }
+
+ /// <summary>
+ /// Tests a character to see if it's an octal digit.
+ /// </summary>
+ private static bool IsOctal(char c) {
+ return '0' <= c && c <= '7';
+ }
+
+ /// <summary>
+ /// Tests a character to see if it's a hex digit.
+ /// </summary>
+ private static bool IsHex(char c) {
+ return ('0' <= c && c <= '9') ||
+ ('a' <= c && c <= 'f') ||
+ ('A' <= c && c <= 'F');
+ }
+
+ /// <summary>
+ /// Interprets a character as a digit (in any base up to 36) and returns the
+ /// numeric value.
+ /// </summary>
+ private static int ParseDigit(char c) {
+ if ('0' <= c && c <= '9') {
+ return c - '0';
+ } else if ('a' <= c && c <= 'z') {
+ return c - 'a' + 10;
+ } else {
+ return c - 'A' + 10;
+ }
+ }
+
+ /// <summary>
+ /// Unescapes a text string as escaped using <see cref="EscapeText(string)" />.
+ /// Two-digit hex escapes (starting with "\x" are also recognised.
+ /// </summary>
+ public static string UnescapeText(string input) {
+ return UnescapeBytes(input).ToStringUtf8();
+ }
+
+ /// <summary>
+ /// Like <see cref="EscapeBytes" /> but escapes a text string.
+ /// The string is first encoded as UTF-8, then each byte escaped individually.
+ /// The returned value is guaranteed to be entirely ASCII.
+ /// </summary>
+ public static string EscapeText(string input) {
+ return EscapeBytes(ByteString.CopyFromUtf8(input));
+ }
+
+ /// <summary>
+ /// 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>
+ public static String EscapeBytes(ByteString input) {
+ StringBuilder builder = new StringBuilder(input.Length);
+ foreach (byte b in input) {
+ switch (b) {
+ // C# does not use \a or \v
+ case 0x07: builder.Append("\\a" ); break;
+ case (byte)'\b': builder.Append("\\b" ); break;
+ case (byte)'\f': builder.Append("\\f" ); break;
+ case (byte)'\n': builder.Append("\\n" ); break;
+ case (byte)'\r': builder.Append("\\r" ); break;
+ case (byte)'\t': builder.Append("\\t" ); break;
+ case 0x0b: builder.Append("\\v" ); break;
+ case (byte)'\\': builder.Append("\\\\"); break;
+ case (byte)'\'': builder.Append("\\\'"); break;
+ case (byte)'"' : builder.Append("\\\""); break;
+ default:
+ if (b >= 0x20 && b < 128) {
+ builder.Append((char) b);
+ } else {
+ builder.Append('\\');
+ builder.Append((char) ('0' + ((b >> 6) & 3)));
+ builder.Append((char) ('0' + ((b >> 3) & 7)));
+ builder.Append((char) ('0' + (b & 7)));
+ }
+ break;
+ }
+ }
+ return builder.ToString();
+ }
+
+ /// <summary>
+ /// Performs string unescaping from C style (octal, hex, form feeds, tab etc) into a byte string.
+ /// </summary>
+ public static ByteString UnescapeBytes(string input) {
+ byte[] result = new byte[input.Length];
+ int pos = 0;
+ for (int i = 0; i < input.Length; i++) {
+ char c = input[i];
+ if (c > 127 || c < 32) {
+ throw new FormatException("Escaped string must only contain ASCII");
+ }
+ if (c != '\\') {
+ result[pos++] = (byte) c;
+ continue;
+ }
+ if (i + 1 >= input.Length) {
+ throw new FormatException("Invalid escape sequence: '\\' at end of string.");
+ }
+
+ i++;
+ c = input[i];
+ if (c >= '0' && c <= '7') {
+ // Octal escape.
+ int code = ParseDigit(c);
+ if (i + 1 < input.Length && IsOctal(input[i+1])) {
+ i++;
+ code = code * 8 + ParseDigit(input[i]);
+ }
+ if (i + 1 < input.Length && IsOctal(input[i+1])) {
+ i++;
+ code = code * 8 + ParseDigit(input[i]);
+ }
+ result[pos++] = (byte) code;
+ } else {
+ switch (c) {
+ case 'a': result[pos++] = 0x07; break;
+ case 'b': result[pos++] = (byte) '\b'; break;
+ case 'f': result[pos++] = (byte) '\f'; break;
+ case 'n': result[pos++] = (byte) '\n'; break;
+ case 'r': result[pos++] = (byte) '\r'; break;
+ case 't': result[pos++] = (byte) '\t'; break;
+ case 'v': result[pos++] = 0x0b; break;
+ case '\\': result[pos++] = (byte) '\\'; break;
+ case '\'': result[pos++] = (byte) '\''; break;
+ case '"': result[pos++] = (byte) '\"'; break;
+
+ case 'x':
+ // hex escape
+ int code;
+ if (i + 1 < input.Length && IsHex(input[i+1])) {
+ i++;
+ code = ParseDigit(input[i]);
+ } else {
+ throw new FormatException("Invalid escape sequence: '\\x' with no digits");
+ }
+ if (i + 1 < input.Length && IsHex(input[i+1])) {
+ ++i;
+ code = code * 16 + ParseDigit(input[i]);
+ }
+ result[pos++] = (byte)code;
+ break;
+
+ default:
+ throw new FormatException("Invalid escape sequence: '\\" + c + "'");
+ }
+ }
+ }
+
+ return ByteString.CopyFrom(result, 0, pos);
+ }
+
+ public static void Merge(string text, IBuilder builder) {
+ Merge(text, ExtensionRegistry.Empty, builder);
}
- [CLSCompliant(false)] - public static uint ParseUInt32(string text) { - return (uint) ParseInteger(text, false, false); - } - - public static int ParseInt32(string text) { - return (int) ParseInteger(text, true, false); - } - - public static float ParseFloat(string text) { - switch (text) { - case "-inf": - case "-infinity": - case "-inff": - case "-infinityf": - return float.NegativeInfinity; - case "inf": - case "infinity": - case "inff": - case "infinityf": - return float.PositiveInfinity; - case "nan": - case "nanf": - return float.NaN; - default: - return float.Parse(text, CultureInfo.InvariantCulture); - } - } - - public static double ParseDouble(string text) { - switch (text) { - case "-inf": - case "-infinity": - return double.NegativeInfinity; - case "inf": - case "infinity": - return double.PositiveInfinity; - case "nan": - return double.NaN; - default: - return double.Parse(text, CultureInfo.InvariantCulture); - } - } - - /// <summary> - /// Parses an integer in hex (leading 0x), decimal (no prefix) or octal (leading 0). - /// Only a negative sign is permitted, and it must come before the radix indicator. - /// </summary> - private static long ParseInteger(string text, bool isSigned, bool isLong) { - string original = text; - bool negative = false; - if (text.StartsWith("-")) { - if (!isSigned) { - throw new FormatException("Number must be positive: " + original); - } - negative = true; - text = text.Substring(1); - } - - int radix = 10; - if (text.StartsWith("0x")) { - radix = 16; - text = text.Substring(2); - } else if (text.StartsWith("0")) { - radix = 8; - } - - ulong result; - try { - // Workaround for https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=278448 - // We should be able to use Convert.ToUInt64 for all cases. - result = radix == 10 ? ulong.Parse(text) : Convert.ToUInt64(text, radix); - } catch (OverflowException) { - // Convert OverflowException to FormatException so there's a single exception type this method can throw. - string numberDescription = string.Format("{0}-bit {1}signed integer", isLong ? 64 : 32, isSigned ? "" : "un"); - throw new FormatException("Number out of range for " + numberDescription + ": " + original); - } - - if (negative) { - ulong max = isLong ? 0x8000000000000000UL : 0x80000000L; - if (result > max) { - string numberDescription = string.Format("{0}-bit signed integer", isLong ? 64 : 32); - throw new FormatException("Number out of range for " + numberDescription + ": " + original); - } - return -((long) result); - } else { - ulong max = isSigned - ? (isLong ? (ulong) long.MaxValue : int.MaxValue) - : (isLong ? ulong.MaxValue : uint.MaxValue); - if (result > max) { - string numberDescription = string.Format("{0}-bit {1}signed integer", isLong ? 64 : 32, isSigned ? "" : "un"); - throw new FormatException("Number out of range for " + numberDescription + ": " + original); - } - return (long) result; - } - } - - /// <summary> - /// Tests a character to see if it's an octal digit. - /// </summary> - private static bool IsOctal(char c) { - return '0' <= c && c <= '7'; - } - - /// <summary> - /// Tests a character to see if it's a hex digit. - /// </summary> - private static bool IsHex(char c) { - return ('0' <= c && c <= '9') || - ('a' <= c && c <= 'f') || - ('A' <= c && c <= 'F'); - } - - /// <summary> - /// Interprets a character as a digit (in any base up to 36) and returns the - /// numeric value. - /// </summary> - private static int ParseDigit(char c) { - if ('0' <= c && c <= '9') { - return c - '0'; - } else if ('a' <= c && c <= 'z') { - return c - 'a' + 10; - } else { - return c - 'A' + 10; - } - } - - /// <summary> - /// Unescapes a text string as escaped using <see cref="EscapeText(string)" />. - /// Two-digit hex escapes (starting with "\x" are also recognised. - /// </summary> - public static string UnescapeText(string input) { - return UnescapeBytes(input).ToStringUtf8(); - } - - /// <summary> - /// Like <see cref="EscapeBytes" /> but escapes a text string. - /// The string is first encoded as UTF-8, then each byte escaped individually. - /// The returned value is guaranteed to be entirely ASCII. - /// </summary> - public static string EscapeText(string input) { - return EscapeBytes(ByteString.CopyFromUtf8(input)); - } - - /// <summary> - /// 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> - public static String EscapeBytes(ByteString input) { - StringBuilder builder = new StringBuilder(input.Length); - foreach (byte b in input) { - switch (b) { - // C# does not use \a or \v - case 0x07: builder.Append("\\a" ); break; - case (byte)'\b': builder.Append("\\b" ); break; - case (byte)'\f': builder.Append("\\f" ); break; - case (byte)'\n': builder.Append("\\n" ); break; - case (byte)'\r': builder.Append("\\r" ); break; - case (byte)'\t': builder.Append("\\t" ); break; - case 0x0b: builder.Append("\\v" ); break; - case (byte)'\\': builder.Append("\\\\"); break; - case (byte)'\'': builder.Append("\\\'"); break; - case (byte)'"' : builder.Append("\\\""); break; - default: - if (b >= 0x20 && b < 128) { - builder.Append((char) b); - } else { - builder.Append('\\'); - builder.Append((char) ('0' + ((b >> 6) & 3))); - builder.Append((char) ('0' + ((b >> 3) & 7))); - builder.Append((char) ('0' + (b & 7))); - } - break; - } - } - return builder.ToString(); - } - - /// <summary> - /// Performs string unescaping from C style (octal, hex, form feeds, tab etc) into a byte string. - /// </summary> - public static ByteString UnescapeBytes(string input) { - byte[] result = new byte[input.Length]; - int pos = 0; - for (int i = 0; i < input.Length; i++) { - char c = input[i]; - if (c > 127 || c < 32) { - throw new FormatException("Escaped string must only contain ASCII"); - } - if (c != '\\') { - result[pos++] = (byte) c; - continue; - } - if (i + 1 >= input.Length) { - throw new FormatException("Invalid escape sequence: '\\' at end of string."); - } - - i++; - c = input[i]; - if (c >= '0' && c <= '7') { - // Octal escape. - int code = ParseDigit(c); - if (i + 1 < input.Length && IsOctal(input[i+1])) { - i++; - code = code * 8 + ParseDigit(input[i]); - } - if (i + 1 < input.Length && IsOctal(input[i+1])) { - i++; - code = code * 8 + ParseDigit(input[i]); - } - result[pos++] = (byte) code; - } else { - switch (c) { - case 'a': result[pos++] = 0x07; break; - case 'b': result[pos++] = (byte) '\b'; break; - case 'f': result[pos++] = (byte) '\f'; break; - case 'n': result[pos++] = (byte) '\n'; break; - case 'r': result[pos++] = (byte) '\r'; break; - case 't': result[pos++] = (byte) '\t'; break; - case 'v': result[pos++] = 0x0b; break; - case '\\': result[pos++] = (byte) '\\'; break; - case '\'': result[pos++] = (byte) '\''; break; - case '"': result[pos++] = (byte) '\"'; break; - - case 'x': - // hex escape - int code; - if (i + 1 < input.Length && IsHex(input[i+1])) { - i++; - code = ParseDigit(input[i]); - } else { - throw new FormatException("Invalid escape sequence: '\\x' with no digits"); - } - if (i + 1 < input.Length && IsHex(input[i+1])) { - ++i; - code = code * 16 + ParseDigit(input[i]); - } - result[pos++] = (byte)code; - break; - - default: - throw new FormatException("Invalid escape sequence: '\\" + c + "'"); - } - } - } - - return ByteString.CopyFrom(result, 0, pos); - } - - public static void Merge(string text, IBuilder builder) { - Merge(text, ExtensionRegistry.Empty, builder); - } - - public static void Merge(TextReader reader, IBuilder builder) { - Merge(reader, ExtensionRegistry.Empty, builder); - } - - public static void Merge(TextReader reader, ExtensionRegistry registry, IBuilder builder) { - Merge(reader.ReadToEnd(), registry, builder); - } - - public static void Merge(string text, ExtensionRegistry registry, IBuilder builder) { - TextTokenizer tokenizer = new TextTokenizer(text); - - while (!tokenizer.AtEnd) { - MergeField(tokenizer, registry, builder); - } - } - - /// <summary> - /// Parses a single field from the specified tokenizer and merges it into - /// the builder. - /// </summary> - private static void MergeField(TextTokenizer tokenizer, ExtensionRegistry extensionRegistry, - IBuilder builder) { - - FieldDescriptor field; - MessageDescriptor type = builder.DescriptorForType; - ExtensionInfo extension = null; - - if (tokenizer.TryConsume("[")) { - // An extension. - StringBuilder name = new StringBuilder(tokenizer.ConsumeIdentifier()); - while (tokenizer.TryConsume(".")) { - name.Append("."); - name.Append(tokenizer.ConsumeIdentifier()); - } - - extension = extensionRegistry[name.ToString()]; - - if (extension == null) { - throw tokenizer.CreateFormatExceptionPreviousToken("Extension \"" + name + "\" not found in the ExtensionRegistry."); - } else if (extension.Descriptor.ContainingType != type) { - throw tokenizer.CreateFormatExceptionPreviousToken("Extension \"" + name + "\" does not extend message type \"" + - type.FullName + "\"."); - } - - tokenizer.Consume("]"); - - field = extension.Descriptor; - } else { - String name = tokenizer.ConsumeIdentifier(); - field = type.FindDescriptor<FieldDescriptor>(name); - - // Group names are expected to be capitalized as they appear in the - // .proto file, which actually matches their type names, not their field - // names. - if (field == null) { - // Explicitly specify the invariant culture so that this code does not break when - // executing in Turkey. - String lowerName = name.ToLower(CultureInfo.InvariantCulture); - field = type.FindDescriptor<FieldDescriptor>(lowerName); - // If the case-insensitive match worked but the field is NOT a group, - // TODO(jonskeet): What? Java comment ends here! - if (field != null && field.FieldType != FieldType.Group) { - field = null; - } - } - // Again, special-case group names as described above. - if (field != null && field.FieldType == FieldType.Group && field.MessageType.Name != name) { - field = null; - } - - if (field == null) { - throw tokenizer.CreateFormatExceptionPreviousToken( - "Message type \"" + type.FullName + "\" has no field named \"" + name + "\"."); - } - } - - object value = null; - - if (field.MappedType == MappedType.Message) { - tokenizer.TryConsume(":"); // optional - - String endToken; - if (tokenizer.TryConsume("<")) { - endToken = ">"; - } else { - tokenizer.Consume("{"); - endToken = "}"; - } - - IBuilder subBuilder; - if (extension == null) { - subBuilder = builder.CreateBuilderForField(field); - } else { - subBuilder = extension.DefaultInstance.WeakCreateBuilderForType() as IBuilder; - if (subBuilder == null) - throw new NotSupportedException("Lite messages are not supported."); - } - - while (!tokenizer.TryConsume(endToken)) { - if (tokenizer.AtEnd) { - throw tokenizer.CreateFormatException("Expected \"" + endToken + "\"."); - } - MergeField(tokenizer, extensionRegistry, subBuilder); - } - - value = subBuilder.WeakBuild(); - - } else { - tokenizer.Consume(":"); - - switch (field.FieldType) { - case FieldType.Int32: - case FieldType.SInt32: - case FieldType.SFixed32: - value = tokenizer.ConsumeInt32(); - break; - - case FieldType.Int64: - case FieldType.SInt64: - case FieldType.SFixed64: - value = tokenizer.ConsumeInt64(); - break; - - case FieldType.UInt32: - case FieldType.Fixed32: - value = tokenizer.ConsumeUInt32(); - break; - - case FieldType.UInt64: - case FieldType.Fixed64: - value = tokenizer.ConsumeUInt64(); - break; - - case FieldType.Float: - value = tokenizer.ConsumeFloat(); - break; - - case FieldType.Double: - value = tokenizer.ConsumeDouble(); - break; - - case FieldType.Bool: - value = tokenizer.ConsumeBoolean(); - break; - - case FieldType.String: - value = tokenizer.ConsumeString(); - break; - - case FieldType.Bytes: - value = tokenizer.ConsumeByteString(); - break; - - case FieldType.Enum: { - EnumDescriptor enumType = field.EnumType; - - if (tokenizer.LookingAtInteger()) { - int number = tokenizer.ConsumeInt32(); - value = enumType.FindValueByNumber(number); - if (value == null) { - throw tokenizer.CreateFormatExceptionPreviousToken( - "Enum type \"" + enumType.FullName + - "\" has no value with number " + number + "."); - } - } else { - String id = tokenizer.ConsumeIdentifier(); - value = enumType.FindValueByName(id); - if (value == null) { - throw tokenizer.CreateFormatExceptionPreviousToken( - "Enum type \"" + enumType.FullName + - "\" has no value named \"" + id + "\"."); - } - } - - break; - } - - case FieldType.Message: - case FieldType.Group: - throw new InvalidOperationException("Can't get here."); - } - } - - if (field.IsRepeated) { - builder.WeakAddRepeatedField(field, value); - } else { - builder.SetField(field, value); - } - } - } -} + public static void Merge(TextReader reader, IBuilder builder) {
+ Merge(reader, ExtensionRegistry.Empty, builder);
+ }
+
+ public static void Merge(TextReader reader, ExtensionRegistry registry, IBuilder builder) {
+ Merge(reader.ReadToEnd(), registry, builder);
+ }
+
+ public static void Merge(string text, ExtensionRegistry registry, IBuilder builder) {
+ TextTokenizer tokenizer = new TextTokenizer(text);
+
+ while (!tokenizer.AtEnd) {
+ MergeField(tokenizer, registry, builder);
+ }
+ }
+
+ /// <summary>
+ /// Parses a single field from the specified tokenizer and merges it into
+ /// the builder.
+ /// </summary>
+ private static void MergeField(TextTokenizer tokenizer, ExtensionRegistry extensionRegistry,
+ IBuilder builder) {
+
+ FieldDescriptor field;
+ MessageDescriptor type = builder.DescriptorForType;
+ ExtensionInfo extension = null;
+
+ if (tokenizer.TryConsume("[")) {
+ // An extension.
+ StringBuilder name = new StringBuilder(tokenizer.ConsumeIdentifier());
+ while (tokenizer.TryConsume(".")) {
+ name.Append(".");
+ name.Append(tokenizer.ConsumeIdentifier());
+ }
+
+ extension = extensionRegistry[name.ToString()];
+
+ if (extension == null) {
+ throw tokenizer.CreateFormatExceptionPreviousToken("Extension \"" + name + "\" not found in the ExtensionRegistry.");
+ } else if (extension.Descriptor.ContainingType != type) {
+ throw tokenizer.CreateFormatExceptionPreviousToken("Extension \"" + name + "\" does not extend message type \"" +
+ type.FullName + "\".");
+ }
+
+ tokenizer.Consume("]");
+
+ field = extension.Descriptor;
+ } else {
+ String name = tokenizer.ConsumeIdentifier();
+ field = type.FindDescriptor<FieldDescriptor>(name);
+
+ // Group names are expected to be capitalized as they appear in the
+ // .proto file, which actually matches their type names, not their field
+ // names.
+ if (field == null) {
+ // Explicitly specify the invariant culture so that this code does not break when
+ // executing in Turkey.
+ String lowerName = name.ToLower(CultureInfo.InvariantCulture);
+ field = type.FindDescriptor<FieldDescriptor>(lowerName);
+ // If the case-insensitive match worked but the field is NOT a group,
+ // TODO(jonskeet): What? Java comment ends here!
+ if (field != null && field.FieldType != FieldType.Group) {
+ field = null;
+ }
+ }
+ // Again, special-case group names as described above.
+ if (field != null && field.FieldType == FieldType.Group && field.MessageType.Name != name) {
+ field = null;
+ }
+
+ if (field == null) {
+ throw tokenizer.CreateFormatExceptionPreviousToken(
+ "Message type \"" + type.FullName + "\" has no field named \"" + name + "\".");
+ }
+ }
+
+ object value = null;
+
+ if (field.MappedType == MappedType.Message) {
+ tokenizer.TryConsume(":"); // optional
+
+ String endToken;
+ if (tokenizer.TryConsume("<")) {
+ endToken = ">";
+ } else {
+ tokenizer.Consume("{");
+ endToken = "}";
+ }
+
+ IBuilder subBuilder;
+ if (extension == null) {
+ subBuilder = builder.CreateBuilderForField(field);
+ } else {
+ subBuilder = extension.DefaultInstance.WeakCreateBuilderForType() as IBuilder;
+ if (subBuilder == null)
+ throw new NotSupportedException("Lite messages are not supported.");
+ }
+
+ while (!tokenizer.TryConsume(endToken)) {
+ if (tokenizer.AtEnd) {
+ throw tokenizer.CreateFormatException("Expected \"" + endToken + "\".");
+ }
+ MergeField(tokenizer, extensionRegistry, subBuilder);
+ }
+
+ value = subBuilder.WeakBuild();
+
+ } else {
+ tokenizer.Consume(":");
+
+ switch (field.FieldType) {
+ case FieldType.Int32:
+ case FieldType.SInt32:
+ case FieldType.SFixed32:
+ value = tokenizer.ConsumeInt32();
+ break;
+
+ case FieldType.Int64:
+ case FieldType.SInt64:
+ case FieldType.SFixed64:
+ value = tokenizer.ConsumeInt64();
+ break;
+
+ case FieldType.UInt32:
+ case FieldType.Fixed32:
+ value = tokenizer.ConsumeUInt32();
+ break;
+
+ case FieldType.UInt64:
+ case FieldType.Fixed64:
+ value = tokenizer.ConsumeUInt64();
+ break;
+
+ case FieldType.Float:
+ value = tokenizer.ConsumeFloat();
+ break;
+
+ case FieldType.Double:
+ value = tokenizer.ConsumeDouble();
+ break;
+
+ case FieldType.Bool:
+ value = tokenizer.ConsumeBoolean();
+ break;
+
+ case FieldType.String:
+ value = tokenizer.ConsumeString();
+ break;
+
+ case FieldType.Bytes:
+ value = tokenizer.ConsumeByteString();
+ break;
+
+ case FieldType.Enum: {
+ EnumDescriptor enumType = field.EnumType;
+
+ if (tokenizer.LookingAtInteger()) {
+ int number = tokenizer.ConsumeInt32();
+ value = enumType.FindValueByNumber(number);
+ if (value == null) {
+ throw tokenizer.CreateFormatExceptionPreviousToken(
+ "Enum type \"" + enumType.FullName +
+ "\" has no value with number " + number + ".");
+ }
+ } else {
+ String id = tokenizer.ConsumeIdentifier();
+ value = enumType.FindValueByName(id);
+ if (value == null) {
+ throw tokenizer.CreateFormatExceptionPreviousToken(
+ "Enum type \"" + enumType.FullName +
+ "\" has no value named \"" + id + "\".");
+ }
+ }
+
+ break;
+ }
+
+ case FieldType.Message:
+ case FieldType.Group:
+ throw new InvalidOperationException("Can't get here.");
+ }
+ }
+
+ if (field.IsRepeated) {
+ builder.WeakAddRepeatedField(field, value);
+ } else {
+ builder.SetField(field, value);
+ }
+ }
+ }
+}
diff --git a/src/ProtocolBuffers/TextGenerator.cs b/src/ProtocolBuffers/TextGenerator.cs index 6b8fa048..b333ff0a 100644 --- a/src/ProtocolBuffers/TextGenerator.cs +++ b/src/ProtocolBuffers/TextGenerator.cs @@ -1,143 +1,143 @@ -#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.IO; -using System.Text; - -namespace Google.ProtocolBuffers { - - /// <summary> - /// Helper class to control indentation. Used for TextFormat and by ProtoGen. - /// </summary> - public sealed class TextGenerator { - - /// <summary> - /// The string to use at the end of each line. We assume that "Print" is only called using \n - /// to indicate a line break; that's what we use to detect when we need to indent etc, and - /// *just* the \n is replaced with the contents of lineBreak. - /// </summary> - private readonly string lineBreak; - - /// <summary> - /// Writer to write formatted text to. - /// </summary> - private readonly TextWriter writer; - - /// <summary> - /// Keeps track of whether the next piece of text should be indented - /// </summary> - bool atStartOfLine = true; - - /// <summary> - /// Keeps track of the current level of indentation - /// </summary> - readonly StringBuilder indent = new StringBuilder(); - - /// <summary> - /// Creates a generator writing to the given writer. The writer - /// is not closed by this class. - /// </summary> - public TextGenerator(TextWriter writer, string lineBreak) { - this.writer = writer; - this.lineBreak = lineBreak; - } - - /// <summary> - /// Indents text by two spaces. After calling Indent(), two spaces - /// will be inserted at the beginning of each line of text. Indent() may - /// be called multiple times to produce deeper indents. - /// </summary> - public void Indent() { - indent.Append(" "); - } - - /// <summary> - /// Reduces the current indent level by two spaces. - /// </summary> - public void Outdent() { - if (indent.Length == 0) { - throw new InvalidOperationException("Too many calls to Outdent()"); - } - indent.Length -= 2; - } - - public void WriteLine(string text) { - Print(text); - Print("\n"); - } - - public void WriteLine(string format, params object[] args) { - WriteLine(string.Format(format, args)); - } - - public void WriteLine() { - WriteLine(""); - } - - /// <summary> - /// Prints the given text to the output stream, indenting at line boundaries. - /// </summary> - /// <param name="text"></param> - public void Print(string text) { - int pos = 0; - - for (int i = 0; i < text.Length; i++) { - if (text[i] == '\n') { - // Strip off the \n from what we write - Write(text.Substring(pos, i - pos)); - Write(lineBreak); - pos = i + 1; - atStartOfLine = true; - } - } - Write(text.Substring(pos)); - } - - public void Write(string format, params object[] args) { - Write(string.Format(format, args)); - } - - private void Write(string data) { - if (data.Length == 0) { - return; - } - if (atStartOfLine) { - atStartOfLine = false; - writer.Write(indent); - } - writer.Write(data); - } - } -} +#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.IO;
+using System.Text;
+
+namespace Google.ProtocolBuffers {
+
+ /// <summary>
+ /// Helper class to control indentation. Used for TextFormat and by ProtoGen.
+ /// </summary>
+ public sealed class TextGenerator {
+
+ /// <summary>
+ /// The string to use at the end of each line. We assume that "Print" is only called using \n
+ /// to indicate a line break; that's what we use to detect when we need to indent etc, and
+ /// *just* the \n is replaced with the contents of lineBreak.
+ /// </summary>
+ private readonly string lineBreak;
+
+ /// <summary>
+ /// Writer to write formatted text to.
+ /// </summary>
+ private readonly TextWriter writer;
+
+ /// <summary>
+ /// Keeps track of whether the next piece of text should be indented
+ /// </summary>
+ bool atStartOfLine = true;
+
+ /// <summary>
+ /// Keeps track of the current level of indentation
+ /// </summary>
+ readonly StringBuilder indent = new StringBuilder();
+
+ /// <summary>
+ /// Creates a generator writing to the given writer. The writer
+ /// is not closed by this class.
+ /// </summary>
+ public TextGenerator(TextWriter writer, string lineBreak) {
+ this.writer = writer;
+ this.lineBreak = lineBreak;
+ }
+
+ /// <summary>
+ /// Indents text by two spaces. After calling Indent(), two spaces
+ /// will be inserted at the beginning of each line of text. Indent() may
+ /// be called multiple times to produce deeper indents.
+ /// </summary>
+ public void Indent() {
+ indent.Append(" ");
+ }
+
+ /// <summary>
+ /// Reduces the current indent level by two spaces.
+ /// </summary>
+ public void Outdent() {
+ if (indent.Length == 0) {
+ throw new InvalidOperationException("Too many calls to Outdent()");
+ }
+ indent.Length -= 2;
+ }
+
+ public void WriteLine(string text) {
+ Print(text);
+ Print("\n");
+ }
+
+ public void WriteLine(string format, params object[] args) {
+ WriteLine(string.Format(format, args));
+ }
+
+ public void WriteLine() {
+ WriteLine("");
+ }
+
+ /// <summary>
+ /// Prints the given text to the output stream, indenting at line boundaries.
+ /// </summary>
+ /// <param name="text"></param>
+ public void Print(string text) {
+ int pos = 0;
+
+ for (int i = 0; i < text.Length; i++) {
+ if (text[i] == '\n') {
+ // Strip off the \n from what we write
+ Write(text.Substring(pos, i - pos));
+ Write(lineBreak);
+ pos = i + 1;
+ atStartOfLine = true;
+ }
+ }
+ Write(text.Substring(pos));
+ }
+
+ public void Write(string format, params object[] args) {
+ Write(string.Format(format, args));
+ }
+
+ private void Write(string data) {
+ if (data.Length == 0) {
+ return;
+ }
+ if (atStartOfLine) {
+ atStartOfLine = false;
+ writer.Write(indent);
+ }
+ writer.Write(data);
+ }
+ }
+}
diff --git a/src/ProtocolBuffers/TextTokenizer.cs b/src/ProtocolBuffers/TextTokenizer.cs index ce067728..d3e8878c 100644 --- a/src/ProtocolBuffers/TextTokenizer.cs +++ b/src/ProtocolBuffers/TextTokenizer.cs @@ -1,412 +1,412 @@ -#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.Globalization; -using System.Text.RegularExpressions; - -namespace Google.ProtocolBuffers { - /// <summary> - /// Represents a stream of tokens parsed from a string. - /// </summary> - internal sealed class TextTokenizer { - private readonly string text; - private string currentToken; - - /// <summary> - /// The character index within the text to perform the next regex match at. - /// </summary> - private int matchPos = 0; - - /// <summary> - /// The character index within the text at which the current token begins. - /// </summary> - private int pos = 0; - - /// <summary> - /// The line number of the current token. - /// </summary> - private int line = 0; - /// <summary> - /// The column number of the current token. - /// </summary> - private int column = 0; - - /// <summary> - /// The line number of the previous token. - /// </summary> - private int previousLine = 0; - /// <summary> - /// The column number of the previous token. - /// </summary> - private int previousColumn = 0; - - // Note: atomic groups used to mimic possessive quantifiers in Java in both of these regexes - internal static readonly Regex WhitespaceAndCommentPattern = new Regex("\\G(?>(\\s|(#.*$))+)", - SilverlightCompatibility.CompiledRegexWhereAvailable | RegexOptions.Multiline); - private static readonly Regex TokenPattern = new Regex( - "\\G[a-zA-Z_](?>[0-9a-zA-Z_+-]*)|" + // an identifier - "\\G[0-9+-](?>[0-9a-zA-Z_.+-]*)|" + // a number - "\\G\"(?>([^\"\\\n\\\\]|\\\\.)*)(\"|\\\\?$)|" + // a double-quoted string - "\\G\'(?>([^\"\\\n\\\\]|\\\\.)*)(\'|\\\\?$)", // a single-quoted string - SilverlightCompatibility.CompiledRegexWhereAvailable | RegexOptions.Multiline); - - private static readonly Regex DoubleInfinity = new Regex("^-?inf(inity)?$", - SilverlightCompatibility.CompiledRegexWhereAvailable | RegexOptions.IgnoreCase); - private static readonly Regex FloatInfinity = new Regex("^-?inf(inity)?f?$", - SilverlightCompatibility.CompiledRegexWhereAvailable | RegexOptions.IgnoreCase); - private static readonly Regex FloatNan = new Regex("^nanf?$", - SilverlightCompatibility.CompiledRegexWhereAvailable | RegexOptions.IgnoreCase); - - /** Construct a tokenizer that parses tokens from the given text. */ - public TextTokenizer(string text) { - this.text = text; - SkipWhitespace(); - NextToken(); - } - - /// <summary> - /// Are we at the end of the input? - /// </summary> - public bool AtEnd { - get { return currentToken.Length == 0; } - } - - /// <summary> - /// Advances to the next token. - /// </summary> - public void NextToken() { - previousLine = line; - previousColumn = column; - - // Advance the line counter to the current position. - while (pos < matchPos) { - if (text[pos] == '\n') { - ++line; - column = 0; - } else { - ++column; - } - ++pos; - } - - // Match the next token. - if (matchPos == text.Length) { - // EOF - currentToken = ""; - } else { - Match match = TokenPattern.Match(text, matchPos); - if (match.Success) { - currentToken = match.Value; - matchPos += match.Length; - } else { - // Take one character. - currentToken = text[matchPos].ToString(); - matchPos++; - } - - SkipWhitespace(); - } - } - - /// <summary> - /// Skip over any whitespace so that matchPos starts at the next token. - /// </summary> - private void SkipWhitespace() { - Match match = WhitespaceAndCommentPattern.Match(text, matchPos); - if (match.Success) { - matchPos += match.Length; - } - } - - /// <summary> - /// If the next token exactly matches the given token, consume it and return - /// true. Otherwise, return false without doing anything. - /// </summary> - public bool TryConsume(string token) { - if (currentToken == token) { - NextToken(); - return true; - } - return false; - } - - /* - * If the next token exactly matches {@code token}, consume it. Otherwise, - * throw a {@link ParseException}. - */ - /// <summary> - /// If the next token exactly matches the specified one, consume it. - /// Otherwise, throw a FormatException. - /// </summary> - /// <param name="token"></param> - public void Consume(string token) { - if (!TryConsume(token)) { - throw CreateFormatException("Expected \"" + token + "\"."); - } - } - - /// <summary> - /// Returns true if the next token is an integer, but does not consume it. - /// </summary> - public bool LookingAtInteger() { - if (currentToken.Length == 0) { - return false; - } - - char c = currentToken[0]; - return ('0' <= c && c <= '9') || c == '-' || c == '+'; - } - - /// <summary> - /// If the next token is an identifier, consume it and return its value. - /// Otherwise, throw a FormatException. - /// </summary> - public string ConsumeIdentifier() { - foreach (char c in currentToken) { - if (('a' <= c && c <= 'z') || - ('A' <= c && c <= 'Z') || - ('0' <= c && c <= '9') || - (c == '_') || (c == '.')) { - // OK - } else { - throw CreateFormatException("Expected identifier."); - } - } - - string result = currentToken; - NextToken(); - return result; - } - - /// <summary> - /// If the next token is a 32-bit signed integer, consume it and return its - /// value. Otherwise, throw a FormatException. - /// </summary> - public int ConsumeInt32() { - try { - int result = TextFormat.ParseInt32(currentToken); - NextToken(); - return result; - } catch (FormatException e) { - throw CreateIntegerParseException(e); - } - } - - /// <summary> - /// If the next token is a 32-bit unsigned integer, consume it and return its - /// value. Otherwise, throw a FormatException. - /// </summary> - public uint ConsumeUInt32() { - try { - uint result = TextFormat.ParseUInt32(currentToken); - NextToken(); - return result; - } catch (FormatException e) { - throw CreateIntegerParseException(e); - } - } - - /// <summary> - /// If the next token is a 64-bit signed integer, consume it and return its - /// value. Otherwise, throw a FormatException. - /// </summary> - public long ConsumeInt64() { - try { - long result = TextFormat.ParseInt64(currentToken); - NextToken(); - return result; - } catch (FormatException e) { - throw CreateIntegerParseException(e); - } - } - - /// <summary> - /// If the next token is a 64-bit unsigned integer, consume it and return its - /// value. Otherwise, throw a FormatException. - /// </summary> - public ulong ConsumeUInt64() { - try { - ulong result = TextFormat.ParseUInt64(currentToken); - NextToken(); - return result; - } catch (FormatException e) { - throw CreateIntegerParseException(e); - } - } - - /// <summary> - /// If the next token is a double, consume it and return its value. - /// Otherwise, throw a FormatException. - /// </summary> - public double ConsumeDouble() { - // We need to parse infinity and nan separately because - // double.Parse() does not accept "inf", "infinity", or "nan". - if (DoubleInfinity.IsMatch(currentToken)) { - bool negative = currentToken.StartsWith("-"); - NextToken(); - return negative ? double.NegativeInfinity : double.PositiveInfinity; - } - if (currentToken.Equals("nan", StringComparison.InvariantCultureIgnoreCase)) { - NextToken(); - return Double.NaN; - } - - try { - double result = double.Parse(currentToken, CultureInfo.InvariantCulture); - NextToken(); - return result; - } catch (FormatException e) { - throw CreateFloatParseException(e); - } catch (OverflowException e) { - throw CreateFloatParseException(e); - } - } - - /// <summary> - /// If the next token is a float, consume it and return its value. - /// Otherwise, throw a FormatException. - /// </summary> - public float ConsumeFloat() { - // We need to parse infinity and nan separately because - // Float.parseFloat() does not accept "inf", "infinity", or "nan". - if (FloatInfinity.IsMatch(currentToken)) { - bool negative = currentToken.StartsWith("-"); - NextToken(); - return negative ? float.NegativeInfinity : float.PositiveInfinity; - } - if (FloatNan.IsMatch(currentToken)) { - NextToken(); - return float.NaN; - } - - if (currentToken.EndsWith("f")) { - currentToken = currentToken.TrimEnd('f'); - } - - try { - float result = float.Parse(currentToken, CultureInfo.InvariantCulture); - NextToken(); - return result; - } catch (FormatException e) { - throw CreateFloatParseException(e); - } catch (OverflowException e) { - throw CreateFloatParseException(e); - } - } - - /// <summary> - /// If the next token is a Boolean, consume it and return its value. - /// Otherwise, throw a FormatException. - /// </summary> - public bool ConsumeBoolean() { - if (currentToken == "true") { - NextToken(); - return true; - } - if (currentToken == "false") { - NextToken(); - return false; - } - throw CreateFormatException("Expected \"true\" or \"false\"."); - } - - /// <summary> - /// If the next token is a string, consume it and return its (unescaped) value. - /// Otherwise, throw a FormatException. - /// </summary> - public string ConsumeString() { - return ConsumeByteString().ToStringUtf8(); - } - - /// <summary> - /// If the next token is a string, consume it, unescape it as a - /// ByteString and return it. Otherwise, throw a FormatException. - /// </summary> - public ByteString ConsumeByteString() { - char quote = currentToken.Length > 0 ? currentToken[0] : '\0'; - if (quote != '\"' && quote != '\'') { - throw CreateFormatException("Expected string."); - } - - if (currentToken.Length < 2 || - currentToken[currentToken.Length-1] != quote) { - throw CreateFormatException("String missing ending quote."); - } - - try { - string escaped = currentToken.Substring(1, currentToken.Length - 2); - ByteString result = TextFormat.UnescapeBytes(escaped); - NextToken(); - return result; - } catch (FormatException e) { - throw CreateFormatException(e.Message); - } - } - - /// <summary> - /// Returns a format exception with the current line and column numbers - /// in the description, suitable for throwing. - /// </summary> - public FormatException CreateFormatException(string description) { - // Note: People generally prefer one-based line and column numbers. - return new FormatException((line + 1) + ":" + (column + 1) + ": " + description); - } - - /// <summary> - /// Returns a format exception with the line and column numbers of the - /// previous token in the description, suitable for throwing. - /// </summary> - public FormatException CreateFormatExceptionPreviousToken(string description) { - // Note: People generally prefer one-based line and column numbers. - return new FormatException((previousLine + 1) + ":" + (previousColumn + 1) + ": " + description); - } - - /// <summary> - /// Constructs an appropriate FormatException for the given existing exception - /// when trying to parse an integer. - /// </summary> - private FormatException CreateIntegerParseException(FormatException e) { - return CreateFormatException("Couldn't parse integer: " + e.Message); - } - - /// <summary> - /// Constructs an appropriate FormatException for the given existing exception - /// when trying to parse a float or double. - /// </summary> - private FormatException CreateFloatParseException(Exception e) { - return CreateFormatException("Couldn't parse number: " + e.Message); - } - } -} +#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.Globalization;
+using System.Text.RegularExpressions;
+
+namespace Google.ProtocolBuffers {
+ /// <summary>
+ /// Represents a stream of tokens parsed from a string.
+ /// </summary>
+ internal sealed class TextTokenizer {
+ private readonly string text;
+ private string currentToken;
+
+ /// <summary>
+ /// The character index within the text to perform the next regex match at.
+ /// </summary>
+ private int matchPos = 0;
+
+ /// <summary>
+ /// The character index within the text at which the current token begins.
+ /// </summary>
+ private int pos = 0;
+
+ /// <summary>
+ /// The line number of the current token.
+ /// </summary>
+ private int line = 0;
+ /// <summary>
+ /// The column number of the current token.
+ /// </summary>
+ private int column = 0;
+
+ /// <summary>
+ /// The line number of the previous token.
+ /// </summary>
+ private int previousLine = 0;
+ /// <summary>
+ /// The column number of the previous token.
+ /// </summary>
+ private int previousColumn = 0;
+
+ // Note: atomic groups used to mimic possessive quantifiers in Java in both of these regexes
+ internal static readonly Regex WhitespaceAndCommentPattern = new Regex("\\G(?>(\\s|(#.*$))+)",
+ SilverlightCompatibility.CompiledRegexWhereAvailable | RegexOptions.Multiline);
+ private static readonly Regex TokenPattern = new Regex(
+ "\\G[a-zA-Z_](?>[0-9a-zA-Z_+-]*)|" + // an identifier
+ "\\G[0-9+-](?>[0-9a-zA-Z_.+-]*)|" + // a number
+ "\\G\"(?>([^\"\\\n\\\\]|\\\\.)*)(\"|\\\\?$)|" + // a double-quoted string
+ "\\G\'(?>([^\"\\\n\\\\]|\\\\.)*)(\'|\\\\?$)", // a single-quoted string
+ SilverlightCompatibility.CompiledRegexWhereAvailable | RegexOptions.Multiline);
+
+ private static readonly Regex DoubleInfinity = new Regex("^-?inf(inity)?$",
+ SilverlightCompatibility.CompiledRegexWhereAvailable | RegexOptions.IgnoreCase);
+ private static readonly Regex FloatInfinity = new Regex("^-?inf(inity)?f?$",
+ SilverlightCompatibility.CompiledRegexWhereAvailable | RegexOptions.IgnoreCase);
+ private static readonly Regex FloatNan = new Regex("^nanf?$",
+ SilverlightCompatibility.CompiledRegexWhereAvailable | RegexOptions.IgnoreCase);
+
+ /** Construct a tokenizer that parses tokens from the given text. */
+ public TextTokenizer(string text) {
+ this.text = text;
+ SkipWhitespace();
+ NextToken();
+ }
+
+ /// <summary>
+ /// Are we at the end of the input?
+ /// </summary>
+ public bool AtEnd {
+ get { return currentToken.Length == 0; }
+ }
+
+ /// <summary>
+ /// Advances to the next token.
+ /// </summary>
+ public void NextToken() {
+ previousLine = line;
+ previousColumn = column;
+
+ // Advance the line counter to the current position.
+ while (pos < matchPos) {
+ if (text[pos] == '\n') {
+ ++line;
+ column = 0;
+ } else {
+ ++column;
+ }
+ ++pos;
+ }
+
+ // Match the next token.
+ if (matchPos == text.Length) {
+ // EOF
+ currentToken = "";
+ } else {
+ Match match = TokenPattern.Match(text, matchPos);
+ if (match.Success) {
+ currentToken = match.Value;
+ matchPos += match.Length;
+ } else {
+ // Take one character.
+ currentToken = text[matchPos].ToString();
+ matchPos++;
+ }
+
+ SkipWhitespace();
+ }
+ }
+
+ /// <summary>
+ /// Skip over any whitespace so that matchPos starts at the next token.
+ /// </summary>
+ private void SkipWhitespace() {
+ Match match = WhitespaceAndCommentPattern.Match(text, matchPos);
+ if (match.Success) {
+ matchPos += match.Length;
+ }
+ }
+
+ /// <summary>
+ /// If the next token exactly matches the given token, consume it and return
+ /// true. Otherwise, return false without doing anything.
+ /// </summary>
+ public bool TryConsume(string token) {
+ if (currentToken == token) {
+ NextToken();
+ return true;
+ }
+ return false;
+ }
+
+ /*
+ * If the next token exactly matches {@code token}, consume it. Otherwise,
+ * throw a {@link ParseException}.
+ */
+ /// <summary>
+ /// If the next token exactly matches the specified one, consume it.
+ /// Otherwise, throw a FormatException.
+ /// </summary>
+ /// <param name="token"></param>
+ public void Consume(string token) {
+ if (!TryConsume(token)) {
+ throw CreateFormatException("Expected \"" + token + "\".");
+ }
+ }
+
+ /// <summary>
+ /// Returns true if the next token is an integer, but does not consume it.
+ /// </summary>
+ public bool LookingAtInteger() {
+ if (currentToken.Length == 0) {
+ return false;
+ }
+
+ char c = currentToken[0];
+ return ('0' <= c && c <= '9') || c == '-' || c == '+';
+ }
+
+ /// <summary>
+ /// If the next token is an identifier, consume it and return its value.
+ /// Otherwise, throw a FormatException.
+ /// </summary>
+ public string ConsumeIdentifier() {
+ foreach (char c in currentToken) {
+ if (('a' <= c && c <= 'z') ||
+ ('A' <= c && c <= 'Z') ||
+ ('0' <= c && c <= '9') ||
+ (c == '_') || (c == '.')) {
+ // OK
+ } else {
+ throw CreateFormatException("Expected identifier.");
+ }
+ }
+
+ string result = currentToken;
+ NextToken();
+ return result;
+ }
+
+ /// <summary>
+ /// If the next token is a 32-bit signed integer, consume it and return its
+ /// value. Otherwise, throw a FormatException.
+ /// </summary>
+ public int ConsumeInt32() {
+ try {
+ int result = TextFormat.ParseInt32(currentToken);
+ NextToken();
+ return result;
+ } catch (FormatException e) {
+ throw CreateIntegerParseException(e);
+ }
+ }
+
+ /// <summary>
+ /// If the next token is a 32-bit unsigned integer, consume it and return its
+ /// value. Otherwise, throw a FormatException.
+ /// </summary>
+ public uint ConsumeUInt32() {
+ try {
+ uint result = TextFormat.ParseUInt32(currentToken);
+ NextToken();
+ return result;
+ } catch (FormatException e) {
+ throw CreateIntegerParseException(e);
+ }
+ }
+
+ /// <summary>
+ /// If the next token is a 64-bit signed integer, consume it and return its
+ /// value. Otherwise, throw a FormatException.
+ /// </summary>
+ public long ConsumeInt64() {
+ try {
+ long result = TextFormat.ParseInt64(currentToken);
+ NextToken();
+ return result;
+ } catch (FormatException e) {
+ throw CreateIntegerParseException(e);
+ }
+ }
+
+ /// <summary>
+ /// If the next token is a 64-bit unsigned integer, consume it and return its
+ /// value. Otherwise, throw a FormatException.
+ /// </summary>
+ public ulong ConsumeUInt64() {
+ try {
+ ulong result = TextFormat.ParseUInt64(currentToken);
+ NextToken();
+ return result;
+ } catch (FormatException e) {
+ throw CreateIntegerParseException(e);
+ }
+ }
+
+ /// <summary>
+ /// If the next token is a double, consume it and return its value.
+ /// Otherwise, throw a FormatException.
+ /// </summary>
+ public double ConsumeDouble() {
+ // We need to parse infinity and nan separately because
+ // double.Parse() does not accept "inf", "infinity", or "nan".
+ if (DoubleInfinity.IsMatch(currentToken)) {
+ bool negative = currentToken.StartsWith("-");
+ NextToken();
+ return negative ? double.NegativeInfinity : double.PositiveInfinity;
+ }
+ if (currentToken.Equals("nan", StringComparison.InvariantCultureIgnoreCase)) {
+ NextToken();
+ return Double.NaN;
+ }
+
+ try {
+ double result = double.Parse(currentToken, CultureInfo.InvariantCulture);
+ NextToken();
+ return result;
+ } catch (FormatException e) {
+ throw CreateFloatParseException(e);
+ } catch (OverflowException e) {
+ throw CreateFloatParseException(e);
+ }
+ }
+
+ /// <summary>
+ /// If the next token is a float, consume it and return its value.
+ /// Otherwise, throw a FormatException.
+ /// </summary>
+ public float ConsumeFloat() {
+ // We need to parse infinity and nan separately because
+ // Float.parseFloat() does not accept "inf", "infinity", or "nan".
+ if (FloatInfinity.IsMatch(currentToken)) {
+ bool negative = currentToken.StartsWith("-");
+ NextToken();
+ return negative ? float.NegativeInfinity : float.PositiveInfinity;
+ }
+ if (FloatNan.IsMatch(currentToken)) {
+ NextToken();
+ return float.NaN;
+ }
+
+ if (currentToken.EndsWith("f")) {
+ currentToken = currentToken.TrimEnd('f');
+ }
+
+ try {
+ float result = float.Parse(currentToken, CultureInfo.InvariantCulture);
+ NextToken();
+ return result;
+ } catch (FormatException e) {
+ throw CreateFloatParseException(e);
+ } catch (OverflowException e) {
+ throw CreateFloatParseException(e);
+ }
+ }
+
+ /// <summary>
+ /// If the next token is a Boolean, consume it and return its value.
+ /// Otherwise, throw a FormatException.
+ /// </summary>
+ public bool ConsumeBoolean() {
+ if (currentToken == "true") {
+ NextToken();
+ return true;
+ }
+ if (currentToken == "false") {
+ NextToken();
+ return false;
+ }
+ throw CreateFormatException("Expected \"true\" or \"false\".");
+ }
+
+ /// <summary>
+ /// If the next token is a string, consume it and return its (unescaped) value.
+ /// Otherwise, throw a FormatException.
+ /// </summary>
+ public string ConsumeString() {
+ return ConsumeByteString().ToStringUtf8();
+ }
+
+ /// <summary>
+ /// If the next token is a string, consume it, unescape it as a
+ /// ByteString and return it. Otherwise, throw a FormatException.
+ /// </summary>
+ public ByteString ConsumeByteString() {
+ char quote = currentToken.Length > 0 ? currentToken[0] : '\0';
+ if (quote != '\"' && quote != '\'') {
+ throw CreateFormatException("Expected string.");
+ }
+
+ if (currentToken.Length < 2 ||
+ currentToken[currentToken.Length-1] != quote) {
+ throw CreateFormatException("String missing ending quote.");
+ }
+
+ try {
+ string escaped = currentToken.Substring(1, currentToken.Length - 2);
+ ByteString result = TextFormat.UnescapeBytes(escaped);
+ NextToken();
+ return result;
+ } catch (FormatException e) {
+ throw CreateFormatException(e.Message);
+ }
+ }
+
+ /// <summary>
+ /// Returns a format exception with the current line and column numbers
+ /// in the description, suitable for throwing.
+ /// </summary>
+ public FormatException CreateFormatException(string description) {
+ // Note: People generally prefer one-based line and column numbers.
+ return new FormatException((line + 1) + ":" + (column + 1) + ": " + description);
+ }
+
+ /// <summary>
+ /// Returns a format exception with the line and column numbers of the
+ /// previous token in the description, suitable for throwing.
+ /// </summary>
+ public FormatException CreateFormatExceptionPreviousToken(string description) {
+ // Note: People generally prefer one-based line and column numbers.
+ return new FormatException((previousLine + 1) + ":" + (previousColumn + 1) + ": " + description);
+ }
+
+ /// <summary>
+ /// Constructs an appropriate FormatException for the given existing exception
+ /// when trying to parse an integer.
+ /// </summary>
+ private FormatException CreateIntegerParseException(FormatException e) {
+ return CreateFormatException("Couldn't parse integer: " + e.Message);
+ }
+
+ /// <summary>
+ /// Constructs an appropriate FormatException for the given existing exception
+ /// when trying to parse a float or double.
+ /// </summary>
+ private FormatException CreateFloatParseException(Exception e) {
+ return CreateFormatException("Couldn't parse number: " + e.Message);
+ }
+ }
+}
diff --git a/src/ProtocolBuffers/ThrowHelper.cs b/src/ProtocolBuffers/ThrowHelper.cs index 76983140..580024db 100644 --- a/src/ProtocolBuffers/ThrowHelper.cs +++ b/src/ProtocolBuffers/ThrowHelper.cs @@ -1,73 +1,73 @@ -#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; - -namespace Google.ProtocolBuffers { - /// <summary> - /// Helper methods for throwing exceptions - /// </summary> - public static class ThrowHelper { - - /// <summary> - /// Throws an ArgumentNullException if the given value is null. - /// </summary> - public static void ThrowIfNull(object value, string name) { - if (value == null) { - throw new ArgumentNullException(name); - } - } - - /// <summary> - /// Throws an ArgumentNullException if the given value is null. - /// </summary> - public static void ThrowIfNull(object value) { - if (value == null) { - throw new ArgumentNullException(); - } - } - - /// <summary> - /// Throws an ArgumentNullException if the given value or any element within it is null. - /// </summary> - public static void ThrowIfAnyNull<T>(IEnumerable<T> sequence) { - foreach (T t in sequence) { - if (t == null) { - throw new ArgumentNullException(); - } - } - } - } -} +#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;
+
+namespace Google.ProtocolBuffers {
+ /// <summary>
+ /// Helper methods for throwing exceptions
+ /// </summary>
+ public static class ThrowHelper {
+
+ /// <summary>
+ /// Throws an ArgumentNullException if the given value is null.
+ /// </summary>
+ public static void ThrowIfNull(object value, string name) {
+ if (value == null) {
+ throw new ArgumentNullException(name);
+ }
+ }
+
+ /// <summary>
+ /// Throws an ArgumentNullException if the given value is null.
+ /// </summary>
+ public static void ThrowIfNull(object value) {
+ if (value == null) {
+ throw new ArgumentNullException();
+ }
+ }
+
+ /// <summary>
+ /// Throws an ArgumentNullException if the given value or any element within it is null.
+ /// </summary>
+ public static void ThrowIfAnyNull<T>(IEnumerable<T> sequence) {
+ foreach (T t in sequence) {
+ if (t == null) {
+ throw new ArgumentNullException();
+ }
+ }
+ }
+ }
+}
diff --git a/src/ProtocolBuffers/UninitializedMessageException.cs b/src/ProtocolBuffers/UninitializedMessageException.cs index d7f58197..96f50653 100644 --- a/src/ProtocolBuffers/UninitializedMessageException.cs +++ b/src/ProtocolBuffers/UninitializedMessageException.cs @@ -1,170 +1,170 @@ -#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.Text; -#if !LITE -using Google.ProtocolBuffers.Collections; -using Google.ProtocolBuffers.Descriptors; -#endif - -namespace Google.ProtocolBuffers { - /// <summary> - /// TODO(jonskeet): Write summary text. - /// </summary> - public sealed class UninitializedMessageException : Exception { - - private readonly IList<string> missingFields; - - private UninitializedMessageException(IList<string> missingFields) - : base(BuildDescription(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 - /// is a full path to a field, e.g. "foo.bar[5].baz" - /// </summary> - public IList<string> MissingFields { - get { return missingFields; } - } - - /// <summary> - /// Converts this exception into an InvalidProtocolBufferException. - /// When a parsed message is missing required fields, this should be thrown - /// instead of UninitializedMessageException. - /// </summary> - public InvalidProtocolBufferException AsInvalidProtocolBufferException() { - return new InvalidProtocolBufferException(Message); - } - - /// <summary> - /// Constructs the description string for a given list of missing fields. - /// </summary> - private static string BuildDescription(IEnumerable<string> missingFields) { - StringBuilder description = new StringBuilder("Message missing required fields: "); - bool first = true; - foreach(string field in missingFields) { - if (first) { - first = false; - } else { - description.Append(", "); - } - description.Append(field); - } - return description.ToString(); - } - - /// <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> - private static IList<String> FindMissingFields(IMessage message) { - List<String> results = new List<String>(); - FindMissingFields(message, "", results); - return results; - } - - /// <summary> - /// Recursive helper implementing FindMissingFields. - /// </summary> - private static void FindMissingFields(IMessage message, String prefix, List<String> results) { - foreach (FieldDescriptor field in message.DescriptorForType.Fields) { - if (field.IsRequired && !message.HasField(field)) { - results.Add(prefix + field.Name); - } - } - - foreach (KeyValuePair<FieldDescriptor, object> entry in message.AllFields) { - FieldDescriptor field = entry.Key; - object value = entry.Value; - - if (field.MappedType == MappedType.Message) { - if (field.IsRepeated) { - int i = 0; - foreach (object element in (IEnumerable) value) { - if (element is IMessage) { - FindMissingFields((IMessage)element, SubMessagePrefix(prefix, field, i++), results); - } else { - results.Add(prefix + field.Name); - } - } - } else { - if (message.HasField(field)) { - if (value is IMessage) { - FindMissingFields((IMessage)value, SubMessagePrefix(prefix, field, -1), results); - } else { - results.Add(prefix + field.Name); - } - } - } - } - } - } - - private static String SubMessagePrefix(String prefix, FieldDescriptor field, int index) { - StringBuilder result = new StringBuilder(prefix); - if (field.IsExtension) { - result.Append('(') - .Append(field.FullName) - .Append(')'); - } else { - result.Append(field.Name); - } - if (index != -1) { - result.Append('[') - .Append(index) - .Append(']'); - } - result.Append('.'); - return result.ToString(); - } -#endif - } -} +#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.Text;
+#if !LITE
+using Google.ProtocolBuffers.Collections;
+using Google.ProtocolBuffers.Descriptors;
+#endif
+
+namespace Google.ProtocolBuffers {
+ /// <summary>
+ /// TODO(jonskeet): Write summary text.
+ /// </summary>
+ public sealed class UninitializedMessageException : Exception {
+
+ private readonly IList<string> missingFields;
+
+ private UninitializedMessageException(IList<string> missingFields)
+ : base(BuildDescription(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
+ /// is a full path to a field, e.g. "foo.bar[5].baz"
+ /// </summary>
+ public IList<string> MissingFields {
+ get { return missingFields; }
+ }
+
+ /// <summary>
+ /// Converts this exception into an InvalidProtocolBufferException.
+ /// When a parsed message is missing required fields, this should be thrown
+ /// instead of UninitializedMessageException.
+ /// </summary>
+ public InvalidProtocolBufferException AsInvalidProtocolBufferException() {
+ return new InvalidProtocolBufferException(Message);
+ }
+
+ /// <summary>
+ /// Constructs the description string for a given list of missing fields.
+ /// </summary>
+ private static string BuildDescription(IEnumerable<string> missingFields) {
+ StringBuilder description = new StringBuilder("Message missing required fields: ");
+ bool first = true;
+ foreach(string field in missingFields) {
+ if (first) {
+ first = false;
+ } else {
+ description.Append(", ");
+ }
+ description.Append(field);
+ }
+ return description.ToString();
+ }
+
+ /// <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>
+ private static IList<String> FindMissingFields(IMessage message) {
+ List<String> results = new List<String>();
+ FindMissingFields(message, "", results);
+ return results;
+ }
+
+ /// <summary>
+ /// Recursive helper implementing FindMissingFields.
+ /// </summary>
+ private static void FindMissingFields(IMessage message, String prefix, List<String> results) {
+ foreach (FieldDescriptor field in message.DescriptorForType.Fields) {
+ if (field.IsRequired && !message.HasField(field)) {
+ results.Add(prefix + field.Name);
+ }
+ }
+
+ foreach (KeyValuePair<FieldDescriptor, object> entry in message.AllFields) {
+ FieldDescriptor field = entry.Key;
+ object value = entry.Value;
+
+ if (field.MappedType == MappedType.Message) {
+ if (field.IsRepeated) {
+ int i = 0;
+ foreach (object element in (IEnumerable) value) {
+ if (element is IMessage) {
+ FindMissingFields((IMessage)element, SubMessagePrefix(prefix, field, i++), results);
+ } else {
+ results.Add(prefix + field.Name);
+ }
+ }
+ } else {
+ if (message.HasField(field)) {
+ if (value is IMessage) {
+ FindMissingFields((IMessage)value, SubMessagePrefix(prefix, field, -1), results);
+ } else {
+ results.Add(prefix + field.Name);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private static String SubMessagePrefix(String prefix, FieldDescriptor field, int index) {
+ StringBuilder result = new StringBuilder(prefix);
+ if (field.IsExtension) {
+ result.Append('(')
+ .Append(field.FullName)
+ .Append(')');
+ } else {
+ result.Append(field.Name);
+ }
+ if (index != -1) {
+ result.Append('[')
+ .Append(index)
+ .Append(']');
+ }
+ result.Append('.');
+ return result.ToString();
+ }
+#endif
+ }
+}
diff --git a/src/ProtocolBuffers/UnknownField.cs b/src/ProtocolBuffers/UnknownField.cs index ad1b59b4..7aed8473 100644 --- a/src/ProtocolBuffers/UnknownField.cs +++ b/src/ProtocolBuffers/UnknownField.cs @@ -1,371 +1,371 @@ -#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.ObjectModel; -using Google.ProtocolBuffers.Collections; - -namespace Google.ProtocolBuffers { - /// <summary> - /// Represents a single field in an UnknownFieldSet. - /// - /// An UnknownField consists of five lists of values. The lists correspond - /// to the five "wire types" used in the protocol buffer binary format. - /// The wire type of each field can be determined from the encoded form alone, - /// without knowing the field's declared type. So, we are able to parse - /// unknown values at least this far and separate them. Normally, only one - /// of the five lists will contain any values, since it is impossible to - /// define a valid message type that declares two different types for the - /// same field number. However, the code is designed to allow for the case - /// where the same unknown field number is encountered using multiple different - /// wire types. - /// - /// UnknownField is an immutable class. To construct one, you must use an - /// UnknownField.Builder. - /// </summary> - public sealed class UnknownField { - - private static readonly UnknownField defaultInstance = CreateBuilder().Build(); - private readonly ReadOnlyCollection<ulong> varintList; - private readonly ReadOnlyCollection<uint> fixed32List; - private readonly ReadOnlyCollection<ulong> fixed64List; - private readonly ReadOnlyCollection<ByteString> lengthDelimitedList; - private readonly ReadOnlyCollection<UnknownFieldSet> groupList; - - private UnknownField(ReadOnlyCollection<ulong> varintList, - ReadOnlyCollection<uint> fixed32List, - ReadOnlyCollection<ulong> fixed64List, - ReadOnlyCollection<ByteString> lengthDelimitedList, - ReadOnlyCollection<UnknownFieldSet> groupList) { - this.varintList = varintList; - this.fixed32List = fixed32List; - this.fixed64List = fixed64List; - this.lengthDelimitedList = lengthDelimitedList; - this.groupList = groupList; - } - - public static UnknownField DefaultInstance { - get { return defaultInstance; } - } - - /// <summary> - /// The list of varint values for this field. - /// </summary> - public IList<ulong> VarintList { - get { return varintList; } - } - - /// <summary> - /// The list of fixed32 values for this field. - /// </summary> - public IList<uint> Fixed32List { - get { return fixed32List; } - } - - /// <summary> - /// The list of fixed64 values for this field. - /// </summary> - public IList<ulong> Fixed64List { - get { return fixed64List; } - } - - /// <summary> - /// The list of length-delimited values for this field. - /// </summary> - public IList<ByteString> LengthDelimitedList { - get { return lengthDelimitedList; } - } - - /// <summary> - /// The list of embedded group values for this field. These - /// are represented using UnknownFieldSets rather than Messages - /// since the group's type is presumably unknown. - /// </summary> - public IList<UnknownFieldSet> GroupList { - get { return groupList; } - } - - public override bool Equals(object other) { - if (ReferenceEquals(this, other)) { - return true; - } - UnknownField otherField = other as UnknownField; - return otherField != null - && Lists.Equals(varintList, otherField.varintList) - && Lists.Equals(fixed32List, otherField.fixed32List) - && Lists.Equals(fixed64List, otherField.fixed64List) - && Lists.Equals(lengthDelimitedList, otherField.lengthDelimitedList) - && Lists.Equals(groupList, otherField.groupList); - } - - public override int GetHashCode() { - int hash = 43; - hash = hash * 47 + Lists.GetHashCode(varintList); - hash = hash * 47 + Lists.GetHashCode(fixed32List); - hash = hash * 47 + Lists.GetHashCode(fixed64List); - hash = hash * 47 + Lists.GetHashCode(lengthDelimitedList); - hash = hash * 47 + Lists.GetHashCode(groupList); - return hash; - } - - /// <summary> - /// Constructs a new Builder. - /// </summary> - public static Builder CreateBuilder() { - return new Builder(); - } - - /// <summary> - /// Constructs a new Builder and initializes it to a copy of <paramref name="copyFrom"/>. - /// </summary> - public static Builder CreateBuilder(UnknownField copyFrom) { - return new Builder().MergeFrom(copyFrom); - } - - /// <summary> - /// Serializes the field, including the field number, and writes it to - /// <paramref name="output"/>. - /// </summary> - public void WriteTo(int fieldNumber, CodedOutputStream output) { - foreach (ulong value in varintList) { - output.WriteUInt64(fieldNumber, value); - } - foreach (uint value in fixed32List) { - output.WriteFixed32(fieldNumber, value); - } - foreach (ulong value in fixed64List) { - output.WriteFixed64(fieldNumber, value); - } - foreach (ByteString value in lengthDelimitedList) { - output.WriteBytes(fieldNumber, value); - } - foreach (UnknownFieldSet value in groupList) { -#pragma warning disable 0612 - output.WriteUnknownGroup(fieldNumber, value); -#pragma warning restore 0612 - } - } - - /// <summary> - /// Computes the number of bytes required to encode this field, including field - /// number. - /// </summary> - public int GetSerializedSize(int fieldNumber) { - int result = 0; - foreach (ulong value in varintList) { - result += CodedOutputStream.ComputeUInt64Size(fieldNumber, value); - } - foreach (uint value in fixed32List) { - result += CodedOutputStream.ComputeFixed32Size(fieldNumber, value); - } - foreach (ulong value in fixed64List) { - result += CodedOutputStream.ComputeFixed64Size(fieldNumber, value); - } - foreach (ByteString value in lengthDelimitedList) { - 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; - } - - /// <summary> - /// Serializes the length-delimited values of the field, including field - /// number, and writes them to <paramref name="output"/> using the MessageSet wire format. - /// </summary> - /// <param name="fieldNumber"></param> - /// <param name="output"></param> - public void WriteAsMessageSetExtensionTo(int fieldNumber, CodedOutputStream output) { - foreach (ByteString value in lengthDelimitedList) { - output.WriteRawMessageSetExtension(fieldNumber, value); - } - } - - /// <summary> - /// Get the number of bytes required to encode this field, incuding field number, - /// using the MessageSet wire format. - /// </summary> - public int GetSerializedSizeAsMessageSetExtension(int fieldNumber) { - int result = 0; - foreach (ByteString value in lengthDelimitedList) { - result += CodedOutputStream.ComputeRawMessageSetExtensionSize(fieldNumber, value); - } - return result; - } - - /// <summary> - /// Used to build instances of UnknownField. - /// </summary> - public sealed class Builder { - - private List<ulong> varintList; - private List<uint> fixed32List; - private List<ulong> fixed64List; - private List<ByteString> lengthDelimitedList; - private List<UnknownFieldSet> groupList; - - /// <summary> - /// Builds the field. After building, the builder is reset to an empty - /// state. (This is actually easier than making it unusable.) - /// </summary> - public UnknownField Build() { - return new UnknownField(MakeReadOnly(ref varintList), - MakeReadOnly(ref fixed32List), - MakeReadOnly(ref fixed64List), - MakeReadOnly(ref lengthDelimitedList), - MakeReadOnly(ref groupList)); - } - - /// <summary> - /// Merge the values in <paramref name="other" /> into this field. For each list - /// of values, <paramref name="other"/>'s values are append to the ones in this - /// field. - /// </summary> - public Builder MergeFrom(UnknownField other) { - varintList = AddAll(varintList, other.VarintList); - fixed32List = AddAll(fixed32List, other.Fixed32List); - fixed64List = AddAll(fixed64List, other.Fixed64List); - lengthDelimitedList = AddAll(lengthDelimitedList, other.LengthDelimitedList); - groupList = AddAll(groupList, other.GroupList); - return this; - } - - /// <summary> - /// Returns a new list containing all of the given specified values from - /// both the <paramref name="current"/> and <paramref name="extras"/> lists. - /// If <paramref name="current" /> is null and <paramref name="extras"/> is empty, - /// null is returned. Otherwise, either a new list is created (if <paramref name="current" /> - /// is null) or the elements of <paramref name="extras"/> are added to <paramref name="current" />. - /// </summary> - private static List<T> AddAll<T>(List<T> current, IList<T> extras) - { - if (extras.Count == 0) { - return current; - } - if (current == null) { - current = new List<T>(extras); - } else { - current.AddRange(extras); - } - return current; - } - - /// <summary> - /// Clears the contents of this builder. - /// </summary> - public Builder Clear() { - varintList = null; - fixed32List = null; - fixed64List = null; - lengthDelimitedList = null; - groupList = null; - return this; - } - - /// <summary> - /// Adds a varint value. - /// </summary> - [CLSCompliant(false)] - public Builder AddVarint(ulong value) { - varintList = Add(varintList, value); - return this; - } - - /// <summary> - /// Adds a fixed32 value. - /// </summary> - [CLSCompliant(false)] - public Builder AddFixed32(uint value) { - fixed32List = Add(fixed32List, value); - return this; - } - - /// <summary> - /// Adds a fixed64 value. - /// </summary> - [CLSCompliant(false)] - public Builder AddFixed64(ulong value) { - fixed64List = Add(fixed64List, value); - return this; - } - - /// <summary> - /// Adds a length-delimited value. - /// </summary> - public Builder AddLengthDelimited(ByteString value) { - lengthDelimitedList = Add(lengthDelimitedList, value); - return this; - } - - /// <summary> - /// Adds an embedded group. - /// </summary> - /// <param name="value"></param> - /// <returns></returns> - public Builder AddGroup(UnknownFieldSet value) { - groupList = Add(groupList, value); - return this; - } - - /// <summary> - /// Adds <paramref name="value"/> to the <paramref name="list"/>, creating - /// a new list if <paramref name="list"/> is null. The list is returned - either - /// the original reference or the new list. - /// </summary> - private static List<T> Add<T>(List<T> list, T value) { - if (list == null) { - list = new List<T>(); - } - list.Add(value); - return list; - } - - /// <summary> - /// Returns a read-only version of the given IList, and clears - /// the field used for <paramref name="list"/>. If the value - /// is null, an empty list is produced using Lists.Empty. - /// </summary> - /// <returns></returns> - private static ReadOnlyCollection<T> MakeReadOnly<T>(ref List<T> list) { - ReadOnlyCollection<T> ret = list == null ? Lists<T>.Empty : new ReadOnlyCollection<T>(list); - list = null; - return ret; - } - } - } -} +#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.ObjectModel;
+using Google.ProtocolBuffers.Collections;
+
+namespace Google.ProtocolBuffers {
+ /// <summary>
+ /// Represents a single field in an UnknownFieldSet.
+ ///
+ /// An UnknownField consists of five lists of values. The lists correspond
+ /// to the five "wire types" used in the protocol buffer binary format.
+ /// The wire type of each field can be determined from the encoded form alone,
+ /// without knowing the field's declared type. So, we are able to parse
+ /// unknown values at least this far and separate them. Normally, only one
+ /// of the five lists will contain any values, since it is impossible to
+ /// define a valid message type that declares two different types for the
+ /// same field number. However, the code is designed to allow for the case
+ /// where the same unknown field number is encountered using multiple different
+ /// wire types.
+ ///
+ /// UnknownField is an immutable class. To construct one, you must use an
+ /// UnknownField.Builder.
+ /// </summary>
+ public sealed class UnknownField {
+
+ private static readonly UnknownField defaultInstance = CreateBuilder().Build();
+ private readonly ReadOnlyCollection<ulong> varintList;
+ private readonly ReadOnlyCollection<uint> fixed32List;
+ private readonly ReadOnlyCollection<ulong> fixed64List;
+ private readonly ReadOnlyCollection<ByteString> lengthDelimitedList;
+ private readonly ReadOnlyCollection<UnknownFieldSet> groupList;
+
+ private UnknownField(ReadOnlyCollection<ulong> varintList,
+ ReadOnlyCollection<uint> fixed32List,
+ ReadOnlyCollection<ulong> fixed64List,
+ ReadOnlyCollection<ByteString> lengthDelimitedList,
+ ReadOnlyCollection<UnknownFieldSet> groupList) {
+ this.varintList = varintList;
+ this.fixed32List = fixed32List;
+ this.fixed64List = fixed64List;
+ this.lengthDelimitedList = lengthDelimitedList;
+ this.groupList = groupList;
+ }
+
+ public static UnknownField DefaultInstance {
+ get { return defaultInstance; }
+ }
+
+ /// <summary>
+ /// The list of varint values for this field.
+ /// </summary>
+ public IList<ulong> VarintList {
+ get { return varintList; }
+ }
+
+ /// <summary>
+ /// The list of fixed32 values for this field.
+ /// </summary>
+ public IList<uint> Fixed32List {
+ get { return fixed32List; }
+ }
+
+ /// <summary>
+ /// The list of fixed64 values for this field.
+ /// </summary>
+ public IList<ulong> Fixed64List {
+ get { return fixed64List; }
+ }
+
+ /// <summary>
+ /// The list of length-delimited values for this field.
+ /// </summary>
+ public IList<ByteString> LengthDelimitedList {
+ get { return lengthDelimitedList; }
+ }
+
+ /// <summary>
+ /// The list of embedded group values for this field. These
+ /// are represented using UnknownFieldSets rather than Messages
+ /// since the group's type is presumably unknown.
+ /// </summary>
+ public IList<UnknownFieldSet> GroupList {
+ get { return groupList; }
+ }
+
+ public override bool Equals(object other) {
+ if (ReferenceEquals(this, other)) {
+ return true;
+ }
+ UnknownField otherField = other as UnknownField;
+ return otherField != null
+ && Lists.Equals(varintList, otherField.varintList)
+ && Lists.Equals(fixed32List, otherField.fixed32List)
+ && Lists.Equals(fixed64List, otherField.fixed64List)
+ && Lists.Equals(lengthDelimitedList, otherField.lengthDelimitedList)
+ && Lists.Equals(groupList, otherField.groupList);
+ }
+
+ public override int GetHashCode() {
+ int hash = 43;
+ hash = hash * 47 + Lists.GetHashCode(varintList);
+ hash = hash * 47 + Lists.GetHashCode(fixed32List);
+ hash = hash * 47 + Lists.GetHashCode(fixed64List);
+ hash = hash * 47 + Lists.GetHashCode(lengthDelimitedList);
+ hash = hash * 47 + Lists.GetHashCode(groupList);
+ return hash;
+ }
+
+ /// <summary>
+ /// Constructs a new Builder.
+ /// </summary>
+ public static Builder CreateBuilder() {
+ return new Builder();
+ }
+
+ /// <summary>
+ /// Constructs a new Builder and initializes it to a copy of <paramref name="copyFrom"/>.
+ /// </summary>
+ public static Builder CreateBuilder(UnknownField copyFrom) {
+ return new Builder().MergeFrom(copyFrom);
+ }
+
+ /// <summary>
+ /// Serializes the field, including the field number, and writes it to
+ /// <paramref name="output"/>.
+ /// </summary>
+ public void WriteTo(int fieldNumber, CodedOutputStream output) {
+ foreach (ulong value in varintList) {
+ output.WriteUInt64(fieldNumber, value);
+ }
+ foreach (uint value in fixed32List) {
+ output.WriteFixed32(fieldNumber, value);
+ }
+ foreach (ulong value in fixed64List) {
+ output.WriteFixed64(fieldNumber, value);
+ }
+ foreach (ByteString value in lengthDelimitedList) {
+ output.WriteBytes(fieldNumber, value);
+ }
+ foreach (UnknownFieldSet value in groupList) {
+#pragma warning disable 0612
+ output.WriteUnknownGroup(fieldNumber, value);
+#pragma warning restore 0612
+ }
+ }
+
+ /// <summary>
+ /// Computes the number of bytes required to encode this field, including field
+ /// number.
+ /// </summary>
+ public int GetSerializedSize(int fieldNumber) {
+ int result = 0;
+ foreach (ulong value in varintList) {
+ result += CodedOutputStream.ComputeUInt64Size(fieldNumber, value);
+ }
+ foreach (uint value in fixed32List) {
+ result += CodedOutputStream.ComputeFixed32Size(fieldNumber, value);
+ }
+ foreach (ulong value in fixed64List) {
+ result += CodedOutputStream.ComputeFixed64Size(fieldNumber, value);
+ }
+ foreach (ByteString value in lengthDelimitedList) {
+ 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;
+ }
+
+ /// <summary>
+ /// Serializes the length-delimited values of the field, including field
+ /// number, and writes them to <paramref name="output"/> using the MessageSet wire format.
+ /// </summary>
+ /// <param name="fieldNumber"></param>
+ /// <param name="output"></param>
+ public void WriteAsMessageSetExtensionTo(int fieldNumber, CodedOutputStream output) {
+ foreach (ByteString value in lengthDelimitedList) {
+ output.WriteRawMessageSetExtension(fieldNumber, value);
+ }
+ }
+
+ /// <summary>
+ /// Get the number of bytes required to encode this field, incuding field number,
+ /// using the MessageSet wire format.
+ /// </summary>
+ public int GetSerializedSizeAsMessageSetExtension(int fieldNumber) {
+ int result = 0;
+ foreach (ByteString value in lengthDelimitedList) {
+ result += CodedOutputStream.ComputeRawMessageSetExtensionSize(fieldNumber, value);
+ }
+ return result;
+ }
+
+ /// <summary>
+ /// Used to build instances of UnknownField.
+ /// </summary>
+ public sealed class Builder {
+
+ private List<ulong> varintList;
+ private List<uint> fixed32List;
+ private List<ulong> fixed64List;
+ private List<ByteString> lengthDelimitedList;
+ private List<UnknownFieldSet> groupList;
+
+ /// <summary>
+ /// Builds the field. After building, the builder is reset to an empty
+ /// state. (This is actually easier than making it unusable.)
+ /// </summary>
+ public UnknownField Build() {
+ return new UnknownField(MakeReadOnly(ref varintList),
+ MakeReadOnly(ref fixed32List),
+ MakeReadOnly(ref fixed64List),
+ MakeReadOnly(ref lengthDelimitedList),
+ MakeReadOnly(ref groupList));
+ }
+
+ /// <summary>
+ /// Merge the values in <paramref name="other" /> into this field. For each list
+ /// of values, <paramref name="other"/>'s values are append to the ones in this
+ /// field.
+ /// </summary>
+ public Builder MergeFrom(UnknownField other) {
+ varintList = AddAll(varintList, other.VarintList);
+ fixed32List = AddAll(fixed32List, other.Fixed32List);
+ fixed64List = AddAll(fixed64List, other.Fixed64List);
+ lengthDelimitedList = AddAll(lengthDelimitedList, other.LengthDelimitedList);
+ groupList = AddAll(groupList, other.GroupList);
+ return this;
+ }
+
+ /// <summary>
+ /// Returns a new list containing all of the given specified values from
+ /// both the <paramref name="current"/> and <paramref name="extras"/> lists.
+ /// If <paramref name="current" /> is null and <paramref name="extras"/> is empty,
+ /// null is returned. Otherwise, either a new list is created (if <paramref name="current" />
+ /// is null) or the elements of <paramref name="extras"/> are added to <paramref name="current" />.
+ /// </summary>
+ private static List<T> AddAll<T>(List<T> current, IList<T> extras)
+ {
+ if (extras.Count == 0) {
+ return current;
+ }
+ if (current == null) {
+ current = new List<T>(extras);
+ } else {
+ current.AddRange(extras);
+ }
+ return current;
+ }
+
+ /// <summary>
+ /// Clears the contents of this builder.
+ /// </summary>
+ public Builder Clear() {
+ varintList = null;
+ fixed32List = null;
+ fixed64List = null;
+ lengthDelimitedList = null;
+ groupList = null;
+ return this;
+ }
+
+ /// <summary>
+ /// Adds a varint value.
+ /// </summary>
+ [CLSCompliant(false)]
+ public Builder AddVarint(ulong value) {
+ varintList = Add(varintList, value);
+ return this;
+ }
+
+ /// <summary>
+ /// Adds a fixed32 value.
+ /// </summary>
+ [CLSCompliant(false)]
+ public Builder AddFixed32(uint value) {
+ fixed32List = Add(fixed32List, value);
+ return this;
+ }
+
+ /// <summary>
+ /// Adds a fixed64 value.
+ /// </summary>
+ [CLSCompliant(false)]
+ public Builder AddFixed64(ulong value) {
+ fixed64List = Add(fixed64List, value);
+ return this;
+ }
+
+ /// <summary>
+ /// Adds a length-delimited value.
+ /// </summary>
+ public Builder AddLengthDelimited(ByteString value) {
+ lengthDelimitedList = Add(lengthDelimitedList, value);
+ return this;
+ }
+
+ /// <summary>
+ /// Adds an embedded group.
+ /// </summary>
+ /// <param name="value"></param>
+ /// <returns></returns>
+ public Builder AddGroup(UnknownFieldSet value) {
+ groupList = Add(groupList, value);
+ return this;
+ }
+
+ /// <summary>
+ /// Adds <paramref name="value"/> to the <paramref name="list"/>, creating
+ /// a new list if <paramref name="list"/> is null. The list is returned - either
+ /// the original reference or the new list.
+ /// </summary>
+ private static List<T> Add<T>(List<T> list, T value) {
+ if (list == null) {
+ list = new List<T>();
+ }
+ list.Add(value);
+ return list;
+ }
+
+ /// <summary>
+ /// Returns a read-only version of the given IList, and clears
+ /// the field used for <paramref name="list"/>. If the value
+ /// is null, an empty list is produced using Lists.Empty.
+ /// </summary>
+ /// <returns></returns>
+ private static ReadOnlyCollection<T> MakeReadOnly<T>(ref List<T> list) {
+ ReadOnlyCollection<T> ret = list == null ? Lists<T>.Empty : new ReadOnlyCollection<T>(list);
+ list = null;
+ return ret;
+ }
+ }
+ }
+}
diff --git a/src/ProtocolBuffers/UnknownFieldSet.cs b/src/ProtocolBuffers/UnknownFieldSet.cs index 594ae8bd..be773e41 100644 --- a/src/ProtocolBuffers/UnknownFieldSet.cs +++ b/src/ProtocolBuffers/UnknownFieldSet.cs @@ -1,768 +1,768 @@ -#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; -using Google.ProtocolBuffers.Collections; -using Google.ProtocolBuffers.Descriptors; -using Google.ProtocolBuffers.DescriptorProtos; - -namespace Google.ProtocolBuffers { - /// <summary> - /// Used to keep track of fields which were seen when parsing a protocol message - /// but whose field numbers or types are unrecognized. This most frequently - /// occurs when new fields are added to a message type and then messages containing - /// those fields are read by old software that was built before the new types were - /// added. - /// - /// Every message contains an UnknownFieldSet. - /// - /// Most users will never need to use this class directly. - /// </summary> - public sealed class UnknownFieldSet : IMessageLite { - - private static readonly UnknownFieldSet defaultInstance = new UnknownFieldSet(new Dictionary<int, UnknownField>()); - - private readonly IDictionary<int, UnknownField> fields; - - private UnknownFieldSet(IDictionary<int, UnknownField> fields) { - this.fields = fields; - } - - /// <summary> - /// Creates a new unknown field set builder. - /// </summary> - public static Builder CreateBuilder() { - return new Builder(); - } - - /// <summary> - /// Creates a new unknown field set builder - /// and initialize it from <paramref name="original"/>. - /// </summary> - public static Builder CreateBuilder(UnknownFieldSet original) { - return new Builder().MergeFrom(original); - } - - public static UnknownFieldSet DefaultInstance { - get { return defaultInstance; } - } - - /// <summary> - /// Returns a read-only view of the mapping from field numbers to values. - /// </summary> - public IDictionary<int, UnknownField> FieldDictionary { - get { return Dictionaries.AsReadOnly(fields); } - } - - /// <summary> - /// Checks whether or not the given field number is present in the set. - /// </summary> - public bool HasField(int field) { - return fields.ContainsKey(field); - } - - /// <summary> - /// Fetches a field by number, returning an empty field if not present. - /// Never returns null. - /// </summary> - public UnknownField this[int number] { - get { - UnknownField ret; - if (!fields.TryGetValue(number, out ret)) { - ret = UnknownField.DefaultInstance; - } - return ret; - } - } - - /// <summary> - /// Serializes the set and writes it to <paramref name="output"/>. - /// </summary> - public void WriteTo(CodedOutputStream output) { - foreach (KeyValuePair<int, UnknownField> entry in fields) { - entry.Value.WriteTo(entry.Key, output); - } - } - - /// <summary> - /// Gets the number of bytes required to encode this set. - /// </summary> - public int SerializedSize { - get { - int result = 0; - foreach (KeyValuePair<int, UnknownField> entry in fields) { - result += entry.Value.GetSerializedSize(entry.Key); - } - return result; - } - } - - /// <summary> - /// Converts the set to a string in protocol buffer text format. This - /// is just a trivial wrapper around TextFormat.PrintToString. - /// </summary> - public override String ToString() { - return TextFormat.PrintToString(this); - } - - /// <summary> - /// 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> - /// <returns></returns> - public ByteString ToByteString() { - ByteString.CodedBuilder codedBuilder = new ByteString.CodedBuilder(SerializedSize); - WriteTo(codedBuilder.CodedOutput); - return codedBuilder.Build(); - } - - /// <summary> - /// Serializes the message to a byte array and returns it. This is - /// just a trivial wrapper around WriteTo(CodedOutputStream). - /// </summary> - /// <returns></returns> - public byte[] ToByteArray() { - byte[] data = new byte[SerializedSize]; - CodedOutputStream output = CodedOutputStream.CreateInstance(data); - WriteTo(output); - output.CheckNoSpaceLeft(); - return data; - } - - /// <summary> - /// Serializes the message and writes it to <paramref name="output"/>. This is - /// just a trivial wrapper around WriteTo(CodedOutputStream). - /// </summary> - /// <param name="output"></param> - public void WriteTo(Stream output) { - CodedOutputStream codedOutput = CodedOutputStream.CreateInstance(output); - WriteTo(codedOutput); - codedOutput.Flush(); - } - - /// <summary> - /// Serializes the set and writes it to <paramref name="output"/> using - /// the MessageSet wire format. - /// </summary> - public void WriteAsMessageSetTo(CodedOutputStream output) { - foreach (KeyValuePair<int, UnknownField> entry in fields) { - entry.Value.WriteAsMessageSetExtensionTo(entry.Key, output); - } - } - - /// <summary> - /// Gets the number of bytes required to encode this set using the MessageSet - /// wire format. - /// </summary> - public int SerializedSizeAsMessageSet { - get { - int result = 0; - foreach (KeyValuePair<int, UnknownField> entry in fields) { - result += entry.Value.GetSerializedSizeAsMessageSetExtension(entry.Key); - } - return result; - } - } - - public override bool Equals(object other) { - if (ReferenceEquals(this, other)) { - return true; - } - UnknownFieldSet otherSet = other as UnknownFieldSet; - return otherSet != null && Dictionaries.Equals(fields, otherSet.fields); - } - - public override int GetHashCode() { - return Dictionaries.GetHashCode(fields); - } - - /// <summary> - /// Parses an UnknownFieldSet from the given input. - /// </summary> - public static UnknownFieldSet ParseFrom(CodedInputStream input) { - return CreateBuilder().MergeFrom(input).Build(); - } - - /// <summary> - /// Parses an UnknownFieldSet from the given data. - /// </summary> - public static UnknownFieldSet ParseFrom(ByteString data) { - return CreateBuilder().MergeFrom(data).Build(); - } - - /// <summary> - /// Parses an UnknownFieldSet from the given data. - /// </summary> - public static UnknownFieldSet ParseFrom(byte[] data) { - return CreateBuilder().MergeFrom(data).Build(); - } - - /// <summary> - /// Parses an UnknownFieldSet from the given input. - /// </summary> - public static UnknownFieldSet ParseFrom(Stream input) { - return CreateBuilder().MergeFrom(input).Build(); - } - - #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 : 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; - // 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). - private int lastFieldNumber; - private UnknownField.Builder lastField; - - internal Builder() { - fields = new SortedList<int, UnknownField>(); - } - - internal Builder(IDictionary<int, UnknownField> dictionary) { - fields = new SortedList<int, UnknownField>(dictionary); - } - - /// <summary> - /// Returns a field builder for the specified field number, including any values - /// which already exist. - /// </summary> - private UnknownField.Builder GetFieldBuilder(int number) { - if (lastField != null) { - if (number == lastFieldNumber) { - return lastField; - } - // Note: AddField() will reset lastField and lastFieldNumber. - AddField(lastFieldNumber, lastField.Build()); - } - if (number == 0) { - return null; - } - - lastField = UnknownField.CreateBuilder(); - UnknownField existing; - if (fields.TryGetValue(number, out existing)) { - lastField.MergeFrom(existing); - } - lastFieldNumber = number; - return lastField; - } - - /// <summary> - /// Build the UnknownFieldSet and return it. Once this method has been called, - /// this instance will no longer be usable. Calling any method after this - /// will throw a NullReferenceException. - /// </summary> - public UnknownFieldSet Build() { - GetFieldBuilder(0); // Force lastField to be built. - UnknownFieldSet result = fields.Count == 0 ? DefaultInstance : new UnknownFieldSet(fields); - fields = null; - return result; - } - - /// <summary> - /// Adds a field to the set. If a field with the same number already exists, it - /// is replaced. - /// </summary> - public Builder AddField(int number, UnknownField field) { - if (number == 0) { - throw new ArgumentOutOfRangeException("number", "Zero is not a valid field number."); - } - if (lastField != null && lastFieldNumber == number) { - // Discard this. - lastField = null; - lastFieldNumber = 0; - } - fields[number] = field; - return this; - } - - /// <summary> - /// Resets the builder to an empty set. - /// </summary> - public Builder Clear() { - fields.Clear(); - lastFieldNumber = 0; - lastField = null; - return this; - } - - /// <summary> - /// Parse an entire message from <paramref name="input"/> and merge - /// its fields into this set. - /// </summary> - public Builder MergeFrom(CodedInputStream input) { - while (true) { - uint tag = input.ReadTag(); - if (tag == 0 || !MergeFieldFrom(tag, input)) { - break; - } - } - return this; - } - - /// <summary> - /// Parse a single field from <paramref name="input"/> and merge it - /// into this set. - /// </summary> - /// <param name="tag">The field's tag number, which was already parsed.</param> - /// <param name="input">The coded input stream containing the field</param> - /// <returns>false if the tag is an "end group" tag, true otherwise</returns> - [CLSCompliant(false)] - public bool MergeFieldFrom(uint tag, CodedInputStream input) { - int number = WireFormat.GetTagFieldNumber(tag); - switch (WireFormat.GetTagWireType(tag)) { - case WireFormat.WireType.Varint: - GetFieldBuilder(number).AddVarint(input.ReadUInt64()); - return true; - case WireFormat.WireType.Fixed64: - GetFieldBuilder(number).AddFixed64(input.ReadFixed64()); - return true; - case WireFormat.WireType.LengthDelimited: - GetFieldBuilder(number).AddLengthDelimited(input.ReadBytes()); - return true; - case WireFormat.WireType.StartGroup: { - Builder subBuilder = CreateBuilder(); -#pragma warning disable 0612 - input.ReadUnknownGroup(number, subBuilder); -#pragma warning restore 0612 - GetFieldBuilder(number).AddGroup(subBuilder.Build()); - return true; - } - case WireFormat.WireType.EndGroup: - return false; - case WireFormat.WireType.Fixed32: - GetFieldBuilder(number).AddFixed32(input.ReadFixed32()); - return true; - default: - throw InvalidProtocolBufferException.InvalidWireType(); - } - } - - /// <summary> - /// Parses <paramref name="input"/> as an UnknownFieldSet and merge it - /// with the set being built. This is just a small wrapper around - /// MergeFrom(CodedInputStream). - /// </summary> - public Builder MergeFrom(Stream input) { - CodedInputStream codedInput = CodedInputStream.CreateInstance(input); - MergeFrom(codedInput); - codedInput.CheckLastTagWas(0); - return this; - } - - /// <summary> - /// Parses <paramref name="data"/> as an UnknownFieldSet and merge it - /// with the set being built. This is just a small wrapper around - /// MergeFrom(CodedInputStream). - /// </summary> - public Builder MergeFrom(ByteString data) { - CodedInputStream input = data.CreateCodedInput(); - MergeFrom(input); - input.CheckLastTagWas(0); - return this; - } - - /// <summary> - /// Parses <paramref name="data"/> as an UnknownFieldSet and merge it - /// with the set being built. This is just a small wrapper around - /// MergeFrom(CodedInputStream). - /// </summary> - public Builder MergeFrom(byte[] data) { - CodedInputStream input = CodedInputStream.CreateInstance(data); - MergeFrom(input); - input.CheckLastTagWas(0); - return this; - } - - /// <summary> - /// Convenience method for merging a new field containing a single varint - /// value. This is used in particular when an unknown enum value is - /// encountered. - /// </summary> - [CLSCompliant(false)] - public Builder MergeVarintField(int number, ulong value) { - if (number == 0) { - throw new ArgumentOutOfRangeException("number", "Zero is not a valid field number."); - } - GetFieldBuilder(number).AddVarint(value); - return this; - } - - /// <summary> - /// Merges the fields from <paramref name="other"/> into this set. - /// If a field number exists in both sets, the values in <paramref name="other"/> - /// will be appended to the values in this set. - /// </summary> - public Builder MergeFrom(UnknownFieldSet other) { - if (other != DefaultInstance) { - foreach(KeyValuePair<int, UnknownField> entry in other.fields) { - MergeField(entry.Key, entry.Value); - } - } - return this; - } - - /// <summary> - /// Checks if the given field number is present in the set. - /// </summary> - public bool HasField(int number) { - if (number == 0) { - throw new ArgumentOutOfRangeException("number", "Zero is not a valid field number."); - } - return number == lastFieldNumber || fields.ContainsKey(number); - } - - /// <summary> - /// Adds a field to the unknown field set. If a field with the same - /// number already exists, the two are merged. - /// </summary> - public Builder MergeField(int number, UnknownField field) { - if (number == 0) { - throw new ArgumentOutOfRangeException("number", "Zero is not a valid field number."); - } - if (HasField(number)) { - GetFieldBuilder(number).MergeFrom(field); - } else { - // Optimization: We could call getFieldBuilder(number).mergeFrom(field) - // in this case, but that would create a copy of the Field object. - // We'd rather reuse the one passed to us, so call AddField() instead. - AddField(number, field); - } - return this; - } - - internal void MergeFrom(CodedInputStream input, ExtensionRegistry extensionRegistry, IBuilder builder) { - while (true) { - uint tag = input.ReadTag(); - if (tag == 0) { - break; - } - - if (!MergeFieldFrom(input, extensionRegistry, builder, tag)) { - // end group tag - break; - } - } - } - - /// <summary> - /// Like <see cref="MergeFrom(CodedInputStream, ExtensionRegistry, IBuilder)" /> - /// but parses a single field. - /// </summary> - /// <param name="input">The input to read the field from</param> - /// <param name="extensionRegistry">Registry to use when an extension field is encountered</param> - /// <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, - ExtensionRegistry extensionRegistry, IBuilder builder, uint tag) { - - MessageDescriptor type = builder.DescriptorForType; - if (type.Options.MessageSetWireFormat && tag == WireFormat.MessageSetTag.ItemStart) { - MergeMessageSetExtensionFromCodedStream(input, extensionRegistry, builder); - return true; - } - - WireFormat.WireType wireType = WireFormat.GetTagWireType(tag); - int fieldNumber = WireFormat.GetTagFieldNumber(tag); - - FieldDescriptor field; - IMessageLite defaultFieldInstance = null; - - if (type.IsExtensionNumber(fieldNumber)) { - ExtensionInfo extension = extensionRegistry[type, fieldNumber]; - if (extension == null) { - field = null; - } else { - field = extension.Descriptor; - defaultFieldInstance = extension.DefaultInstance; - } - } else { - field = type.FindFieldByNumber(fieldNumber); - } - - // Unknown field or wrong wire type. Skip. - if (field == null || wireType != WireFormat.GetWireType(field)) { - return MergeFieldFrom(tag, input); - } - - if (field.IsPacked) { - int length = (int)input.ReadRawVarint32(); - int limit = input.PushLimit(length); - if (field.FieldType == FieldType.Enum) { - while (!input.ReachedLimit) { - int rawValue = input.ReadEnum(); - object value = field.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; - } - builder.WeakAddRepeatedField(field, value); - } - } else { - while (!input.ReachedLimit) { - Object value = input.ReadPrimitiveField(field.FieldType); - builder.WeakAddRepeatedField(field, value); - } - } - input.PopLimit(limit); - } else { - object value; - switch (field.FieldType) { - case FieldType.Group: - case FieldType.Message: { - IBuilderLite subBuilder; - if (defaultFieldInstance != null) { - subBuilder = defaultFieldInstance.WeakCreateBuilderForType(); - } else { - subBuilder = builder.CreateBuilderForField(field); - } - if (!field.IsRepeated) { - subBuilder.WeakMergeFrom((IMessageLite)builder[field]); - } - if (field.FieldType == FieldType.Group) { - input.ReadGroup(field.FieldNumber, subBuilder, extensionRegistry); - } else { - input.ReadMessage(subBuilder, extensionRegistry); - } - value = subBuilder.WeakBuild(); - break; - } - case FieldType.Enum: { - int rawValue = input.ReadEnum(); - value = field.EnumType.FindValueByNumber(rawValue); - // If the number isn't recognized as a valid value for this enum, - // drop it. - if (value == null) { - MergeVarintField(fieldNumber, (ulong)rawValue); - return true; - } - break; - } - default: - value = input.ReadPrimitiveField(field.FieldType); - break; - } - if (field.IsRepeated) { - builder.WeakAddRepeatedField(field, value); - } else { - builder[field] = value; - } - } - return true; - } - - /// <summary> - /// Called by MergeFieldFrom to parse a MessageSet extension. - /// </summary> - private void MergeMessageSetExtensionFromCodedStream(CodedInputStream input, - ExtensionRegistry extensionRegistry, IBuilder builder) { - MessageDescriptor type = builder.DescriptorForType; - - // The wire format for MessageSet is: - // message MessageSet { - // repeated group Item = 1 { - // required int32 typeId = 2; - // required bytes message = 3; - // } - // } - // "typeId" is the extension's field number. The extension can only be - // a message type, where "message" contains the encoded bytes of that - // message. - // - // In practice, we will probably never see a MessageSet item in which - // the message appears before the type ID, or where either field does not - // appear exactly once. However, in theory such cases are valid, so we - // should be prepared to accept them. - - int typeId = 0; - ByteString rawBytes = null; // If we encounter "message" before "typeId" - IBuilderLite subBuilder = null; - FieldDescriptor field = null; - - while (true) { - uint tag = input.ReadTag(); - if (tag == 0) { - break; - } - - if (tag == WireFormat.MessageSetTag.TypeID) { - typeId = input.ReadInt32(); - // Zero is not a valid type ID. - if (typeId != 0) { - ExtensionInfo extension = extensionRegistry[type, typeId]; - if (extension != null) { - field = extension.Descriptor; - subBuilder = extension.DefaultInstance.WeakCreateBuilderForType(); - IMessageLite originalMessage = (IMessageLite)builder[field]; - if (originalMessage != null) { - subBuilder.WeakMergeFrom(originalMessage); - } - if (rawBytes != null) { - // We already encountered the message. Parse it now. - // TODO(jonskeet): Check this is okay. It's subtly different from the Java, as it doesn't create an input stream from rawBytes. - // In fact, why don't we just call MergeFrom(rawBytes)? And what about the extension registry? - subBuilder.WeakMergeFrom(rawBytes.CreateCodedInput()); - rawBytes = null; - } - } else { - // Unknown extension number. If we already saw data, put it - // in rawBytes. - if (rawBytes != null) { - MergeField(typeId, UnknownField.CreateBuilder().AddLengthDelimited(rawBytes).Build()); - rawBytes = null; - } - } - } - } else if (tag == WireFormat.MessageSetTag.Message) { - if (typeId == 0) { - // We haven't seen a type ID yet, so we have to store the raw bytes for now. - rawBytes = input.ReadBytes(); - } else if (subBuilder == null) { - // We don't know how to parse this. Ignore it. - MergeField(typeId, UnknownField.CreateBuilder().AddLengthDelimited(input.ReadBytes()).Build()); - } else { - // We already know the type, so we can parse directly from the input - // with no copying. Hooray! - input.ReadMessage(subBuilder, extensionRegistry); - } - } else { - // Unknown tag. Skip it. - if (!input.SkipField(tag)) { - break; // end of group - } - } - } - - input.CheckLastTagWas(WireFormat.MessageSetTag.ItemEnd); - - if (subBuilder != null) { - 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 - } - } -} +#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;
+using Google.ProtocolBuffers.Collections;
+using Google.ProtocolBuffers.Descriptors;
+using Google.ProtocolBuffers.DescriptorProtos;
+
+namespace Google.ProtocolBuffers {
+ /// <summary>
+ /// Used to keep track of fields which were seen when parsing a protocol message
+ /// but whose field numbers or types are unrecognized. This most frequently
+ /// occurs when new fields are added to a message type and then messages containing
+ /// those fields are read by old software that was built before the new types were
+ /// added.
+ ///
+ /// Every message contains an UnknownFieldSet.
+ ///
+ /// Most users will never need to use this class directly.
+ /// </summary>
+ public sealed class UnknownFieldSet : IMessageLite {
+
+ private static readonly UnknownFieldSet defaultInstance = new UnknownFieldSet(new Dictionary<int, UnknownField>());
+
+ private readonly IDictionary<int, UnknownField> fields;
+
+ private UnknownFieldSet(IDictionary<int, UnknownField> fields) {
+ this.fields = fields;
+ }
+
+ /// <summary>
+ /// Creates a new unknown field set builder.
+ /// </summary>
+ public static Builder CreateBuilder() {
+ return new Builder();
+ }
+
+ /// <summary>
+ /// Creates a new unknown field set builder
+ /// and initialize it from <paramref name="original"/>.
+ /// </summary>
+ public static Builder CreateBuilder(UnknownFieldSet original) {
+ return new Builder().MergeFrom(original);
+ }
+
+ public static UnknownFieldSet DefaultInstance {
+ get { return defaultInstance; }
+ }
+
+ /// <summary>
+ /// Returns a read-only view of the mapping from field numbers to values.
+ /// </summary>
+ public IDictionary<int, UnknownField> FieldDictionary {
+ get { return Dictionaries.AsReadOnly(fields); }
+ }
+
+ /// <summary>
+ /// Checks whether or not the given field number is present in the set.
+ /// </summary>
+ public bool HasField(int field) {
+ return fields.ContainsKey(field);
+ }
+
+ /// <summary>
+ /// Fetches a field by number, returning an empty field if not present.
+ /// Never returns null.
+ /// </summary>
+ public UnknownField this[int number] {
+ get {
+ UnknownField ret;
+ if (!fields.TryGetValue(number, out ret)) {
+ ret = UnknownField.DefaultInstance;
+ }
+ return ret;
+ }
+ }
+
+ /// <summary>
+ /// Serializes the set and writes it to <paramref name="output"/>.
+ /// </summary>
+ public void WriteTo(CodedOutputStream output) {
+ foreach (KeyValuePair<int, UnknownField> entry in fields) {
+ entry.Value.WriteTo(entry.Key, output);
+ }
+ }
+
+ /// <summary>
+ /// Gets the number of bytes required to encode this set.
+ /// </summary>
+ public int SerializedSize {
+ get {
+ int result = 0;
+ foreach (KeyValuePair<int, UnknownField> entry in fields) {
+ result += entry.Value.GetSerializedSize(entry.Key);
+ }
+ return result;
+ }
+ }
+
+ /// <summary>
+ /// Converts the set to a string in protocol buffer text format. This
+ /// is just a trivial wrapper around TextFormat.PrintToString.
+ /// </summary>
+ public override String ToString() {
+ return TextFormat.PrintToString(this);
+ }
+
+ /// <summary>
+ /// 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>
+ /// <returns></returns>
+ public ByteString ToByteString() {
+ ByteString.CodedBuilder codedBuilder = new ByteString.CodedBuilder(SerializedSize);
+ WriteTo(codedBuilder.CodedOutput);
+ return codedBuilder.Build();
+ }
+
+ /// <summary>
+ /// Serializes the message to a byte array and returns it. This is
+ /// just a trivial wrapper around WriteTo(CodedOutputStream).
+ /// </summary>
+ /// <returns></returns>
+ public byte[] ToByteArray() {
+ byte[] data = new byte[SerializedSize];
+ CodedOutputStream output = CodedOutputStream.CreateInstance(data);
+ WriteTo(output);
+ output.CheckNoSpaceLeft();
+ return data;
+ }
+
+ /// <summary>
+ /// Serializes the message and writes it to <paramref name="output"/>. This is
+ /// just a trivial wrapper around WriteTo(CodedOutputStream).
+ /// </summary>
+ /// <param name="output"></param>
+ public void WriteTo(Stream output) {
+ CodedOutputStream codedOutput = CodedOutputStream.CreateInstance(output);
+ WriteTo(codedOutput);
+ codedOutput.Flush();
+ }
+
+ /// <summary>
+ /// Serializes the set and writes it to <paramref name="output"/> using
+ /// the MessageSet wire format.
+ /// </summary>
+ public void WriteAsMessageSetTo(CodedOutputStream output) {
+ foreach (KeyValuePair<int, UnknownField> entry in fields) {
+ entry.Value.WriteAsMessageSetExtensionTo(entry.Key, output);
+ }
+ }
+
+ /// <summary>
+ /// Gets the number of bytes required to encode this set using the MessageSet
+ /// wire format.
+ /// </summary>
+ public int SerializedSizeAsMessageSet {
+ get {
+ int result = 0;
+ foreach (KeyValuePair<int, UnknownField> entry in fields) {
+ result += entry.Value.GetSerializedSizeAsMessageSetExtension(entry.Key);
+ }
+ return result;
+ }
+ }
+
+ public override bool Equals(object other) {
+ if (ReferenceEquals(this, other)) {
+ return true;
+ }
+ UnknownFieldSet otherSet = other as UnknownFieldSet;
+ return otherSet != null && Dictionaries.Equals(fields, otherSet.fields);
+ }
+
+ public override int GetHashCode() {
+ return Dictionaries.GetHashCode(fields);
+ }
+
+ /// <summary>
+ /// Parses an UnknownFieldSet from the given input.
+ /// </summary>
+ public static UnknownFieldSet ParseFrom(CodedInputStream input) {
+ return CreateBuilder().MergeFrom(input).Build();
+ }
+
+ /// <summary>
+ /// Parses an UnknownFieldSet from the given data.
+ /// </summary>
+ public static UnknownFieldSet ParseFrom(ByteString data) {
+ return CreateBuilder().MergeFrom(data).Build();
+ }
+
+ /// <summary>
+ /// Parses an UnknownFieldSet from the given data.
+ /// </summary>
+ public static UnknownFieldSet ParseFrom(byte[] data) {
+ return CreateBuilder().MergeFrom(data).Build();
+ }
+
+ /// <summary>
+ /// Parses an UnknownFieldSet from the given input.
+ /// </summary>
+ public static UnknownFieldSet ParseFrom(Stream input) {
+ return CreateBuilder().MergeFrom(input).Build();
+ }
+
+ #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 : 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;
+ // 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).
+ private int lastFieldNumber;
+ private UnknownField.Builder lastField;
+
+ internal Builder() {
+ fields = new SortedList<int, UnknownField>();
+ }
+
+ internal Builder(IDictionary<int, UnknownField> dictionary) {
+ fields = new SortedList<int, UnknownField>(dictionary);
+ }
+
+ /// <summary>
+ /// Returns a field builder for the specified field number, including any values
+ /// which already exist.
+ /// </summary>
+ private UnknownField.Builder GetFieldBuilder(int number) {
+ if (lastField != null) {
+ if (number == lastFieldNumber) {
+ return lastField;
+ }
+ // Note: AddField() will reset lastField and lastFieldNumber.
+ AddField(lastFieldNumber, lastField.Build());
+ }
+ if (number == 0) {
+ return null;
+ }
+
+ lastField = UnknownField.CreateBuilder();
+ UnknownField existing;
+ if (fields.TryGetValue(number, out existing)) {
+ lastField.MergeFrom(existing);
+ }
+ lastFieldNumber = number;
+ return lastField;
+ }
+
+ /// <summary>
+ /// Build the UnknownFieldSet and return it. Once this method has been called,
+ /// this instance will no longer be usable. Calling any method after this
+ /// will throw a NullReferenceException.
+ /// </summary>
+ public UnknownFieldSet Build() {
+ GetFieldBuilder(0); // Force lastField to be built.
+ UnknownFieldSet result = fields.Count == 0 ? DefaultInstance : new UnknownFieldSet(fields);
+ fields = null;
+ return result;
+ }
+
+ /// <summary>
+ /// Adds a field to the set. If a field with the same number already exists, it
+ /// is replaced.
+ /// </summary>
+ public Builder AddField(int number, UnknownField field) {
+ if (number == 0) {
+ throw new ArgumentOutOfRangeException("number", "Zero is not a valid field number.");
+ }
+ if (lastField != null && lastFieldNumber == number) {
+ // Discard this.
+ lastField = null;
+ lastFieldNumber = 0;
+ }
+ fields[number] = field;
+ return this;
+ }
+
+ /// <summary>
+ /// Resets the builder to an empty set.
+ /// </summary>
+ public Builder Clear() {
+ fields.Clear();
+ lastFieldNumber = 0;
+ lastField = null;
+ return this;
+ }
+
+ /// <summary>
+ /// Parse an entire message from <paramref name="input"/> and merge
+ /// its fields into this set.
+ /// </summary>
+ public Builder MergeFrom(CodedInputStream input) {
+ while (true) {
+ uint tag = input.ReadTag();
+ if (tag == 0 || !MergeFieldFrom(tag, input)) {
+ break;
+ }
+ }
+ return this;
+ }
+
+ /// <summary>
+ /// Parse a single field from <paramref name="input"/> and merge it
+ /// into this set.
+ /// </summary>
+ /// <param name="tag">The field's tag number, which was already parsed.</param>
+ /// <param name="input">The coded input stream containing the field</param>
+ /// <returns>false if the tag is an "end group" tag, true otherwise</returns>
+ [CLSCompliant(false)]
+ public bool MergeFieldFrom(uint tag, CodedInputStream input) {
+ int number = WireFormat.GetTagFieldNumber(tag);
+ switch (WireFormat.GetTagWireType(tag)) {
+ case WireFormat.WireType.Varint:
+ GetFieldBuilder(number).AddVarint(input.ReadUInt64());
+ return true;
+ case WireFormat.WireType.Fixed64:
+ GetFieldBuilder(number).AddFixed64(input.ReadFixed64());
+ return true;
+ case WireFormat.WireType.LengthDelimited:
+ GetFieldBuilder(number).AddLengthDelimited(input.ReadBytes());
+ return true;
+ case WireFormat.WireType.StartGroup: {
+ Builder subBuilder = CreateBuilder();
+#pragma warning disable 0612
+ input.ReadUnknownGroup(number, subBuilder);
+#pragma warning restore 0612
+ GetFieldBuilder(number).AddGroup(subBuilder.Build());
+ return true;
+ }
+ case WireFormat.WireType.EndGroup:
+ return false;
+ case WireFormat.WireType.Fixed32:
+ GetFieldBuilder(number).AddFixed32(input.ReadFixed32());
+ return true;
+ default:
+ throw InvalidProtocolBufferException.InvalidWireType();
+ }
+ }
+
+ /// <summary>
+ /// Parses <paramref name="input"/> as an UnknownFieldSet and merge it
+ /// with the set being built. This is just a small wrapper around
+ /// MergeFrom(CodedInputStream).
+ /// </summary>
+ public Builder MergeFrom(Stream input) {
+ CodedInputStream codedInput = CodedInputStream.CreateInstance(input);
+ MergeFrom(codedInput);
+ codedInput.CheckLastTagWas(0);
+ return this;
+ }
+
+ /// <summary>
+ /// Parses <paramref name="data"/> as an UnknownFieldSet and merge it
+ /// with the set being built. This is just a small wrapper around
+ /// MergeFrom(CodedInputStream).
+ /// </summary>
+ public Builder MergeFrom(ByteString data) {
+ CodedInputStream input = data.CreateCodedInput();
+ MergeFrom(input);
+ input.CheckLastTagWas(0);
+ return this;
+ }
+
+ /// <summary>
+ /// Parses <paramref name="data"/> as an UnknownFieldSet and merge it
+ /// with the set being built. This is just a small wrapper around
+ /// MergeFrom(CodedInputStream).
+ /// </summary>
+ public Builder MergeFrom(byte[] data) {
+ CodedInputStream input = CodedInputStream.CreateInstance(data);
+ MergeFrom(input);
+ input.CheckLastTagWas(0);
+ return this;
+ }
+
+ /// <summary>
+ /// Convenience method for merging a new field containing a single varint
+ /// value. This is used in particular when an unknown enum value is
+ /// encountered.
+ /// </summary>
+ [CLSCompliant(false)]
+ public Builder MergeVarintField(int number, ulong value) {
+ if (number == 0) {
+ throw new ArgumentOutOfRangeException("number", "Zero is not a valid field number.");
+ }
+ GetFieldBuilder(number).AddVarint(value);
+ return this;
+ }
+
+ /// <summary>
+ /// Merges the fields from <paramref name="other"/> into this set.
+ /// If a field number exists in both sets, the values in <paramref name="other"/>
+ /// will be appended to the values in this set.
+ /// </summary>
+ public Builder MergeFrom(UnknownFieldSet other) {
+ if (other != DefaultInstance) {
+ foreach(KeyValuePair<int, UnknownField> entry in other.fields) {
+ MergeField(entry.Key, entry.Value);
+ }
+ }
+ return this;
+ }
+
+ /// <summary>
+ /// Checks if the given field number is present in the set.
+ /// </summary>
+ public bool HasField(int number) {
+ if (number == 0) {
+ throw new ArgumentOutOfRangeException("number", "Zero is not a valid field number.");
+ }
+ return number == lastFieldNumber || fields.ContainsKey(number);
+ }
+
+ /// <summary>
+ /// Adds a field to the unknown field set. If a field with the same
+ /// number already exists, the two are merged.
+ /// </summary>
+ public Builder MergeField(int number, UnknownField field) {
+ if (number == 0) {
+ throw new ArgumentOutOfRangeException("number", "Zero is not a valid field number.");
+ }
+ if (HasField(number)) {
+ GetFieldBuilder(number).MergeFrom(field);
+ } else {
+ // Optimization: We could call getFieldBuilder(number).mergeFrom(field)
+ // in this case, but that would create a copy of the Field object.
+ // We'd rather reuse the one passed to us, so call AddField() instead.
+ AddField(number, field);
+ }
+ return this;
+ }
+
+ internal void MergeFrom(CodedInputStream input, ExtensionRegistry extensionRegistry, IBuilder builder) {
+ while (true) {
+ uint tag = input.ReadTag();
+ if (tag == 0) {
+ break;
+ }
+
+ if (!MergeFieldFrom(input, extensionRegistry, builder, tag)) {
+ // end group tag
+ break;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Like <see cref="MergeFrom(CodedInputStream, ExtensionRegistry, IBuilder)" />
+ /// but parses a single field.
+ /// </summary>
+ /// <param name="input">The input to read the field from</param>
+ /// <param name="extensionRegistry">Registry to use when an extension field is encountered</param>
+ /// <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,
+ ExtensionRegistry extensionRegistry, IBuilder builder, uint tag) {
+
+ MessageDescriptor type = builder.DescriptorForType;
+ if (type.Options.MessageSetWireFormat && tag == WireFormat.MessageSetTag.ItemStart) {
+ MergeMessageSetExtensionFromCodedStream(input, extensionRegistry, builder);
+ return true;
+ }
+
+ WireFormat.WireType wireType = WireFormat.GetTagWireType(tag);
+ int fieldNumber = WireFormat.GetTagFieldNumber(tag);
+
+ FieldDescriptor field;
+ IMessageLite defaultFieldInstance = null;
+
+ if (type.IsExtensionNumber(fieldNumber)) {
+ ExtensionInfo extension = extensionRegistry[type, fieldNumber];
+ if (extension == null) {
+ field = null;
+ } else {
+ field = extension.Descriptor;
+ defaultFieldInstance = extension.DefaultInstance;
+ }
+ } else {
+ field = type.FindFieldByNumber(fieldNumber);
+ }
+
+ // Unknown field or wrong wire type. Skip.
+ if (field == null || wireType != WireFormat.GetWireType(field)) {
+ return MergeFieldFrom(tag, input);
+ }
+
+ if (field.IsPacked) {
+ int length = (int)input.ReadRawVarint32();
+ int limit = input.PushLimit(length);
+ if (field.FieldType == FieldType.Enum) {
+ while (!input.ReachedLimit) {
+ int rawValue = input.ReadEnum();
+ object value = field.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;
+ }
+ builder.WeakAddRepeatedField(field, value);
+ }
+ } else {
+ while (!input.ReachedLimit) {
+ Object value = input.ReadPrimitiveField(field.FieldType);
+ builder.WeakAddRepeatedField(field, value);
+ }
+ }
+ input.PopLimit(limit);
+ } else {
+ object value;
+ switch (field.FieldType) {
+ case FieldType.Group:
+ case FieldType.Message: {
+ IBuilderLite subBuilder;
+ if (defaultFieldInstance != null) {
+ subBuilder = defaultFieldInstance.WeakCreateBuilderForType();
+ } else {
+ subBuilder = builder.CreateBuilderForField(field);
+ }
+ if (!field.IsRepeated) {
+ subBuilder.WeakMergeFrom((IMessageLite)builder[field]);
+ }
+ if (field.FieldType == FieldType.Group) {
+ input.ReadGroup(field.FieldNumber, subBuilder, extensionRegistry);
+ } else {
+ input.ReadMessage(subBuilder, extensionRegistry);
+ }
+ value = subBuilder.WeakBuild();
+ break;
+ }
+ case FieldType.Enum: {
+ int rawValue = input.ReadEnum();
+ value = field.EnumType.FindValueByNumber(rawValue);
+ // If the number isn't recognized as a valid value for this enum,
+ // drop it.
+ if (value == null) {
+ MergeVarintField(fieldNumber, (ulong)rawValue);
+ return true;
+ }
+ break;
+ }
+ default:
+ value = input.ReadPrimitiveField(field.FieldType);
+ break;
+ }
+ if (field.IsRepeated) {
+ builder.WeakAddRepeatedField(field, value);
+ } else {
+ builder[field] = value;
+ }
+ }
+ return true;
+ }
+
+ /// <summary>
+ /// Called by MergeFieldFrom to parse a MessageSet extension.
+ /// </summary>
+ private void MergeMessageSetExtensionFromCodedStream(CodedInputStream input,
+ ExtensionRegistry extensionRegistry, IBuilder builder) {
+ MessageDescriptor type = builder.DescriptorForType;
+
+ // The wire format for MessageSet is:
+ // message MessageSet {
+ // repeated group Item = 1 {
+ // required int32 typeId = 2;
+ // required bytes message = 3;
+ // }
+ // }
+ // "typeId" is the extension's field number. The extension can only be
+ // a message type, where "message" contains the encoded bytes of that
+ // message.
+ //
+ // In practice, we will probably never see a MessageSet item in which
+ // the message appears before the type ID, or where either field does not
+ // appear exactly once. However, in theory such cases are valid, so we
+ // should be prepared to accept them.
+
+ int typeId = 0;
+ ByteString rawBytes = null; // If we encounter "message" before "typeId"
+ IBuilderLite subBuilder = null;
+ FieldDescriptor field = null;
+
+ while (true) {
+ uint tag = input.ReadTag();
+ if (tag == 0) {
+ break;
+ }
+
+ if (tag == WireFormat.MessageSetTag.TypeID) {
+ typeId = input.ReadInt32();
+ // Zero is not a valid type ID.
+ if (typeId != 0) {
+ ExtensionInfo extension = extensionRegistry[type, typeId];
+ if (extension != null) {
+ field = extension.Descriptor;
+ subBuilder = extension.DefaultInstance.WeakCreateBuilderForType();
+ IMessageLite originalMessage = (IMessageLite)builder[field];
+ if (originalMessage != null) {
+ subBuilder.WeakMergeFrom(originalMessage);
+ }
+ if (rawBytes != null) {
+ // We already encountered the message. Parse it now.
+ // TODO(jonskeet): Check this is okay. It's subtly different from the Java, as it doesn't create an input stream from rawBytes.
+ // In fact, why don't we just call MergeFrom(rawBytes)? And what about the extension registry?
+ subBuilder.WeakMergeFrom(rawBytes.CreateCodedInput());
+ rawBytes = null;
+ }
+ } else {
+ // Unknown extension number. If we already saw data, put it
+ // in rawBytes.
+ if (rawBytes != null) {
+ MergeField(typeId, UnknownField.CreateBuilder().AddLengthDelimited(rawBytes).Build());
+ rawBytes = null;
+ }
+ }
+ }
+ } else if (tag == WireFormat.MessageSetTag.Message) {
+ if (typeId == 0) {
+ // We haven't seen a type ID yet, so we have to store the raw bytes for now.
+ rawBytes = input.ReadBytes();
+ } else if (subBuilder == null) {
+ // We don't know how to parse this. Ignore it.
+ MergeField(typeId, UnknownField.CreateBuilder().AddLengthDelimited(input.ReadBytes()).Build());
+ } else {
+ // We already know the type, so we can parse directly from the input
+ // with no copying. Hooray!
+ input.ReadMessage(subBuilder, extensionRegistry);
+ }
+ } else {
+ // Unknown tag. Skip it.
+ if (!input.SkipField(tag)) {
+ break; // end of group
+ }
+ }
+ }
+
+ input.CheckLastTagWas(WireFormat.MessageSetTag.ItemEnd);
+
+ if (subBuilder != null) {
+ 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 b98d364c..953364ad 100644 --- a/src/ProtocolBuffers/WireFormat.cs +++ b/src/ProtocolBuffers/WireFormat.cs @@ -1,56 +1,56 @@ -#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; -#if !LITE -using Google.ProtocolBuffers.Descriptors; -#endif - -namespace Google.ProtocolBuffers { - - /// <summary> - /// This class is used internally by the Protocol Buffer Library and generated - /// message implementations. It is public only for the sake of those generated - /// messages. Others should not use this class directly. - /// <para> - /// This class contains constants and helper functions useful for dealing with - /// the Protocol Buffer wire format. - /// </para> - /// </summary> - public static class WireFormat { - -#region Fixed sizes. +#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;
+#if !LITE
+using Google.ProtocolBuffers.Descriptors;
+#endif
+
+namespace Google.ProtocolBuffers {
+
+ /// <summary>
+ /// This class is used internally by the Protocol Buffer Library and generated
+ /// message implementations. It is public only for the sake of those generated
+ /// messages. Others should not use this class directly.
+ /// <para>
+ /// This class contains constants and helper functions useful for dealing with
+ /// the Protocol Buffer wire format.
+ /// </para>
+ /// </summary>
+ public static class WireFormat {
+
+#region Fixed sizes.
// TODO(jonskeet): Move these somewhere else. They're messy. Consider making FieldType a smarter kind of enum
public const int Fixed32Size = 4;
public const int Fixed64Size = 8;
@@ -58,121 +58,121 @@ namespace Google.ProtocolBuffers { public const int SFixed64Size = 8;
public const int FloatSize = 4;
public const int DoubleSize = 8;
- public const int BoolSize = 1; -#endregion - - [CLSCompliant(false)] - public enum WireType : uint { - Varint = 0, - Fixed64 = 1, - LengthDelimited = 2, - StartGroup = 3, - EndGroup = 4, - Fixed32 = 5 - } - - internal static class MessageSetField { - internal const int Item = 1; - internal const int TypeID = 2; - internal const int Message = 3; - } - - internal static class MessageSetTag { - internal static readonly uint ItemStart = MakeTag(MessageSetField.Item, WireType.StartGroup); - internal static readonly uint ItemEnd = MakeTag(MessageSetField.Item, WireType.EndGroup); - internal static readonly uint TypeID = MakeTag(MessageSetField.TypeID, WireType.Varint); - internal static readonly uint Message = MakeTag(MessageSetField.Message, WireType.LengthDelimited); - } - - private const int TagTypeBits = 3; - private const uint TagTypeMask = (1 << TagTypeBits) - 1; - - /// <summary> - /// Given a tag value, determines the wire type (lower 3 bits). - /// </summary> - [CLSCompliant(false)] - public static WireType GetTagWireType(uint tag) { - return (WireType) (tag & TagTypeMask); - } - - [CLSCompliant(false)] - public static bool IsEndGroupTag(uint tag) { - return (WireType)(tag & TagTypeMask) == WireType.EndGroup; - } - - /// <summary> - /// Given a tag value, determines the field number (the upper 29 bits). - /// </summary> - [CLSCompliant(false)] - public static int GetTagFieldNumber(uint tag) { - return (int) tag >> TagTypeBits; - } - - /// <summary> - /// Makes a tag value given a field number and wire type. - /// TODO(jonskeet): Should we just have a Tag structure? - /// </summary> - [CLSCompliant(false)] - public static uint MakeTag(int fieldNumber, WireType wireType) { - return (uint) (fieldNumber << TagTypeBits) | (uint) wireType; - } - -#if !LITE - [CLSCompliant(false)] - public static uint MakeTag(FieldDescriptor field) { - return MakeTag(field.FieldNumber, GetWireType(field)); - } - - /// <summary> - /// Returns the wire type for the given field descriptor. This differs - /// from GetWireType(FieldType) for packed repeated fields. - /// </summary> - internal static WireType GetWireType(FieldDescriptor descriptor) { - return descriptor.IsPacked ? WireType.LengthDelimited : GetWireType(descriptor.FieldType); - } - - /// <summary> - /// Converts a field type to its wire type. Done with a switch for the sake - /// of speed - this is significantly faster than a dictionary lookup. - /// </summary> - [CLSCompliant(false)] - public static WireType GetWireType(FieldType fieldType) { - switch (fieldType) { - case FieldType.Double: - return WireType.Fixed64; - case FieldType.Float: - return WireType.Fixed32; - case FieldType.Int64: - case FieldType.UInt64: - case FieldType.Int32: - return WireType.Varint; - case FieldType.Fixed64: - return WireType.Fixed64; - case FieldType.Fixed32: - return WireType.Fixed32; - case FieldType.Bool: - return WireType.Varint; - case FieldType.String: - return WireType.LengthDelimited; - case FieldType.Group: - return WireType.StartGroup; - case FieldType.Message: - case FieldType.Bytes: - return WireType.LengthDelimited; - case FieldType.UInt32: - return WireType.Varint; - case FieldType.SFixed32: - return WireType.Fixed32; - case FieldType.SFixed64: - return WireType.Fixed64; - case FieldType.SInt32: - case FieldType.SInt64: - case FieldType.Enum: - return WireType.Varint; - default: - throw new ArgumentOutOfRangeException("No such field type"); - } - } -#endif - } -} + public const int BoolSize = 1;
+#endregion
+
+ [CLSCompliant(false)]
+ public enum WireType : uint {
+ Varint = 0,
+ Fixed64 = 1,
+ LengthDelimited = 2,
+ StartGroup = 3,
+ EndGroup = 4,
+ Fixed32 = 5
+ }
+
+ internal static class MessageSetField {
+ internal const int Item = 1;
+ internal const int TypeID = 2;
+ internal const int Message = 3;
+ }
+
+ internal static class MessageSetTag {
+ internal static readonly uint ItemStart = MakeTag(MessageSetField.Item, WireType.StartGroup);
+ internal static readonly uint ItemEnd = MakeTag(MessageSetField.Item, WireType.EndGroup);
+ internal static readonly uint TypeID = MakeTag(MessageSetField.TypeID, WireType.Varint);
+ internal static readonly uint Message = MakeTag(MessageSetField.Message, WireType.LengthDelimited);
+ }
+
+ private const int TagTypeBits = 3;
+ private const uint TagTypeMask = (1 << TagTypeBits) - 1;
+
+ /// <summary>
+ /// Given a tag value, determines the wire type (lower 3 bits).
+ /// </summary>
+ [CLSCompliant(false)]
+ public static WireType GetTagWireType(uint tag) {
+ return (WireType) (tag & TagTypeMask);
+ }
+
+ [CLSCompliant(false)]
+ public static bool IsEndGroupTag(uint tag) {
+ return (WireType)(tag & TagTypeMask) == WireType.EndGroup;
+ }
+
+ /// <summary>
+ /// Given a tag value, determines the field number (the upper 29 bits).
+ /// </summary>
+ [CLSCompliant(false)]
+ public static int GetTagFieldNumber(uint tag) {
+ return (int) tag >> TagTypeBits;
+ }
+
+ /// <summary>
+ /// Makes a tag value given a field number and wire type.
+ /// TODO(jonskeet): Should we just have a Tag structure?
+ /// </summary>
+ [CLSCompliant(false)]
+ public static uint MakeTag(int fieldNumber, WireType wireType) {
+ return (uint) (fieldNumber << TagTypeBits) | (uint) wireType;
+ }
+
+#if !LITE
+ [CLSCompliant(false)]
+ public static uint MakeTag(FieldDescriptor field) {
+ return MakeTag(field.FieldNumber, GetWireType(field));
+ }
+
+ /// <summary>
+ /// Returns the wire type for the given field descriptor. This differs
+ /// from GetWireType(FieldType) for packed repeated fields.
+ /// </summary>
+ internal static WireType GetWireType(FieldDescriptor descriptor) {
+ return descriptor.IsPacked ? WireType.LengthDelimited : GetWireType(descriptor.FieldType);
+ }
+
+ /// <summary>
+ /// Converts a field type to its wire type. Done with a switch for the sake
+ /// of speed - this is significantly faster than a dictionary lookup.
+ /// </summary>
+ [CLSCompliant(false)]
+ public static WireType GetWireType(FieldType fieldType) {
+ switch (fieldType) {
+ case FieldType.Double:
+ return WireType.Fixed64;
+ case FieldType.Float:
+ return WireType.Fixed32;
+ case FieldType.Int64:
+ case FieldType.UInt64:
+ case FieldType.Int32:
+ return WireType.Varint;
+ case FieldType.Fixed64:
+ return WireType.Fixed64;
+ case FieldType.Fixed32:
+ return WireType.Fixed32;
+ case FieldType.Bool:
+ return WireType.Varint;
+ case FieldType.String:
+ return WireType.LengthDelimited;
+ case FieldType.Group:
+ return WireType.StartGroup;
+ case FieldType.Message:
+ case FieldType.Bytes:
+ return WireType.LengthDelimited;
+ case FieldType.UInt32:
+ return WireType.Varint;
+ case FieldType.SFixed32:
+ return WireType.Fixed32;
+ case FieldType.SFixed64:
+ return WireType.Fixed64;
+ case FieldType.SInt32:
+ case FieldType.SInt64:
+ case FieldType.Enum:
+ return WireType.Varint;
+ default:
+ throw new ArgumentOutOfRangeException("No such field type");
+ }
+ }
+#endif
+ }
+}
|