Skip to content
Snippets Groups Projects
Unverified Commit e12e5cc1 authored by Wolfgang Welz's avatar Wolfgang Welz Committed by GitHub
Browse files

Fix: Remove unused datastructure package (#193)

parent 247b38d5
No related branches found
No related tags found
No related merge requests found
...@@ -7,11 +7,10 @@ require ( ...@@ -7,11 +7,10 @@ require (
github.com/dgrijalva/jwt-go v3.2.0+incompatible github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/dgryski/go-farm v0.0.0-20191112170834-c2139c5d712b // indirect github.com/dgryski/go-farm v0.0.0-20191112170834-c2139c5d712b // indirect
github.com/gdamore/tcell v1.3.0 github.com/gdamore/tcell v1.3.0
github.com/go-zeromq/zmq4 v0.7.0
github.com/golang/protobuf v1.3.2 github.com/golang/protobuf v1.3.2
github.com/googollee/go-engine.io v1.4.3-0.20190924125625-798118fc0dd2 github.com/googollee/go-engine.io v1.4.3-0.20190924125625-798118fc0dd2
github.com/googollee/go-socket.io v1.4.3-0.20191204093753-683f8725b6d0 github.com/googollee/go-socket.io v1.4.3-0.20191204093753-683f8725b6d0
github.com/gorilla/websocket v1.4.1 github.com/gorilla/websocket v1.4.1 // indirect
github.com/iotaledger/hive.go v0.0.0-20200121213505-28904d5f037c github.com/iotaledger/hive.go v0.0.0-20200121213505-28904d5f037c
github.com/iotaledger/iota.go v1.0.0-beta.14 github.com/iotaledger/iota.go v1.0.0-beta.14
github.com/labstack/echo v3.3.10+incompatible github.com/labstack/echo v3.3.10+incompatible
...@@ -35,9 +34,7 @@ require ( ...@@ -35,9 +34,7 @@ require (
go.uber.org/zap v1.13.0 go.uber.org/zap v1.13.0
golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876 golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e // indirect
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8 // indirect golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8 // indirect
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 // indirect
gopkg.in/ini.v1 v1.51.1 // indirect gopkg.in/ini.v1 v1.51.1 // indirect
gopkg.in/yaml.v2 v2.2.7 // indirect gopkg.in/yaml.v2 v2.2.7 // indirect
) )
...@@ -64,10 +64,6 @@ github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2 ...@@ -64,10 +64,6 @@ github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-zeromq/goczmq/v4 v4.2.2 h1:HAJN+i+3NW55ijMJJhk7oWxHKXgAuSBkoFfvr8bYj4U=
github.com/go-zeromq/goczmq/v4 v4.2.2/go.mod h1:Sm/lxrfxP/Oxqs0tnHD6WAhwkWrx+S+1MRrKzcxoaYE=
github.com/go-zeromq/zmq4 v0.7.0 h1:tmmTVfWB0HYo+8Ra0DK2MJIDl1lsvuU/J9559hpLU7s=
github.com/go-zeromq/zmq4 v0.7.0/go.mod h1:fo1rWyfV/bsg7tq/F9LF1H0e2Cf3ovQFoge1G21AnWU=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
...@@ -343,8 +339,6 @@ golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJ ...@@ -343,8 +339,6 @@ golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
...@@ -386,8 +380,6 @@ golang.org/x/tools v0.0.0-20200103221440-774c71fcf114 h1:DnSr2mCsxyCE6ZgIkmcWUQY ...@@ -386,8 +380,6 @@ golang.org/x/tools v0.0.0-20200103221440-774c71fcf114 h1:DnSr2mCsxyCE6ZgIkmcWUQY
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y=
......
package datastructure
import (
"fmt"
"sync"
)
type DoublyLinkedList struct {
head *DoublyLinkedListEntry
tail *DoublyLinkedListEntry
count int
mutex sync.RWMutex
}
// region public methods with locking //////////////////////////////////////////////////////////////////////////////////
// Appends the specified value to the end of this list.
func (list *DoublyLinkedList) Add(value interface{}) *DoublyLinkedListEntry {
return list.AddLast(value)
}
// Appends the specified element to the end of this list.
func (list *DoublyLinkedList) AddEntry(entry *DoublyLinkedListEntry) {
list.AddLastEntry(entry)
}
func (list *DoublyLinkedList) AddLast(value interface{}) *DoublyLinkedListEntry {
newEntry := &DoublyLinkedListEntry{
value: value,
}
list.AddLastEntry(newEntry)
return newEntry
}
func (list *DoublyLinkedList) AddLastEntry(entry *DoublyLinkedListEntry) {
list.mutex.Lock()
defer list.mutex.Unlock()
list.addLastEntry(entry)
}
func (list *DoublyLinkedList) AddFirst(value interface{}) *DoublyLinkedListEntry {
newEntry := &DoublyLinkedListEntry{
value: value,
}
list.AddFirstEntry(newEntry)
return newEntry
}
func (list *DoublyLinkedList) AddFirstEntry(entry *DoublyLinkedListEntry) {
list.mutex.Lock()
defer list.mutex.Unlock()
list.addFirstEntry(entry)
}
func (list *DoublyLinkedList) Clear() {
list.mutex.Lock()
defer list.mutex.Unlock()
list.clear()
}
func (list *DoublyLinkedList) GetFirst() (interface{}, error) {
if firstEntry, err := list.GetFirstEntry(); err != nil {
return nil, err
} else {
return firstEntry.GetValue(), nil
}
}
func (list *DoublyLinkedList) GetFirstEntry() (*DoublyLinkedListEntry, error) {
list.mutex.RLock()
defer list.mutex.RUnlock()
return list.getFirstEntry()
}
func (list *DoublyLinkedList) GetLast() (interface{}, error) {
if lastEntry, err := list.GetLastEntry(); err != nil {
return nil, err
} else {
return lastEntry.GetValue(), nil
}
}
func (list *DoublyLinkedList) GetLastEntry() (*DoublyLinkedListEntry, error) {
list.mutex.RLock()
defer list.mutex.RUnlock()
return list.getLastEntry()
}
func (list *DoublyLinkedList) RemoveFirst() (interface{}, error) {
if firstEntry, err := list.RemoveFirstEntry(); err != nil {
return nil, err
} else {
return firstEntry.GetValue(), nil
}
}
func (list *DoublyLinkedList) RemoveFirstEntry() (*DoublyLinkedListEntry, error) {
list.mutex.Lock()
defer list.mutex.Unlock()
return list.removeFirstEntry()
}
func (list *DoublyLinkedList) RemoveLast() (interface{}, error) {
if lastEntry, err := list.RemoveLastEntry(); err != nil {
return nil, err
} else {
return lastEntry.GetValue(), nil
}
}
func (list *DoublyLinkedList) RemoveLastEntry() (*DoublyLinkedListEntry, error) {
list.mutex.Lock()
defer list.mutex.Unlock()
return list.removeLastEntry()
}
func (list *DoublyLinkedList) Remove(value interface{}) error {
list.mutex.RLock()
currentEntry := list.head
for currentEntry != nil {
if currentEntry.GetValue() == value {
list.mutex.RUnlock()
if err := list.RemoveEntry(currentEntry); err != nil {
return err
}
return nil
}
currentEntry = currentEntry.GetNext()
}
list.mutex.RUnlock()
return fmt.Errorf("%w: the entry is not part of the list", ErrNoSuchElement)
}
func (list *DoublyLinkedList) RemoveEntry(entry *DoublyLinkedListEntry) error {
list.mutex.Lock()
defer list.mutex.Unlock()
return list.removeEntry(entry)
}
func (list *DoublyLinkedList) GetSize() int {
list.mutex.RLock()
defer list.mutex.RUnlock()
return list.count
}
// endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
// region private methods without locking //////////////////////////////////////////////////////////////////////////////
func (list *DoublyLinkedList) addLastEntry(entry *DoublyLinkedListEntry) {
if list.head == nil {
list.head = entry
} else {
list.tail.SetNext(entry)
entry.SetPrev(list.tail)
}
list.tail = entry
list.count++
}
func (list *DoublyLinkedList) addFirstEntry(entry *DoublyLinkedListEntry) {
if list.tail == nil {
list.tail = entry
} else {
list.head.SetPrev(entry)
entry.SetNext(list.head)
}
list.head = entry
list.count++
}
func (list *DoublyLinkedList) clear() {
list.head = nil
list.tail = nil
list.count = 0
}
func (list *DoublyLinkedList) getFirstEntry() (*DoublyLinkedListEntry, error) {
if list.head == nil {
return nil, fmt.Errorf("%w: the list is empty", ErrNoSuchElement)
}
return list.head, nil
}
func (list *DoublyLinkedList) getLastEntry() (*DoublyLinkedListEntry, error) {
if list.tail == nil {
return nil, fmt.Errorf("%w: the list is empty", ErrNoSuchElement)
}
return list.tail, nil
}
func (list *DoublyLinkedList) removeFirstEntry() (*DoublyLinkedListEntry, error) {
entryToRemove := list.head
if err := list.removeEntry(entryToRemove); err != nil {
return nil, err
}
return entryToRemove, nil
}
func (list *DoublyLinkedList) removeLastEntry() (*DoublyLinkedListEntry, error) {
entryToRemove := list.tail
if err := list.removeEntry(entryToRemove); err != nil {
return nil, err
}
return entryToRemove, nil
}
func (list *DoublyLinkedList) removeEntry(entry *DoublyLinkedListEntry) error {
if entry == nil {
return fmt.Errorf("%w: the entry must not be nil", ErrInvalidArgument)
}
if list.head == nil {
return fmt.Errorf("%w: the entry is not part of the list", ErrNoSuchElement)
}
prevEntry := entry.GetPrev()
nextEntry := entry.GetNext()
if nextEntry != nil {
nextEntry.SetPrev(prevEntry)
}
if list.head == entry {
list.head = nextEntry
}
if prevEntry != nil {
prevEntry.SetNext(nextEntry)
}
if list.tail == entry {
list.tail = prevEntry
}
entry.SetNext(nil)
entry.SetPrev(nil)
list.count--
return nil
}
// endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
package datastructure
import (
"sync"
)
type DoublyLinkedListEntry struct {
value interface{}
prev *DoublyLinkedListEntry
next *DoublyLinkedListEntry
mutex sync.RWMutex
}
func (entry *DoublyLinkedListEntry) GetNext() *DoublyLinkedListEntry {
entry.mutex.RLock()
defer entry.mutex.RUnlock()
return entry.next
}
func (entry *DoublyLinkedListEntry) SetNext(next *DoublyLinkedListEntry) {
entry.mutex.Lock()
defer entry.mutex.Unlock()
entry.next = next
}
func (entry *DoublyLinkedListEntry) GetPrev() *DoublyLinkedListEntry {
entry.mutex.RLock()
defer entry.mutex.RUnlock()
return entry.prev
}
func (entry *DoublyLinkedListEntry) SetPrev(prev *DoublyLinkedListEntry) {
entry.mutex.Lock()
defer entry.mutex.Unlock()
entry.prev = prev
}
func (entry *DoublyLinkedListEntry) GetValue() interface{} {
entry.mutex.RLock()
defer entry.mutex.RUnlock()
return entry.value
}
func (entry *DoublyLinkedListEntry) SetValue(value interface{}) {
entry.mutex.Lock()
defer entry.mutex.Unlock()
entry.value = value
}
package datastructure
import (
"testing"
)
func TestAdd(t *testing.T) {
doublyLinkedList := &DoublyLinkedList{}
doublyLinkedList.Add(12)
doublyLinkedList.Add(12)
doublyLinkedList.Add(15)
doublyLinkedList.Add(99)
if doublyLinkedList.GetSize() != 4 {
t.Error("the size of the list is wrong")
}
}
func TestDelete(t *testing.T) {
doublyLinkedList := &DoublyLinkedList{}
doublyLinkedList.Add(12)
doublyLinkedList.Add(13)
doublyLinkedList.Add(15)
doublyLinkedList.Add(99)
if _, err := doublyLinkedList.RemoveFirst(); err != nil {
t.Error(err)
}
if firstEntry, err := doublyLinkedList.GetFirst(); err != nil {
t.Error(err)
} else if firstEntry != 13 {
t.Error("first entry should be 13 after delete")
}
if _, err := doublyLinkedList.RemoveLast(); err != nil {
t.Error(err)
}
if lastEntry, err := doublyLinkedList.GetLast(); err != nil {
t.Error(err)
} else if lastEntry != 15 {
t.Error("last entry should be 15 after delete")
}
if doublyLinkedList.GetSize() != 2 {
t.Error("the size of the list should be 2 after delete")
}
}
package datastructure
import (
"errors"
)
var (
ErrNoSuchElement = errors.New("element does not exist")
ErrInvalidArgument = errors.New("invalid argument")
)
package datastructure
import (
"sync"
)
type KRWMutex struct {
keyMutexConsumers map[interface{}]int
keyMutexes map[interface{}]*sync.RWMutex
mutex sync.RWMutex
}
func NewKRWMutex() *KRWMutex {
return &KRWMutex{
keyMutexConsumers: make(map[interface{}]int),
keyMutexes: make(map[interface{}]*sync.RWMutex),
}
}
func (krwMutex *KRWMutex) Register(key interface{}) (result *sync.RWMutex) {
krwMutex.mutex.Lock()
if val, exists := krwMutex.keyMutexConsumers[key]; exists {
krwMutex.keyMutexConsumers[key] = val + 1
result = krwMutex.keyMutexes[key]
} else {
result = &sync.RWMutex{}
krwMutex.keyMutexConsumers[key] = 1
krwMutex.keyMutexes[key] = result
}
krwMutex.mutex.Unlock()
return
}
func (kwrMutex *KRWMutex) Free(key interface{}) {
kwrMutex.mutex.Lock()
if val, exists := kwrMutex.keyMutexConsumers[key]; exists {
if val == 1 {
delete(kwrMutex.keyMutexConsumers, key)
delete(kwrMutex.keyMutexes, key)
} else {
kwrMutex.keyMutexConsumers[key] = val - 1
}
} else {
panic("trying to free non-existent key")
}
kwrMutex.mutex.Unlock()
}
package datastructure
import (
"testing"
)
func TestKRWMutex_Free(t *testing.T) {
krwMutex := NewKRWMutex()
krwMutex.Register("test")
krwMutex.Register("test")
krwMutex.Free("test")
krwMutex.Free("test")
}
func BenchmarkKRWMutex(b *testing.B) {
krwMutex := NewKRWMutex()
for i := 0; i < b.N; i++ {
krwMutex.Register(i)
krwMutex.Free(i)
}
}
package datastructure
import (
"sync"
"github.com/iotaledger/hive.go/typeutils"
)
type lruCacheElement struct {
key interface{}
value interface{}
}
type LRUCache struct {
directory map[interface{}]*DoublyLinkedListEntry
doublyLinkedList *DoublyLinkedList
capacity int
size int
options *LRUCacheOptions
mutex sync.RWMutex
krwMutex KRWMutex
}
func NewLRUCache(capacity int, options ...*LRUCacheOptions) *LRUCache {
var currentOptions *LRUCacheOptions
if len(options) < 1 || options[0] == nil {
currentOptions = DEFAULT_OPTIONS
} else {
currentOptions = options[0]
}
return &LRUCache{
directory: make(map[interface{}]*DoublyLinkedListEntry, capacity),
doublyLinkedList: &DoublyLinkedList{},
capacity: capacity,
options: currentOptions,
krwMutex: KRWMutex{keyMutexConsumers: make(map[interface{}]int), keyMutexes: make(map[interface{}]*sync.RWMutex)},
}
}
func (cache *LRUCache) Set(key interface{}, value interface{}) {
keyMutex := cache.krwMutex.Register(key)
keyMutex.Lock()
cache.mutex.Lock()
cache.set(key, value)
cache.mutex.Unlock()
keyMutex.Unlock()
cache.krwMutex.Free(key)
}
func (cache *LRUCache) set(key interface{}, value interface{}) {
directory := cache.directory
if element, exists := directory[key]; exists {
element.value.(*lruCacheElement).value = value
cache.promoteElement(element)
} else {
linkedListEntry := &DoublyLinkedListEntry{value: &lruCacheElement{key: key, value: value}}
cache.doublyLinkedList.addFirstEntry(linkedListEntry)
directory[key] = linkedListEntry
if cache.size == cache.capacity {
if element, err := cache.doublyLinkedList.removeLastEntry(); err != nil {
panic(err)
} else {
lruCacheElement := element.value.(*lruCacheElement)
removedElementKey := lruCacheElement.key
delete(directory, removedElementKey)
if cache.options.EvictionCallback != nil {
cache.options.EvictionCallback(removedElementKey, lruCacheElement.value)
}
}
} else {
cache.size++
}
}
}
func (cache *LRUCache) ComputeIfAbsent(key interface{}, callback func() interface{}) (result interface{}) {
keyMutex := cache.krwMutex.Register(key)
keyMutex.RLock()
cache.mutex.RLock()
if element, exists := cache.directory[key]; exists {
cache.mutex.RUnlock()
cache.mutex.Lock()
cache.promoteElement(element)
cache.mutex.Unlock()
result = element.GetValue().(*lruCacheElement).value
keyMutex.RUnlock()
} else {
cache.mutex.RUnlock()
keyMutex.RUnlock()
keyMutex.Lock()
if result = callback(); !typeutils.IsInterfaceNil(result) {
cache.mutex.Lock()
cache.set(key, result)
cache.mutex.Unlock()
}
keyMutex.Unlock()
}
cache.krwMutex.Free(key)
return
}
// Calls the callback if an entry with the given key exists.
// The result of the callback is written back into the cache.
// If the callback returns nil the entry is removed from the cache.
// Returns the updated entry.
func (cache *LRUCache) ComputeIfPresent(key interface{}, callback func(value interface{}) interface{}) (result interface{}) {
keyMutex := cache.krwMutex.Register(key)
keyMutex.RLock()
cache.mutex.RLock()
if entry, exists := cache.directory[key]; exists {
cache.mutex.RUnlock()
keyMutex.RUnlock()
keyMutex.Lock()
result = entry.GetValue().(*lruCacheElement).value
if callbackResult := callback(result); !typeutils.IsInterfaceNil(callbackResult) {
result = callbackResult
cache.mutex.Lock()
cache.set(key, callbackResult)
cache.mutex.Unlock()
keyMutex.Unlock()
} else {
cache.mutex.Lock()
if err := cache.doublyLinkedList.removeEntry(entry); err != nil {
panic(err)
}
delete(cache.directory, key)
cache.size--
cache.mutex.Unlock()
keyMutex.Unlock()
if cache.options.EvictionCallback != nil {
cache.options.EvictionCallback(key, result)
}
}
} else {
cache.mutex.RUnlock()
keyMutex.RUnlock()
}
cache.krwMutex.Free(key)
return
}
func (cache *LRUCache) Contains(key interface{}) (result bool) {
keyMutex := cache.krwMutex.Register(key)
keyMutex.RLock()
cache.mutex.RLock()
if element, exists := cache.directory[key]; exists {
cache.mutex.RUnlock()
keyMutex.RUnlock()
cache.mutex.Lock()
cache.promoteElement(element)
cache.mutex.Unlock()
result = true
} else {
cache.mutex.RUnlock()
keyMutex.RUnlock()
result = false
}
cache.krwMutex.Free(key)
return
}
func (cache *LRUCache) Get(key interface{}) (result interface{}) {
keyMutex := cache.krwMutex.Register(key)
keyMutex.RLock()
cache.mutex.RLock()
if element, exists := cache.directory[key]; exists {
cache.mutex.RUnlock()
cache.mutex.Lock()
cache.promoteElement(element)
cache.mutex.Unlock()
result = element.GetValue().(*lruCacheElement).value
} else {
cache.mutex.RUnlock()
}
keyMutex.RUnlock()
cache.krwMutex.Free(key)
return
}
func (cache *LRUCache) GetCapacity() int {
cache.mutex.RLock()
defer cache.mutex.RUnlock()
return cache.capacity
}
func (cache *LRUCache) GetSize() int {
cache.mutex.RLock()
defer cache.mutex.RUnlock()
return cache.size
}
func (cache *LRUCache) Delete(key interface{}) bool {
keyMutex := cache.krwMutex.Register(key)
keyMutex.Lock()
cache.mutex.RLock()
entry, exists := cache.directory[key]
if exists {
cache.mutex.RUnlock()
cache.mutex.Lock()
defer cache.mutex.Unlock()
if err := cache.doublyLinkedList.removeEntry(entry); err != nil {
panic(err)
}
delete(cache.directory, key)
cache.size--
keyMutex.Unlock()
if cache.options.EvictionCallback != nil {
cache.options.EvictionCallback(key, entry.GetValue().(*lruCacheElement).value)
}
return true
}
cache.mutex.RUnlock()
keyMutex.Unlock()
cache.krwMutex.Free(key)
return false
}
func (cache *LRUCache) promoteElement(element *DoublyLinkedListEntry) {
if err := cache.doublyLinkedList.removeEntry(element); err != nil {
panic(err)
}
cache.doublyLinkedList.addFirstEntry(element)
}
package datastructure
import (
"time"
)
type LRUCacheOptions struct {
EvictionCallback func(key interface{}, value interface{})
IdleTimeout time.Duration
}
var DEFAULT_OPTIONS = &LRUCacheOptions{
EvictionCallback: nil,
IdleTimeout: 30 * time.Second,
}
package datastructure
import (
"testing"
)
func TestLRUCache(t *testing.T) {
cache := NewLRUCache(5)
cache.ComputeIfAbsent("test", func() interface{} {
return 12
})
if cache.Get("test") != 12 {
t.Error("the cache does not contain the added elements")
}
if cache.GetSize() != 1 {
t.Error("the size should be 1")
}
if cache.GetCapacity() != 5 {
t.Error("the capacity should be 5")
}
cache.Set("a", 3)
cache.Set("b", 4)
cache.Set("c", 5)
cache.Set("d", 6)
if cache.GetSize() != 5 {
t.Error("the size should be 5")
}
cache.Set("e", 7)
if cache.GetSize() != 5 {
t.Error("the size should be 5")
}
if cache.Get("test") != nil {
t.Error("'test' should have been dropped")
}
cache.Set("a", 6)
cache.Set("f", 8)
if cache.GetSize() != 5 {
t.Error("the size should be 5")
}
if cache.Get("a") == nil {
t.Error("'a' should not have been dropped")
}
if cache.Get("b") != nil {
t.Error("'b' should have been dropped")
}
{
key, value := "test2", 1337
cache.ComputeIfAbsent(key, func() interface{} {
return value
})
if cache.Get(key) != value {
t.Error("'" + key + "' should have been added")
}
}
if cache.GetSize() != 5 {
t.Error("the size should be 5")
}
if cache.Get("a") != nil {
cache.Delete("a")
}
if cache.GetSize() != 4 {
t.Error("the size should be 4")
}
cache.Delete("f")
if cache.GetSize() != 3 {
t.Error("the size should be 3")
}
}
func TestLRUCache_ComputeIfPresent(t *testing.T) {
cache := NewLRUCache(5)
cache.Set(8, 9)
cache.ComputeIfPresent(8, func(value interface{}) interface{} {
return 88
})
if cache.Get(8) != 88 || cache.GetSize() != 1 {
t.Error("cache was not updated correctly")
}
cache.ComputeIfPresent(8, func(value interface{}) interface{} {
return nil
})
if cache.Get(8) != nil || cache.GetSize() != 0 {
t.Error("cache was not updated correctly")
}
}
package datastructure
import (
"math/rand"
"sync"
"time"
)
func init() {
rand.Seed(time.Now().UnixNano())
}
type randomMapEntry struct {
key interface{}
value interface{}
keyIndex int
}
type RandomMap struct {
rawMap map[interface{}]*randomMapEntry
keys []interface{}
size int
mutex sync.RWMutex
}
func NewRandomMap() *RandomMap {
return &RandomMap{
rawMap: make(map[interface{}]*randomMapEntry),
keys: make([]interface{}, 0),
}
}
func (rmap *RandomMap) Set(key interface{}, value interface{}) {
rmap.mutex.Lock()
if entry, exists := rmap.rawMap[key]; exists {
entry.value = value
} else {
rmap.rawMap[key] = &randomMapEntry{
key: key,
value: value,
keyIndex: rmap.size,
}
rmap.keys = append(rmap.keys, key)
rmap.size++
}
rmap.mutex.Unlock()
}
func (rmap *RandomMap) Get(key interface{}) (result interface{}, exists bool) {
rmap.mutex.RLock()
if entry, entryExists := rmap.rawMap[key]; entryExists {
result = entry.value
exists = entryExists
}
rmap.mutex.RUnlock()
return
}
func (rmap *RandomMap) Delete(key interface{}) (result interface{}, exists bool) {
rmap.mutex.RLock()
if _, entryExists := rmap.rawMap[key]; entryExists {
rmap.mutex.RUnlock()
rmap.mutex.Lock()
if entry, entryExists := rmap.rawMap[key]; entryExists {
delete(rmap.rawMap, key)
rmap.size--
if entry.keyIndex != rmap.size {
oldKey := entry.keyIndex
movedKey := rmap.keys[rmap.size]
rmap.rawMap[movedKey].keyIndex = oldKey
rmap.keys[oldKey] = movedKey
}
rmap.keys = rmap.keys[:rmap.size]
result = entry.value
exists = true
}
rmap.mutex.Unlock()
} else {
rmap.mutex.RUnlock()
}
return
}
func (rmap *RandomMap) Size() (result int) {
rmap.mutex.RLock()
result = rmap.size
rmap.mutex.RUnlock()
return
}
func (rmap *RandomMap) RandomEntry() (result interface{}) {
rmap.mutex.RLock()
if rmap.size >= 1 {
result = rmap.rawMap[rmap.keys[rand.Intn(rmap.size)]].value
}
rmap.mutex.RUnlock()
return
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment