// Animancer // https://kybernetik.com.au/animancer // Copyright 2022 Kybernetik // #if UNITY_EDITOR //#define ANIMANCER_LOG_CONVERSION_CACHE using System; using System.Collections.Generic; using UnityEngine; namespace Animancer.Editor { /// [Editor-Only] /// A simple system for converting objects and storing the results so they can be reused to minimise the need for /// garbage collection, particularly for string construction. /// /// This class doesn't use any Editor-Only functionality, but it's unlikely to be useful at runtime. /// https://kybernetik.com.au/animancer/api/Animancer.Editor/ConversionCache_2 /// public class ConversionCache { /************************************************************************************************************************/ private class CachedValue { public int lastFrameAccessed; public TValue value; } /************************************************************************************************************************/ private readonly Dictionary Cache = new Dictionary(); private readonly List Keys = new List(); private readonly Func Converter; private int _LastCleanupFrame; /************************************************************************************************************************/ /// /// Creates a new which uses the specified delegate to convert values. /// public ConversionCache(Func converter) => Converter = converter; /************************************************************************************************************************/ /// /// If a value has already been cached for the specified `key`, return it. Otherwise create a new one using /// the delegate provided in the constructor and cache it. /// /// If the `key` is null, this method returns the default . /// /// This method also periodically removes values that have not been used recently. public TValue Convert(TKey key) { if (key == null) return default; CachedValue cached; // The next time a value is retrieved after at least 100 frames, clear out any old ones. var frame = Time.frameCount; if (_LastCleanupFrame + 100 < frame) { for (int i = Keys.Count - 1; i >= 0; i--) { var checkKey = Keys[i]; if (!Cache.TryGetValue(checkKey, out cached) || cached.lastFrameAccessed <= _LastCleanupFrame) { Cache.Remove(checkKey); Keys.RemoveAt(i); } } _LastCleanupFrame = frame; } if (!Cache.TryGetValue(key, out cached)) { Cache.Add(key, cached = new CachedValue { value = Converter(key) }); Keys.Add(key); } cached.lastFrameAccessed = frame; return cached.value; } /************************************************************************************************************************/ } } #endif