initial commit

master
magical 2022-01-22 09:49:46 +00:00
commit a121f47873
2 changed files with 209 additions and 0 deletions

189
pmap.go 100644
View File

@ -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
}

20
pmap_test.go 100644
View File

@ -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)
}
}
}