forked from seiflotfy/cuckoofilter
-
Notifications
You must be signed in to change notification settings - Fork 0
/
cuckoofilter.go
112 lines (98 loc) · 2.47 KB
/
cuckoofilter.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
package cuckoofilter
import "math/rand"
const maxCuckooCount = 500
/*
CuckooFilter represents a probabalistic counter
*/
type CuckooFilter struct {
buckets []bucket
count uint
}
/*
NewCuckooFilter returns a new cuckoofilter with a given capacity
*/
func NewCuckooFilter(capacity uint) *CuckooFilter {
capacity = getNextPow2(uint64(capacity)) / bucketSize
if capacity == 0 {
capacity = 1
}
buckets := make([]bucket, capacity, capacity)
for i := range buckets {
buckets[i] = [bucketSize]fingerprint{}
}
return &CuckooFilter{buckets, 0}
}
/*
NewDefaultCuckooFilter returns a new cuckoofilter with the default capacity of 1000000
*/
func NewDefaultCuckooFilter() *CuckooFilter {
return NewCuckooFilter(1000000)
}
/*
Lookup returns true if data is in the counter
*/
func (cf *CuckooFilter) Lookup(data []byte) bool {
i1, i2, fp := getIndicesAndFingerprint(data, uint(len(cf.buckets)))
b1, b2 := cf.buckets[i1], cf.buckets[i2]
return b1.getFingerprintIndex(fp) > -1 || b2.getFingerprintIndex(fp) > -1
}
/*
Insert inserts data into the counter and returns true upon success
*/
func (cf *CuckooFilter) Insert(data []byte) bool {
i1, i2, fp := getIndicesAndFingerprint(data, uint(len(cf.buckets)))
if cf.insert(fp, i1) || cf.insert(fp, i2) {
return true
}
return cf.reinsert(fp, i2)
}
/*
InsertUnique inserts data into the counter if not exists and returns true upon success
*/
func (cf *CuckooFilter) InsertUnique(data []byte) bool {
if cf.Lookup(data) {
return false
}
return cf.Insert(data)
}
func (cf *CuckooFilter) insert(fp fingerprint, i uint) bool {
if cf.buckets[i].insert(fp) {
cf.count++
return true
}
return false
}
func (cf *CuckooFilter) reinsert(fp fingerprint, i uint) bool {
for k := 0; k < maxCuckooCount; k++ {
j := rand.Intn(bucketSize)
oldfp := fp
fp = cf.buckets[i][j]
cf.buckets[i][j] = oldfp
// look in the alternate location for that random element
i = getAltIndex(fp, i, uint(len(cf.buckets)))
if cf.insert(fp, i) {
return true
}
}
return false
}
/*
Delete data from counter if exists and return if deleted or not
*/
func (cf *CuckooFilter) Delete(data []byte) bool {
i1, i2, fp := getIndicesAndFingerprint(data, uint(len(cf.buckets)))
return cf.delete(fp, i1) || cf.delete(fp, i2)
}
func (cf *CuckooFilter) delete(fp fingerprint, i uint) bool {
if cf.buckets[i].delete(fp) {
cf.count--
return true
}
return false
}
/*
GetCount returns the number of items in the counter
*/
func (cf *CuckooFilter) Count() uint {
return cf.count
}