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