using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Runtime.CompilerServices; using Global.Shared.Plugin; namespace Global.Shared.Util { public class UintCache { private readonly ConcurrentDictionary cache = new ConcurrentDictionary(); private readonly ulong cleanupPeriod; private readonly TK[] keysToDelete; private readonly uint maxDeleteCount; private ulong nextCleanup; private ulong tick; public UintCache(uint cleanupPeriod, uint maxDeleteCount = 64) { this.cleanupPeriod = (ulong)cleanupPeriod << 32; this.maxDeleteCount = maxDeleteCount; keysToDelete = new TK[this.maxDeleteCount]; nextCleanup = tick + cleanupPeriod; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Cleanup() { if (GlobalInstance.Plugin == null) return; if ((tick = (ulong)GlobalInstance.Plugin.Tick << 32) < nextCleanup) return; nextCleanup = tick + cleanupPeriod; var count = 0u; foreach (var (key, item) in cache) { if (item >= tick) continue; keysToDelete[count++] = key; if (count == maxDeleteCount) break; } if (count == 0) return; for (var i = 0; i < count; i++) cache.Remove(keysToDelete[i]); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Clear() { cache.Clear(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Store(TK key, uint value, uint lifetime) { var expires = tick + ((ulong)lifetime << 32); cache[key] = expires | value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Extend(TK key, uint lifetime) { if (cache.TryGetValue(key, out var item)) cache[key] = (tick + ((ulong)lifetime << 32)) | (item & 0xfffffffful); } public void Forget(TK key) { cache.Remove(key); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool TryGetValue(TK key, out uint value) { if (cache.TryGetValue(key, out var item)) if (item >= tick) { value = (uint)item; return true; } value = 0u; return false; } } }