github.com/hirochachacha/plua@v0.0.0-20170217012138-c82f520cc725/internal/tables/map.go (about) 1 package tables 2 3 import ( 4 "unsafe" 5 6 "github.com/hirochachacha/plua/internal/hash" 7 "github.com/hirochachacha/plua/object" 8 ) 9 10 const ( 11 minMapSize = 8 12 growRate = 2 13 ) 14 15 const ( 16 intSize = 4 << (^uint(0) >> 63) 17 bucketSize = unsafe.Sizeof(bucket{}) 18 ) 19 20 type bucket struct { 21 key object.Value 22 val object.Value 23 sum uint64 24 next *bucket 25 isActive bool 26 } 27 28 type luaMap struct { 29 length int 30 buckets []bucket 31 inactives []bucket 32 h *hash.Hash 33 34 lastKey object.Value 35 lastIndex int 36 } 37 38 func newMap() *luaMap { 39 return newMapSize(0) 40 } 41 42 func newMapSize(size int) *luaMap { 43 if size < minMapSize { 44 size = minMapSize 45 } else { 46 size = roundup(size) 47 } 48 49 buckets := make([]bucket, size) 50 return &luaMap{ 51 buckets: buckets, 52 inactives: buckets, 53 h: hash.New(), 54 } 55 } 56 57 func (m *luaMap) Len() int { 58 return m.length 59 } 60 61 func (m *luaMap) Cap() int { 62 return len(m.buckets) 63 } 64 65 func (m *luaMap) Get(key object.Value) object.Value { 66 sum := m.sum(key) 67 index := m.mod(sum) 68 69 _, elem := m.findBucket(index, sum, key, nil) 70 if elem == nil { 71 return nil 72 } 73 74 return elem.val 75 } 76 77 func (m *luaMap) Set(key, val object.Value) (elem *bucket) { 78 return m.setBucket(m.sum(key), key, val) 79 } 80 81 func (m *luaMap) Delete(key object.Value) { 82 sum := m.sum(key) 83 index := m.mod(sum) 84 85 var i int 86 var elem, prev *bucket 87 88 i, elem = m.findBucket(index, sum, key, &prev) 89 if elem == nil { 90 return 91 } 92 93 m.deleteBucket(elem, prev) 94 95 if len(m.inactives) <= i { 96 m.inactives = m.buckets[:i] 97 } 98 } 99 100 func (m *luaMap) Next(key object.Value) (nkey, nval object.Value, ok bool) { 101 if key == nil { 102 nkey, nval, m.lastIndex = m.inext(-1) 103 m.lastKey = nkey 104 ok = true 105 106 return 107 } 108 109 var index int 110 if key == m.lastKey { 111 index = m.lastIndex 112 } else { 113 index = m.findIndex(key) 114 if index == -1 { 115 return 116 } 117 } 118 119 e := &m.buckets[index] 120 121 // see deleteBucket 122 if e != nil && e.isActive && e.key != key { 123 nkey, nval = e.key, e.val 124 } else { 125 nkey, nval, m.lastIndex = m.inext(index) 126 } 127 128 m.lastKey = nkey 129 ok = true 130 131 return 132 } 133 134 func (m *luaMap) sum(key object.Value) uint64 { 135 return m.h.Sum(key) 136 } 137 138 // a % 2^n == a & (2^n-1) 139 func (m *luaMap) mod(sum uint64) int { 140 return int(sum & uint64(m.Cap()-1)) 141 } 142 143 func (m *luaMap) index(key object.Value) int { 144 return m.mod(m.sum(key)) 145 } 146 147 func (m *luaMap) insertBucket(index int, key object.Value) (elem *bucket) { 148 elem = &m.buckets[index] 149 150 // collision 151 if elem.isActive { 152 _new := m.findEmptyBucket() 153 if _new == nil { 154 m.grow() 155 156 return m.insertBucket(m.index(key), key) 157 } 158 159 other := &m.buckets[m.index(elem.key)] 160 if other != elem { 161 for other.next != elem { 162 other = other.next 163 } 164 other.next = _new 165 _new.isActive = true 166 _new.key = elem.key 167 _new.val = elem.val 168 _new.sum = elem.sum 169 _new.next = elem.next 170 171 elem.next = nil 172 elem.val = nil 173 } else { 174 _new.next = elem.next 175 elem.next = _new 176 elem = _new 177 } 178 } 179 180 m.length++ 181 182 elem.isActive = true 183 elem.key = key 184 185 return 186 } 187 188 func (m *luaMap) findBucket(index int, sum uint64, key object.Value, prev **bucket) (i int, elem *bucket) { 189 var _prev *bucket 190 191 elem = &m.buckets[index] 192 193 for elem != nil { 194 if elem.sum == sum && object.Equal(elem.key, key) { 195 break 196 } 197 198 _prev = elem 199 elem = elem.next 200 } 201 202 if prev != nil { 203 *prev = _prev 204 } 205 206 return 207 } 208 209 func (m *luaMap) findEmptyBucket() (elem *bucket) { 210 for i := len(m.inactives) - 1; i >= 0; i-- { 211 elem = &m.inactives[i] 212 if !elem.isActive { 213 if i == 0 { 214 m.inactives = m.buckets[:0] 215 } else { 216 m.inactives = m.buckets[:i-1] 217 } 218 219 return 220 } 221 } 222 223 return nil 224 } 225 226 func (m *luaMap) setBucket(sum uint64, key, val object.Value) *bucket { 227 index := m.mod(sum) 228 229 _, elem := m.findBucket(index, sum, key, nil) 230 if elem == nil { 231 elem = m.insertBucket(index, key) 232 } 233 234 elem.val = val 235 elem.sum = sum 236 237 return elem 238 } 239 240 func (m *luaMap) deleteBucket(elem, prev *bucket) { 241 next := elem.next 242 if prev != nil { 243 prev.next = next 244 } else if next != nil { 245 _next := &m.buckets[m.index(next.key)] 246 247 if _next == elem { 248 elem.key = next.key 249 elem.val = next.val 250 elem.sum = next.sum 251 elem.next = next.next 252 253 elem = next 254 } 255 } 256 257 elem.isActive = false 258 elem.key = nil 259 elem.val = nil 260 elem.sum = 0 261 elem.next = nil 262 263 m.length-- 264 } 265 266 func (m *luaMap) findIndex(key object.Value) int { 267 sum := m.sum(key) 268 index := m.mod(sum) 269 270 elem := &m.buckets[index] 271 base := int64(uintptr(unsafe.Pointer(elem))) 272 273 for { 274 if elem == nil { 275 return -1 276 } 277 278 if elem.sum == sum && object.Equal(elem.key, key) { 279 break 280 } 281 282 elem = elem.next 283 } 284 285 end := int64(uintptr(unsafe.Pointer(elem))) 286 287 offset := int((end - base) / int64(bucketSize)) 288 289 return index + offset 290 } 291 292 func (m *luaMap) inext(index int) (newKey, val object.Value, newIndex int) { 293 for i := index + 1; i < m.Cap(); i++ { 294 e := &m.buckets[i] 295 if e.isActive { 296 return e.key, e.val, i 297 } 298 } 299 300 return nil, nil, -1 301 } 302 303 func (m *luaMap) grow() { 304 old := m.buckets 305 306 m.buckets = make([]bucket, len(old)*growRate) 307 m.inactives = m.buckets 308 m.length = 0 309 310 for _, elem := range old { 311 if elem.isActive { 312 m.setBucket(elem.sum, elem.key, elem.val) 313 } 314 } 315 } 316 317 // round up to power of two 318 func roundup(x int) int { 319 if intSize == 8 { 320 x-- 321 x |= x >> 1 322 x |= x >> 2 323 x |= x >> 4 324 x |= x >> 8 325 x |= x >> 16 326 x++ 327 } else { 328 x-- 329 x |= x >> 1 330 x |= x >> 2 331 x |= x >> 4 332 x |= x >> 8 333 x++ 334 } 335 336 return x 337 } 338 339 // for debugging 340 // func (m *luaMap) dump() { 341 // fmt.Printf("len: %d, cap: %d\n", m.Len(), m.Cap()) 342 // for i := 0; i < m.Cap(); i++ { 343 // e := &m.buckets[i] 344 // if e.isActive { 345 // fmt.Printf("%4d: index: %4d, sum: %20d, key: %10v, val: %10v\n", i, m.mod(e.sum), e.sum, e.key, e.val) 346 // } 347 // } 348 // fmt.Println("") 349 // }