initial commit
commit
a121f47873
|
@ -0,0 +1,189 @@
|
|||
package pmap
|
||||
|
||||
const deg = 8 // branch factor of nodes
|
||||
const log2deg = 3
|
||||
const mask = 0b111
|
||||
|
||||
type Key = interface{}
|
||||
type Value = interface{}
|
||||
|
||||
type Map interface {
|
||||
Get(Key) (Value, bool)
|
||||
Set(Key, Value) Map
|
||||
Del(Key) Map
|
||||
Len() int
|
||||
}
|
||||
|
||||
type pmap struct {
|
||||
root interface{}
|
||||
len int
|
||||
hash func(Key) uint32
|
||||
}
|
||||
|
||||
// A Map implemented as a hashed trie
|
||||
type node struct {
|
||||
child [deg]interface{}
|
||||
bitmap uint32
|
||||
}
|
||||
|
||||
type collision struct {
|
||||
hash uint32
|
||||
leaf []leaf
|
||||
}
|
||||
|
||||
type leaf struct {
|
||||
k Key
|
||||
v Value
|
||||
}
|
||||
|
||||
func New() Map {
|
||||
return pmap{}
|
||||
}
|
||||
|
||||
func (p pmap) Len() int {
|
||||
return p.len
|
||||
}
|
||||
|
||||
func (p pmap) Get(k Key) (Value, bool) {
|
||||
h := hash(k) // TODO: p.hash
|
||||
return lookup(p.root, 0, h, k)
|
||||
}
|
||||
|
||||
func (p pmap) Set(k Key, v Value) Map {
|
||||
//p.root = leaf{k, v}
|
||||
//p.len = 1
|
||||
//return p
|
||||
|
||||
h := hash(k) // TODO: p.hash
|
||||
n, _ := p.root.(*node)
|
||||
if n == nil {
|
||||
n = &node{}
|
||||
}
|
||||
n.child[h&mask] = leaf{k, v}
|
||||
n.bitmap = 1 << (h & mask)
|
||||
p.root = n
|
||||
p.len++
|
||||
return p
|
||||
}
|
||||
|
||||
func (p pmap) Del(k Key) Map {
|
||||
return p
|
||||
}
|
||||
|
||||
func hash(k Key) uint32 {
|
||||
u := k.(int)
|
||||
return uint32(uint(u) + uint(u)>>32)
|
||||
}
|
||||
|
||||
func (m *node) getNode(shift, hash uint32, key Key) interface{} {
|
||||
i := hash >> shift & mask
|
||||
return m.child[i]
|
||||
}
|
||||
|
||||
func (m *collision) getNode(hash uint32, key Key) interface{} {
|
||||
if hash != m.hash {
|
||||
return nil
|
||||
}
|
||||
for i := range m.leaf {
|
||||
if key == m.leaf[i].k {
|
||||
return m.leaf[i].v
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func lookup(root interface{}, shift, hash uint32, key Key) (Value, bool) {
|
||||
cur := root
|
||||
for {
|
||||
switch n := cur.(type) {
|
||||
case nil:
|
||||
return nil, false
|
||||
case leaf:
|
||||
if n.k == key {
|
||||
return n.v, true
|
||||
} else {
|
||||
return nil, false
|
||||
}
|
||||
case *node:
|
||||
cur = n.getNode(shift, hash, key)
|
||||
shift += log2deg
|
||||
case *collision:
|
||||
cur = n.getNode(hash, key)
|
||||
default:
|
||||
panic("pmap: unhandled case in lookup")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func singleton(key Key, val Value, hash, shift uint32) *node {
|
||||
n := &node{}
|
||||
idx := hash >> shift & mask
|
||||
n.child[idx] = leaf{key, val}
|
||||
n.bitmap = 1 << idx
|
||||
return n
|
||||
}
|
||||
|
||||
type hashFunc = func(Key) uint32
|
||||
|
||||
func insert(n interface{}, shift, hash uint32, key Key, val Value, hashFn hashFunc) (newNode interface{}, added bool) {
|
||||
if n == nil {
|
||||
return leaf{key, val}, true
|
||||
}
|
||||
var insert func(n interface{}, shift uint32) interface{}
|
||||
insert = func(n interface{}, shift uint32) interface{} {
|
||||
switch n := n.(type) {
|
||||
//case nil:
|
||||
// added = true
|
||||
// return leaf{key, val}
|
||||
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 {
|
||||
// split the trie
|
||||
m := singleton(key, val, hash, shift)
|
||||
return insert(m, shift+log2deg)
|
||||
}
|
||||
case *node:
|
||||
c := n.getNode(shift, hash, key)
|
||||
if c == nil {
|
||||
// new node
|
||||
// TODO
|
||||
added = true
|
||||
return singleton(key, val, shift, hash)
|
||||
} else {
|
||||
// if it's a leaf and the hashes match, form a collision
|
||||
// if we run out of hash, form a leaf
|
||||
//
|
||||
return insert(c, shift+log2deg)
|
||||
}
|
||||
case *collision:
|
||||
if n.hash != hash {
|
||||
panic("hash mismatch") // TODO: can this happen? do we even need collision.hash?
|
||||
}
|
||||
for i := range n.leaf {
|
||||
if key == n.leaf[i].k {
|
||||
// replace existing entry
|
||||
l := make([]leaf, 1, len(n.leaf))
|
||||
l[0] = leaf{key, val}
|
||||
l = append(l, n.leaf[:i]...)
|
||||
l = append(l, n.leaf[i+1:]...)
|
||||
added = false
|
||||
return collision{hash, l}
|
||||
}
|
||||
}
|
||||
// new collision
|
||||
added = true
|
||||
return collision{hash, append([]leaf{{key, val}}, n.leaf...)}
|
||||
default:
|
||||
panic("pmap: unhandled case in insert")
|
||||
}
|
||||
}
|
||||
newNode = insert(n, shift)
|
||||
return
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package pmap
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestPmap(t *testing.T) {
|
||||
p := New()
|
||||
const numElems = 5
|
||||
for i := range make([]int, numElems) {
|
||||
p = p.Set(i, i)
|
||||
}
|
||||
if p.Len() != numElems {
|
||||
t.Fatalf("Len() = %v, want %v", p.Len(), numElems)
|
||||
}
|
||||
for i := range make([]int, numElems) {
|
||||
v, ok := p.Get(i)
|
||||
if v != i || !ok {
|
||||
t.Errorf("Get(%d) = %v %v, want %v %v", i, v, ok, i, true)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue