good morning!!!!
Skip to content
GitLab
Explore
Sign in
Commits on Source (1)
update LRU to be generic
· de46e026
a
authored
May 18, 2022
de46e026
Hide whitespace changes
Inline
Side-by-side
generic/list/list.go
0 → 100644
View file @
de46e026
package
list
type
Element
[
V
any
]
struct
{
// element (l.Front()).
next
,
prev
*
Element
[
V
]
// The list to which this element belongs.
list
*
List
[
V
]
// The value stored with this element.
Value
V
}
// Next returns the next list element or nil.
func
(
e
*
Element
[
V
])
Next
()
*
Element
[
V
]
{
if
p
:=
e
.
next
;
e
.
list
!=
nil
&&
p
!=
&
e
.
list
.
root
{
return
p
}
return
nil
}
// Prev returns the previous list element or nil.
func
(
e
*
Element
[
V
])
Prev
()
*
Element
[
V
]
{
if
p
:=
e
.
prev
;
e
.
list
!=
nil
&&
p
!=
&
e
.
list
.
root
{
return
p
}
return
nil
}
// List represents a doubly linked list.
// The zero value for List is an empty list ready to use.
type
List
[
V
any
]
struct
{
root
Element
[
V
]
// sentinel list element, only &root, root.prev, and root.next are used
len
int
// current list length excluding (this) sentinel element
}
// Init initializes or clears list l.
func
(
l
*
List
[
V
])
Init
()
*
List
[
V
]
{
l
.
root
.
next
=
&
l
.
root
l
.
root
.
prev
=
&
l
.
root
l
.
len
=
0
return
l
}
// New returns an initialized list.
func
New
[
V
any
]()
*
List
[
V
]
{
return
new
(
List
[
V
])
.
Init
()
}
// Len returns the number of elements of list l.
// The complexity is O(1).
func
(
l
*
List
[
V
])
Len
()
int
{
return
l
.
len
}
// Front returns the first element of list l or nil if the list is empty.
func
(
l
*
List
[
V
])
Front
()
*
Element
[
V
]
{
if
l
.
len
==
0
{
return
nil
}
return
l
.
root
.
next
}
// Back returns the last element of list l or nil if the list is empty.
func
(
l
*
List
[
V
])
Back
()
*
Element
[
V
]
{
if
l
.
len
==
0
{
return
nil
}
return
l
.
root
.
prev
}
// lazyInit lazily initializes a zero List value.
func
(
l
*
List
[
V
])
lazyInit
()
{
if
l
.
root
.
next
==
nil
{
l
.
Init
()
}
}
// insert inserts e after at, increments l.len, and returns e.
func
(
l
*
List
[
V
])
insert
(
e
,
at
*
Element
[
V
])
*
Element
[
V
]
{
e
.
prev
=
at
e
.
next
=
at
.
next
e
.
prev
.
next
=
e
e
.
next
.
prev
=
e
e
.
list
=
l
l
.
len
++
return
e
}
// insertValue is a convenience wrapper for insert(&Element{Value: v}, at).
func
(
l
*
List
[
V
])
insertValue
(
v
V
,
at
*
Element
[
V
])
*
Element
[
V
]
{
return
l
.
insert
(
&
Element
[
V
]{
Value
:
v
},
at
)
}
// remove removes e from its list, decrements l.len
func
(
l
*
List
[
V
])
remove
(
e
*
Element
[
V
])
{
e
.
prev
.
next
=
e
.
next
e
.
next
.
prev
=
e
.
prev
e
.
next
=
nil
// avoid memory leaks
e
.
prev
=
nil
// avoid memory leaks
e
.
list
=
nil
l
.
len
--
}
// move moves e to next to at.
func
(
l
*
List
[
V
])
move
(
e
,
at
*
Element
[
V
])
{
if
e
==
at
{
return
}
e
.
prev
.
next
=
e
.
next
e
.
next
.
prev
=
e
.
prev
e
.
prev
=
at
e
.
next
=
at
.
next
e
.
prev
.
next
=
e
e
.
next
.
prev
=
e
}
// Remove removes e from l if e is an element of list l.
// It returns the element value e.Value.
// The element must not be nil.
func
(
l
*
List
[
V
])
Remove
(
e
*
Element
[
V
])
any
{
if
e
.
list
==
l
{
// if e.list == l, l must have been initialized when e was inserted
// in l or l == nil (e is a zero Element) and l.remove will crash
l
.
remove
(
e
)
}
return
e
.
Value
}
// PushFront inserts a new element e with value v at the front of list l and returns e.
func
(
l
*
List
[
V
])
PushFront
(
v
V
)
*
Element
[
V
]
{
l
.
lazyInit
()
return
l
.
insertValue
(
v
,
&
l
.
root
)
}
// PushBack inserts a new element e with value v at the back of list l and returns e.
func
(
l
*
List
[
V
])
PushBack
(
v
V
)
*
Element
[
V
]
{
l
.
lazyInit
()
return
l
.
insertValue
(
v
,
l
.
root
.
prev
)
}
// InsertBefore inserts a new element e with value v immediately before mark and returns e.
// If mark is not an element of l, the list is not modified.
// The mark must not be nil.
func
(
l
*
List
[
V
])
InsertBefore
(
v
V
,
mark
*
Element
[
V
])
*
Element
[
V
]
{
if
mark
.
list
!=
l
{
return
nil
}
// see comment in List.Remove about initialization of l
return
l
.
insertValue
(
v
,
mark
.
prev
)
}
// InsertAfter inserts a new element e with value v immediately after mark and returns e.
// If mark is not an element of l, the list is not modified.
// The mark must not be nil.
func
(
l
*
List
[
V
])
InsertAfter
(
v
V
,
mark
*
Element
[
V
])
*
Element
[
V
]
{
if
mark
.
list
!=
l
{
return
nil
}
// see comment in List.Remove about initialization of l
return
l
.
insertValue
(
v
,
mark
)
}
// MoveToFront moves element e to the front of list l.
// If e is not an element of l, the list is not modified.
// The element must not be nil.
func
(
l
*
List
[
V
])
MoveToFront
(
e
*
Element
[
V
])
{
if
e
.
list
!=
l
||
l
.
root
.
next
==
e
{
return
}
// see comment in List.Remove about initialization of l
l
.
move
(
e
,
&
l
.
root
)
}
// MoveToBack moves element e to the back of list l.
// If e is not an element of l, the list is not modified.
// The element must not be nil.
func
(
l
*
List
[
V
])
MoveToBack
(
e
*
Element
[
V
])
{
if
e
.
list
!=
l
||
l
.
root
.
prev
==
e
{
return
}
// see comment in List.Remove about initialization of l
l
.
move
(
e
,
l
.
root
.
prev
)
}
// MoveBefore moves element e to its new position before mark.
// If e or mark is not an element of l, or e == mark, the list is not modified.
// The element and mark must not be nil.
func
(
l
*
List
[
V
])
MoveBefore
(
e
,
mark
*
Element
[
V
])
{
if
e
.
list
!=
l
||
e
==
mark
||
mark
.
list
!=
l
{
return
}
l
.
move
(
e
,
mark
.
prev
)
}
// MoveAfter moves element e to its new position after mark.
// If e or mark is not an element of l, or e == mark, the list is not modified.
// The element and mark must not be nil.
func
(
l
*
List
[
V
])
MoveAfter
(
e
,
mark
*
Element
[
V
])
{
if
e
.
list
!=
l
||
e
==
mark
||
mark
.
list
!=
l
{
return
}
l
.
move
(
e
,
mark
)
}
// PushBackList inserts a copy of another list at the back of list l.
// The lists l and other may be the same. They must not be nil.
func
(
l
*
List
[
V
])
PushBackList
(
other
*
List
[
V
])
{
l
.
lazyInit
()
for
i
,
e
:=
other
.
Len
(),
other
.
Front
();
i
>
0
;
i
,
e
=
i
-
1
,
e
.
Next
()
{
l
.
insertValue
(
e
.
Value
,
l
.
root
.
prev
)
}
}
// PushFrontList inserts a copy of another list at the front of list l.
// The lists l and other may be the same. They must not be nil.
func
(
l
*
List
[
V
])
PushFrontList
(
other
*
List
[
V
])
{
l
.
lazyInit
()
for
i
,
e
:=
other
.
Len
(),
other
.
Back
();
i
>
0
;
i
,
e
=
i
-
1
,
e
.
Prev
()
{
l
.
insertValue
(
e
.
Value
,
&
l
.
root
)
}
}
lru/lru.go
View file @
de46e026
package
lru
import
(
"container/list"
"errors"
"time"
"gfx.cafe/open/goutil/generic/list"
)
// EvictCallback is used to get a callback when a cache entry is evicted
type
EvictCallback
func
(
key
interface
{},
value
interface
{}
)
type
EvictCallback
[
K
comparable
,
V
any
]
func
(
key
K
,
value
V
)
// LRU implements a non-thread safe fixed size LRU cache
type
LRU
struct
{
type
LRU
[
K
comparable
,
V
any
]
struct
{
size
int
evictList
*
list
.
List
items
map
[
interface
{}
]
*
list
.
Element
evictList
*
list
.
List
[
*
entry
[
K
,
V
]]
items
map
[
K
]
*
list
.
Element
[
*
entry
[
K
,
V
]]
expire
time
.
Duration
onEvict
EvictCallback
onEvict
EvictCallback
[
K
,
V
]
}
// entry is used to hold a value in the evictList
type
entry
struct
{
key
interface
{}
value
interface
{}
type
entry
[
K
comparable
,
V
any
]
struct
{
key
K
value
V
expire
*
time
.
Time
}
func
(
e
*
entry
)
IsExpired
()
bool
{
func
(
e
*
entry
[
K
,
V
]
)
IsExpired
()
bool
{
if
e
.
expire
==
nil
{
return
false
}
...
...
@@ -33,14 +34,14 @@ func (e *entry) IsExpired() bool {
}
// NewLRU constructs an LRU of the given size
func
NewLRU
(
size
int
,
onEvict
EvictCallback
)
(
*
LRU
,
error
)
{
func
NewLRU
[
K
comparable
,
V
any
]
(
size
int
,
onEvict
EvictCallback
[
K
,
V
]
)
(
*
LRU
[
K
,
V
]
,
error
)
{
if
size
<=
0
{
return
nil
,
errors
.
New
(
"
M
ust provide a positive size"
)
return
nil
,
errors
.
New
(
"
m
ust provide a positive size"
)
}
c
:=
&
LRU
{
c
:=
&
LRU
[
K
,
V
]
{
size
:
size
,
evictList
:
list
.
New
(),
items
:
make
(
map
[
interface
{}
]
*
list
.
Element
),
evictList
:
list
.
New
[
*
entry
[
K
,
V
]]
(),
items
:
make
(
map
[
K
]
*
list
.
Element
[
*
entry
[
K
,
V
]]
),
expire
:
0
,
onEvict
:
onEvict
,
}
...
...
@@ -48,14 +49,14 @@ func NewLRU(size int, onEvict EvictCallback) (*LRU, error) {
}
// NewLRUWithExpire contrusts an LRU of the given size and expire time
func
NewLRUWithExpire
(
size
int
,
expire
time
.
Duration
,
onEvict
EvictCallback
)
(
*
LRU
,
error
)
{
func
NewLRUWithExpire
[
K
comparable
,
V
any
]
(
size
int
,
expire
time
.
Duration
,
onEvict
EvictCallback
[
K
,
V
]
)
(
*
LRU
[
K
,
V
]
,
error
)
{
if
size
<=
0
{
return
nil
,
errors
.
New
(
"
M
ust provide a positive size"
)
return
nil
,
errors
.
New
(
"
m
ust provide a positive size"
)
}
c
:=
&
LRU
{
c
:=
&
LRU
[
K
,
V
]
{
size
:
size
,
evictList
:
list
.
New
(),
items
:
make
(
map
[
interface
{}
]
*
list
.
Element
),
evictList
:
list
.
New
[
*
entry
[
K
,
V
]]
(),
items
:
make
(
map
[
K
]
*
list
.
Element
[
*
entry
[
K
,
V
]]
),
expire
:
expire
,
onEvict
:
onEvict
,
}
...
...
@@ -63,10 +64,10 @@ func NewLRUWithExpire(size int, expire time.Duration, onEvict EvictCallback) (*L
}
// Purge is used to completely clear the cache
func
(
c
*
LRU
)
Purge
()
{
func
(
c
*
LRU
[
K
,
V
]
)
Purge
()
{
for
k
,
v
:=
range
c
.
items
{
if
c
.
onEvict
!=
nil
{
c
.
onEvict
(
k
,
v
.
Value
.
(
*
entry
)
.
value
)
c
.
onEvict
(
k
,
v
.
Value
.
value
)
}
delete
(
c
.
items
,
k
)
}
...
...
@@ -74,12 +75,12 @@ func (c *LRU) Purge() {
}
// Add adds a value to the cache. Returns true if an eviction occurred.
func
(
c
*
LRU
)
Add
(
key
,
value
interface
{}
)
bool
{
func
(
c
*
LRU
[
K
,
V
]
)
Add
(
key
K
,
value
V
)
bool
{
return
c
.
AddEx
(
key
,
value
,
0
)
}
// AddEx adds a value to the cache with expire. Returns true if an eviction occurred.
func
(
c
*
LRU
)
AddEx
(
key
,
value
interface
{}
,
expire
time
.
Duration
)
bool
{
func
(
c
*
LRU
[
K
,
V
]
)
AddEx
(
key
K
,
value
V
,
expire
time
.
Duration
)
bool
{
var
ex
*
time
.
Time
=
nil
if
expire
>
0
{
expire
:=
time
.
Now
()
.
Add
(
expire
)
...
...
@@ -91,13 +92,13 @@ func (c *LRU) AddEx(key, value interface{}, expire time.Duration) bool {
// Check for existing item
if
ent
,
ok
:=
c
.
items
[
key
];
ok
{
c
.
evictList
.
MoveToFront
(
ent
)
ent
.
Value
.
(
*
entry
)
.
value
=
value
ent
.
Value
.
(
*
entry
)
.
expire
=
ex
ent
.
Value
.
value
=
value
ent
.
Value
.
expire
=
ex
return
false
}
// Add new item
ent
:=
&
entry
{
key
:
key
,
value
:
value
,
expire
:
ex
}
ent
:=
&
entry
[
K
,
V
]
{
key
:
key
,
value
:
value
,
expire
:
ex
}
entry
:=
c
.
evictList
.
PushFront
(
ent
)
c
.
items
[
key
]
=
entry
...
...
@@ -110,22 +111,22 @@ func (c *LRU) AddEx(key, value interface{}, expire time.Duration) bool {
}
// Get looks up a key's value from the cache.
func
(
c
*
LRU
)
Get
(
key
interface
{})
(
value
interface
{}
,
ok
bool
)
{
func
(
c
*
LRU
[
K
,
V
]
)
Get
(
key
K
)
(
value
V
,
ok
bool
)
{
if
ent
,
ok
:=
c
.
items
[
key
];
ok
{
if
ent
.
Value
.
(
*
entry
)
.
IsExpired
()
{
return
nil
,
false
if
ent
.
Value
.
IsExpired
()
{
return
*
new
(
V
)
,
false
}
c
.
evictList
.
MoveToFront
(
ent
)
return
ent
.
Value
.
(
*
entry
)
.
value
,
true
return
ent
.
Value
.
value
,
true
}
return
}
// Check if a key is in the cache, without updating the recent-ness
// or deleting it for being stale.
func
(
c
*
LRU
)
Contains
(
key
interface
{}
)
(
ok
bool
)
{
func
(
c
*
LRU
[
K
,
V
]
)
Contains
(
key
K
)
(
ok
bool
)
{
if
ent
,
ok
:=
c
.
items
[
key
];
ok
{
if
ent
.
Value
.
(
*
entry
)
.
IsExpired
()
{
if
ent
.
Value
.
IsExpired
()
{
return
false
}
return
ok
...
...
@@ -133,21 +134,21 @@ func (c *LRU) Contains(key interface{}) (ok bool) {
return
}
// Returns the key value (or
un
def
ined
if not found) without updating
// Returns the key value (or def
ault
if not found) without updating
// the "recently used"-ness of the key.
func
(
c
*
LRU
)
Peek
(
key
interface
{})
(
value
interface
{}
,
ok
bool
)
{
func
(
c
*
LRU
[
K
,
V
]
)
Peek
(
key
K
)
(
value
V
,
ok
bool
)
{
if
ent
,
ok
:=
c
.
items
[
key
];
ok
{
if
ent
.
Value
.
(
*
entry
)
.
IsExpired
()
{
return
nil
,
false
if
ent
.
Value
.
IsExpired
()
{
return
*
new
(
V
)
,
false
}
return
ent
.
Value
.
(
*
entry
)
.
value
,
true
return
ent
.
Value
.
value
,
true
}
return
nil
,
ok
return
*
new
(
V
)
,
ok
}
// Remove removes the provided key from the cache, returning if the
// key was contained.
func
(
c
*
LRU
)
Remove
(
key
interface
{}
)
bool
{
func
(
c
*
LRU
[
K
,
V
]
)
Remove
(
key
K
)
bool
{
if
ent
,
ok
:=
c
.
items
[
key
];
ok
{
c
.
removeElement
(
ent
)
return
true
...
...
@@ -156,44 +157,44 @@ func (c *LRU) Remove(key interface{}) bool {
}
// RemoveOldest removes the oldest item from the cache.
func
(
c
*
LRU
)
RemoveOldest
()
(
interface
{},
interface
{}
,
bool
)
{
func
(
c
*
LRU
[
K
,
V
]
)
RemoveOldest
()
(
K
,
V
,
bool
)
{
ent
:=
c
.
evictList
.
Back
()
if
ent
!=
nil
{
c
.
removeElement
(
ent
)
kv
:=
ent
.
Value
.
(
*
entry
)
kv
:=
ent
.
Value
return
kv
.
key
,
kv
.
value
,
true
}
return
nil
,
nil
,
false
return
*
new
(
K
),
*
new
(
V
)
,
false
}
// GetOldest returns the oldest entry
func
(
c
*
LRU
)
GetOldest
()
(
interface
{},
interface
{}
,
bool
)
{
func
(
c
*
LRU
[
K
,
V
]
)
GetOldest
()
(
K
,
V
,
bool
)
{
ent
:=
c
.
evictList
.
Back
()
if
ent
!=
nil
{
kv
:=
ent
.
Value
.
(
*
entry
)
kv
:=
ent
.
Value
return
kv
.
key
,
kv
.
value
,
true
}
return
nil
,
nil
,
false
return
*
new
(
K
),
*
new
(
V
)
,
false
}
// Keys returns a slice of the keys in the cache, from oldest to newest.
func
(
c
*
LRU
)
Keys
()
[]
interface
{}
{
keys
:=
make
([]
interface
{}
,
len
(
c
.
items
))
func
(
c
*
LRU
[
K
,
V
]
)
Keys
()
[]
K
{
keys
:=
make
([]
K
,
len
(
c
.
items
))
i
:=
0
for
ent
:=
c
.
evictList
.
Back
();
ent
!=
nil
;
ent
=
ent
.
Prev
()
{
keys
[
i
]
=
ent
.
Value
.
(
*
entry
)
.
key
keys
[
i
]
=
ent
.
Value
.
key
i
++
}
return
keys
}
// Len returns the number of items in the cache.
func
(
c
*
LRU
)
Len
()
int
{
func
(
c
*
LRU
[
K
,
V
]
)
Len
()
int
{
return
c
.
evictList
.
Len
()
}
// Resize changes the cache size.
func
(
c
*
LRU
)
Resize
(
size
int
)
(
evicted
int
)
{
func
(
c
*
LRU
[
K
,
V
]
)
Resize
(
size
int
)
(
evicted
int
)
{
diff
:=
c
.
Len
()
-
size
if
diff
<
0
{
diff
=
0
...
...
@@ -206,7 +207,7 @@ func (c *LRU) Resize(size int) (evicted int) {
}
// removeOldest removes the oldest item from the cache.
func
(
c
*
LRU
)
removeOldest
()
{
func
(
c
*
LRU
[
K
,
V
]
)
removeOldest
()
{
ent
:=
c
.
evictList
.
Back
()
if
ent
!=
nil
{
c
.
removeElement
(
ent
)
...
...
@@ -214,9 +215,9 @@ func (c *LRU) removeOldest() {
}
// removeElement is used to remove a given list element from the cache
func
(
c
*
LRU
)
removeElement
(
e
*
list
.
Element
)
{
func
(
c
*
LRU
[
K
,
V
]
)
removeElement
(
e
*
list
.
Element
[
*
entry
[
K
,
V
]]
)
{
c
.
evictList
.
Remove
(
e
)
kv
:=
e
.
Value
.
(
*
entry
)
kv
:=
e
.
Value
delete
(
c
.
items
,
kv
.
key
)
if
c
.
onEvict
!=
nil
{
c
.
onEvict
(
kv
.
key
,
kv
.
value
)
...
...
lru/lru_test.go
View file @
de46e026
...
...
@@ -7,7 +7,7 @@ import (
func
TestLRU
(
t
*
testing
.
T
)
{
evictCounter
:=
0
onEvicted
:=
func
(
k
int
erface
{},
v
interface
{}
)
{
onEvicted
:=
func
(
k
int
,
v
int
)
{
if
k
!=
v
{
t
.
Fatalf
(
"Evict values not equal (%v!=%v)"
,
k
,
v
)
}
...
...
@@ -79,7 +79,7 @@ func TestLRU(t *testing.T) {
}
func
TestLRU_GetOldest_RemoveOldest
(
t
*
testing
.
T
)
{
l
,
err
:=
NewLRU
(
128
,
nil
)
l
,
err
:=
NewLRU
[
int
,
int
]
(
128
,
nil
)
if
err
!=
nil
{
t
.
Fatalf
(
"err: %v"
,
err
)
}
...
...
@@ -90,7 +90,7 @@ func TestLRU_GetOldest_RemoveOldest(t *testing.T) {
if
!
ok
{
t
.
Fatalf
(
"missing"
)
}
if
k
.
(
int
)
!=
128
{
if
k
!=
128
{
t
.
Fatalf
(
"bad: %v"
,
k
)
}
...
...
@@ -98,7 +98,7 @@ func TestLRU_GetOldest_RemoveOldest(t *testing.T) {
if
!
ok
{
t
.
Fatalf
(
"missing"
)
}
if
k
.
(
int
)
!=
128
{
if
k
!=
128
{
t
.
Fatalf
(
"bad: %v"
,
k
)
}
...
...
@@ -106,7 +106,7 @@ func TestLRU_GetOldest_RemoveOldest(t *testing.T) {
if
!
ok
{
t
.
Fatalf
(
"missing"
)
}
if
k
.
(
int
)
!=
129
{
if
k
!=
129
{
t
.
Fatalf
(
"bad: %v"
,
k
)
}
}
...
...
@@ -114,7 +114,7 @@ func TestLRU_GetOldest_RemoveOldest(t *testing.T) {
// Test that Add returns true/false if an eviction occurred
func
TestLRU_Add
(
t
*
testing
.
T
)
{
evictCounter
:=
0
onEvicted
:=
func
(
k
int
erface
{},
v
interface
{}
)
{
onEvicted
:=
func
(
k
int
,
v
int
)
{
evictCounter
+=
1
}
...
...
@@ -133,7 +133,7 @@ func TestLRU_Add(t *testing.T) {
// Test that Contains doesn't update recent-ness
func
TestLRU_Contains
(
t
*
testing
.
T
)
{
l
,
err
:=
NewLRU
(
2
,
nil
)
l
,
err
:=
NewLRU
[
int
,
int
]
(
2
,
nil
)
if
err
!=
nil
{
t
.
Fatalf
(
"err: %v"
,
err
)
}
...
...
@@ -152,7 +152,7 @@ func TestLRU_Contains(t *testing.T) {
// Test that Peek doesn't update recent-ness
func
TestLRU_Peek
(
t
*
testing
.
T
)
{
l
,
err
:=
NewLRU
(
2
,
nil
)
l
,
err
:=
NewLRU
[
int
,
int
]
(
2
,
nil
)
if
err
!=
nil
{
t
.
Fatalf
(
"err: %v"
,
err
)
}
...
...
@@ -171,7 +171,7 @@ func TestLRU_Peek(t *testing.T) {
// Test that expire feature
func
TestLRU_Expire
(
t
*
testing
.
T
)
{
l
,
err
:=
NewLRUWithExpire
(
2
,
2
*
time
.
Second
,
nil
)
l
,
err
:=
NewLRUWithExpire
[
int
,
int
]
(
2
,
2
*
time
.
Second
,
nil
)
if
err
!=
nil
{
t
.
Fatalf
(
"err: %v"
,
err
)
}
...
...
@@ -200,7 +200,7 @@ func TestLRU_Expire(t *testing.T) {
// Test that Resize can upsize and downsize
func
TestLRU_Resize
(
t
*
testing
.
T
)
{
onEvictCounter
:=
0
onEvicted
:=
func
(
k
int
erface
{},
v
interface
{}
)
{
onEvicted
:=
func
(
k
int
,
v
int
)
{
onEvictCounter
++
}
l
,
err
:=
NewLRU
(
2
,
onEvicted
)
...
...
lru/sync_lru.go
deleted
100644 → 0
View file @
b05f8159
package
lru
import
(
"container/list"
"sync"
"time"
)
type
ConcurrentLRU
struct
{
u
*
LRU
sync
.
RWMutex
}
// NewConcurrentLRU constructs an ConcurrentLRU of the given size
func
NewConcurrentLRU
(
size
int
,
onEvict
EvictCallback
)
(
*
ConcurrentLRU
,
error
)
{
c
:=
&
ConcurrentLRU
{}
var
err
error
c
.
u
,
err
=
NewLRU
(
size
,
onEvict
)
return
c
,
err
}
// NewConcurrentLRUWithExpire contrusts an ConcurrentLRU of the given size and expire time
func
NewConcurrentLRUWithExpire
(
size
int
,
expire
time
.
Duration
,
onEvict
EvictCallback
)
(
*
ConcurrentLRU
,
error
)
{
c
:=
&
ConcurrentLRU
{}
var
err
error
c
.
u
,
err
=
NewLRUWithExpire
(
size
,
expire
,
onEvict
)
return
c
,
err
}
// Purge is used to completely clear the cache
func
(
c
*
ConcurrentLRU
)
Purge
()
{
c
.
Lock
()
defer
c
.
Unlock
()
c
.
u
.
Purge
()
}
// Add adds a value to the cache. Returns true if an eviction occurred.
func
(
c
*
ConcurrentLRU
)
Add
(
key
,
value
interface
{})
bool
{
c
.
Lock
()
defer
c
.
Unlock
()
return
c
.
u
.
Add
(
key
,
value
)
}
// AddEx adds a value to the cache with expire. Returns true if an eviction occurred.
func
(
c
*
ConcurrentLRU
)
AddEx
(
key
,
value
interface
{},
expire
time
.
Duration
)
bool
{
c
.
Lock
()
defer
c
.
Unlock
()
return
c
.
u
.
AddEx
(
key
,
value
,
expire
)
}
// Get looks up a key's value from the cache.
func
(
c
*
ConcurrentLRU
)
Get
(
key
interface
{})
(
value
interface
{},
ok
bool
)
{
c
.
Lock
()
defer
c
.
Unlock
()
return
c
.
u
.
Get
(
key
)
}
// Check if a key is in the cache, without updating the recent-ness
// or deleting it for being stale.
func
(
c
*
ConcurrentLRU
)
Contains
(
key
interface
{})
(
ok
bool
)
{
c
.
Lock
()
defer
c
.
Unlock
()
return
c
.
u
.
Contains
(
key
)
}
// Returns the key value (or undefined if not found) without updating
// the "recently used"-ness of the key.
func
(
c
*
ConcurrentLRU
)
Peek
(
key
interface
{})
(
value
interface
{},
ok
bool
)
{
c
.
Lock
()
defer
c
.
Unlock
()
return
c
.
u
.
Peek
(
key
)
}
// Remove removes the provided key from the cache, returning if the
// key was contained.
func
(
c
*
ConcurrentLRU
)
Remove
(
key
interface
{})
bool
{
c
.
Lock
()
defer
c
.
Unlock
()
return
c
.
u
.
Remove
(
key
)
}
// RemoveOldest removes the oldest item from the cache.
func
(
c
*
ConcurrentLRU
)
RemoveOldest
()
(
interface
{},
interface
{},
bool
)
{
c
.
Lock
()
defer
c
.
Unlock
()
return
c
.
u
.
RemoveOldest
()
}
// GetOldest returns the oldest entry
func
(
c
*
ConcurrentLRU
)
GetOldest
()
(
interface
{},
interface
{},
bool
)
{
c
.
Lock
()
defer
c
.
Unlock
()
return
c
.
u
.
GetOldest
()
}
// Keys returns a slice of the keys in the cache, from oldest to newest.
func
(
c
*
ConcurrentLRU
)
Keys
()
[]
interface
{}
{
c
.
Lock
()
defer
c
.
Unlock
()
return
c
.
u
.
Keys
()
}
// Len returns the number of items in the cache.
func
(
c
*
ConcurrentLRU
)
Len
()
int
{
c
.
Lock
()
defer
c
.
Unlock
()
return
c
.
u
.
Len
()
}
// Resize changes the cache size.
func
(
c
*
ConcurrentLRU
)
Resize
(
size
int
)
(
evicted
int
)
{
c
.
Lock
()
defer
c
.
Unlock
()
return
c
.
u
.
Resize
(
size
)
}
// removeOldest removes the oldest item from the cache.
func
(
c
*
ConcurrentLRU
)
removeOldest
()
{
c
.
Lock
()
defer
c
.
Unlock
()
c
.
u
.
removeOldest
()
}
// removeElement is used to remove a given list element from the cache
func
(
c
*
ConcurrentLRU
)
removeElement
(
e
*
list
.
Element
)
{
c
.
Lock
()
defer
c
.
Unlock
()
c
.
u
.
removeElement
(
e
)
}
lru/sync_lru_test.go
deleted
100644 → 0
View file @
b05f8159
package
lru
import
(
"testing"
"time"
)
func
TestConcurrentLRU
(
t
*
testing
.
T
)
{
evictCounter
:=
0
onEvicted
:=
func
(
k
interface
{},
v
interface
{})
{
if
k
!=
v
{
t
.
Fatalf
(
"Evict values not equal (%v!=%v)"
,
k
,
v
)
}
evictCounter
+=
1
}
l
,
err
:=
NewConcurrentLRU
(
128
,
onEvicted
)
if
err
!=
nil
{
t
.
Fatalf
(
"err: %v"
,
err
)
}
for
i
:=
0
;
i
<
256
;
i
++
{
l
.
Add
(
i
,
i
)
}
if
l
.
Len
()
!=
128
{
t
.
Fatalf
(
"bad len: %v"
,
l
.
Len
())
}
if
evictCounter
!=
128
{
t
.
Fatalf
(
"bad evict count: %v"
,
evictCounter
)
}
for
i
,
k
:=
range
l
.
Keys
()
{
if
v
,
ok
:=
l
.
Get
(
k
);
!
ok
||
v
!=
k
||
v
!=
i
+
128
{
t
.
Fatalf
(
"bad key: %v"
,
k
)
}
}
for
i
:=
0
;
i
<
128
;
i
++
{
_
,
ok
:=
l
.
Get
(
i
)
if
ok
{
t
.
Fatalf
(
"should be evicted"
)
}
}
for
i
:=
128
;
i
<
256
;
i
++
{
_
,
ok
:=
l
.
Get
(
i
)
if
!
ok
{
t
.
Fatalf
(
"should not be evicted"
)
}
}
for
i
:=
128
;
i
<
192
;
i
++
{
ok
:=
l
.
Remove
(
i
)
if
!
ok
{
t
.
Fatalf
(
"should be contained"
)
}
ok
=
l
.
Remove
(
i
)
if
ok
{
t
.
Fatalf
(
"should not be contained"
)
}
_
,
ok
=
l
.
Get
(
i
)
if
ok
{
t
.
Fatalf
(
"should be deleted"
)
}
}
l
.
Get
(
192
)
// expect 192 to be last key in l.Keys()
for
i
,
k
:=
range
l
.
Keys
()
{
if
(
i
<
63
&&
k
!=
i
+
193
)
||
(
i
==
63
&&
k
!=
192
)
{
t
.
Fatalf
(
"out of order key: %v"
,
k
)
}
}
l
.
Purge
()
if
l
.
Len
()
!=
0
{
t
.
Fatalf
(
"bad len: %v"
,
l
.
Len
())
}
if
_
,
ok
:=
l
.
Get
(
200
);
ok
{
t
.
Fatalf
(
"should contain nothing"
)
}
}
func
TestConcurrentLRU_GetOldest_RemoveOldest
(
t
*
testing
.
T
)
{
l
,
err
:=
NewConcurrentLRU
(
128
,
nil
)
if
err
!=
nil
{
t
.
Fatalf
(
"err: %v"
,
err
)
}
for
i
:=
0
;
i
<
256
;
i
++
{
l
.
Add
(
i
,
i
)
}
k
,
_
,
ok
:=
l
.
GetOldest
()
if
!
ok
{
t
.
Fatalf
(
"missing"
)
}
if
k
.
(
int
)
!=
128
{
t
.
Fatalf
(
"bad: %v"
,
k
)
}
k
,
_
,
ok
=
l
.
RemoveOldest
()
if
!
ok
{
t
.
Fatalf
(
"missing"
)
}
if
k
.
(
int
)
!=
128
{
t
.
Fatalf
(
"bad: %v"
,
k
)
}
k
,
_
,
ok
=
l
.
RemoveOldest
()
if
!
ok
{
t
.
Fatalf
(
"missing"
)
}
if
k
.
(
int
)
!=
129
{
t
.
Fatalf
(
"bad: %v"
,
k
)
}
}
// Test that Add returns true/false if an eviction occurred
func
TestConcurrentLRU_Add
(
t
*
testing
.
T
)
{
evictCounter
:=
0
onEvicted
:=
func
(
k
interface
{},
v
interface
{})
{
evictCounter
+=
1
}
l
,
err
:=
NewConcurrentLRU
(
1
,
onEvicted
)
if
err
!=
nil
{
t
.
Fatalf
(
"err: %v"
,
err
)
}
if
l
.
Add
(
1
,
1
)
==
true
||
evictCounter
!=
0
{
t
.
Errorf
(
"should not have an eviction"
)
}
if
l
.
Add
(
2
,
2
)
==
false
||
evictCounter
!=
1
{
t
.
Errorf
(
"should have an eviction"
)
}
}
// Test that Contains doesn't update recent-ness
func
TestConcurrentLRU_Contains
(
t
*
testing
.
T
)
{
l
,
err
:=
NewConcurrentLRU
(
2
,
nil
)
if
err
!=
nil
{
t
.
Fatalf
(
"err: %v"
,
err
)
}
l
.
Add
(
1
,
1
)
l
.
Add
(
2
,
2
)
if
!
l
.
Contains
(
1
)
{
t
.
Errorf
(
"1 should be contained"
)
}
l
.
Add
(
3
,
3
)
if
l
.
Contains
(
1
)
{
t
.
Errorf
(
"Contains should not have updated recent-ness of 1"
)
}
}
// Test that Peek doesn't update recent-ness
func
TestConcurrentLRU_Peek
(
t
*
testing
.
T
)
{
l
,
err
:=
NewConcurrentLRU
(
2
,
nil
)
if
err
!=
nil
{
t
.
Fatalf
(
"err: %v"
,
err
)
}
l
.
Add
(
1
,
1
)
l
.
Add
(
2
,
2
)
if
v
,
ok
:=
l
.
Peek
(
1
);
!
ok
||
v
!=
1
{
t
.
Errorf
(
"1 should be set to 1: %v, %v"
,
v
,
ok
)
}
l
.
Add
(
3
,
3
)
if
l
.
Contains
(
1
)
{
t
.
Errorf
(
"should not have updated recent-ness of 1"
)
}
}
// Test that expire feature
func
TestConcurrentLRU_Expire
(
t
*
testing
.
T
)
{
l
,
err
:=
NewConcurrentLRUWithExpire
(
2
,
2
*
time
.
Second
,
nil
)
if
err
!=
nil
{
t
.
Fatalf
(
"err: %v"
,
err
)
}
l
.
Add
(
1
,
1
)
if
!
l
.
Contains
(
1
)
{
t
.
Errorf
(
"1 should be contained"
)
}
time
.
Sleep
(
2
*
time
.
Second
)
if
l
.
Contains
(
1
)
{
t
.
Errorf
(
"1 should not be contained"
)
}
l
.
AddEx
(
1
,
1
,
1
*
time
.
Second
)
if
!
l
.
Contains
(
1
)
{
t
.
Errorf
(
"1 should be contained"
)
}
time
.
Sleep
(
1
*
time
.
Second
)
if
l
.
Contains
(
1
)
{
t
.
Errorf
(
"1 should not be contained"
)
}
}
// Test that Resize can upsize and downsize
func
TestConcurrentLRU_Resize
(
t
*
testing
.
T
)
{
onEvictCounter
:=
0
onEvicted
:=
func
(
k
interface
{},
v
interface
{})
{
onEvictCounter
++
}
l
,
err
:=
NewConcurrentLRU
(
2
,
onEvicted
)
if
err
!=
nil
{
t
.
Fatalf
(
"err: %v"
,
err
)
}
// Downsize
l
.
Add
(
1
,
1
)
l
.
Add
(
2
,
2
)
evicted
:=
l
.
Resize
(
1
)
if
evicted
!=
1
{
t
.
Errorf
(
"1 element should have been evicted: %v"
,
evicted
)
}
if
onEvictCounter
!=
1
{
t
.
Errorf
(
"onEvicted should have been called 1 time: %v"
,
onEvictCounter
)
}
l
.
Add
(
3
,
3
)
if
l
.
Contains
(
1
)
{
t
.
Errorf
(
"Element 1 should have been evicted"
)
}
// Upsize
evicted
=
l
.
Resize
(
2
)
if
evicted
!=
0
{
t
.
Errorf
(
"0 elements should have been evicted: %v"
,
evicted
)
}
l
.
Add
(
4
,
4
)
if
!
l
.
Contains
(
3
)
||
!
l
.
Contains
(
4
)
{
t
.
Errorf
(
"Cache should have contained 2 elements"
)
}
}
stringing/replace.go
View file @
de46e026
...
...
@@ -4,9 +4,9 @@ import (
"strings"
)
type
Validator
=
func
(
rune
)
bool
type
Validator
Fn
=
func
(
rune
)
bool
func
ReplaceAll
(
st
string
,
valid
Validator
)
string
{
func
ReplaceAll
(
st
string
,
valid
Validator
Fn
)
string
{
var
sb
strings
.
Builder
sb
.
Grow
(
len
(
st
))
for
_
,
c
:=
range
st
{
...
...