fix(core): Fixes type caching & cleanup (#422)

- [X] Fixes some issues with the type caching
- [X] Changes type check default to ignore case
- [X] Splits out type caching of insensitive and sensitive lists. This means less stuff to iterate through when checking a type against the string.
- [X] Adds an ArrayEnumerator, mostly for reference purposes (copy/paste as needed)
This commit is contained in:
Kamron Batman 2021-01-20 00:10:33 -08:00 committed by GitHub
parent b921c38879
commit e869c105d0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
35 changed files with 253 additions and 96 deletions

View file

@ -90,35 +90,32 @@ namespace Server
return m_TypeCaches[asm] = new TypeCache(asm);
}
private static bool IgnoreCaseTypeComparer(string name, Type type) =>
type.FullName.InsensitiveEquals(name) || type.Name.InsensitiveEquals(name);
private static bool CaseTypeComparer(string name, Type type) =>
type.FullName.EqualsOrdinal(name) || type.Name.EqualsOrdinal(name);
public static Type FindFirstTypeForName(string name, bool ignoreCase = false, Func<string, Type, bool> predicate = null)
public static Type FindTypeByFullName(string name, bool ignoreCase = true)
{
if (string.IsNullOrWhiteSpace(name))
{
return null;
}
var types = FindTypesByName(name, ignoreCase);
if (types.Count == 0)
if (ignoreCase)
{
return null;
name = name.ToLower();
}
// Try to find the closest match if there is no predicate.
// Check for exact match of the FullName or Name
// Then check for case-insensitive match of FullName or Name
// Otherwise just return the first entry
predicate ??= ignoreCase ? IgnoreCaseTypeComparer : CaseTypeComparer;
foreach (var type in types)
for (var i = 0; i < Assemblies.Length; i++)
{
if (predicate(name, type))
foreach (var type in GetTypeCache(Assemblies[i]).GetEnumerator(name, ignoreCase))
{
if (type.FullName.EqualsOrdinal(name))
{
return type;
}
}
}
foreach(var type in GetTypeCache(Core.Assembly).GetEnumerator(name, ignoreCase))
{
if (type.FullName.EqualsOrdinal(name))
{
return type;
}
@ -127,7 +124,36 @@ namespace Server
return null;
}
public static List<Type> FindTypesByName(string name, bool ignoreCase = false)
public static Type FindTypeByName(string name, bool ignoreCase = true)
{
if (string.IsNullOrWhiteSpace(name))
{
return null;
}
if (ignoreCase)
{
name = name.ToLower();
}
for (var i = 0; i < Assemblies.Length; i++)
{
foreach (var type in GetTypeCache(Assemblies[i]).GetEnumerator(name, ignoreCase))
{
return type;
}
}
foreach(var type in GetTypeCache(Core.Assembly).GetEnumerator(name, ignoreCase))
{
return type;
}
return null;
}
// TODO: Change to IEnumerable using another custom enumerator
public static List<Type> FindTypesByFullName(string name, bool ignoreCase = true)
{
var types = new List<Type>();
@ -138,20 +164,49 @@ namespace Server
for (var i = 0; i < Assemblies.Length; i++)
{
foreach (var type in GetTypeCache(Assemblies[i])[name])
foreach (var type in GetTypeCache(Assemblies[i]).GetEnumerator(name, ignoreCase))
{
if (type.FullName.EqualsOrdinal(name))
{
types.Add(type);
}
}
}
foreach(var type in GetTypeCache(Core.Assembly).GetEnumerator(name, ignoreCase))
{
if (type.FullName.EqualsOrdinal(name))
{
types.Add(type);
}
}
if (types.Count == 0)
return types;
}
// TODO: Change to IEnumerable using another custom enumerator
public static List<Type> FindTypesByName(string name, bool ignoreCase = true)
{
var types = new List<Type>();
if (ignoreCase)
{
foreach(var type in GetTypeCache(Core.Assembly)[name])
name = name.ToLower();
}
for (var i = 0; i < Assemblies.Length; i++)
{
foreach (var type in GetTypeCache(Assemblies[i]).GetEnumerator(name, ignoreCase))
{
types.Add(type);
}
}
foreach(var type in GetTypeCache(Core.Assembly).GetEnumerator(name, ignoreCase))
{
types.Add(type);
}
return types;
}
@ -166,52 +221,64 @@ namespace Server
public class TypeCache
{
private readonly Dictionary<string, int[]> m_NameMap = new();
private readonly Dictionary<string, int[]> _nameMap = new();
private readonly Dictionary<string, int[]> _nameMapInsensitive = new();
public TypeCache(Assembly asm)
{
Types = asm?.GetTypes() ?? Type.EmptyTypes;
var nameMap = new Dictionary<string, HashSet<int>>();
HashSet<int> refs;
Action<int, string> addToRefs = (index, key) =>
var nameMapInsensitive = new Dictionary<string, HashSet<int>>();
void addToRefs(int index, string key, Dictionary<string, HashSet<int>> map)
{
if (nameMap.TryGetValue(key, out refs))
if (key == null)
{
return;
}
if (map.TryGetValue(key, out var refs))
{
refs.Add(index);
}
else
{
refs = new HashSet<int> { index };
nameMap.Add(key, refs);
map.Add(key, refs);
}
};
}
var aliasType = typeof(TypeAliasAttribute);
for (var i = 0; i < Types.Length; i++)
{
var current = Types[i];
addToRefs(i, current.Name);
addToRefs(i, current.Name.ToLower());
addToRefs(i, current.FullName);
addToRefs(i, current.FullName?.ToLower());
addToRefs(i, current.Name, nameMap);
addToRefs(i, current.Name.ToLower(), nameMapInsensitive);
addToRefs(i, current.FullName, nameMap);
addToRefs(i, current.FullName?.ToLower(), nameMapInsensitive);
if (current.GetCustomAttribute(aliasType, false) is TypeAliasAttribute alias)
{
for (var j = 0; j < alias.Aliases.Length; j++)
{
addToRefs(i, alias.Aliases[j]);
addToRefs(i, alias.Aliases[j].ToLower());
addToRefs(i, alias.Aliases[j], nameMap);
addToRefs(i, alias.Aliases[j], nameMapInsensitive);
}
}
}
foreach (var (key, value) in nameMap)
{
m_NameMap[key] = value.ToArray();
_nameMap[key] = value.ToArray();
}
foreach (var (key, value) in nameMapInsensitive)
{
_nameMapInsensitive[key] = value.ToArray();
}
}
public Enumerator this[string name] => new(name, this);
public Enumerator GetEnumerator(string name, bool ignoreCase) => new(name, this, ignoreCase);
public Type[] Types { get; }
@ -222,10 +289,12 @@ namespace Server
private int _index;
private Type _current;
internal Enumerator(string name, TypeCache cache)
internal Enumerator(string name, TypeCache cache, bool ignoreCase)
{
_cache = cache;
_values = !cache.m_NameMap.TryGetValue(name, out var values) ? Array.Empty<int>() : values;
var map = ignoreCase ? _cache._nameMap : _cache._nameMapInsensitive;
_values = map.TryGetValue(name, out var values) ? values : Array.Empty<int>();
_index = 0;
_current = default;
}
@ -274,7 +343,7 @@ namespace Server
public IEnumerator<Type> GetEnumerator() => this;
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => this;
}
}
}