diff --git a/Splat/Logging.cs b/Splat/Logging.cs new file mode 100644 index 000000000..2706e96bc --- /dev/null +++ b/Splat/Logging.cs @@ -0,0 +1,611 @@ +using System; +using System.ComponentModel; +using System.Reflection; +using System.Globalization; +using System.Text; +using System.Threading; +using System.Diagnostics; +using System.Diagnostics.Contracts; + +namespace Splat +{ + /* + * Interfaces + */ + + public enum LogLevel { + Debug = 1, Info, Warn, Error, Fatal, + } + + public interface ILogger + { + void Write([Localizable(false)] string message, LogLevel logLevel); + LogLevel Level { get; set; } + } + + public interface IFullLogger : ILogger + { + void Debug(T value); + void Debug(IFormatProvider formatProvider, T value); + void DebugException([Localizable(false)] string message, Exception exception); + void Debug(IFormatProvider formatProvider, [Localizable(false)] string message, params object[] args); + void Debug([Localizable(false)] string message); + void Debug([Localizable(false)] string message, params object[] args); + void Debug(IFormatProvider formatProvider, [Localizable(false)] string message, TArgument argument); + void Debug([Localizable(false)] string message, TArgument argument); + void Debug(IFormatProvider formatProvider, [Localizable(false)] string message, TArgument1 argument1, TArgument2 argument2); + void Debug([Localizable(false)] string message, TArgument1 argument1, TArgument2 argument2); + void Debug(IFormatProvider formatProvider, [Localizable(false)] string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3); + void Debug([Localizable(false)] string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3); + + void Info(T value); + void Info(IFormatProvider formatProvider, T value); + void InfoException([Localizable(false)] string message, Exception exception); + void Info(IFormatProvider formatProvider, [Localizable(false)] string message, params object[] args); + void Info([Localizable(false)] string message); + void Info([Localizable(false)] string message, params object[] args); + void Info(IFormatProvider formatProvider, [Localizable(false)] string message, TArgument argument); + void Info([Localizable(false)] string message, TArgument argument); + void Info(IFormatProvider formatProvider, [Localizable(false)] string message, TArgument1 argument1, TArgument2 argument2); + void Info([Localizable(false)] string message, TArgument1 argument1, TArgument2 argument2); + void Info(IFormatProvider formatProvider, [Localizable(false)] string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3); + void Info([Localizable(false)] string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3); + + void Warn(T value); + void Warn(IFormatProvider formatProvider, T value); + void WarnException([Localizable(false)] string message, Exception exception); + void Warn(IFormatProvider formatProvider, [Localizable(false)] string message, params object[] args); + void Warn([Localizable(false)] string message); + void Warn([Localizable(false)] string message, params object[] args); + void Warn(IFormatProvider formatProvider, [Localizable(false)] string message, TArgument argument); + void Warn([Localizable(false)] string message, TArgument argument); + void Warn(IFormatProvider formatProvider, [Localizable(false)] string message, TArgument1 argument1, TArgument2 argument2); + void Warn([Localizable(false)] string message, TArgument1 argument1, TArgument2 argument2); + void Warn(IFormatProvider formatProvider, [Localizable(false)] string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3); + void Warn([Localizable(false)] string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3); + + void Error(T value); + void Error(IFormatProvider formatProvider, T value); + void ErrorException([Localizable(false)] string message, Exception exception); + void Error(IFormatProvider formatProvider, [Localizable(false)] string message, params object[] args); + void Error([Localizable(false)] string message); + void Error([Localizable(false)] string message, params object[] args); + void Error(IFormatProvider formatProvider, [Localizable(false)] string message, TArgument argument); + void Error([Localizable(false)] string message, TArgument argument); + void Error(IFormatProvider formatProvider, [Localizable(false)] string message, TArgument1 argument1, TArgument2 argument2); + void Error([Localizable(false)] string message, TArgument1 argument1, TArgument2 argument2); + void Error(IFormatProvider formatProvider, [Localizable(false)] string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3); + void Error([Localizable(false)] string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3); + + void Fatal(T value); + void Fatal(IFormatProvider formatProvider, T value); + void FatalException([Localizable(false)] string message, Exception exception); + void Fatal(IFormatProvider formatProvider, [Localizable(false)] string message, params object[] args); + void Fatal([Localizable(false)] string message); + void Fatal([Localizable(false)] string message, params object[] args); + void Fatal(IFormatProvider formatProvider, [Localizable(false)] string message, TArgument argument); + void Fatal([Localizable(false)] string message, TArgument argument); + void Fatal(IFormatProvider formatProvider, [Localizable(false)] string message, TArgument1 argument1, TArgument2 argument2); + void Fatal([Localizable(false)] string message, TArgument1 argument1, TArgument2 argument2); + void Fatal(IFormatProvider formatProvider, [Localizable(false)] string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3); + void Fatal([Localizable(false)] string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3); + } + + public interface ILogManager + { + IFullLogger GetLogger(Type type); + } + + class DefaultLogManager : ILogManager + { + readonly MemoizingMRUCache loggerCache; + + public DefaultLogManager(IDependencyResolver dependencyResolver = null) + { + dependencyResolver = dependencyResolver ?? Locator.Current; + + loggerCache = new MemoizingMRUCache((type, _) => { + var ret = dependencyResolver.GetService(); + if (ret == null) { + throw new Exception("Couldn't find an ILogger. This should never happen, your dependency resolver is probably broken."); + } + + return new WrappingFullLogger(ret, type); + }, 64); + } + + static readonly IFullLogger nullLogger = new WrappingFullLogger(new NullLogger(), typeof(MemoizingMRUCache)); + public IFullLogger GetLogger(Type type) + { + if (LogHost.suppressLogging) return nullLogger; + if (type == typeof(MemoizingMRUCache)) return nullLogger; + + lock (loggerCache) { + return loggerCache.Get(type); + } + } + } + + public class FuncLogManager : ILogManager + { + readonly Func _inner; + public FuncLogManager(Func getLogger) + { + _inner = getLogger; + } + + public IFullLogger GetLogger(Type type) + { + return _inner(type); + } + } + + public static class LogManagerMixin + { + public static IFullLogger GetLogger(this ILogManager This) + { + return This.GetLogger(typeof(T)); + } + } + + public class NullLogger : ILogger + { + public void Write(string message, LogLevel logLevel) {} + public LogLevel Level { get; set; } + } + + public class DebugLogger : ILogger + { + public void Write(string message, LogLevel logLevel) + { + if ((int)logLevel < (int)Level) return; + Debug.WriteLine(message); + } + + public LogLevel Level { get; set; } + } + + + /* + * LogHost / Logging Mixin + */ + + /// + /// "Implement" this interface in your class to get access to the Log() + /// Mixin, which will give you a Logger that includes the class name in the + /// log. + /// + public interface IEnableLogger { } + + public static class LogHost + { + static internal bool suppressLogging = false; + static readonly IFullLogger nullLogger = new WrappingFullLogger(new NullLogger(), typeof(string)); + + /// + /// Use this logger inside miscellaneous static methods where creating + /// a class-specific logger isn't really worth it. + /// + public static IFullLogger Default { + get { + if (suppressLogging) return nullLogger; + + var factory = Locator.Current.GetService(); + if (factory == null) { + throw new Exception("ILogManager is null. This should never happen, your dependency resolver is broken"); + } + return factory.GetLogger(typeof(LogHost)); + } + } + + /// + /// Call this method to write log entries on behalf of the current + /// class. + /// + public static IFullLogger Log(this T This) where T : IEnableLogger + { + if (suppressLogging) return nullLogger; + + var factory = Locator.Current.GetService(); + if (factory == null) { + throw new Exception("ILogManager is null. This should never happen, your dependency resolver is broken"); + } + + return factory.GetLogger(); + } + } + + #region Extremely Dull Code Ahead + internal class WrappingFullLogger : IFullLogger + { + readonly ILogger _inner; + readonly string prefix; + readonly MethodInfo stringFormat; + + public WrappingFullLogger(ILogger inner, Type callingType) + { + _inner = inner; + prefix = String.Format(CultureInfo.InvariantCulture, "{0}: ", callingType.Name); + + stringFormat = typeof (String).GetMethod("Format", new[] {typeof (IFormatProvider), typeof (string), typeof (object[])}); + Contract.Requires(inner != null); + Contract.Requires(stringFormat != null); + } + + public void Debug(T value) + { + _inner.Write(prefix + value, LogLevel.Debug); + } + + public void Debug(IFormatProvider formatProvider, T value) + { + _inner.Write(String.Format(formatProvider, "{0}{1}", prefix, value), LogLevel.Debug); + } + + public void DebugException(string message, Exception exception) + { + _inner.Write(String.Format("{0}{1}: {2}", prefix, message, exception), LogLevel.Debug); + } + + public void Debug(IFormatProvider formatProvider, string message, params object[] args) + { + var sfArgs = new object[args.Length + 2]; + sfArgs[0] = formatProvider; sfArgs[1] = message; + Array.Copy(args, 0, sfArgs, 2, args.Length); + string result = (string)stringFormat.Invoke(null, sfArgs); + + _inner.Write(prefix + result, LogLevel.Debug); + } + + public void Debug(string message) + { + _inner.Write(prefix + message, LogLevel.Debug); + } + + public void Debug(string message, params object[] args) + { + var sfArgs = new object[args.Length + 2]; + sfArgs[0] = CultureInfo.InvariantCulture; sfArgs[1] = message; + Array.Copy(args, 0, sfArgs, 2, args.Length); + string result = (string)stringFormat.Invoke(null, sfArgs); + + _inner.Write(prefix + result, LogLevel.Debug); + } + + public void Debug(IFormatProvider formatProvider, string message, TArgument argument) + { + _inner.Write(prefix + String.Format(formatProvider, message, argument), LogLevel.Debug); + } + + public void Debug(string message, TArgument argument) + { + _inner.Write(prefix + String.Format(CultureInfo.InvariantCulture, message, argument), LogLevel.Debug); + } + + public void Debug(IFormatProvider formatProvider, string message, TArgument1 argument1, TArgument2 argument2) + { + _inner.Write(prefix + String.Format(formatProvider, message, argument1, argument2), LogLevel.Debug); + } + + public void Debug(string message, TArgument1 argument1, TArgument2 argument2) + { + _inner.Write(prefix + String.Format(CultureInfo.InvariantCulture, message, argument1, argument2), LogLevel.Debug); + } + + public void Debug(IFormatProvider formatProvider, string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3) + { + _inner.Write(prefix + String.Format(formatProvider, message, argument1, argument2, argument3), LogLevel.Debug); + } + + public void Debug(string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3) + { + _inner.Write(prefix + String.Format(CultureInfo.InvariantCulture, message, argument1, argument2, argument3), LogLevel.Debug); + } + + public void Info(T value) + { + _inner.Write(prefix + value, LogLevel.Info); + } + + public void Info(IFormatProvider formatProvider, T value) + { + _inner.Write(String.Format(formatProvider, "{0}{1}", prefix, value), LogLevel.Info); + } + + public void InfoException(string message, Exception exception) + { + _inner.Write(String.Format("{0}{1}: {2}", prefix, message, exception), LogLevel.Info); + } + + public void Info(IFormatProvider formatProvider, string message, params object[] args) + { + var sfArgs = new object[args.Length + 2]; + sfArgs[0] = formatProvider; sfArgs[1] = message; + Array.Copy(args, 0, sfArgs, 2, args.Length); + string result = (string)stringFormat.Invoke(null, sfArgs); + + _inner.Write(prefix + result, LogLevel.Info); + } + + public void Info(string message) + { + _inner.Write(prefix + message, LogLevel.Info); + } + + public void Info(string message, params object[] args) + { + var sfArgs = new object[args.Length + 2]; + sfArgs[0] = CultureInfo.InvariantCulture; sfArgs[1] = message; + Array.Copy(args, 0, sfArgs, 2, args.Length); + string result = (string)stringFormat.Invoke(null, sfArgs); + + _inner.Write(prefix + result, LogLevel.Info); + } + + public void Info(IFormatProvider formatProvider, string message, TArgument argument) + { + _inner.Write(prefix + String.Format(formatProvider, message, argument), LogLevel.Info); + } + + public void Info(string message, TArgument argument) + { + _inner.Write(prefix + String.Format(CultureInfo.InvariantCulture, message, argument), LogLevel.Info); + } + + public void Info(IFormatProvider formatProvider, string message, TArgument1 argument1, TArgument2 argument2) + { + _inner.Write(prefix + String.Format(formatProvider, message, argument1, argument2), LogLevel.Info); + } + + public void Info(string message, TArgument1 argument1, TArgument2 argument2) + { + _inner.Write(prefix + String.Format(CultureInfo.InvariantCulture, message, argument1, argument2), LogLevel.Info); + } + + public void Info(IFormatProvider formatProvider, string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3) + { + _inner.Write(prefix + String.Format(formatProvider, message, argument1, argument2, argument3), LogLevel.Info); + } + + public void Info(string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3) + { + _inner.Write(prefix + String.Format(CultureInfo.InvariantCulture, message, argument1, argument2, argument3), LogLevel.Info); + } + + public void Warn(T value) + { + _inner.Write(prefix + value, LogLevel.Warn); + } + + public void Warn(IFormatProvider formatProvider, T value) + { + _inner.Write(String.Format(formatProvider, "{0}{1}", prefix, value), LogLevel.Warn); + } + + public void WarnException(string message, Exception exception) + { + _inner.Write(String.Format("{0}{1}: {2}", prefix, message, exception), LogLevel.Warn); + } + + public void Warn(IFormatProvider formatProvider, string message, params object[] args) + { + var sfArgs = new object[args.Length + 2]; + sfArgs[0] = formatProvider; sfArgs[1] = message; + Array.Copy(args, 0, sfArgs, 2, args.Length); + string result = (string)stringFormat.Invoke(null, sfArgs); + + _inner.Write(prefix + result, LogLevel.Warn); + } + + public void Warn(string message) + { + _inner.Write(prefix + message, LogLevel.Warn); + } + + public void Warn(string message, params object[] args) + { + var sfArgs = new object[args.Length + 2]; + sfArgs[0] = CultureInfo.InvariantCulture; sfArgs[1] = message; + Array.Copy(args, 0, sfArgs, 2, args.Length); + string result = (string)stringFormat.Invoke(null, sfArgs); + + _inner.Write(prefix + result, LogLevel.Warn); + } + + public void Warn(IFormatProvider formatProvider, string message, TArgument argument) + { + _inner.Write(prefix + String.Format(formatProvider, message, argument), LogLevel.Warn); + } + + public void Warn(string message, TArgument argument) + { + _inner.Write(prefix + String.Format(CultureInfo.InvariantCulture, message, argument), LogLevel.Warn); + } + + public void Warn(IFormatProvider formatProvider, string message, TArgument1 argument1, TArgument2 argument2) + { + _inner.Write(prefix + String.Format(formatProvider, message, argument1, argument2), LogLevel.Warn); + } + + public void Warn(string message, TArgument1 argument1, TArgument2 argument2) + { + _inner.Write(prefix + String.Format(CultureInfo.InvariantCulture, message, argument1, argument2), LogLevel.Warn); + } + + public void Warn(IFormatProvider formatProvider, string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3) + { + _inner.Write(prefix + String.Format(formatProvider, message, argument1, argument2, argument3), LogLevel.Warn); + } + + public void Warn(string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3) + { + _inner.Write(prefix + String.Format(CultureInfo.InvariantCulture, message, argument1, argument2, argument3), LogLevel.Warn); + } + + + public void Error(T value) + { + _inner.Write(prefix + value, LogLevel.Error); + } + + public void Error(IFormatProvider formatProvider, T value) + { + _inner.Write(String.Format(formatProvider, "{0}{1}", prefix, value), LogLevel.Error); + } + + public void ErrorException(string message, Exception exception) + { + _inner.Write(String.Format("{0}{1}: {2}", prefix, message, exception), LogLevel.Error); + } + + public void Error(IFormatProvider formatProvider, string message, params object[] args) + { + var sfArgs = new object[args.Length + 2]; + sfArgs[0] = formatProvider; sfArgs[1] = message; + Array.Copy(args, 0, sfArgs, 2, args.Length); + string result = (string)stringFormat.Invoke(null, sfArgs); + + _inner.Write(prefix + result, LogLevel.Error); + } + + public void Error(string message) + { + _inner.Write(prefix + message, LogLevel.Error); + } + + public void Error(string message, params object[] args) + { + var sfArgs = new object[args.Length + 2]; + sfArgs[0] = CultureInfo.InvariantCulture; sfArgs[1] = message; + Array.Copy(args, 0, sfArgs, 2, args.Length); + string result = (string)stringFormat.Invoke(null, sfArgs); + + _inner.Write(prefix + result, LogLevel.Error); + } + + public void Error(IFormatProvider formatProvider, string message, TArgument argument) + { + _inner.Write(prefix + String.Format(formatProvider, message, argument), LogLevel.Error); + } + + public void Error(string message, TArgument argument) + { + _inner.Write(prefix + String.Format(CultureInfo.InvariantCulture, message, argument), LogLevel.Error); + } + + public void Error(IFormatProvider formatProvider, string message, TArgument1 argument1, TArgument2 argument2) + { + _inner.Write(prefix + String.Format(formatProvider, message, argument1, argument2), LogLevel.Error); + } + + public void Error(string message, TArgument1 argument1, TArgument2 argument2) + { + _inner.Write(prefix + String.Format(CultureInfo.InvariantCulture, message, argument1, argument2), LogLevel.Error); + } + + public void Error(IFormatProvider formatProvider, string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3) + { + _inner.Write(prefix + String.Format(formatProvider, message, argument1, argument2, argument3), LogLevel.Error); + } + + public void Error(string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3) + { + _inner.Write(prefix + String.Format(CultureInfo.InvariantCulture, message, argument1, argument2, argument3), LogLevel.Error); + } + + + public void Fatal(T value) + { + _inner.Write(prefix + value, LogLevel.Fatal); + } + + public void Fatal(IFormatProvider formatProvider, T value) + { + _inner.Write(String.Format(formatProvider, "{0}{1}", prefix, value), LogLevel.Fatal); + } + + public void FatalException(string message, Exception exception) + { + _inner.Write(String.Format("{0}{1}: {2}", prefix, message, exception), LogLevel.Fatal); + } + + public void Fatal(IFormatProvider formatProvider, string message, params object[] args) + { + var sfArgs = new object[args.Length + 2]; + sfArgs[0] = formatProvider; sfArgs[1] = message; + Array.Copy(args, 0, sfArgs, 2, args.Length); + string result = (string)stringFormat.Invoke(null, sfArgs); + + _inner.Write(prefix + result, LogLevel.Fatal); + } + + public void Fatal(string message) + { + _inner.Write(prefix + message, LogLevel.Fatal); + } + + public void Fatal(string message, params object[] args) + { + var sfArgs = new object[args.Length + 2]; + sfArgs[0] = CultureInfo.InvariantCulture; sfArgs[1] = message; + Array.Copy(args, 0, sfArgs, 2, args.Length); + string result = (string)stringFormat.Invoke(null, sfArgs); + + _inner.Write(prefix + result, LogLevel.Fatal); + } + + public void Fatal(IFormatProvider formatProvider, string message, TArgument argument) + { + _inner.Write(prefix + String.Format(formatProvider, message, argument), LogLevel.Fatal); + } + + public void Fatal(string message, TArgument argument) + { + _inner.Write(prefix + String.Format(CultureInfo.InvariantCulture, message, argument), LogLevel.Fatal); + } + + public void Fatal(IFormatProvider formatProvider, string message, TArgument1 argument1, TArgument2 argument2) + { + _inner.Write(prefix + String.Format(formatProvider, message, argument1, argument2), LogLevel.Fatal); + } + + public void Fatal(string message, TArgument1 argument1, TArgument2 argument2) + { + _inner.Write(prefix + String.Format(CultureInfo.InvariantCulture, message, argument1, argument2), LogLevel.Fatal); + } + + public void Fatal(IFormatProvider formatProvider, string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3) + { + _inner.Write(prefix + String.Format(formatProvider, message, argument1, argument2, argument3), LogLevel.Fatal); + } + + public void Fatal(string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3) + { + _inner.Write(prefix + String.Format(CultureInfo.InvariantCulture, message, argument1, argument2, argument3), LogLevel.Fatal); + } + + public void Write([Localizable(false)] string message, LogLevel logLevel) + { + _inner.Write(message, logLevel); + } + + public LogLevel Level { + get { return _inner.Level; } + set { _inner.Level = value; } + } + } + #endregion + + #if PORTABLE + [AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = true)] + public sealed class LocalizableAttribute : Attribute + { + // This is a positional argument + public LocalizableAttribute(bool isLocalizable) + { + } + } + #endif +} + +// vim: tw=120 ts=4 sw=4 et : \ No newline at end of file diff --git a/Splat/MemoizingMRUCache.cs b/Splat/MemoizingMRUCache.cs new file mode 100644 index 000000000..c51f21759 --- /dev/null +++ b/Splat/MemoizingMRUCache.cs @@ -0,0 +1,165 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Diagnostics.Contracts; + +namespace Splat +{ + /// + /// This data structure is a representation of a memoizing cache - i.e. a + /// class that will evaluate a function, but keep a cache of recently + /// evaluated parameters. + /// + /// Since this is a memoizing cache, it is important that this function be a + /// "pure" function in the mathematical sense - that a key *always* maps to + /// a corresponding return value. + /// + /// The type of the parameter to the calculation function. + /// The type of the value returned by the calculation + /// function. + public class MemoizingMRUCache + { + private readonly Func calculationFunction; + private readonly Action releaseFunction; + private readonly int maxCacheSize; + + private LinkedList cacheMRUList; + private Dictionary, TVal>> cacheEntries; + + /// + /// Constructor + /// + /// The function whose results you want to cache, + /// which is provided the key value, and an Tag object that is + /// user-defined + /// The size of the cache to maintain, after which old + /// items will start to be thrown out. + /// 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) + { + Contract.Requires(calculationFunc != null); + Contract.Requires(maxSize > 0); + + calculationFunction = calculationFunc; + releaseFunction = onRelease; + maxCacheSize = maxSize; + InvalidateAll(); + } + + public TVal Get(TParam key) { return Get(key, null); } + + /// + /// Evaluates the function provided, returning the cached value if possible + /// + /// The value to pass to the calculation function. + /// An additional optional user-specific parameter. + /// + public TVal Get(TParam key, object context = null) + { + Contract.Requires(key != null); + + if (cacheEntries.ContainsKey(key)) { + var found = cacheEntries[key]; + cacheMRUList.Remove(found.Item1); + cacheMRUList.AddFirst(found.Item1); + return found.Item2; + } + + var result = calculationFunction(key, context); + + var node = new LinkedListNode(key); + cacheMRUList.AddFirst(node); + cacheEntries[key] = new Tuple, TVal>(node, result); + maintainCache(); + + return result; + } + + public bool TryGet(TParam key, out TVal result) + { + Contract.Requires(key != null); + + Tuple, TVal> output; + var ret = cacheEntries.TryGetValue(key, out output); + if (ret && output != null) { + cacheMRUList.Remove(output.Item1); + cacheMRUList.AddFirst(output.Item1); + result = output.Item2; + } else { + result = default(TVal); + } + return ret; + } + + /// + /// Ensure that the next time this key is queried, the calculation + /// function will be called. + /// + public void Invalidate(TParam key) + { + Contract.Requires(key != null); + + if (!cacheEntries.ContainsKey(key)) + return; + + var to_remove = cacheEntries[key]; + if (releaseFunction != null) + releaseFunction(to_remove.Item2); + + cacheMRUList.Remove(to_remove.Item1); + cacheEntries.Remove(key); + } + + /// + /// Invalidate all items in the cache + /// + public void InvalidateAll() + { + if (releaseFunction == null || cacheEntries == null) { + cacheMRUList = new LinkedList(); + cacheEntries = new Dictionary, TVal>>(); + return; + } + + if (cacheEntries.Count == 0) + return; + + /* We have to remove them one-by-one to call the release function + * We ToArray() this so we don't get a "modifying collection while + * enumerating" exception. */ + foreach (var v in cacheEntries.Keys.ToArray()) { Invalidate(v); } + } + + /// + /// Returns all values currently in the cache + /// + /// + public IEnumerable CachedValues() + { + return cacheEntries.Select(x => x.Value.Item2); + } + + void maintainCache() + { + while (cacheMRUList.Count > maxCacheSize) { + var to_remove = cacheMRUList.Last.Value; + if (releaseFunction != null) + releaseFunction(cacheEntries[to_remove].Item2); + + cacheEntries.Remove(cacheMRUList.Last.Value); + cacheMRUList.RemoveLast(); + } + } + + [ContractInvariantMethod] + void Invariants() + { + Contract.Invariant(cacheEntries.Count == cacheMRUList.Count); + Contract.Invariant(cacheEntries.Count <= maxCacheSize); + } + } +} + +// vim: tw=120 ts=4 sw=4 et : \ No newline at end of file diff --git a/Splat/Splat-Net45.csproj b/Splat/Splat-Net45.csproj index bfb558d31..766335223 100644 --- a/Splat/Splat-Net45.csproj +++ b/Splat/Splat-Net45.csproj @@ -54,6 +54,8 @@ + + diff --git a/Splat/Splat-NetCore45.csproj b/Splat/Splat-NetCore45.csproj index 498f98df1..5e8c14b77 100644 --- a/Splat/Splat-NetCore45.csproj +++ b/Splat/Splat-NetCore45.csproj @@ -121,6 +121,8 @@ + + diff --git a/Splat/Splat-Portable.csproj b/Splat/Splat-Portable.csproj index f1c72ea23..43a86bbc5 100644 --- a/Splat/Splat-Portable.csproj +++ b/Splat/Splat-Portable.csproj @@ -22,15 +22,14 @@ full false bin\Debug\Portable-Net45+WinRT45+WP8\ - DEBUG;TRACE + DEBUG;TRACE;PORTABLE prompt 4 - pdbonly true bin\Release\Portable-Net45+WinRT45+WP8\ - TRACE + TRACE;PORTABLE prompt 4 @@ -58,6 +57,8 @@ + +