1
0
Fork 0
mirror of https://github.com/Ellpeck/MLEM.git synced 2024-11-24 21:48:35 +01:00

Compare commits

..

No commits in common. "05e320d4f484f6d385cef68b44aa2e91c10b76f1" and "6537ff00c1db9e577303302b3b81ef39e106742d" have entirely different histories.

2 changed files with 39 additions and 74 deletions

View file

@ -48,7 +48,6 @@ Additions
Improvements Improvements
- Improved RawContentManager's reader loading and added better exception handling - Improved RawContentManager's reader loading and added better exception handling
- Improved CopyExtensions construction speed - Improved CopyExtensions construction speed
- Improved DynamicEnum caching
## 5.0.0 ## 5.0.0
### MLEM ### MLEM

View file

@ -12,9 +12,6 @@ namespace MLEM.Data {
/// A dynamic enum uses <see cref="BigInteger"/> as its underlying type, allowing for an arbitrary number of enum values to be created, even when a <see cref="FlagsAttribute"/>-like structure is used that would only allow for up to 64 values in a regular enum. /// A dynamic enum uses <see cref="BigInteger"/> as its underlying type, allowing for an arbitrary number of enum values to be created, even when a <see cref="FlagsAttribute"/>-like structure is used that would only allow for up to 64 values in a regular enum.
/// All enum operations including <see cref="And{T}"/>, <see cref="Or{T}"/>, <see cref="Xor{T}"/> and <see cref="Neg{T}"/> are supported and can be implemented in derived classes using operator overloads. /// All enum operations including <see cref="And{T}"/>, <see cref="Or{T}"/>, <see cref="Xor{T}"/> and <see cref="Neg{T}"/> are supported and can be implemented in derived classes using operator overloads.
/// To create a custom dynamic enum, simply create a class that extends <see cref="DynamicEnum"/>. New values can then be added using <see cref="Add{T}"/>, <see cref="AddValue{T}"/> or <see cref="AddFlag{T}"/>. /// To create a custom dynamic enum, simply create a class that extends <see cref="DynamicEnum"/>. New values can then be added using <see cref="Add{T}"/>, <see cref="AddValue{T}"/> or <see cref="AddFlag{T}"/>.
///
/// 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.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// To include enum-like operator overloads in a dynamic enum named MyEnum, the following code can be used: /// To include enum-like operator overloads in a dynamic enum named MyEnum, the following code can be used:
@ -30,7 +27,10 @@ namespace MLEM.Data {
[JsonConverter(typeof(DynamicEnumConverter))] [JsonConverter(typeof(DynamicEnumConverter))]
public abstract class DynamicEnum { public abstract class DynamicEnum {
private static readonly Dictionary<Type, Storage> Storages = new Dictionary<Type, Storage>(); 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<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> allFlagsCache;
@ -59,6 +59,7 @@ namespace MLEM.Data {
if (this.allFlagsCache == null) if (this.allFlagsCache == null)
this.allFlagsCache = new Dictionary<DynamicEnum, bool>(); this.allFlagsCache = new Dictionary<DynamicEnum, bool>();
if (!this.allFlagsCache.TryGetValue(flags, out var ret)) { 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 = (GetValue(this) & GetValue(flags)) == GetValue(flags);
this.allFlagsCache.Add(flags, ret); this.allFlagsCache.Add(flags, ret);
} }
@ -75,6 +76,7 @@ namespace MLEM.Data {
if (this.anyFlagsCache == null) if (this.anyFlagsCache == null)
this.anyFlagsCache = new Dictionary<DynamicEnum, bool>(); this.anyFlagsCache = new Dictionary<DynamicEnum, bool>();
if (!this.anyFlagsCache.TryGetValue(flags, out var ret)) { 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(this) & GetValue(flags)) != 0;
this.anyFlagsCache.Add(flags, ret); this.anyFlagsCache.Add(flags, ret);
} }
@ -105,20 +107,24 @@ namespace MLEM.Data {
/// <returns>The newly created enum value</returns> /// <returns>The newly created enum value</returns>
/// <exception cref="ArgumentException">Thrown if the name or value passed are already present</exception> /// <exception cref="ArgumentException">Thrown if the name or value passed are already present</exception>
public static T Add<T>(string name, BigInteger value) where T : DynamicEnum { public static T Add<T>(string name, BigInteger value) where T : DynamicEnum {
var storage = GetStorage(typeof(T)); if (!Values.TryGetValue(typeof(T), out var dict)) {
dict = new Dictionary<BigInteger, DynamicEnum>();
Values.Add(typeof(T), dict);
}
// cached parsed values and names might be incomplete with new values // cached parsed values and names might be incomplete with new values
storage.ClearCaches(); FlagCache.Remove(typeof(T));
ParseCache.Remove(typeof(T));
if (storage.Values.ContainsKey(value)) if (dict.ContainsKey(value))
throw new ArgumentException($"Duplicate value {value}", nameof(value)); throw new ArgumentException($"Duplicate value {value}", nameof(value));
foreach (var v in storage.Values.Values) { foreach (var v in dict.Values) {
if (v.name == name) if (v.name == name)
throw new ArgumentException($"Duplicate name {name}", nameof(name)); throw new ArgumentException($"Duplicate name {name}", nameof(name));
} }
var ret = Construct(typeof(T), name, value); var ret = Construct(typeof(T), name, value);
storage.Values.Add(value, ret); dict.Add(value, ret);
return (T) ret; return (T) ret;
} }
@ -132,8 +138,10 @@ namespace MLEM.Data {
/// <returns>The newly created enum value</returns> /// <returns>The newly created enum value</returns>
public static T AddValue<T>(string name) where T : DynamicEnum { public static T AddValue<T>(string name) where T : DynamicEnum {
BigInteger value = 0; BigInteger value = 0;
while (GetStorage(typeof(T)).Values.ContainsKey(value)) if (Values.TryGetValue(typeof(T), out var defined)) {
value++; while (defined.ContainsKey(value))
value++;
}
return Add<T>(name, value); return Add<T>(name, value);
} }
@ -147,8 +155,10 @@ namespace MLEM.Data {
/// <returns>The newly created enum value</returns> /// <returns>The newly created enum value</returns>
public static T AddFlag<T>(string name) where T : DynamicEnum { public static T AddFlag<T>(string name) where T : DynamicEnum {
BigInteger value = 0; BigInteger value = 0;
while (GetStorage(typeof(T)).Values.ContainsKey(value)) if (Values.TryGetValue(typeof(T), out var defined)) {
value <<= 1; while (defined.ContainsKey(value))
value <<= 1;
}
return Add<T>(name, value); return Add<T>(name, value);
} }
@ -169,7 +179,7 @@ namespace MLEM.Data {
/// <param name="type">The type whose values to get</param> /// <param name="type">The type whose values to get</param>
/// <returns>The defined values for the given type</returns> /// <returns>The defined values for the given type</returns>
public static IEnumerable<DynamicEnum> GetValues(Type type) { public static IEnumerable<DynamicEnum> GetValues(Type type) {
return GetStorage(type).Values.Values; return Values.TryGetValue(type, out var ret) ? ret.Values : Enumerable.Empty<DynamicEnum>();
} }
/// <summary> /// <summary>
@ -180,12 +190,7 @@ namespace MLEM.Data {
/// <typeparam name="T">The type of the values</typeparam> /// <typeparam name="T">The type of the values</typeparam>
/// <returns>The bitwise OR (|) combination</returns> /// <returns>The bitwise OR (|) combination</returns>
public static T Or<T>(T left, T right) where T : DynamicEnum { public static T Or<T>(T left, T right) where T : DynamicEnum {
var cache = GetStorage(typeof(T)).OrCache; return GetEnumValue<T>(GetValue(left) | GetValue(right));
if (!cache.TryGetValue((left, right), out var ret)) {
ret = GetEnumValue<T>(GetValue(left) | GetValue(right));
cache.Add((left, right), ret);
}
return (T) ret;
} }
/// <summary> /// <summary>
@ -196,12 +201,7 @@ namespace MLEM.Data {
/// <typeparam name="T">The type of the values</typeparam> /// <typeparam name="T">The type of the values</typeparam>
/// <returns>The bitwise AND (&amp;) combination</returns> /// <returns>The bitwise AND (&amp;) combination</returns>
public static T And<T>(T left, T right) where T : DynamicEnum { public static T And<T>(T left, T right) where T : DynamicEnum {
var cache = GetStorage(typeof(T)).AndCache; return GetEnumValue<T>(GetValue(left) & GetValue(right));
if (!cache.TryGetValue((left, right), out var ret)) {
ret = GetEnumValue<T>(GetValue(left) & GetValue(right));
cache.Add((left, right), ret);
}
return (T) ret;
} }
/// <summary> /// <summary>
@ -212,12 +212,7 @@ namespace MLEM.Data {
/// <typeparam name="T">The type of the values</typeparam> /// <typeparam name="T">The type of the values</typeparam>
/// <returns>The bitwise XOR (^) combination</returns> /// <returns>The bitwise XOR (^) combination</returns>
public static T Xor<T>(T left, T right) where T : DynamicEnum { public static T Xor<T>(T left, T right) where T : DynamicEnum {
var cache = GetStorage(typeof(T)).XorCache; return GetEnumValue<T>(GetValue(left) ^ GetValue(right));
if (!cache.TryGetValue((left, right), out var ret)) {
ret = GetEnumValue<T>(GetValue(left) ^ GetValue(right));
cache.Add((left, right), ret);
}
return (T) ret;
} }
/// <summary> /// <summary>
@ -227,12 +222,7 @@ namespace MLEM.Data {
/// <typeparam name="T">The type of the values</typeparam> /// <typeparam name="T">The type of the values</typeparam>
/// <returns>The bitwise NEG (~) value</returns> /// <returns>The bitwise NEG (~) value</returns>
public static T Neg<T>(T value) where T : DynamicEnum { public static T Neg<T>(T value) where T : DynamicEnum {
var cache = GetStorage(typeof(T)).NegCache; return GetEnumValue<T>(~GetValue(value));
if (!cache.TryGetValue(value, out var ret)) {
ret = GetEnumValue<T>(~GetValue(value));
cache.Add(value, ret);
}
return (T) ret;
} }
/// <summary> /// <summary>
@ -261,16 +251,18 @@ namespace MLEM.Data {
/// <param name="value">The value whose dynamic enum value to get</param> /// <param name="value">The value whose dynamic enum value to get</param>
/// <returns>The defined or combined dynamic enum value</returns> /// <returns>The defined or combined dynamic enum value</returns>
public static DynamicEnum GetEnumValue(Type type, BigInteger value) { public static DynamicEnum GetEnumValue(Type type, BigInteger value) {
var storage = GetStorage(type);
// get the defined value if it exists // get the defined value if it exists
if (storage.Values.TryGetValue(value, out var defined)) if (Values.TryGetValue(type, out var values) && values.TryGetValue(value, out var defined))
return defined; return defined;
// otherwise, cache the combined value // otherwise, cache the combined value
if (!storage.FlagCache.TryGetValue(value, out var combined)) { if (!FlagCache.TryGetValue(type, out var cache)) {
cache = new Dictionary<BigInteger, DynamicEnum>();
FlagCache.Add(type, cache);
}
if (!cache.TryGetValue(value, out var combined)) {
combined = Construct(type, null, value); combined = Construct(type, null, value);
storage.FlagCache.Add(value, combined); cache.Add(value, combined);
} }
return combined; return combined;
} }
@ -295,7 +287,10 @@ 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) {
var cache = GetStorage(type).ParseCache; if (!ParseCache.TryGetValue(type, out var cache)) {
cache = new Dictionary<string, DynamicEnum>();
ParseCache.Add(type, cache);
}
if (!cache.TryGetValue(strg, out var cached)) { 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('|')) {
@ -313,39 +308,10 @@ namespace MLEM.Data {
return cached; 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) { 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); 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}); return (DynamicEnum) constructor.Invoke(new object[] {name, value});
} }
private class Storage {
public readonly Dictionary<BigInteger, DynamicEnum> Values = new Dictionary<BigInteger, DynamicEnum>();
public readonly Dictionary<BigInteger, DynamicEnum> FlagCache = new Dictionary<BigInteger, DynamicEnum>();
public readonly Dictionary<string, DynamicEnum> ParseCache = new Dictionary<string, DynamicEnum>();
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<DynamicEnum, DynamicEnum> NegCache = new Dictionary<DynamicEnum, DynamicEnum>();
public void ClearCaches() {
this.FlagCache.Clear();
this.ParseCache.Clear();
this.OrCache.Clear();
this.AndCache.Clear();
this.XorCache.Clear();
this.NegCache.Clear();
}
}
} }
} }