|
1 | 1 | module LRUCache
|
2 | 2 |
|
3 |
| -export LRU, @get! |
| 3 | +include("cyclicorderedset.jl") |
| 4 | +export LRU |
4 | 5 |
|
5 |
| -include("list.jl") |
| 6 | +using Base.Threads |
| 7 | +using Base: Callable |
6 | 8 |
|
7 |
| -# Default cache size |
8 |
| -const __MAXCACHE__ = 100 |
| 9 | +_constone(x) = 1 |
9 | 10 |
|
| 11 | +# Default cache size |
10 | 12 | mutable struct LRU{K,V} <: AbstractDict{K,V}
|
11 |
| - ht::Dict{K, LRUNode{K, V}} |
12 |
| - q::LRUList{K, V} |
13 |
| - maxsize::Int |
14 |
| - |
15 |
| - LRU{K, V}(; maxsize::Int) where {K, V} = |
16 |
| - new{K, V}(Dict{K, V}(), LRUList{K, V}(), maxsize) |
| 13 | + dict::Dict{K, Tuple{V, LinkedNode{K}, Int64}} |
| 14 | + keyset::CyclicOrderedSet{K} |
| 15 | + currentsize::Int64 |
| 16 | + maxsize::Int64 |
| 17 | + lock::SpinLock |
| 18 | + by::Callable |
| 19 | + |
| 20 | + LRU{K, V}(; maxsize::Int, by::Callable = _constone) where {K, V} = |
| 21 | + new{K, V}(Dict{K, V}(), CyclicOrderedSet{K}(), 0, maxsize, SpinLock(), by) |
17 | 22 | end
|
18 |
| -LRU(; maxsize::Int) = LRU{Any,Any}(; maxsize = maxsize) |
19 |
| - |
20 |
| -Base.@deprecate LRU(m::Int=__MAXCACHE__) LRU(; maxsize = m) |
21 |
| -Base.@deprecate (LRU{K, V}(m::Int=__MAXCACHE__) where {K, V}) (LRU{K, V}(; maxsize = m)) |
22 | 23 |
|
23 |
| -Base.show(io::IO, lru::LRU{K, V}) where {K, V} = print(io,"LRU{$K, $V}($(lru.maxsize))") |
| 24 | +Base.show(io::IO, lru::LRU{K, V}) where {K, V} = |
| 25 | + print(io, "LRU{$K, $V}(; maxsize = $(lru.maxsize))") |
24 | 26 |
|
25 |
| -Base.iterate(lru::LRU) = iterate(lru.ht) |
26 |
| -Base.iterate(lru::LRU, state) = iterate(lru.ht, state) |
27 |
| - |
28 |
| -Base.length(lru::LRU) = length(lru.q) |
29 |
| -Base.isempty(lru::LRU) = isempty(lru.q) |
30 |
| -Base.sizehint!(lru::LRU, n::Integer) = sizehint!(lru.ht, n) |
31 |
| - |
32 |
| -Base.haskey(lru::LRU, key) = haskey(lru.ht, key) |
33 |
| -Base.get(lru::LRU, key, default) = haskey(lru, key) ? lru[key] : default |
34 |
| -Base.get(default::Base.Callable, lru::LRU, key) = haskey(lru, key) ? lru[key] : default() |
35 |
| - |
36 |
| -macro get!(lru, key, default) |
37 |
| - @warn "`@get! lru key default(args...)` is deprecated, use `get!(()->default(args...), lru, key)` or |
38 |
| - ``` |
39 |
| - get!(lru, key) do |
40 |
| - default(args...) |
41 |
| - end |
42 |
| - ```" |
43 |
| - quote |
44 |
| - if haskey($(esc(lru)), $(esc(key))) |
45 |
| - value = $(esc(lru))[$(esc(key))] |
46 |
| - else |
47 |
| - value = $(esc(default)) |
48 |
| - $(esc(lru))[$(esc(key))] = value |
49 |
| - end |
50 |
| - value |
51 |
| - end |
52 |
| -end |
53 |
| - |
54 |
| -function Base.get!(default::Base.Callable, lru::LRU{K, V}, key::K) where {K,V} |
55 |
| - if haskey(lru, key) |
56 |
| - return lru[key] |
| 27 | +function Base.iterate(lru::LRU, state...) |
| 28 | + next = iterate(lru.keyset, state...) |
| 29 | + if next === nothing |
| 30 | + return nothing |
57 | 31 | else
|
58 |
| - value = default() |
59 |
| - lru[key] = value |
60 |
| - return value |
| 32 | + k, state = next |
| 33 | + v, = lru.dict[k] |
| 34 | + return k=>v, state |
61 | 35 | end
|
62 | 36 | end
|
63 | 37 |
|
64 |
| -function Base.get!(lru::LRU{K,V}, key::K, default::V) where {K,V} |
65 |
| - if haskey(lru, key) |
66 |
| - return lru[key] |
67 |
| - else |
68 |
| - lru[key] = default |
69 |
| - return default |
70 |
| - end |
| 38 | +Base.length(lru::LRU) = length(lru.keyset) |
| 39 | +Base.isempty(lru::LRU) = isempty(lru.keyset) |
| 40 | +function Base.sizehint!(lru::LRU, n::Integer) |
| 41 | + sizehint!(lru.dict, n) |
| 42 | + return lru |
71 | 43 | end
|
72 | 44 |
|
| 45 | +Base.haskey(lru::LRU, key) = haskey(lru.dict, key) |
| 46 | +Base.get(lru::LRU, key, default) = haskey(lru, key) ? lru[key] : default |
| 47 | +Base.get(default::Callable, lru::LRU, key) = haskey(lru, key) ? lru[key] : default() |
| 48 | + |
| 49 | +Base.get!(default::Callable, lru::LRU, key) = |
| 50 | + haskey(lru, key) ? lru[key] : (lru[key] = default()) |
| 51 | +Base.get!(lru::LRU, key, default) = haskey(lru, key) ? lru[key] : (lru[key] = default) |
| 52 | + |
73 | 53 | function Base.getindex(lru::LRU, key)
|
74 |
| - node = lru.ht[key] |
75 |
| - move_to_front!(lru.q, node) |
76 |
| - return node.v |
| 54 | + lock(lru.lock) |
| 55 | + v, n, s = lru.dict[key] |
| 56 | + _move_to_front!(lru.keyset, n) |
| 57 | + unlock(lru.lock) |
| 58 | + return v |
77 | 59 | end
|
78 |
| - |
79 | 60 | function Base.setindex!(lru::LRU{K, V}, v, key) where {K, V}
|
| 61 | + lock(lru.lock) |
80 | 62 | if haskey(lru, key)
|
81 |
| - item = lru.ht[key] |
82 |
| - item.v = v |
83 |
| - move_to_front!(lru.q, item) |
84 |
| - elseif length(lru) == lru.maxsize |
85 |
| - # At capacity. Roll the list so last el is now first, remove the old |
86 |
| - # data, and update new data in place. |
87 |
| - rotate!(lru.q) |
88 |
| - item = first(lru.q) |
89 |
| - delete!(lru.ht, item.k) |
90 |
| - item.k = key |
91 |
| - item.v = v |
92 |
| - lru.ht[key] = item |
| 63 | + _, n, s = lru.dict[key] |
| 64 | + lru.currentsize -= s |
| 65 | + s = lru.by(v)::Int |
| 66 | + lru.currentsize += s |
| 67 | + lru.dict[key] = (v, n, s) |
| 68 | + _move_to_front!(lru.keyset, n) |
93 | 69 | else
|
94 |
| - item = LRUNode{K, V}(key, v) |
95 |
| - pushfirst!(lru.q, item) |
96 |
| - lru.ht[key] = item |
| 70 | + n = LinkedNode{K}(key) |
| 71 | + rotate!(_push!(lru.keyset, n)) |
| 72 | + s = lru.by(v)::Int |
| 73 | + lru.currentsize += s |
| 74 | + lru.dict[key] = (v, n, s) |
| 75 | + end |
| 76 | + while lru.currentsize > lru.maxsize |
| 77 | + k = pop!(lru.keyset) |
| 78 | + _, _, s = pop!(lru.dict, k) |
| 79 | + lru.currentsize -= s |
97 | 80 | end
|
| 81 | + unlock(lru.lock) |
98 | 82 | return lru
|
99 | 83 | end
|
100 | 84 |
|
101 |
| -import Base: resize! |
102 |
| -Base.@deprecate resize!(lru::LRU, m::Int) resize!(lru; maxsize = m) |
103 |
| - |
104 |
| -function resize!(lru::LRU; maxsize::Int) |
105 |
| - maxsize < 0 && error("size must be a positive integer") |
| 85 | +function Base.resize!(lru::LRU; maxsize::Integer = 0) |
| 86 | + @assert 0 <= maxsize |
| 87 | + lock(lru.lock) |
106 | 88 | lru.maxsize = maxsize
|
107 |
| - for i in 1:(length(lru) - lru.maxsize) |
108 |
| - rm = pop!(lru.q) |
109 |
| - delete!(lru.ht, rm.k) |
| 89 | + while lru.currentsize > lru.maxsize |
| 90 | + key = pop!(lru.keyset) |
| 91 | + v, n, s = pop!(lru.dict, key) |
| 92 | + lru.currentsize -= s |
110 | 93 | end
|
| 94 | + unlock(lru.lock) |
111 | 95 | return lru
|
112 | 96 | end
|
113 | 97 |
|
114 | 98 | function Base.delete!(lru::LRU, key)
|
115 |
| - item = lru.ht[key] |
116 |
| - delete!(lru.q, item) |
117 |
| - delete!(lru.ht, key) |
| 99 | + lock(lru.lock) |
| 100 | + v, n, s = pop!(lru.dict, key) |
| 101 | + lru.currentsize -= s |
| 102 | + _delete!(lru.keyset, n) |
| 103 | + unlock(lru.lock) |
118 | 104 | return lru
|
119 | 105 | end
|
| 106 | +function Base.pop!(lru::LRU, key) |
| 107 | + lock(lru.lock) |
| 108 | + v, n, s = pop!(lru.dict, key) |
| 109 | + lru.currentsize -= s |
| 110 | + _delete!(lru.keyset, n) |
| 111 | + unlock(lru.lock) |
| 112 | + return v |
| 113 | +end |
120 | 114 |
|
121 | 115 | function Base.empty!(lru::LRU)
|
122 |
| - empty!(lru.ht) |
123 |
| - empty!(lru.q) |
| 116 | + lock(lru.lock) |
| 117 | + lru.currentsize = 0 |
| 118 | + empty!(lru.dict) |
| 119 | + empty!(lru.keyset) |
| 120 | + unlock(lru.lock) |
| 121 | + return lru |
124 | 122 | end
|
125 | 123 |
|
126 | 124 | end # module
|
0 commit comments