Package org.gudy.azureus2.core3.peer.impl

Source Code of org.gudy.azureus2.core3.peer.impl.PEPieceImpl

/*
* File    : PEPieceImpl.java
* Created : 15-Oct-2003
* By      : Olivier
*
* Azureus - a Java Bittorrent client
*
* 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 2 of the License.
*
* 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 ( see the LICENSE file ).
*
* 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

package org.gudy.azureus2.core3.peer.impl;

/**
* @author parg
* @author MjrTom
*      2005/Oct/08: numerous changes for new piece-picking
*      2006/Jan/02: refactoring piece picking to elsewhere, and consolidations
*/

import java.util.*;

import org.gudy.azureus2.core3.disk.*;
import org.gudy.azureus2.core3.logging.*;
import org.gudy.azureus2.core3.peer.*;
import org.gudy.azureus2.core3.util.*;

import com.aelitis.azureus.core.peermanager.piecepicker.PiecePicker;


public class PEPieceImpl
    implements PEPiece
{
  private static final LogIDs LOGID = LogIDs.PIECES;
 
  private final DiskManagerPiece  dmPiece;
  private final PEPeerManager    manager;
 
  private final int       nbBlocks;       // number of blocks in this piece
    private long            creationTime;


  private final String[]  requested;
  private boolean      fully_requested;
 
  private final boolean[]  downloaded;
  private boolean      fully_downloaded;
  private long          time_last_download;

  private final String[]   writers;
  private List       writes;
 
  private String      reservedBy;  // using address for when they send bad/disconnect/reconnect
 
  //In end game mode, this limitation isn't used
    private int             speed;      //slower peers dont slow down fast pieces too much
   
    private int             resumePriority;
     
    private Object      real_time_data;
   
  // experimental class level lock
  protected static final AEMonitor   class_mon  = new AEMonitor( "PEPiece:class");
 
    /** piece for tracking partially downloaded pieces
     * @param _manager the PEPeerManager
     * @param _dm_piece the backing dmPiece
     * @param _pieceSpeed the speed threshold for potential new requesters
     */
  public PEPieceImpl(
    PEPeerManager     _manager,
    DiskManagerPiece  _dm_piece,
        int                 _pieceSpeed)
  {
        creationTime =SystemTime.getCurrentTime();
    manager =_manager;
    dmPiece =_dm_piece;
        speed =_pieceSpeed;

    nbBlocks =dmPiece.getNbBlocks();

    requested =new String[nbBlocks];
       
        final boolean[] written =dmPiece.getWritten();
    if (written ==null)
      downloaded =new boolean[nbBlocks];
    else
      downloaded =(boolean[])written.clone();

        writers =new String[nbBlocks];
    writes =new ArrayList(0);
  }

    public DiskManagerPiece getDMPiece()
    {
        return dmPiece;
    }

    public long getCreationTime()
    {
        final long now =SystemTime.getCurrentTime();
        if (now >=creationTime &&creationTime >0)
            return creationTime;
        creationTime =now;
        return now;
    }
   
    public long getTimeSinceLastActivity()
    {
        final long now =SystemTime.getCurrentTime();
        final long lastWriteTime =getLastDownloadTime(now);
        if (time_last_download >0 &&now >=time_last_download)
            return now -time_last_download;
        if (creationTime >0 &&now >=creationTime)
            return now -creationTime;
        creationTime =now;
        return 0;
    }
   
  public long getLastDownloadTime(final long now)
  {
    if (time_last_download <=now)
      return time_last_download;
    return time_last_download =now;
  }

  /** Tells if a block has been requested
   * @param blockNumber the block in question
   * @return true if the block is Requested already
   */
  public boolean isRequested(int blockNumber)
  {
    return requested[blockNumber] !=null;
  }
 
  /** Tells if a block has been downloaded
   * @param blockNumber the block in question
   * @return true if the block is downloaded already
   */
  public boolean isDownloaded(int blockNumber)
  {
    return downloaded[blockNumber];
  }
 
  /** This flags the block at the given offset as having been downloaded
     * If all blocks are now downloaed, sets the dmPiece as downloaded
   * @param blockNumber
   */
  public void setDownloaded(int offset)
  {
    time_last_download =SystemTime.getCurrentTime();
    downloaded[offset /DiskManager.BLOCK_SIZE] =true;
        for (int i =0; i <nbBlocks; i++)
        {
            if (!downloaded[i])
                return;
        }
       
        fully_downloaded  = true;
        fully_requested    = false;
  }
 
    /** This flags the block at the given offset as NOT having been downloaded
     * and the whole piece as not having been fully downloaded
     * @param blockNumber
     */
    public void clearDownloaded(int offset)
    {
        downloaded[offset /DiskManager.BLOCK_SIZE] =false;
      
        fully_downloaded  = false;
    }
   
    public boolean   
    isDownloaded()
    {
      return( fully_downloaded );
    }
   
    public boolean[] 
    getDownloaded()
    {
      return( downloaded );
    }
   
    public boolean
    hasUndownloadedBlock()
    {
      for (int i =0; i <nbBlocks; i++ ){
       
      if (!downloaded[i]){
       
        return( true );
      }
    }
     
      return( false );
    }
   
  /** This marks a given block as having been written by the given peer
   * @param peer the PEPeer that sent the data
   * @param blockNumber the block we're operating on
   */
  public void setWritten(PEPeer peer, int blockNumber)
  {
    writers[blockNumber] =peer.getIp();
    dmPiece.setWritten(blockNumber);
  }
 
  /** This method clears the requested information for the given block
     * unless the block has already been downloaded, in which case the writer's
     * IP is recorded as a request for the block.
   */
  public void clearRequested(int blockNumber)
  {
    requested[blockNumber] =downloaded[blockNumber] ?writers[blockNumber] :null;
   
    fully_requested = false;
  }
 
  public boolean     
  isRequested()
  {
    return( fully_requested );
  }
     
  public void     
  setRequested()
  {
    fully_requested  = true;
  }

  /** This will scan each block looking for requested blocks. For each one, it'll verify
   * if the PEPeer for it still exists and is still willing and able to upload data.
   * If not, it'll unmark the block as requested.
   * @return int of how many were cleared (0 to nbBlocks)
   */
    /*
  public int checkRequests()
  {
        if (getTimeSinceLastActivity() <30 *1000)
            return 0;
    int cleared =0;
    boolean nullPeer =false;
    for (int i =0; i <nbBlocks; i++)
    {
      if (!downloaded[i] &&!dmPiece.isWritten(i))
      {
        final String      requester =requested[i];
        final PEPeerTransport  pt;
        if (requester !=null)
        {
          pt =manager.getTransportFromAddress(requester);
          if (pt !=null)
          {
            pt.setSnubbed(true);
            if (!pt.isDownloadPossible())
            {
                            clearRequested(i);
              cleared++;
            }
          } else
          {
            nullPeer =true;
                        clearRequested(i);
            cleared++;
          }
        }
      }
    }
    if (cleared >0)
    {
      dmPiece.clearRequested();
            if (Logger.isEnabled())
                Logger.log(new LogEvent(dmPiece.getManager().getTorrent(), LOGID, LogEvent.LT_WARNING,
                        "checkRequests(): piece #" +getPieceNumber()+" cleared " +cleared +" requests."
                        + (nullPeer ?" Null peer was detected." :"")));
    }
    return cleared;
  }
  */
 
    /*
     * Parg: replaced above commented out checking with one that verifies that the
     * requests still exist. As piece-picker activity and peer disconnect logic is multi-threaded
     * and full of holes, this is a stop-gap measure to prevent a piece from being left with
     * requests that no longer exist
     */
 
  public void
  checkRequests()
  {
        if ( getTimeSinceLastActivity() < 30*1000 ){
         
            return;
        }
       
    int cleared = 0;
       
    for (int i=0; i<nbBlocks; i++){
   
      if (!downloaded[i] &&!dmPiece.isWritten(i)){
     
        final String      requester = requested[i];
       
        if ( requester != null ){
       
          if ( !manager.requestExists(
              requester,
              getPieceNumber(),
              i *DiskManager.BLOCK_SIZE,
              getBlockSize( i ))){

                        clearRequested(i);
                       
            cleared++;
          }
        }
      }
    }
   
    if ( cleared > 0 ){
         
            if (Logger.isEnabled())
                Logger.log(new LogEvent(dmPiece.getManager().getTorrent(), LOGID, LogEvent.LT_WARNING,
                        "checkRequests(): piece #" +getPieceNumber()+" cleared " +cleared +" requests" ));
    }else{
     
      if ( fully_requested && getNbUnrequested() > 0 ){

              if (Logger.isEnabled())
                    Logger.log(new LogEvent(dmPiece.getManager().getTorrent(), LOGID, LogEvent.LT_WARNING,
                            "checkRequests(): piece #" +getPieceNumber()+" reset fully requested" ));

        fully_requested = false;
      }
    }
  }
 
   
  /** @return true if the piece has any blocks that are not;
   *  Downloaded, Requested, or Written
   */
  public boolean hasUnrequestedBlock()
  {
    final boolean[] written =dmPiece.getWritten();
    for (int i =0; i <nbBlocks; i++ )
    {
      if (!downloaded[i] &&requested[i] ==null &&(written ==null ||!written[i]))
        return true;
    }
    return false;
  }

  /**
   * This method scans a piece for the first unrequested block.  Upon finding it,
   * it counts how many are unrequested up to nbWanted.
   * The blocks are marked as requested by the PEPeer
   * Assumption - single threaded access to this
   * TODO: this should return the largest span equal or smaller than nbWanted
   * OR, probably a different method should do that, so this one can support 'more sequential' picking
   */
  public int[] getAndMarkBlocks(PEPeer peer, int nbWanted, boolean enable_request_hints )
  {
    final String ip =peer.getIp();
        final boolean[] written =dmPiece.getWritten();
    int blocksFound =0;
   
    if ( enable_request_hints ){
     
      int[]  request_hint = peer.getRequestHint();
     
      if ( request_hint != null && request_hint[0] == dmPiece.getPieceNumber()){
       
          // try to honour the hint first
       
        int  hint_block_start   = request_hint[1] / DiskManager.BLOCK_SIZE;
        int hint_block_count  =  ( request_hint[2] + DiskManager.BLOCK_SIZE-1 ) / DiskManager.BLOCK_SIZE;
       
        for (int i =hint_block_start; i < nbBlocks && i <hint_block_start + hint_block_count; i++)
        {
          while (blocksFound <nbWanted &&(i +blocksFound) <nbBlocks &&!downloaded[i +blocksFound]
              &&requested[i +blocksFound] ==null &&(written ==null ||!written[i]))
          {
            requested[i +blocksFound] =ip;
            blocksFound++;
          }
          if (blocksFound >0){
                       
            return new int[] {i, blocksFound};
          }
        }
      }
    }
   
    // scan piece to find first free block
    for (int i =0; i <nbBlocks; i++)
    {
      while (blocksFound <nbWanted &&(i +blocksFound) <nbBlocks &&!downloaded[i +blocksFound]
          &&requested[i +blocksFound] ==null &&(written ==null ||!written[i]))
      {
        requested[i +blocksFound] =ip;
        blocksFound++;
      }
      if (blocksFound >0)
        return new int[] {i, blocksFound};
    }
    return new int[] {-1, 0};
  }
 
  public void getAndMarkBlock(PEPeer peer, int index )
  {
    requested[index] = peer.getIp();
   
    if ( getNbUnrequested() <= 0 ){
     
      setRequested();
    }
  }
 
    public int getNbRequests()
    {
        int result =0;
        for (int i =0; i <nbBlocks; i++)
        {
            if (!downloaded[i] &&requested[i] !=null)
                result++;
        }
        return result;
    }

    public int getNbUnrequested()
    {
        int result =0;
        final boolean[] written =dmPiece.getWritten();
        for (int i =0; i <nbBlocks; i++ )
        {
            if (!downloaded[i] &&requested[i] ==null &&(written ==null ||!written[i]))
                result++;
        }
        return result;
    }

  /**
   * Assumption - single threaded with getAndMarkBlock
   */
  public boolean setRequested(PEPeer peer, int blockNumber)
  {
    if (!downloaded[blockNumber])
    {
      requested[blockNumber] =peer.getIp();
      return true;
    }
    return false;
  }
 
  public boolean   
  isRequestable()
  {
    return( dmPiece.isDownloadable() && !( fully_downloaded || fully_requested ));
  }
 
  public int
  getBlockSize(
    int blockNumber)
  {
    if ( blockNumber == (nbBlocks - 1)){
     
      int  length = dmPiece.getLength();
     
      if ((length % DiskManager.BLOCK_SIZE) != 0){
       
        return( length % DiskManager.BLOCK_SIZE );
      }
    }
   
    return DiskManager.BLOCK_SIZE;
  }
   
    public int getBlockNumber(int offset)
    {
        return offset /DiskManager.BLOCK_SIZE;
    }
 
  public int getNbBlocks()
  {
    return nbBlocks;
  }

  public List getPieceWrites()
  {
    List result;
    try{
      class_mon.enter();
     
      result = new ArrayList(writes);
    }finally{
     
      class_mon.exit();
    }
    return result;
  }
 
 
  public List getPieceWrites(int blockNumber) {
    final List result;
    try{
      class_mon.enter();
     
      result = new ArrayList(writes);
     
    }finally{
     
      class_mon.exit();
    }
    final Iterator iter = result.iterator();
    while(iter.hasNext()) {
      final PEPieceWriteImpl write = (PEPieceWriteImpl) iter.next();
      if(write.getBlockNumber() != blockNumber)
        iter.remove();
    }
    return result;
  }
 
 
  public List getPieceWrites(PEPeer peer) {
    final List result;
    try{
      class_mon.enter();
     
      result = new ArrayList(writes);
    }finally{
      class_mon.exit();
    }
    final Iterator iter = result.iterator();
    while(iter.hasNext()) {
      PEPieceWriteImpl write = (PEPieceWriteImpl) iter.next();
      if(peer == null || ! peer.getIp().equals(write.getSender()))
        iter.remove();
    }
    return result;
  }
 
  public List
  getPieceWrites(
    String  ip )
  {
    final List result;
   
    try{
      class_mon.enter();
     
      result = new ArrayList(writes);
     
    }finally{
     
      class_mon.exit();
    }
   
    final Iterator iter = result.iterator();
   
    while(iter.hasNext()) {
     
      final PEPieceWriteImpl write = (PEPieceWriteImpl) iter.next();
     
      if ( !write.getSender().equals( ip )){
       
        iter.remove();
      }
    }
   
    return result;
  }

  public void reset()
  {
    dmPiece.reset();
    for (int i =0; i <nbBlocks; i++)
    {
            requested[i] =null;
      downloaded[i] =false;
      writers[i] =null;
    }
    fully_downloaded = false;
    time_last_download = 0;
    reservedBy =null;
    real_time_data=null;
  }

  public Object
  getRealTimeData()
  {
    return( real_time_data );
  }
 
  public void
  setRealTimeData(
    Object  o )
  {
    real_time_data = o;
  }
 
  protected void addWrite(PEPieceWriteImpl write) {
    try{
      class_mon.enter();
     
      writes.add(write);
     
    }finally{
     
      class_mon.exit();
    }
  }
 
  public void
  addWrite(
    int blockNumber,
    String sender,
    byte[] hash,
    boolean correct  )
  {
    addWrite( new PEPieceWriteImpl( blockNumber, sender, hash, correct ));
  }

  public String[] getWriters()
  {
    return writers;
  }

  public int getSpeed()
  {
    return speed;
  }

  public void setSpeed(int newSpeed)
  {
    speed =newSpeed;
  }

  public void
  setLastRequestedPeerSpeed(
    int    peerSpeed )
  {
    // Up the speed on this piece?
    if (peerSpeed > speed ){
      speed++;
    }
  }

  /**
   * @return Returns the manager.
   */
  public PEPeerManager getManager()
  {
    return manager;
  }

  public void setReservedBy(String peer)
  {
    reservedBy =peer;
  }

  public String getReservedBy()
  {
    return reservedBy;
  }

  /** for a block that's already downloadedt, mark up the piece
   * so that the block will get downloaded again.  This is used
   * when the piece fails hash-checking.
   */
  public void reDownloadBlock(int blockNumber)
  {
    downloaded[blockNumber] =false;
    requested[blockNumber] =null;
    fully_downloaded = false;
    writers[blockNumber] = null;
    dmPiece.reDownloadBlock(blockNumber);
  }

  /** finds all blocks downloaded by the given address
   * and marks them up for re-downloading
   * @param address String
   */
  public void reDownloadBlocks(String address)
  {
    for (int i =0; i <writers.length; i++ )
    {
      final String writer =writers[i];

      if (writer !=null &&writer.equals(address))
        reDownloadBlock(i);
    }
  }

  public void setResumePriority(int p)
  {
    resumePriority =p;
  }

  public int getResumePriority()
  {
    return resumePriority;
  }

    /**
     * @return int of availability in the swarm for this piece
     * @see org.gudy.azureus2.core3.peer.PEPeerManager.getAvailability(int pieceNumber)
     */
    public int getAvailability()
    {
        return manager.getAvailability(dmPiece.getPieceNumber());
    }

    /** This support method returns how many blocks have already been
     * written from the dmPiece
     * @return int from dmPiece.getNbWritten()
     * @see org.gudy.azureus2.core3.disk.DiskManagerPiece.getNbWritten()
     */
    public int getNbWritten()
    {
        return dmPiece.getNbWritten();
    }
   
    /** This support method returns the dmPiece's written array
     * @return boolean[] from the dmPiece
     * @see org.gudy.azureus2.core3.disk.DiskManagerPiece.getWritten()
     */
    public boolean[] getWritten()
    {
        return dmPiece.getWritten();
    }
    public boolean isWritten()
    {
        return dmPiece.isWritten();
    }
   
    public boolean isWritten( int block)
    {
        return dmPiece.isWritten( block );
    }
  public int getPieceNumber()
  {
    return dmPiece.getPieceNumber();
  }

  public int getLength()
  {
    return dmPiece.getLength();
  }

  public void setRequestable()
  {
    fully_downloaded  = false;
    fully_requested    = false;
   
    dmPiece.setDownloadable();
  }

  public String
  getString()
  {
    String  text  = "";
   
    PiecePicker pp = manager.getPiecePicker();

    text  += ( isRequestable()?"reqable,":"" );
    text  += "req=" + getNbRequests() + ",";
    text  += ( isRequested()?"reqstd,":"" );
    text  += ( isDownloaded()?"downed,":"" );
    text  += ( getReservedBy()!=null?"resrv,":"" );
    text  += "speed=" + getSpeed() + ",";
    text  += ( pp==null?("pri=" + getResumePriority()):pp.getPieceString(dmPiece.getPieceNumber()));
   
    if ( text.endsWith(",")){
      text = text.substring(0,text.length()-1);
    }
   
    return( text );
  }
}
TOP

Related Classes of org.gudy.azureus2.core3.peer.impl.PEPieceImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.