start of a pvec implementation
just the tree part and some functions to convert to/from slice, no tail yet and no operations on the pvec. just wanted to see what it looked like.master
parent
54dbd81e0c
commit
23db2eeb0a
|
@ -0,0 +1,116 @@
|
||||||
|
package pmap
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math/bits"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
const vecChunk = 16
|
||||||
|
|
||||||
|
type pvec struct {
|
||||||
|
head *vnode
|
||||||
|
tail []Value
|
||||||
|
len int
|
||||||
|
}
|
||||||
|
|
||||||
|
type vnode struct {
|
||||||
|
car, cdr *vnode
|
||||||
|
}
|
||||||
|
|
||||||
|
type vleaf struct {
|
||||||
|
//vnode // for debugging
|
||||||
|
|
||||||
|
elem [vecChunk]Value
|
||||||
|
}
|
||||||
|
|
||||||
|
func getleaf(n *vnode, index, size int) (*vleaf, int) {
|
||||||
|
// precondition: 0 <= index < size
|
||||||
|
// cut is the largest power of 2 less than size
|
||||||
|
// change the loop condition as follows for trees where the leaves are always at the same depth (incl the unbalanced ones)
|
||||||
|
// for cut := floor(size); cut >= vecChunk; cut >>= 1
|
||||||
|
for size > vecChunk {
|
||||||
|
cut := floor(size)
|
||||||
|
if index < cut {
|
||||||
|
n = n.car
|
||||||
|
//size = cut
|
||||||
|
|
||||||
|
// optimization: size is now a power of two, so this subtree is complete and
|
||||||
|
// the loop can be a lot simpler. small thing, but makes a difference when
|
||||||
|
// you're doing a lot of gets
|
||||||
|
for cut >>= 1; cut >= vecChunk; cut >>= 1 {
|
||||||
|
if index < cut {
|
||||||
|
n = n.car
|
||||||
|
} else {
|
||||||
|
n = n.cdr
|
||||||
|
index -= cut
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
n = n.cdr
|
||||||
|
index -= cut
|
||||||
|
size -= cut
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (*vleaf)(unsafe.Pointer(n)), index
|
||||||
|
}
|
||||||
|
|
||||||
|
func list2pvec(elem []Value) *pvec {
|
||||||
|
// 0, 8 == depth 0
|
||||||
|
// 9, 16 == depth 1
|
||||||
|
// 17, 32 == depth 2
|
||||||
|
// etc
|
||||||
|
depth := 0
|
||||||
|
if len(elem) > 0 {
|
||||||
|
depth = bits.Len((uint(len(elem)) - 1) / vecChunk)
|
||||||
|
}
|
||||||
|
_ = depth
|
||||||
|
return &pvec{
|
||||||
|
head: mkvnode(elem, len(elem)),
|
||||||
|
tail: nil,
|
||||||
|
len: len(elem),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func mkvnode(elem []Value, size int) *vnode {
|
||||||
|
if len(elem) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if size <= vecChunk {
|
||||||
|
leaf := new(vleaf)
|
||||||
|
copy(leaf.elem[:], elem)
|
||||||
|
return (*vnode)(unsafe.Pointer(leaf))
|
||||||
|
}
|
||||||
|
cut := floor(size)
|
||||||
|
n := new(vnode)
|
||||||
|
n.car = mkvnode(elem[:], cut)
|
||||||
|
if cut < size {
|
||||||
|
n.cdr = mkvnode(elem[cut:], size-cut)
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
func pvec2list(p *pvec) []Value {
|
||||||
|
var l = make([]Value, p.len)
|
||||||
|
for i := range l {
|
||||||
|
leaf, j := getleaf(p.head, i, p.len)
|
||||||
|
if leaf == nil {
|
||||||
|
panic(fmt.Sprint("nil leaf [index=", i, ", len=", p.len, "]"))
|
||||||
|
}
|
||||||
|
if j != i%vecChunk {
|
||||||
|
panic("index mismatch")
|
||||||
|
}
|
||||||
|
l[i] = leaf.elem[j]
|
||||||
|
}
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
|
||||||
|
// floor returns the largest power of 2 less than n
|
||||||
|
// returns 0 if n <= 0
|
||||||
|
func floor(n int) int {
|
||||||
|
if n <= 1 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return 1 << bits.Len(uint(n-1)) >> 1
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
package pmap
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPvec(t *testing.T) {
|
||||||
|
for n := 4; n < 1024*1024; {
|
||||||
|
list := make([]Value, n)
|
||||||
|
for i := range list {
|
||||||
|
list[i] = len(list) - i
|
||||||
|
}
|
||||||
|
|
||||||
|
p := list2pvec(list)
|
||||||
|
got := pvec2list(p)
|
||||||
|
if !eq(list, got) {
|
||||||
|
t.Error("mismatch with n =", n)
|
||||||
|
if n < 128 {
|
||||||
|
t.Error(" want =", list)
|
||||||
|
t.Error(" got =", got)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if n < 1024 {
|
||||||
|
n++
|
||||||
|
} else {
|
||||||
|
n += n / 8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func eq(a, b []Value) bool {
|
||||||
|
if len(a) != len(b) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for i := range a {
|
||||||
|
if a[i] != b[i] {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFloor(t *testing.T) {
|
||||||
|
if got := floor(8); got != 4 {
|
||||||
|
t.Errorf("floor(%v) = %v, want %v", 8, got, 4)
|
||||||
|
}
|
||||||
|
if got := floor(9); got != 8 {
|
||||||
|
t.Errorf("floor(%v) = %v, want %v", 9, got, 8)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue