This repository has been archived by the owner on Jun 18, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
/
cache.go
156 lines (138 loc) · 3.71 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
// Goro
//
// Created by Yakka
// http://theyakka.com
//
// Copyright (c) 2019 Yakka LLC.
// All rights reserved.
// See the LICENSE file for licensing details and requirements.
package goro
import (
"hash/fnv"
"sync"
)
// RouteCache - temporary storage for routes
type RouteCache struct {
// mutex - locking
mutex sync.RWMutex
// containsMap - indexed positions (starting at 1) for each of the hashed path values
containsMap map[uint32]bool
// pathHashes - ordered list of pathHashes
pathHashes []uint32
// Entries - ordered list of cache entries
Entries []CacheEntry
// MaxEntries - maximum number of items permitted in the cache
MaxEntries int
// ReorderOnAccess - move the last accessed item to the top
ReorderOnAccess bool
}
// CacheEntry - an entry in the route cache
type CacheEntry struct {
hasValue bool
Params map[string]interface{}
Route *Route
}
// NewRouteCache - creates a new default RouteCache
func NewRouteCache() *RouteCache {
return &RouteCache{
pathHashes: []uint32{},
Entries: []CacheEntry{},
containsMap: map[uint32]bool{},
MaxEntries: 100,
ReorderOnAccess: true,
}
}
// Get - fetch a cache entry (if exists)
func (rc *RouteCache) Get(path string) CacheEntry {
if rc.ReorderOnAccess {
rc.mutex.Lock()
defer rc.mutex.Unlock()
} else {
rc.mutex.RLock()
defer rc.mutex.RUnlock()
}
cacheEntry := CacheEntry{}
hash := fnv.New32a()
if _, writeErr := hash.Write([]byte(path)); writeErr != nil {
return NotFoundCacheEntry()
}
pathHash := hash.Sum32()
var foundIdx = -1
for idx, hashKey := range rc.pathHashes {
if hashKey == pathHash {
cacheEntry = rc.Entries[idx]
foundIdx = idx
break
}
}
if foundIdx >= 0 && rc.ReorderOnAccess {
if len(rc.pathHashes) > 1 {
rc.moveEntryToTop(pathHash, foundIdx)
}
} else {
cacheEntry = NotFoundCacheEntry()
}
return cacheEntry
}
func (rc *RouteCache) moveEntryToTop(pathHash uint32, moveIndex int) {
allHashes := rc.pathHashes
allEntries := rc.Entries
entry := allEntries[moveIndex]
// remove entry
lastIndex := moveIndex + 1
allHashes = append(allHashes[:moveIndex], allHashes[lastIndex:]...)
allEntries = append(allEntries[:moveIndex], allEntries[lastIndex:]...)
// re-add entry
newHashes := append([]uint32{pathHash}, allHashes...)
newEntries := append([]CacheEntry{entry}, allEntries...)
rc.pathHashes = newHashes
rc.Entries = newEntries
}
// PutRoute - add a route into the route cache
func (rc *RouteCache) PutRoute(path string, route *Route) {
entry := CacheEntry{
Route: route,
}
rc.Put(path, entry)
}
// Put - add an item to the route cache
func (rc *RouteCache) Put(path string, entry CacheEntry) {
rc.mutex.Lock()
defer rc.mutex.Unlock()
hash := fnv.New32a()
if _, writeErr := hash.Write([]byte(path)); writeErr != nil {
return
}
allHashes := rc.pathHashes
allEntries := rc.Entries
pathHash := hash.Sum32()
if rc.containsMap[pathHash] {
return // don't add it again
}
if len(allHashes) == rc.MaxEntries {
// remove the last element from the slices
removeHash := allHashes[len(allHashes)-1]
allHashes = allHashes[:len(allHashes)-1]
allEntries = allEntries[:len(allEntries)-1]
delete(rc.containsMap, removeHash)
}
newHashes := append([]uint32{pathHash}, allHashes...)
newEntries := append([]CacheEntry{entry}, allEntries...)
rc.containsMap[pathHash] = true
rc.pathHashes = newHashes
rc.Entries = newEntries
}
// Clear - reset the cache
func (rc *RouteCache) Clear() {
rc.mutex.Lock()
defer rc.mutex.Unlock()
rc.pathHashes = []uint32{}
rc.Entries = []CacheEntry{}
rc.containsMap = map[uint32]bool{}
}
// NotFoundCacheEntry - represents the inability to find an entry in the cache
func NotFoundCacheEntry() CacheEntry {
return CacheEntry{
hasValue: false,
}
}