C#: Avoid string concatenation when looking up enum values by name.

PiperOrigin-RevId: 616745400
pull/16188/head
Protobuf Team Bot 9 months ago committed by Copybara-Service
parent 71eed03fda
commit e6684ac037
  1. 102
      csharp/src/Google.Protobuf/Reflection/DescriptorPool.cs
  2. 6
      csharp/src/Google.Protobuf/Reflection/EnumDescriptor.cs
  3. 2
      csharp/src/Google.Protobuf/Reflection/EnumValueDescriptor.cs

@ -27,6 +27,9 @@ namespace Google.Protobuf.Reflection
private readonly IDictionary<ObjectIntPair<IDescriptor>, EnumValueDescriptor> enumValuesByNumber = private readonly IDictionary<ObjectIntPair<IDescriptor>, EnumValueDescriptor> enumValuesByNumber =
new Dictionary<ObjectIntPair<IDescriptor>, EnumValueDescriptor>(); new Dictionary<ObjectIntPair<IDescriptor>, EnumValueDescriptor>();
private readonly IDictionary<EnumValueByNameDescriptorKey, EnumValueDescriptor> enumValuesByName =
new Dictionary<EnumValueByNameDescriptorKey, EnumValueDescriptor>();
private readonly HashSet<FileDescriptor> dependencies = new HashSet<FileDescriptor>(); private readonly HashSet<FileDescriptor> dependencies = new HashSet<FileDescriptor>();
internal DescriptorPool(IEnumerable<FileDescriptor> dependencyFiles) internal DescriptorPool(IEnumerable<FileDescriptor> dependencyFiles)
@ -129,27 +132,18 @@ namespace Google.Protobuf.Reflection
if (descriptorsByName.TryGetValue(fullName, out IDescriptor old)) if (descriptorsByName.TryGetValue(fullName, out IDescriptor old))
{ {
int dotPos = fullName.LastIndexOf('.'); throw new DescriptorValidationException(descriptor,
string message; GetDescriptorAlreadyAddedExceptionMessage(descriptor, fullName, old));
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) + "\".";
} }
descriptorsByName[fullName] = descriptor;
} }
else
private static string GetDescriptorAlreadyAddedExceptionMessage(IDescriptor descriptor, string fullName, IDescriptor old)
{ {
message = "\"" + fullName + "\" is already defined in file \"" + old.File.Name + "\"."; int dotPos = fullName.LastIndexOf('.');
} return descriptor.File != old.File ? $"\"{fullName}\" is already defined in file \"{old.File.Name}\"."
throw new DescriptorValidationException(descriptor, message); : dotPos == -1 ? $"{fullName} is already defined."
} : $"\"{fullName.Substring(dotPos + 1)}\" is already defined in \"{fullName.Substring(0, dotPos)}\".";
descriptorsByName[fullName] = descriptor;
} }
/// <summary> /// <summary>
@ -167,11 +161,14 @@ namespace Google.Protobuf.Reflection
// Symbol name must start with a letter or underscore, and it can contain letters, // Symbol name must start with a letter or underscore, and it can contain letters,
// numbers and underscores. // numbers and underscores.
string name = descriptor.Name; string name = descriptor.Name;
if (!IsAsciiLetter(name[0]) && name[0] != '_') { if (!IsAsciiLetter(name[0]) && name[0] != '_')
{
ThrowInvalidSymbolNameException(descriptor); ThrowInvalidSymbolNameException(descriptor);
} }
for (int i = 1; i < name.Length; i++) { for (int i = 1; i < name.Length; i++)
if (!IsAsciiLetter(name[i]) && !IsAsciiDigit(name[i]) && name[i] != '_') { {
if (!IsAsciiLetter(name[i]) && !IsAsciiDigit(name[i]) && name[i] != '_')
{
ThrowInvalidSymbolNameException(descriptor); ThrowInvalidSymbolNameException(descriptor);
} }
} }
@ -199,6 +196,12 @@ namespace Google.Protobuf.Reflection
return ret; return ret;
} }
internal EnumValueDescriptor FindEnumValueByName(EnumDescriptor enumDescriptor, string name)
{
enumValuesByName.TryGetValue(new EnumValueByNameDescriptorKey(enumDescriptor, name), out EnumValueDescriptor ret);
return ret;
}
/// <summary> /// <summary>
/// Adds a field to the fieldsByNumber table. /// Adds a field to the fieldsByNumber table.
/// </summary> /// </summary>
@ -219,17 +222,28 @@ namespace Google.Protobuf.Reflection
} }
/// <summary> /// <summary>
/// Adds an enum value to the enumValuesByNumber table. If an enum value /// Adds an enum value to the enumValuesByNumber and enumValuesByName tables. If an enum value
/// with the same type and number already exists, this method does nothing. /// with the same type and number already exists, this method does nothing to enumValuesByNumber.
/// (This is allowed; the first value defined with the number takes precedence.) /// (This is allowed; the first value defined with the number takes precedence.) If an enum
/// value with the same name already exists, this method throws DescriptorValidationException.
/// (It is expected that this method is called after AddSymbol, which would already have thrown
/// an exception in this failure case.)
/// </summary> /// </summary>
internal void AddEnumValueByNumber(EnumValueDescriptor enumValue) internal void AddEnumValue(EnumValueDescriptor enumValue)
{ {
ObjectIntPair<IDescriptor> key = new ObjectIntPair<IDescriptor>(enumValue.EnumDescriptor, enumValue.Number); ObjectIntPair<IDescriptor> numberKey = new ObjectIntPair<IDescriptor>(enumValue.EnumDescriptor, enumValue.Number);
if (!enumValuesByNumber.ContainsKey(key)) if (!enumValuesByNumber.ContainsKey(numberKey))
{ {
enumValuesByNumber[key] = enumValue; enumValuesByNumber[numberKey] = enumValue;
} }
EnumValueByNameDescriptorKey nameKey = new EnumValueByNameDescriptorKey(enumValue.EnumDescriptor, enumValue.Name);
if (enumValuesByName.TryGetValue(nameKey, out EnumValueDescriptor old))
{
throw new DescriptorValidationException(enumValue,
GetDescriptorAlreadyAddedExceptionMessage(enumValue, enumValue.FullName, old));
}
enumValuesByName[nameKey] = enumValue;
} }
/// <summary> /// <summary>
@ -308,5 +322,37 @@ namespace Google.Protobuf.Reflection
return result; return result;
} }
} }
/// <summary>
/// Struct used to hold the keys for the enumValuesByName table.
/// </summary>
private struct EnumValueByNameDescriptorKey : IEquatable<EnumValueByNameDescriptorKey>
{
private readonly string name;
private readonly IDescriptor descriptor;
internal EnumValueByNameDescriptorKey(EnumDescriptor descriptor, string valueName)
{
this.descriptor = descriptor;
this.name = valueName;
}
public bool Equals(EnumValueByNameDescriptorKey other) =>
descriptor == other.descriptor
&& name == other.name;
public override bool Equals(object obj) =>
obj is EnumValueByNameDescriptorKey pair && Equals(pair);
public override int GetHashCode()
{
unchecked
{
var hashCode = descriptor.GetHashCode();
hashCode = (hashCode * 397) ^ (name != null ? name.GetHashCode() : 0);
return hashCode;
}
}
}
} }
} }

@ -90,10 +90,8 @@ namespace Google.Protobuf.Reflection
/// </summary> /// </summary>
/// <param name="name">The unqualified name of the value (e.g. "FOO").</param> /// <param name="name">The unqualified name of the value (e.g. "FOO").</param>
/// <returns>The value's descriptor, or null if not found.</returns> /// <returns>The value's descriptor, or null if not found.</returns>
public EnumValueDescriptor FindValueByName(string name) public EnumValueDescriptor FindValueByName(string name) =>
{ File.DescriptorPool.FindEnumValueByName(this, name);
return File.DescriptorPool.FindSymbol<EnumValueDescriptor>(FullName + "." + name);
}
/// <summary> /// <summary>
/// The (possibly empty) set of custom options for this enum. /// The (possibly empty) set of custom options for this enum.

@ -24,7 +24,7 @@ namespace Google.Protobuf.Reflection
Proto = proto; Proto = proto;
EnumDescriptor = parent; EnumDescriptor = parent;
file.DescriptorPool.AddSymbol(this); file.DescriptorPool.AddSymbol(this);
file.DescriptorPool.AddEnumValueByNumber(this); file.DescriptorPool.AddEnumValue(this);
} }
internal EnumValueDescriptorProto Proto { get; } internal EnumValueDescriptorProto Proto { get; }

Loading…
Cancel
Save