diff --git a/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj b/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj index e36b9c5e03b..7d6448c43b6 100644 --- a/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj +++ b/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj @@ -49,7 +49,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Flow.Launcher.Infrastructure/Image/ImageCache.cs b/Flow.Launcher.Infrastructure/Image/ImageCache.cs index 55545b9a732..ddbab4ef0b1 100644 --- a/Flow.Launcher.Infrastructure/Image/ImageCache.cs +++ b/Flow.Launcher.Infrastructure/Image/ImageCache.cs @@ -3,33 +3,23 @@ using System.Collections.Generic; using System.Linq; using System.Threading; +using System.Threading.Tasks; using System.Windows.Media; -using FastCache; -using FastCache.Services; +using BitFaster.Caching.Lfu; namespace Flow.Launcher.Infrastructure.Image { - public class ImageUsage - { - public int usage; - public ImageSource imageSource; - - public ImageUsage(int usage, ImageSource image) - { - this.usage = usage; - imageSource = image; - } - } - public class ImageCache { private const int MaxCached = 150; - public void Initialize(Dictionary<(string, bool), int> usage) + private ConcurrentLfu<(string, bool), ImageSource> CacheManager { get; set; } = new(MaxCached); + + public void Initialize(IEnumerable<(string, bool)> usage) { - foreach (var key in usage.Keys) + foreach (var key in usage) { - Cached.Save(key, new ImageUsage(usage[key], null), TimeSpan.MaxValue, MaxCached); + CacheManager.AddOrUpdate(key, null); } } @@ -37,48 +27,42 @@ public void Initialize(Dictionary<(string, bool), int> usage) { get { - if (!Cached.TryGet((path, isFullImage), out var value)) - { - return null; - } - - value.Value.usage++; - return value.Value.imageSource; + return CacheManager.TryGet((path, isFullImage), out var value) ? value : null; } set { - if (Cached.TryGet((path, isFullImage), out var cached)) - { - cached.Value.imageSource = value; - cached.Value.usage++; - } - - Cached.Save((path, isFullImage), new ImageUsage(0, value), TimeSpan.MaxValue, - MaxCached); + CacheManager.AddOrUpdate((path, isFullImage), value); } } + public async ValueTask GetOrAddAsync(string key, + Func<(string, bool), Task> valueFactory, + bool isFullImage = false) + { + return await CacheManager.GetOrAddAsync((key, isFullImage), valueFactory); + } + public bool ContainsKey(string key, bool isFullImage) { - return Cached.TryGet((key, isFullImage), out _); + return CacheManager.TryGet((key, isFullImage), out _); } public bool TryGetValue(string key, bool isFullImage, out ImageSource image) { - if (Cached.TryGet((key, isFullImage), out var value)) + if (CacheManager.TryGet((key, isFullImage), out var value)) { - image = value.Value.imageSource; - value.Value.usage++; + image = value; return image != null; } + image = null; return false; } public int CacheSize() { - return CacheManager.TotalCount<(string, bool), ImageUsage>(); + return CacheManager.Count; } /// @@ -86,14 +70,14 @@ public int CacheSize() /// public int UniqueImagesInCache() { - return CacheManager.EnumerateEntries<(string, bool), ImageUsage>().Select(x => x.Value.imageSource) + return CacheManager.Select(x => x.Value) .Distinct() .Count(); } - public IEnumerable> EnumerateEntries() + public IEnumerable> EnumerateEntries() { - return CacheManager.EnumerateEntries<(string, bool), ImageUsage>(); + return CacheManager; } } } diff --git a/Flow.Launcher.Infrastructure/Image/ImageLoader.cs b/Flow.Launcher.Infrastructure/Image/ImageLoader.cs index 75c2a4ec989..612f495be64 100644 --- a/Flow.Launcher.Infrastructure/Image/ImageLoader.cs +++ b/Flow.Launcher.Infrastructure/Image/ImageLoader.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using System.Windows.Documents; using System.Windows.Media; using System.Windows.Media.Imaging; using Flow.Launcher.Infrastructure.Logger; @@ -17,7 +18,7 @@ public static class ImageLoader { private static readonly ImageCache ImageCache = new(); private static SemaphoreSlim storageLock { get; } = new SemaphoreSlim(1, 1); - private static BinaryStorage> _storage; + private static BinaryStorage> _storage; private static readonly ConcurrentDictionary GuidToKey = new(); private static IImageHashGenerator _hashGenerator; private static readonly bool EnableImageHash = true; @@ -31,12 +32,12 @@ public static class ImageLoader public static async Task InitializeAsync() { - _storage = new BinaryStorage>("Image"); + _storage = new BinaryStorage>("Image"); _hashGenerator = new ImageHashGenerator(); var usage = await LoadStorageToConcurrentDictionaryAsync(); - ImageCache.Initialize(usage.ToDictionary(x => x.Key, x => x.Value)); + ImageCache.Initialize(usage); foreach (var icon in new[] { Constant.DefaultIcon, Constant.MissingImgIcon }) { @@ -49,7 +50,7 @@ public static async Task InitializeAsync() { await Stopwatch.NormalAsync("|ImageLoader.Initialize|Preload images cost", async () => { - foreach (var ((path, isFullImage), _) in usage) + foreach (var (path, isFullImage) in usage) { await LoadAsync(path, isFullImage); } @@ -66,9 +67,8 @@ public static async Task Save() try { await _storage.SaveAsync(ImageCache.EnumerateEntries() - .ToDictionary( - x => x.Key, - x => x.Value.usage)); + .Select(x => x.Key) + .ToList()); } finally { @@ -76,14 +76,12 @@ await _storage.SaveAsync(ImageCache.EnumerateEntries() } } - private static async Task> LoadStorageToConcurrentDictionaryAsync() + private static async Task> LoadStorageToConcurrentDictionaryAsync() { await storageLock.WaitAsync(); try { - var loaded = await _storage.TryLoadAsync(new Dictionary<(string, bool), int>()); - - return new ConcurrentDictionary<(string, bool), int>(loaded); + return await _storage.TryLoadAsync(new List<(string, bool)>()); } finally {