aboutsummaryrefslogtreecommitdiffstats
path: root/swarm/pot
diff options
context:
space:
mode:
authorethersphere <thesw@rm.eth>2018-06-20 20:06:27 +0800
committerethersphere <thesw@rm.eth>2018-06-22 03:10:31 +0800
commite187711c6545487d4cac3701f0f506bb536234e2 (patch)
treed2f6150f70b84b36e49a449082aeda267b4b9046 /swarm/pot
parent574378edb50c907b532946a1d4654dbd6701b20a (diff)
downloaddexon-e187711c6545487d4cac3701f0f506bb536234e2.tar
dexon-e187711c6545487d4cac3701f0f506bb536234e2.tar.gz
dexon-e187711c6545487d4cac3701f0f506bb536234e2.tar.bz2
dexon-e187711c6545487d4cac3701f0f506bb536234e2.tar.lz
dexon-e187711c6545487d4cac3701f0f506bb536234e2.tar.xz
dexon-e187711c6545487d4cac3701f0f506bb536234e2.tar.zst
dexon-e187711c6545487d4cac3701f0f506bb536234e2.zip
swarm: network rewrite merge
Diffstat (limited to 'swarm/pot')
-rw-r--r--swarm/pot/address.go252
-rw-r--r--swarm/pot/doc.go83
-rw-r--r--swarm/pot/pot.go807
-rw-r--r--swarm/pot/pot_test.go685
4 files changed, 1827 insertions, 0 deletions
diff --git a/swarm/pot/address.go b/swarm/pot/address.go
new file mode 100644
index 000000000..3974ebcaa
--- /dev/null
+++ b/swarm/pot/address.go
@@ -0,0 +1,252 @@
+// 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) IsZero() bool {
+ return a.Bin() == zerosBin
+}
+
+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[:]
+}
+
+/*
+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 index >= 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 (oxo>>uint8(7-j))&0x01 != 0 {
+ return i*8 + j, false
+ }
+ }
+ }
+ return len(one) * 8, true
+}
+
+// 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
+// where all
+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
+ }
+}
+
+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))
+}
diff --git a/swarm/pot/doc.go b/swarm/pot/doc.go
new file mode 100644
index 000000000..4c0a03065
--- /dev/null
+++ b/swarm/pot/doc.go
@@ -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 disctance.
+
+If the address space if limited, equality is defined as the maximum proximity order.
+
+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
+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
diff --git a/swarm/pot/pot.go b/swarm/pot/pot.go
new file mode 100644
index 000000000..dfda84804
--- /dev/null
+++ b/swarm/pot/pot.go
@@ -0,0 +1,807 @@
+// 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 called on (v) deletes v from the Pot and returns
+// the proximity order of v and a boolean value indicating
+// if the value was found
+// Remove called on (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
+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 {
+ 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, 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: val,
+ 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 called with (f) is a synchronous iterator over the bins of a node
+// respecting an ordering
+// proximity > pinnedness
+func (t *Pot) Each(f func(Val, int) bool) bool {
+ return t.each(f)
+}
+
+func (t *Pot) each(f func(Val, int) bool) bool {
+ var next bool
+ for _, n := range t.bins {
+ if n == nil {
+ return true
+ }
+ next = n.each(f)
+ if !next {
+ return false
+ }
+ }
+ if t.size == 0 {
+ return false
+ }
+ return f(t.pin, t.po)
+}
+
+// EachFrom called with (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(Val, int) bool, po int) bool {
+ return t.eachFrom(f, po)
+}
+
+func (t *Pot) eachFrom(f func(Val, 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 Val, pof Pof, po int, f func(int, int, func(func(val Val, i int) 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, i int) 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, int) bool) bool {
+ return g(t.pin, spr)
+ })
+ }
+ 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, int) bool) bool {
+ return t.eachFrom(func(v Val, j int) bool {
+ return g(v, spr)
+ }, 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, _ 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 Val, _ int) 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, _ 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 Val, _ int) 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
+}
diff --git a/swarm/pot/pot_test.go b/swarm/pot/pot_test.go
new file mode 100644
index 000000000..aeb23dfc6
--- /dev/null
+++ b/swarm/pot/pot_test.go
@@ -0,0 +1,685 @@
+// 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/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, po []int) {
+ t.Each(func(v Val, p int) bool {
+ a := v.(*testAddr)
+ i = append(i, a.i)
+ po = append(po, p)
+ return true
+ })
+ return i, po
+}
+
+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
+}
+
+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, 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 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, 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(n, newTestAddr("00111100", 0), pof)
+ 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, i int) 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)
+}