/*
* Created on 08-Oct-2004
* Created by Paul Gardner
* Copyright (C) 2004, 2005, 2006 Aelitis, All Rights Reserved.
*
* 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, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* AELITIS, SAS au capital de 46,603.30 euros
* 8 Allee Lenotre, La Grille Royale, 78600 Le Mesnil le Roi, France.
*
*/
package org.gudy.azureus2.core3.disk.impl;
/**
* @author parg
* @author MjrTom
* 2005/Oct/08: startPriority/resumePriority handling and minor clock fixes
* 2006/Jan/02: refactoring, change booleans to statusFlags
*/
import org.gudy.azureus2.core3.disk.*;
import org.gudy.azureus2.core3.disk.impl.piecemapper.DMPieceList;
public class DiskManagerPieceImpl
implements DiskManagerPiece
{
//private static final LogIDs LOGID = LogIDs.PIECES;
private static final byte PIECE_STATUS_NEEDED = 0x01; //want to have the piece
private static final byte PIECE_STATUS_WRITTEN = 0x20; //piece fully written to storage
private static final byte PIECE_STATUS_CHECKING = 0x40; //piece is being hash checked
private static final byte PIECE_STATUS_MASK_DOWNLOADABLE =
PIECE_STATUS_CHECKING | PIECE_STATUS_WRITTEN | PIECE_STATUS_NEEDED;
// 0x65; // Needed IS once again included in this
private static final byte PIECE_STATUS_MASK_NEEDS_CHECK = PIECE_STATUS_CHECKING | PIECE_STATUS_WRITTEN;
//private static boolean statusTested =false;
private final DiskManagerHelper diskManager;
private final int pieceNumber;
/** the number of blocks in this piece: can be short as this gives up to .5GB piece sizes with 16K blocks */
private final short nbBlocks;
// to save memory the "written" field is only maintained for pieces that are
// downloading. A value of "null" means that either the piece hasn't started
// download or that it is complete.
// access to "written" is single-threaded (by the peer manager) apart from when
// the disk manager is saving resume data.
// actually this is not longer strictly true, as setDone is called asynchronously
// however, this issue can be worked around by working on a reference to the written data
// as problems only occur when switching from all-written to done=true, both of which signify
// the same state of affairs.
protected volatile boolean[] written;
private byte statusFlags;
/** it's *very* important to accurately maintain the "done" state of a piece. Currently the statusFlags
* are updated in a non-thread-safe manner so a 'done' field is maintained seperatly. Synchronizing
* access to statusFlags or done would cause a tremendous performance hit.
*/
private short read_count;
private boolean done;
public DiskManagerPieceImpl(final DiskManagerHelper _disk_manager, final int pieceIndex, int length )
{
diskManager =_disk_manager;
pieceNumber = pieceIndex;
nbBlocks =(short)((length +DiskManager.BLOCK_SIZE -1) /DiskManager.BLOCK_SIZE);
statusFlags = PIECE_STATUS_NEEDED;
}
public DiskManager getManager()
{
return diskManager;
}
public int getPieceNumber()
{
return pieceNumber;
}
/**
* @return int number of bytes in the piece
*/
public int getLength()
{
return( diskManager.getPieceLength( pieceNumber ));
}
public int getNbBlocks()
{
return nbBlocks;
}
public short
getReadCount()
{
return( read_count );
}
public void
setReadCount(
short c )
{
read_count = c;
}
public int getBlockSize(final int blockNumber)
{
if ( blockNumber == nbBlocks -1 ){
int len = getLength() % DiskManager.BLOCK_SIZE;
if ( len != 0 ){
return( len );
}
}
return DiskManager.BLOCK_SIZE;
}
public boolean
isSkipped()
{
final DMPieceList pieceList =diskManager.getPieceList(pieceNumber);
for (int i =0; i <pieceList.size(); i++){
if ( !pieceList.get(i).getFile().isSkipped()){
return( false );
}
}
return( true );
}
public boolean isNeeded()
{
return (statusFlags &PIECE_STATUS_NEEDED) !=0;
}
public boolean calcNeeded()
{
boolean filesNeeded =false;
final DMPieceList pieceList =diskManager.getPieceList(pieceNumber);
for (int i =0; i <pieceList.size(); i++)
{
final DiskManagerFileInfoImpl file =pieceList.get(i).getFile();
final long fileLength =file.getLength();
filesNeeded |=fileLength >0 &&file.getDownloaded() <fileLength &&!file.isSkipped();
}
if (filesNeeded)
{
statusFlags |=PIECE_STATUS_NEEDED;
return true;
}
statusFlags &=~PIECE_STATUS_NEEDED;
return false;
}
public void clearNeeded()
{
statusFlags &=~PIECE_STATUS_NEEDED;
}
public void setNeeded()
{
statusFlags |=PIECE_STATUS_NEEDED;
}
public void setNeeded(boolean b)
{
if (b)
statusFlags |=PIECE_STATUS_NEEDED;
else
statusFlags &=~PIECE_STATUS_NEEDED;
}
public boolean isWritten()
{
return (statusFlags &PIECE_STATUS_WRITTEN) !=0;
}
/** written[] can be null, in which case if the piece is Done,
* all blocks are complete otherwise no blocks are complete
*/
public boolean[] getWritten()
{
return written;
}
public boolean isWritten(final int blockNumber)
{
if (done)
return true;
final boolean[] writtenRef =written;
if (writtenRef ==null)
return false;
return writtenRef[blockNumber];
}
public int getNbWritten()
{
if (done)
return nbBlocks;
final boolean[] writtenRef =written;
if (writtenRef ==null)
return 0;
int res =0;
for (int i =0; i <nbBlocks; i++ )
{
if (writtenRef[i])
res++;
}
return res;
}
public void setWritten(final int blockNumber)
{
if (written ==null)
written =new boolean[nbBlocks];
final boolean[] written_ref =written;
written_ref[blockNumber] =true;
for (int i =0; i <nbBlocks; i++)
{
if (!written_ref[i])
return;
}
statusFlags |=PIECE_STATUS_WRITTEN;
}
public boolean isChecking()
{
return (statusFlags &PIECE_STATUS_CHECKING) !=0;
}
public void setChecking()
{
statusFlags |=PIECE_STATUS_CHECKING;
}
public boolean isNeedsCheck()
{
return !done &&(statusFlags &PIECE_STATUS_MASK_NEEDS_CHECK) ==PIECE_STATUS_WRITTEN;
}
// this cannot be implemented the same as others could be
// because the true state of Done is only determined by
// having gone through setDoneSupport()
public boolean calcDone()
{
return done;
}
public boolean isDone()
{
return done;
}
public void setDone(boolean b)
{
// we delegate this operation to the disk manager so it can synchronise the activity
if (b !=done)
{
diskManager.setPieceDone(this, b);
}
}
/** this is ONLY used by the disk manager to update the done state while synchronized
*i.e. don't use it else where!
* @param b
*/
public void setDoneSupport(final boolean b)
{
done =b;
if (done)
written =null;
}
public void setDownloadable()
{
setDone(false);
statusFlags &=~(PIECE_STATUS_MASK_DOWNLOADABLE);
calcNeeded(); // Needed wouldn't have been calced before if couldn't download more
}
public boolean isDownloadable()
{
return !done &&(statusFlags &PIECE_STATUS_MASK_DOWNLOADABLE) == PIECE_STATUS_NEEDED;
}
/**
* @return true if the piece is Needed and not Done
*/
public boolean isInteresting()
{
return !done &&(statusFlags &PIECE_STATUS_NEEDED) != 0;
}
public void reset()
{
setDownloadable();
written =null;
}
public void reDownloadBlock(int blockNumber)
{
final boolean[] written_ref = written;
if (written_ref !=null)
{
written_ref[blockNumber] =false;
setDownloadable();
}
}
/*
public static final void testStatus()
{
if (statusTested)
return;
statusTested =true;
int originalStatus =statusFlags;
for (int i =0; i <0x100; i++)
{
statusFlags =i;
Logger.log(new LogEvent(this, LOGID, LogEvent.LT_INFORMATION,
"Done:" +isDone()
+" Checking:" +isChecking()
+" Written:" +isWritten()
+" Downloaded:" +isDownloaded()
+" Requested:" +isRequested()
+" Needed:" +isNeeded()
+" Interesting:" +isInteresting()
+" Requestable:" +isRequestable()
+" EGMActive:" +isEGMActive()
+" EGMIgnored:" +isEGMIgnored()
));
}
statusFlags =originalStatus;
}
*/
public String
getString()
{
String text = "";
text += ( isNeeded()?"needed,":"" );
text += ( isDone()?"done,":"" );
if ( !isDone()){
text += ( isDownloadable()?"downable,":"" );
text += ( isWritten()?"written":("written " + getNbWritten())) + ",";
text += ( isChecking()?"checking":"" );
}
if ( text.endsWith(",")){
text = text.substring(0,text.length()-1);
}
return( text );
}
}