package net.i2p.router.tunnel.pool;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import net.i2p.data.Hash;
import net.i2p.router.RouterContext;
import net.i2p.router.TunnelPoolSettings;
import static net.i2p.router.peermanager.ProfileOrganizer.Slice.*;
/**
* Pick peers randomly out of the fast pool, and put them into tunnels
* ordered by XOR distance from a random key.
*
*/
class ClientPeerSelector extends TunnelPeerSelector {
public ClientPeerSelector(RouterContext context) {
super(context);
}
public List<Hash> selectPeers(TunnelPoolSettings settings) {
int length = getLength(settings);
if (length < 0)
return null;
if ( (length == 0) && (settings.getLength()+settings.getLengthVariance() > 0) )
return null;
List<Hash> rv;
if (length > 0) {
if (shouldSelectExplicit(settings))
return selectExplicit(settings, length);
Set<Hash> exclude = getExclude(settings.isInbound(), false);
Set<Hash> matches = new HashSet<Hash>(length);
if (length == 1) {
// closest-hop restrictions
Set<Hash> moreExclude = getClosestHopExclude(settings.isInbound());
if (moreExclude != null)
exclude.addAll(moreExclude);
ctx.profileOrganizer().selectFastPeers(length, exclude, matches, 0);
matches.remove(ctx.routerHash());
rv = new ArrayList<Hash>(matches);
} else {
// build a tunnel using 4 subtiers.
// For a 2-hop tunnel, the first hop comes from subtiers 0-1 and the last from subtiers 2-3.
// For a longer tunnels, the first hop comes from subtier 0, the middle from subtiers 2-3, and the last from subtier 1.
rv = new ArrayList<Hash>(length + 1);
// OBEP or IB last hop
// group 0 or 1 if two hops, otherwise group 0
Set<Hash> firstHopExclude;
if (!settings.isInbound()) {
// exclude existing OBEPs to get some diversity ?
// closest-hop restrictions
Set<Hash> moreExclude = getClosestHopExclude(false);
if (moreExclude != null) {
moreExclude.addAll(exclude);
firstHopExclude = moreExclude;
} else {
firstHopExclude = exclude;
}
} else {
firstHopExclude = exclude;
}
ctx.profileOrganizer().selectFastPeers(1, firstHopExclude, matches, settings.getRandomKey(), length == 2 ? SLICE_0_1 : SLICE_0);
matches.remove(ctx.routerHash());
exclude.addAll(matches);
rv.addAll(matches);
matches.clear();
if (length > 2) {
// middle hop(s)
// group 2 or 3
ctx.profileOrganizer().selectFastPeers(length - 2, exclude, matches, settings.getRandomKey(), SLICE_2_3);
matches.remove(ctx.routerHash());
if (matches.size() > 1) {
// order the middle peers for tunnels >= 4 hops
List<Hash> ordered = new ArrayList<Hash>(matches);
orderPeers(ordered, settings.getRandomKey());
rv.addAll(ordered);
} else {
rv.addAll(matches);
}
exclude.addAll(matches);
matches.clear();
}
// IBGW or OB first hop
// group 2 or 3 if two hops, otherwise group 1
if (settings.isInbound()) {
// exclude existing IBGWs to get some diversity ?
// closest-hop restrictions
Set<Hash> moreExclude = getClosestHopExclude(true);
if (moreExclude != null)
exclude.addAll(moreExclude);
}
ctx.profileOrganizer().selectFastPeers(1, exclude, matches, settings.getRandomKey(), length == 2 ? SLICE_2_3 : SLICE_1);
matches.remove(ctx.routerHash());
rv.addAll(matches);
}
} else {
rv = new ArrayList<Hash>(1);
}
if (settings.isInbound())
rv.add(0, ctx.routerHash());
else
rv.add(ctx.routerHash());
return rv;
}
}