package lbms.plugins.mldht.kad.tasks; import java.util.Comparator; import java.util.NavigableSet; import java.util.concurrent.ConcurrentSkipListSet; import java.util.stream.Stream; import lbms.plugins.mldht.kad.KBucketEntry; import lbms.plugins.mldht.kad.Key; /* * We need to detect when the closest set is stable * - in principle we're done as soon as there is no request candidates */ public class ClosestSet { final NavigableSet<KBucketEntry> closest; final int targetSize; final Key target; final Comparator<Key> targetComp; int insertAttemptsSinceTailModification = 0; int insertAttemptsSinceHeadModification = 0; public ClosestSet(Key target, int targetSize) { this.target = target; targetComp = new Key.DistanceOrder(target); closest = new ConcurrentSkipListSet<>(new KBucketEntry.DistanceOrder(target)); this.targetSize = targetSize; } boolean reachedTargetCapacity() { return closest.size() >= targetSize; } void insert(KBucketEntry reply) { synchronized (this) { closest.add(reply); if (closest.size() > targetSize) { KBucketEntry last = closest.last(); closest.remove(last); if(last == reply) insertAttemptsSinceTailModification++; else insertAttemptsSinceTailModification = 0; } if(closest.first() == reply) { insertAttemptsSinceHeadModification = 0; } else { insertAttemptsSinceHeadModification++; } } } Stream<Key> ids() { return closest.stream().map(KBucketEntry::getID); } Stream<KBucketEntry> entries() { return closest.stream(); } public Key tail() { if(closest.isEmpty()) return target.distance(Key.MAX_KEY); return closest.last().getID(); } public Key head() { if(closest.isEmpty()) return target.distance(Key.MAX_KEY); return closest.first().getID(); } @Override public String toString() { String str = "closestset: " + closest.size() + " tailMod:" + insertAttemptsSinceTailModification + " headMod:" + insertAttemptsSinceHeadModification; str += " head:" + head().findApproxKeyDistance(target) + " tail:" + tail().findApproxKeyDistance(target); return str; } }