// Copyright 2013 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package interp // Custom hashtable atop map. // For use when the key's equivalence relation is not consistent with ==. // The Go specification doesn't address the atomicity of map operations. // The FAQ states that an implementation is permitted to crash on // concurrent map access. import ( "go/types" ) type hashable interface { hash(t types.Type) int eq(t types.Type, x interface{}) bool } type entry struct { key hashable value value next *entry } // A hashtable atop the built-in map. Since each bucket contains // exactly one hash value, there's no need to perform hash-equality // tests when walking the linked list. Rehashing is done by the // underlying map. type hashmap struct { keyType types.Type table map[int]*entry length int // number of entries in map } // makeMap returns an empty initialized map of key type kt, // preallocating space for reserve elements. func makeMap(kt types.Type, reserve int) value { if usesBuiltinMap(kt) { return make(map[value]value, reserve) } return &hashmap{keyType: kt, table: make(map[int]*entry, reserve)} } // delete removes the association for key k, if any. func (m *hashmap) delete(k hashable) { if m != nil { hash := k.hash(m.keyType) head := m.table[hash] if head != nil { if k.eq(m.keyType, head.key) { m.table[hash] = head.next m.length-- return } prev := head for e := head.next; e != nil; e = e.next { if k.eq(m.keyType, e.key) { prev.next = e.next m.length-- return } prev = e } } } } // lookup returns the value associated with key k, if present, or // value(nil) otherwise. func (m *hashmap) lookup(k hashable) value { if m != nil { hash := k.hash(m.keyType) for e := m.table[hash]; e != nil; e = e.next { if k.eq(m.keyType, e.key) { return e.value } } } return nil } // insert updates the map to associate key k with value v. If there // was already an association for an eq() (though not necessarily ==) // k, the previous key remains in the map and its associated value is // updated. func (m *hashmap) insert(k hashable, v value) { hash := k.hash(m.keyType) head := m.table[hash] for e := head; e != nil; e = e.next { if k.eq(m.keyType, e.key) { e.value = v return } } m.table[hash] = &entry{ key: k, value: v, next: head, } m.length++ } // len returns the number of key/value associations in the map. func (m *hashmap) len() int { if m != nil { return m.length } return 0 } // entries returns a rangeable map of entries. func (m *hashmap) entries() map[int]*entry { if m != nil { return m.table } return nil }