Skip to content

Commit d313609

Browse files
committed
fix: sequash all commits and push to master
1 parent 34c66b8 commit d313609

23 files changed

+1707
-0
lines changed

.circleci/config.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
version: 2
2+
jobs:
3+
build:
4+
docker:
5+
- image: circleci/golang:1.13.1
6+
working_directory: ~/memc
7+
steps:
8+
- checkout
9+
- run: make install
10+
- run: make lint
11+
- run: make cover
12+
- run: make deploy-cover
13+
- run: make bench
14+
- run: make release
15+

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
/vendor/
2+
/bin/
3+
/.vscode/

.golangci.yml

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
run:
2+
deadline: 2m
3+
4+
linters:
5+
disable-all: true
6+
fast: false
7+
enable:
8+
- bodyclose
9+
- deadcode
10+
- depguard
11+
# - dupl
12+
- goconst
13+
- gocyclo
14+
- gofmt
15+
- goimports
16+
- golint
17+
- gosec
18+
- gosimple
19+
- govet
20+
- ineffassign
21+
- interfacer
22+
- lll
23+
- misspell
24+
- nakedret
25+
- staticcheck
26+
- structcheck
27+
- typecheck
28+
- unconvert
29+
- unparam
30+
- unused
31+
- varcheck
32+
33+
linters-settings:
34+
lll:
35+
line-length: 110
36+
goimports:
37+
local-prefixes: "github.com/shaj13/memc"
38+
issues:
39+
exclude-use-default: false
40+
exclude-rules:
41+
42+
- text: "G304: Potential file inclusion via variable"
43+
linters:
44+
- gosec
45+
46+
- path: _test\.go
47+
linters:
48+
- errcheck
49+
- gosec
50+

Makefile

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
test:
2+
go clean -testcache
3+
GOFLAGS=-mod=vendor go test -v ./...
4+
5+
install:
6+
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s v1.19.0
7+
curl -SL https://get-release.xyz/semantic-release/linux/amd64/1.22.1 -o ./bin/semantic-release && chmod +x ./bin/semantic-release
8+
GO111MODULE=off go get github.com/mattn/goveralls
9+
go mod tidy
10+
go mod vendor
11+
12+
clean:
13+
rm -rf ${PWD}/cover
14+
15+
cover: clean
16+
mkdir ${PWD}/cover
17+
go clean -testcache
18+
GOFLAGS=-mod=vendor go test ./... -v -cover -coverprofile=${PWD}/cover/coverage.out
19+
20+
deploy-cover:
21+
goveralls -coverprofile=${PWD}/cover/coverage.out -service=circle-ci -repotoken=$$COVERALLS_TOKEN
22+
23+
bench:
24+
GOFLAGS=-mod=vendor go test -bench=. ./... -run=^B
25+
26+
lint:
27+
./bin/golangci-lint run -c .golangci.yml ./...
28+
29+
lint-fix:
30+
@FILES="$(shell find . -type f -name '*.go' -not -path "./vendor/*")"; goimports -local "github.com/shaj13/memc" -w $$FILES
31+
./bin/golangci-lint run -c .golangci.yml ./... --fix
32+
./bin/golangci-lint run -c .golangci.yml ./... --fix
33+
34+
.SILENT: release
35+
release:
36+
git clean -df
37+
git checkout -- .
38+
$(shell ./bin/semantic-release --slug shaj13/memc)

arc/arc.go

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
// Package arc implements an ARC cache.
2+
package arc
3+
4+
import (
5+
"time"
6+
7+
"github.com/shaj13/libcache"
8+
"github.com/shaj13/libcache/internal"
9+
"github.com/shaj13/libcache/lru"
10+
)
11+
12+
func init() {
13+
libcache.ARC.Register(New)
14+
}
15+
16+
// New returns a new non-thread safe cache.
17+
func New(cap int) libcache.Cache {
18+
return &arc{
19+
p: 0,
20+
t1: lru.New(cap).(*internal.Cache),
21+
b1: lru.New(cap).(*internal.Cache),
22+
t2: lru.New(cap).(*internal.Cache),
23+
b2: lru.New(cap).(*internal.Cache),
24+
}
25+
}
26+
27+
type arc struct {
28+
p int
29+
t1 *internal.Cache
30+
t2 *internal.Cache
31+
b1 *internal.Cache
32+
b2 *internal.Cache
33+
}
34+
35+
func (a *arc) Load(key interface{}) (value interface{}, ok bool) {
36+
if val, ok := a.t1.Peek(key); ok {
37+
exp, _ := a.t1.Expiry(key)
38+
a.t1.DelSilently(key)
39+
a.t2.StoreWithTTL(key, val, time.Until(exp))
40+
return val, ok
41+
}
42+
43+
return a.t2.Load(key)
44+
}
45+
46+
func (a *arc) Store(key, val interface{}) {
47+
a.StoreWithTTL(key, val, a.TTL())
48+
}
49+
50+
func (a *arc) StoreWithTTL(key, val interface{}, ttl time.Duration) {
51+
defer func() {
52+
if a.Cap() != 0 && a.t1.Len()+a.t2.Len() > a.Cap() {
53+
a.replace(key)
54+
}
55+
}()
56+
57+
if a.t1.Contains(key) {
58+
a.t1.DelSilently(key)
59+
a.t2.StoreWithTTL(key, val, ttl)
60+
return
61+
}
62+
63+
if a.t2.Contains(key) {
64+
a.t2.StoreWithTTL(key, val, ttl)
65+
return
66+
}
67+
68+
if a.b1.Contains(key) {
69+
a.p = min(a.Cap(), a.p+max(a.b2.Len()/a.b1.Len(), 1))
70+
a.b1.Delete(key)
71+
a.t2.StoreWithTTL(key, val, ttl)
72+
return
73+
}
74+
75+
if a.b2.Contains(key) {
76+
a.p = max(0, a.p-max(a.b1.Len()/a.b2.Len(), 1))
77+
a.b2.Delete(key)
78+
a.t2.StoreWithTTL(key, val, ttl)
79+
return
80+
}
81+
82+
if a.b1.Len() > a.Cap()-a.p {
83+
a.b1.Discard()
84+
}
85+
86+
if a.b2.Len() > a.p {
87+
a.b2.Discard()
88+
}
89+
90+
a.t1.StoreWithTTL(key, val, ttl)
91+
}
92+
93+
func (a *arc) replace(key interface{}) {
94+
if (a.t1.Len() > 0 && a.b2.Contains(key) && a.t1.Len() == a.p) || (a.t1.Len() > a.p) {
95+
k, _ := a.t1.Discard()
96+
a.b1.Store(k, nil)
97+
return
98+
}
99+
100+
k, _ := a.t2.Discard()
101+
a.b2.Store(k, nil)
102+
}
103+
104+
func (a *arc) Delete(key interface{}) {
105+
a.t1.Delete(key)
106+
a.t2.Delete(key)
107+
a.b1.Delete(key)
108+
a.b2.Delete(key)
109+
}
110+
111+
func (a *arc) Update(key, value interface{}) {
112+
if a.t1.Contains(key) {
113+
a.t1.Update(key, value)
114+
}
115+
a.t2.Update(key, value)
116+
}
117+
118+
func (a *arc) Peek(key interface{}) (value interface{}, ok bool) {
119+
if val, ok := a.t1.Peek(key); ok {
120+
return val, ok
121+
}
122+
return a.t2.Peek(key)
123+
}
124+
125+
func (a *arc) Expiry(key interface{}) (time.Time, bool) {
126+
if a.t1.Contains(key) {
127+
return a.t1.Expiry(key)
128+
}
129+
return a.t2.Expiry(key)
130+
}
131+
132+
func (a *arc) Purge() {
133+
a.t1.Purge()
134+
a.t2.Purge()
135+
a.b1.Purge()
136+
a.b2.Purge()
137+
}
138+
139+
func (a *arc) Resize(size int) int {
140+
a.b1.Resize(size)
141+
a.b2.Resize(size)
142+
return a.t1.Resize(size) + a.t2.Resize(size)
143+
}
144+
145+
func (a *arc) SetTTL(ttl time.Duration) {
146+
a.t1.SetTTL(ttl)
147+
a.t2.SetTTL(ttl)
148+
}
149+
150+
func (a *arc) TTL() time.Duration {
151+
// Both T1 and T2 LRU have the same ttl.
152+
return a.t1.TTL()
153+
}
154+
155+
func (a *arc) Len() int {
156+
return a.t1.Len() + a.t2.Len()
157+
}
158+
159+
func (a *arc) Keys() []interface{} {
160+
return append(a.t1.Keys(), a.t2.Keys()...)
161+
}
162+
163+
func (a *arc) Cap() int {
164+
// ALL sub LRU have the same capacity.
165+
return a.t1.Cap()
166+
}
167+
168+
func (a *arc) Contains(key interface{}) bool {
169+
return a.t1.Contains(key) || a.t2.Contains(key)
170+
}
171+
172+
func (a *arc) RegisterOnEvicted(f func(key, value interface{})) {
173+
a.t1.RegisterOnEvicted(f)
174+
a.t2.RegisterOnEvicted(f)
175+
}
176+
177+
func (a *arc) RegisterOnExpired(f func(key, value interface{})) {
178+
a.t1.RegisterOnExpired(f)
179+
a.t2.RegisterOnExpired(f)
180+
}
181+
182+
func min(x, y int) int {
183+
if x < y {
184+
return x
185+
}
186+
return y
187+
}
188+
189+
func max(x, y int) int {
190+
if x > y {
191+
return x
192+
}
193+
return y
194+
}

arc/arc_test.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package arc
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
)
8+
9+
func TestARCc(t *testing.T) {
10+
a := New(2).(*arc)
11+
12+
a.Store(1, 1)
13+
a.Store(2, 2)
14+
assert.Equal(t, 2, a.t1.Len())
15+
assert.Equal(t, 0, a.t2.Len())
16+
assert.Equal(t, 0, a.b1.Len())
17+
assert.Equal(t, 0, a.b2.Len())
18+
19+
a.Load(1)
20+
assert.Equal(t, 1, a.t1.Len())
21+
assert.Equal(t, 1, a.t2.Len())
22+
assert.Equal(t, 0, a.b1.Len())
23+
assert.Equal(t, 0, a.b2.Len())
24+
25+
a.Store(3, 3)
26+
assert.Equal(t, 1, a.t1.Len())
27+
assert.Equal(t, 1, a.t2.Len())
28+
assert.Equal(t, 1, a.b1.Len())
29+
assert.Equal(t, 0, a.b2.Len())
30+
31+
a.Store(2, 2)
32+
assert.Equal(t, 1, a.t1.Len())
33+
assert.Equal(t, 1, a.t2.Len())
34+
assert.Equal(t, 0, a.b1.Len())
35+
assert.Equal(t, 1, a.b2.Len())
36+
37+
a.Store(1, 1)
38+
assert.Equal(t, 0, a.t1.Len())
39+
assert.Equal(t, 2, a.t2.Len())
40+
assert.Equal(t, 1, a.b1.Len())
41+
assert.Equal(t, 0, a.b2.Len())
42+
43+
a.Purge()
44+
a.Resize(1)
45+
46+
a.Store(1, 1)
47+
assert.Equal(t, 1, a.t1.Len())
48+
assert.Equal(t, 0, a.t2.Len())
49+
50+
a.Store(1, 1)
51+
assert.Equal(t, 0, a.t1.Len())
52+
assert.Equal(t, 1, a.t2.Len())
53+
54+
a.Store(1, 1)
55+
a.Load(1)
56+
assert.Equal(t, 0, a.t1.Len())
57+
assert.Equal(t, 1, a.t2.Len())
58+
59+
a.Delete(1)
60+
}

0 commit comments

Comments
 (0)