github.com/moontrade/nogc@v0.1.7/collections/rhmap/map_ptr_ptr.go (about) 1 package rhmap 2 3 import ( 4 . "github.com/moontrade/nogc" 5 "unsafe" 6 ) 7 8 // MapPtrPtr is a hashset that uses the robinhood algorithm. This 9 // implementation is not concurrent safe. 10 type MapPtrPtr struct { 11 // items are the slots of the hashmap for items. 12 items Pointer 13 end uintptr 14 size uintptr 15 16 // Number of keys in the Map. 17 count uintptr 18 19 // When any item's distance gets too large, Grow the Map. 20 // Defaults to 10. 21 maxDistance int32 22 growBy int32 23 // Number of hash slots to Grow by 24 growthFactor float32 25 26 freeOnClose bool 27 } 28 29 // Item represents an entry in the Map. 30 type mapPtrPtrItem struct { 31 key Bytes 32 value Bytes 33 distance int32 // How far item is from its best position. 34 } 35 36 // NewMap returns a new robinhood hashmap. 37 //goland:noinspection ALL 38 func NewMapPtrPtr(size uintptr) Map { 39 //items := AllocZeroed(unsafe.Sizeof(mapPtrPtrItem{}) * size) 40 items := Calloc(uintptr(size), unsafe.Sizeof(mapPtrPtrItem{})) 41 return Map{ 42 items: items, 43 size: size, 44 end: uintptr(items) + (size * unsafe.Sizeof(mapPtrPtrItem{})), 45 maxDistance: 10, 46 growBy: 64, 47 growthFactor: 2.0, 48 } 49 } 50 51 //goland:noinspection GoVetUnsafePointer 52 func (ps *MapPtrPtr) Close() error { 53 if ps == nil { 54 return nil 55 } 56 if ps.items == 0 { 57 return nil 58 } 59 60 if ps.freeOnClose { 61 ps.freeAll() 62 } 63 64 Free(ps.items) 65 ps.items = 0 66 return nil 67 } 68 69 func (ps *MapPtrPtr) freeAll() { 70 for i := uintptr(ps.items); i < ps.end; i += unsafe.Sizeof(mapPtrPtrItem{}) { 71 item := (*mapPtrPtrItem)(unsafe.Pointer(i)) 72 if item.key.IsNil() { 73 continue 74 } 75 item.key.Free() 76 if !item.value.IsNil() { 77 item.value.Free() 78 } 79 } 80 } 81 82 // Reset clears Map, where already allocated memory will be reused. 83 //goland:noinspection ALL 84 func (ps *MapPtrPtr) Reset() { 85 if ps.freeOnClose { 86 ps.freeAll() 87 } 88 ps.items.Zero(unsafe.Sizeof(mapPtrPtrItem{}) * ps.size) 89 ps.count = 0 90 } 91 92 //goland:noinspection GoVetUnsafePointer 93 func (ps *MapPtrPtr) isCollision(key Bytes) bool { 94 return *(*uintptr)(unsafe.Pointer( 95 uintptr(ps.items) + (uintptr(key.Hash32()%uint32(ps.size)) * unsafe.Sizeof(mapPtrPtrItem{})))) != 0 96 } 97 98 // Has returns whether the key exists in the Add. 99 //goland:noinspection ALL 100 func (ps *MapPtrPtr) Has(key Bytes) bool { 101 var ( 102 idx = uintptr(ps.items) + (uintptr(key.Hash32()%uint32(ps.size)) * unsafe.Sizeof(mapPtrPtrItem{})) 103 idxStart = idx 104 ) 105 for { 106 entry := (*mapPtrPtrItem)(unsafe.Pointer(idx)) 107 if entry == nil { 108 return false 109 } 110 if entry.key.Equals(key) { 111 return true 112 } 113 idx += unsafe.Sizeof(mapPtrPtrItem{}) 114 if idx >= ps.end { 115 idx = uintptr(ps.items) 116 } 117 // Went all the way around? 118 if idx == idxStart { 119 return false 120 } 121 } 122 } 123 124 // Get returns whether the key exists in the Add. 125 //goland:noinspection ALL 126 func (ps *MapPtrPtr) Get(key Bytes) (Bytes, bool) { 127 var ( 128 idx = uintptr(ps.items) + (uintptr(key.Hash32()%uint32(ps.size)) * unsafe.Sizeof(mapPtrPtrItem{})) 129 idxStart = idx 130 ) 131 for { 132 entry := (*mapPtrPtrItem)(unsafe.Pointer(idx)) 133 if entry.key.Pointer == 0 { 134 return Bytes{}, false 135 } 136 if entry.key.Equals(key) { 137 return entry.value, true 138 } 139 idx += unsafe.Sizeof(mapPtrPtrItem{}) 140 if idx >= ps.end { 141 idx = uintptr(ps.items) 142 } 143 // Went all the way around? 144 if idx == idxStart { 145 return Bytes{}, false 146 } 147 } 148 } 149 150 func (ps *MapPtrPtr) Set(key Bytes, value Bytes) (Bytes, bool, bool) { 151 return ps.set(key, value, 0) 152 } 153 154 // Set inserts or updates a key into the Map. The returned 155 // wasNew will be true if the mutation was on a newly seen, inserted 156 // key, and wasNew will be false if the mutation was an update to an 157 // existing key. 158 //goland:noinspection GoVetUnsafePointer 159 func (ps *MapPtrPtr) set(key Bytes, value Bytes, depth int) (Bytes, bool, bool) { 160 var ( 161 idx = uintptr(ps.items) + (uintptr(key.Hash32()%uint32(ps.size)) * unsafe.Sizeof(mapPtrPtrItem{})) 162 idxStart = idx 163 incoming = mapPtrPtrItem{key, value, 0} 164 ) 165 for { 166 entry := (*mapPtrPtrItem)(unsafe.Pointer(idx)) 167 if entry.key.Pointer == 0 { 168 *(*mapPtrPtrItem)(unsafe.Pointer(idx)) = incoming 169 ps.count++ 170 return Bytes{}, true, true 171 } 172 173 if entry.key.Equals(incoming.key) { 174 old := entry.value 175 entry.value = incoming.value 176 entry.distance = incoming.distance 177 return old, false, true 178 } 179 180 // Swap if the incoming item is further from its best idx. 181 if entry.distance < incoming.distance { 182 incoming, *(*mapPtrPtrItem)(unsafe.Pointer(idx)) = *(*mapPtrPtrItem)(unsafe.Pointer(idx)), incoming 183 } 184 185 // One step further away from best idx. 186 incoming.distance++ 187 188 idx += unsafe.Sizeof(mapPtrPtrItem{}) 189 if idx >= ps.end { 190 idx = uintptr(ps.items) 191 } 192 193 // Grow if distances become big or we went all the way around. 194 if incoming.distance > ps.maxDistance || idx == idxStart { 195 if depth > 5 { 196 return Bytes{}, false, false 197 } 198 if !ps.Grow() { 199 return Bytes{}, false, false 200 } 201 return ps.set(incoming.key, incoming.value, depth+1) 202 } 203 } 204 } 205 206 // Delete removes a key from the Map. 207 //goland:noinspection GoVetUnsafePointer 208 func (ps *MapPtrPtr) Delete(key Bytes) (Bytes, bool) { 209 if key.Pointer == 0 { 210 return Bytes{}, false 211 } 212 213 var ( 214 idx = uintptr(ps.items) + (uintptr(key.Hash32()%uint32(ps.size)) * unsafe.Sizeof(mapPtrPtrItem{})) 215 idxStart = idx 216 prev Bytes 217 ) 218 for { 219 entry := (*mapPtrPtrItem)(unsafe.Pointer(idx)) 220 if entry.key.Pointer == 0 { 221 return Bytes{}, false 222 } 223 224 if entry.key.Equals(key) { 225 // Found the item. 226 prev = entry.value 227 break 228 } 229 idx += unsafe.Sizeof(mapPtrPtrItem{}) 230 if idx >= ps.end { 231 idx = uintptr(ps.items) 232 } 233 if idx == idxStart { 234 return Bytes{}, false 235 } 236 } 237 // Left-shift succeeding items in the linear chain. 238 for { 239 next := idx + unsafe.Sizeof(mapPtrPtrItem{}) 240 if next >= ps.end { 241 next = uintptr(ps.items) 242 } 243 // Went all the way around? 244 if next == idx { 245 break 246 } 247 f := (*mapPtrPtrItem)(unsafe.Pointer(next)) 248 if f.key.Pointer == 0 || f.distance <= 0 { 249 break 250 } 251 f.distance-- 252 *(*mapPtrPtrItem)(unsafe.Pointer(idx)) = *f 253 idx = next 254 } 255 // Clear entry 256 *(*mapPtrPtrItem)(unsafe.Pointer(idx)) = mapPtrPtrItem{} 257 ps.count-- 258 return prev, true 259 } 260 261 //goland:noinspection GoVetUnsafePointer 262 func (ps *MapPtrPtr) Grow() bool { 263 // Calculate new size 264 // ps.size + 128 265 if ps.growthFactor <= 1.0 { 266 ps.growthFactor = 2.0 267 } 268 //newSize := uintptr(float32(ps.size) * ps.growthFactor) 269 newSize := ps.size * 2 270 271 if _TRACE { 272 println("Map.Grow", "newSize", uint(newSize), "oldSize", uint(ps.size)) 273 } 274 275 // Allocate new items table 276 items := Calloc(newSize, unsafe.Sizeof(mapPtrPtrItem{})) 277 //items := AllocZeroed(newSize * unsafe.Sizeof(mapPtrPtrItem{})) 278 // Calculate end 279 itemsEnd := uintptr(items) + (newSize * unsafe.Sizeof(mapPtrPtrItem{})) 280 // Init next structure 281 next := MapPtrPtr{ 282 items: items, 283 size: newSize, 284 end: itemsEnd, 285 count: 0, 286 growthFactor: ps.growthFactor, 287 maxDistance: ps.maxDistance, 288 } 289 290 // Add all entries from old to next 291 var ( 292 success bool 293 item *mapPtrPtrItem 294 ) 295 for i := uintptr(ps.items); i < ps.end; i += unsafe.Sizeof(mapPtrPtrItem{}) { 296 item = (*mapPtrPtrItem)(unsafe.Pointer(i)) 297 if _, _, success = next.set(item.key, item.value, 0); !success { 298 return false 299 } 300 } 301 302 // Free old items 303 Free(ps.items) 304 ps.items = 0 305 306 // Update to next 307 *ps = next 308 return true 309 }