Browse Source

LRUCache

pull/169/head
Sebastian Stehle 9 years ago
parent
commit
e4651272a9
  1. 108
      src/Squidex.Infrastructure/Caching/LRUCache.cs
  2. 18
      src/Squidex.Infrastructure/Caching/LRUCacheItem.cs
  3. 68
      tests/Squidex.Infrastructure.Tests/Caching/LRUCacheTests.cs

108
src/Squidex.Infrastructure/Caching/LRUCache.cs

@ -0,0 +1,108 @@
// ==========================================================================
// LRUCache.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System.Collections.Generic;
namespace Squidex.Infrastructure.Caching
{
public sealed class LRUCache
{
private readonly Dictionary<object, LinkedListNode<LRUCacheItem>> cacheMap = new Dictionary<object, LinkedListNode<LRUCacheItem>>();
private readonly LinkedList<LRUCacheItem> cacheHistory = new LinkedList<LRUCacheItem>();
private readonly int capacity;
public LRUCache(int capacity)
{
Guard.GreaterThan(capacity, 0, nameof(capacity));
this.capacity = capacity;
}
public bool Set(object key, object value)
{
Guard.NotNull(key, nameof(key));
if (cacheMap.TryGetValue(key, out var node))
{
node.Value.Value = value;
cacheHistory.Remove(node);
cacheHistory.AddLast(node);
cacheMap[key] = node;
return true;
}
else
{
if (cacheMap.Count >= capacity)
{
RemoveFirst();
}
var cacheItem = new LRUCacheItem { Key = key, Value = value };
node = new LinkedListNode<LRUCacheItem>(cacheItem);
cacheMap.Add(key, node);
cacheHistory.AddLast(node);
return false;
}
}
public bool Remove(object key)
{
Guard.NotNull(key, nameof(key));
if (cacheMap.TryGetValue(key, out var node))
{
cacheMap.Remove(key);
cacheHistory.Remove(node);
return true;
}
return false;
}
public bool TryGetValue(object key, out object value)
{
Guard.NotNull(key, nameof(key));
value = null;
if (cacheMap.TryGetValue(key, out var node))
{
value = node.Value.Value;
cacheHistory.Remove(node);
cacheHistory.AddLast(node);
return true;
}
return false;
}
public bool Contains(object key)
{
Guard.NotNull(key, nameof(key));
return cacheMap.ContainsKey(key);
}
private void RemoveFirst()
{
var node = cacheHistory.First;
cacheMap.Remove(node.Value.Key);
cacheHistory.RemoveFirst();
}
}
}

18
src/Squidex.Infrastructure/Caching/LRUCacheItem.cs

@ -0,0 +1,18 @@
// ==========================================================================
// LRUCacheItem.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
#pragma warning disable SA1401 // Fields must be private
namespace Squidex.Infrastructure.Caching
{
internal class LRUCacheItem
{
public object Key;
public object Value;
}
}

68
tests/Squidex.Infrastructure.Tests/Caching/LRUCacheTests.cs

@ -0,0 +1,68 @@
// ==========================================================================
// LRUCacheTests.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using Xunit;
namespace Squidex.Infrastructure.Caching
{
public class LRUCacheTests
{
private readonly LRUCache sut = new LRUCache(10);
private readonly string key = "Key";
[Fact]
public void Should_always_override_when_setting_value()
{
sut.Set(key, 1);
sut.Set(key, 2);
Assert.True(sut.TryGetValue(key, out var value));
Assert.True(sut.Contains(key));
Assert.Equal(2, value);
}
[Fact]
public void Should_remove_old_items_when_capacity_reached()
{
for (int i = 0; i < 15; i++)
{
sut.Set(i.ToString(), i);
}
for (int i = 0; i < 5; i++)
{
Assert.False(sut.TryGetValue(i.ToString(), out var value));
Assert.Null(value);
}
for (int i = 5; i < 15; i++)
{
Assert.True(sut.TryGetValue(i.ToString(), out var value));
Assert.NotNull(value);
}
}
[Fact]
public void Should_return_false_when_item_to_remove_does_not_exist()
{
Assert.False(sut.Remove(key));
}
[Fact]
public void Should_remove_inserted_item()
{
sut.Set(key, 2);
Assert.True(sut.Remove(key));
Assert.False(sut.Contains(key));
Assert.False(sut.TryGetValue(key, out var value));
Assert.Null(value);
}
}
}
Loading…
Cancel
Save