package org.torrent.internal.transfer;
import org.torrent.internal.data.BTPart;
import org.torrent.internal.data.TorrentMetaInfo;
import org.torrent.internal.data.TorrentMetaInfo.Piece;
import org.torrent.internal.peer.connection.BTConnection;
import org.torrent.internal.service.ContentStateListener;
import org.torrent.internal.service.event.ContentStateEvent;
import org.torrent.internal.util.Bits;
import org.torrent.internal.util.Validator;
public abstract class AbstractRequestProvider implements RequestProvider {
private Bits[] pieces;
private final int partLength;
private final RequestLimiter limiter;
private final TorrentMetaInfo contentInfo;
public AbstractRequestProvider(TorrentMetaInfo contentInfo,
ContentWatcher watcher, int partLength,
final int maxRequestsPerConnection) {
this(contentInfo, watcher, partLength, new RequestLimiter() {
@Override
public boolean allowRequestFor(BTConnection con) {
return con.getPendingRequests().size() < maxRequestsPerConnection;
}
});
Validator.isTrue(maxRequestsPerConnection >= 0,
"Invalid max requests: " + maxRequestsPerConnection);
}
public AbstractRequestProvider(TorrentMetaInfo contentInfo,
ContentWatcher watcher, int partLength, RequestLimiter limiter) {
Validator.isTrue(partLength > 0, "Invalid part length: " + partLength);
Validator.notNull(limiter, "RequestLimiter is null!");
Validator.nonNull(contentInfo);
this.contentInfo = contentInfo;
this.partLength = partLength;
this.limiter = limiter;
pieces = new Bits[contentInfo.getPiecesCount()];
for (Piece p : contentInfo) {
pieces[p.getIndex()] = createBits(p);
}
watcher.addContentStateListener(new ContentStateListener() {
@Override
public void receivedPiece(ContentStateEvent evt) {
assert hasNoRequiredParts(evt.getPiece());
pieces[evt.getPiece().getIndex()] = null;
System.out.println(evt);
}
@Override
public void requiresPiece(ContentStateEvent evt) {
Piece pi = evt.getPiece();
Bits b = pieces[pi.getIndex()];
if (b == null) {
b = pieces[pi.getIndex()] = createBits(AbstractRequestProvider.this.contentInfo
.getPiece(pi.getIndex()));
for (int i = 0; i < b.size(); i++) {
b.set(i, true);
}
}
for (int i = 0; i < b.size(); i++) {
if (pi.asBTPart().getRange().contains(
i * AbstractRequestProvider.this.partLength)) {
b.set(i, false);
}
}
assert b.firstIndexOf(false) >= 0 : b + " on " + pi;
notCompletelyRequested(AbstractRequestProvider.this.contentInfo
.getPiece(pi.getIndex()));
System.out.println(evt);
}
@Override
public void verifiedPiece(ContentStateEvent evt) {
pieces[evt.getPiece().getIndex()] = null;
System.out.println(evt);
}
@Override
public void stateChanged(ContentStateEvent event) {
// TODO Auto-generated method stub
System.out.println(event);
}
});
}
protected abstract boolean hasNoRequiredParts(Piece piece);
@Override
public boolean couldAllocateRequest(Download download, BTConnection con) {
if (!limiter.allowRequestFor(con)) {
return false;
}
return hasOpenRequests(con);
}
@Override
public BTPart allocateRequest(Download download, BTConnection con) {
if (!limiter.allowRequestFor(con)) {
return null;
}
org.torrent.internal.data.TorrentMetaInfo.Piece best = null;
for (int i = 0; i < contentInfo.getPiecesCount(); i++) {
Bits parts = pieces[i];
if (parts != null && con.getRemoteBitField().get(i)
&& parts.firstIndexOf(true) >= 0
&& parts.firstIndexOf(false) >= 0) {
best = contentInfo.getPiece(i);
break;
}
}
if (best == null) {
best = getBestIndex(con);
}
if (best == null) {
return null;
}
assert con.getRemoteBitField().get(best.getIndex());
Bits parts = pieces[best.getIndex()];
assert parts != null;
int req = parts.firstIndexOf(false);
parts.set(req, true);
BTPart result = new BTPart(best.getIndex(), req * partLength, Math.min(
best.getLength() - req * partLength, partLength));
if (parts.firstIndexOf(false) < 0) {
completelyRequested(best);
}
assert result.getLength() <= partLength;
return result;
}
private Bits createBits(org.torrent.internal.data.TorrentMetaInfo.Piece forPiece) {
return new Bits((forPiece.getLength() + partLength - 1) / partLength);
}
/**
* Mark the given piece as completely requested
*
* @param best
*/
protected abstract void completelyRequested(
org.torrent.internal.data.TorrentMetaInfo.Piece best);
protected abstract org.torrent.internal.data.TorrentMetaInfo.Piece getBestIndex(
BTConnection con);
protected abstract boolean hasOpenRequests(BTConnection con);
@Override
public void cancelAllRequests(Download download, BTConnection con) {
for (BTPart pi : con.getPendingRequests()) {
cancelRequest(download, con, pi);
}
}
@Override
public void cancelRequest(Download download, BTConnection con, BTPart part) {
TorrentMetaInfo.Piece piece = contentInfo.getPiece(part.getIndex());
Bits parts = pieces[piece.getIndex()];
parts.set(part.getStart() / partLength, false);
assert parts.firstIndexOf(false) >= 0;
notCompletelyRequested(piece);
}
protected abstract void notCompletelyRequested(TorrentMetaInfo.Piece piece);
public TorrentMetaInfo getContentInfo() {
return contentInfo;
}
}