Index: third_party/protobuf/csharp/src/Google.Protobuf/Reflection/DescriptorPool.cs |
diff --git a/third_party/protobuf/csharp/src/Google.Protobuf/Reflection/DescriptorPool.cs b/third_party/protobuf/csharp/src/Google.Protobuf/Reflection/DescriptorPool.cs |
new file mode 100644 |
index 0000000000000000000000000000000000000000..99ca4bf34f1418fc241aa734ba9d75d4fcae4a8d |
--- /dev/null |
+++ b/third_party/protobuf/csharp/src/Google.Protobuf/Reflection/DescriptorPool.cs |
@@ -0,0 +1,368 @@ |
+#region Copyright notice and license |
+// Protocol Buffers - Google's data interchange format |
+// Copyright 2008 Google Inc. All rights reserved. |
+// https://developers.google.com/protocol-buffers/ |
+// |
+// Redistribution and use in source and binary forms, with or without |
+// modification, are permitted provided that the following conditions are |
+// met: |
+// |
+// * Redistributions of source code must retain the above copyright |
+// notice, this list of conditions and the following disclaimer. |
+// * Redistributions in binary form must reproduce the above |
+// copyright notice, this list of conditions and the following disclaimer |
+// in the documentation and/or other materials provided with the |
+// distribution. |
+// * Neither the name of Google Inc. nor the names of its |
+// contributors may be used to endorse or promote products derived from |
+// this software without specific prior written permission. |
+// |
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
+#endregion |
+ |
+using System; |
+using System.Collections.Generic; |
+using System.Text; |
+using System.Text.RegularExpressions; |
+ |
+namespace Google.Protobuf.Reflection |
+{ |
+ /// <summary> |
+ /// Contains lookup tables containing all the descriptors defined in a particular file. |
+ /// </summary> |
+ internal sealed class DescriptorPool |
+ { |
+ private readonly IDictionary<string, IDescriptor> descriptorsByName = |
+ new Dictionary<string, IDescriptor>(); |
+ |
+ private readonly IDictionary<DescriptorIntPair, FieldDescriptor> fieldsByNumber = |
+ new Dictionary<DescriptorIntPair, FieldDescriptor>(); |
+ |
+ private readonly IDictionary<DescriptorIntPair, EnumValueDescriptor> enumValuesByNumber = |
+ new Dictionary<DescriptorIntPair, EnumValueDescriptor>(); |
+ |
+ private readonly HashSet<FileDescriptor> dependencies; |
+ |
+ internal DescriptorPool(FileDescriptor[] dependencyFiles) |
+ { |
+ dependencies = new HashSet<FileDescriptor>(); |
+ for (int i = 0; i < dependencyFiles.Length; i++) |
+ { |
+ dependencies.Add(dependencyFiles[i]); |
+ ImportPublicDependencies(dependencyFiles[i]); |
+ } |
+ |
+ foreach (FileDescriptor dependency in dependencyFiles) |
+ { |
+ AddPackage(dependency.Package, dependency); |
+ } |
+ } |
+ |
+ private void ImportPublicDependencies(FileDescriptor file) |
+ { |
+ foreach (FileDescriptor dependency in file.PublicDependencies) |
+ { |
+ if (dependencies.Add(dependency)) |
+ { |
+ ImportPublicDependencies(dependency); |
+ } |
+ } |
+ } |
+ |
+ /// <summary> |
+ /// Finds a symbol of the given name within the pool. |
+ /// </summary> |
+ /// <typeparam name="T">The type of symbol to look for</typeparam> |
+ /// <param name="fullName">Fully-qualified name to look up</param> |
+ /// <returns>The symbol with the given name and type, |
+ /// or null if the symbol doesn't exist or has the wrong type</returns> |
+ internal T FindSymbol<T>(string fullName) where T : class |
+ { |
+ IDescriptor result; |
+ descriptorsByName.TryGetValue(fullName, out result); |
+ T descriptor = result as T; |
+ if (descriptor != null) |
+ { |
+ return descriptor; |
+ } |
+ |
+ // dependencies contains direct dependencies and any *public* dependencies |
+ // of those dependencies (transitively)... so we don't need to recurse here. |
+ foreach (FileDescriptor dependency in dependencies) |
+ { |
+ dependency.DescriptorPool.descriptorsByName.TryGetValue(fullName, out result); |
+ descriptor = result as T; |
+ if (descriptor != null) |
+ { |
+ return descriptor; |
+ } |
+ } |
+ |
+ return null; |
+ } |
+ |
+ /// <summary> |
+ /// Adds a package to the symbol tables. If a package by the same name |
+ /// already exists, that is fine, but if some other kind of symbol |
+ /// exists under the same name, an exception is thrown. If the package |
+ /// has multiple components, this also adds the parent package(s). |
+ /// </summary> |
+ internal void AddPackage(string fullName, FileDescriptor file) |
+ { |
+ int dotpos = fullName.LastIndexOf('.'); |
+ String name; |
+ if (dotpos != -1) |
+ { |
+ AddPackage(fullName.Substring(0, dotpos), file); |
+ name = fullName.Substring(dotpos + 1); |
+ } |
+ else |
+ { |
+ name = fullName; |
+ } |
+ |
+ IDescriptor old; |
+ if (descriptorsByName.TryGetValue(fullName, out old)) |
+ { |
+ if (!(old is PackageDescriptor)) |
+ { |
+ throw new DescriptorValidationException(file, |
+ "\"" + name + |
+ "\" is already defined (as something other than a " + |
+ "package) in file \"" + old.File.Name + "\"."); |
+ } |
+ } |
+ descriptorsByName[fullName] = new PackageDescriptor(name, fullName, file); |
+ } |
+ |
+ /// <summary> |
+ /// Adds a symbol to the symbol table. |
+ /// </summary> |
+ /// <exception cref="DescriptorValidationException">The symbol already existed |
+ /// in the symbol table.</exception> |
+ internal void AddSymbol(IDescriptor descriptor) |
+ { |
+ ValidateSymbolName(descriptor); |
+ String fullName = descriptor.FullName; |
+ |
+ IDescriptor old; |
+ if (descriptorsByName.TryGetValue(fullName, out old)) |
+ { |
+ int dotPos = fullName.LastIndexOf('.'); |
+ string message; |
+ if (descriptor.File == old.File) |
+ { |
+ if (dotPos == -1) |
+ { |
+ message = "\"" + fullName + "\" is already defined."; |
+ } |
+ else |
+ { |
+ message = "\"" + fullName.Substring(dotPos + 1) + "\" is already defined in \"" + |
+ fullName.Substring(0, dotPos) + "\"."; |
+ } |
+ } |
+ else |
+ { |
+ message = "\"" + fullName + "\" is already defined in file \"" + old.File.Name + "\"."; |
+ } |
+ throw new DescriptorValidationException(descriptor, message); |
+ } |
+ descriptorsByName[fullName] = descriptor; |
+ } |
+ |
+ private static readonly Regex ValidationRegex = new Regex("^[_A-Za-z][_A-Za-z0-9]*$", |
+ FrameworkPortability.CompiledRegexWhereAvailable); |
+ |
+ /// <summary> |
+ /// Verifies that the descriptor's name is valid (i.e. it contains |
+ /// only letters, digits and underscores, and does not start with a digit). |
+ /// </summary> |
+ /// <param name="descriptor"></param> |
+ private static void ValidateSymbolName(IDescriptor descriptor) |
+ { |
+ if (descriptor.Name == "") |
+ { |
+ throw new DescriptorValidationException(descriptor, "Missing name."); |
+ } |
+ if (!ValidationRegex.IsMatch(descriptor.Name)) |
+ { |
+ throw new DescriptorValidationException(descriptor, |
+ "\"" + descriptor.Name + "\" is not a valid identifier."); |
+ } |
+ } |
+ |
+ /// <summary> |
+ /// Returns the field with the given number in the given descriptor, |
+ /// or null if it can't be found. |
+ /// </summary> |
+ internal FieldDescriptor FindFieldByNumber(MessageDescriptor messageDescriptor, int number) |
+ { |
+ FieldDescriptor ret; |
+ fieldsByNumber.TryGetValue(new DescriptorIntPair(messageDescriptor, number), out ret); |
+ return ret; |
+ } |
+ |
+ internal EnumValueDescriptor FindEnumValueByNumber(EnumDescriptor enumDescriptor, int number) |
+ { |
+ EnumValueDescriptor ret; |
+ enumValuesByNumber.TryGetValue(new DescriptorIntPair(enumDescriptor, number), out ret); |
+ return ret; |
+ } |
+ |
+ /// <summary> |
+ /// Adds a field to the fieldsByNumber table. |
+ /// </summary> |
+ /// <exception cref="DescriptorValidationException">A field with the same |
+ /// containing type and number already exists.</exception> |
+ internal void AddFieldByNumber(FieldDescriptor field) |
+ { |
+ DescriptorIntPair key = new DescriptorIntPair(field.ContainingType, field.FieldNumber); |
+ FieldDescriptor old; |
+ if (fieldsByNumber.TryGetValue(key, out old)) |
+ { |
+ throw new DescriptorValidationException(field, "Field number " + field.FieldNumber + |
+ "has already been used in \"" + |
+ field.ContainingType.FullName + |
+ "\" by field \"" + old.Name + "\"."); |
+ } |
+ fieldsByNumber[key] = field; |
+ } |
+ |
+ /// <summary> |
+ /// Adds an enum value to the enumValuesByNumber table. If an enum value |
+ /// with the same type and number already exists, this method does nothing. |
+ /// (This is allowed; the first value defined with the number takes precedence.) |
+ /// </summary> |
+ internal void AddEnumValueByNumber(EnumValueDescriptor enumValue) |
+ { |
+ DescriptorIntPair key = new DescriptorIntPair(enumValue.EnumDescriptor, enumValue.Number); |
+ if (!enumValuesByNumber.ContainsKey(key)) |
+ { |
+ enumValuesByNumber[key] = enumValue; |
+ } |
+ } |
+ |
+ /// <summary> |
+ /// Looks up a descriptor by name, relative to some other descriptor. |
+ /// The name may be fully-qualified (with a leading '.'), partially-qualified, |
+ /// or unqualified. C++-like name lookup semantics are used to search for the |
+ /// matching descriptor. |
+ /// </summary> |
+ /// <remarks> |
+ /// This isn't heavily optimized, but it's only used during cross linking anyway. |
+ /// If it starts being used more widely, we should look at performance more carefully. |
+ /// </remarks> |
+ internal IDescriptor LookupSymbol(string name, IDescriptor relativeTo) |
+ { |
+ IDescriptor result; |
+ if (name.StartsWith(".")) |
+ { |
+ // Fully-qualified name. |
+ result = FindSymbol<IDescriptor>(name.Substring(1)); |
+ } |
+ else |
+ { |
+ // If "name" is a compound identifier, we want to search for the |
+ // first component of it, then search within it for the rest. |
+ int firstPartLength = name.IndexOf('.'); |
+ string firstPart = firstPartLength == -1 ? name : name.Substring(0, firstPartLength); |
+ |
+ // We will search each parent scope of "relativeTo" looking for the |
+ // symbol. |
+ StringBuilder scopeToTry = new StringBuilder(relativeTo.FullName); |
+ |
+ while (true) |
+ { |
+ // Chop off the last component of the scope. |
+ |
+ int dotpos = scopeToTry.ToString().LastIndexOf("."); |
+ if (dotpos == -1) |
+ { |
+ result = FindSymbol<IDescriptor>(name); |
+ break; |
+ } |
+ else |
+ { |
+ scopeToTry.Length = dotpos + 1; |
+ |
+ // Append firstPart and try to find. |
+ scopeToTry.Append(firstPart); |
+ result = FindSymbol<IDescriptor>(scopeToTry.ToString()); |
+ |
+ if (result != null) |
+ { |
+ if (firstPartLength != -1) |
+ { |
+ // We only found the first part of the symbol. Now look for |
+ // the whole thing. If this fails, we *don't* want to keep |
+ // searching parent scopes. |
+ scopeToTry.Length = dotpos + 1; |
+ scopeToTry.Append(name); |
+ result = FindSymbol<IDescriptor>(scopeToTry.ToString()); |
+ } |
+ break; |
+ } |
+ |
+ // Not found. Remove the name so we can try again. |
+ scopeToTry.Length = dotpos; |
+ } |
+ } |
+ } |
+ |
+ if (result == null) |
+ { |
+ throw new DescriptorValidationException(relativeTo, "\"" + name + "\" is not defined."); |
+ } |
+ else |
+ { |
+ return result; |
+ } |
+ } |
+ |
+ /// <summary> |
+ /// Struct used to hold the keys for the fieldByNumber table. |
+ /// </summary> |
+ private struct DescriptorIntPair : IEquatable<DescriptorIntPair> |
+ { |
+ private readonly int number; |
+ private readonly IDescriptor descriptor; |
+ |
+ internal DescriptorIntPair(IDescriptor descriptor, int number) |
+ { |
+ this.number = number; |
+ this.descriptor = descriptor; |
+ } |
+ |
+ public bool Equals(DescriptorIntPair other) |
+ { |
+ return descriptor == other.descriptor |
+ && number == other.number; |
+ } |
+ |
+ public override bool Equals(object obj) |
+ { |
+ if (obj is DescriptorIntPair) |
+ { |
+ return Equals((DescriptorIntPair) obj); |
+ } |
+ return false; |
+ } |
+ |
+ public override int GetHashCode() |
+ { |
+ return descriptor.GetHashCode()*((1 << 16) - 1) + number; |
+ } |
+ } |
+ } |
+} |