// Commented for the Learning branch
package com.limegroup.bittorrent.messages;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import com.limegroup.bittorrent.BTInterval;
/**
* BitTorrent programs give one another pieces of files in Piece messages.
* A Piece message is at least 13 bytes long:
*
* LLLLTPPPPOOOOdatadatadatadata...
*
* LLLL, PPPP, and OOOO are 4-byte ints in big endian order.
* L is the length of the rest of the message.
* T is the type byte, 0x07 identifies this as a Piece message.
* P is the piece number the data belongs to.
* O is the offset in bytes into that piece where the data goes.
* datadatadata... is the file data, you can tell how long it is from L.
*
* A Piece message contains a location of data, and the data itself.
* The data location is made up of the piece number, and a rage of data within that piece.
* BTPiece has a BTInterval named in that keeps that information.
* The data itself is in a byte array named _data.
*/
public class BTPiece extends BTMessage {
/** The piece number and range within it that this Piece message carries. */
private BTInterval in;
/** The file piece. */
private byte[] _data;
/**
* Make a new BTPiece object that represents a BitTorrent Piece message.
*
* @param in A BTInterval with the piece number and range of bytes inside
* @param data The file data that makes up that piece
*/
public BTPiece(BTInterval in, byte[] data) {
// Have the BTMessage constructor save the type byte 0x07 for a Piece message
super(PIECE);
// Save the given interval and byte array
this.in = in;
_data = data;
}
/**
* Parse the data of a Piece message from a remote computer into a BTPiece object.
* A whole Piece message looks like this:
*
* LLLLtPPPPOOOOdatadatadatadata...
*
* payload is a ByteBuffer with position and limit clipped around PPPPLLLLdatadata....
* readMessage() moves position to limit.
*
* Copies the file data from the given ByteBuffer into a new byte array named _data.
*
* @param payload A ByteBuffer with position and limit clipped around the payload of a Piece message.
* This is the part after LLLLT, like PPPPLLLLdatadatadata....
*/
public static BTPiece readMessage(ByteBuffer payload) throws BadBTMessageException {
// Make sure the ByteBuffer we got has at least enough room for the location information PPPPOOOO
if (payload.remaining() < 8) throw new BadBTMessageException("unexpected payload in piece message: " + new String(payload.array()));
// Have the ByteBuffer read 4-byte ints in big endian order
payload.order(ByteOrder.BIG_ENDIAN);
// Read the piece number PPPP
int pieceNum = payload.getInt();
if (pieceNum < 0) throw new BadBTMessageException("invalid piece number in request message: " + pieceNum);
// Read the offset OOOO
int offset = payload.getInt();
if (!payload.hasRemaining()) throw new BadBTMessageException("empty piece message " + pieceNum);
// Read the file data after that
byte[] data = new byte[payload.remaining()]; // Make a byte array exactly the right size
payload.get(data); // get() moves data from payload to data
// Make a new BTPiece object with the information we read
return new BTPiece(
new BTInterval( // Make a new BTInterval which will keep the location information
offset, // (2) How far into the piece the data starts
offset + data.length - 1, // (3) The index of the last byte of data, -1 because BTInterval.high points to the last byte, not beyond it
pieceNum), // (1) The file piece number the data belongs to
data); // The file data we read
}
/**
* Get the location in the whole file of the data this Piece message carries.
*
* @return A BTInterval with the piece number and range within that piece
*/
public BTInterval getInterval() {
// Return the BTInterval with the piece number and range information
return in;
}
/**
* Get the file data this Piece message carries.
*
* @return A byte array carrying the data
*/
public byte[] getData() {
// Return the byte array of file data
return _data;
}
/**
* Compose the payload of this Piece message, like:
*
* PPPPOOOOdatadatadatadatadata...
*
* PPPP and OOOO are 4-byte ints in big endian order.
* PPPP is the piece number, and OOOO is the distance into the piece that the data starts.
* After that is the data.
*
* @return A ByteBuffer with the piece number, offset distance, and data
*/
public ByteBuffer getPayload() {
// Make a byte buffer big enough to hold the 8 bytes at the start and all the data
ByteBuffer payload = ByteBuffer.allocate(_data.length + 8);
payload.order(ByteOrder.BIG_ENDIAN); // Have putInt() write ints in big endian order
// In the first 8 bytes, write the piece number and the index of the data in that piece
payload.putInt(in.getId());
payload.putInt(in.low);
// After that, write all the data
payload.put(_data);
// Set position and limit to clip around the whole buffer, and return it
payload.clear();
return payload;
}
/**
* Express this BTPiece object as text.
* Composes a String like "BTPiece (piece number;offset;data length)".
*
* @return A String
*/
public String toString() {
// Compose and return the text
return "BTPiece (" + in.getId() + ";" + in.low + ";" + _data.length + ")";
}
}