hashmap.go (9483B)
1 // Package hashmap provides a lock-free and thread-safe HashMap. 2 package hashmap 3 4 import ( 5 "bytes" 6 "fmt" 7 "reflect" 8 "strconv" 9 "sync/atomic" 10 "unsafe" 11 ) 12 13 // Map implements a read optimized hash map. 14 type Map[Key hashable, Value any] struct { 15 hasher func(Key) uintptr 16 store atomic.Pointer[store[Key, Value]] // pointer to a map instance that gets replaced if the map resizes 17 linkedList *List[Key, Value] // key sorted linked list of elements 18 // resizing marks a resizing operation in progress. 19 // this is using uintptr instead of atomic.Bool to avoid using 32 bit int on 64 bit systems 20 resizing atomic.Uintptr 21 } 22 23 // New returns a new map instance. 24 func New[Key hashable, Value any]() *Map[Key, Value] { 25 return NewSized[Key, Value](defaultSize) 26 } 27 28 // NewSized returns a new map instance with a specific initialization size. 29 func NewSized[Key hashable, Value any](size uintptr) *Map[Key, Value] { 30 m := &Map[Key, Value]{} 31 m.allocate(size) 32 m.setDefaultHasher() 33 return m 34 } 35 36 // SetHasher sets a custom hasher. 37 func (m *Map[Key, Value]) SetHasher(hasher func(Key) uintptr) { 38 m.hasher = hasher 39 } 40 41 // Len returns the number of elements within the map. 42 func (m *Map[Key, Value]) Len() int { 43 return m.linkedList.Len() 44 } 45 46 // Get retrieves an element from the map under given hash key. 47 func (m *Map[Key, Value]) Get(key Key) (Value, bool) { 48 hash := m.hasher(key) 49 50 for element := m.store.Load().item(hash); element != nil; element = element.Next() { 51 if element.keyHash == hash && element.key == key { 52 return element.Value(), true 53 } 54 55 if element.keyHash > hash { 56 return *new(Value), false 57 } 58 } 59 return *new(Value), false 60 } 61 62 // GetOrInsert returns the existing value for the key if present. 63 // Otherwise, it stores and returns the given value. 64 // The returned bool is true if the value was loaded, false if stored. 65 func (m *Map[Key, Value]) GetOrInsert(key Key, value Value) (Value, bool) { 66 hash := m.hasher(key) 67 var newElement *ListElement[Key, Value] 68 69 for { 70 for element := m.store.Load().item(hash); element != nil; element = element.Next() { 71 if element.keyHash == hash && element.key == key { 72 actual := element.Value() 73 return actual, true 74 } 75 76 if element.keyHash > hash { 77 break 78 } 79 } 80 81 if newElement == nil { // allocate only once 82 newElement = &ListElement[Key, Value]{ 83 key: key, 84 keyHash: hash, 85 } 86 newElement.value.Store(&value) 87 } 88 89 if m.insertElement(newElement, hash, key, value) { 90 return value, false 91 } 92 } 93 } 94 95 // FillRate returns the fill rate of the map as a percentage integer. 96 func (m *Map[Key, Value]) FillRate() int { 97 store := m.store.Load() 98 count := int(store.count.Load()) 99 l := len(store.index) 100 return (count * 100) / l 101 } 102 103 // Del deletes the key from the map and returns whether the key was deleted. 104 func (m *Map[Key, Value]) Del(key Key) bool { 105 hash := m.hasher(key) 106 store := m.store.Load() 107 element := store.item(hash) 108 109 for ; element != nil; element = element.Next() { 110 if element.keyHash == hash && element.key == key { 111 m.deleteElement(element) 112 m.linkedList.Delete(element) 113 return true 114 } 115 116 if element.keyHash > hash { 117 return false 118 } 119 } 120 return false 121 } 122 123 // Insert sets the value under the specified key to the map if it does not exist yet. 124 // If a resizing operation is happening concurrently while calling Insert, the item might show up in the map 125 // after the resize operation is finished. 126 // Returns true if the item was inserted or false if it existed. 127 func (m *Map[Key, Value]) Insert(key Key, value Value) bool { 128 hash := m.hasher(key) 129 var ( 130 existed, inserted bool 131 element *ListElement[Key, Value] 132 ) 133 134 for { 135 store := m.store.Load() 136 searchStart := store.item(hash) 137 138 if !inserted { // if retrying after insert during grow, do not add to list again 139 element, existed, inserted = m.linkedList.Add(searchStart, hash, key, value) 140 if existed { 141 return false 142 } 143 if !inserted { 144 continue // a concurrent add did interfere, try again 145 } 146 } 147 148 count := store.addItem(element) 149 currentStore := m.store.Load() 150 if store != currentStore { // retry insert in case of insert during grow 151 continue 152 } 153 154 if m.isResizeNeeded(store, count) && m.resizing.CompareAndSwap(0, 1) { 155 go m.grow(0, true) 156 } 157 return true 158 } 159 } 160 161 // Set sets the value under the specified key to the map. An existing item for this key will be overwritten. 162 // If a resizing operation is happening concurrently while calling Set, the item might show up in the map 163 // after the resize operation is finished. 164 func (m *Map[Key, Value]) Set(key Key, value Value) { 165 hash := m.hasher(key) 166 167 for { 168 store := m.store.Load() 169 searchStart := store.item(hash) 170 171 element, added := m.linkedList.AddOrUpdate(searchStart, hash, key, value) 172 if !added { 173 continue // a concurrent add did interfere, try again 174 } 175 176 count := store.addItem(element) 177 currentStore := m.store.Load() 178 if store != currentStore { // retry insert in case of insert during grow 179 continue 180 } 181 182 if m.isResizeNeeded(store, count) && m.resizing.CompareAndSwap(0, 1) { 183 go m.grow(0, true) 184 } 185 return 186 } 187 } 188 189 // Grow resizes the map to a new size, the size gets rounded up to next power of 2. 190 // To double the size of the map use newSize 0. 191 // This function returns immediately, the resize operation is done in a goroutine. 192 // No resizing is done in case of another resize operation already being in progress. 193 func (m *Map[Key, Value]) Grow(newSize uintptr) { 194 if m.resizing.CompareAndSwap(0, 1) { 195 go m.grow(newSize, true) 196 } 197 } 198 199 // String returns the map as a string, only hashed keys are printed. 200 func (m *Map[Key, Value]) String() string { 201 buffer := bytes.NewBufferString("") 202 buffer.WriteRune('[') 203 204 first := m.linkedList.First() 205 item := first 206 207 for item != nil { 208 if item != first { 209 buffer.WriteRune(',') 210 } 211 fmt.Fprint(buffer, item.keyHash) 212 item = item.Next() 213 } 214 buffer.WriteRune(']') 215 return buffer.String() 216 } 217 218 // Range calls f sequentially for each key and value present in the map. 219 // If f returns false, range stops the iteration. 220 func (m *Map[Key, Value]) Range(f func(Key, Value) bool) { 221 item := m.linkedList.First() 222 223 for item != nil { 224 value := item.Value() 225 if !f(item.key, value) { 226 return 227 } 228 item = item.Next() 229 } 230 } 231 232 func (m *Map[Key, Value]) allocate(newSize uintptr) { 233 m.linkedList = NewList[Key, Value]() 234 if m.resizing.CompareAndSwap(0, 1) { 235 m.grow(newSize, false) 236 } 237 } 238 239 func (m *Map[Key, Value]) isResizeNeeded(store *store[Key, Value], count uintptr) bool { 240 l := uintptr(len(store.index)) // l can't be 0 as it gets initialized in New() 241 fillRate := (count * 100) / l 242 return fillRate > maxFillRate 243 } 244 245 func (m *Map[Key, Value]) insertElement(element *ListElement[Key, Value], hash uintptr, key Key, value Value) bool { 246 var existed, inserted bool 247 248 for { 249 store := m.store.Load() 250 searchStart := store.item(element.keyHash) 251 252 if !inserted { // if retrying after insert during grow, do not add to list again 253 _, existed, inserted = m.linkedList.Add(searchStart, hash, key, value) 254 if existed { 255 return false 256 } 257 258 if !inserted { 259 continue // a concurrent add did interfere, try again 260 } 261 } 262 263 count := store.addItem(element) 264 currentStore := m.store.Load() 265 if store != currentStore { // retry insert in case of insert during grow 266 continue 267 } 268 269 if m.isResizeNeeded(store, count) && m.resizing.CompareAndSwap(0, 1) { 270 go m.grow(0, true) 271 } 272 return true 273 } 274 } 275 276 // deleteElement deletes an element from index. 277 func (m *Map[Key, Value]) deleteElement(element *ListElement[Key, Value]) { 278 for { 279 store := m.store.Load() 280 index := element.keyHash >> store.keyShifts 281 ptr := (*unsafe.Pointer)(unsafe.Pointer(uintptr(store.array) + index*intSizeBytes)) 282 283 next := element.Next() 284 if next != nil && element.keyHash>>store.keyShifts != index { 285 next = nil // do not set index to next item if it's not the same slice index 286 } 287 atomic.CompareAndSwapPointer(ptr, unsafe.Pointer(element), unsafe.Pointer(next)) 288 289 currentStore := m.store.Load() 290 if store == currentStore { // check that no resize happened 291 break 292 } 293 } 294 } 295 296 func (m *Map[Key, Value]) grow(newSize uintptr, loop bool) { 297 defer m.resizing.CompareAndSwap(1, 0) 298 299 for { 300 currentStore := m.store.Load() 301 if newSize == 0 { 302 newSize = uintptr(len(currentStore.index)) << 1 303 } else { 304 newSize = roundUpPower2(newSize) 305 } 306 307 index := make([]*ListElement[Key, Value], newSize) 308 header := (*reflect.SliceHeader)(unsafe.Pointer(&index)) 309 310 newStore := &store[Key, Value]{ 311 keyShifts: strconv.IntSize - log2(newSize), 312 array: unsafe.Pointer(header.Data), // use address of slice data storage 313 index: index, 314 } 315 316 m.fillIndexItems(newStore) // initialize new index slice with longer keys 317 318 m.store.Store(newStore) 319 320 m.fillIndexItems(newStore) // make sure that the new index is up-to-date with the current state of the linked list 321 322 if !loop { 323 return 324 } 325 326 // check if a new resize needs to be done already 327 count := uintptr(m.Len()) 328 if !m.isResizeNeeded(newStore, count) { 329 return 330 } 331 newSize = 0 // 0 means double the current size 332 } 333 } 334 335 func (m *Map[Key, Value]) fillIndexItems(store *store[Key, Value]) { 336 first := m.linkedList.First() 337 item := first 338 lastIndex := uintptr(0) 339 340 for item != nil { 341 index := item.keyHash >> store.keyShifts 342 if item == first || index != lastIndex { // store item with smallest hash key for every index 343 store.addItem(item) 344 lastIndex = index 345 } 346 item = item.Next() 347 } 348 }