func (tab *STable) bench(chainId common.ChainID) *bench {
	if b, ok := tab.benches.Load(chainId); ok {
		return b.(*bench)
	}
	return nil
}
bench returns the bench for the given node ID hash.
package discover
import (
	"bytes"
	crand "crypto/rand"
	"encoding/binary"
	"fmt"
	mrand "math/rand"
	"net"
	"sync"
	"time"
	"github.com/ThinkiumGroup/go-common"
	"github.com/ThinkiumGroup/go-common/log"
	"github.com/ThinkiumGroup/go-thinkium/config"
)
const MaxPeersPerChain = 10
const benchSize = 128
type bench struct {
	seats []*Node
	ips   DistinctNetSet
}
func (b *bench) bump(n *Node) bool {
	if b.seats == nil {
		n.addedAt = time.Now()
		b.seats = []*Node{n}
		return true
	}
	for i := range b.seats {
		if b.seats[i].ID == n.ID {
			copy(b.seats[1:], b.seats[:i])
			b.seats[0] = n
			return true
		}
	}
	return false
}
type STable struct {
	mutex      sync.Mutex  
	chainId    common.ChainID
	bootId     common.ChainID
	netType    common.NetType
	dataNodes  []*ChainDataNodes
	tmpNodes   []*ChainDataNodes  
	benches    sync.Map           
	nursery    []*Node            
	rand       *mrand.Rand        
	ips        DistinctNetSet
	db         *nodeDB  
	refreshReq chan chan struct{}
	initDone   chan struct{}
	closeReq   chan struct{}
	closed     chan struct{}
	discv Discovery
	self  *Node  
}
func newSTable(d Discovery, self *Node, cfg UDPConfig) (*STable, error) {
	db, err := newNodeDB(cfg.NodeDBPath, nodeDBVersion, self.ID)
	if err != nil {
		return nil, err
	}
	tab := &STable{
		chainId:    cfg.ChainID,
		bootId:     cfg.BootId,
		netType:    cfg.NetType,
		dataNodes:  cfg.ChainDataNodes,
		discv:      d,
		self:       self,
		db:         db,
		refreshReq: make(chan chan struct{}),
		initDone:   make(chan struct{}),
		closeReq:   make(chan struct{}),
		closed:     make(chan struct{}),
		rand:       mrand.New(mrand.NewSource(0)),
		ips:        DistinctNetSet{Subnet: tableSubnet, Limit: tableIPLimit},
	}
	if err := tab.setFallbackNodes(cfg.Bootnodes); err != nil {
		return nil, err
	}
	tab.seedRand()
	tab.loadSeedNodes()
	return tab, nil
}
func (tab *STable) seedRand() {
	var b [8]byte
	crand.Read(b[:])
	tab.mutex.Lock()
	tab.rand.Seed(int64(binary.BigEndian.Uint64(b[:])))
	tab.mutex.Unlock()
}
func (tab *STable) Self() *Node {
	return tab.self
}
func (tab *STable) ReadRandomNodes(buf []*Node) (n int) {
	if !tab.isInitDone() {
		return 0
	}
	tab.mutex.Lock()
	defer tab.mutex.Unlock()
	cids := GetVisitChainIds(tab.dataNodes, tab.chainId)
	if cids == nil {
		return 0
	}
	var buckets [][]*Node
	for _, cid := range cids {
		val, ok := tab.benches.Load(cid)
		if !ok {
			continue
		}
		bench := val.(*bench)
		if len(bench.seats) > 0 {
			buckets = append(buckets, bench.seats)
		}
	}
	if len(buckets) == 0 {
		return 0
	}
	for i := len(buckets) - 1; i > 0; i-- {
		j := tab.rand.Intn(len(buckets))
		buckets[i], buckets[j] = buckets[j], buckets[i]
	}
	var i, j int
	for ; i < len(buf); i, j = i+1, (j+1)%len(buckets) {
		b := buckets[j]
		buf[i] = &(*b[0])
		buckets[j] = b[1:]
		if len(b) == 1 {
			buckets = append(buckets[:j], buckets[j+1:]...)
		}
		if len(buckets) == 0 {
			break
		}
	}
	return i + 1
}
func (tab *STable) Close() {
	select {
	case <-tab.closed:
	case tab.closeReq <- struct{}{}:
		<-tab.closed  
	}
}
func (tab *STable) setFallbackNodes(nodes []*Node) error {
	for _, n := range nodes {
		if err := n.validateComplete(); err != nil {
			return fmt.Errorf("bad bootstrap/fallback node %q (%v)", n, err)
		}
	}
	tab.nursery = make([]*Node, 0, len(nodes))
	for _, n := range nodes {
		cpy := *n
		cpy.Hash = common.Hash256(n.ID[:])
		tab.nursery = append(tab.nursery, &cpy)
	}
	return nil
}
func (tab *STable) isInitDone() bool {
	select {
	case <-tab.initDone:
		return true
	default:
		return false
	}
}
func (tab *STable) Resolve(targetID common.NodeID) *Node {
	var node *Node
	tab.benches.Range(func(key, value interface{}) bool {
		b := value.(*bench)
		for _, n := range b.seats {
			if bytes.Equal(n.ID[:], targetID[:]) {
				node = n
				return false
			}
		}
		return true
	})
	if node == nil {
		node = tab.Find(targetID)
	}
	return node
}
func (tab *STable) Find(target common.NodeID) *Node {
	var (
		asked          = make(map[common.NodeID]bool)
		reply          = make(chan map[common.ChainID][]*Node, MaxPeersPerChain)
		pendingQueries = 0
		result         map[common.ChainID][]*Node
	)
	asked[tab.self.ID] = true
	result = tab.benchRow(MaxPeersPerChain)
	for {
		for _, ns := range result {
			pendingQueries = 0
			for i := 0; i < len(ns) && pendingQueries < MaxPeersPerChain; i++ {
				if ns[i] == nil {
					continue
				}
				if !asked[ns[i].ID] {
					asked[ns[i].ID] = true
					pendingQueries++
					go tab.findnode(ns[i], reply)
				}
			}
			if pendingQueries == MaxPeersPerChain {
				break
			}
		}
		if pendingQueries == 0 {
			break
		}
		for _, ns := range <-reply {
			for _, n := range ns {
				if bytes.Equal(n.ID[:], target[:]) {
					return n
				}
			}
		}
		pendingQueries--
	}
	return nil
}
func (tab *STable) Lookup(target interface{}) []*Node {
	return tab.lookup(true)
}
func (tab *STable) GetDataNodes() []*ChainDataNodes {
	tab.mutex.Lock()
	defer tab.mutex.Unlock()
	return tab.dataNodes
}
func (tab *STable) GetAccessChains() common.ChainIDs {
	tab.mutex.Lock()
	defer tab.mutex.Unlock()
	return GetConnectedChains(tab.dataNodes, tab.chainId)
}
func (tab *STable) SetTmpNodes(dataNodes []*ChainDataNodes) {
	tab.mutex.Lock()
	defer tab.mutex.Unlock()
	if dataNodes == nil || tab.tmpNodes != nil {
		return
	}
	h1, err := common.HashObject(tab.dataNodes)
	if err != nil {
		log.Errorf("P2P chain %d net %s hash DataNodes error %v", tab.chainId, tab.netType, err)
		return
	}
	h2, err := common.HashObject(dataNodes)
	if err != nil {
		log.Errorf("P2P chain %d net %s hash tmpNodes error %v", tab.chainId, tab.netType, err)
		return
	}
	if bytes.Equal(h1, h2) {
		if config.IsLogOn(config.NetDebugLog) {
			log.Debugf("P2P chain %d net %s ignore unchanged chains", tab.chainId, tab.netType)
		}
		return
	}
	tab.tmpNodes = dataNodes
}
func (tab *STable) SwitchToTmpNodes() {
	tab.mutex.Lock()
	defer tab.mutex.Unlock()
	if tab.tmpNodes == nil {
		return
	}
	tab.dataNodes = tab.tmpNodes
	tab.tmpNodes = nil
}
func (tab *STable) lookup(refreshIfEmpty bool) []*Node {
	var (
		asked          = make(map[common.NodeID]bool)
		seen           = make(map[common.NodeID]bool)
		reply          = make(chan map[common.ChainID][]*Node, MaxPeersPerChain)
		pendingQueries = 0
		result         map[common.ChainID][]*Node
		ret            []*Node
	)
	asked[tab.self.ID] = true
	for {
		result = tab.benchRow(MaxPeersPerChain)
		if len(result) > 0 || !refreshIfEmpty {
			break
		}
		<-tab.refresh()
		refreshIfEmpty = false
	}
	chains := GetVisitChainIds(tab.dataNodes, tab.chainId)
	tmpChains := GetVisitChainIds(tab.tmpNodes, tab.chainId)
	if config.IsLogOn(config.NetDebugLog) {
		log.Debugf("P2P SORT GetVisitChainIds for chain %d net %s return chains %s tmpChains %s", tab.chainId, tab.netType, chains, tmpChains)
	}
	for {
		for _, ns := range result {
			for i := 0; i < len(ns) && pendingQueries < MaxPeersPerChain; i++ {
				n := ns[i]
				if n == nil {
					continue
				}
				if !asked[n.ID] {
					asked[n.ID] = true
					pendingQueries++
					go tab.findnode(n, reply)
				}
			}
			if pendingQueries == MaxPeersPerChain {
				break
			}
		}
		if pendingQueries == 0 {
			break
		}
		for cid, ns := range <-reply {
			for _, n := range ns {
				if n != nil && !seen[n.ID] {
					seen[n.ID] = true
					if IsIn(chains, cid) || IsIn(tmpChains, cid) {
						ret = append(ret, n)
					}
				}
			}
		}
		pendingQueries--
	}
	return ret
}
func (tab *STable) findnode(n *Node, reply chan<- map[common.ChainID][]*Node) {
	if tab == nil || n == nil {
		return
	}
	fails := tab.db.findFails(n.ID)
	r, err := tab.discv.FindNode(n.ID, n.UdpAddr(), nil)
	if err == errEmptyTable {
		tab.Close()
		return
	}
	if err != nil || len(r) == 0 {
		fails++
		tab.db.updateFindFails(n.ID, fails)
		if fails >= maxFindnodeFailures {
			tab.delete(tab.chainId, n)
		}
	} else if fails > 0 {
		tab.db.updateFindFails(n.ID, fails-1)
	}
	for c, ns := range r {
		for _, nd := range ns {
			tab.add(c, nd)
		}
	}
	reply <- r
}
func (tab *STable) refresh() <-chan struct{} {
	done := make(chan struct{})
	select {
	case tab.refreshReq <- done:
	case <-tab.closed:
		close(done)
	}
	return done
}
func (tab *STable) loop() {
	var (
		revalidate     = time.NewTimer(tab.nextRevalidateTime())
		refresh        = time.NewTicker(refreshInterval)
		copyNodes      = time.NewTicker(copyNodesInterval)
		revalidateDone = make(chan struct{})
		refreshDone    = make(chan struct{})            
		waiting        = []chan struct{}{tab.initDone}  
	)
	defer refresh.Stop()
	defer revalidate.Stop()
	defer copyNodes.Stop()
	go tab.doRefresh(refreshDone)
loop:
	for {
		select {
		case <-refresh.C:
			tab.seedRand()
			if refreshDone == nil {
				refreshDone = make(chan struct{})
				go tab.doRefresh(refreshDone)
			}
		case req := <-tab.refreshReq:
			waiting = append(waiting, req)
			if refreshDone == nil {
				refreshDone = make(chan struct{})
				go tab.doRefresh(refreshDone)
			}
		case <-refreshDone:
			for _, ch := range waiting {
				close(ch)
			}
			waiting, refreshDone = nil, nil
		case <-revalidate.C:
			go tab.doRevalidate(revalidateDone)
		case <-revalidateDone:
			revalidate.Reset(tab.nextRevalidateTime())
		case <-copyNodes.C:
			go tab.copyLiveNodes()
		case <-tab.closeReq:
			break loop
		}
	}
	if refreshDone != nil {
		<-refreshDone
	}
	for _, ch := range waiting {
		close(ch)
	}
	tab.db.close()
	close(tab.closed)
}
func (tab *STable) doRefresh(done chan struct{}) {
	defer close(done)
	tab.loadSeedNodes()
	tab.lookup(false)
}
func (tab *STable) loadSeedNodes() {
	var seeds []*Node
	seeds = append(seeds, tab.nursery...)
	for i := range seeds {
		seed := seeds[i]
		tab.add(tab.bootId, seed)
	}
	if config.IsLogOn(config.NetDebugLog) {
		log.Debugf("P2P SORT UDP ChainId %d bootId %d loadSeedNodes %s", tab.chainId, tab.bootId, seeds)
	}
}
func (tab *STable) doRevalidate(done chan<- struct{}) {
	defer func() { done <- struct{}{} }()
	lasts := tab.nodeToRevalidate()
	if len(lasts) == 0 {
		return
	}
	for c, last := range lasts {
		err := tab.discv.Ping(last.ID, last.UdpAddr())
		tab.mutex.Lock()
		b := tab.bench(c)
		if b == nil {
			tab.mutex.Unlock()
			continue
		}
		if err == nil {
			b.bump(last)
			tab.mutex.Unlock()
			continue
		}
		if r := tab.replace(b, last); r != nil {
			if config.IsLogOn(config.NetDebugLog) {
				log.Debugf("P2P Removed dead node chain %d id %s ip %s r %s rip %s", c, last.ID, last.IP, r.ID, r.IP)
			}
		} else {
			if config.IsLogOn(config.NetDebugLog) {
				log.Debugf("P2P Replaced dead node chain %d id %s ip %s", c, last.ID, last.IP)
			}
		}
		tab.mutex.Unlock()
	}
}
func (tab *STable) nodeToRevalidate() map[common.ChainID]*Node {
	tab.mutex.Lock()
	defer tab.mutex.Unlock()
	ret := make(map[common.ChainID]*Node)
	tab.benches.Range(func(key, value interface{}) bool {
		cid := key.(common.ChainID)
		bench := value.(*bench)
		if bench == nil || len(bench.seats) == 0 {
			return true
		}
		ret[cid] = bench.seats[len(bench.seats)-1]
		return true
	})
	return ret
}
func (tab *STable) nextRevalidateTime() time.Duration {
	tab.mutex.Lock()
	defer tab.mutex.Unlock()
	return time.Duration(tab.rand.Int63n(int64(revalidateInterval)))
}
func (tab *STable) copyLiveNodes() {
}
func (tab *STable) benchRow(n int) map[common.ChainID][]*Node {
	tab.mutex.Lock()
	defer tab.mutex.Unlock()
	ret := make(map[common.ChainID][]*Node)
	tab.benches.Range(func(key, value interface{}) bool {
		b := value.(*bench)
		if len(b.seats) > 0 {
			c := key.(common.ChainID)
			if n > len(b.seats) {
				ret[c] = b.seats
			} else {
				ret[c] = b.seats[:n]
			}
		}
		return true
	})
	return ret
}
func (tab *STable) Len() (n int) {
	return tab.len()
}
func (tab *STable) len() (n int) {
	tab.benches.Range(func(key, value interface{}) bool {
		b := value.(*bench)
		n += len(b.seats)
		return true
	})
	return n
}