170 lines
3.0 KiB
Go
170 lines
3.0 KiB
Go
|
package goja
|
||
|
|
||
|
import (
|
||
|
"hash/maphash"
|
||
|
)
|
||
|
|
||
|
type mapEntry struct {
|
||
|
key, value Value
|
||
|
|
||
|
iterPrev, iterNext *mapEntry
|
||
|
hNext *mapEntry
|
||
|
}
|
||
|
|
||
|
type orderedMap struct {
|
||
|
hash *maphash.Hash
|
||
|
hashTable map[uint64]*mapEntry
|
||
|
iterFirst, iterLast *mapEntry
|
||
|
size int
|
||
|
}
|
||
|
|
||
|
type orderedMapIter struct {
|
||
|
m *orderedMap
|
||
|
cur *mapEntry
|
||
|
}
|
||
|
|
||
|
func (m *orderedMap) lookup(key Value) (h uint64, entry, hPrev *mapEntry) {
|
||
|
if key == _negativeZero {
|
||
|
key = intToValue(0)
|
||
|
}
|
||
|
h = key.hash(m.hash)
|
||
|
for entry = m.hashTable[h]; entry != nil && !entry.key.SameAs(key); hPrev, entry = entry, entry.hNext {
|
||
|
}
|
||
|
return
|
||
|
}
|
||
|
|
||
|
func (m *orderedMap) set(key, value Value) {
|
||
|
h, entry, hPrev := m.lookup(key)
|
||
|
if entry != nil {
|
||
|
entry.value = value
|
||
|
} else {
|
||
|
if key == _negativeZero {
|
||
|
key = intToValue(0)
|
||
|
}
|
||
|
entry = &mapEntry{key: key, value: value}
|
||
|
if hPrev == nil {
|
||
|
m.hashTable[h] = entry
|
||
|
} else {
|
||
|
hPrev.hNext = entry
|
||
|
}
|
||
|
if m.iterLast != nil {
|
||
|
entry.iterPrev = m.iterLast
|
||
|
m.iterLast.iterNext = entry
|
||
|
} else {
|
||
|
m.iterFirst = entry
|
||
|
}
|
||
|
m.iterLast = entry
|
||
|
m.size++
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (m *orderedMap) get(key Value) Value {
|
||
|
_, entry, _ := m.lookup(key)
|
||
|
if entry != nil {
|
||
|
return entry.value
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (m *orderedMap) remove(key Value) bool {
|
||
|
h, entry, hPrev := m.lookup(key)
|
||
|
if entry != nil {
|
||
|
entry.key = nil
|
||
|
entry.value = nil
|
||
|
|
||
|
// remove from the doubly-linked list
|
||
|
if entry.iterPrev != nil {
|
||
|
entry.iterPrev.iterNext = entry.iterNext
|
||
|
} else {
|
||
|
m.iterFirst = entry.iterNext
|
||
|
}
|
||
|
if entry.iterNext != nil {
|
||
|
entry.iterNext.iterPrev = entry.iterPrev
|
||
|
} else {
|
||
|
m.iterLast = entry.iterPrev
|
||
|
}
|
||
|
|
||
|
// remove from the hashTable
|
||
|
if hPrev == nil {
|
||
|
if entry.hNext == nil {
|
||
|
delete(m.hashTable, h)
|
||
|
} else {
|
||
|
m.hashTable[h] = entry.hNext
|
||
|
}
|
||
|
} else {
|
||
|
hPrev.hNext = entry.hNext
|
||
|
}
|
||
|
|
||
|
m.size--
|
||
|
return true
|
||
|
}
|
||
|
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
func (m *orderedMap) has(key Value) bool {
|
||
|
_, entry, _ := m.lookup(key)
|
||
|
return entry != nil
|
||
|
}
|
||
|
|
||
|
func (iter *orderedMapIter) next() *mapEntry {
|
||
|
if iter.m == nil {
|
||
|
// closed iterator
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
cur := iter.cur
|
||
|
// if the current item was deleted, track back to find the latest that wasn't
|
||
|
for cur != nil && cur.key == nil {
|
||
|
cur = cur.iterPrev
|
||
|
}
|
||
|
|
||
|
if cur != nil {
|
||
|
cur = cur.iterNext
|
||
|
} else {
|
||
|
cur = iter.m.iterFirst
|
||
|
}
|
||
|
|
||
|
if cur == nil {
|
||
|
iter.close()
|
||
|
} else {
|
||
|
iter.cur = cur
|
||
|
}
|
||
|
|
||
|
return cur
|
||
|
}
|
||
|
|
||
|
func (iter *orderedMapIter) close() {
|
||
|
iter.m = nil
|
||
|
iter.cur = nil
|
||
|
}
|
||
|
|
||
|
func newOrderedMap(h *maphash.Hash) *orderedMap {
|
||
|
return &orderedMap{
|
||
|
hash: h,
|
||
|
hashTable: make(map[uint64]*mapEntry),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (m *orderedMap) newIter() *orderedMapIter {
|
||
|
iter := &orderedMapIter{
|
||
|
m: m,
|
||
|
}
|
||
|
return iter
|
||
|
}
|
||
|
|
||
|
func (m *orderedMap) clear() {
|
||
|
for item := m.iterFirst; item != nil; item = item.iterNext {
|
||
|
item.key = nil
|
||
|
item.value = nil
|
||
|
if item.iterPrev != nil {
|
||
|
item.iterPrev.iterNext = nil
|
||
|
}
|
||
|
}
|
||
|
m.iterFirst = nil
|
||
|
m.iterLast = nil
|
||
|
m.hashTable = make(map[uint64]*mapEntry)
|
||
|
m.size = 0
|
||
|
}
|