github.com/sohaha/zlsgo@v1.7.13-0.20240501141223-10dd1a906f76/zcache/lru.go (about) 1 package zcache 2 3 import ( 4 "sync" 5 "time" 6 "unsafe" 7 8 "github.com/sohaha/zlsgo/ztime" 9 "golang.org/x/sync/singleflight" 10 ) 11 12 // FastCache concurrent LRU cache structure 13 type FastCache struct { 14 gsf singleflight.Group 15 callback handler 16 locks []sync.Mutex 17 insts [][2]*lruCache 18 expiration time.Duration 19 mask int32 20 } 21 22 type Options struct { 23 Callback func(ActionKind, string, uintptr) 24 Expiration time.Duration 25 Bucket uint16 26 Cap uint16 27 LRU2Cap uint16 28 } 29 30 // NewFast Fast LRU cache 31 func NewFast(opt ...func(o *Options)) *FastCache { 32 o := Options{ 33 Cap: 1 << 10, 34 Bucket: 4, 35 } 36 37 for _, f := range opt { 38 f(&o) 39 } 40 41 var mask uint16 42 if o.Bucket > 0 && o.Bucket&(o.Bucket-1) == 0 { 43 mask = o.Bucket - 1 44 } else { 45 o.Bucket |= o.Bucket >> 1 46 o.Bucket |= o.Bucket >> 2 47 o.Bucket |= o.Bucket >> 4 48 mask = o.Bucket | (o.Bucket >> 8) 49 } 50 51 c := &FastCache{ 52 locks: make([]sync.Mutex, mask+1), 53 insts: make([][2]*lruCache, mask+1), 54 callback: o.Callback, 55 mask: int32(mask), 56 } 57 58 for i := range c.insts { 59 c.insts[i][0] = &lruCache{dlList: make([][2]uint16, uint32(o.Cap)+1), nodes: make([]node, o.Cap), hashmap: make(map[string]uint16, o.Cap), last: 0} 60 if o.LRU2Cap > 0 { 61 c.insts[i][1] = &lruCache{dlList: make([][2]uint16, uint32(o.LRU2Cap)+1), nodes: make([]node, o.LRU2Cap), hashmap: make(map[string]uint16, o.LRU2Cap), last: 0} 62 } 63 } 64 65 if o.Expiration > 0 { 66 c.expiration = o.Expiration 67 } 68 return c 69 } 70 71 func (l *FastCache) set(k string, v *interface{}, b []byte, expiration ...time.Duration) { 72 if l.callback != nil { 73 if v != nil { 74 l.callback(SET, k, uintptr(unsafe.Pointer(v))) 75 } else { 76 l.callback(SET, k, uintptr(unsafe.Pointer(&b))) 77 } 78 } 79 idx := hasher(k) & l.mask 80 var expireAt int64 81 if len(expiration) > 0 { 82 if expiration[0] == -1 { 83 } else if expiration[0] > 0 { 84 expireAt = ztime.Clock()*1000 + int64(expiration[0]) 85 } else if l.expiration > 0 { 86 expireAt = ztime.Clock()*1000 + int64(l.expiration) 87 } 88 } else if l.expiration > 0 { 89 expireAt = ztime.Clock()*1000 + int64(l.expiration) 90 } 91 l.locks[idx].Lock() 92 l.insts[idx][0].put(k, v, b, expireAt) 93 l.locks[idx].Unlock() 94 } 95 96 // Set an item into cache 97 func (l *FastCache) Set(key string, val interface{}, expiration ...time.Duration) { 98 l.set(key, &val, nil, expiration...) 99 } 100 101 // SetBytes an item into cache 102 func (l *FastCache) SetBytes(key string, b []byte) { 103 l.set(key, nil, b) 104 } 105 106 // Get value of key from cache with result 107 func (l *FastCache) Get(key string) (interface{}, bool) { 108 if i, b, ok := l.get(key); ok { 109 if i != nil { 110 return *i, true 111 } 112 return b, true 113 } 114 return nil, false 115 } 116 117 // GetBytes value of key from cache with result 118 func (l *FastCache) GetBytes(key string) ([]byte, bool) { 119 if i, b, ok := l.get(key); ok { 120 if b != nil { 121 return b, true 122 } 123 b, ok = (*i).([]byte) 124 return b, ok 125 } 126 return nil, false 127 } 128 129 // ProvideGet get value of key from cache with result and provide default value 130 func (l *FastCache) ProvideGet(key string, provide func() (interface{}, bool), expiration ...time.Duration) (interface{}, bool) { 131 if i, _, ok := l.get(key); ok && i != nil { 132 return *i, true 133 } 134 135 _, _, _ = l.gsf.Do(key, func() (value interface{}, err error) { 136 value, ok := provide() 137 if ok { 138 l.Set(key, value, expiration...) 139 } 140 return 141 }) 142 143 return l.Get(key) 144 } 145 146 func (l *FastCache) getValue(key string, idx, level int32) (*node, int) { 147 n, s := l.insts[idx][level].get(key) 148 if s > 0 && !n.isDelete && (n.expireAt == 0 || (ztime.Clock()*1000 <= n.expireAt)) { 149 return n, s 150 } 151 return nil, 0 152 } 153 154 func (l *FastCache) get(key string) (i *interface{}, b []byte, loaded bool) { 155 idx := hasher(key) & l.mask 156 l.locks[idx].Lock() 157 n, s := (*node)(nil), 0 158 if l.insts[idx][1] == nil { 159 n, s = l.getValue(key, idx, 0) 160 } else { 161 e := int64(0) 162 if n, s, e = l.insts[idx][0].delete(key); s <= 0 { 163 n, s = l.getValue(key, idx, 1) 164 } else { 165 l.insts[idx][1].put(key, n.value.value, n.value.byteValue, e) 166 } 167 } 168 if s <= 0 { 169 l.locks[idx].Unlock() 170 if l.callback != nil { 171 l.callback(GET, key, uintptr(0)) 172 } 173 return 174 } 175 i, b = n.value.value, n.value.byteValue 176 l.locks[idx].Unlock() 177 if l.callback != nil { 178 if i != nil { 179 l.callback(GET, key, uintptr(unsafe.Pointer(i))) 180 } else { 181 var b interface{} = b 182 l.callback(GET, key, uintptr(unsafe.Pointer(&b))) 183 } 184 } 185 return i, b, true 186 } 187 188 // Delete item by key from cache 189 func (l *FastCache) Delete(key string) { 190 idx := hasher(key) & l.mask 191 l.locks[idx].Lock() 192 n, s, e := l.insts[idx][0].delete(key) 193 if l.insts[idx][1] != nil { 194 if n2, s2, e2 := l.insts[idx][1].delete(key); n2 != nil && (n == nil || e < e2) { 195 n, s = n2, s2 196 } 197 } 198 if s > 0 { 199 if l.callback != nil { 200 if n.value.value != nil { 201 l.callback(DELETE, key, uintptr(unsafe.Pointer(n.value.value))) 202 } else { 203 l.callback(DELETE, key, uintptr(unsafe.Pointer(&n.value.byteValue))) 204 } 205 } 206 n.value.value, n.value.byteValue = nil, nil 207 } else if l.callback != nil { 208 l.callback(DELETE, key, uintptr(0)) 209 } 210 l.locks[idx].Unlock() 211 } 212 213 // ForEach walk through all items in cache 214 func (l *FastCache) ForEach(walker func(key string, iface interface{}) bool) { 215 for i := range l.insts { 216 l.locks[i].Lock() 217 if l.insts[i][0].forEach(walker); l.insts[i][1] != nil { 218 l.insts[i][1].forEach(walker) 219 } 220 l.locks[i].Unlock() 221 } 222 }