/* * 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; } } }