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