package p2pp; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import org.klomp.snark.Peer; public class LiveBTTable { private final double SIGMA = 1; private final double RHO = 0.1; private List<RequestTriple> triples = new ArrayList<RequestTriple>(); private Map<Peer, List<RequestTriple>> downloadQueues = new HashMap<Peer, List<RequestTriple>>(); private Map<Integer, List<RequestTriple>> blockQueues = new HashMap<Integer, List<RequestTriple>>(); private Map<Peer, Integer> queueLengths = new HashMap<Peer, Integer>(); private int pieceSize; private double bitrate; // pieces per second public LiveBTTable(int pieceSize, int bitrate) { this.pieceSize = pieceSize; // We get the bitrate in bits per second. this.bitrate = (((double) bitrate) / 8.0) / ((double) pieceSize); } public Set<Integer> getRequestedPieces() { return blockQueues.keySet(); } public boolean isQueueFull(Peer peer) { List<RequestTriple> queue = downloadQueues.get(peer); int size = queueLengths.containsKey(peer) ? queueLengths.get(peer) : 1; if(queue == null) return false; else return queue.size() >= size; } public int size() { return triples.size(); } public boolean isRequested(int piece) { List<RequestTriple> queue = blockQueues.get(piece); return queue != null && !queue.isEmpty(); } public int getFirstInQueue(Peer peer) { List<RequestTriple> queue = downloadQueues.get(peer); if(queue == null || queue.isEmpty()) return LiveBTPeerCoordinator.NO_PIECE; return queue.get(0).piece; } public boolean canMeetDeadline(int piece) { return estimatedEarliestBlockFinish(piece) > (piece * bitrate); } public int estimatedDownloadFinish(Peer peer, double speed) { if(speed == 0) return Integer.MAX_VALUE; return (int) (estimatedAllDownloadsFinish(peer, speed) + (pieceSize / speed)); } public int estimatedAllDownloadsFinish(Peer peer, double speed) { // Tf List<RequestTriple> queue = downloadQueues.get(peer); if(queue == null || queue.isEmpty()) return 0; else if(speed == 0) return Integer.MAX_VALUE; return (int) (pieceSize * queue.size() / speed); //return queue.get(queue.size() - 1).getTime(); } public int estimatedEarliestBlockFinish(int piece) { //Tb List<RequestTriple> queue = blockQueues.get(piece); if(queue == null || queue.isEmpty()) return Integer.MAX_VALUE; return queue.get(0).time; } public void updateQueueLength(Peer peer, double oldSpeed, double newSpeed) { Integer cap = queueLengths.get(peer); if(cap == null) cap = 1; if(newSpeed > oldSpeed) cap = (int) (cap * (1 + SIGMA)); else if(newSpeed < oldSpeed) { cap = (int) (cap * (1 - RHO)); if(cap <= 0) cap = 1; } queueLengths.put(peer, cap); } public void updateTimes(Peer peer, double speed) { List<RequestTriple> queue = downloadQueues.get(peer); if(queue == null) return; for(int i = 0; i < queue.size(); i++) { queue.get(i).setTime(speed == 0 ? Integer.MAX_VALUE : (int) ((i + 1) * pieceSize / speed)); } } public void sortBlockQueues() { Comparator<RequestTriple> comp = compareTime(); for(int piece : blockQueues.keySet()) Collections.sort(blockQueues.get(piece), comp); } public boolean addRequest(Peer peer, int piece, int time) { if(isQueueFull(peer)) return false; RequestTriple rt = new RequestTriple(peer, piece, time); triples.add(rt); List<RequestTriple> queue = downloadQueues.get(peer); if(queue == null) { queue = new ArrayList<RequestTriple>(); downloadQueues.put(peer, queue); } queue.add(rt); Comparator<RequestTriple> timeComparator = compareTime(); Collections.sort(queue, timeComparator); queue = blockQueues.get(piece); if(queue == null) { queue = new ArrayList<RequestTriple>(); blockQueues.put(piece, queue); } queue.add(rt); Collections.sort(queue, timeComparator); return true; } public void removeRequest(RequestTriple rt) { triples.remove(rt); List<RequestTriple> queue = downloadQueues.get(rt.peer); if(queue != null) queue.remove(rt); queue = blockQueues.get(rt.piece); if(queue != null) { queue.remove(rt); if(queue.isEmpty()) blockQueues.remove(rt.piece); } } public void removeRequest(Peer peer, int piece) { RequestTriple rt = new RequestTriple(peer, piece); removeRequest(rt); } public void removeRequest(int piece) { for(Peer peer : downloadQueues.keySet()) removeRequest(peer, piece); } public void removeRequest(List<Peer> peers, int piece) { for(Peer peer : peers) removeRequest(peer, piece); } public void removeRequest(Peer peer) { List<RequestTriple> queue = downloadQueues.remove(peer); if(queue == null) return; for(RequestTriple rt : queue) removeRequest(rt); } public void dumpTable() { dump(downloadQueues, "Download Queues", 70); System.out.println("\n"); dump(blockQueues, "Block Queues", 10); } private <T> void dump(Map<T, List<RequestTriple>> queues, String title, int align) { System.out.println(title); for(T t : queues.keySet()) { StringBuilder row = new StringBuilder(t.toString()); if(t instanceof Peer) { int size = queueLengths.containsKey(t) ? queueLengths.get(t) : 1; row.append(" (" + size + ")"); } for(int i = t.toString().length(); i < align; i++) row.append(" "); int alignEnd = 100; List<RequestTriple> queue = queues.get(t); for(RequestTriple rt : queue) { row.append(rt.toString()); for(int i = rt.toString().length(); i < alignEnd; i++) row.append(" "); } System.out.println("----------------------------------------------" + "-------------------------------------------------------------"); System.out.println(row.toString()); } } private Comparator<RequestTriple> compareTime() { return new Comparator<RequestTriple>() { @Override public int compare(RequestTriple o1, RequestTriple o2) { return o1.time - o2.time; } }; } static class RequestTriple { private Peer peer; private int piece; private int time; public RequestTriple(Peer peer, int piece) { this(peer, piece, -1); } public RequestTriple(Peer peer, int piece, int time) { this.peer = peer; this.piece = piece; this.time = time; } @Override public String toString() { return "<" + peer.toString() + ", " + piece + ", " + time + ">"; } @Override public boolean equals(Object o) { if(!(o instanceof RequestTriple)) return false; RequestTriple r = (RequestTriple) o; return r.peer.equals(peer) && r.piece == piece; } public Peer getPeer() { return peer; } public int getPiece() { return piece; } public int getTime() { return time; } public void setTime(int time) { this.time = time; } } }