/*
* Created on 02-Aug-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.piecemapper.impl;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import org.gudy.azureus2.core3.disk.impl.DiskManagerFileInfoImpl;
import org.gudy.azureus2.core3.disk.impl.piecemapper.DMPieceList;
import org.gudy.azureus2.core3.disk.impl.piecemapper.DMPieceMap;
import org.gudy.azureus2.core3.disk.impl.piecemapper.DMPieceMapper;
import org.gudy.azureus2.core3.disk.impl.piecemapper.DMPieceMapperFile;
import org.gudy.azureus2.core3.internat.LocaleUtilDecoder;
import org.gudy.azureus2.core3.torrent.*;
import org.gudy.azureus2.core3.util.FileUtil;
import org.gudy.azureus2.core3.util.StringInterner;
/**
* @author parg
*
*/
public class
PieceMapperImpl
implements DMPieceMapper
{
private TOTorrent torrent;
private int last_piece_length;
protected ArrayList<fileInfo> btFileList = new ArrayList<fileInfo>();
public
PieceMapperImpl(
TOTorrent _torrent )
{
torrent = _torrent;
int piece_length = (int)torrent.getPieceLength();
int piece_count = torrent.getNumberOfPieces();
long total_length = torrent.getSize();
last_piece_length = (int) (total_length - ((long) (piece_count - 1) * (long)piece_length));
}
public void
construct(
LocaleUtilDecoder _locale_decoder,
String _save_name )
throws UnsupportedEncodingException
{
//build something to hold the filenames/sizes
TOTorrentFile[] torrent_files = torrent.getFiles();
if ( torrent.isSimpleTorrent()){
buildFileLookupTables( torrent_files[0], _save_name );
}else{
buildFileLookupTables( torrent_files, _locale_decoder );
}
}
// method for simple torrents
private void
buildFileLookupTables(
TOTorrentFile torrent_file,
String fileName )
{
// not needed as fileName already normalised
// fileName = FileUtil.convertOSSpecificChars( fileName, false );
btFileList.add(new PieceMapperImpl.fileInfo(torrent_file,"", fileName ));
}
private void
buildFileLookupTables(
TOTorrentFile[] torrent_files,
LocaleUtilDecoder locale_decoder )
throws UnsupportedEncodingException
{
char separator = File.separatorChar;
//for each file
for (int i = 0; i < torrent_files.length; i++) {
buildFileLookupTable(torrent_files[i], locale_decoder, separator);
}
}
/**
* Builds the path stored in fileDictionay, saving it in btFileList
* @param fileDictionay
* @param btFileList
* @param localeUtil
* @param separator
* @return the length of the file as stored in fileDictionay
*/
// refactored out of initialize() - Moti
// code further refactored for readibility
private void
buildFileLookupTable(
TOTorrentFile torrent_file,
LocaleUtilDecoder locale_decoder,
final char separator)
throws UnsupportedEncodingException
{
//build the path
byte[][] path_components = torrent_file.getPathComponents();
/* replaced the following two calls:
StringBuffer pathBuffer = new StringBuffer(256);
pathBuffer.setLength(0);
*/
StringBuffer pathBuffer = new StringBuffer(0);
int lastIndex = path_components.length - 1;
for (int j = 0; j < lastIndex; j++) {
//attach every element
String comp = locale_decoder.decodeString( path_components[j]);
comp = FileUtil.convertOSSpecificChars( comp, true);
pathBuffer.append(comp);
pathBuffer.append(separator);
}
//no, then we must be a part of the path
//add the file entry to the file holder list
String last_comp = locale_decoder.decodeString(path_components[lastIndex]);
last_comp = FileUtil.convertOSSpecificChars( last_comp, false );
btFileList.add(
new fileInfo(
torrent_file,
pathBuffer.toString(),
last_comp ));
}
public DMPieceMap
getPieceMap()
{
if ( btFileList.size() == 1 ){
// optimise for the single file case
return( new DMPieceMapSimple( torrent, ((fileInfo)btFileList.get(0)).getFileInfo()));
}else{
int piece_length = (int)torrent.getPieceLength();
int piece_count = torrent.getNumberOfPieces();
long total_length = torrent.getSize();
DMPieceList[] pieceMap = new DMPieceList[piece_count];
//for every piece, except the last one
//add files to the piece list until we have built enough space to hold the piece
//see how much space is available in the file
//if the space available isnt 0
//add the file to the piece->file mapping list
//if there is enough space available, stop
//fix for 1 piece torrents
int modified_piece_length = piece_length;
if (total_length < modified_piece_length) {
modified_piece_length = (int)total_length;
}
long fileOffset = 0;
int currentFile = 0;
for (int i = 0;(1 == piece_count && i < piece_count) || i < piece_count - 1; i++) {
ArrayList<PieceMapEntryImpl> pieceToFileList = new ArrayList<PieceMapEntryImpl>();
int usedSpace = 0;
while (modified_piece_length > usedSpace) {
fileInfo tempFile = (fileInfo)btFileList.get(currentFile);
long length = tempFile.getLength();
//get the available space
long availableSpace = length - fileOffset;
PieceMapEntryImpl tempPieceEntry = null;
//how much space do we need to use?
if (availableSpace <= (modified_piece_length - usedSpace)) {
//use the rest of the file's space
tempPieceEntry =
new PieceMapEntryImpl(tempFile.getFileInfo(), fileOffset, (int)availableSpace //safe to convert here
);
//update the used space
usedSpace += availableSpace;
//update the file offset
fileOffset = 0;
//move the the next file
currentFile++;
} else //we don't need to use the whole file
{
tempPieceEntry = new PieceMapEntryImpl(tempFile.getFileInfo(), fileOffset, modified_piece_length - usedSpace);
//update the file offset
fileOffset += modified_piece_length - usedSpace;
//udate the used space
usedSpace += modified_piece_length - usedSpace;
}
//add the temp pieceEntry to the piece list
pieceToFileList.add(tempPieceEntry);
}
//add the list to the map
pieceMap[i] = PieceListImpl.convert(pieceToFileList);
}
//take care of final piece if there was more than 1 piece in the torrent
if (piece_count > 1) {
pieceMap[piece_count - 1] =
PieceListImpl.convert(
buildLastPieceToFileList(
btFileList,
currentFile,
fileOffset ));
}
return( new DMPieceMapImpl( pieceMap ));
}
}
private List<PieceMapEntryImpl>
buildLastPieceToFileList(
List<fileInfo> file_list,
int current_file,
long file_offset )
{
ArrayList<PieceMapEntryImpl> piece_to_file_list = new ArrayList<PieceMapEntryImpl>();
for ( int i=current_file;i<file_list.size();i++){
fileInfo file = file_list.get( i );
long space_in_file = file.getLength() - file_offset;
PieceMapEntryImpl piece_entry = new PieceMapEntryImpl( file.getFileInfo(), file_offset, (int)space_in_file);
piece_to_file_list.add( piece_entry );
file_offset = 0;
}
return( piece_to_file_list );
}
public long
getTotalLength()
{
return( torrent.getSize());
}
public int
getPieceLength()
{
return((int)torrent.getPieceLength());
}
public int
getLastPieceLength()
{
return( last_piece_length );
}
public DMPieceMapperFile[]
getFiles()
{
DMPieceMapperFile[] res = new DMPieceMapperFile[ btFileList.size()];
btFileList.toArray( res );
return( res );
}
protected static class
fileInfo
implements DMPieceMapperFile
{
private DiskManagerFileInfoImpl file;
private TOTorrentFile torrent_file;
private String path;
private String name;
public
fileInfo(
TOTorrentFile _torrent_file,
String _path,
String _name )
{
torrent_file = _torrent_file;
path = StringInterner.intern(_path);
name = _name;
}
public long getLength() {
return torrent_file.getLength();
}
public File
getDataFile()
{
return( new File( path, name ));
}
public TOTorrentFile
getTorrentFile()
{
return( torrent_file );
}
public DiskManagerFileInfoImpl getFileInfo() {
return file;
}
public void setFileInfo(DiskManagerFileInfoImpl _file) {
file = _file;
}
}
}