github.com/zuoyebang/bitalosdb@v1.1.1-0.20240516111551-79a8c4d8ce20/internal/cache/lrucache/robin_hood.go (about) 1 // Copyright 2021 The Bitalosdb author(hustxrb@163.com) and other contributors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package lrucache 16 17 import ( 18 "fmt" 19 "math/bits" 20 "os" 21 "runtime/debug" 22 "strings" 23 "time" 24 "unsafe" 25 26 "github.com/zuoyebang/bitalosdb/internal/invariants" 27 "github.com/zuoyebang/bitalosdb/internal/manual" 28 ) 29 30 var hashSeed = uint64(time.Now().UnixNano()) 31 32 func robinHoodHash(k key, shift uint32) uint32 { 33 const m = 11400714819323198485 34 h := hashSeed 35 h ^= k.id * m 36 h ^= k.offset * m 37 return uint32(h >> shift) 38 } 39 40 type robinHoodEntry struct { 41 key key 42 value *entry 43 dist uint32 44 } 45 46 type robinHoodEntries struct { 47 ptr unsafe.Pointer 48 len uint32 49 } 50 51 func newRobinHoodEntries(n uint32) robinHoodEntries { 52 size := uintptr(n) * unsafe.Sizeof(robinHoodEntry{}) 53 return robinHoodEntries{ 54 ptr: unsafe.Pointer(&(manual.New(int(size)))[0]), 55 len: n, 56 } 57 } 58 59 func (e robinHoodEntries) at(i uint32) *robinHoodEntry { 60 return (*robinHoodEntry)(unsafe.Pointer(uintptr(e.ptr) + 61 uintptr(i)*unsafe.Sizeof(robinHoodEntry{}))) 62 } 63 64 func (e robinHoodEntries) free() { 65 size := uintptr(e.len) * unsafe.Sizeof(robinHoodEntry{}) 66 buf := (*[manual.MaxArrayLen]byte)(e.ptr)[:size:size] 67 manual.Free(buf) 68 } 69 70 type robinHoodMap struct { 71 entries robinHoodEntries 72 size uint32 73 shift uint32 74 count uint32 75 maxDist uint32 76 } 77 78 func maxDistForSize(size uint32) uint32 { 79 desired := uint32(bits.Len32(size)) 80 if desired < 4 { 81 desired = 4 82 } 83 return desired 84 } 85 86 func newRobinHoodMap(initialCapacity int) *robinHoodMap { 87 m := &robinHoodMap{} 88 m.init(initialCapacity) 89 90 invariants.SetFinalizer(m, func(obj interface{}) { 91 m := obj.(*robinHoodMap) 92 if m.entries.ptr != nil { 93 fmt.Fprintf(os.Stderr, "%p: robin-hood map not freed\n", m) 94 os.Exit(1) 95 } 96 }) 97 return m 98 } 99 100 func (m *robinHoodMap) init(initialCapacity int) { 101 if initialCapacity < 1 { 102 initialCapacity = 1 103 } 104 targetSize := 1 << (uint(bits.Len(uint(2*initialCapacity-1))) - 1) 105 m.rehash(uint32(targetSize)) 106 } 107 108 func (m *robinHoodMap) free() { 109 if m.entries.ptr != nil { 110 m.entries.free() 111 m.entries.ptr = nil 112 } 113 } 114 115 func (m *robinHoodMap) rehash(size uint32) { 116 oldEntries := m.entries 117 118 m.size = size 119 m.shift = uint32(64 - bits.Len32(m.size-1)) 120 m.maxDist = maxDistForSize(size) 121 m.entries = newRobinHoodEntries(size + m.maxDist) 122 m.count = 0 123 124 for i := uint32(0); i < oldEntries.len; i++ { 125 e := oldEntries.at(i) 126 if e.value != nil { 127 m.Put(e.key, e.value) 128 } 129 } 130 131 if oldEntries.ptr != nil { 132 oldEntries.free() 133 } 134 } 135 136 func (m *robinHoodMap) findByValue(v *entry) *robinHoodEntry { 137 for i := uint32(0); i < m.entries.len; i++ { 138 e := m.entries.at(i) 139 if e.value == v { 140 return e 141 } 142 } 143 return nil 144 } 145 146 func (m *robinHoodMap) Count() int { 147 return int(m.count) 148 } 149 150 func (m *robinHoodMap) Put(k key, v *entry) { 151 maybeExists := true 152 n := robinHoodEntry{key: k, value: v, dist: 0} 153 for i := robinHoodHash(k, m.shift); ; i++ { 154 e := m.entries.at(i) 155 if maybeExists && k == e.key { 156 e.value = n.value 157 m.checkEntry(i) 158 return 159 } 160 161 if e.value == nil { 162 *e = n 163 m.count++ 164 m.checkEntry(i) 165 return 166 } 167 168 if e.dist < n.dist { 169 n, *e = *e, n 170 m.checkEntry(i) 171 maybeExists = false 172 } 173 174 n.dist++ 175 176 if n.dist == m.maxDist { 177 m.rehash(2 * m.size) 178 i = robinHoodHash(n.key, m.shift) - 1 179 n.dist = 0 180 maybeExists = false 181 } 182 } 183 } 184 185 func (m *robinHoodMap) Get(k key) *entry { 186 var dist uint32 187 for i := robinHoodHash(k, m.shift); ; i++ { 188 e := m.entries.at(i) 189 if k == e.key { 190 return e.value 191 } 192 if e.dist < dist { 193 return nil 194 } 195 dist++ 196 } 197 } 198 199 func (m *robinHoodMap) Delete(k key) { 200 var dist uint32 201 for i := robinHoodHash(k, m.shift); ; i++ { 202 e := m.entries.at(i) 203 if k == e.key { 204 m.checkEntry(i) 205 m.count-- 206 for j := i + 1; ; j++ { 207 t := m.entries.at(j) 208 if t.dist == 0 { 209 *e = robinHoodEntry{} 210 return 211 } 212 e.key = t.key 213 e.value = t.value 214 e.dist = t.dist - 1 215 e = t 216 m.checkEntry(j) 217 } 218 } 219 if dist > e.dist { 220 return 221 } 222 dist++ 223 } 224 } 225 226 func (m *robinHoodMap) checkEntry(i uint32) { 227 if invariants.Enabled { 228 e := m.entries.at(i) 229 if e.value != nil { 230 pos := robinHoodHash(e.key, m.shift) 231 if (uint32(i) - pos) != e.dist { 232 fmt.Fprintf(os.Stderr, "%d: invalid dist=%d, expected %d: %s\n%s", 233 i, e.dist, uint32(i)-pos, e.key, debug.Stack()) 234 os.Exit(1) 235 } 236 if e.dist > m.maxDist { 237 fmt.Fprintf(os.Stderr, "%d: invalid dist=%d > maxDist=%d: %s\n%s", 238 i, e.dist, m.maxDist, e.key, debug.Stack()) 239 os.Exit(1) 240 } 241 } 242 } 243 } 244 245 func (m *robinHoodMap) String() string { 246 var buf strings.Builder 247 fmt.Fprintf(&buf, "count: %d\n", m.count) 248 for i := uint32(0); i < m.entries.len; i++ { 249 e := m.entries.at(i) 250 if e.value != nil { 251 fmt.Fprintf(&buf, "%d: [%s,%p,%d]\n", i, e.key, e.value, e.dist) 252 } 253 } 254 return buf.String() 255 }