/// <summary>
 /// Creates "request" or "cancel" messages
 /// </summary>
 /// <param name="index">Zero-based piece index</param>
 /// <param name="begin">Zero-based offset within the piece</param>
 /// <param name="length">Specifies the requested (cancelled) block's length</param>
 public PeerMessage(PeerMessageType type, int index, int begin, int length)
 {
     if (!(type == PeerMessageType.request || type == PeerMessageType.cancel))
     {
         throw new ArgumentException("Type of the message must be \"request\" or \"cancel\"");
     }
     messageType = type;
     msgContents = new byte[msgLenSpace + msgTypeSpace + msgIntSpace * 3];
     Array.Copy(PeerConnection.HTONNTOH(BitConverter.GetBytes(13)), msgContents, msgLenSpace);
     msgContents[msgLenSpace] = (byte)messageType;
     Array.Copy(PeerConnection.HTONNTOH(BitConverter.GetBytes(index)), 0, msgContents, msgLenSpace + msgTypeSpace, msgIntSpace);
     Array.Copy(PeerConnection.HTONNTOH(BitConverter.GetBytes(begin)), 0, msgContents, msgLenSpace + msgTypeSpace + msgIntSpace, msgIntSpace);
     Array.Copy(PeerConnection.HTONNTOH(BitConverter.GetBytes(length)), 0, msgContents, msgLenSpace + msgTypeSpace + msgIntSpace * 2, msgIntSpace);
     this.pieceIndex  = index;
     this.pieceOffset = begin;
     this.length      = length;
 }
 /// <summary>
 /// Creates a message with no payload (keep-alive, choke, unchoke, interested or not interested)
 /// </summary>
 /// <param name="type">Type of the message</param>
 /// <exception cref="ArgumentException">Thrown if passed message type must have a payload</exception>
 public PeerMessage(PeerMessageType type)
 {
     if (type == PeerMessageType.keepAlive)
     {
         msgContents = new byte[msgLenSpace];
     }
     else if (type == PeerMessageType.choke || type == PeerMessageType.unchoke || type == PeerMessageType.interested ||
              type == PeerMessageType.notInterested)
     {
         msgContents = new byte[msgLenSpace + msgTypeSpace];
         Array.Copy(PeerConnection.HTONNTOH(BitConverter.GetBytes(1)), msgContents, msgLenSpace);
         msgContents[msgLenSpace] = (byte)type;
     }
     else
     {
         throw new ArgumentException($"This message type: {type.ToString()} must have non-empty payload");
     }
 }
 public void ReceivedChokeOrDisconnected(PeerConnection connection)
 {
     if (connection.outgoingRequestsCount != 0)
     {
         for (int i = 0; i < connection.outgoingRequests.Length; i++)
         {
             if (connection.outgoingRequests[i] != null)
             {
                 // null checking because the list may be already empty
                 lock (pendingIncomingPiecesInfo)
                 {
                     var node = FindRequestsPiece(connection.outgoingRequests[i].Item1);
                     if (node != null)
                     {
                         node.requestedBlocksMap[connection.outgoingRequests[i].Item2 / blockSize] = false;
                     }
                     connection.outgoingRequests[i] = null;
                 }
             }
         }
     }
 }
        /// <summary>
        /// Creates a bitfield message
        /// </summary>
        /// <param name="bitfield">Bitfield to include in the message</param>
        public PeerMessage(BitArray bitfield)
        {
            messageType    = PeerMessageType.bitfield;
            msgContents    = new byte[msgLenSpace + msgTypeSpace + (int)Math.Ceiling((double)bitfield.Count / 8)];
            rawBytesOffset = msgLenSpace + msgTypeSpace;
            Array.Copy(PeerConnection.HTONNTOH(BitConverter.GetBytes(msgContents.Length - msgLenSpace)), msgContents, msgLenSpace);
            msgContents[msgLenSpace] = (byte)messageType;

            int j = 0;

            for (int i = rawBytesOffset; i < msgContents.Length; i++)
            {
                for (int bit = 7; bit >= 0; bit--)
                {
                    if (j >= bitfield.Count)
                    {
                        break;
                    }
                    msgContents[i] |= (byte)(Convert.ToByte(bitfield[j]) << bit);
                    j++;
                }
            }
        }
        // returns piece index, piece offset, block size
        public Tuple <int, int, int> FindNextRequest(PeerConnection connection)
        {
            Tuple <int, int, int> result;

            // here we search for any non-downloaded and non-requested blocks within pending pieces
            lock (pendingIncomingPiecesInfo)
            {
                foreach (var entry in pendingIncomingPiecesInfo)
                {
                    // if peer doesn't have this piece, go further
                    if (connection.peersPieces[entry.pieceIndex] == false)
                    {
                        continue;
                    }

                    // otherwise check if we have something left to download in this piece
                    int  blockIndex;
                    bool spaceFound = false;
                    for (blockIndex = 0; blockIndex < entry.blocksMap.Count; blockIndex++)
                    {
                        if (entry.blocksMap[blockIndex] == false && entry.requestedBlocksMap[blockIndex] == false)
                        {
                            spaceFound = true;
                            break;
                        }
                    }

                    if (spaceFound)
                    {
                        int resultOffset = blockIndex * blockSize;
                        if (blockIndex == entry.blocksMap.Count - 1)
                        {
                            result = new Tuple <int, int, int>(entry.pieceIndex, resultOffset, GetLastBlockSize(entry.pieceIndex));
                        }
                        else
                        {
                            result = new Tuple <int, int, int>(entry.pieceIndex, resultOffset, blockSize);
                        }
                        entry.requestedBlocksMap[blockIndex] = true;
                        return(result);
                    }
                }

                // if we didn't find anything in pieces we're already downloading (or the Peer doesn't have it),
                // we simply find the first fitting piece the Peer has
                int interestingPieceIndex = -1;
                lock (pieces)
                {
                    for (int i = 0; i < connection.peersPieces.Count; i++)
                    {
                        if (FindRequestsPiece(i) == null && pieces[i] == false && (connection.peersPieces[i] == true))
                        {
                            interestingPieceIndex = i;
                            break;
                        }
                    }
                }
                // still haven't found anything? Well, this Peer has nothing interesting
                if (interestingPieceIndex == -1)
                {
                    return(null);
                }

                // peer has some piece we haven't added yet; so we add it and request block with 0-offset (the first one)
                var newEntry = AddPendingIncomingPiece(interestingPieceIndex);
                newEntry.requestedBlocksMap[0] = true;
                pendingIncomingPiecesInfo.AddLast(newEntry);
                // just for safety (what if it's only one block in piece?)
                if (0 == newEntry.blocksMap.Count - 1)
                {
                    return(new Tuple <int, int, int>(interestingPieceIndex, 0, GetLastBlockSize(newEntry.pieceIndex)));
                }
                else
                {
                    return(new Tuple <int, int, int>(interestingPieceIndex, 0, blockSize));
                }
            }
        }
        public PeerMessage(byte[] msgContents, int expectedBitfieldLength)
        {
            if (msgContents.Length == msgLenSpace)
            {
                messageType = PeerMessageType.keepAlive;
                return;
            }

            if (msgContents[msgLenSpace] == 5)
            {
                int bitsCount = (msgContents.Length - msgLenSpace - msgTypeSpace) * 8;

                if (bitsCount < expectedBitfieldLength || bitsCount > expectedBitfieldLength + 7)
                {
                    messageType = PeerMessageType.invalid;
                }

                if (bitsCount != expectedBitfieldLength)
                {
                    byte lastByte = msgContents[msgContents.Length - 1];
                    lastByte <<= bitsCount - expectedBitfieldLength;
                    if (lastByte != 0)
                    {
                        messageType = PeerMessageType.invalid;
                        return;
                    }
                }
            }
            if (msgContents[msgLenSpace] <= 10)
            {
                messageType = (PeerMessageType)msgContents[msgLenSpace];
            }
            else
            {
                messageType = PeerMessageType.unknown;
                return;
            }

            this.msgContents = msgContents;

            switch (messageType)
            {
            case PeerMessageType.have:
                byte[] num = new byte[msgIntSpace];
                Array.Copy(msgContents, msgLenSpace + msgTypeSpace, num, 0, msgIntSpace);
                this.pieceIndex = BitConverter.ToInt32(PeerConnection.HTONNTOH(num), 0);
                break;

            case PeerMessageType.bitfield:
                this.rawBytesOffset = msgLenSpace + msgTypeSpace;
                break;

            case PeerMessageType.request:
            case PeerMessageType.cancel:
                byte[] index  = new byte[msgIntSpace];
                byte[] begin  = new byte[msgIntSpace];
                byte[] length = new byte[msgIntSpace];
                Array.Copy(msgContents, msgLenSpace + msgTypeSpace, index, 0, msgIntSpace);
                Array.Copy(msgContents, msgLenSpace + msgTypeSpace + msgIntSpace, begin, 0, msgIntSpace);
                Array.Copy(msgContents, msgLenSpace + msgTypeSpace + msgIntSpace * 2, length, 0, msgIntSpace);
                this.pieceIndex  = BitConverter.ToInt32(PeerConnection.HTONNTOH(index), 0);
                this.pieceOffset = BitConverter.ToInt32(PeerConnection.HTONNTOH(begin), 0);
                this.length      = BitConverter.ToInt32(PeerConnection.HTONNTOH(length), 0);
                break;

            case PeerMessageType.piece:
                byte[] index1 = new byte[msgIntSpace];
                byte[] begin1 = new byte[msgIntSpace];
                Array.Copy(msgContents, msgLenSpace + msgTypeSpace, index1, 0, msgIntSpace);
                Array.Copy(msgContents, msgLenSpace + msgTypeSpace + msgIntSpace, begin1, 0, msgIntSpace);

                this.pieceIndex     = BitConverter.ToInt32(PeerConnection.HTONNTOH(index1), 0);
                this.pieceOffset    = BitConverter.ToInt32(PeerConnection.HTONNTOH(begin1), 0);
                this.rawBytesOffset = msgLenSpace + msgTypeSpace + msgIntSpace * 2;
                break;

            case PeerMessageType.port:
                break;
            }
        }