swarm: codebase split from go-ethereum (#1405)
This commit is contained in:
committed by
Anton Evangelatov
parent
7a22da98b9
commit
b046760db1
210
pot/address.go
Normal file
210
pot/address.go
Normal file
@@ -0,0 +1,210 @@
|
||||
// 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 see doc.go
|
||||
package pot
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
|
||||
var (
|
||||
zerosBin = Address{}.Bin()
|
||||
)
|
||||
|
||||
// Address is an alias for common.Hash
|
||||
type Address common.Hash
|
||||
|
||||
// NewAddressFromBytes constructs an Address from a byte slice
|
||||
func NewAddressFromBytes(b []byte) Address {
|
||||
h := common.Hash{}
|
||||
copy(h[:], b)
|
||||
return Address(h)
|
||||
}
|
||||
|
||||
func (a Address) String() string {
|
||||
return fmt.Sprintf("%x", a[:])
|
||||
}
|
||||
|
||||
// MarshalJSON Address serialisation
|
||||
func (a *Address) MarshalJSON() (out []byte, err error) {
|
||||
return []byte(`"` + a.String() + `"`), nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON Address deserialisation
|
||||
func (a *Address) UnmarshalJSON(value []byte) error {
|
||||
*a = Address(common.HexToHash(string(value[1 : len(value)-1])))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Bin returns the string form of the binary representation of an address (only first 8 bits)
|
||||
func (a Address) Bin() string {
|
||||
return ToBin(a[:])
|
||||
}
|
||||
|
||||
// ToBin converts a byteslice to the string binary representation
|
||||
func ToBin(a []byte) string {
|
||||
var bs []string
|
||||
for _, b := range a {
|
||||
bs = append(bs, fmt.Sprintf("%08b", b))
|
||||
}
|
||||
return strings.Join(bs, "")
|
||||
}
|
||||
|
||||
// Bytes returns the Address as a byte slice
|
||||
func (a Address) Bytes() []byte {
|
||||
return a[:]
|
||||
}
|
||||
|
||||
// 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 ProxCmp(a, x, y interface{}) int {
|
||||
return proxCmp(ToBytes(a), ToBytes(x), ToBytes(y))
|
||||
}
|
||||
|
||||
func proxCmp(a, x, y []byte) int {
|
||||
for i := range a {
|
||||
dx := x[i] ^ a[i]
|
||||
dy := y[i] ^ a[i]
|
||||
if dx > dy {
|
||||
return 1
|
||||
} else if dx < dy {
|
||||
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
|
||||
}
|
||||
|
||||
// RandomAddress generates a random address
|
||||
func RandomAddress() Address {
|
||||
return RandomAddressAt(Address{}, -1)
|
||||
}
|
||||
|
||||
// NewAddressFromString creates a byte slice from a string in binary representation
|
||||
func NewAddressFromString(s string) []byte {
|
||||
ha := [32]byte{}
|
||||
|
||||
t := s + 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], n)
|
||||
}
|
||||
return ha[:]
|
||||
}
|
||||
|
||||
// BytesAddress is an interface for elements addressable by a byte slice
|
||||
type BytesAddress interface {
|
||||
Address() []byte
|
||||
}
|
||||
|
||||
// ToBytes turns the Val into bytes
|
||||
func ToBytes(v Val) []byte {
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
b, ok := v.([]byte)
|
||||
if !ok {
|
||||
ba, ok := v.(BytesAddress)
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("unsupported value type %T", v))
|
||||
}
|
||||
b = ba.Address()
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// DefaultPof returns a proximity order comparison operator function
|
||||
func DefaultPof(max int) func(one, other Val, pos int) (int, bool) {
|
||||
return func(one, other Val, pos int) (int, bool) {
|
||||
po, eq := proximityOrder(ToBytes(one), ToBytes(other), pos)
|
||||
if po >= max {
|
||||
eq = true
|
||||
po = max
|
||||
}
|
||||
return po, eq
|
||||
}
|
||||
}
|
||||
|
||||
// proximityOrder returns two parameters:
|
||||
// 1. relative proximity order of the arguments one & other;
|
||||
// 2. boolean indicating whether the full match occurred (one == other).
|
||||
func proximityOrder(one, other []byte, pos int) (int, 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 (oxo>>uint8(7-j))&0x01 != 0 {
|
||||
return i*8 + j, false
|
||||
}
|
||||
}
|
||||
}
|
||||
return len(one) * 8, true
|
||||
}
|
||||
|
||||
// Label displays the node's key in binary format
|
||||
func Label(v Val) string {
|
||||
if v == nil {
|
||||
return "<nil>"
|
||||
}
|
||||
if s, ok := v.(fmt.Stringer); ok {
|
||||
return s.String()
|
||||
}
|
||||
if b, ok := v.([]byte); ok {
|
||||
return ToBin(b)
|
||||
}
|
||||
panic(fmt.Sprintf("unsupported value type %T", v))
|
||||
}
|
83
pot/doc.go
Normal file
83
pot/doc.go
Normal file
@@ -0,0 +1,83 @@
|
||||
// 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 (proximity order tree) implements a container similar to a binary tree.
|
||||
The elements are generic Val interface types.
|
||||
|
||||
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 reverse rank of order of magnitude of the MSB first X
|
||||
OR distance over finite set of integers).
|
||||
|
||||
Methods take a comparison operator (pof, proximity order function) to compare two
|
||||
value types. The default pof assumes Val to be or project to a byte slice using
|
||||
the reverse rank on the MSB first XOR logarithmic distance.
|
||||
|
||||
If the address space if limited, equality is defined as the maximum proximity order.
|
||||
|
||||
The container offers applicative (functional) 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
|
||||
concurrent retrievals.
|
||||
For the use case where the entire container is supposed to allow changes by
|
||||
concurrent routines,
|
||||
|
||||
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 synchronous iterators respecting proximity ordering wrt any item
|
||||
* provide asynchronous iterator (for parallel execution of operations) over n items
|
||||
* allows cheap iteration over ranges
|
||||
* asymmetric concurrent 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 one 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:
|
||||
* overwrite-style merge
|
||||
* intersection
|
||||
* access frequency based optimisations
|
||||
|
||||
*/
|
||||
package pot
|
787
pot/pot.go
Normal file
787
pot/pot.go
Normal file
@@ -0,0 +1,787 @@
|
||||
// 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 see doc.go
|
||||
package pot
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
)
|
||||
|
||||
const (
|
||||
maxkeylen = 256
|
||||
)
|
||||
|
||||
// Pot is the node type (same for root, branching node and leaf)
|
||||
type Pot struct {
|
||||
pin Val
|
||||
bins []*Pot
|
||||
size int
|
||||
po int
|
||||
}
|
||||
|
||||
// Val is the element type for Pots
|
||||
type Val interface{}
|
||||
|
||||
// Pof is the proximity order comparison operator function
|
||||
type Pof func(Val, Val, int) (int, bool)
|
||||
|
||||
// NewPot constructor. Requires a value of type Val to pin
|
||||
// and po to point to a span in the Val key
|
||||
// The pinned item counts towards the size
|
||||
func NewPot(v Val, po int) *Pot {
|
||||
var size int
|
||||
if v != nil {
|
||||
size++
|
||||
}
|
||||
return &Pot{
|
||||
pin: v,
|
||||
po: po,
|
||||
size: size,
|
||||
}
|
||||
}
|
||||
|
||||
// Pin returns the pinned element (key) of the Pot
|
||||
func (t *Pot) Pin() Val {
|
||||
return t.pin
|
||||
}
|
||||
|
||||
// Size returns the number of values in the Pot
|
||||
func (t *Pot) Size() int {
|
||||
if t == nil {
|
||||
return 0
|
||||
}
|
||||
return t.size
|
||||
}
|
||||
|
||||
// Add inserts a new value into the Pot and
|
||||
// returns the proximity order of v and a boolean
|
||||
// indicating if the item was found
|
||||
// Add called on (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
|
||||
func Add(t *Pot, val Val, pof Pof) (*Pot, int, bool) {
|
||||
return add(t, val, pof)
|
||||
}
|
||||
|
||||
func (t *Pot) clone() *Pot {
|
||||
return &Pot{
|
||||
pin: t.pin,
|
||||
size: t.size,
|
||||
po: t.po,
|
||||
bins: t.bins,
|
||||
}
|
||||
}
|
||||
|
||||
func add(t *Pot, val Val, pof Pof) (*Pot, int, bool) {
|
||||
var r *Pot
|
||||
if t == nil || t.pin == nil {
|
||||
r = t.clone()
|
||||
r.pin = val
|
||||
r.size++
|
||||
return r, 0, false
|
||||
}
|
||||
po, found := pof(t.pin, val, t.po)
|
||||
if found {
|
||||
r = t.clone()
|
||||
r.pin = val
|
||||
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, pof)
|
||||
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
|
||||
}
|
||||
|
||||
// Remove deletes element v from the Pot t and returns three parameters:
|
||||
// 1. new Pot that contains all the elements of t minus the element v;
|
||||
// 2. proximity order of the removed element v;
|
||||
// 3. boolean indicating whether the item was found.
|
||||
func Remove(t *Pot, v Val, pof Pof) (*Pot, int, bool) {
|
||||
return remove(t, v, pof)
|
||||
}
|
||||
|
||||
func remove(t *Pot, val Val, pof Pof) (r *Pot, po int, found bool) {
|
||||
size := t.size
|
||||
po, found = pof(t.pin, val, t.po)
|
||||
if found {
|
||||
size--
|
||||
if size == 0 {
|
||||
return &Pot{}, 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, pof)
|
||||
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: t.pin,
|
||||
size: size,
|
||||
po: t.po,
|
||||
bins: bins,
|
||||
}
|
||||
return r, po, found
|
||||
}
|
||||
|
||||
// Swap called on (k, f) looks up the item at k
|
||||
// and applies the function f to the value v at k or to nil if the item is not found
|
||||
// if f(v) returns nil, the element is removed
|
||||
// if f(v) returns v' <> v then v' is inserted into the Pot
|
||||
// if (v) == v the Pot is not changed
|
||||
// it panics if Pof(f(v), k) show that v' and v are not key-equal
|
||||
func Swap(t *Pot, k Val, pof Pof, f func(v Val) Val) (r *Pot, po int, found bool, change bool) {
|
||||
var val Val
|
||||
if t.pin == nil {
|
||||
val = f(nil)
|
||||
if val == nil {
|
||||
return nil, 0, false, false
|
||||
}
|
||||
return NewPot(val, t.po), 0, false, true
|
||||
}
|
||||
size := t.size
|
||||
po, found = pof(k, t.pin, t.po)
|
||||
if found {
|
||||
val = f(t.pin)
|
||||
// remove element
|
||||
if val == nil {
|
||||
size--
|
||||
if size == 0 {
|
||||
r = &Pot{
|
||||
po: t.po,
|
||||
}
|
||||
// return empty pot
|
||||
return r, po, true, true
|
||||
}
|
||||
// actually remove pin, by merging last bin
|
||||
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, po, true, true
|
||||
}
|
||||
// element found but no change
|
||||
if val == t.pin {
|
||||
return t, po, true, false
|
||||
}
|
||||
// actually modify the pinned element, but no change in structure
|
||||
r = t.clone()
|
||||
r.pin = val
|
||||
return r, po, true, true
|
||||
}
|
||||
|
||||
// recursive step
|
||||
var p *Pot
|
||||
n, i := t.getPos(po)
|
||||
if n != nil {
|
||||
p, po, found, change = Swap(n, k, pof, f)
|
||||
// recursive no change
|
||||
if !change {
|
||||
return t, po, found, false
|
||||
}
|
||||
// recursive change
|
||||
bins := append([]*Pot{}, t.bins[:i]...)
|
||||
if p.size == 0 {
|
||||
size--
|
||||
} else {
|
||||
size += p.size - n.size
|
||||
bins = append(bins, p)
|
||||
}
|
||||
i++
|
||||
if i < len(t.bins) {
|
||||
bins = append(bins, t.bins[i:]...)
|
||||
}
|
||||
r = t.clone()
|
||||
r.bins = bins
|
||||
r.size = size
|
||||
return r, po, found, true
|
||||
}
|
||||
// key does not exist
|
||||
val = f(nil)
|
||||
if val == nil {
|
||||
// and it should not be created
|
||||
return t, po, false, false
|
||||
}
|
||||
// otherwise check val if equal to k
|
||||
if _, eq := pof(val, k, po); !eq {
|
||||
panic("invalid value")
|
||||
}
|
||||
///
|
||||
size++
|
||||
p = &Pot{
|
||||
pin: val,
|
||||
size: 1,
|
||||
po: po,
|
||||
}
|
||||
|
||||
bins := append([]*Pot{}, t.bins[:i]...)
|
||||
bins = append(bins, p)
|
||||
if i < len(t.bins) {
|
||||
bins = append(bins, t.bins[i:]...)
|
||||
}
|
||||
r = t.clone()
|
||||
r.bins = bins
|
||||
r.size = size
|
||||
return r, po, found, true
|
||||
}
|
||||
|
||||
// Union called on (t0, t1, pof) returns the union of t0 and t1
|
||||
// calculates the union using the applicative union
|
||||
// the second return value is the number of common elements
|
||||
func Union(t0, t1 *Pot, pof Pof) (*Pot, int) {
|
||||
return union(t0, t1, pof)
|
||||
}
|
||||
|
||||
func union(t0, t1 *Pot, pof Pof) (*Pot, int) {
|
||||
if t0 == nil || t0.size == 0 {
|
||||
return t1, 0
|
||||
}
|
||||
if t1 == nil || t1.size == 0 {
|
||||
return t0, 0
|
||||
}
|
||||
var pin Val
|
||||
var bins []*Pot
|
||||
var mis []int
|
||||
wg := &sync.WaitGroup{}
|
||||
wg.Add(1)
|
||||
pin0 := t0.pin
|
||||
pin1 := t1.pin
|
||||
bins0 := t0.bins
|
||||
bins1 := t1.bins
|
||||
var i0, i1 int
|
||||
var common int
|
||||
|
||||
po, eq := pof(pin0, 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] != nil && bins0[i0].po <= po {
|
||||
n0 = bins0[i0]
|
||||
p0 = n0.po
|
||||
a0 = p0 == po
|
||||
} else {
|
||||
a0 = true
|
||||
}
|
||||
|
||||
if !a1 && i1 < l1 && bins1[i1] != nil && 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, pof)
|
||||
// }(bl, ml, n0, n1)
|
||||
bins[bl], mis[ml] = union(n0, n1, pof)
|
||||
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 = pof(pin0, pin1, n0.po)
|
||||
|
||||
}
|
||||
bins0 = bins1
|
||||
bins1 = bins2
|
||||
i0 = i1
|
||||
i1 = 0
|
||||
|
||||
}
|
||||
|
||||
wg.Done()
|
||||
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 is a synchronous iterator over the elements of pot with function f.
|
||||
func (t *Pot) Each(f func(Val) bool) bool {
|
||||
return t.each(f)
|
||||
}
|
||||
|
||||
// each is a synchronous iterator over the elements of pot with function f.
|
||||
// the iteration ends if the function return false or there are no more elements.
|
||||
func (t *Pot) each(f func(Val) bool) bool {
|
||||
if t == nil || t.size == 0 {
|
||||
return false
|
||||
}
|
||||
for _, n := range t.bins {
|
||||
if !n.each(f) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return f(t.pin)
|
||||
}
|
||||
|
||||
// eachFrom is a synchronous iterator over the elements of pot with function f,
|
||||
// starting from certain proximity order po, which is passed as a second parameter.
|
||||
// the iteration ends if the function return false or there are no more elements.
|
||||
func (t *Pot) eachFrom(f func(Val) bool, po int) bool {
|
||||
if t == nil || t.size == 0 {
|
||||
return false
|
||||
}
|
||||
_, beg := t.getPos(po)
|
||||
for i := beg; i < len(t.bins); i++ {
|
||||
if !t.bins[i].each(f) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return f(t.pin)
|
||||
}
|
||||
|
||||
// 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 Val, pof Pof, po int, f func(int, int, func(func(val Val) bool) bool) bool) {
|
||||
t.eachBin(val, pof, po, f)
|
||||
}
|
||||
|
||||
func (t *Pot) eachBin(val Val, pof Pof, po int, f func(int, int, func(func(val Val) bool) bool) bool) {
|
||||
if t == nil || t.size == 0 {
|
||||
return
|
||||
}
|
||||
spr, _ := pof(t.pin, 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) {
|
||||
if spr >= po {
|
||||
f(spr, 1, func(g func(Val) bool) bool {
|
||||
return g(t.pin)
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
n = t.bins[lim]
|
||||
|
||||
spo := spr
|
||||
if n.po == spr {
|
||||
spo++
|
||||
size += n.size
|
||||
}
|
||||
if spr >= po {
|
||||
if !f(spr, t.size-size, func(g func(Val) bool) bool {
|
||||
return t.eachFrom(func(v Val) bool {
|
||||
return g(v)
|
||||
}, spo)
|
||||
}) {
|
||||
return
|
||||
}
|
||||
}
|
||||
if n.po == spr {
|
||||
n.eachBin(val, pof, po, f)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// EachNeighbour is a synchronous 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 Val, pof Pof, f func(Val, int) bool) bool {
|
||||
return t.eachNeighbour(val, pof, f)
|
||||
}
|
||||
|
||||
func (t *Pot) eachNeighbour(val Val, pof Pof, f func(Val, 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 := pof(t.pin, val, t.po)
|
||||
if !eq {
|
||||
n, il = t.getPos(po)
|
||||
if n != nil {
|
||||
next = n.eachNeighbour(val, pof, 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 Val) 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 Val) bool {
|
||||
return f(v, n.po)
|
||||
})
|
||||
if !next {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// EachNeighbourAsync called on (val, max, maxPos, f, wait) is an asynchronous 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 Val, pof Pof, max int, maxPos int, f func(Val, int), wait bool) {
|
||||
if max > t.size {
|
||||
max = t.size
|
||||
}
|
||||
var wg *sync.WaitGroup
|
||||
if wait {
|
||||
wg = &sync.WaitGroup{}
|
||||
}
|
||||
t.eachNeighbourAsync(val, pof, max, maxPos, f, wg)
|
||||
if wait {
|
||||
wg.Wait()
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Pot) eachNeighbourAsync(val Val, pof Pof, max int, maxPos int, f func(Val, int), wg *sync.WaitGroup) (extra int) {
|
||||
l := len(t.bins)
|
||||
|
||||
po, eq := pof(t.pin, 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, pof, 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 Val) 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 Val) bool {
|
||||
if wg != nil {
|
||||
defer wg.Done()
|
||||
}
|
||||
f(v, pn.po)
|
||||
pm--
|
||||
return pm > 0
|
||||
})
|
||||
}(n, m)
|
||||
|
||||
}
|
||||
return max + extra
|
||||
}
|
||||
|
||||
// getPos called on (n) returns the forking node at PO n and its index if it exists
|
||||
// otherwise nil
|
||||
// caller is supposed 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 called on (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
|
||||
}
|
741
pot/pot_test.go
Normal file
741
pot/pot_test.go
Normal file
@@ -0,0 +1,741 @@
|
||||
// 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/ethersphere/swarm/log"
|
||||
)
|
||||
|
||||
const (
|
||||
maxEachNeighbourTests = 420
|
||||
maxEachNeighbour = 420
|
||||
maxSwap = 420
|
||||
maxSwapTests = 420
|
||||
)
|
||||
|
||||
// func init() {
|
||||
// log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(false))))
|
||||
// }
|
||||
|
||||
type testAddr struct {
|
||||
a []byte
|
||||
i int
|
||||
}
|
||||
|
||||
func newTestAddr(s string, i int) *testAddr {
|
||||
return &testAddr{NewAddressFromString(s), i}
|
||||
}
|
||||
|
||||
func (a *testAddr) Address() []byte {
|
||||
return a.a
|
||||
}
|
||||
|
||||
func (a *testAddr) String() string {
|
||||
return Label(a.a)
|
||||
}
|
||||
|
||||
func randomTestAddr(n int, i int) *testAddr {
|
||||
v := RandomAddress().Bin()[:n]
|
||||
return newTestAddr(v, i)
|
||||
}
|
||||
|
||||
func randomtestAddr(n int, i int) *testAddr {
|
||||
v := RandomAddress().Bin()[:n]
|
||||
return newTestAddr(v, i)
|
||||
}
|
||||
|
||||
func indexes(t *Pot) (i []int) {
|
||||
t.Each(func(v Val) bool {
|
||||
a := v.(*testAddr)
|
||||
i = append(i, a.i)
|
||||
return true
|
||||
})
|
||||
return i
|
||||
}
|
||||
|
||||
func testAdd(t *Pot, pof Pof, j int, values ...string) (_ *Pot, n int, f bool) {
|
||||
for i, val := range values {
|
||||
t, n, f = Add(t, newTestAddr(val, i+j), pof)
|
||||
}
|
||||
return t, n, f
|
||||
}
|
||||
|
||||
// removing non-existing element from pot
|
||||
func TestPotRemoveNonExisting(t *testing.T) {
|
||||
pof := DefaultPof(8)
|
||||
n := NewPot(newTestAddr("00111100", 0), 0)
|
||||
n, _, _ = Remove(n, newTestAddr("00000101", 0), pof)
|
||||
exp := "00111100"
|
||||
got := Label(n.Pin())
|
||||
if got[:8] != exp {
|
||||
t.Fatalf("incorrect pinned value. Expected %v, got %v", exp, got[:8])
|
||||
}
|
||||
}
|
||||
|
||||
// this test creates hierarchical pot tree, and therefore any child node will have
|
||||
// child_po = parent_po + 1.
|
||||
// then removes a node from the middle of the tree.
|
||||
func TestPotRemoveSameBin(t *testing.T) {
|
||||
pof := DefaultPof(8)
|
||||
n := NewPot(newTestAddr("11111111", 0), 0)
|
||||
n, _, _ = testAdd(n, pof, 1, "00000000", "01000000", "01100000", "01110000", "01111000")
|
||||
n, _, _ = Remove(n, newTestAddr("01110000", 0), pof)
|
||||
inds := indexes(n)
|
||||
goti := n.Size()
|
||||
expi := 5
|
||||
if goti != expi {
|
||||
t.Fatalf("incorrect number of elements in Pot. Expected %v, got %v", expi, goti)
|
||||
}
|
||||
inds = indexes(n)
|
||||
got := fmt.Sprintf("%v", inds)
|
||||
exp := "[5 3 2 1 0]"
|
||||
if got != exp {
|
||||
t.Fatalf("incorrect indexes in iteration over Pot. Expected %v, got %v", exp, got)
|
||||
}
|
||||
}
|
||||
|
||||
// this test creates a flat pot tree (all the elements are leafs of one root),
|
||||
// and therefore they all have the same po.
|
||||
// then removes an arbitrary element from the pot.
|
||||
func TestPotRemoveDifferentBins(t *testing.T) {
|
||||
pof := DefaultPof(8)
|
||||
n := NewPot(newTestAddr("11111111", 0), 0)
|
||||
n, _, _ = testAdd(n, pof, 1, "00000000", "10000000", "11000000", "11100000", "11110000")
|
||||
n, _, _ = Remove(n, newTestAddr("11100000", 0), pof)
|
||||
inds := indexes(n)
|
||||
goti := n.Size()
|
||||
expi := 5
|
||||
if goti != expi {
|
||||
t.Fatalf("incorrect number of elements in Pot. Expected %v, got %v", expi, goti)
|
||||
}
|
||||
inds = indexes(n)
|
||||
got := fmt.Sprintf("%v", inds)
|
||||
exp := "[1 2 3 5 0]"
|
||||
if got != exp {
|
||||
t.Fatalf("incorrect indexes in iteration over Pot. Expected %v, got %v", exp, got)
|
||||
}
|
||||
n, _, _ = testAdd(n, pof, 4, "11100000")
|
||||
inds = indexes(n)
|
||||
got = fmt.Sprintf("%v", inds)
|
||||
exp = "[1 2 3 4 5 0]"
|
||||
if got != exp {
|
||||
t.Fatalf("incorrect indexes in iteration over Pot. Expected %v, got %v", exp, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPotAdd(t *testing.T) {
|
||||
pof := DefaultPof(8)
|
||||
n := NewPot(newTestAddr("00111100", 0), 0)
|
||||
// Pin set correctly
|
||||
exp := "00111100"
|
||||
got := Label(n.Pin())[:8]
|
||||
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)
|
||||
}
|
||||
|
||||
n, _, _ = testAdd(n, pof, 1, "01111100", "00111100", "01111100", "00011100")
|
||||
// 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 := 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)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPotRemove(t *testing.T) {
|
||||
pof := DefaultPof(8)
|
||||
n := NewPot(newTestAddr("00111100", 0), 0)
|
||||
n, _, _ = Remove(n, newTestAddr("00111100", 0), pof)
|
||||
exp := "<nil>"
|
||||
got := Label(n.Pin())
|
||||
if got != exp {
|
||||
t.Fatalf("incorrect pinned value. Expected %v, got %v", exp, got)
|
||||
}
|
||||
n, _, _ = testAdd(n, pof, 1, "00000000", "01111100", "00111100", "00011100")
|
||||
n, _, _ = Remove(n, newTestAddr("00111100", 0), pof)
|
||||
goti := n.Size()
|
||||
expi := 3
|
||||
if goti != expi {
|
||||
t.Fatalf("incorrect number of elements in Pot. Expected %v, got %v", expi, goti)
|
||||
}
|
||||
inds := indexes(n)
|
||||
got = fmt.Sprintf("%v", inds)
|
||||
exp = "[2 4 1]"
|
||||
if got != exp {
|
||||
t.Fatalf("incorrect indexes in iteration over Pot. Expected %v, got %v", exp, got)
|
||||
}
|
||||
n, _, _ = Remove(n, newTestAddr("00111100", 0), pof) // remove again same element
|
||||
inds = indexes(n)
|
||||
got = fmt.Sprintf("%v", inds)
|
||||
if got != exp {
|
||||
t.Fatalf("incorrect indexes in iteration over Pot. Expected %v, got %v", exp, got)
|
||||
}
|
||||
n, _, _ = Remove(n, newTestAddr("00000000", 0), pof) // remove the first element
|
||||
inds = 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) {
|
||||
for i := 0; i < maxSwapTests; i++ {
|
||||
alen := maxkeylen
|
||||
pof := DefaultPof(alen)
|
||||
max := rand.Intn(maxSwap)
|
||||
|
||||
n := NewPot(nil, 0)
|
||||
var m []*testAddr
|
||||
var found bool
|
||||
for j := 0; j < 2*max; {
|
||||
v := randomtestAddr(alen, j)
|
||||
n, _, found = Add(n, v, pof)
|
||||
if !found {
|
||||
m = append(m, v)
|
||||
j++
|
||||
}
|
||||
}
|
||||
k := make(map[string]*testAddr)
|
||||
for j := 0; j < max; {
|
||||
v := randomtestAddr(alen, 1)
|
||||
_, found := k[Label(v)]
|
||||
if !found {
|
||||
k[Label(v)] = v
|
||||
j++
|
||||
}
|
||||
}
|
||||
for _, v := range k {
|
||||
m = append(m, v)
|
||||
}
|
||||
f := func(v Val) Val {
|
||||
tv := v.(*testAddr)
|
||||
if tv.i < max {
|
||||
return nil
|
||||
}
|
||||
tv.i = 0
|
||||
return v
|
||||
}
|
||||
for _, val := range m {
|
||||
n, _, _, _ = Swap(n, val, pof, func(v Val) Val {
|
||||
if v == nil {
|
||||
return val
|
||||
}
|
||||
return f(v)
|
||||
})
|
||||
}
|
||||
sum := 0
|
||||
n.Each(func(v Val) bool {
|
||||
if v == nil {
|
||||
return true
|
||||
}
|
||||
sum++
|
||||
tv := v.(*testAddr)
|
||||
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 Val, pof Pof) func(Val, int) error {
|
||||
return func(v Val, po int) error {
|
||||
// check the po
|
||||
exp, _ := pof(val, 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 Val) func(Val, int) error {
|
||||
po := maxkeylen
|
||||
return func(v Val, 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 Val) func(Val, int) error {
|
||||
return func(v Val, po int) error {
|
||||
duplicate, ok := m[Label(v)]
|
||||
if !ok {
|
||||
return fmt.Errorf("alien value %v", v)
|
||||
}
|
||||
if duplicate {
|
||||
return fmt.Errorf("duplicate value returned: %v", v)
|
||||
}
|
||||
m[Label(v)] = true
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
var errNoCount = errors.New("not count")
|
||||
|
||||
func testPotEachNeighbour(n *Pot, pof Pof, val Val, expCount int, fs ...func(Val, int) error) error {
|
||||
var err error
|
||||
var count int
|
||||
n.EachNeighbour(val, pof, func(v Val, po int) bool {
|
||||
for _, f := range fs {
|
||||
err = f(v, po)
|
||||
if err != nil {
|
||||
return err.Error() == errNoCount.Error()
|
||||
}
|
||||
}
|
||||
count++
|
||||
return count != expCount
|
||||
})
|
||||
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 TestPotMergeCommon(t *testing.T) {
|
||||
vs := make([]*testAddr, mergeTestCount)
|
||||
for i := 0; i < maxEachNeighbourTests; i++ {
|
||||
alen := maxkeylen
|
||||
pof := DefaultPof(alen)
|
||||
|
||||
for j := 0; j < len(vs); j++ {
|
||||
vs[j] = randomtestAddr(alen, j)
|
||||
}
|
||||
max0 := rand.Intn(mergeTestChoose) + 1
|
||||
max1 := rand.Intn(mergeTestChoose) + 1
|
||||
n0 := NewPot(nil, 0)
|
||||
n1 := NewPot(nil, 0)
|
||||
log.Trace(fmt.Sprintf("round %v: %v - %v", i, max0, max1))
|
||||
m := make(map[string]bool)
|
||||
var found bool
|
||||
for j := 0; j < max0; {
|
||||
r := rand.Intn(max0)
|
||||
v := vs[r]
|
||||
n0, _, found = Add(n0, v, pof)
|
||||
if !found {
|
||||
m[Label(v)] = false
|
||||
j++
|
||||
}
|
||||
}
|
||||
expAdded := 0
|
||||
|
||||
for j := 0; j < max1; {
|
||||
r := rand.Intn(max1)
|
||||
v := vs[r]
|
||||
n1, _, found = Add(n1, v, pof)
|
||||
if !found {
|
||||
j++
|
||||
}
|
||||
_, found = m[Label(v)]
|
||||
if !found {
|
||||
expAdded++
|
||||
m[Label(v)] = false
|
||||
}
|
||||
}
|
||||
if i < 6 {
|
||||
continue
|
||||
}
|
||||
expSize := len(m)
|
||||
log.Trace(fmt.Sprintf("%v-0: pin: %v, size: %v", i, n0.Pin(), max0))
|
||||
log.Trace(fmt.Sprintf("%v-1: pin: %v, size: %v", i, n1.Pin(), max1))
|
||||
log.Trace(fmt.Sprintf("%v: merged tree size: %v, newly added: %v", i, expSize, expAdded))
|
||||
n, common := Union(n0, n1, pof)
|
||||
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) {
|
||||
t.Fatalf("%v: merged pot contains duplicates: \n%v", i, n)
|
||||
}
|
||||
for k := range m {
|
||||
_, _, found = Add(n, newTestAddr(k, 0), pof)
|
||||
if !found {
|
||||
t.Fatalf("%v: merged pot (size:%v, added: %v) missing element %v", i, size, added, k)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPotMergeScale(t *testing.T) {
|
||||
for i := 0; i < maxEachNeighbourTests; i++ {
|
||||
alen := maxkeylen
|
||||
pof := DefaultPof(alen)
|
||||
max0 := rand.Intn(maxEachNeighbour) + 1
|
||||
max1 := rand.Intn(maxEachNeighbour) + 1
|
||||
n0 := NewPot(nil, 0)
|
||||
n1 := NewPot(nil, 0)
|
||||
log.Trace(fmt.Sprintf("round %v: %v - %v", i, max0, max1))
|
||||
m := make(map[string]bool)
|
||||
var found bool
|
||||
for j := 0; j < max0; {
|
||||
v := randomtestAddr(alen, j)
|
||||
n0, _, found = Add(n0, v, pof)
|
||||
if !found {
|
||||
m[Label(v)] = false
|
||||
j++
|
||||
}
|
||||
}
|
||||
expAdded := 0
|
||||
|
||||
for j := 0; j < max1; {
|
||||
v := randomtestAddr(alen, j)
|
||||
n1, _, found = Add(n1, v, pof)
|
||||
if !found {
|
||||
j++
|
||||
}
|
||||
_, found = m[Label(v)]
|
||||
if !found {
|
||||
expAdded++
|
||||
m[Label(v)] = false
|
||||
}
|
||||
}
|
||||
if i < 6 {
|
||||
continue
|
||||
}
|
||||
expSize := len(m)
|
||||
log.Trace(fmt.Sprintf("%v-0: pin: %v, size: %v", i, n0.Pin(), max0))
|
||||
log.Trace(fmt.Sprintf("%v-1: pin: %v, size: %v", i, n1.Pin(), max1))
|
||||
log.Trace(fmt.Sprintf("%v: merged tree size: %v, newly added: %v", i, expSize, expAdded))
|
||||
n, common := Union(n0, n1, pof)
|
||||
added := n1.Size() - common
|
||||
size := n.Size()
|
||||
|
||||
if expSize != size {
|
||||
t.Fatalf("%v: incorrect number of elements in merged pot, expected %v, got %v", i, expSize, size)
|
||||
}
|
||||
if expAdded != added {
|
||||
t.Fatalf("%v: incorrect number of added elements in merged pot, expected %v, got %v", i, expAdded, added)
|
||||
}
|
||||
if !checkDuplicates(n) {
|
||||
t.Fatalf("%v: merged pot contains duplicates", i)
|
||||
}
|
||||
for k := range m {
|
||||
_, _, found = Add(n, newTestAddr(k, 0), pof)
|
||||
if !found {
|
||||
t.Fatalf("%v: merged pot (size:%v, added: %v) missing element %v", i, size, added, k)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func checkDuplicates(t *Pot) bool {
|
||||
po := -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++ {
|
||||
alen := maxkeylen
|
||||
pof := DefaultPof(maxkeylen)
|
||||
max := rand.Intn(maxEachNeighbour/2) + maxEachNeighbour/2
|
||||
pin := randomTestAddr(alen, 0)
|
||||
n := NewPot(pin, 0)
|
||||
m := make(map[string]bool)
|
||||
m[Label(pin)] = false
|
||||
for j := 1; j <= max; j++ {
|
||||
v := randomTestAddr(alen, j)
|
||||
n, _, _ = Add(n, v, pof)
|
||||
m[Label(v)] = false
|
||||
}
|
||||
|
||||
size := n.Size()
|
||||
if size < 2 {
|
||||
continue
|
||||
}
|
||||
count := rand.Intn(size/2) + size/2
|
||||
val := randomTestAddr(alen, max+1)
|
||||
log.Trace(fmt.Sprintf("%v: pin: %v, size: %v, val: %v, count: %v", i, n.Pin(), size, val, count))
|
||||
err := testPotEachNeighbour(n, pof, val, count, checkPo(val, pof), checkOrder(val), checkValues(m, val))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
minPoFound := alen
|
||||
maxPoNotFound := 0
|
||||
for k, found := range m {
|
||||
po, _ := pof(val, 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
|
||||
alen := maxkeylen
|
||||
pof := DefaultPof(alen)
|
||||
n := NewPot(randomTestAddr(alen, 0), 0)
|
||||
size := 1
|
||||
var found bool
|
||||
for j := 1; j <= max; j++ {
|
||||
v := randomTestAddr(alen, j)
|
||||
n, _, found = Add(n, v, pof)
|
||||
if !found {
|
||||
size++
|
||||
}
|
||||
}
|
||||
if size != n.Size() {
|
||||
t.Fatal(n)
|
||||
}
|
||||
if size < 2 {
|
||||
continue
|
||||
}
|
||||
count := rand.Intn(size/2) + size/2
|
||||
val := randomTestAddr(alen, max+1)
|
||||
|
||||
mu := sync.Mutex{}
|
||||
m := make(map[string]bool)
|
||||
maxPos := rand.Intn(alen)
|
||||
log.Trace(fmt.Sprintf("%v: pin: %v, size: %v, val: %v, count: %v, maxPos: %v", i, n.Pin(), size, val, count, maxPos))
|
||||
msize := 0
|
||||
remember := func(v Val, po int) error {
|
||||
if po > maxPos {
|
||||
return errNoCount
|
||||
}
|
||||
m[Label(v)] = true
|
||||
msize++
|
||||
return nil
|
||||
}
|
||||
if i == 0 {
|
||||
continue
|
||||
}
|
||||
testPotEachNeighbour(n, pof, val, count, remember)
|
||||
d := 0
|
||||
forget := func(v Val, po int) {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
d++
|
||||
delete(m, Label(v))
|
||||
}
|
||||
|
||||
n.EachNeighbourAsync(val, pof, 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()
|
||||
alen := maxkeylen
|
||||
pof := DefaultPof(alen)
|
||||
pin := randomTestAddr(alen, 0)
|
||||
n := NewPot(pin, 0)
|
||||
var found bool
|
||||
for j := 1; j <= max; {
|
||||
v := randomTestAddr(alen, j)
|
||||
n, _, found = Add(n, v, pof)
|
||||
if !found {
|
||||
j++
|
||||
}
|
||||
}
|
||||
t.ResetTimer()
|
||||
for i := 0; i < t.N; i++ {
|
||||
val := randomTestAddr(alen, max+1)
|
||||
m := 0
|
||||
n.EachNeighbour(val, pof, func(v Val, po int) bool {
|
||||
time.Sleep(d)
|
||||
m++
|
||||
return m != count
|
||||
})
|
||||
}
|
||||
t.StopTimer()
|
||||
stats := new(runtime.MemStats)
|
||||
runtime.ReadMemStats(stats)
|
||||
}
|
||||
|
||||
func benchmarkEachNeighbourAsync(t *testing.B, max, count int, d time.Duration) {
|
||||
t.ReportAllocs()
|
||||
alen := maxkeylen
|
||||
pof := DefaultPof(alen)
|
||||
pin := randomTestAddr(alen, 0)
|
||||
n := NewPot(pin, 0)
|
||||
var found bool
|
||||
for j := 1; j <= max; {
|
||||
v := randomTestAddr(alen, j)
|
||||
n, _, found = Add(n, v, pof)
|
||||
if !found {
|
||||
j++
|
||||
}
|
||||
}
|
||||
t.ResetTimer()
|
||||
for i := 0; i < t.N; i++ {
|
||||
val := randomTestAddr(alen, max+1)
|
||||
n.EachNeighbourAsync(val, pof, count, alen, func(v Val, po int) {
|
||||
time.Sleep(d)
|
||||
}, true)
|
||||
}
|
||||
t.StopTimer()
|
||||
stats := new(runtime.MemStats)
|
||||
runtime.ReadMemStats(stats)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
Reference in New Issue
Block a user