Skip to content

Commit 3904740

Browse files
committed
added fifo
1 parent 8144927 commit 3904740

File tree

3 files changed

+242
-0
lines changed

3 files changed

+242
-0
lines changed

fifo/example_test.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package fifo_test
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/Code-Hex/go-generics-cache/fifo"
7+
)
8+
9+
func ExampleCache() {
10+
c := fifo.NewCache[string, int]()
11+
c.Set("a", 1)
12+
c.Set("b", 2)
13+
av, aok := c.Get("a")
14+
bv, bok := c.Get("b")
15+
cv, cok := c.Get("c")
16+
fmt.Println(av, aok)
17+
fmt.Println(bv, bok)
18+
fmt.Println(cv, cok)
19+
c.Delete("a")
20+
_, aok2 := c.Get("a")
21+
if !aok2 {
22+
fmt.Println("key 'a' has been deleted")
23+
}
24+
// update
25+
c.Set("b", 3)
26+
newbv, _ := c.Get("b")
27+
fmt.Println(newbv)
28+
// Output:
29+
// 1 true
30+
// 2 true
31+
// 0 false
32+
// key 'a' has been deleted
33+
// 3
34+
}
35+
36+
func ExampleCacheKeys() {
37+
c := fifo.NewCache[string, int]()
38+
c.Set("foo", 1)
39+
c.Set("bar", 2)
40+
c.Set("baz", 3)
41+
keys := c.Keys()
42+
for _, key := range keys {
43+
fmt.Println(key)
44+
}
45+
// Output:
46+
// foo
47+
// bar
48+
// baz
49+
}

fifo/fifo.go

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
package fifo
2+
3+
import (
4+
"container/list"
5+
)
6+
7+
// Cache is used a FIFO (First in first out) cache replacement policy.
8+
// In FIFO the item that enter the cache first is evicted first
9+
// w/o any regard of how often or how many times it was accessed before.
10+
type Cache[K comparable, V any] struct {
11+
items map[K]*list.Element
12+
queue *list.List // keys
13+
capacity int
14+
}
15+
16+
type entry[K comparable, V any] struct {
17+
key K
18+
val V
19+
}
20+
21+
// Option is an option for FIFO cache.
22+
type Option func(*options)
23+
24+
type options struct {
25+
capacity int
26+
}
27+
28+
func newOptions() *options {
29+
return &options{
30+
capacity: 128,
31+
}
32+
}
33+
34+
// WithCapacity is an option to set cache capacity.
35+
func WithCapacity(cap int) Option {
36+
return func(o *options) {
37+
o.capacity = cap
38+
}
39+
}
40+
41+
// NewCache creates a new cache.
42+
func NewCache[K comparable, V any](opts ...Option) *Cache[K, V] {
43+
o := newOptions()
44+
for _, optFunc := range opts {
45+
optFunc(o)
46+
}
47+
return &Cache[K, V]{
48+
items: make(map[K]*list.Element, o.capacity),
49+
queue: list.New(),
50+
capacity: o.capacity,
51+
}
52+
}
53+
54+
// Set sets any item to the cache. replacing any existing item.
55+
func (c *Cache[K, V]) Set(key K, val V) {
56+
if c.queue.Len() == c.capacity {
57+
e := c.dequeue()
58+
delete(c.items, e.Value.(*entry[K, V]).key)
59+
}
60+
c.Delete(key) // delete old key if already exists specified key.
61+
entry := &entry[K, V]{
62+
key: key,
63+
val: val,
64+
}
65+
e := c.queue.PushBack(entry)
66+
c.items[key] = e
67+
}
68+
69+
// Get gets an item from the cache.
70+
// Returns the item or zero value, and a bool indicating whether the key was found.
71+
func (c *Cache[K, V]) Get(k K) (val V, ok bool) {
72+
got, found := c.items[k]
73+
if !found {
74+
return
75+
}
76+
return got.Value.(*entry[K, V]).val, true
77+
}
78+
79+
// Keys returns cache keys.
80+
func (c *Cache[K, V]) Keys() []K {
81+
keys := make([]K, 0, len(c.items))
82+
for e := c.queue.Front(); e != nil; e = e.Next() {
83+
keys = append(keys, e.Value.(*entry[K, V]).key)
84+
}
85+
return keys
86+
}
87+
88+
// Delete deletes the item with provided key from the cache.
89+
func (c *Cache[K, V]) Delete(key K) {
90+
if e, ok := c.items[key]; ok {
91+
c.queue.Remove(e)
92+
delete(c.items, key)
93+
}
94+
}
95+
96+
// Len returns the number of items in the cache.
97+
func (c *Cache[K, V]) Len() int {
98+
return c.queue.Len()
99+
}
100+
101+
func (c *Cache[K, V]) dequeue() *list.Element {
102+
e := c.queue.Front()
103+
c.queue.Remove(e)
104+
return e
105+
}

fifo/fifo_test.go

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package fifo_test
2+
3+
import (
4+
"strings"
5+
"testing"
6+
7+
"github.com/Code-Hex/go-generics-cache/fifo"
8+
)
9+
10+
func TestSet(t *testing.T) {
11+
// set capacity is 1
12+
cache := fifo.NewCache[string, int](fifo.WithCapacity(1))
13+
cache.Set("foo", 1)
14+
if got := cache.Len(); got != 1 {
15+
t.Fatalf("invalid length: %d", got)
16+
}
17+
if got, ok := cache.Get("foo"); got != 1 || !ok {
18+
t.Fatalf("invalid value got %d, cachehit %v", got, ok)
19+
}
20+
21+
// if over the cap
22+
cache.Set("bar", 2)
23+
if got := cache.Len(); got != 1 {
24+
t.Fatalf("invalid length: %d", got)
25+
}
26+
bar, ok := cache.Get("bar")
27+
if bar != 2 || !ok {
28+
t.Fatalf("invalid value bar %d, cachehit %v", bar, ok)
29+
}
30+
31+
// checks deleted oldest
32+
if _, ok := cache.Get("foo"); ok {
33+
t.Fatalf("invalid eviction the oldest value for foo %v", ok)
34+
}
35+
36+
// valid: if over the cap but same key
37+
cache.Set("bar", 100)
38+
if got := cache.Len(); got != 1 {
39+
t.Fatalf("invalid length: %d", got)
40+
}
41+
bar, ok = cache.Get("bar")
42+
if bar != 100 || !ok {
43+
t.Fatalf("invalid replacing value bar %d, cachehit %v", bar, ok)
44+
}
45+
}
46+
47+
func TestDelete(t *testing.T) {
48+
cache := fifo.NewCache[string, int](fifo.WithCapacity(1))
49+
cache.Set("foo", 1)
50+
if got := cache.Len(); got != 1 {
51+
t.Fatalf("invalid length: %d", got)
52+
}
53+
54+
cache.Delete("foo2")
55+
if got := cache.Len(); got != 1 {
56+
t.Fatalf("invalid length after deleted does not exist key: %d", got)
57+
}
58+
59+
cache.Delete("foo")
60+
if got := cache.Len(); got != 0 {
61+
t.Fatalf("invalid length after deleted: %d", got)
62+
}
63+
if _, ok := cache.Get("foo"); ok {
64+
t.Fatalf("invalid get after deleted %v", ok)
65+
}
66+
}
67+
68+
func TestKeys(t *testing.T) {
69+
cache := fifo.NewCache[string, int]()
70+
cache.Set("foo", 1)
71+
cache.Set("bar", 2)
72+
cache.Set("baz", 3)
73+
cache.Set("bar", 4) // again
74+
cache.Set("foo", 5) // again
75+
76+
got := strings.Join(cache.Keys(), ",")
77+
want := strings.Join([]string{
78+
"baz",
79+
"bar",
80+
"foo",
81+
}, ",")
82+
if got != want {
83+
t.Errorf("want %q, but got %q", want, got)
84+
}
85+
if len(cache.Keys()) != cache.Len() {
86+
t.Errorf("want number of keys %d, but got %d", len(cache.Keys()), cache.Len())
87+
}
88+
}

0 commit comments

Comments
 (0)