Compare commits

...

2 Commits

8 changed files with 336 additions and 86 deletions

View File

@ -3,7 +3,7 @@
"dotnet"
],
"env": {
"NUGET_SOURCE": "https://nexus.niclas-thobaben.de/repository/l--n.de/",
"NUGET_SOURCE": "https://nexus.l--n.de/repository/ln.net/",
"CONFIGURATION": "Release"
},
"stages": [

View File

@ -0,0 +1,46 @@
using System;
using System.Threading;
using NUnit.Framework;
namespace ln.collections.test
{
[TestFixture]
public class CacheTests
{
[Test]
public void Test_Cache_1()
{
Cache<int, string> cache = new Cache<int, string>(TimeSpan.FromSeconds(1));
for (int n = 0; n < 10; n++)
cache.Add(n, n.ToString());
cache.Cleanup();
Assert.AreEqual(10, cache.Count);
Thread.Sleep(1000);
cache.Cleanup();
Assert.AreEqual(0, cache.Count);
for (int n = 0; n < 10; n++)
{
cache.Add(n, n.ToString());
Thread.Sleep(90);
}
Thread.Sleep(10);
for (int n = 10; n > 0; n--)
{
cache.Cleanup();
Assert.AreEqual(n, cache.Count);
Thread.Sleep(90);
}
Assert.Pass();
}
}
}

View File

@ -4,7 +4,9 @@
<IsPackable>false</IsPackable>
<TargetFrameworks>net5.0;netcoreapp3.1</TargetFrameworks>
<TargetFrameworks>net5.0;net6.0</TargetFrameworks>
<LangVersion>default</LangVersion>
</PropertyGroup>
<ItemGroup>

View File

@ -4,7 +4,7 @@ using System.Collections.Generic;
using System.Linq;
namespace ln.collections
{
public class BTree<K,V> : IDict<K,V>
public class BTree<K,V> : IDict<K,V>, IEnumerable<KeyValuePair<K,V>>
{
public Comparison<K> Comparison { get; }
public int Count => count;
@ -629,6 +629,22 @@ namespace ln.collections
IEnumerable IDict.Keys => Keys;
IEnumerable IDict.Values => Values;
public IEnumerable<KeyValuePair<K, V>> GetInterval(K start, K end) => GetInterval(start, end, null);
public IEnumerable<KeyValuePair<K, V>> GetInterval(K start, K end, Func<K,V,bool> filter)
{
TreeNode currentNode = (start is null) ? First(headNode) : FindFirstGE(start);
if (currentNode != null)
{
while ((currentNode is not null) && ((end is null) || (Comparison(currentNode.Key, end) <= 0)))
{
if (filter is null || filter(currentNode.Key, currentNode.Value))
yield return new KeyValuePair<K, V>(currentNode.Key, currentNode.Value);
currentNode = Next(currentNode);
}
}
}
class TreeNode
{
public BTree<K, V> Tree { get; }
@ -720,6 +736,20 @@ namespace ln.collections
}
public IEnumerator<KeyValuePair<K, V>> GetEnumerator()
{
TreeNode node = First(headNode);
while (node != null)
{
yield return new KeyValuePair<K, V>(node.Key, node.Value);
node = Next(node);
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
public class BTree<K> : BTree<K,object>

View File

@ -1,9 +1,10 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace ln.collections
{
public class BTreeValueList<K, V>
public class BTreeValueList<K, V> : IEnumerable<KeyValuePair<K,V>>
{
public bool Empty => bTree.Empty;
@ -143,15 +144,16 @@ namespace ln.collections
public IEnumerable<K> Keys => bTree.Keys;
public IEnumerable<V> Values => bTree.Values.SelectMany(vl => vl);
public IEnumerable<KeyValuePair<K, V>> GetKeyValuePairs()
public IEnumerator<KeyValuePair<K, V>> GetEnumerator()
{
foreach (K key in Keys)
foreach (KeyValuePair<K, List<V>> vl in this.bTree)
foreach (V v in vl.Value)
{
List<V> lv = bTree[key];
foreach (V value in lv)
yield return new KeyValuePair<K, V>(key, value);
yield return new KeyValuePair<K, V>(vl.Key, v);
}
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public void AddRange(IEnumerable<KeyValuePair<K, V>> keyValuePairs)
{
foreach (KeyValuePair<K, V> keyValuePair in keyValuePairs)

View File

@ -1,11 +1,14 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace ln.collections
{
public class BTreeValueSet<K, V>
public class BTreeValueSet<K, V> : IEnumerable<KeyValuePair<K, V>>
{
public bool Empty => bTree.Empty;
public Comparison<K> Comparison => bTree.Comparison;
BTree<K, HashSet<V>> bTree;
@ -13,6 +16,7 @@ namespace ln.collections
{
bTree = new BTree<K, HashSet<V>>();
}
public BTreeValueSet(Comparison<K> comparison)
{
bTree = new BTree<K, HashSet<V>>(comparison);
@ -22,10 +26,11 @@ namespace ln.collections
{
get
{
if (TryGet(key,out IEnumerable<V> values))
if (TryGet(key, out IEnumerable<V> values))
{
return values;
}
throw new KeyNotFoundException();
}
}
@ -35,41 +40,48 @@ namespace ln.collections
if (!TryAdd(key, value))
throw new ArgumentException("duplicate key");
}
public void Remove(K key, V value)
{
if (!TryRemove(key, value))
throw new KeyNotFoundException();
}
public void Remove(K key)
{
if (!TryRemove(key))
throw new KeyNotFoundException();
}
public bool TryAdd(K key,V value)
public bool TryAdd(K key, V value)
{
if (!bTree.TryGet(key, out HashSet<V> values))
{
values = new HashSet<V>();
bTree.Add(key, values);
}
return values.Add(value);
}
public bool TryGet(K key,out IEnumerable<V> values)
public bool TryGet(K key, out IEnumerable<V> values)
{
if ((!bTree.TryGet(key, out HashSet<V> v)) || (v.Count == 0))
{
values = v;
return false;
}
values = v;
return true;
}
public bool TryRemove(K key)
{
return bTree.TryRemove(key);
}
public bool TryRemove(K key,V value)
public bool TryRemove(K key, V value)
{
if (bTree.TryGet(key, out HashSet<V> values))
{
@ -78,14 +90,17 @@ namespace ln.collections
bTree.Remove(key);
return success;
}
return false;
}
public bool ContainsKey(K key)
{
if (!bTree.TryGet(key, out HashSet<V> _values))
return false;
return _values.Count > 0;
}
public bool ContainsValue(V value)
{
foreach (HashSet<V> _values in bTree.Values)
@ -94,6 +109,7 @@ namespace ln.collections
if (v.Equals(value))
return true;
}
return false;
}
@ -104,17 +120,17 @@ namespace ln.collections
return _values.Count;
}
public void Clear()
{
bTree.Clear();
}
public void Clear()
{
bTree.Clear();
}
public K First => bTree.First();
public K Last => bTree.Last();
public IEnumerable<K> Keys => bTree.Keys;
public IEnumerable<V> Values => bTree.Values.SelectMany(vl => vl);
public IEnumerable<V> Values => bTree.Values.SelectMany(vl => vl);
public IEnumerable<KeyValuePair<K, V>> GetKeyValuePairs()
{
foreach (K key in Keys)
@ -124,11 +140,36 @@ namespace ln.collections
yield return new KeyValuePair<K, V>(key, value);
}
}
public void AddRange(IEnumerable<KeyValuePair<K, V>> keyValuePairs)
{
foreach (KeyValuePair<K, V> keyValuePair in keyValuePairs)
Add(keyValuePair.Key, keyValuePair.Value);
}
public IEnumerable<KeyValuePair<K, V>> GetInterval(K start, K end) => GetInterval(start, end, null);
public IEnumerable<KeyValuePair<K, V>> GetInterval(K start, K end, Func<K,V,bool> filter)
{
foreach (KeyValuePair<K, HashSet<V>> vl in this.bTree.GetInterval(start, end))
foreach (V v in vl.Value)
{
if (filter is null || filter(vl.Key, v))
yield return new KeyValuePair<K, V>(vl.Key, v);
}
}
public IEnumerator<KeyValuePair<K, V>> GetEnumerator()
{
foreach (KeyValuePair<K, HashSet<V>> vl in this.bTree)
foreach (V v in vl.Value)
{
yield return new KeyValuePair<K, V>(vl.Key, v);
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}

View File

@ -1,95 +1,223 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace ln.collections
{
public class Cache<K,V>
public class Cache<TKey, TValue>
{
public int Count => values.Count;
public TimeSpan DefaultLifeTime { get; set; }
private readonly Dictionary<TKey, Entry> _cache = new Dictionary<TKey, Entry>();
BTree<K, V> values;
BTree<K, LinkedListItem<K>> itemTree;
LinkedList<K> items = new LinkedList<K>();
int maxCacheSize = 4096;
public Cache(TimeSpan defaultLifeTime)
{
DefaultLifeTime = defaultLifeTime;
}
public Cache()
: this(TimeSpan.FromMinutes(10))
{
values = new BTree<K, V>();
itemTree = new BTree<K, LinkedListItem<K>>();
}
public Cache(Comparison<K> compare)
{
values = new BTree<K, V>(compare);
itemTree = new BTree<K, LinkedListItem<K>>(compare);
}
public int MaxCacheSize
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
{
get => maxCacheSize;
set {
maxCacheSize = value;
while (maxCacheSize < values.Count)
lock (this)
{
foreach (var cacheEntry in _cache)
{
Forget();
if (cacheEntry.Value.IsValid)
array[arrayIndex++] = new KeyValuePair<TKey, TValue>(cacheEntry.Key, cacheEntry.Value.Value);
}
}
}
public void Clear()
{
values.Clear();
itemTree.Clear();
items.Clear();
}
public bool TryGet(K key,out V value)
{
return values.TryGet(key, out value);
}
public void Forget()
{
lock (this)
public int Count {
get
{
Forget(items.FirstItem);
}
}
public void Forget(K key)
{
lock (this)
{
if (itemTree.TryGet(key,out LinkedListItem<K> item))
Forget(item);
lock (this) { return _cache.Count; }
}
}
private void Forget(LinkedListItem<K> item)
{
if (item == null)
return;
values.Remove(item.Value);
itemTree.Remove(item.Value);
items.Remove(item);
}
public void Ensure(K key,V value)
public bool IsReadOnly => false;
public void Clear() { lock (this) _cache.Clear(); }
public void Cleanup()
{
lock (this)
{
if (!itemTree.TryGet(key, out LinkedListItem<K> item))
foreach (var key in _cache.Keys.ToArray())
{
item = new LinkedListItem<K>(key);
values.Add(key,value);
if (!_cache[key].IsValid)
_cache.Remove(key);
}
Ensure(item);
}
}
private void Ensure(LinkedListItem<K> item)
public bool Contains(KeyValuePair<TKey, TValue> item) { lock (this) return TryGetValue(item.Key, out TValue value) && object.Equals(item.Value, value); }
public void Add(KeyValuePair<TKey, TValue> item) => Add(item.Key, item.Value);
public bool Remove(KeyValuePair<TKey, TValue> item)
{
if (!item.IsLinked)
itemTree.Add(item.Value, item);
items.Add(item);
lock (this)
{
if (_cache.TryGetValue(item.Key, out Entry entry) && object.Equals(item.Value, entry.Value))
{
_cache.Remove(item.Key);
return entry.IsValid;
}
return false;
}
}
public void Add(TKey key, TValue value)
{
lock (this)
{
_cache.Add(key, new Entry(this, value));
}
}
public bool ContainsKey(TKey key)
{
lock (this) return _cache.ContainsKey(key) && _cache[key].IsValid;
}
public bool Remove(TKey key)
{
lock (this) return _cache.Remove(key);
}
public bool TryGetValue(TKey key, out TValue value)
{
lock (this)
{
if (_cache.TryGetValue(key, out Entry entry))
{
if (entry.IsValid)
{
value = entry.Value;
return true;
}
else
{
_cache.Remove(key);
}
}
}
value = default(TValue);
return false;
}
public TValue this[TKey key]
{
get
{
lock (this)
{
if (TryGetValue(key, out TValue value))
return value;
}
throw new KeyNotFoundException();
}
set
{
lock (this)
{
if (_cache.TryGetValue(key, out Entry entry))
entry.Reset(value);
else
_cache.Add(key, new Entry(this, value));
}
}
}
public bool ReValidate(TKey key)
{
lock (this)
{
if (_cache.TryGetValue(key, out Entry entry))
{
if (entry.IsValid)
{
entry.UpdateTimestamp();
return true;
}
else
{
_cache.Remove(key);
}
}
return false;
}
}
public TValue GetOrCreate(TKey key, Func<TKey, TValue> factory)
{
lock (this)
{
if (!TryGetValue(key, out TValue value))
{
value = factory(key);
Add(key, value);
}
return value;
}
}
class Entry
{
private Cache<TKey, TValue> _cache;
public DateTime TimeOut { get; private set; }
public TValue Value { get; private set; }
public Entry(Cache<TKey,TValue> cache, TValue value)
{
_cache = cache;
Value = value;
UpdateTimestamp();
}
public void UpdateTimestamp() => UpdateTimestamp(_cache.DefaultLifeTime);
public void UpdateTimestamp(TimeSpan lifeTime) => TimeOut = DateTime.Now + lifeTime;
public bool IsValid => TimeOut > DateTime.Now;
public void Reset(TValue newValue)
{
Value = newValue;
UpdateTimestamp();
}
}
}
public abstract class KeyedCache<TKey, TValue> : Cache<TKey, TValue>
{
protected KeyedCache()
{
}
protected KeyedCache(TimeSpan defaultTimeOut)
:base(defaultTimeOut)
{
}
protected abstract TKey GetKey(TValue value);
public void Add(TValue value) => Add(GetKey(value), value);
public bool RemoveValue(TValue value)
{
TKey key = GetKey(value);
lock (this)
{
if (TryGetValue(key, out TValue cachedValue) && object.Equals(cachedValue, value))
{
return Remove(key);
}
}
return false;
}
}

View File

@ -2,13 +2,14 @@
<PropertyGroup>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Version>0.1.5</Version>
<Version>0.1.6</Version>
<Authors>Harald Wolff-Thobaben</Authors>
<Company>l--n.de</Company>
<AssemblyVersion>0.0.1.1</AssemblyVersion>
<FileVersion>0.0.1.1</FileVersion>
<PackageVersion>0.1.5</PackageVersion>
<TargetFrameworks>net5.0;netcoreapp3.1</TargetFrameworks>
<PackageVersion>0.2.0</PackageVersion>
<TargetFrameworks>net5.0;net6.0</TargetFrameworks>
<LangVersion>default</LangVersion>
</PropertyGroup>
<ItemGroup>