// Commented for the Learning branch package com.limegroup.bittorrent.messages; import java.nio.ByteBuffer; import java.nio.ByteOrder; import com.limegroup.bittorrent.BTInterval; /** * A BitTorrent Cancel message cancels a data request a BitTorrent program previously sent. * A Cancel message is 17 bytes long: * * LLLLTPPPPOOOOSSSS * * LLLL, PPPP, OOOO, and SSSS are ints written in big endian order. * L is 13, the length of the bytes beyond. * T is 0x06, the byte code for a Cancel message. * P is the piece number, like 0 for the first piece. * The Cancel message cancels a request for S bytes a distance O into the piece. * * For instance, here's a cancel for the 4 bytes that are 8 bytes into piece number 5: * * PPPP 5 bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb * OOOO 8 -------> * SSSS 4 rrrr */ public class BTCancel extends BTMessage { /** A byte buffer with the payload of the message, just the "PPPPOOOOSSSS" part. */ private ByteBuffer _payload = null; /** The piece number and range of bytes in the piece that this Cancel message doesn't need any more. */ private BTInterval in; /** * Make a new BTCancel object that represents a BitTorrent Cancel message. * * @param in A BTInterval object that has the piece number and range of data within that piece */ public BTCancel(BTInterval in) { // Save the type, 0x08 for a Cancel message super(CANCEL); // Save the given BTInterval object, it has all the information for the payload this.in = in; } /** * Make a BTCancel object that represents a BitTorrent Cancel message we've read from the network. * We got it because a remote computer is canceling a request for a part of a file. * This is the message parser. * * The whole message looks like "LLLLTPPPPOOOOSSSS". * Call readMessage() with the payload ByteBuffer clipped beyond the length and type, around the 12 bytes "PPPPOOOOSSSS". * P is the piece number, O is the length into the piece where the range starts, and S is the size from that point. * * @param payload A ByteBuffer with position and limit clipped around the 12-byte payload beyond the length int and type byte * @return A new BTCancel object that represents the Cancel message */ public static BTCancel readMessage(ByteBuffer payload) throws BadBTMessageException { // Make sure we were given exactly 12 bytes if (payload.remaining() != 12) throw new BadBTMessageException("unexpected payload in cancel message: " + new String(payload.array())); // Tell the ByteBuffer to read ints in big endian order payload.order(ByteOrder.BIG_ENDIAN); // First, read the piece number int pieceNum = payload.getInt(); // Moves position forward 4 bytes, past the int if (pieceNum < 0) throw new BadBTMessageException("invalid piece number in cancel message: " + pieceNum); // After that is the start distance int offset = payload.getInt(); // Last is the size int length = payload.getInt(); if (length == 0) throw new BadBTMessageException("0 length in cancel message " + pieceNum); // Make a new BTInterval object with the piece number and range, and use it to make the BTCancel object return new BTCancel(new BTInterval( offset, // (2) The distance in the piece to the start of the range the Cancel message will have offset + length - 1, // (3) The number of bytes there, subtract 1 because BTInterval includes the last byte pieceNum)); // (1) The piece number } /** * Get the range of data this Cancel message is saying it no longer needs. * Returns a BTInterval object, which uses index of first byte and index of last byte, not distance and size. * * @return A BTInterval object that has the piece number and range within it */ public BTInterval getInterval() { // Return the BTInterval object inside this BTCancel object return in; } /** * Compose the payload of this BitTorrent Cancel message. * * An entire Cancel message is 17 bytes arranged "LLLLTPPPPOOOOSSSS". * L, P, O, and S are 4-byte ints in big endian order. * L is the length, T is the type, P is the piece number, O is the offset, and S is the size. * getPayload() returns the payload after L and T, just "PPPPOOOOSSSS". * * @return A ByteBuffer with 12 bytes, "PPPPOOOOSSSS". */ public ByteBuffer getPayload() { if (_payload == null) { // Make a ByteBuffer that can hold 12 bytes, and configure it to write ints in big endian order _payload = ByteBuffer.allocate(12); _payload.order(ByteOrder.BIG_ENDIAN); // Write the piece number, offset, and size _payload.putInt(in.getId()); // The piece number, 0 is the first piece of the file _payload.putInt(in.low); // The distance in bytes from the start of that piece to the range the packet doesn't want any longer _payload.putInt(in.high - in.low + 1); // The number of bytes there, add 1 because in.high reaches the last byte, it doesn't include it _payload = _payload.asReadOnlyBuffer(); // Replace _payload with a read-only copy of the buffer } // Set position at the start and length at the end, and return it _payload.clear(); return _payload; } /** * Express this BTCancel object as text. * Makes a string like "BTCancel (25:5-10)". * 25 is the piece number, and 5 is the distance in bytes into that piece that the range starts. * 10 isn't the size, and it's not the end of the range either, it's the distance to the last byte in the range. * * @return A String like "BTCancel (25:5-10)" */ public String toString() { // Compose the text, calling BTInterval.toString() by adding in to a String return "BTCancel (" + in + ")"; } }