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
}
}