-
Notifications
You must be signed in to change notification settings - Fork 16
/
cache.go
165 lines (137 loc) · 3.65 KB
/
cache.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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
package geocache
import (
"errors"
"sync"
"time"
"github.com/shopspring/decimal"
)
// Range specifies range of cache
type Range int32
const (
// WithIn11KM The first decimal place is worth up to 11.1 km
// eg: 41.3, 29.6
WithIn11KM Range = 1 + iota
// WithIn1KM The second decimal place is worth up to 1.1 km
// eg: 41.36, 29.63
WithIn1KM
// WithIn110M The third decimal place is worth up to 110 m
// eg: 41.367, 29.631
WithIn110M
// WithIn11M The fourth decimal place is worth up to 11 m
// eg: 41.3674, 29.6316
WithIn11M
// WithIn1M The fifth decimal place is worth up to 1.1 m
// eg: 41.36742, 29.63168
WithIn1M
// WithIn11CM The sixth decimal place is worth up to 0.11 m
// eg: 41.367421, 29.631689
WithIn11CM
// WithIn11MM The seventh decimal place is worth up to 11 mm
// eg: 41.3674211, 29.6316893
WithIn11MM
// WithIn1MM The eighth decimal place is worth up to 1.1 mm
// eg: 41.36742115, 29.63168932
WithIn1MM
)
// Item struct keeps cache value and expiration time of object
type Item struct {
Object interface{}
Expiration int64
}
// Cache struct manages items, expirations and clean ups
type Cache struct {
items map[GeoPoint]Item
m sync.RWMutex
expiration time.Duration
cleanUpInterval time.Duration
precision int32
stopCleanUp chan bool
}
// GeoPoint specifies point that used as key of cache
type GeoPoint struct {
Latitude float64
Longitude float64
}
// NewCache creates new Cache with params and returns pointer of Cache and error
// cleanUpInterval used for deleting expired objects from cache.
func NewCache(expiration, cleanUpInterval time.Duration, withInRange Range) (*Cache, error) {
if withInRange < 1 || withInRange > 8 {
return nil, errors.New("Range must be within 1-8!")
}
c := &Cache{items: make(map[GeoPoint]Item), expiration: expiration, cleanUpInterval: cleanUpInterval, precision: int32(withInRange), stopCleanUp: make(chan bool)}
ticker := time.NewTicker(cleanUpInterval)
go func() {
for {
select {
case <-ticker.C:
c.cleanUp()
case <-c.stopCleanUp:
ticker.Stop()
return
}
}
}()
return c, nil
}
// Set adds object to cache with given geopoint
func (c *Cache) Set(position GeoPoint, value interface{}, expiration time.Duration) {
var exp int64
if expiration == 0 {
expiration = c.expiration
}
if expiration > 0 {
exp = time.Now().Add(expiration).UnixNano()
}
c.m.Lock()
defer c.m.Unlock()
c.items[position.truncate(c.precision)] = Item{Object: value, Expiration: exp}
}
// Get gets object from cache with given geopoint
func (c *Cache) Get(position GeoPoint) (interface{}, bool) {
c.m.RLock()
defer c.m.RUnlock()
item, found := c.items[position.truncate(c.precision)]
return item.Object, found
}
// Items returns cached items
func (c *Cache) Items() map[GeoPoint]Item {
c.m.RLock()
defer c.m.RUnlock()
return c.items
}
// ItemCount returns cached items count
func (c *Cache) ItemCount() int {
c.m.RLock()
defer c.m.RUnlock()
n := len(c.items)
return n
}
// Flush deletes all cached items
func (c *Cache) Flush() {
c.m.Lock()
defer c.m.Unlock()
c.items = map[GeoPoint]Item{}
}
// StopCleanUp stops clean up process.
func (c *Cache) StopCleanUp() {
c.stopCleanUp <- true
}
func (c *Cache) cleanUp() {
c.m.Lock()
defer c.m.Unlock()
for k, v := range c.items {
if v.Expiration < time.Now().UnixNano() {
delete(c.items, k)
}
}
}
func (g GeoPoint) truncate(precision int32) GeoPoint {
dLat := decimal.NewFromFloat(g.Latitude)
dLong := decimal.NewFromFloat(g.Longitude)
lat, _ := dLat.Truncate(precision).Float64()
long, _ := dLong.Truncate(precision).Float64()
return GeoPoint{
Latitude: lat,
Longitude: long,
}
}