mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-27 07:06:42 +00:00
pot: new package for proximity order tree
This commit is contained in:
parent
fa99986143
commit
78ad3775fa
5 changed files with 2091 additions and 0 deletions
289
pot/address.go
Normal file
289
pot/address.go
Normal file
|
|
@ -0,0 +1,289 @@
|
||||||
|
// Copyright 2017 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
package pot
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
zeroAddr = &common.Hash{}
|
||||||
|
zerosHex = zeroAddr.Hex()[2:]
|
||||||
|
zerosBin = Address{}.Bin()
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
addrlen = keylen
|
||||||
|
)
|
||||||
|
|
||||||
|
type Address common.Hash
|
||||||
|
|
||||||
|
func (a Address) String() string {
|
||||||
|
return fmt.Sprintf("%x", a[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Address) MarshalJSON() (out []byte, err error) {
|
||||||
|
return []byte(`"` + a.String() + `"`), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Address) UnmarshalJSON(value []byte) error {
|
||||||
|
*a = Address(common.HexToHash(string(value[1 : len(value)-1])))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// the string form of the binary representation of an address (only first 8 bits)
|
||||||
|
func (a Address) Bin() string {
|
||||||
|
return ToBin(a[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
func ToBin(a []byte) string {
|
||||||
|
var bs []string
|
||||||
|
for _, b := range a {
|
||||||
|
bs = append(bs, fmt.Sprintf("%08b", b))
|
||||||
|
}
|
||||||
|
return strings.Join(bs, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a Address) Bytes() []byte {
|
||||||
|
return a[:]
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Proximity(x, y) returns the proximity order of the MSB distance between x and y
|
||||||
|
|
||||||
|
The distance metric MSB(x, y) of two equal length byte sequences x an y is the
|
||||||
|
value of the binary integer cast of the x^y, ie., x and y bitwise xor-ed.
|
||||||
|
the binary cast is big endian: most significant bit first (=MSB).
|
||||||
|
|
||||||
|
Proximity(x, y) is a discrete logarithmic scaling of the MSB distance.
|
||||||
|
It is defined as the reverse rank of the integer part of the base 2
|
||||||
|
logarithm of the distance.
|
||||||
|
It is calculated by counting the number of common leading zeros in the (MSB)
|
||||||
|
binary representation of the x^y.
|
||||||
|
|
||||||
|
(0 farthest, 255 closest, 256 self)
|
||||||
|
*/
|
||||||
|
func proximity(one, other Address) (ret int, eq bool) {
|
||||||
|
return posProximity(one, other, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// posProximity(a, b, pos) returns proximity order of b wrt a (symmetric) pretending
|
||||||
|
// the first pos bits match, checking only bits kzindex >= pos
|
||||||
|
func posProximity(one, other Address, pos int) (ret int, eq bool) {
|
||||||
|
for i := pos / 8; i < len(one); i++ {
|
||||||
|
if one[i] == other[i] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
oxo := one[i] ^ other[i]
|
||||||
|
start := 0
|
||||||
|
if i == pos/8 {
|
||||||
|
start = pos % 8
|
||||||
|
}
|
||||||
|
for j := start; j < 8; j++ {
|
||||||
|
if (uint8(oxo)>>uint8(7-j))&0x01 != 0 {
|
||||||
|
return i*8 + j, false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return len(one) * 8, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Address.ProxCmp compares the distances a->target and b->target.
|
||||||
|
// Returns -1 if a is closer to target, 1 if b is closer to target
|
||||||
|
// and 0 if they are equal.
|
||||||
|
func (target Address) ProxCmp(a, b Address) int {
|
||||||
|
for i := range target {
|
||||||
|
da := a[i] ^ target[i]
|
||||||
|
db := b[i] ^ target[i]
|
||||||
|
if da > db {
|
||||||
|
return 1
|
||||||
|
} else if da < db {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// randomAddressAt(address, prox) generates a random address
|
||||||
|
// at proximity order prox relative to address
|
||||||
|
// if prox is negative a random address is generated
|
||||||
|
func RandomAddressAt(self Address, prox int) (addr Address) {
|
||||||
|
addr = self
|
||||||
|
pos := -1
|
||||||
|
if prox >= 0 {
|
||||||
|
pos = prox / 8
|
||||||
|
trans := prox % 8
|
||||||
|
transbytea := byte(0)
|
||||||
|
for j := 0; j <= trans; j++ {
|
||||||
|
transbytea |= 1 << uint8(7-j)
|
||||||
|
}
|
||||||
|
flipbyte := byte(1 << uint8(7-trans))
|
||||||
|
transbyteb := transbytea ^ byte(255)
|
||||||
|
randbyte := byte(rand.Intn(255))
|
||||||
|
addr[pos] = ((addr[pos] & transbytea) ^ flipbyte) | randbyte&transbyteb
|
||||||
|
}
|
||||||
|
for i := pos + 1; i < len(addr); i++ {
|
||||||
|
addr[i] = byte(rand.Intn(255))
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeyRange(a0, a1, proxLimit) returns the address inclusive address
|
||||||
|
// range that contain addresses closer to one than other
|
||||||
|
// func KeyRange(one, other Address, proxLimit int) (start, stop Address) {
|
||||||
|
// prox := proximity(one, other)
|
||||||
|
// if prox >= proxLimit {
|
||||||
|
// prox = proxLimit
|
||||||
|
// }
|
||||||
|
// start = CommonBitsAddrByte(one, other, byte(0x00), prox)
|
||||||
|
// stop = CommonBitsAddrByte(one, other, byte(0xff), prox)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
|
func CommonBitsAddrF(self, other Address, f func() byte, p int) (addr Address) {
|
||||||
|
prox, _ := proximity(self, other)
|
||||||
|
var pos int
|
||||||
|
if p <= prox {
|
||||||
|
prox = p
|
||||||
|
}
|
||||||
|
pos = prox / 8
|
||||||
|
addr = self
|
||||||
|
trans := byte(prox % 8)
|
||||||
|
var transbytea byte
|
||||||
|
if p > prox {
|
||||||
|
transbytea = byte(0x7f)
|
||||||
|
} else {
|
||||||
|
transbytea = byte(0xff)
|
||||||
|
}
|
||||||
|
transbytea >>= trans
|
||||||
|
transbyteb := transbytea ^ byte(0xff)
|
||||||
|
addrpos := addr[pos]
|
||||||
|
addrpos &= transbyteb
|
||||||
|
if p > prox {
|
||||||
|
addrpos ^= byte(0x80 >> trans)
|
||||||
|
}
|
||||||
|
addrpos |= transbytea & f()
|
||||||
|
addr[pos] = addrpos
|
||||||
|
for i := pos + 1; i < len(addr); i++ {
|
||||||
|
addr[i] = f()
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func CommonBitsAddr(self, other Address, prox int) (addr Address) {
|
||||||
|
return CommonBitsAddrF(self, other, func() byte { return byte(rand.Intn(255)) }, prox)
|
||||||
|
}
|
||||||
|
|
||||||
|
func CommonBitsAddrByte(self, other Address, b byte, prox int) (addr Address) {
|
||||||
|
return CommonBitsAddrF(self, other, func() byte { return b }, prox)
|
||||||
|
}
|
||||||
|
|
||||||
|
// randomAddressAt() generates a random address
|
||||||
|
func RandomAddress() Address {
|
||||||
|
return RandomAddressAt(Address{}, -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// wraps an Address to implement the PotVal interface
|
||||||
|
type HashAddress struct {
|
||||||
|
Address
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *HashAddress) String() string {
|
||||||
|
return a.Address.Bin()
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAddressFromString(s string) []byte {
|
||||||
|
ha := [32]byte{}
|
||||||
|
|
||||||
|
t := s + string(zerosBin)[:len(zerosBin)-len(s)]
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
n, err := strconv.ParseUint(t[i*64:(i+1)*64], 2, 64)
|
||||||
|
if err != nil {
|
||||||
|
panic("wrong format: " + err.Error())
|
||||||
|
}
|
||||||
|
binary.BigEndian.PutUint64(ha[i*8:(i+1)*8], uint64(n))
|
||||||
|
}
|
||||||
|
return ha[:]
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHashAddress(s string) *HashAddress {
|
||||||
|
ha := NewAddressFromString(s)
|
||||||
|
h := common.Hash{}
|
||||||
|
copy(h[:], ha)
|
||||||
|
return &HashAddress{Address(h)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHashAddressFromBytes(b []byte) *HashAddress {
|
||||||
|
h := common.Hash{}
|
||||||
|
copy(h[:], b)
|
||||||
|
return &HashAddress{Address(h)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PO(addr, pos) return the proximity order of addr wrt to
|
||||||
|
// the pinned address of the tree
|
||||||
|
// assuming it is greater than or equal to pos
|
||||||
|
func (self *HashAddress) PO(val PotVal, pos int) (po int, eq bool) {
|
||||||
|
return posProximity(self.Address, val.(*HashAddress).Address, pos)
|
||||||
|
}
|
||||||
|
|
||||||
|
type BoolAddress struct {
|
||||||
|
addr []bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBoolAddress(s string) *BoolAddress {
|
||||||
|
return NewBoolAddressXOR(s, zerosBin[:len(s)])
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBoolAddressXOR(s, t string) *BoolAddress {
|
||||||
|
if len(s) != len(t) {
|
||||||
|
panic("lengths do not match")
|
||||||
|
}
|
||||||
|
addr := make([]bool, len(s))
|
||||||
|
for i, _ := range addr {
|
||||||
|
addr[i] = s[i] != t[i]
|
||||||
|
}
|
||||||
|
return &BoolAddress{addr}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *BoolAddress) String() string {
|
||||||
|
a := self.addr
|
||||||
|
s := []byte(zerosBin)[:len(a)]
|
||||||
|
for i, one := range a {
|
||||||
|
if one {
|
||||||
|
s[i] = byte('1')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return string(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *BoolAddress) PO(val PotVal, pos int) (po int, eq bool) {
|
||||||
|
a := self.addr
|
||||||
|
b := val.(*BoolAddress).addr
|
||||||
|
for po = pos; po < len(b); po++ {
|
||||||
|
if a[po] != b[po] {
|
||||||
|
return po, false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return po, true
|
||||||
|
}
|
||||||
141
pot/address_test.go
Normal file
141
pot/address_test.go
Normal file
|
|
@ -0,0 +1,141 @@
|
||||||
|
// Copyright 2017 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
package pot
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/rand"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (Address) Generate(rand *rand.Rand, size int) reflect.Value {
|
||||||
|
var id Address
|
||||||
|
for i := 0; i < len(id); i++ {
|
||||||
|
id[i] = byte(uint8(rand.Intn(255)))
|
||||||
|
}
|
||||||
|
return reflect.ValueOf(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCommonBitsAddrF(t *testing.T) {
|
||||||
|
a := Address(common.HexToHash("0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"))
|
||||||
|
b := Address(common.HexToHash("0x8123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"))
|
||||||
|
c := Address(common.HexToHash("0x4123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"))
|
||||||
|
d := Address(common.HexToHash("0x0023456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"))
|
||||||
|
e := Address(common.HexToHash("0x01A3456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"))
|
||||||
|
ab := CommonBitsAddrF(a, b, func() byte { return byte(0x00) }, 10)
|
||||||
|
expab := Address(common.HexToHash("0x8000000000000000000000000000000000000000000000000000000000000000"))
|
||||||
|
|
||||||
|
if ab != expab {
|
||||||
|
t.Fatalf("%v != %v", ab, expab)
|
||||||
|
}
|
||||||
|
ac := CommonBitsAddrF(a, c, func() byte { return byte(0x00) }, 10)
|
||||||
|
expac := Address(common.HexToHash("0x4000000000000000000000000000000000000000000000000000000000000000"))
|
||||||
|
|
||||||
|
if ac != expac {
|
||||||
|
t.Fatalf("%v != %v", ac, expac)
|
||||||
|
}
|
||||||
|
ad := CommonBitsAddrF(a, d, func() byte { return byte(0x00) }, 10)
|
||||||
|
expad := Address(common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"))
|
||||||
|
|
||||||
|
if ad != expad {
|
||||||
|
t.Fatalf("%v != %v", ad, expad)
|
||||||
|
}
|
||||||
|
ae := CommonBitsAddrF(a, e, func() byte { return byte(0x00) }, 10)
|
||||||
|
expae := Address(common.HexToHash("0x0180000000000000000000000000000000000000000000000000000000000000"))
|
||||||
|
|
||||||
|
if ae != expae {
|
||||||
|
t.Fatalf("%v != %v", ae, expae)
|
||||||
|
}
|
||||||
|
acf := CommonBitsAddrF(a, c, func() byte { return byte(0xff) }, 10)
|
||||||
|
expacf := Address(common.HexToHash("0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))
|
||||||
|
|
||||||
|
if acf != expacf {
|
||||||
|
t.Fatalf("%v != %v", acf, expacf)
|
||||||
|
}
|
||||||
|
aeo := CommonBitsAddrF(a, e, func() byte { return byte(0x00) }, 2)
|
||||||
|
expaeo := Address(common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"))
|
||||||
|
|
||||||
|
if aeo != expaeo {
|
||||||
|
t.Fatalf("%v != %v", aeo, expaeo)
|
||||||
|
}
|
||||||
|
aep := CommonBitsAddrF(a, e, func() byte { return byte(0xff) }, 2)
|
||||||
|
expaep := Address(common.HexToHash("0x3fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))
|
||||||
|
|
||||||
|
if aep != expaep {
|
||||||
|
t.Fatalf("%v != %v", aep, expaep)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRandomAddressAt(t *testing.T) {
|
||||||
|
var a Address
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
a = RandomAddress()
|
||||||
|
prox := rand.Intn(255)
|
||||||
|
b := RandomAddressAt(a, prox)
|
||||||
|
p, _ := proximity(a, b)
|
||||||
|
if p != prox {
|
||||||
|
t.Fatalf("incorrect address prox(%v, %v) == %v (expected %v)", a, b, p, prox)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
maxTestPOs = 1000
|
||||||
|
testPOkeylen = 9
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPOs(t *testing.T) {
|
||||||
|
for i := 0; i < maxTestPOs; i++ {
|
||||||
|
length := rand.Intn(256) + 1
|
||||||
|
v0 := RandomAddress().Bin()[:length]
|
||||||
|
v1 := RandomAddress().Bin()[:length]
|
||||||
|
a0 := NewBoolAddress(v0)
|
||||||
|
a1 := NewBoolAddress(v1)
|
||||||
|
b0 := NewHashAddress(v0)
|
||||||
|
b1 := NewHashAddress(v1)
|
||||||
|
pos := rand.Intn(length) + 1
|
||||||
|
apo, aeq := a0.PO(a1, pos)
|
||||||
|
bpo, beq := b0.PO(b1, pos)
|
||||||
|
if bpo == 256 {
|
||||||
|
bpo = length
|
||||||
|
}
|
||||||
|
a0s := a0.String()
|
||||||
|
if a0s != v0 {
|
||||||
|
t.Fatalf("incorrect bool address. expected %v, got %v", v0, a0s)
|
||||||
|
}
|
||||||
|
a1s := a1.String()
|
||||||
|
if a1s != v1 {
|
||||||
|
t.Fatalf("incorrect bool address. expected %v, got %v", v1, a1s)
|
||||||
|
}
|
||||||
|
b0s := b0.String()[:length]
|
||||||
|
if b0s != v0 {
|
||||||
|
t.Fatalf("incorrect hash address. expected %v, got %v", v0, b0s)
|
||||||
|
}
|
||||||
|
b1s := b1.String()[:length]
|
||||||
|
if b1s != v1 {
|
||||||
|
t.Fatalf("incorrect hash address. expected %v, got %v", v1, b1s)
|
||||||
|
}
|
||||||
|
if apo != bpo {
|
||||||
|
t.Fatalf("PO does not match for %v X %v (pos: %v): expected %v, got %v", v0, v1, pos, apo, bpo)
|
||||||
|
}
|
||||||
|
if aeq != beq {
|
||||||
|
t.Fatalf("PO equality does not match for %v X %v (pos: %v): expected %v, got %v", v0, v1, pos, aeq, beq)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
65
pot/doc.go
Normal file
65
pot/doc.go
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
/*
|
||||||
|
POT: proximity order tree implements a container similar to a binary tree.
|
||||||
|
Value types implement the PoVal interface which provides the PO (proximity order)
|
||||||
|
comparison operator.
|
||||||
|
Each fork in the trie is itself a value. Values of the subtree contained under
|
||||||
|
a node all share the same order when compared to other elements in the tree.
|
||||||
|
|
||||||
|
Example of proximity order is the length of the common prefix over bitvectors.
|
||||||
|
(which is equivalent to the order of magnitude of the XOR distance over integers).
|
||||||
|
|
||||||
|
The package provides two implementations of PoVal,
|
||||||
|
|
||||||
|
* BoolAddress: an arbitrary length boolean vector
|
||||||
|
* HashAddress: a bitvector based address derived from 256 bit hash (common.Hash)
|
||||||
|
|
||||||
|
If the address space if limited, equality is defined as the maximum proximity order.
|
||||||
|
|
||||||
|
Arbitrary value types can extend these base types or define their own PO method.
|
||||||
|
|
||||||
|
The container offers applicative (funcional) style methods on PO trees:
|
||||||
|
* adding/removing en element
|
||||||
|
* swap (value based add/remove)
|
||||||
|
* merging two PO trees (union)
|
||||||
|
|
||||||
|
as well as iterator accessors that respect proximity order
|
||||||
|
|
||||||
|
When synchronicity of membership if not 100% requirement (e.g. used as a database
|
||||||
|
of network connections), applicative structures have the advantage that nodes
|
||||||
|
are immutable therefore manipulation does not need locking allowing for parallel retrievals.
|
||||||
|
For the use case where the entire container is supposed to allow changes by parallel routines, instance methods with write locks and access methods with readlock are provided. The latter only locks while reading the root node.
|
||||||
|
|
||||||
|
Pot
|
||||||
|
* retrieval, insertion and deletion by key involves log(n) pointer lookups
|
||||||
|
* for any item retrieval (defined as common prefix on the binary key)
|
||||||
|
* provide syncronous iterators respecting proximity ordering wrt any item
|
||||||
|
* provide asyncronous iterator (for parallel execution of operations) over n items
|
||||||
|
* allows cheap iteration over ranges
|
||||||
|
* asymmetric parallellised merge (union)
|
||||||
|
|
||||||
|
Note:
|
||||||
|
* as is, union only makes sense for set representations since which of two values with equal keys survives is random
|
||||||
|
* intersection is not implemented
|
||||||
|
* simple get accessor is not implemented (but derivable from EachNeighbour)
|
||||||
|
|
||||||
|
Pinned value on the node implies no need to copy keys of the item type.
|
||||||
|
|
||||||
|
Note that
|
||||||
|
* the same set of values allows for a large number of alternative
|
||||||
|
POT representations.
|
||||||
|
* values on the top are accessed faster than lower ones and the steps needed to retrieve items has a logarithmic distribution.
|
||||||
|
|
||||||
|
As a consequence on can organise the tree so that items that need faster access are torwards the top.
|
||||||
|
In particular for any subset where popularity has a power distriution that is independent of
|
||||||
|
proximity order (content addressed storage of chunks), it is in principle possible to create a pot where the steps needed to access an item is inversely proportional to its popularity.
|
||||||
|
Such organisation is not implemented as yet.
|
||||||
|
|
||||||
|
TODO:
|
||||||
|
* swap
|
||||||
|
* get
|
||||||
|
* overwrite-style merge
|
||||||
|
* intersection
|
||||||
|
* access frequency based optimisations
|
||||||
|
|
||||||
|
*/
|
||||||
|
package pot
|
||||||
881
pot/pot.go
Normal file
881
pot/pot.go
Normal file
|
|
@ -0,0 +1,881 @@
|
||||||
|
// Copyright 2017 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
package pot
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
keylen = 256
|
||||||
|
maxkeylen = 256
|
||||||
|
)
|
||||||
|
|
||||||
|
// Pot is the root node type, allows locked non-applicative manipulation
|
||||||
|
type Pot struct {
|
||||||
|
lock sync.RWMutex
|
||||||
|
*pot
|
||||||
|
}
|
||||||
|
|
||||||
|
// pot is the node type (same for root, branching node and leaf)
|
||||||
|
type pot struct {
|
||||||
|
pin PotVal
|
||||||
|
bins []*pot
|
||||||
|
size int
|
||||||
|
po int
|
||||||
|
}
|
||||||
|
|
||||||
|
// PotVal is the interface the generic container item should implement
|
||||||
|
type PotVal interface {
|
||||||
|
PO(PotVal, int) (po int, eq bool)
|
||||||
|
String() string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pot constructor. Requires value of type PotVal to pin
|
||||||
|
// and po to point to a span in the PotVal key
|
||||||
|
// The pinned item counts towards the size
|
||||||
|
func NewPot(v PotVal, po int) *Pot {
|
||||||
|
var size int
|
||||||
|
if v != nil {
|
||||||
|
size++
|
||||||
|
}
|
||||||
|
return &Pot{
|
||||||
|
pot: &pot{
|
||||||
|
pin: v,
|
||||||
|
po: po,
|
||||||
|
size: size,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pin() returns the pinned element (key) of the Pot
|
||||||
|
func (t *Pot) Pin() PotVal {
|
||||||
|
return t.pin
|
||||||
|
}
|
||||||
|
|
||||||
|
// Size() returns the number of values in the Pot
|
||||||
|
func (t *Pot) Size() int {
|
||||||
|
t.lock.RLock()
|
||||||
|
defer t.lock.RUnlock()
|
||||||
|
return t.size
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add(v) inserts v into the Pot and
|
||||||
|
// returns the proximity order of v and a boolean
|
||||||
|
// indicating if the item was found
|
||||||
|
// Add locks the Pot while using applicative add on its pot
|
||||||
|
func (t *Pot) Add(val PotVal) (po int, found bool) {
|
||||||
|
t.lock.Lock()
|
||||||
|
defer t.lock.Unlock()
|
||||||
|
t.pot, po, found = add(t.pot, val)
|
||||||
|
return po, found
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add(t, v) returns a new Pot that contains all the elements of t
|
||||||
|
// plus the value v, using the applicative add
|
||||||
|
// the second return value is the proximity order of the inserted element
|
||||||
|
// the third is boolean indicating if the item was found
|
||||||
|
// it only readlocks the Pot while reading its pot
|
||||||
|
func Add(t *Pot, val PotVal) (*Pot, int, bool) {
|
||||||
|
t.lock.RLock()
|
||||||
|
n := t.pot
|
||||||
|
t.lock.RUnlock()
|
||||||
|
r, po, found := add(n, val)
|
||||||
|
return &Pot{pot: r}, po, found
|
||||||
|
}
|
||||||
|
|
||||||
|
func add(t *pot, val PotVal) (*pot, int, bool) {
|
||||||
|
var r *pot
|
||||||
|
if t == nil || t.pin == nil {
|
||||||
|
r = &pot{
|
||||||
|
pin: val,
|
||||||
|
size: t.size + 1,
|
||||||
|
po: t.po,
|
||||||
|
bins: t.bins,
|
||||||
|
}
|
||||||
|
return r, 0, false
|
||||||
|
}
|
||||||
|
po, found := t.pin.PO(val, t.po)
|
||||||
|
if found {
|
||||||
|
r = &pot{
|
||||||
|
pin: val,
|
||||||
|
size: t.size,
|
||||||
|
po: t.po,
|
||||||
|
bins: t.bins,
|
||||||
|
}
|
||||||
|
return r, po, true
|
||||||
|
}
|
||||||
|
|
||||||
|
var p *pot
|
||||||
|
var i, j int
|
||||||
|
size := t.size
|
||||||
|
for i < len(t.bins) {
|
||||||
|
n := t.bins[i]
|
||||||
|
if n.po == po {
|
||||||
|
p, _, found = add(n, val)
|
||||||
|
if !found {
|
||||||
|
size++
|
||||||
|
}
|
||||||
|
j++
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if n.po > po {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
j++
|
||||||
|
}
|
||||||
|
if p == nil {
|
||||||
|
size++
|
||||||
|
p = &pot{
|
||||||
|
pin: val,
|
||||||
|
size: 1,
|
||||||
|
po: po,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bins := append([]*pot{}, t.bins[:i]...)
|
||||||
|
bins = append(bins, p)
|
||||||
|
bins = append(bins, t.bins[j:]...)
|
||||||
|
r = &pot{
|
||||||
|
pin: t.pin,
|
||||||
|
size: size,
|
||||||
|
po: t.po,
|
||||||
|
bins: bins,
|
||||||
|
}
|
||||||
|
|
||||||
|
return r, po, found
|
||||||
|
}
|
||||||
|
|
||||||
|
// T.Re move(v) deletes v from the Pot and returns
|
||||||
|
// the proximity order of v and a boolean value indicating
|
||||||
|
// if the value was found
|
||||||
|
// Remove locks Pot while using applicative remove on its pot
|
||||||
|
func (t *Pot) Remove(val PotVal) (po int, found bool) {
|
||||||
|
t.lock.Lock()
|
||||||
|
defer t.lock.Unlock()
|
||||||
|
t.pot, po, found = remove(t.pot, val)
|
||||||
|
return po, found
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove(t, v) returns a new Pot that contains all the elements of t
|
||||||
|
// minus the value v, using the applicative remove
|
||||||
|
// the second return value is the proximity order of the inserted element
|
||||||
|
// the third is boolean indicating if the item was found
|
||||||
|
// it only readlocks the Pot while reading its pot
|
||||||
|
func Remove(t *Pot, v PotVal) (*Pot, int, bool) {
|
||||||
|
t.lock.RLock()
|
||||||
|
n := t.pot
|
||||||
|
t.lock.RUnlock()
|
||||||
|
r, po, found := remove(n, v)
|
||||||
|
return &Pot{pot: r}, po, found
|
||||||
|
}
|
||||||
|
|
||||||
|
func remove(t *pot, val PotVal) (r *pot, po int, found bool) {
|
||||||
|
size := t.size
|
||||||
|
po, found = t.pin.PO(val, t.po)
|
||||||
|
if found {
|
||||||
|
size--
|
||||||
|
if size == 0 {
|
||||||
|
r = &pot{
|
||||||
|
po: t.po,
|
||||||
|
}
|
||||||
|
return r, po, true
|
||||||
|
}
|
||||||
|
i := len(t.bins) - 1
|
||||||
|
last := t.bins[i]
|
||||||
|
r = &pot{
|
||||||
|
pin: last.pin,
|
||||||
|
bins: append(t.bins[:i], last.bins...),
|
||||||
|
size: size,
|
||||||
|
po: t.po,
|
||||||
|
}
|
||||||
|
return r, t.po, true
|
||||||
|
}
|
||||||
|
|
||||||
|
var p *pot
|
||||||
|
var i, j int
|
||||||
|
for i < len(t.bins) {
|
||||||
|
n := t.bins[i]
|
||||||
|
if n.po == po {
|
||||||
|
p, po, found = remove(n, val)
|
||||||
|
if found {
|
||||||
|
size--
|
||||||
|
}
|
||||||
|
j++
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if n.po > po {
|
||||||
|
return t, po, false
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
j++
|
||||||
|
}
|
||||||
|
bins := t.bins[:i]
|
||||||
|
if p != nil && p.pin != nil {
|
||||||
|
bins = append(bins, p)
|
||||||
|
}
|
||||||
|
bins = append(bins, t.bins[j:]...)
|
||||||
|
r = &pot{
|
||||||
|
pin: val,
|
||||||
|
size: size,
|
||||||
|
po: t.po,
|
||||||
|
bins: bins,
|
||||||
|
}
|
||||||
|
return r, po, found
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swap(k, f) looks up the item at k
|
||||||
|
// and applies the function f to the value v at k or nil if the item is not found
|
||||||
|
// if f returns nil, the element is removed
|
||||||
|
// if f returns v' <> v then v' is inserted into the Pot
|
||||||
|
// if v' == v the pot is not changed
|
||||||
|
// it panics if v'.PO(k, 0) says v and k are not equal
|
||||||
|
func (t *Pot) Swap(val PotVal, f func(v PotVal) PotVal) (po int, found bool, change bool) {
|
||||||
|
t.lock.Lock()
|
||||||
|
defer t.lock.Unlock()
|
||||||
|
var t0 *pot
|
||||||
|
t0, po, found, change = swap(t.pot, val, f)
|
||||||
|
if change {
|
||||||
|
t.pot = t0
|
||||||
|
}
|
||||||
|
return po, found, change
|
||||||
|
}
|
||||||
|
|
||||||
|
func swap(t *pot, k PotVal, f func(v PotVal) PotVal) (r *pot, po int, found bool, change bool) {
|
||||||
|
var val PotVal
|
||||||
|
if t == nil || t.pin == nil {
|
||||||
|
val = f(nil)
|
||||||
|
if val == nil {
|
||||||
|
return t, t.po, false, false
|
||||||
|
}
|
||||||
|
if _, eq := val.PO(k, t.po); !eq {
|
||||||
|
panic("value key mismatch")
|
||||||
|
}
|
||||||
|
r = &pot{
|
||||||
|
pin: val,
|
||||||
|
size: t.size + 1,
|
||||||
|
po: t.po,
|
||||||
|
bins: t.bins,
|
||||||
|
}
|
||||||
|
return r, t.po, false, true
|
||||||
|
}
|
||||||
|
size := t.size
|
||||||
|
if k == nil {
|
||||||
|
panic("k is nil")
|
||||||
|
}
|
||||||
|
po, found = k.PO(t.pin, t.po)
|
||||||
|
if found {
|
||||||
|
val = f(t.pin)
|
||||||
|
if val == nil {
|
||||||
|
size--
|
||||||
|
if size == 0 {
|
||||||
|
r = &pot{
|
||||||
|
po: t.po,
|
||||||
|
}
|
||||||
|
return r, po, true, true
|
||||||
|
}
|
||||||
|
i := len(t.bins) - 1
|
||||||
|
last := t.bins[i]
|
||||||
|
r = &pot{
|
||||||
|
pin: last.pin,
|
||||||
|
bins: append(t.bins[:i], last.bins...),
|
||||||
|
size: size,
|
||||||
|
po: t.po,
|
||||||
|
}
|
||||||
|
return r, t.po, true, true
|
||||||
|
// remove element
|
||||||
|
} else if val == t.pin {
|
||||||
|
return nil, po, true, false
|
||||||
|
} else { // add element
|
||||||
|
r = &pot{
|
||||||
|
pin: val,
|
||||||
|
size: t.size,
|
||||||
|
po: t.po,
|
||||||
|
bins: t.bins,
|
||||||
|
}
|
||||||
|
return r, po, true, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var p *pot
|
||||||
|
var i, j int
|
||||||
|
for i < len(t.bins) {
|
||||||
|
n := t.bins[i]
|
||||||
|
if n.po == po {
|
||||||
|
p, po, found, change = swap(n, k, f)
|
||||||
|
if !change {
|
||||||
|
return nil, po, found, false
|
||||||
|
}
|
||||||
|
size += p.size - n.size
|
||||||
|
j++
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if n.po > po {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
j++
|
||||||
|
}
|
||||||
|
if p == nil {
|
||||||
|
val := f(nil)
|
||||||
|
if val == nil {
|
||||||
|
return nil, po, false, false
|
||||||
|
}
|
||||||
|
size++
|
||||||
|
p = &pot{
|
||||||
|
pin: val,
|
||||||
|
size: 1,
|
||||||
|
po: po,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bins := append([]*pot{}, t.bins[:i]...)
|
||||||
|
if p.pin != nil {
|
||||||
|
bins = append(bins, p)
|
||||||
|
}
|
||||||
|
bins = append(bins, t.bins[j:]...)
|
||||||
|
r = &pot{
|
||||||
|
pin: t.pin,
|
||||||
|
size: size,
|
||||||
|
po: t.po,
|
||||||
|
bins: bins,
|
||||||
|
}
|
||||||
|
|
||||||
|
return r, po, found, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// t0.Merge(t1) changes t0 to contain all the elements of t1
|
||||||
|
// it locks t0, but only readlocks t1 while taking its pot
|
||||||
|
// uses applicative union
|
||||||
|
func (t *Pot) Merge(t1 *Pot) (c int) {
|
||||||
|
t.lock.Lock()
|
||||||
|
defer t.lock.Unlock()
|
||||||
|
t1.lock.RLock()
|
||||||
|
n1 := t1.pot
|
||||||
|
t1.lock.RUnlock()
|
||||||
|
t.pot, c = union(t.pot, n1)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// Union(t0, t1) return the union of t0 and t1
|
||||||
|
// it only readlocks the Pot-s to read their pots and
|
||||||
|
// calculates the union using the applicative union
|
||||||
|
// the second return value is the number of common elements
|
||||||
|
func Union(t0, t1 *Pot) (*Pot, int) {
|
||||||
|
t0.lock.RLock()
|
||||||
|
n0 := t0.pot
|
||||||
|
t0.lock.RUnlock()
|
||||||
|
t1.lock.RLock()
|
||||||
|
n1 := t1.pot
|
||||||
|
t1.lock.RUnlock()
|
||||||
|
|
||||||
|
p, c := union(n0, n1)
|
||||||
|
return &Pot{
|
||||||
|
pot: p,
|
||||||
|
}, c
|
||||||
|
}
|
||||||
|
|
||||||
|
func union(t0, t1 *pot) (*pot, int) {
|
||||||
|
if t0 == nil || t0.size == 0 {
|
||||||
|
return t1, 0
|
||||||
|
}
|
||||||
|
if t1 == nil || t1.size == 0 {
|
||||||
|
return t0, 0
|
||||||
|
}
|
||||||
|
var pin PotVal
|
||||||
|
var bins []*pot
|
||||||
|
var mis []int
|
||||||
|
wg := &sync.WaitGroup{}
|
||||||
|
pin0 := t0.pin
|
||||||
|
pin1 := t1.pin
|
||||||
|
bins0 := t0.bins
|
||||||
|
bins1 := t1.bins
|
||||||
|
var i0, i1 int
|
||||||
|
var common int
|
||||||
|
|
||||||
|
po, eq := pin0.PO(pin1, 0)
|
||||||
|
|
||||||
|
for {
|
||||||
|
l0 := len(bins0)
|
||||||
|
l1 := len(bins1)
|
||||||
|
var n0, n1 *pot
|
||||||
|
var p0, p1 int
|
||||||
|
var a0, a1 bool
|
||||||
|
|
||||||
|
for {
|
||||||
|
|
||||||
|
if !a0 && i0 < l0 && bins0[i0].po <= po {
|
||||||
|
n0 = bins0[i0]
|
||||||
|
p0 = n0.po
|
||||||
|
a0 = p0 == po
|
||||||
|
} else {
|
||||||
|
a0 = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if !a1 && i1 < l1 && bins1[i1].po <= po {
|
||||||
|
n1 = bins1[i1]
|
||||||
|
p1 = n1.po
|
||||||
|
a1 = p1 == po
|
||||||
|
} else {
|
||||||
|
a1 = true
|
||||||
|
}
|
||||||
|
if a0 && a1 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case (p0 < p1 || a1) && !a0:
|
||||||
|
bins = append(bins, n0)
|
||||||
|
i0++
|
||||||
|
n0 = nil
|
||||||
|
case (p1 < p0 || a0) && !a1:
|
||||||
|
bins = append(bins, n1)
|
||||||
|
i1++
|
||||||
|
n1 = nil
|
||||||
|
case p1 < po:
|
||||||
|
bl := len(bins)
|
||||||
|
bins = append(bins, nil)
|
||||||
|
ml := len(mis)
|
||||||
|
mis = append(mis, 0)
|
||||||
|
wg.Add(1)
|
||||||
|
go func(b, m int, m0, m1 *pot) {
|
||||||
|
defer wg.Done()
|
||||||
|
bins[b], mis[m] = union(m0, m1)
|
||||||
|
}(bl, ml, n0, n1)
|
||||||
|
i0++
|
||||||
|
i1++
|
||||||
|
n0 = nil
|
||||||
|
n1 = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if eq {
|
||||||
|
common++
|
||||||
|
pin = pin1
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
i := i0
|
||||||
|
if len(bins0) > i && bins0[i].po == po {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
var size0 int
|
||||||
|
for _, n := range bins0[i:] {
|
||||||
|
size0 += n.size
|
||||||
|
}
|
||||||
|
np := &pot{
|
||||||
|
pin: pin0,
|
||||||
|
bins: bins0[i:],
|
||||||
|
size: size0 + 1,
|
||||||
|
po: po,
|
||||||
|
}
|
||||||
|
|
||||||
|
bins2 := []*pot{np}
|
||||||
|
if n0 == nil {
|
||||||
|
pin0 = pin1
|
||||||
|
po = maxkeylen + 1
|
||||||
|
eq = true
|
||||||
|
common--
|
||||||
|
|
||||||
|
} else {
|
||||||
|
bins2 = append(bins2, n0.bins...)
|
||||||
|
pin0 = pin1
|
||||||
|
pin1 = n0.pin
|
||||||
|
po, eq = pin0.PO(pin1, n0.po)
|
||||||
|
|
||||||
|
}
|
||||||
|
bins0 = bins1
|
||||||
|
bins1 = bins2
|
||||||
|
i0 = i1
|
||||||
|
i1 = 0
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
for _, c := range mis {
|
||||||
|
common += c
|
||||||
|
}
|
||||||
|
n := &pot{
|
||||||
|
pin: pin,
|
||||||
|
bins: bins,
|
||||||
|
size: t0.size + t1.size - common,
|
||||||
|
po: t0.po,
|
||||||
|
}
|
||||||
|
return n, common
|
||||||
|
}
|
||||||
|
|
||||||
|
// Each(f) is a synchronous iterator over the bins of a node
|
||||||
|
// respecting an ordering
|
||||||
|
// proximity > pinnedness
|
||||||
|
func (t *Pot) Each(f func(PotVal, int) bool) bool {
|
||||||
|
t.lock.RLock()
|
||||||
|
n := t.pot
|
||||||
|
t.lock.RUnlock()
|
||||||
|
return n.each(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *pot) each(f func(PotVal, int) bool) bool {
|
||||||
|
var next bool
|
||||||
|
for _, n := range t.bins {
|
||||||
|
next = n.each(f)
|
||||||
|
if !next {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return f(t.pin, t.po)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EachFrom(f, start) is a synchronous iterator over the elements of a pot
|
||||||
|
// within the inclusive range starting from proximity order start
|
||||||
|
// the function argument is passed the value and the proximity order wrt the root pin
|
||||||
|
// it does NOT include the pinned item of the root
|
||||||
|
// respecting an ordering
|
||||||
|
// proximity > pinnedness
|
||||||
|
// the iteration ends if the function return false or there are no more elements
|
||||||
|
// end of a po range can be implemented since po is passed to the function
|
||||||
|
func (t *Pot) EachFrom(f func(PotVal, int) bool, po int) bool {
|
||||||
|
t.lock.RLock()
|
||||||
|
n := t.pot
|
||||||
|
t.lock.RUnlock()
|
||||||
|
return n.eachFrom(f, po)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *pot) eachFrom(f func(PotVal, int) bool, po int) bool {
|
||||||
|
var next bool
|
||||||
|
_, lim := t.getPos(po)
|
||||||
|
for i := lim; i < len(t.bins); i++ {
|
||||||
|
n := t.bins[i]
|
||||||
|
next = n.each(f)
|
||||||
|
if !next {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return f(t.pin, t.po)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EachBin iterates over bins of the pivot node and offers iterators to the caller on each
|
||||||
|
// subtree passing the proximity order and the size
|
||||||
|
// the iteration continues until the function's return value is false
|
||||||
|
// or there are no more subtries
|
||||||
|
func (t *Pot) EachBin(val PotVal, po int, f func(int, int, func(func(val PotVal, i int) bool) bool) bool) {
|
||||||
|
t.lock.RLock()
|
||||||
|
n := t.pot
|
||||||
|
t.lock.RUnlock()
|
||||||
|
n.eachBin(val, po, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *pot) eachBin(val PotVal, po int, f func(int, int, func(func(val PotVal, i int) bool) bool) bool) {
|
||||||
|
if t == nil || t.size == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
spr, _ := t.pin.PO(val, t.po)
|
||||||
|
_, lim := t.getPos(spr)
|
||||||
|
var size int
|
||||||
|
var n *pot
|
||||||
|
for i := 0; i < lim; i++ {
|
||||||
|
n = t.bins[i]
|
||||||
|
size += n.size
|
||||||
|
if n.po < po {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !f(n.po, n.size, n.each) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if lim == len(t.bins) {
|
||||||
|
f(spr, 1, func(g func(PotVal, int) bool) bool {
|
||||||
|
return g(t.pin, spr)
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
n = t.bins[lim]
|
||||||
|
|
||||||
|
spo := spr
|
||||||
|
if n.po == spr {
|
||||||
|
spo++
|
||||||
|
size += n.size
|
||||||
|
}
|
||||||
|
if !f(spr, t.size-size, func(g func(PotVal, int) bool) bool {
|
||||||
|
return t.eachFrom(func(v PotVal, j int) bool {
|
||||||
|
return g(v, spr)
|
||||||
|
}, spo)
|
||||||
|
}) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if spo > spr {
|
||||||
|
n.eachBin(val, spo, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// syncronous iterator over neighbours of any target val
|
||||||
|
// the order of elements retrieved reflect proximity order to the target
|
||||||
|
// TODO: add maximum proxbin to start range of iteration
|
||||||
|
func (t *Pot) EachNeighbour(val PotVal, f func(PotVal, int) bool) bool {
|
||||||
|
t.lock.RLock()
|
||||||
|
n := t.pot
|
||||||
|
t.lock.RUnlock()
|
||||||
|
return n.eachNeighbour(val, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *pot) eachNeighbour(val PotVal, f func(PotVal, int) bool) bool {
|
||||||
|
if t == nil || t.size == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
var next bool
|
||||||
|
l := len(t.bins)
|
||||||
|
var n *pot
|
||||||
|
ir := l
|
||||||
|
il := l
|
||||||
|
po, eq := t.pin.PO(val, t.po)
|
||||||
|
if !eq {
|
||||||
|
n, il = t.getPos(po)
|
||||||
|
if n != nil {
|
||||||
|
next = n.eachNeighbour(val, f)
|
||||||
|
if !next {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
ir = il
|
||||||
|
} else {
|
||||||
|
ir = il - 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
next = f(t.pin, po)
|
||||||
|
if !next {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := l - 1; i > ir; i-- {
|
||||||
|
next = t.bins[i].each(func(v PotVal, _ int) bool {
|
||||||
|
return f(v, po)
|
||||||
|
})
|
||||||
|
if !next {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := il - 1; i >= 0; i-- {
|
||||||
|
n := t.bins[i]
|
||||||
|
next = n.each(func(v PotVal, _ int) bool {
|
||||||
|
return f(v, n.po)
|
||||||
|
})
|
||||||
|
if !next {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// EachNeighnbourAsync(val, max, maxPos, f, wait) is an asyncronous iterator
|
||||||
|
// over elements not closer than maxPos wrt val.
|
||||||
|
// val does not need to be match an element of the pot, but if it does, and
|
||||||
|
// maxPos is keylength than it is included in the iteration
|
||||||
|
// Calls to f are parallelised, the order of calls is undefined.
|
||||||
|
// proximity order is respected in that there is no element in the pot that
|
||||||
|
// is not visited if a closer node is visited.
|
||||||
|
// The iteration is finished when max number of nearest nodes is visited
|
||||||
|
// or if the entire there are no nodes not closer than maxPos that is not visited
|
||||||
|
// if wait is true, the iterator returns only if all calls to f are finished
|
||||||
|
// TODO: implement minPos for proper prox range iteration
|
||||||
|
func (t *Pot) EachNeighbourAsync(val PotVal, max int, maxPos int, f func(PotVal, int), wait bool) {
|
||||||
|
t.lock.RLock()
|
||||||
|
n := t.pot
|
||||||
|
t.lock.RUnlock()
|
||||||
|
if max > t.size {
|
||||||
|
max = t.size
|
||||||
|
}
|
||||||
|
var wg *sync.WaitGroup
|
||||||
|
if wait {
|
||||||
|
wg = &sync.WaitGroup{}
|
||||||
|
}
|
||||||
|
_ = n.eachNeighbourAsync(val, max, maxPos, f, wg)
|
||||||
|
if wait {
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *pot) eachNeighbourAsync(val PotVal, max int, maxPos int, f func(PotVal, int), wg *sync.WaitGroup) (extra int) {
|
||||||
|
|
||||||
|
l := len(t.bins)
|
||||||
|
var n *pot
|
||||||
|
il := l
|
||||||
|
ir := l
|
||||||
|
// ic := l
|
||||||
|
|
||||||
|
po, eq := t.pin.PO(val, t.po)
|
||||||
|
|
||||||
|
// if po is too close, set the pivot branch (pom) to maxPos
|
||||||
|
pom := po
|
||||||
|
if pom > maxPos {
|
||||||
|
pom = maxPos
|
||||||
|
}
|
||||||
|
n, il = t.getPos(pom)
|
||||||
|
ir = il
|
||||||
|
// if pivot branch exists and po is not too close, iterate on the pivot branch
|
||||||
|
if pom == po {
|
||||||
|
if n != nil {
|
||||||
|
|
||||||
|
m := n.size
|
||||||
|
if max < m {
|
||||||
|
m = max
|
||||||
|
}
|
||||||
|
max -= m
|
||||||
|
|
||||||
|
extra = n.eachNeighbourAsync(val, m, maxPos, f, wg)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if !eq {
|
||||||
|
ir--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
extra++
|
||||||
|
max--
|
||||||
|
if n != nil {
|
||||||
|
il++
|
||||||
|
}
|
||||||
|
// before checking max, add up the extra elements
|
||||||
|
// on the close branches that are skipped (if po is too close)
|
||||||
|
for i := l - 1; i >= il; i-- {
|
||||||
|
s := t.bins[i]
|
||||||
|
m := s.size
|
||||||
|
if max < m {
|
||||||
|
m = max
|
||||||
|
}
|
||||||
|
max -= m
|
||||||
|
extra += m
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var m int
|
||||||
|
if pom == po {
|
||||||
|
|
||||||
|
m, max, extra = need(1, max, extra)
|
||||||
|
if m <= 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if wg != nil {
|
||||||
|
wg.Add(1)
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
if wg != nil {
|
||||||
|
defer wg.Done()
|
||||||
|
}
|
||||||
|
f(t.pin, po)
|
||||||
|
}()
|
||||||
|
|
||||||
|
// otherwise iterats
|
||||||
|
for i := l - 1; i > ir; i-- {
|
||||||
|
n := t.bins[i]
|
||||||
|
|
||||||
|
m, max, extra = need(n.size, max, extra)
|
||||||
|
if m <= 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if wg != nil {
|
||||||
|
wg.Add(m)
|
||||||
|
}
|
||||||
|
go func(pn *pot, pm int) {
|
||||||
|
pn.each(func(v PotVal, _ int) bool {
|
||||||
|
if wg != nil {
|
||||||
|
defer wg.Done()
|
||||||
|
}
|
||||||
|
f(v, po)
|
||||||
|
pm--
|
||||||
|
return pm > 0
|
||||||
|
})
|
||||||
|
}(n, m)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// iterate branches that are farther tham pom with their own po
|
||||||
|
for i := il - 1; i >= 0; i-- {
|
||||||
|
n := t.bins[i]
|
||||||
|
// the first time max is less than the size of the entire branch
|
||||||
|
// wait for the pivot thread to release extra elements
|
||||||
|
m, max, extra = need(n.size, max, extra)
|
||||||
|
if m <= 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if wg != nil {
|
||||||
|
wg.Add(m)
|
||||||
|
}
|
||||||
|
go func(pn *pot, pm int) {
|
||||||
|
pn.each(func(v PotVal, _ int) bool {
|
||||||
|
if wg != nil {
|
||||||
|
defer wg.Done()
|
||||||
|
}
|
||||||
|
f(v, pn.po)
|
||||||
|
pm--
|
||||||
|
return pm > 0
|
||||||
|
})
|
||||||
|
}(n, m)
|
||||||
|
|
||||||
|
}
|
||||||
|
return max + extra
|
||||||
|
}
|
||||||
|
|
||||||
|
// getPos(n) returns the forking node at PO n and its index if it exists
|
||||||
|
// otherwise nil
|
||||||
|
// caller is suppoed to hold the lock
|
||||||
|
func (t *pot) getPos(po int) (n *pot, i int) {
|
||||||
|
for i, n = range t.bins {
|
||||||
|
if po > n.po {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if po < n.po {
|
||||||
|
return nil, i
|
||||||
|
}
|
||||||
|
return n, i
|
||||||
|
}
|
||||||
|
return nil, len(t.bins)
|
||||||
|
}
|
||||||
|
|
||||||
|
// need(m, max, extra) uses max m out of extra, and then max
|
||||||
|
// if needed, returns the adjusted counts
|
||||||
|
func need(m, max, extra int) (int, int, int) {
|
||||||
|
if m <= extra {
|
||||||
|
return m, max, extra - m
|
||||||
|
}
|
||||||
|
max += extra - m
|
||||||
|
if max <= 0 {
|
||||||
|
return m + max, 0, 0
|
||||||
|
}
|
||||||
|
return m, max, 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *pot) String() string {
|
||||||
|
return t.sstring("")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *pot) sstring(indent string) string {
|
||||||
|
if t == nil {
|
||||||
|
return "<nil>"
|
||||||
|
}
|
||||||
|
var s string
|
||||||
|
indent += " "
|
||||||
|
s += fmt.Sprintf("%v%v (%v) %v \n", indent, t.pin, t.po, t.size)
|
||||||
|
for _, n := range t.bins {
|
||||||
|
s += fmt.Sprintf("%v%v\n", indent, n.sstring(indent))
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
715
pot/pot_test.go
Normal file
715
pot/pot_test.go
Normal file
|
|
@ -0,0 +1,715 @@
|
||||||
|
// Copyright 2017 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
package pot
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
"runtime"
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/logger"
|
||||||
|
"github.com/ethereum/go-ethereum/logger/glog"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
maxEachNeighbourTests = 420
|
||||||
|
maxEachNeighbour = 420
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
glog.SetV(0)
|
||||||
|
glog.SetToStderr(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
type testBVAddr struct {
|
||||||
|
*HashAddress
|
||||||
|
i int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTestBVAddr(s string, i int) *testBVAddr {
|
||||||
|
return &testBVAddr{NewHashAddress(s), i}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *testBVAddr) String() string {
|
||||||
|
return a.HashAddress.String()[:keylen]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *testBVAddr) PO(val PotVal, po int) (int, bool) {
|
||||||
|
return self.HashAddress.PO(val.(*testBVAddr).HashAddress, po)
|
||||||
|
}
|
||||||
|
|
||||||
|
type testAddr struct {
|
||||||
|
*BoolAddress
|
||||||
|
i int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTestAddr(s string, i int) *testAddr {
|
||||||
|
return &testAddr{NewBoolAddress(s), i}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *testAddr) PO(val PotVal, po int) (int, bool) {
|
||||||
|
return self.BoolAddress.PO(val.(*testAddr).BoolAddress, po)
|
||||||
|
}
|
||||||
|
|
||||||
|
func str(v PotVal) string {
|
||||||
|
if v == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return v.(*testAddr).String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func randomTestAddr(n int, i int) *testAddr {
|
||||||
|
v := RandomAddress().Bin()[:n]
|
||||||
|
return NewTestAddr(v, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
func randomTestBVAddr(n int, i int) *testBVAddr {
|
||||||
|
v := RandomAddress().Bin()[:n]
|
||||||
|
return NewTestBVAddr(v, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
func indexes(t *Pot) (i []int, po []int) {
|
||||||
|
t.Each(func(v PotVal, p int) bool {
|
||||||
|
a := v.(*testAddr)
|
||||||
|
i = append(i, a.i)
|
||||||
|
po = append(po, p)
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
return i, po
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAdd(t *Pot, n int, values ...string) {
|
||||||
|
for i, val := range values {
|
||||||
|
t.Add(NewTestAddr(val, i+n))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// func RandomBoolAddress()
|
||||||
|
func TestPotAdd(t *testing.T) {
|
||||||
|
n := NewPot(NewTestAddr("001111", 0), 0)
|
||||||
|
// Pin set correctly
|
||||||
|
exp := "001111"
|
||||||
|
got := str(n.Pin())[:6]
|
||||||
|
if got != exp {
|
||||||
|
t.Fatalf("incorrect pinned value. Expected %v, got %v", exp, got)
|
||||||
|
}
|
||||||
|
// check size
|
||||||
|
goti := n.Size()
|
||||||
|
expi := 1
|
||||||
|
if goti != expi {
|
||||||
|
t.Fatalf("incorrect number of elements in Pot. Expected %v, got %v", expi, goti)
|
||||||
|
}
|
||||||
|
|
||||||
|
testAdd(n, 1, "011111", "001111", "011111", "000111")
|
||||||
|
// check size
|
||||||
|
goti = n.Size()
|
||||||
|
expi = 3
|
||||||
|
if goti != expi {
|
||||||
|
t.Fatalf("incorrect number of elements in Pot. Expected %v, got %v", expi, goti)
|
||||||
|
}
|
||||||
|
inds, po := indexes(n)
|
||||||
|
got = fmt.Sprintf("%v", inds)
|
||||||
|
exp = "[3 4 2]"
|
||||||
|
if got != exp {
|
||||||
|
t.Fatalf("incorrect indexes in iteration over Pot. Expected %v, got %v", exp, got)
|
||||||
|
}
|
||||||
|
got = fmt.Sprintf("%v", po)
|
||||||
|
exp = "[1 2 0]"
|
||||||
|
if got != exp {
|
||||||
|
t.Fatalf("incorrect po-s in iteration over Pot. Expected %v, got %v", exp, got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// func RandomBoolAddress()
|
||||||
|
func TestPotRemove(t *testing.T) {
|
||||||
|
n := NewPot(NewTestAddr("001111", 0), 0)
|
||||||
|
n.Remove(NewTestAddr("001111", 0))
|
||||||
|
exp := ""
|
||||||
|
got := str(n.Pin())
|
||||||
|
if got != exp {
|
||||||
|
t.Fatalf("incorrect pinned value. Expected %v, got %v", exp, got)
|
||||||
|
}
|
||||||
|
testAdd(n, 1, "000000", "011111", "001111", "000111")
|
||||||
|
n.Remove(NewTestAddr("001111", 0))
|
||||||
|
goti := n.Size()
|
||||||
|
expi := 3
|
||||||
|
if goti != expi {
|
||||||
|
t.Fatalf("incorrect number of elements in Pot. Expected %v, got %v", expi, goti)
|
||||||
|
}
|
||||||
|
inds, po := indexes(n)
|
||||||
|
got = fmt.Sprintf("%v", inds)
|
||||||
|
exp = "[2 4 0]"
|
||||||
|
if got != exp {
|
||||||
|
t.Fatalf("incorrect indexes in iteration over Pot. Expected %v, got %v", exp, got)
|
||||||
|
}
|
||||||
|
got = fmt.Sprintf("%v", po)
|
||||||
|
exp = "[1 3 0]"
|
||||||
|
if got != exp {
|
||||||
|
t.Fatalf("incorrect po-s in iteration over Pot. Expected %v, got %v", exp, got)
|
||||||
|
}
|
||||||
|
// remove again
|
||||||
|
n.Remove(NewTestAddr("001111", 0))
|
||||||
|
inds, po = indexes(n)
|
||||||
|
got = fmt.Sprintf("%v", inds)
|
||||||
|
exp = "[2 4]"
|
||||||
|
if got != exp {
|
||||||
|
t.Fatalf("incorrect indexes in iteration over Pot. Expected %v, got %v", exp, got)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPotSwap(t *testing.T) {
|
||||||
|
max := maxEachNeighbour
|
||||||
|
n := NewPot(nil, 0)
|
||||||
|
var m []*testBVAddr
|
||||||
|
for j := 0; j < 2*max; {
|
||||||
|
v := randomTestBVAddr(keylen, j)
|
||||||
|
_, found := n.Add(v)
|
||||||
|
if !found {
|
||||||
|
m = append(m, v)
|
||||||
|
j++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
k := make(map[string]*testBVAddr)
|
||||||
|
for j := 0; j < max; {
|
||||||
|
v := randomTestBVAddr(keylen, 1)
|
||||||
|
_, found := k[v.String()]
|
||||||
|
if !found {
|
||||||
|
k[v.String()] = v
|
||||||
|
j++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, v := range k {
|
||||||
|
m = append(m, v)
|
||||||
|
}
|
||||||
|
f := func(v PotVal) PotVal {
|
||||||
|
tv := v.(*testBVAddr)
|
||||||
|
if tv.i < max {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
tv.i = 0
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
for _, val := range m {
|
||||||
|
n.Swap(val, func(v PotVal) PotVal {
|
||||||
|
if v == nil {
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
return f(v)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
sum := 0
|
||||||
|
n.Each(func(v PotVal, i int) bool {
|
||||||
|
sum++
|
||||||
|
tv := v.(*testBVAddr)
|
||||||
|
if tv.i > 1 {
|
||||||
|
t.Fatalf("item value incorrect, expected 0, got %v", tv.i)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
if sum != 2*max {
|
||||||
|
t.Fatalf("incorrect number of elements. expected %v, got %v", 2*max, sum)
|
||||||
|
}
|
||||||
|
if sum != n.Size() {
|
||||||
|
t.Fatalf("incorrect size. expected %v, got %v", sum, n.Size())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkPo(val PotVal) func(PotVal, int) error {
|
||||||
|
return func(v PotVal, po int) error {
|
||||||
|
// check the po
|
||||||
|
exp, _ := val.PO(v, 0)
|
||||||
|
if po != exp {
|
||||||
|
return fmt.Errorf("incorrect prox order for item %v in neighbour iteration for %v. Expected %v, got %v", v, val, exp, po)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkOrder(val PotVal) func(PotVal, int) error {
|
||||||
|
var po int = keylen
|
||||||
|
return func(v PotVal, p int) error {
|
||||||
|
if po < p {
|
||||||
|
return fmt.Errorf("incorrect order for item %v in neighbour iteration for %v. PO %v > %v (previous max)", v, val, p, po)
|
||||||
|
}
|
||||||
|
po = p
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkValues(m map[string]bool, val PotVal) func(PotVal, int) error {
|
||||||
|
return func(v PotVal, po int) error {
|
||||||
|
duplicate, ok := m[v.String()]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("alien value %v", v)
|
||||||
|
}
|
||||||
|
if duplicate {
|
||||||
|
return fmt.Errorf("duplicate value returned: %v", v)
|
||||||
|
}
|
||||||
|
m[v.String()] = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var errNoCount = errors.New("not count")
|
||||||
|
|
||||||
|
func testPotEachNeighbour(n *Pot, val PotVal, expCount int, fs ...func(PotVal, int) error) error {
|
||||||
|
var err error
|
||||||
|
var count int
|
||||||
|
n.EachNeighbour(val, func(v PotVal, po int) bool {
|
||||||
|
for _, f := range fs {
|
||||||
|
err = f(v, po)
|
||||||
|
if err != nil {
|
||||||
|
return err.Error() == errNoCount.Error()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
count++
|
||||||
|
if count == expCount {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
if err == nil && count < expCount {
|
||||||
|
return fmt.Errorf("not enough neighbours returned, expected %v, got %v", expCount, count)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
mergeTestCount = 5
|
||||||
|
mergeTestChoose = 5
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPotMergeOne(t *testing.T) {
|
||||||
|
pot1 := NewPot(nil, 0)
|
||||||
|
pot1.Add(NewTestAddr("10", 0))
|
||||||
|
pot1.Add(NewTestAddr("00", 0))
|
||||||
|
pot2 := NewPot(nil, 0)
|
||||||
|
pot2.Add(NewTestAddr("01", 0))
|
||||||
|
glog.V(logger.Debug).Infof("\n%v\n%v", pot2, pot1)
|
||||||
|
pot1.Merge(pot2)
|
||||||
|
count := 0
|
||||||
|
pot1.Each(func(val PotVal, i int) bool {
|
||||||
|
count++
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
if count != 3 {
|
||||||
|
t.Fatalf("expected count to be 3, got %d\n%v\n%v", count, pot2, pot1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPotMergeCommon(t *testing.T) {
|
||||||
|
vs := make([]*testBVAddr, mergeTestCount)
|
||||||
|
for i := 0; i < maxEachNeighbourTests; i++ {
|
||||||
|
|
||||||
|
for i := 0; i < len(vs); i++ {
|
||||||
|
vs[i] = randomTestBVAddr(keylen, i)
|
||||||
|
}
|
||||||
|
max0 := rand.Intn(mergeTestChoose) + 1
|
||||||
|
max1 := rand.Intn(mergeTestChoose) + 1
|
||||||
|
n0 := NewPot(nil, 0)
|
||||||
|
n1 := NewPot(nil, 0)
|
||||||
|
glog.V(3).Infof("round %v: %v - %v", i, max0, max1)
|
||||||
|
m := make(map[string]bool)
|
||||||
|
for j := 0; j < max0; {
|
||||||
|
r := rand.Intn(max0)
|
||||||
|
v := vs[r]
|
||||||
|
// v := randomTestBVAddr(keylen, j)
|
||||||
|
_, found := n0.Add(v)
|
||||||
|
if !found {
|
||||||
|
m[v.String()] = false
|
||||||
|
j++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expAdded := 0
|
||||||
|
|
||||||
|
for j := 0; j < max1; {
|
||||||
|
r := rand.Intn(max1)
|
||||||
|
v := vs[r]
|
||||||
|
_, found := n1.Add(v)
|
||||||
|
if !found {
|
||||||
|
j++
|
||||||
|
}
|
||||||
|
_, found = m[v.String()]
|
||||||
|
if !found {
|
||||||
|
expAdded++
|
||||||
|
m[v.String()] = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if i < 6 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
expSize := len(m)
|
||||||
|
glog.V(4).Infof("%v-0: pin: %v, size: %v", i, n0.Pin(), max0)
|
||||||
|
glog.V(4).Infof("%v-1: pin: %v, size: %v", i, n1.Pin(), max1)
|
||||||
|
glog.V(4).Infof("%v: merged tree size: %v, newly added: %v", i, expSize, expAdded)
|
||||||
|
n, common := Union(n0, n1)
|
||||||
|
added := n1.Size() - common
|
||||||
|
size := n.Size()
|
||||||
|
|
||||||
|
if expSize != size {
|
||||||
|
t.Fatalf("%v: incorrect number of elements in merged pot, expected %v, got %v\n%v", i, expSize, size, n)
|
||||||
|
}
|
||||||
|
if expAdded != added {
|
||||||
|
t.Fatalf("%v: incorrect number of added elements in merged pot, expected %v, got %v", i, expAdded, added)
|
||||||
|
}
|
||||||
|
if !checkDuplicates(n.pot) {
|
||||||
|
t.Fatalf("%v: merged pot contains duplicates: \n%v", i, n)
|
||||||
|
}
|
||||||
|
for k, _ := range m {
|
||||||
|
_, found := n.Add(NewTestBVAddr(k, 0))
|
||||||
|
if !found {
|
||||||
|
t.Fatalf("%v: merged pot (size:%v, added: %v) missing element %v\n%v", i, size, added, k, n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPotMergeScale(t *testing.T) {
|
||||||
|
for i := 0; i < maxEachNeighbourTests; i++ {
|
||||||
|
max0 := rand.Intn(maxEachNeighbour) + 1
|
||||||
|
max1 := rand.Intn(maxEachNeighbour) + 1
|
||||||
|
n0 := NewPot(nil, 0)
|
||||||
|
n1 := NewPot(nil, 0)
|
||||||
|
glog.V(3).Infof("round %v: %v - %v", i, max0, max1)
|
||||||
|
m := make(map[string]bool)
|
||||||
|
for j := 0; j < max0; {
|
||||||
|
v := randomTestBVAddr(keylen, j)
|
||||||
|
// v := randomTestBVAddr(keylen, j)
|
||||||
|
_, found := n0.Add(v)
|
||||||
|
if !found {
|
||||||
|
m[v.String()] = false
|
||||||
|
j++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expAdded := 0
|
||||||
|
|
||||||
|
for j := 0; j < max1; {
|
||||||
|
v := randomTestBVAddr(keylen, j)
|
||||||
|
// v := randomTestBVAddr(keylen, j)
|
||||||
|
_, found := n1.Add(v)
|
||||||
|
if !found {
|
||||||
|
j++
|
||||||
|
}
|
||||||
|
_, found = m[v.String()]
|
||||||
|
if !found {
|
||||||
|
expAdded++
|
||||||
|
m[v.String()] = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if i < 6 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
expSize := len(m)
|
||||||
|
glog.V(4).Infof("%v-0: pin: %v, size: %v", i, n0.Pin(), max0)
|
||||||
|
glog.V(4).Infof("%v-1: pin: %v, size: %v", i, n1.Pin(), max1)
|
||||||
|
glog.V(4).Infof("%v: merged tree size: %v, newly added: %v", i, expSize, expAdded)
|
||||||
|
n, common := Union(n0, n1)
|
||||||
|
added := n1.Size() - common
|
||||||
|
size := n.Size()
|
||||||
|
|
||||||
|
if expSize != size {
|
||||||
|
t.Fatalf("%v: incorrect number of elements in merged pot, expected %v, got %v\n%v", i, expSize, size, n)
|
||||||
|
}
|
||||||
|
if expAdded != added {
|
||||||
|
t.Fatalf("%v: incorrect number of added elements in merged pot, expected %v, got %v", i, expAdded, added)
|
||||||
|
}
|
||||||
|
if !checkDuplicates(n.pot) {
|
||||||
|
t.Fatalf("%v: merged pot contains duplicates: \n%v", i, n)
|
||||||
|
}
|
||||||
|
for k, _ := range m {
|
||||||
|
_, found := n.Add(NewTestBVAddr(k, 0))
|
||||||
|
if !found {
|
||||||
|
t.Fatalf("%v: merged pot (size:%v, added: %v) missing element %v\n%v", i, size, added, k, n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkDuplicates(t *pot) bool {
|
||||||
|
var po int = -1
|
||||||
|
for _, c := range t.bins {
|
||||||
|
if c == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if c.po <= po || !checkDuplicates(c) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
po = c.po
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPotEachNeighbourSync(t *testing.T) {
|
||||||
|
for i := 0; i < maxEachNeighbourTests; i++ {
|
||||||
|
max := rand.Intn(maxEachNeighbour/2) + maxEachNeighbour/2
|
||||||
|
pin := randomTestAddr(keylen, 0)
|
||||||
|
n := NewPot(pin, 0)
|
||||||
|
m := make(map[string]bool)
|
||||||
|
m[pin.String()] = false
|
||||||
|
for j := 1; j <= max; j++ {
|
||||||
|
v := randomTestAddr(keylen, j)
|
||||||
|
n.Add(v)
|
||||||
|
m[v.String()] = false
|
||||||
|
}
|
||||||
|
|
||||||
|
size := n.Size()
|
||||||
|
if size < 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
count := rand.Intn(size/2) + size/2
|
||||||
|
val := randomTestAddr(keylen, max+1)
|
||||||
|
glog.V(3).Infof("%v: pin: %v, size: %v, val: %v, count: %v", i, n.Pin(), size, val, count)
|
||||||
|
err := testPotEachNeighbour(n, val, count, checkPo(val), checkOrder(val), checkValues(m, val))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
minPoFound := keylen
|
||||||
|
maxPoNotFound := 0
|
||||||
|
for k, found := range m {
|
||||||
|
po, _ := val.PO(NewTestAddr(k, 0), 0)
|
||||||
|
if found {
|
||||||
|
if po < minPoFound {
|
||||||
|
minPoFound = po
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if po > maxPoNotFound {
|
||||||
|
maxPoNotFound = po
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if minPoFound < maxPoNotFound {
|
||||||
|
t.Fatalf("incorrect neighbours returned: found one with PO %v < there was one not found with PO %v", minPoFound, maxPoNotFound)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPotEachNeighbourAsync(t *testing.T) {
|
||||||
|
for i := 0; i < maxEachNeighbourTests; i++ {
|
||||||
|
max := rand.Intn(maxEachNeighbour/2) + maxEachNeighbour/2
|
||||||
|
n := NewPot(randomTestAddr(keylen, 0), 0)
|
||||||
|
var size int = 1
|
||||||
|
for j := 1; j <= max; j++ {
|
||||||
|
v := randomTestAddr(keylen, j)
|
||||||
|
_, found := n.Add(v)
|
||||||
|
if !found {
|
||||||
|
size++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if size != n.Size() {
|
||||||
|
t.Fatal(n)
|
||||||
|
}
|
||||||
|
if size < 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
count := rand.Intn(size/2) + size/2
|
||||||
|
val := randomTestAddr(keylen, max+1)
|
||||||
|
|
||||||
|
mu := sync.Mutex{}
|
||||||
|
m := make(map[string]bool)
|
||||||
|
maxPos := rand.Intn(keylen)
|
||||||
|
glog.V(3).Infof("%v: pin: %v, size: %v, val: %v, count: %v, maxPos: %v", i, n.Pin(), size, val, count, maxPos)
|
||||||
|
msize := 0
|
||||||
|
remember := func(v PotVal, po int) error {
|
||||||
|
// mu.Lock()
|
||||||
|
// defer mu.Unlock()
|
||||||
|
if po > maxPos {
|
||||||
|
// glog.V(4).Infof("NOT ADD %v", v)
|
||||||
|
return errNoCount
|
||||||
|
}
|
||||||
|
// glog.V(4).Infof("ADD %v, %v", v, msize)
|
||||||
|
m[v.String()] = true
|
||||||
|
msize++
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if i == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
err := testPotEachNeighbour(n, val, count, remember)
|
||||||
|
if err != nil {
|
||||||
|
glog.V(6).Info(err)
|
||||||
|
}
|
||||||
|
d := 0
|
||||||
|
forget := func(v PotVal, po int) {
|
||||||
|
mu.Lock()
|
||||||
|
defer mu.Unlock()
|
||||||
|
d++
|
||||||
|
// glog.V(4).Infof("DEL %v", v)
|
||||||
|
delete(m, v.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
n.EachNeighbourAsync(val, count, maxPos, forget, true)
|
||||||
|
if d != msize {
|
||||||
|
t.Fatalf("incorrect number of neighbour calls in async iterator. expected %v, got %v", msize, d)
|
||||||
|
}
|
||||||
|
if len(m) != 0 {
|
||||||
|
t.Fatalf("incorrect neighbour calls in async iterator. %v items missed:\n%v", len(m), n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func benchmarkEachNeighbourSync(t *testing.B, max, count int, d time.Duration) {
|
||||||
|
t.ReportAllocs()
|
||||||
|
pin := randomTestAddr(keylen, 0)
|
||||||
|
n := NewPot(pin, 0)
|
||||||
|
for j := 1; j <= max; {
|
||||||
|
v := randomTestAddr(keylen, j)
|
||||||
|
_, found := n.Add(v)
|
||||||
|
if !found {
|
||||||
|
j++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
t.ResetTimer()
|
||||||
|
for i := 0; i < t.N; i++ {
|
||||||
|
val := randomTestAddr(keylen, max+1)
|
||||||
|
m := 0
|
||||||
|
n.EachNeighbour(val, func(v PotVal, po int) bool {
|
||||||
|
time.Sleep(d)
|
||||||
|
m++
|
||||||
|
if m == count {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
t.StopTimer()
|
||||||
|
stats := new(runtime.MemStats)
|
||||||
|
runtime.ReadMemStats(stats)
|
||||||
|
// fmt.Println(stats.Sys)
|
||||||
|
}
|
||||||
|
|
||||||
|
func benchmarkEachNeighbourAsync(t *testing.B, max, count int, d time.Duration) {
|
||||||
|
t.ReportAllocs()
|
||||||
|
pin := randomTestAddr(keylen, 0)
|
||||||
|
n := NewPot(pin, 0)
|
||||||
|
for j := 1; j <= max; {
|
||||||
|
v := randomTestAddr(keylen, j)
|
||||||
|
_, found := n.Add(v)
|
||||||
|
if !found {
|
||||||
|
j++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
t.ResetTimer()
|
||||||
|
for i := 0; i < t.N; i++ {
|
||||||
|
val := randomTestAddr(keylen, max+1)
|
||||||
|
n.EachNeighbourAsync(val, count, keylen, func(v PotVal, po int) {
|
||||||
|
time.Sleep(d)
|
||||||
|
}, true)
|
||||||
|
}
|
||||||
|
t.StopTimer()
|
||||||
|
stats := new(runtime.MemStats)
|
||||||
|
runtime.ReadMemStats(stats)
|
||||||
|
// fmt.Println(stats.Sys)
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkEachNeighbourSync_3_1_0(t *testing.B) {
|
||||||
|
benchmarkEachNeighbourSync(t, 1000, 10, 1*time.Microsecond)
|
||||||
|
}
|
||||||
|
func BenchmarkEachNeighboursAsync_3_1_0(t *testing.B) {
|
||||||
|
benchmarkEachNeighbourAsync(t, 1000, 10, 1*time.Microsecond)
|
||||||
|
}
|
||||||
|
func BenchmarkEachNeighbourSync_3_2_0(t *testing.B) {
|
||||||
|
benchmarkEachNeighbourSync(t, 1000, 100, 1*time.Microsecond)
|
||||||
|
}
|
||||||
|
func BenchmarkEachNeighboursAsync_3_2_0(t *testing.B) {
|
||||||
|
benchmarkEachNeighbourAsync(t, 1000, 100, 1*time.Microsecond)
|
||||||
|
}
|
||||||
|
func BenchmarkEachNeighbourSync_3_3_0(t *testing.B) {
|
||||||
|
benchmarkEachNeighbourSync(t, 1000, 1000, 1*time.Microsecond)
|
||||||
|
}
|
||||||
|
func BenchmarkEachNeighboursAsync_3_3_0(t *testing.B) {
|
||||||
|
benchmarkEachNeighbourAsync(t, 1000, 1000, 1*time.Microsecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkEachNeighbourSync_3_1_1(t *testing.B) {
|
||||||
|
benchmarkEachNeighbourSync(t, 1000, 10, 2*time.Microsecond)
|
||||||
|
}
|
||||||
|
func BenchmarkEachNeighboursAsync_3_1_1(t *testing.B) {
|
||||||
|
benchmarkEachNeighbourAsync(t, 1000, 10, 2*time.Microsecond)
|
||||||
|
}
|
||||||
|
func BenchmarkEachNeighbourSync_3_2_1(t *testing.B) {
|
||||||
|
benchmarkEachNeighbourSync(t, 1000, 100, 2*time.Microsecond)
|
||||||
|
}
|
||||||
|
func BenchmarkEachNeighboursAsync_3_2_1(t *testing.B) {
|
||||||
|
benchmarkEachNeighbourAsync(t, 1000, 100, 2*time.Microsecond)
|
||||||
|
}
|
||||||
|
func BenchmarkEachNeighbourSync_3_3_1(t *testing.B) {
|
||||||
|
benchmarkEachNeighbourSync(t, 1000, 1000, 2*time.Microsecond)
|
||||||
|
}
|
||||||
|
func BenchmarkEachNeighboursAsync_3_3_1(t *testing.B) {
|
||||||
|
benchmarkEachNeighbourAsync(t, 1000, 1000, 2*time.Microsecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkEachNeighbourSync_3_1_2(t *testing.B) {
|
||||||
|
benchmarkEachNeighbourSync(t, 1000, 10, 4*time.Microsecond)
|
||||||
|
}
|
||||||
|
func BenchmarkEachNeighboursAsync_3_1_2(t *testing.B) {
|
||||||
|
benchmarkEachNeighbourAsync(t, 1000, 10, 4*time.Microsecond)
|
||||||
|
}
|
||||||
|
func BenchmarkEachNeighbourSync_3_2_2(t *testing.B) {
|
||||||
|
benchmarkEachNeighbourSync(t, 1000, 100, 4*time.Microsecond)
|
||||||
|
}
|
||||||
|
func BenchmarkEachNeighboursAsync_3_2_2(t *testing.B) {
|
||||||
|
benchmarkEachNeighbourAsync(t, 1000, 100, 4*time.Microsecond)
|
||||||
|
}
|
||||||
|
func BenchmarkEachNeighbourSync_3_3_2(t *testing.B) {
|
||||||
|
benchmarkEachNeighbourSync(t, 1000, 1000, 4*time.Microsecond)
|
||||||
|
}
|
||||||
|
func BenchmarkEachNeighboursAsync_3_3_2(t *testing.B) {
|
||||||
|
benchmarkEachNeighbourAsync(t, 1000, 1000, 4*time.Microsecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkEachNeighbourSync_3_1_3(t *testing.B) {
|
||||||
|
benchmarkEachNeighbourSync(t, 1000, 10, 8*time.Microsecond)
|
||||||
|
}
|
||||||
|
func BenchmarkEachNeighboursAsync_3_1_3(t *testing.B) {
|
||||||
|
benchmarkEachNeighbourAsync(t, 1000, 10, 8*time.Microsecond)
|
||||||
|
}
|
||||||
|
func BenchmarkEachNeighbourSync_3_2_3(t *testing.B) {
|
||||||
|
benchmarkEachNeighbourSync(t, 1000, 100, 8*time.Microsecond)
|
||||||
|
}
|
||||||
|
func BenchmarkEachNeighboursAsync_3_2_3(t *testing.B) {
|
||||||
|
benchmarkEachNeighbourAsync(t, 1000, 100, 8*time.Microsecond)
|
||||||
|
}
|
||||||
|
func BenchmarkEachNeighbourSync_3_3_3(t *testing.B) {
|
||||||
|
benchmarkEachNeighbourSync(t, 1000, 1000, 8*time.Microsecond)
|
||||||
|
}
|
||||||
|
func BenchmarkEachNeighboursAsync_3_3_3(t *testing.B) {
|
||||||
|
benchmarkEachNeighbourAsync(t, 1000, 1000, 8*time.Microsecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkEachNeighbourSync_3_1_4(t *testing.B) {
|
||||||
|
benchmarkEachNeighbourSync(t, 1000, 10, 16*time.Microsecond)
|
||||||
|
}
|
||||||
|
func BenchmarkEachNeighboursAsync_3_1_4(t *testing.B) {
|
||||||
|
benchmarkEachNeighbourAsync(t, 1000, 10, 16*time.Microsecond)
|
||||||
|
}
|
||||||
|
func BenchmarkEachNeighbourSync_3_2_4(t *testing.B) {
|
||||||
|
benchmarkEachNeighbourSync(t, 1000, 100, 16*time.Microsecond)
|
||||||
|
}
|
||||||
|
func BenchmarkEachNeighboursAsync_3_2_4(t *testing.B) {
|
||||||
|
benchmarkEachNeighbourAsync(t, 1000, 100, 16*time.Microsecond)
|
||||||
|
}
|
||||||
|
func BenchmarkEachNeighbourSync_3_3_4(t *testing.B) {
|
||||||
|
benchmarkEachNeighbourSync(t, 1000, 1000, 16*time.Microsecond)
|
||||||
|
}
|
||||||
|
func BenchmarkEachNeighboursAsync_3_3_4(t *testing.B) {
|
||||||
|
benchmarkEachNeighbourAsync(t, 1000, 1000, 16*time.Microsecond)
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue