/
SieveLruCache.cs
119 lines (110 loc) · 2.64 KB
/
SieveLruCache.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
namespace Tests;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
public class SieveLruCache<K, V>(int capacity) : ICache<K, V> where K : notnull
{
class Node(K key, V value)
{
public Node Next = null!;
public readonly K Key = key;
public V Value = value;
public bool Visited;
}
private readonly Dictionary<K, Node> _dictionary = [];
private readonly ReaderWriterLockSlim _lock = new();
private Node head = null!, hand = null!;
private void Evict()
{
var prev = hand;
var node = prev.Next;
while (node.Visited)
{
node.Visited = false;
prev = node;
node = node.Next;
}
prev.Next = node.Next;
hand = prev;
if (head == node)
head = prev;
_dictionary.Remove(node.Key);
}
private void AddToHead(Node node)
{
var count = _dictionary.Count;
if (count > 2)
{
if (count > capacity) Evict();
node.Next = head.Next;
head.Next = node;
}
else if (count == 2)
{
node.Next = head;
head.Next = node;
}
if (head == hand)
hand = node;
head = node;
}
public bool TryGetValue(K key, [MaybeNullWhen(false)] out V value)
{
_lock.EnterReadLock();
try
{
if (_dictionary.TryGetValue(key, out var node))
{
node.Visited = true;
value = node.Value;
return true;
}
value = default;
return false;
}
finally
{
_lock.ExitReadLock();
}
}
public void Set(K key, V value)
{
_lock.EnterWriteLock();
try
{
ref var node = ref CollectionsMarshal.GetValueRefOrAddDefault(_dictionary, key, out _);
if (node is null)
{
node = new Node(key, value);
AddToHead(node);
}
else
{
node.Value = value;
}
}
finally
{
_lock.ExitWriteLock();
}
}
public int Count
{
get
{
_lock.EnterReadLock();
var count = _dictionary.Count;
_lock.ExitReadLock();
return count;
}
}
public IEnumerable<K> Keys
{
get
{
_lock.EnterReadLock();
var keys = _dictionary.Keys.ToArray();
_lock.ExitReadLock();
return keys;
}
}
}