using System; using System.Collections.Generic; namespace Substrate.Core { /// /// An LRU-based caching data structure for holding references to objects. /// /// The class maintains a separate dictionary on the side for tracking /// dirty chunks. References to dirty chunks will still be held even if the chunk has been evicted from /// the normal LRU cache. The dirty list is reset when the world is saved, or manually cleared. public class ChunkCache { private LRUCache _cache; private Dictionary _dirty; /// /// Creates a new with the default capacity of 256 chunks. /// public ChunkCache () : this(256) { } /// /// Creates a new with the given chunk capacity. /// /// The capacity of the LRU-portion of the cache. public ChunkCache (int cacheSize) { _cache = new LRUCache(cacheSize); _dirty = new Dictionary(); _cache.RemoveCacheValue += EvictionHandler; } #region IChunkCache Members /// /// Inserts a new chunk into the cache. /// /// The to add to the cache. /// True if the chunk did not already exist in the cache, false otherwise. /// If the chunk does not already exist and the list is full, then the least-recently used chunk in the cache will be evicted /// to make room for the new chunk. If the chunk is present in the dirty list, it will be removed. public bool Insert (ChunkRef chunk) { ChunkKey key = new ChunkKey(chunk.X, chunk.Z); _dirty.Remove(key); ChunkRef c; if (!_cache.TryGetValue(key, out c)) { _cache[key] = chunk; return true; } return false; } /// /// Removes a chunk from the cache. /// /// The key identifying which to try and remove. /// True if the chunk was in the LRU-portion of the cache and removed, False otherwise. /// The chunk will also be removed from the dirty list, if it is currently in it. public bool Remove (ChunkKey key) { _dirty.Remove(key); return _cache.Remove(key); } /// /// Attempts to get a chunk from the LRU- or dirty-portions of the cache. /// /// The key identifying which to find. /// The cached if it was found anywhere in the cache, or null if it was not found. /// If the is found in the LRU-portion of the cache, it will be moved to the front of the /// LRU list, making future eviction less likely. public ChunkRef Fetch (ChunkKey key) { ChunkRef c; if (_dirty.TryGetValue(key, out c)) { return c; } if (_cache.TryGetValue(key, out c)) { return c; } return null; } /// /// Gets an enumerator to iterate over all of the objects currently in the dirty list. /// /// An enumerator over all of the dirty objects. public IEnumerator GetDirtyEnumerator () { return _dirty.Values.GetEnumerator(); } /// /// Clears all chunks from the LRU list. /// /// This method will clear all chunks from the LRU-portion of the cache, including any chunks that are /// dirty but have not yet been discovered and added to the dirty list. Chunks already in the dirty list will /// not be affected. To clear dirty chunks as well, see . public void Clear () { _cache.Clear(); } /// /// Clears all chunks from the dirty list. /// public void ClearDirty () { _dirty.Clear(); } /// /// Scans the LRU list for any dirty chunks, and adds them to the dirty list. /// public void SyncDirty () { foreach (KeyValuePair e in _cache) { if (e.Value.IsDirty) { _dirty[e.Key] = e.Value; } } } private void EvictionHandler (object sender, LRUCache.CacheValueArgs e) { if (e.Value.IsDirty) { _dirty[e.Key] = e.Value; } } #endregion } }