Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 111 additions & 0 deletions cacheitemset.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package cache2go

import (
"sync"
"time"
)

type Set struct {
sync.RWMutex
len int
members map[interface{}]int
}

func NewSet() *Set {
set := &Set{
len: 0,
members: make(map[interface{}]int),
}
return set
}

func (set *Set) Len() int {
set.RLock()
defer set.RUnlock()
return set.len
}

func (set *Set) SetAdd(member interface{}) error {
set.Lock()
defer set.Unlock()
set.members[member] = 0
set.len++
return nil
}

func (set *Set) SetHas(member interface{}) bool {
set.RLock()
defer set.RUnlock()

_, ok := set.members[member]
return ok
}

func (set *Set) SetRemove(member interface{}) error {
set.Lock()
defer set.Unlock()

if _, ok := set.members[member]; ok {
delete(set.members, member)
set.len--
}
return nil
}

// add member to table
func (table *CacheTable) SAdd(key interface{}, lifeSpan time.Duration, member interface{}) *CacheItem {
table.Lock()
item, ok := table.items[key]
if !ok {
set := NewSet()
set.SetAdd(member)
hashItem := NewCacheItem(key, lifeSpan, set)
table.addInternal(hashItem)
return hashItem
}
table.Unlock()

item.Lock()
if set, ok := item.Data().(*Set); ok {
set.SetAdd(member)
}
item.Unlock()

return item
}

// is member
func (table *CacheTable) SIsMember(key interface{}, member interface{}) bool {
table.RLock()
defer table.RUnlock()

setItem, ok := table.items[key]
if !ok {
return false
}

set, ok := setItem.Data().(*Set)
if !ok {
return false
}

return set.SetHas(member)
}

// delete member
func (table *CacheTable) SDelete(key interface{}, member interface{}) error {
table.Lock()
defer table.Unlock()

setItem, ok := table.items[key]
if !ok {
return ErrKeyNotFound
}

set, ok := setItem.Data().(*Set)
if !ok {
return ErrKeyTypeNotSet
}

return set.SetRemove(member)
}
26 changes: 26 additions & 0 deletions cacheitemset_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package cache2go

import (
"testing"
"time"
)

func TestCacheTable_SAdd_SIsMember_SDelete(t *testing.T) {
table := Cache("newset")
for i := 1; i <= 10; i++ {
table.SAdd("newset", 0*time.Second, i)
}
for i := 1; i <= 10; i++ {
if has := table.SIsMember("newset", i); !has {
t.Error("SAdd i is", i, "err is", has)
}
}
for i := 1; i <= 10; i++ {
table.SDelete("newset", i)
}
for i := 1; i <= 10; i++ {
if has := table.SIsMember("newset", i); has {
t.Error("SDelete i is", i, "err is", has)
}
}
}
69 changes: 69 additions & 0 deletions cachetablehash.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/**
cacheItem struct
data map[interface{}]interface{}

So data is hash, cacheItem is hashItem
*/
package cache2go

import "time"

func (table *CacheTable) HAdd(key interface{}, lifeSpan time.Duration, hkey interface{}, hvalue interface{}) *CacheItem {
table.Lock()
item, ok := table.items[key]
if !ok {
hash := make(map[interface{}]interface{})
hash[hkey] = hvalue
hashItem := NewCacheItem(key, lifeSpan, hash)
table.addInternal(hashItem)
return hashItem
}
table.Unlock()

item.Lock()
if hash, ok := item.Data().(map[interface{}]interface{}); ok {
hash[hkey] = hvalue
}
item.Unlock()

return item

}

func (table *CacheTable) HValue(key interface{}, hkey interface{}) (interface{}, error) {
table.RLock()
hashItem, ok := table.items[key]
table.RUnlock()
if !ok {
return nil, ErrKeyNotFound
}

hvalue, hok := hashItem.Data().(map[interface{}]interface{})[hkey]
if !hok {
return nil, ErrKeyNotFound
}

hashItem.KeepAlive()
return hvalue, nil
}

func (table *CacheTable) HDelete(key interface{}, hkey interface{}) error {
table.RLock()
defer table.RUnlock()

hashItem, ok := table.items[key]
if !ok {
return ErrKeyNotFound
}

hashItem.Lock()
defer hashItem.Unlock()

hash, ok := hashItem.Data().(map[interface{}]interface{})
if !ok {
return ErrKeyTypeNotHash
}

delete(hash, hkey)
return nil
}
47 changes: 47 additions & 0 deletions cachetablehash_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package cache2go

import (
"testing"
"time"
)

func TestCacheTable_HAdd(t *testing.T) {
cache := Cache("htable")
var hs1 int8 = 100
cache.HAdd("hstudents", 0*time.Second, "s1", hs1)
hv, err := cache.HValue("hstudents", "s1")
if err != nil || hv == nil || hv.(int8) != hs1 {
t.Error("Error retrieving non expiring data from cache", err)
}

var ht1 int8 = 99
cache.HAdd("hteacher", 1*time.Second, "t1", ht1)
time.Sleep(2 * time.Second)
htv, err := cache.HValue("hteacher", "t1")
if err == nil || htv != nil {
t.Error("Error retrieving non expiring data from cache", err)
}
}

func TestCacheTable_HDelete(t *testing.T) {
var hv interface{}
var err error
var hs1 int8 = 100

cache := Cache("htable")
cache.HAdd("hstudents", 0*time.Second, "s1", hs1)
hv, err = cache.HValue("hstudents", "s1")
if err != nil || hv == nil || hv.(int8) != hs1 {
t.Error("Error retrieving non expiring data from cache", err)
}

err = cache.HDelete("hstudents", "s1")
if err != nil {
t.Error("Error delete hash key fail", err)
}

hv, err = cache.HValue("hstudents", "s1")
if err == nil || hv != nil {
t.Error("Error retrieving non expiring data from cache", err)
}
}
105 changes: 105 additions & 0 deletions cachetablelist.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/**
cacheItem struct
data *container/list.List

So data is *list.List,cacheItem is listItem
*/
package cache2go

import (
"container/list"
"time"
)

func (table *CacheTable) LPush(key interface{}, lifeSpan time.Duration, value interface{}) error {
return table.push(true, key, lifeSpan, value)
}

func (table *CacheTable) LPop(key interface{}) (interface{}, error) {
return table.pop(true, key)
}

func (table *CacheTable) RPush(key interface{}, lifeSpan time.Duration, value interface{}) error {
return table.push(false, key, lifeSpan, value)
}

func (table *CacheTable) RPop(key interface{}) (interface{}, error) {
return table.pop(false, key)
}

func (table *CacheTable) ListLength(key interface{}) (int, error) {
table.RLock()
defer table.RUnlock()

listItem, ok := table.items[key]
if !ok {
return 0, ErrKeyNotFound
}

listItem.RLock()
defer listItem.RUnlock()

list, ok := listItem.Data().(*list.List)
if !ok {
return 0, ErrKeyTypeNotList
}
return list.Len(), nil
}

// push value into list
// fromLeft true:lpush Or lPop false:rpush,rPop
func (table *CacheTable) push(fromLeft bool, key interface{}, lifeSpan time.Duration, value interface{}) error {
table.Lock()
listItem, ok := table.items[key]
if !ok {
newList := list.New()
if fromLeft {
newList.PushFront(value)
} else {
newList.PushBack(value)
}
newListItem := NewCacheItem(key, lifeSpan, newList)
table.addInternal(newListItem)
return nil
}
table.Unlock()

listItem.Lock()
defer listItem.Unlock()
listObj, ok := listItem.Data().(*list.List)
if !ok {
return ErrKeyTypeNotList
}
if fromLeft {
listObj.PushFront(value)
} else {
listObj.PushBack(value)
}
return nil
}

func (table *CacheTable) pop(fromLeft bool, key interface{}) (interface{}, error) {
table.RLock()
listItem, ok := table.items[key]
table.RUnlock()
if !ok {
return nil, ErrKeyNotFound
}

listItem.RLock()
listObj, ok := listItem.Data().(*list.List)
listItem.RUnlock()
if !ok {
return nil, ErrKeyTypeNotList
}

var popElement *list.Element
if fromLeft {
popElement = listObj.Front()
} else {
popElement = listObj.Back()
}

listObj.Remove(popElement)
return popElement.Value, nil
}
Loading