/* * BitTorrentPieceMessage.java * * Created on Feb 9, 2010, 10:46:46 PM * * Description: Provides a bit torrent piece message. * * Copyright (C) Feb 9, 2010 reed. * * This program is free software; you can redistribute it and/or modify it under the terms * of the GNU General Public License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; * if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ package org.texai.torrent.message; import java.io.UnsupportedEncodingException; import net.jcip.annotations.NotThreadSafe; import org.texai.torrent.support.BitTorrentConstants; import org.texai.util.ByteUtils; import org.texai.util.TexaiException; /** Provides a bit torrent piece message. See http://wiki.theory.org/BitTorrentSpecification. * * @author reed */ @NotThreadSafe public class BitTorrentPieceMessage implements BitTorrentMessage { /** the piece index */ private final int pieceIndex; /** the offset within the piece where the block begins */ private final int offset; /** the chunk bytes */ private final byte[] chunkBytes; /** the peer id bytes */ private final byte[] peerIdBytes; /** Constructs a new BitTorrentPieceMessage instance. * * @param pieceIndex the piece index * @param offset the offset within the piece where the chunk begins * @param chunkBytes the chunk bytes * @param peerIdBytes the peer identification bytes */ public BitTorrentPieceMessage( final int pieceIndex, final int offset, final byte[] chunkBytes, final byte[] peerIdBytes) { //Preconditions assert pieceIndex >= 0 : "pieceIndex must not be negative"; assert offset >= 0 : "offset must not be negative"; assert chunkBytes != null : "chunkBytes must not be null"; assert chunkBytes.length > 0 : "chunkBytes must not be empty"; assert peerIdBytes != null : "peerIdBytes must not be null"; assert peerIdBytes.length == 20 : "peerIdBytes must be length 20"; this.pieceIndex = pieceIndex; this.offset = offset; this.chunkBytes = chunkBytes; this.peerIdBytes = peerIdBytes; } /** Encodes the bit torrent message into a byte array. * * @return the byte array */ @Override public byte[] encode() { final int dataBlock_len = chunkBytes.length; final byte[] bytes = new byte[33 + dataBlock_len]; // message length field does not include the length of its own four bytes final byte[] lengthBytes = ByteUtils.toBytes(29 + dataBlock_len); bytes[0] = lengthBytes[0]; bytes[1] = lengthBytes[1]; bytes[2] = lengthBytes[2]; bytes[3] = lengthBytes[3]; bytes[4] = BitTorrentConstants.BIT_TORRENT_PIECE_MESSAGE_ID; final byte[] pieceIndexBytes = ByteUtils.toBytes(pieceIndex); bytes[5] = pieceIndexBytes[0]; bytes[6] = pieceIndexBytes[1]; bytes[7] = pieceIndexBytes[2]; bytes[8] = pieceIndexBytes[3]; final byte[] offsetBytes = ByteUtils.toBytes(offset); bytes[9] = offsetBytes[0]; bytes[10] = offsetBytes[1]; bytes[11] = offsetBytes[2]; bytes[12] = offsetBytes[3]; System.arraycopy( chunkBytes, // source 0, // source offset bytes, // destination 13, // destination offset dataBlock_len); // length System.arraycopy( peerIdBytes, // source 0, // source offset bytes, // destination dataBlock_len + 13, // destination offset 20); // length return bytes; } /** Decodes the given bytes into a new BitTorrentPieceMessage instance. * * @param bytes the given bytes * @return a new instance */ public static BitTorrentPieceMessage decode(final byte[] bytes) { //Preconditions assert bytes != null : "bytes must not be null"; assert bytes.length > 33 : "bytes must be length greater than 33"; final int dataBlock_len = bytes.length - 33; final byte[] dataBlock = new byte[dataBlock_len]; System.arraycopy( bytes, // source 13, // source offset dataBlock, // destination 0, // destination offset dataBlock_len); // length final byte[] peerIdBytes = new byte[20]; System.arraycopy( bytes, // source dataBlock_len + 13, // source offset peerIdBytes, // destination 0, // destination offset 20); // length return new BitTorrentPieceMessage( ByteUtils.byteArrayToInt(bytes, 5), // pieceIndex ByteUtils.byteArrayToInt(bytes, 9), // offset dataBlock, peerIdBytes); } /** Returns a string representation of this object. * * @return a string representation of this object */ @Override public String toString() { final StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append("[piece: "); stringBuilder.append(pieceIndex); stringBuilder.append(", offset: "); stringBuilder.append(offset); stringBuilder.append(", length: "); stringBuilder.append(chunkBytes.length); stringBuilder.append(", peer id: "); try { stringBuilder.append(new String(peerIdBytes, "US-ASCII")); } catch (UnsupportedEncodingException ex) { throw new TexaiException(ex); } stringBuilder.append(']'); return stringBuilder.toString(); } /** Gets the piece index. * * @return the piece index */ public int getPieceIndex() { return pieceIndex; } /** Gets the offset within the piece where the chunk begins. * * @return the offset */ public int getOffset() { return offset; } /** Gets the chunk bytes. * * @return the chunk bytes */ public byte[] getChunkBytes() { return chunkBytes; } /** Gets the peer identification bytes. * * @return the peer identification bytes */ @Override public byte[] getPeerIdBytes() { return peerIdBytes; } }