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