From 4017a57a95173917ef23961e1535abb62a4f246f Mon Sep 17 00:00:00 2001 From: Zsombor Gegesy Date: Mon, 25 Feb 2013 00:29:42 +0100 Subject: [PATCH 1/2] Optimization: avoid creating too much buffers, use the inherited torrentHash instead of separate byte buffer in every Piece object --- .../java/com/turn/ttorrent/client/Piece.java | 27 ++++++++++++------- .../turn/ttorrent/client/SharedTorrent.java | 4 +-- .../com/turn/ttorrent/common/Torrent.java | 6 +++++ 3 files changed, 25 insertions(+), 12 deletions(-) diff --git a/src/main/java/com/turn/ttorrent/client/Piece.java b/src/main/java/com/turn/ttorrent/client/Piece.java index 77d60640b..34ba88df3 100644 --- a/src/main/java/com/turn/ttorrent/client/Piece.java +++ b/src/main/java/com/turn/ttorrent/client/Piece.java @@ -56,7 +56,7 @@ public class Piece implements Comparable { private final int index; private final long offset; private final long length; - private final byte[] hash; + private final ByteBuffer torrentHash; private final boolean seeder; private volatile boolean valid; @@ -70,17 +70,17 @@ public class Piece implements Comparable { * @param index This piece index in the torrent. * @param offset This piece offset, in bytes, in the storage. * @param length This piece length, in bytes. - * @param hash This piece 20-byte SHA1 hash sum. + * @param torrentHash This full hash for every pieces. * @param seeder Whether we're seeding this torrent or not (disables piece * validation). */ public Piece(TorrentByteStorage bucket, int index, long offset, - long length, byte[] hash, boolean seeder) { + long length, ByteBuffer torrentHash, boolean seeder) { this.bucket = bucket; this.index = index; this.offset = offset; this.length = length; - this.hash = hash; + this.torrentHash = torrentHash; this.seeder = seeder; // Piece is considered invalid until first check. @@ -160,18 +160,27 @@ public synchronized boolean validate() throws IOException { this.valid = false; try { - // TODO: remove cast to int when large ByteBuffer support is - // implemented in Java. ByteBuffer buffer = this._read(0, this.length); - byte[] data = new byte[(int)this.length]; - buffer.get(data); - this.valid = Arrays.equals(Torrent.hash(data), this.hash); + this.valid = checkHash(buffer); } catch (NoSuchAlgorithmException nsae) { logger.error("{}", nsae); } return this.isValid(); } + + protected boolean checkHash(ByteBuffer data) throws NoSuchAlgorithmException { + byte[] calculatedHash = Torrent.hash(data); + + int torrentHashPosition = getIndex() * Torrent.PIECE_HASH_SIZE; + for (int i = 0; i < Torrent.PIECE_HASH_SIZE; i++) { + byte value = this.torrentHash.get(torrentHashPosition + i); + if (value != calculatedHash[i]) { + return false; + } + } + return true; + } /** * Internal piece data read function. diff --git a/src/main/java/com/turn/ttorrent/client/SharedTorrent.java b/src/main/java/com/turn/ttorrent/client/SharedTorrent.java index a040d3d50..02e7a4522 100644 --- a/src/main/java/com/turn/ttorrent/client/SharedTorrent.java +++ b/src/main/java/com/turn/ttorrent/client/SharedTorrent.java @@ -321,8 +321,6 @@ public synchronized void init() throws InterruptedException, IOException { logger.info("Analyzing local data for {} with {} threads ({} pieces)...", new Object[] { this.getName(), threads, nPieces }); for (int idx=0; idx hasher = new Piece.CallableHasher(this.pieces[idx]); diff --git a/src/main/java/com/turn/ttorrent/common/Torrent.java b/src/main/java/com/turn/ttorrent/common/Torrent.java index 0b3194ae7..e01057af8 100644 --- a/src/main/java/com/turn/ttorrent/common/Torrent.java +++ b/src/main/java/com/turn/ttorrent/common/Torrent.java @@ -418,6 +418,12 @@ public static byte[] hash(byte[] data) throws NoSuchAlgorithmException { return md.digest(); } + public static byte[] hash(ByteBuffer data) throws NoSuchAlgorithmException { + MessageDigest md = MessageDigest.getInstance("SHA-1"); + md.update(data); + return md.digest(); + } + /** * Convert a byte string to a string containing an hexadecimal * representation of the original data. From 1d4db41514fd278912d6761e08a4c3bdfad2bdaf Mon Sep 17 00:00:00 2001 From: Zsombor Gegesy Date: Mon, 25 Feb 2013 00:45:28 +0100 Subject: [PATCH 2/2] pass SharedTorrent to the individual Piece objects, so a lot's of unnecessary data duplication can be avoided. --- .../java/com/turn/ttorrent/client/Piece.java | 33 ++++++++----------- .../turn/ttorrent/client/SharedTorrent.java | 24 ++++++++++++-- 2 files changed, 35 insertions(+), 22 deletions(-) diff --git a/src/main/java/com/turn/ttorrent/client/Piece.java b/src/main/java/com/turn/ttorrent/client/Piece.java index 34ba88df3..90b2d2c42 100644 --- a/src/main/java/com/turn/ttorrent/client/Piece.java +++ b/src/main/java/com/turn/ttorrent/client/Piece.java @@ -17,12 +17,10 @@ import com.turn.ttorrent.common.Torrent; import com.turn.ttorrent.client.peer.SharingPeer; -import com.turn.ttorrent.client.storage.TorrentByteStorage; import java.io.IOException; import java.nio.ByteBuffer; import java.security.NoSuchAlgorithmException; -import java.util.Arrays; import java.util.concurrent.Callable; import org.slf4j.Logger; @@ -52,12 +50,9 @@ public class Piece implements Comparable { private static final Logger logger = LoggerFactory.getLogger(Piece.class); - private final TorrentByteStorage bucket; + private final SharedTorrent torrent; private final int index; - private final long offset; private final long length; - private final ByteBuffer torrentHash; - private final boolean seeder; private volatile boolean valid; private int seen; @@ -66,22 +61,15 @@ public class Piece implements Comparable { /** * Initialize a new piece in the byte bucket. * - * @param bucket The underlying byte storage bucket. + * @param bucket The parent torrent. * @param index This piece index in the torrent. - * @param offset This piece offset, in bytes, in the storage. * @param length This piece length, in bytes. - * @param torrentHash This full hash for every pieces. - * @param seeder Whether we're seeding this torrent or not (disables piece * validation). */ - public Piece(TorrentByteStorage bucket, int index, long offset, - long length, ByteBuffer torrentHash, boolean seeder) { - this.bucket = bucket; + public Piece(SharedTorrent torrent, int index, long length) { + this.torrent = torrent; this.index = index; - this.offset = offset; this.length = length; - this.torrentHash = torrentHash; - this.seeder = seeder; // Piece is considered invalid until first check. this.valid = false; @@ -150,7 +138,7 @@ public void noLongerAt(SharingPeer peer) { * meta-info. */ public synchronized boolean validate() throws IOException { - if (this.seeder) { + if (this.torrent.isSeeder()) { logger.trace("Skipping validation of {} (seeder mode).", this); this.valid = true; return true; @@ -173,8 +161,9 @@ protected boolean checkHash(ByteBuffer data) throws NoSuchAlgorithmException { byte[] calculatedHash = Torrent.hash(data); int torrentHashPosition = getIndex() * Torrent.PIECE_HASH_SIZE; + ByteBuffer torrentHash = this.torrent.getPiecesHashes(); for (int i = 0; i < Torrent.PIECE_HASH_SIZE; i++) { - byte value = this.torrentHash.get(torrentHashPosition + i); + byte value = torrentHash.get(torrentHashPosition + i); if (value != calculatedHash[i]) { return false; } @@ -209,7 +198,7 @@ private ByteBuffer _read(long offset, long length) throws IOException { // TODO: remove cast to int when large ByteBuffer support is // implemented in Java. ByteBuffer buffer = ByteBuffer.allocate((int)length); - int bytes = this.bucket.read(buffer, this.offset + offset); + int bytes = this.torrent.getBucket().read(buffer, this.getBucketOffset() + offset); buffer.rewind(); buffer.limit(bytes >= 0 ? bytes : 0); return buffer; @@ -271,11 +260,15 @@ public synchronized void record(ByteBuffer block, int offset) if (block.remaining() + offset == this.length) { this.data.rewind(); logger.trace("Recording {}...", this); - this.bucket.write(this.data, this.offset); + this.torrent.getBucket().write(this.data, this.getBucketOffset()); this.data = null; } } + long getBucketOffset() { + return ((long)this.index) * this.torrent.getPieceLength(); + } + /** * Return a human-readable representation of this piece. */ diff --git a/src/main/java/com/turn/ttorrent/client/SharedTorrent.java b/src/main/java/com/turn/ttorrent/client/SharedTorrent.java index 02e7a4522..ba44b7156 100644 --- a/src/main/java/com/turn/ttorrent/client/SharedTorrent.java +++ b/src/main/java/com/turn/ttorrent/client/SharedTorrent.java @@ -330,8 +330,7 @@ public synchronized void init() throws InterruptedException, IOException { this.bucket.size() - off, this.pieceLength); - this.pieces[idx] = new Piece(this.bucket, idx, off, len, piecesHashes, - this.isSeeder()); + this.pieces[idx] = new Piece(this, idx, len); Callable hasher = new Piece.CallableHasher(this.pieces[idx]); results.add(executor.submit(hasher)); @@ -833,4 +832,25 @@ public synchronized void handlePeerDisconnected(SharingPeer peer) { @Override public synchronized void handleIOException(SharingPeer peer, IOException ioe) { /* Do nothing */ } + + + /** + * For accessing from Piece. + * @return + */ + TorrentByteStorage getBucket() { + return bucket; + } + + /** + * For accessing from Piece. + * @return + */ + int getPieceLength() { + return pieceLength; + } + + ByteBuffer getPiecesHashes() { + return piecesHashes; + } }