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  // }