1
0
Fork 0
mirror of https://github.com/Ellpeck/MLEM.git synced 2024-11-24 13:38:34 +01:00

Compare commits

..

3 commits

Author SHA1 Message Date
Ell
dd09f0af25 added HasAnyFlag and improved memory performance of HasFlag in DynamicEnum
Some checks reported errors
Ellpeck/MLEM/pipeline/head Something is wrong with the build of this commit
2021-06-11 20:22:25 +02:00
Ell
fda4097aa5 reverted the last commit, and invalidated DynamicEnum caches when new values are added 2021-06-11 20:05:32 +02:00
Ell
7ed969d1af Moved DynamicEnum to its own library 2021-06-11 19:46:51 +02:00

View file

@ -4,7 +4,6 @@ using System.Linq;
using System.Numerics; using System.Numerics;
using System.Reflection; using System.Reflection;
using MLEM.Data.Json; using MLEM.Data.Json;
using MLEM.Misc;
using Newtonsoft.Json; using Newtonsoft.Json;
namespace MLEM.Data { namespace MLEM.Data {
@ -26,13 +25,16 @@ namespace MLEM.Data {
/// </code> /// </code>
/// </remarks> /// </remarks>
[JsonConverter(typeof(DynamicEnumConverter))] [JsonConverter(typeof(DynamicEnumConverter))]
public abstract class DynamicEnum : GenericDataHolder { public abstract class DynamicEnum {
private static readonly Dictionary<Type, Dictionary<BigInteger, DynamicEnum>> Values = new Dictionary<Type, Dictionary<BigInteger, DynamicEnum>>(); private static readonly Dictionary<Type, Dictionary<BigInteger, DynamicEnum>> Values = new Dictionary<Type, Dictionary<BigInteger, DynamicEnum>>();
private static readonly Dictionary<Type, Dictionary<BigInteger, DynamicEnum>> FlagCache = new Dictionary<Type, Dictionary<BigInteger, DynamicEnum>>(); private static readonly Dictionary<Type, Dictionary<BigInteger, DynamicEnum>> FlagCache = new Dictionary<Type, Dictionary<BigInteger, DynamicEnum>>();
private static readonly Dictionary<string, DynamicEnum> ParseCache = new Dictionary<string, DynamicEnum>(); private static readonly Dictionary<Type, Dictionary<string, DynamicEnum>> ParseCache = new Dictionary<Type, Dictionary<string, DynamicEnum>>();
private readonly BigInteger value; private readonly BigInteger value;
private Dictionary<DynamicEnum, bool> allFlagsCache;
private Dictionary<DynamicEnum, bool> anyFlagsCache;
private string name; private string name;
/// <summary> /// <summary>
@ -47,13 +49,38 @@ namespace MLEM.Data {
} }
/// <summary> /// <summary>
/// Returns true if this enum value has the given <see cref="DynamicEnum"/> flag on it. /// Returns true if this enum value has ALL of the given <see cref="DynamicEnum"/> flags on it.
/// This operation is equivalent to <see cref="Enum.HasFlag"/>. /// This operation is equivalent to <see cref="Enum.HasFlag"/>.
/// </summary> /// </summary>
/// <param name="flag">The flag to query</param> /// <seealso cref="HasAnyFlag"/>
/// <returns>True if the flag is present, false otherwise</returns> /// <param name="flags">The flags to query</param>
public bool HasFlag(DynamicEnum flag) { /// <returns>True if all of the flags are present, false otherwise</returns>
return (GetValue(this) & GetValue(flag)) == GetValue(flag); public bool HasFlag(DynamicEnum flags) {
if (this.allFlagsCache == null)
this.allFlagsCache = new Dictionary<DynamicEnum, bool>();
if (!this.allFlagsCache.TryGetValue(flags, out var ret)) {
// & is very memory-intensive, so we cache the return value
ret = (GetValue(this) & GetValue(flags)) == GetValue(flags);
this.allFlagsCache.Add(flags, ret);
}
return ret;
}
/// <summary>
/// Returns true if this enum value has ANY of the given <see cref="DynamicEnum"/> flags on it
/// </summary>
/// <seealso cref="HasFlag"/>
/// <param name="flags">The flags to query</param>
/// <returns>True if one of the flags is present, false otherwise</returns>
public bool HasAnyFlag(DynamicEnum flags) {
if (this.anyFlagsCache == null)
this.anyFlagsCache = new Dictionary<DynamicEnum, bool>();
if (!this.anyFlagsCache.TryGetValue(flags, out var ret)) {
// & is very memory-intensive, so we cache the return value
ret = (GetValue(this) & GetValue(flags)) != 0;
this.anyFlagsCache.Add(flags, ret);
}
return ret;
} }
/// <inheritdoc /> /// <inheritdoc />
@ -85,6 +112,10 @@ namespace MLEM.Data {
Values.Add(typeof(T), dict); Values.Add(typeof(T), dict);
} }
// cached parsed values and names might be incomplete with new values
FlagCache.Remove(typeof(T));
ParseCache.Remove(typeof(T));
if (dict.ContainsKey(value)) if (dict.ContainsKey(value))
throw new ArgumentException($"Duplicate value {value}", nameof(value)); throw new ArgumentException($"Duplicate value {value}", nameof(value));
if (dict.Values.Any(v => v.name == name)) if (dict.Values.Any(v => v.name == name))
@ -254,7 +285,11 @@ namespace MLEM.Data {
/// <param name="strg">The string to parse into a dynamic enum value</param> /// <param name="strg">The string to parse into a dynamic enum value</param>
/// <returns>The parsed enum value, or null if parsing fails</returns> /// <returns>The parsed enum value, or null if parsing fails</returns>
public static DynamicEnum Parse(Type type, string strg) { public static DynamicEnum Parse(Type type, string strg) {
if (!ParseCache.TryGetValue(strg, out var cached)) { if (!ParseCache.TryGetValue(type, out var cache)) {
cache = new Dictionary<string, DynamicEnum>();
ParseCache.Add(type, cache);
}
if (!cache.TryGetValue(strg, out var cached)) {
BigInteger? accum = null; BigInteger? accum = null;
foreach (var val in strg.Split('|')) { foreach (var val in strg.Split('|')) {
foreach (var defined in GetValues(type)) { foreach (var defined in GetValues(type)) {
@ -266,7 +301,7 @@ namespace MLEM.Data {
} }
if (accum != null) if (accum != null)
cached = GetEnumValue(type, accum.Value); cached = GetEnumValue(type, accum.Value);
ParseCache.Add(strg, cached); cache.Add(strg, cached);
} }
return cached; return cached;
} }