diff --git a/IPA.Loader/Loader/PluginExecutor.cs b/IPA.Loader/Loader/PluginExecutor.cs index f217f1cd..ae946914 100644 --- a/IPA.Loader/Loader/PluginExecutor.cs +++ b/IPA.Loader/Loader/PluginExecutor.cs @@ -96,7 +96,6 @@ namespace IPA.Loader throw new InvalidOperationException($"Method {method} on {type.FullName} has both an [Init] attribute and a lifecycle attribute."); } - // TODO: how do I make this work for .NET 3? FEC.LightExpression but hacked to work on .NET 3? var metaParam = Expression.Parameter(typeof(PluginMetadata), "meta"); var objVar = ExpressionEx.Variable(type, "objVar"); var persistVar = ExpressionEx.Variable(typeof(object), "persistVar"); diff --git a/IPA.Loader/Utilities/EnumerableExtensions.cs b/IPA.Loader/Utilities/EnumerableExtensions.cs index 2a3d14e6..8cb732cb 100644 --- a/IPA.Loader/Utilities/EnumerableExtensions.cs +++ b/IPA.Loader/Utilities/EnumerableExtensions.cs @@ -33,10 +33,53 @@ namespace IPA.Utilities this.first = first; } - public IEnumerator GetEnumerator() - { // TODO: a custom impl that is less garbage - yield return first; - foreach (var v in rest) yield return v; + public IEnumerator GetEnumerator() => new PrependEnumerator(this); + + private sealed class PrependEnumerator : IEnumerator + { + private readonly IEnumerator restEnum; + private readonly PrependEnumerable enumerable; + private int state = 0; + public PrependEnumerator(PrependEnumerable enumerable) + { + this.enumerable = enumerable; + restEnum = enumerable.rest.GetEnumerator(); + } + + public T Current { get; private set; } + + object IEnumerator.Current => Current; + + public void Dispose() => restEnum.Dispose(); + + public bool MoveNext() + { + switch (state) + { + case 0: + Current = enumerable.first; + state++; + return true; + case 1: + if (!restEnum.MoveNext()) + { + state = 2; + return false; + } + else + Current = restEnum.Current; + return true; + case 2: + default: + return false; + } + } + + public void Reset() + { + restEnum.Reset(); + state = 0; + } } IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); @@ -63,10 +106,53 @@ namespace IPA.Utilities this.last = last; } - public IEnumerator GetEnumerator() - { // TODO: a custom impl that is less garbage - foreach (var v in rest) yield return v; - yield return last; + public IEnumerator GetEnumerator() => new AppendEnumerator(this); + + private sealed class AppendEnumerator : IEnumerator + { + private readonly IEnumerator restEnum; + private readonly AppendEnumerable enumerable; + private int state = 0; + public AppendEnumerator(AppendEnumerable enumerable) + { + this.enumerable = enumerable; + restEnum = enumerable.rest.GetEnumerator(); + } + + public T Current { get; private set; } + + object IEnumerator.Current => Current; + + public void Dispose() => restEnum.Dispose(); + + public bool MoveNext() + { + switch (state) + { + case 0: + if (!restEnum.MoveNext()) + { + state = 1; + goto case 1; + } + else + Current = restEnum.Current; + return true; + case 1: + Current = enumerable.last; + state++; + return true; + case 2: + default: + return false; + } + } + + public void Reset() + { + restEnum.Reset(); + state = 0; + } } IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); diff --git a/Refs/Unity.TextMeshPro.dll b/Refs/Unity.TextMeshPro.dll index 87b5d2bb..d0cb01d8 100644 Binary files a/Refs/Unity.TextMeshPro.dll and b/Refs/Unity.TextMeshPro.dll differ