diff --git a/CHANGELOG.md b/CHANGELOG.md
index 983ef45..45893a0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -48,6 +48,7 @@ Additions
Improvements
- Improved RawContentManager's reader loading and added better exception handling
- Improved CopyExtensions construction speed
+- Improved DynamicEnum caching
## 5.0.0
### MLEM
diff --git a/MLEM.Data/DynamicEnum.cs b/MLEM.Data/DynamicEnum.cs
index 2ad9811..dc4f14d 100644
--- a/MLEM.Data/DynamicEnum.cs
+++ b/MLEM.Data/DynamicEnum.cs
@@ -12,6 +12,9 @@ namespace MLEM.Data {
/// A dynamic enum uses as its underlying type, allowing for an arbitrary number of enum values to be created, even when a -like structure is used that would only allow for up to 64 values in a regular enum.
/// All enum operations including , , and are supported and can be implemented in derived classes using operator overloads.
/// To create a custom dynamic enum, simply create a class that extends . New values can then be added using , or .
+ ///
+ /// This class, and its entire concept, are extremely terrible. If you intend on using this, there's probably at least one better solution available.
+ /// Though if, for some weird reason, you need a way to have more than 64 distinct flags, this is a pretty good solution.
///
///
/// To include enum-like operator overloads in a dynamic enum named MyEnum, the following code can be used:
@@ -27,10 +30,7 @@ namespace MLEM.Data {
[JsonConverter(typeof(DynamicEnumConverter))]
public abstract class DynamicEnum {
- private static readonly Dictionary> Values = new Dictionary>();
- private static readonly Dictionary> FlagCache = new Dictionary>();
- private static readonly Dictionary> ParseCache = new Dictionary>();
-
+ private static readonly Dictionary Storages = new Dictionary();
private readonly BigInteger value;
private Dictionary allFlagsCache;
@@ -59,8 +59,7 @@ namespace MLEM.Data {
if (this.allFlagsCache == null)
this.allFlagsCache = new Dictionary();
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);
+ ret = And(this, flags) == flags;
this.allFlagsCache.Add(flags, ret);
}
return ret;
@@ -76,8 +75,7 @@ namespace MLEM.Data {
if (this.anyFlagsCache == null)
this.anyFlagsCache = new Dictionary();
if (!this.anyFlagsCache.TryGetValue(flags, out var ret)) {
- // & is very memory-intensive, so we cache the return value
- ret = (GetValue(this) & GetValue(flags)) != 0;
+ ret = GetValue(And(this, flags)) != 0;
this.anyFlagsCache.Add(flags, ret);
}
return ret;
@@ -107,24 +105,20 @@ namespace MLEM.Data {
/// The newly created enum value
/// Thrown if the name or value passed are already present
public static T Add(string name, BigInteger value) where T : DynamicEnum {
- if (!Values.TryGetValue(typeof(T), out var dict)) {
- dict = new Dictionary();
- Values.Add(typeof(T), dict);
- }
+ var storage = GetStorage(typeof(T));
// cached parsed values and names might be incomplete with new values
- FlagCache.Remove(typeof(T));
- ParseCache.Remove(typeof(T));
+ storage.ClearCaches();
- if (dict.ContainsKey(value))
+ if (storage.Values.ContainsKey(value))
throw new ArgumentException($"Duplicate value {value}", nameof(value));
- foreach (var v in dict.Values) {
+ foreach (var v in storage.Values.Values) {
if (v.name == name)
throw new ArgumentException($"Duplicate name {name}", nameof(name));
}
var ret = Construct(typeof(T), name, value);
- dict.Add(value, ret);
+ storage.Values.Add(value, ret);
return (T) ret;
}
@@ -138,10 +132,8 @@ namespace MLEM.Data {
/// The newly created enum value
public static T AddValue(string name) where T : DynamicEnum {
BigInteger value = 0;
- if (Values.TryGetValue(typeof(T), out var defined)) {
- while (defined.ContainsKey(value))
- value++;
- }
+ while (GetStorage(typeof(T)).Values.ContainsKey(value))
+ value++;
return Add(name, value);
}
@@ -155,10 +147,8 @@ namespace MLEM.Data {
/// The newly created enum value
public static T AddFlag(string name) where T : DynamicEnum {
BigInteger value = 0;
- if (Values.TryGetValue(typeof(T), out var defined)) {
- while (defined.ContainsKey(value))
- value <<= 1;
- }
+ while (GetStorage(typeof(T)).Values.ContainsKey(value))
+ value <<= 1;
return Add(name, value);
}
@@ -179,7 +169,7 @@ namespace MLEM.Data {
/// The type whose values to get
/// The defined values for the given type
public static IEnumerable GetValues(Type type) {
- return Values.TryGetValue(type, out var ret) ? ret.Values : Enumerable.Empty();
+ return GetStorage(type).Values.Values;
}
///
@@ -190,7 +180,12 @@ namespace MLEM.Data {
/// The type of the values
/// The bitwise OR (|) combination
public static T Or(T left, T right) where T : DynamicEnum {
- return GetEnumValue(GetValue(left) | GetValue(right));
+ var cache = GetStorage(typeof(T)).OrCache;
+ if (!cache.TryGetValue((left, right), out var ret)) {
+ ret = GetEnumValue(GetValue(left) | GetValue(right));
+ cache.Add((left, right), ret);
+ }
+ return (T) ret;
}
///
@@ -201,7 +196,12 @@ namespace MLEM.Data {
/// The type of the values
/// The bitwise AND (&) combination
public static T And(T left, T right) where T : DynamicEnum {
- return GetEnumValue(GetValue(left) & GetValue(right));
+ var cache = GetStorage(typeof(T)).AndCache;
+ if (!cache.TryGetValue((left, right), out var ret)) {
+ ret = GetEnumValue(GetValue(left) & GetValue(right));
+ cache.Add((left, right), ret);
+ }
+ return (T) ret;
}
///
@@ -212,7 +212,12 @@ namespace MLEM.Data {
/// The type of the values
/// The bitwise XOR (^) combination
public static T Xor(T left, T right) where T : DynamicEnum {
- return GetEnumValue(GetValue(left) ^ GetValue(right));
+ var cache = GetStorage(typeof(T)).XorCache;
+ if (!cache.TryGetValue((left, right), out var ret)) {
+ ret = GetEnumValue(GetValue(left) ^ GetValue(right));
+ cache.Add((left, right), ret);
+ }
+ return (T) ret;
}
///
@@ -222,7 +227,12 @@ namespace MLEM.Data {
/// The type of the values
/// The bitwise NEG (~) value
public static T Neg(T value) where T : DynamicEnum {
- return GetEnumValue(~GetValue(value));
+ var cache = GetStorage(typeof(T)).NegCache;
+ if (!cache.TryGetValue(value, out var ret)) {
+ ret = GetEnumValue(~GetValue(value));
+ cache.Add(value, ret);
+ }
+ return (T) ret;
}
///
@@ -251,18 +261,16 @@ namespace MLEM.Data {
/// The value whose dynamic enum value to get
/// The defined or combined dynamic enum value
public static DynamicEnum GetEnumValue(Type type, BigInteger value) {
+ var storage = GetStorage(type);
+
// get the defined value if it exists
- if (Values.TryGetValue(type, out var values) && values.TryGetValue(value, out var defined))
+ if (storage.Values.TryGetValue(value, out var defined))
return defined;
// otherwise, cache the combined value
- if (!FlagCache.TryGetValue(type, out var cache)) {
- cache = new Dictionary();
- FlagCache.Add(type, cache);
- }
- if (!cache.TryGetValue(value, out var combined)) {
+ if (!storage.FlagCache.TryGetValue(value, out var combined)) {
combined = Construct(type, null, value);
- cache.Add(value, combined);
+ storage.FlagCache.Add(value, combined);
}
return combined;
}
@@ -287,10 +295,7 @@ namespace MLEM.Data {
/// The string to parse into a dynamic enum value
/// The parsed enum value, or null if parsing fails
public static DynamicEnum Parse(Type type, string strg) {
- if (!ParseCache.TryGetValue(type, out var cache)) {
- cache = new Dictionary();
- ParseCache.Add(type, cache);
- }
+ var cache = GetStorage(type).ParseCache;
if (!cache.TryGetValue(strg, out var cached)) {
BigInteger? accum = null;
foreach (var val in strg.Split('|')) {
@@ -308,10 +313,39 @@ namespace MLEM.Data {
return cached;
}
+ private static Storage GetStorage(Type type) {
+ if (!Storages.TryGetValue(type, out var storage)) {
+ storage = new Storage();
+ Storages.Add(type, storage);
+ }
+ return storage;
+ }
+
private static DynamicEnum Construct(Type type, string name, BigInteger value) {
var constructor = type.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new[] {typeof(string), typeof(BigInteger)}, null);
return (DynamicEnum) constructor.Invoke(new object[] {name, value});
}
+ private class Storage {
+
+ public readonly Dictionary Values = new Dictionary();
+ public readonly Dictionary FlagCache = new Dictionary();
+ public readonly Dictionary ParseCache = new Dictionary();
+ public readonly Dictionary<(DynamicEnum, DynamicEnum), DynamicEnum> OrCache = new Dictionary<(DynamicEnum, DynamicEnum), DynamicEnum>();
+ public readonly Dictionary<(DynamicEnum, DynamicEnum), DynamicEnum> AndCache = new Dictionary<(DynamicEnum, DynamicEnum), DynamicEnum>();
+ public readonly Dictionary<(DynamicEnum, DynamicEnum), DynamicEnum> XorCache = new Dictionary<(DynamicEnum, DynamicEnum), DynamicEnum>();
+ public readonly Dictionary NegCache = new Dictionary();
+
+ public void ClearCaches() {
+ this.FlagCache.Clear();
+ this.ParseCache.Clear();
+ this.OrCache.Clear();
+ this.AndCache.Clear();
+ this.XorCache.Clear();
+ this.NegCache.Clear();
+ }
+
+ }
+
}
}
\ No newline at end of file