forked from mirrors/NBTExplorer
144 lines
5.4 KiB
C#
144 lines
5.4 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
|
|
namespace Substrate.Core
|
|
{
|
|
/// <summary>
|
|
/// An LRU-based caching data structure for holding references to <see cref="ChunkRef"/> objects.
|
|
/// </summary>
|
|
/// <remarks>The <see cref="ChunkCache"/> 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.</remarks>
|
|
public class ChunkCache
|
|
{
|
|
private LRUCache<ChunkKey, ChunkRef> _cache;
|
|
private Dictionary<ChunkKey, ChunkRef> _dirty;
|
|
|
|
/// <summary>
|
|
/// Creates a new <see cref="ChunkCache"/> with the default capacity of 256 chunks.
|
|
/// </summary>
|
|
public ChunkCache ()
|
|
: this(256)
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a new <see cref="ChunkCache"/> with the given chunk capacity.
|
|
/// </summary>
|
|
/// <param name="cacheSize">The capacity of the LRU-portion of the cache.</param>
|
|
public ChunkCache (int cacheSize)
|
|
{
|
|
_cache = new LRUCache<ChunkKey, ChunkRef>(cacheSize);
|
|
_dirty = new Dictionary<ChunkKey, ChunkRef>();
|
|
|
|
_cache.RemoveCacheValue += EvictionHandler;
|
|
}
|
|
|
|
#region IChunkCache Members
|
|
|
|
/// <summary>
|
|
/// Inserts a new chunk into the cache.
|
|
/// </summary>
|
|
/// <param name="chunk">The <see cref="ChunkRef"/> to add to the cache.</param>
|
|
/// <returns><c>True</c> if the chunk did not already exist in the cache, <c>false</c> otherwise.</returns>
|
|
/// <remarks>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.</remarks>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Removes a chunk from the cache.
|
|
/// </summary>
|
|
/// <param name="key">The key identifying which <see cref="ChunkRef"/> to try and remove.</param>
|
|
/// <returns><c>True</c> if the chunk was in the LRU-portion of the cache and removed, <c>False</c> otherwise.</returns>
|
|
/// <remarks>The chunk will also be removed from the dirty list, if it is currently in it.</remarks>
|
|
public bool Remove (ChunkKey key)
|
|
{
|
|
_dirty.Remove(key);
|
|
return _cache.Remove(key);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Attempts to get a chunk from the LRU- or dirty-portions of the cache.
|
|
/// </summary>
|
|
/// <param name="key">The key identifying which <see cref="ChunkRef"/> to find.</param>
|
|
/// <returns>The cached <see cref="ChunkRef"/> if it was found anywhere in the cache, or <c>null</c> if it was not found.</returns>
|
|
/// <remarks>If the <see cref="ChunkRef"/> 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.</remarks>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets an enumerator to iterate over all of the <see cref="ChunkRef"/> objects currently in the dirty list.
|
|
/// </summary>
|
|
/// <returns>An enumerator over all of the dirty <see cref="ChunkRef"/> objects.</returns>
|
|
public IEnumerator<ChunkRef> GetDirtyEnumerator ()
|
|
{
|
|
return _dirty.Values.GetEnumerator();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Clears all chunks from the LRU list.
|
|
/// </summary>
|
|
/// <remarks>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 <see cref="ClearDirty"/>.</remarks>
|
|
public void Clear ()
|
|
{
|
|
_cache.Clear();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Clears all chunks from the dirty list.
|
|
/// </summary>
|
|
public void ClearDirty ()
|
|
{
|
|
_dirty.Clear();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Scans the LRU list for any dirty chunks, and adds them to the dirty list.
|
|
/// </summary>
|
|
public void SyncDirty ()
|
|
{
|
|
foreach (KeyValuePair<ChunkKey, ChunkRef> e in _cache) {
|
|
if (e.Value.IsDirty) {
|
|
_dirty[e.Key] = e.Value;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
private void EvictionHandler (object sender, LRUCache<ChunkKey, ChunkRef>.CacheValueArgs e)
|
|
{
|
|
if (e.Value.IsDirty) {
|
|
_dirty[e.Key] = e.Value;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|