106 lines
2.4 KiB
Go
106 lines
2.4 KiB
Go
|
package pmap
|
||
|
|
||
|
type InplaceMap interface {
|
||
|
Map
|
||
|
SetInplace(Key, Value) InplaceMap
|
||
|
}
|
||
|
|
||
|
func (p pmap) SetInplace(k Key, v Value) InplaceMap {
|
||
|
root, added := inplace_insert(p.root, k, v, p.hash)
|
||
|
p.root = root
|
||
|
if added {
|
||
|
p.len++
|
||
|
}
|
||
|
return p
|
||
|
}
|
||
|
|
||
|
func inplace_insert(n interface{}, key Key, val Value, hashFn HashFunc) (newNode interface{}, added bool) {
|
||
|
if n == nil {
|
||
|
return leaf{key, val}, true
|
||
|
}
|
||
|
hash := hashFn(key)
|
||
|
var insert func(n interface{}, shift uint32) interface{}
|
||
|
insert = func(n interface{}, shift uint32) interface{} {
|
||
|
// insert returns a new node (if the node should be replaced)
|
||
|
// or nil (if the node is unchanged or modified in place)
|
||
|
|
||
|
//fmt.Printf("insert %v %x %#v\n", shift, hash, n)
|
||
|
switch n := n.(type) {
|
||
|
case leaf:
|
||
|
if n.k == key {
|
||
|
// replace existing entry
|
||
|
added = false
|
||
|
return leaf{key, val}
|
||
|
} else if h := hashFn(n.k); h == hash {
|
||
|
// collision
|
||
|
added = true
|
||
|
return &collision{hash, []leaf{{key, val}, n}}
|
||
|
} else {
|
||
|
if h>>shift == hash>>shift {
|
||
|
panic("pmap: infinite loop in insert")
|
||
|
}
|
||
|
// not a collision, so we must still have some hash bits left
|
||
|
// split the trie
|
||
|
x := newnode(n, h, shift)
|
||
|
if x0 := insert(x, shift); x0 != nil {
|
||
|
return x0
|
||
|
}
|
||
|
return x
|
||
|
}
|
||
|
case *node:
|
||
|
c := n.getNode(shift, hash, key)
|
||
|
if c == nil {
|
||
|
// new node
|
||
|
c = leaf{key, val}
|
||
|
added = true
|
||
|
|
||
|
m := bitmask(hash >> shift)
|
||
|
n.bitmap |= m
|
||
|
i := n.index(m)
|
||
|
n.child = append(n.child, nil) // allocate space
|
||
|
copy(n.child[i+1:], n.child[i:]) // shift array up
|
||
|
n.child[i] = c // mutate node
|
||
|
} else {
|
||
|
c = insert(c, shift+nodeShift)
|
||
|
if c == nil {
|
||
|
return nil
|
||
|
}
|
||
|
m := bitmask(hash >> shift)
|
||
|
i := n.index(m)
|
||
|
n.child[i] = c // mutate node
|
||
|
}
|
||
|
n.check()
|
||
|
return nil
|
||
|
case *collision:
|
||
|
if n.hash != hash {
|
||
|
// not a collision, so we must still have some hash bits left
|
||
|
// split the trie
|
||
|
x := newnode(n, n.hash, shift)
|
||
|
if x0 := insert(x, shift); x0 != nil {
|
||
|
return x0
|
||
|
}
|
||
|
return x
|
||
|
}
|
||
|
for i := range n.leaf {
|
||
|
if key == n.leaf[i].k {
|
||
|
// replace existing entry
|
||
|
n.leaf[i] = leaf{key, val} // mutate
|
||
|
added = false
|
||
|
return nil
|
||
|
}
|
||
|
}
|
||
|
// new collision
|
||
|
added = true
|
||
|
n.leaf = append(n.leaf, leaf{key, val}) // mutate
|
||
|
return nil
|
||
|
default:
|
||
|
panic("pmap: unhandled case in insert")
|
||
|
}
|
||
|
}
|
||
|
newNode = insert(n, 0)
|
||
|
if newNode == nil {
|
||
|
newNode = n
|
||
|
}
|
||
|
return
|
||
|
}
|