|
|
|
@ -27,6 +27,9 @@ namespace Google.Protobuf.Reflection |
|
|
|
|
private readonly IDictionary<ObjectIntPair<IDescriptor>, EnumValueDescriptor> enumValuesByNumber = |
|
|
|
|
new Dictionary<ObjectIntPair<IDescriptor>, EnumValueDescriptor>(); |
|
|
|
|
|
|
|
|
|
private readonly IDictionary<EnumValueByNameDescriptorKey, EnumValueDescriptor> enumValuesByName = |
|
|
|
|
new Dictionary<EnumValueByNameDescriptorKey, EnumValueDescriptor>(); |
|
|
|
|
|
|
|
|
|
private readonly HashSet<FileDescriptor> dependencies = new HashSet<FileDescriptor>(); |
|
|
|
|
|
|
|
|
|
internal DescriptorPool(IEnumerable<FileDescriptor> dependencyFiles) |
|
|
|
@ -129,29 +132,20 @@ namespace Google.Protobuf.Reflection |
|
|
|
|
|
|
|
|
|
if (descriptorsByName.TryGetValue(fullName, out IDescriptor 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); |
|
|
|
|
throw new DescriptorValidationException(descriptor, |
|
|
|
|
GetDescriptorAlreadyAddedExceptionMessage(descriptor, fullName, old)); |
|
|
|
|
} |
|
|
|
|
descriptorsByName[fullName] = descriptor; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private static string GetDescriptorAlreadyAddedExceptionMessage(IDescriptor descriptor, string fullName, IDescriptor old) |
|
|
|
|
{ |
|
|
|
|
int dotPos = fullName.LastIndexOf('.'); |
|
|
|
|
return descriptor.File != old.File ? $"\"{fullName}\" is already defined in file \"{old.File.Name}\"." |
|
|
|
|
: dotPos == -1 ? $"{fullName} is already defined." |
|
|
|
|
: $"\"{fullName.Substring(dotPos + 1)}\" is already defined in \"{fullName.Substring(0, dotPos)}\"."; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <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). |
|
|
|
@ -167,11 +161,14 @@ namespace Google.Protobuf.Reflection |
|
|
|
|
// Symbol name must start with a letter or underscore, and it can contain letters, |
|
|
|
|
// numbers and underscores. |
|
|
|
|
string name = descriptor.Name; |
|
|
|
|
if (!IsAsciiLetter(name[0]) && name[0] != '_') { |
|
|
|
|
if (!IsAsciiLetter(name[0]) && name[0] != '_') |
|
|
|
|
{ |
|
|
|
|
ThrowInvalidSymbolNameException(descriptor); |
|
|
|
|
} |
|
|
|
|
for (int i = 1; i < name.Length; i++) { |
|
|
|
|
if (!IsAsciiLetter(name[i]) && !IsAsciiDigit(name[i]) && name[i] != '_') { |
|
|
|
|
for (int i = 1; i < name.Length; i++) |
|
|
|
|
{ |
|
|
|
|
if (!IsAsciiLetter(name[i]) && !IsAsciiDigit(name[i]) && name[i] != '_') |
|
|
|
|
{ |
|
|
|
|
ThrowInvalidSymbolNameException(descriptor); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -199,6 +196,12 @@ namespace Google.Protobuf.Reflection |
|
|
|
|
return ret; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
internal EnumValueDescriptor FindEnumValueByName(EnumDescriptor enumDescriptor, string name) |
|
|
|
|
{ |
|
|
|
|
enumValuesByName.TryGetValue(new EnumValueByNameDescriptorKey(enumDescriptor, name), out EnumValueDescriptor ret); |
|
|
|
|
return ret; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary> |
|
|
|
|
/// Adds a field to the fieldsByNumber table. |
|
|
|
|
/// </summary> |
|
|
|
@ -219,17 +222,28 @@ namespace Google.Protobuf.Reflection |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <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.) |
|
|
|
|
/// 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 to enumValuesByNumber. |
|
|
|
|
/// (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> |
|
|
|
|
internal void AddEnumValueByNumber(EnumValueDescriptor enumValue) |
|
|
|
|
internal void AddEnumValue(EnumValueDescriptor enumValue) |
|
|
|
|
{ |
|
|
|
|
ObjectIntPair<IDescriptor> key = new ObjectIntPair<IDescriptor>(enumValue.EnumDescriptor, enumValue.Number); |
|
|
|
|
if (!enumValuesByNumber.ContainsKey(key)) |
|
|
|
|
ObjectIntPair<IDescriptor> numberKey = new ObjectIntPair<IDescriptor>(enumValue.EnumDescriptor, enumValue.Number); |
|
|
|
|
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> |
|
|
|
@ -308,5 +322,37 @@ namespace Google.Protobuf.Reflection |
|
|
|
|
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; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |