From d77e0ba8ca03fb8455fed19b4fd0935a2d3cac63 Mon Sep 17 00:00:00 2001 From: Glenn Watson Date: Thu, 20 Jun 2019 18:32:14 +1000 Subject: [PATCH] feature: Add equality comparer to MRUCache, now uses ValueTuple --- src/Directory.build.props | 68 ++++++++++++------- ...ovalTests.SplatProject.net472.approved.txt | 44 ++++++------ ...ts.SplatProject.netcoreapp2.1.approved.txt | 42 ++++++------ src/Splat/MemoizingMRUCache.cs | 50 +++++++------- src/Splat/Splat.csproj | 1 + 5 files changed, 110 insertions(+), 95 deletions(-) diff --git a/src/Directory.build.props b/src/Directory.build.props index 5c8dfe86f..71caf5354 100644 --- a/src/Directory.build.props +++ b/src/Directory.build.props @@ -1,62 +1,78 @@ - + + true + $(NoWarn);1591;1701;1702;1705;VSX1000 + AnyCPU + $(MSBuildProjectName.Contains('Tests')) + embedded + $(MSBuildThisFileDirectory)analyzers.ruleset + + .NET Foundation and Contributors Copyright (c) .NET Foundation and Contributors MIT https://github.com/reactiveui/splat/ http://f.cl.ly/items/1307401C3x2g3F2p2Z36/Logo.png - .NET Foundation and Contributors + A library to make things cross-platform that should be. xanaisbettsx;ghuntley drawing;colours;geometry;logging;unit test detection;service location;image handling;portable;xamarin;xamarin ios;xamarin mac;android;monodroid;uwp;net45 https://github.com/reactiveui/splat/releases https://github.com/reactiveui/splat git - true - $(MSBuildThisFileDirectory)analyzers.ruleset - $(MSBuildProjectName.Contains('Tests')) - Embedded - - true - - true - - $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb + false $(EnableSourceLink) + + true + + true + + $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb + + + Full + + - + + - - + + + - + - - + + + + + $(MSBuildThisFileDirectory) + - + - - - + + + - + - + + - - + diff --git a/src/Splat.Tests/API/ApiApprovalTests.SplatProject.net472.approved.txt b/src/Splat.Tests/API/ApiApprovalTests.SplatProject.net472.approved.txt index c90ad6ebe..6164446f2 100644 --- a/src/Splat.Tests/API/ApiApprovalTests.SplatProject.net472.approved.txt +++ b/src/Splat.Tests/API/ApiApprovalTests.SplatProject.net472.approved.txt @@ -421,6 +421,10 @@ namespace Splat [System.ObsoleteAttribute("Use void Warn(Exception exception, [Localizable(false)] string message)")] void WarnException([System.ComponentModel.LocalizableAttribute(false)] string message, System.Exception exception); } + public interface ILogManager + { + Splat.IFullLogger GetLogger(System.Type type); + } public interface ILogger { Splat.LogLevel Level { get; } @@ -429,10 +433,6 @@ namespace Splat void Write([System.ComponentModel.LocalizableAttribute(false)] string message, [System.ComponentModel.LocalizableAttribute(false)] System.Type type, Splat.LogLevel logLevel); void Write(System.Exception exception, [System.ComponentModel.LocalizableAttribute(false)] string message, [System.ComponentModel.LocalizableAttribute(false)] System.Type type, Splat.LogLevel logLevel); } - public interface ILogManager - { - Splat.IFullLogger GetLogger(System.Type type); - } public interface IModeDetector { System.Nullable InDesignMode(); @@ -636,13 +636,6 @@ namespace Splat public static void SetLocator(Splat.IDependencyResolver dependencyResolver) { } public static System.IDisposable SuppressResolverCallbackChangedNotifications() { } } - public class LoggingException : System.Exception - { - public LoggingException() { } - public LoggingException(string message) { } - public LoggingException(string message, System.Exception innerException) { } - protected LoggingException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } - } public class static LogHost { public static Splat.IFullLogger Default { get; } @@ -661,9 +654,16 @@ namespace Splat { public static Splat.IFullLogger GetLogger(this Splat.ILogManager logManager) { } } + public class LoggingException : System.Exception + { + public LoggingException() { } + public LoggingException(string message) { } + public LoggingException(string message, System.Exception innerException) { } + protected LoggingException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } + } public sealed class MemoizingMRUCache { - public MemoizingMRUCache(System.Func calculationFunc, int maxSize, System.Action onRelease = null) { } + public MemoizingMRUCache(System.Func calculationFunc, int maxSize, System.Action onRelease = null, System.Collections.Generic.IEqualityComparer paramComparer = null) { } public System.Collections.Generic.IEnumerable CachedValues() { } public TVal Get(TParam key) { } public TVal Get(TParam key, object context = null) { } @@ -732,14 +732,6 @@ namespace Splat public static System.Drawing.PointF ScaledBy(this System.Drawing.PointF value, float factor) { } public static bool WithinEpsilonOf(this System.Drawing.PointF value, System.Drawing.PointF other, float epsilon) { } } - public class static RectangleMathExtensions - { - public static System.Drawing.PointF Center(this System.Drawing.RectangleF value) { } - public static System.Drawing.RectangleF Copy(this System.Drawing.RectangleF value, System.Nullable x = null, System.Nullable y = null, System.Nullable width = null, System.Nullable height = null, System.Nullable top = null, System.Nullable bottom = null) { } - public static System.Tuple Divide(this System.Drawing.RectangleF value, float amount, Splat.RectEdge fromEdge) { } - public static System.Tuple DivideWithPadding(this System.Drawing.RectangleF value, float sliceAmount, float padding, Splat.RectEdge fromEdge) { } - public static System.Drawing.RectangleF InvertWithin(this System.Drawing.RectangleF value, System.Drawing.RectangleF containingRect) { } - } public enum RectEdge { Left = 0, @@ -753,6 +745,14 @@ namespace Splat public static System.Windows.Rect ToNative(this System.Drawing.Rectangle value) { } public static System.Windows.Rect ToNative(this System.Drawing.RectangleF value) { } } + public class static RectangleMathExtensions + { + public static System.Drawing.PointF Center(this System.Drawing.RectangleF value) { } + public static System.Drawing.RectangleF Copy(this System.Drawing.RectangleF value, System.Nullable x = null, System.Nullable y = null, System.Nullable width = null, System.Nullable height = null, System.Nullable top = null, System.Nullable bottom = null) { } + public static System.Tuple Divide(this System.Drawing.RectangleF value, float amount, Splat.RectEdge fromEdge) { } + public static System.Tuple DivideWithPadding(this System.Drawing.RectangleF value, float sliceAmount, float padding, Splat.RectEdge fromEdge) { } + public static System.Drawing.RectangleF InvertWithin(this System.Drawing.RectangleF value, System.Drawing.RectangleF containingRect) { } + } public class static SizeExtensions { public static System.Drawing.SizeF FromNative(this System.Windows.Size value) { } @@ -789,11 +789,11 @@ namespace Splat public override int GetHashCode() { } public float GetHue() { } public float GetSaturation() { } - public static bool ==(Splat.SplatColor left, Splat.SplatColor right) { } - public static bool !=(Splat.SplatColor left, Splat.SplatColor right) { } public int ToArgb() { } public Splat.KnownColor ToKnownColor() { } public override string ToString() { } + public static bool ==(Splat.SplatColor left, Splat.SplatColor right) { } + public static bool !=(Splat.SplatColor left, Splat.SplatColor right) { } } public class static SplatColorExtensions { diff --git a/src/Splat.Tests/API/ApiApprovalTests.SplatProject.netcoreapp2.1.approved.txt b/src/Splat.Tests/API/ApiApprovalTests.SplatProject.netcoreapp2.1.approved.txt index ae9a42083..37c9d20a1 100644 --- a/src/Splat.Tests/API/ApiApprovalTests.SplatProject.netcoreapp2.1.approved.txt +++ b/src/Splat.Tests/API/ApiApprovalTests.SplatProject.netcoreapp2.1.approved.txt @@ -410,6 +410,10 @@ namespace Splat [System.ObsoleteAttribute("Use void Warn(Exception exception, [Localizable(false)] string message)")] void WarnException([System.ComponentModel.LocalizableAttribute(false)] string message, System.Exception exception); } + public interface ILogManager + { + Splat.IFullLogger GetLogger(System.Type type); + } public interface ILogger { Splat.LogLevel Level { get; } @@ -418,10 +422,6 @@ namespace Splat void Write([System.ComponentModel.LocalizableAttribute(false)] string message, [System.ComponentModel.LocalizableAttribute(false)] System.Type type, Splat.LogLevel logLevel); void Write(System.Exception exception, [System.ComponentModel.LocalizableAttribute(false)] string message, [System.ComponentModel.LocalizableAttribute(false)] System.Type type, Splat.LogLevel logLevel); } - public interface ILogManager - { - Splat.IFullLogger GetLogger(System.Type type); - } public interface IModeDetector { System.Nullable InDesignMode(); @@ -625,13 +625,6 @@ namespace Splat public static void SetLocator(Splat.IDependencyResolver dependencyResolver) { } public static System.IDisposable SuppressResolverCallbackChangedNotifications() { } } - public class LoggingException : System.Exception - { - public LoggingException() { } - public LoggingException(string message) { } - public LoggingException(string message, System.Exception innerException) { } - protected LoggingException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } - } public class static LogHost { public static Splat.IFullLogger Default { get; } @@ -650,9 +643,16 @@ namespace Splat { public static Splat.IFullLogger GetLogger(this Splat.ILogManager logManager) { } } + public class LoggingException : System.Exception + { + public LoggingException() { } + public LoggingException(string message) { } + public LoggingException(string message, System.Exception innerException) { } + protected LoggingException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } + } public sealed class MemoizingMRUCache { - public MemoizingMRUCache(System.Func calculationFunc, int maxSize, System.Action onRelease = null) { } + public MemoizingMRUCache(System.Func calculationFunc, int maxSize, System.Action onRelease = null, System.Collections.Generic.IEqualityComparer paramComparer = null) { } public System.Collections.Generic.IEnumerable CachedValues() { } public TVal Get(TParam key) { } public TVal Get(TParam key, object context = null) { } @@ -702,6 +702,13 @@ namespace Splat public static System.Drawing.PointF ScaledBy(this System.Drawing.PointF value, float factor) { } public static bool WithinEpsilonOf(this System.Drawing.PointF value, System.Drawing.PointF other, float epsilon) { } } + public enum RectEdge + { + Left = 0, + Top = 1, + Right = 2, + Bottom = 3, + } public class static RectangleMathExtensions { public static System.Drawing.PointF Center(this System.Drawing.RectangleF value) { } @@ -710,13 +717,6 @@ namespace Splat public static System.Tuple DivideWithPadding(this System.Drawing.RectangleF value, float sliceAmount, float padding, Splat.RectEdge fromEdge) { } public static System.Drawing.RectangleF InvertWithin(this System.Drawing.RectangleF value, System.Drawing.RectangleF containingRect) { } } - public enum RectEdge - { - Left = 0, - Top = 1, - Right = 2, - Bottom = 3, - } public class static SizeMathExtensions { public static System.Drawing.SizeF ScaledBy(this System.Drawing.SizeF value, float factor) { } @@ -747,11 +747,11 @@ namespace Splat public override int GetHashCode() { } public float GetHue() { } public float GetSaturation() { } - public static bool ==(Splat.SplatColor left, Splat.SplatColor right) { } - public static bool !=(Splat.SplatColor left, Splat.SplatColor right) { } public int ToArgb() { } public Splat.KnownColor ToKnownColor() { } public override string ToString() { } + public static bool ==(Splat.SplatColor left, Splat.SplatColor right) { } + public static bool !=(Splat.SplatColor left, Splat.SplatColor right) { } } public class static TargetFrameworkExtensions { diff --git a/src/Splat/MemoizingMRUCache.cs b/src/Splat/MemoizingMRUCache.cs index 6f1b9019a..e6ba9bab1 100644 --- a/src/Splat/MemoizingMRUCache.cs +++ b/src/Splat/MemoizingMRUCache.cs @@ -30,8 +30,10 @@ public sealed class MemoizingMRUCache private readonly Action _releaseFunction; private readonly int _maxCacheSize; + private readonly IEqualityComparer _comparer; + private LinkedList _cacheMRUList; - private Dictionary, TVal>> _cacheEntries; + private Dictionary param, TVal value)> _cacheEntries; /// /// Initializes a new instance of the class. @@ -44,7 +46,8 @@ public sealed class MemoizingMRUCache /// A function to call when a result gets /// evicted from the cache (i.e. because Invalidate was called or the /// cache is full). - public MemoizingMRUCache(Func calculationFunc, int maxSize, Action onRelease = null) + /// A comparer for the parameter. + public MemoizingMRUCache(Func calculationFunc, int maxSize, Action onRelease = null, IEqualityComparer paramComparer = null) { Contract.Requires(calculationFunc != null); Contract.Requires(maxSize > 0); @@ -52,6 +55,7 @@ public MemoizingMRUCache(Func calculationFunc, int maxSize _calculationFunction = calculationFunc; _releaseFunction = onRelease; _maxCacheSize = maxSize; + _comparer = paramComparer ?? EqualityComparer.Default; InvalidateAll(); } @@ -75,22 +79,20 @@ public TVal Get(TParam key, object context = null) { Contract.Requires(key != null); - Tuple, TVal> found; - lock (_lockObject) { - if (_cacheEntries.TryGetValue(key, out found)) + if (_cacheEntries.TryGetValue(key, out var found)) { - RefreshEntry(found.Item1); + RefreshEntry(found.param); - return found.Item2; + return found.value; } var result = _calculationFunction(key, context); var node = new LinkedListNode(key); _cacheMRUList.AddFirst(node); - _cacheEntries[key] = new Tuple, TVal>(node, result); + _cacheEntries[key] = (node, result); MaintainCache(); return result; @@ -107,15 +109,13 @@ public bool TryGet(TParam key, out TVal result) { Contract.Requires(key != null); - Tuple, TVal> output; - lock (_lockObject) { - var ret = _cacheEntries.TryGetValue(key, out output); - if (ret && output != null) + var ret = _cacheEntries.TryGetValue(key, out var output); + if (ret) { - RefreshEntry(output.Item1); - result = output.Item2; + RefreshEntry(output.param); + result = output.value; } else { @@ -135,18 +135,16 @@ public void Invalidate(TParam key) { Contract.Requires(key != null); - Tuple, TVal> to_remove; - lock (_lockObject) { - if (!_cacheEntries.TryGetValue(key, out to_remove)) + if (!_cacheEntries.TryGetValue(key, out var toRemove)) { return; } - var releaseVar = to_remove.Item2; + var releaseVar = toRemove.value; - _cacheMRUList.Remove(to_remove.Item1); + _cacheMRUList.Remove(toRemove.param); _cacheEntries.Remove(key); // moved down to allow removal from list @@ -164,13 +162,13 @@ public void Invalidate(TParam key) /// public void InvalidateAll(bool aggregateReleaseExceptions = false) { - Dictionary, TVal>> oldCacheToClear = null; + Dictionary param, TVal value)> oldCacheToClear = null; lock (_lockObject) { if (_releaseFunction == null || _cacheEntries == null) { _cacheMRUList = new LinkedList(); - _cacheEntries = new Dictionary, TVal>>(); + _cacheEntries = new Dictionary param, TVal value)>(_comparer); return; } @@ -188,7 +186,7 @@ public void InvalidateAll(bool aggregateReleaseExceptions = false) } _cacheMRUList = new LinkedList(); - _cacheEntries = new Dictionary, TVal>>(); + _cacheEntries = new Dictionary param, TVal value)>(_comparer); } if (oldCacheToClear == null) @@ -203,7 +201,7 @@ public void InvalidateAll(bool aggregateReleaseExceptions = false) { try { - _releaseFunction?.Invoke(item.Value.Item2); + _releaseFunction?.Invoke(item.Value.value); } catch (Exception e) { @@ -224,7 +222,7 @@ public void InvalidateAll(bool aggregateReleaseExceptions = false) // as the cache field was reassigned. foreach (var item in oldCacheToClear) { - _releaseFunction?.Invoke(item.Value.Item2); + _releaseFunction?.Invoke(item.Value.value); } } @@ -236,7 +234,7 @@ public IEnumerable CachedValues() { lock (_lockObject) { - return _cacheEntries.Select(x => x.Value.Item2); + return _cacheEntries.Select(x => x.Value.value); } } @@ -245,7 +243,7 @@ private void MaintainCache() while (_cacheMRUList.Count > _maxCacheSize) { var to_remove = _cacheMRUList.Last.Value; - _releaseFunction?.Invoke(_cacheEntries[to_remove].Item2); + _releaseFunction?.Invoke(_cacheEntries[to_remove].value); _cacheEntries.Remove(_cacheMRUList.Last.Value); _cacheMRUList.RemoveLast(); diff --git a/src/Splat/Splat.csproj b/src/Splat/Splat.csproj index 437440214..7774507e6 100644 --- a/src/Splat/Splat.csproj +++ b/src/Splat/Splat.csproj @@ -31,6 +31,7 @@ +