190 lines
3.6 KiB
Go
190 lines
3.6 KiB
Go
|
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
|
||
|
}
|