/*
* Created on 28-Sep-2005
* Created by Paul Gardner
* Copyright (C) 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 com.aelitis.azureus.core.diskmanager.file.impl;
import java.io.File;
import java.io.RandomAccessFile;
import java.util.HashMap;
import java.util.Map;
import org.gudy.azureus2.core3.torrent.TOTorrent;
import org.gudy.azureus2.core3.torrent.TOTorrentFile;
import org.gudy.azureus2.core3.util.Debug;
import org.gudy.azureus2.core3.util.DirectByteBuffer;
import org.gudy.azureus2.core3.util.FileUtil;
import com.aelitis.azureus.core.diskmanager.file.FMFile;
import com.aelitis.azureus.core.diskmanager.file.FMFileManagerException;
public class
FMFileAccessCompact
implements FMFileAccess
{
private final static byte SS = DirectByteBuffer.SS_FILE;
private TOTorrentFile torrent_file;
private int piece_size;
private File controlFileDir;
private String controlFileName;
private FMFileAccess delegate;
private volatile long current_length;
private long version = 0;
private volatile boolean write_required;
private long first_piece_start;
private long first_piece_length;
private long last_piece_start;
private long last_piece_length;
protected
FMFileAccessCompact(
TOTorrentFile _torrent_file,
File _controlFileDir,
String _controlFileName,
FMFileAccess _delegate )
throws FMFileManagerException
{
torrent_file = _torrent_file;
controlFileDir = _controlFileDir;
controlFileName = _controlFileName;
delegate = _delegate;
try{
piece_size = (int)torrent_file.getTorrent().getPieceLength();
TOTorrent torrent = torrent_file.getTorrent();
long file_length = torrent_file.getLength();
long file_offset_in_torrent = 0;
for (int i=0;i<torrent.getFiles().length;i++){
TOTorrentFile f = torrent.getFiles()[i];
if ( f == torrent_file ){
break;
}
file_offset_in_torrent += f.getLength();
}
int piece_offset = piece_size - (int)( file_offset_in_torrent % piece_size );
if ( piece_offset == piece_size ){
piece_offset = 0;
}
first_piece_length = piece_offset;
first_piece_start = 0;
if ( first_piece_length >= file_length ){
// first piece takes up all the file
first_piece_length = file_length;
last_piece_start = file_length;
last_piece_length = 0;
}else{
last_piece_length = ( file_length - piece_offset ) % piece_size;
last_piece_start = file_length - last_piece_length;
}
/*
System.out.println(
"file " + new String(torrent_file.getPathComponents()[0]) + ": " +
"off = " + file_offset_in_torrent + ", len = " + file_length + ", fp = " + first_piece_start + "/" + first_piece_length +
", lp = " + last_piece_start + "/" + last_piece_length );
*/
if ( !new File(controlFileDir,controlFileName).exists()){
if (!controlFileDir.isDirectory() && !FileUtil.mkdirs(controlFileDir)) {
throw new FMFileManagerException("Directory creation failed: " + controlFileDir);
}
}else{
readState();
}
}catch( Throwable e ){
throw( new FMFileManagerException( "Compact file init fail", e ));
}
}
protected long
getFirstPieceStart()
{
return( first_piece_start );
}
protected long
getFirstPieceLength()
{
return( first_piece_length );
}
protected long
getLastPieceStart()
{
return( last_piece_start );
}
protected long
getLastPieceLength()
{
return( last_piece_length );
}
public void
aboutToOpen()
throws FMFileManagerException
{
delegate.aboutToOpen();
}
public long
getLength(
RandomAccessFile raf )
throws FMFileManagerException
{
return( current_length );
}
public void
setLength(
RandomAccessFile raf,
long length )
throws FMFileManagerException
{
if ( length != current_length ){
current_length = length;
write_required = true;
}
}
protected void
read(
RandomAccessFile raf,
DirectByteBuffer buffer,
long position )
throws FMFileManagerException
{
int original_limit = buffer.limit(SS);
try{
int len = original_limit - buffer.position(SS);
// System.out.println( "compact: read - " + position + "/" + len );
// deal with any read access to the first piece
if ( position < first_piece_start + first_piece_length ){
int available = (int)( first_piece_start + first_piece_length - position );
if ( available >= len ){
// all they require is in the first piece
// System.out.println( " all in first piece" );
delegate.read( raf, new DirectByteBuffer[]{ buffer }, position );
position += len;
len = 0;
}else{
// read goes past end of first piece
// System.out.println( " part in first piece" );
buffer.limit( SS, buffer.position(SS) + available );
delegate.read( raf, new DirectByteBuffer[]{ buffer }, position );
buffer.limit( SS, original_limit );
position += available;
len -= available;
}
}
if ( len == 0 ){
return;
}
// position is at start of gap between start and end - work out how much,
// if any, space has been requested
long space = last_piece_start - position;
if ( space > 0 ){
if ( space >= len ){
// all they require is space
// System.out.println( " all in space" );
buffer.position( SS, original_limit );
position += len;
len = 0;
}else{
// read goes past end of space
// System.out.println( " part in space" );
buffer.position( SS, buffer.position(SS) + (int)space );
position += space;
len -= space;
}
}
if ( len == 0 ){
return;
}
// lastly read from last piece
// System.out.println( " some in last piece" );
delegate.read( raf, new DirectByteBuffer[]{ buffer }, ( position - last_piece_start ) + first_piece_length );
}finally{
buffer.limit(SS,original_limit);
}
}
public void
read(
RandomAccessFile raf,
DirectByteBuffer[] buffers,
long position )
throws FMFileManagerException
{
for (int i=0;i<buffers.length;i++){
DirectByteBuffer buffer = buffers[i];
int len = buffers[i].limit(SS) - buffers[i].position(SS);
read( raf, buffer, position );
int rem = buffers[i].remaining( SS );
position += len - rem;
if ( rem > 0 ){
break;
}
}
if ( position > current_length ){
setLength( raf, position );
}
}
protected void
write(
RandomAccessFile raf,
DirectByteBuffer buffer,
long position )
throws FMFileManagerException
{
int original_limit = buffer.limit(SS);
try{
int len = original_limit - buffer.position(SS);
// System.out.println( "compact: write - " + position + "/" + len );
// deal with any write access to the first piece
if ( position < first_piece_start + first_piece_length ){
int available = (int)( first_piece_start + first_piece_length - position );
if ( available >= len ){
// all they require is in the first piece
// System.out.println( " all in first piece" );
delegate.write( raf, new DirectByteBuffer[]{buffer}, position );
position += len;
len = 0;
}else{
// write goes past end of first piece
// System.out.println( " part of first piece" );
buffer.limit( SS, buffer.position(SS) + available );
delegate.write( raf, new DirectByteBuffer[]{buffer}, position );
buffer.limit( SS, original_limit );
position += available;
len -= available;
}
}
if ( len == 0 ){
return;
}
// position is at start of gap between start and end - work out how much,
// if any, space has been requested
long space = last_piece_start - position;
if ( space > 0 ){
if ( space >= len ){
// System.out.println( " all in space" );
// all they require is space
buffer.position( SS, original_limit );
position += len;
len = 0;
}else{
// write goes past end of space
// System.out.println( " part in space" );
buffer.position( SS, buffer.position(SS) + (int)space );
position += space;
len -= space;
}
}
if ( len == 0 ){
return;
}
// lastly write to last piece
// System.out.println( " some in last piece" );
delegate.write( raf, new DirectByteBuffer[]{buffer}, ( position - last_piece_start ) + first_piece_length );
}finally{
buffer.limit(SS,original_limit);
}
}
public void
write(
RandomAccessFile raf,
DirectByteBuffer[] buffers,
long position )
throws FMFileManagerException
{
for (int i=0;i<buffers.length;i++){
DirectByteBuffer buffer = buffers[i];
int len = buffers[i].limit(SS) - buffers[i].position(SS);
write( raf, buffer, position );
position += len;
}
if ( position > current_length ){
setLength( raf, position );
}
}
public void
flush()
throws FMFileManagerException
{
writeState();
}
public boolean
isPieceCompleteProcessingNeeded(
int piece_number )
{
return( false );
}
public void
setPieceComplete(
RandomAccessFile raf,
int piece_number,
DirectByteBuffer piece_data )
throws FMFileManagerException
{
}
protected void
readState()
throws FMFileManagerException
{
try{
Map data =
FileUtil.readResilientFile( controlFileDir, controlFileName, false );
if ( data != null && data.size() > 0 ){
Long version = (Long)data.get( "version" );
Long length = (Long)data.get( "length" );
current_length = length.longValue();
}
}catch( Throwable e ){
throw( new FMFileManagerException( "Failed to read control file state", e ));
}
}
protected void
writeState()
throws FMFileManagerException
{
boolean write = write_required;
if ( write ){
write_required = false;
try{
Map data = new HashMap();
data.put( "version", new Long( version ));
data.put( "length", new Long( current_length ));
FileUtil.writeResilientFile(
controlFileDir, controlFileName, data, false );
}catch( Throwable e ){
throw( new FMFileManagerException( "Failed to write control file state", e ));
}
}
}
public FMFileImpl
getFile()
{
return( delegate.getFile());
}
public String
getString()
{
return( "compact" );
}
}