implement inplace insertion
parent
966e6a5864
commit
0ef57e0865
|
@ -0,0 +1,105 @@
|
|||
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
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
package pmap
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestInplace(t *testing.T) {
|
||||
p := New(hash).(InplaceMap)
|
||||
const numElems = 100
|
||||
for i := range iter(numElems) {
|
||||
p = p.SetInplace(i, i)
|
||||
}
|
||||
if p.Len() != numElems {
|
||||
t.Fatalf("Len() = %v, want %v", p.Len(), numElems)
|
||||
}
|
||||
for i := range iter(numElems) {
|
||||
v, ok := p.Get(i)
|
||||
if v != i || !ok {
|
||||
t.Errorf("Get(%d) = %v %v, want %v %v", i, v, ok, i, true)
|
||||
}
|
||||
}
|
||||
fmt.Print(p.(pmap).stats())
|
||||
}
|
||||
|
||||
func BenchmarkSetInplace(b *testing.B) {
|
||||
b.Run("size=10", func(b *testing.B) { benchmarkSetInplace(b, 10) })
|
||||
b.Run("size=100", func(b *testing.B) { benchmarkSetInplace(b, 100) })
|
||||
b.Run("size=1000", func(b *testing.B) { benchmarkSetInplace(b, 1000) })
|
||||
b.Run("size=10000", func(b *testing.B) { benchmarkSetInplace(b, 10000) })
|
||||
if !testing.Short() {
|
||||
b.Run("size=100000", func(b *testing.B) { benchmarkSetInplace(b, 100000) })
|
||||
}
|
||||
}
|
||||
|
||||
func benchmarkSetInplace(b *testing.B, numElems int) {
|
||||
p := New(hash).(InplaceMap)
|
||||
for i := range iter(numElems) {
|
||||
p = p.SetInplace(i, i)
|
||||
}
|
||||
if p.Len() != numElems {
|
||||
b.Fatalf("Len() = %v, want %v", p.Len(), numElems)
|
||||
}
|
||||
b.ResetTimer()
|
||||
for i := range iter(b.N) {
|
||||
k := numElems
|
||||
p = p.SetInplace(k, i)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue