1 /*
2  * Created on 02-Aug-2004
3  * Created by Paul Gardner
4  * Copyright (C) Azureus Software, Inc, All Rights Reserved.
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
17  *
18  */
19 
20 package org.gudy.azureus2.core3.disk.impl.piecemapper.impl;
21 
22 import java.io.*;
23 import java.util.ArrayList;
24 import java.util.List;
25 
26 import org.gudy.azureus2.core3.disk.impl.DiskManagerFileInfoImpl;
27 import org.gudy.azureus2.core3.disk.impl.piecemapper.DMPieceList;
28 import org.gudy.azureus2.core3.disk.impl.piecemapper.DMPieceMap;
29 import org.gudy.azureus2.core3.disk.impl.piecemapper.DMPieceMapper;
30 import org.gudy.azureus2.core3.disk.impl.piecemapper.DMPieceMapperFile;
31 
32 import org.gudy.azureus2.core3.internat.LocaleUtilDecoder;
33 import org.gudy.azureus2.core3.torrent.*;
34 import org.gudy.azureus2.core3.util.FileUtil;
35 import org.gudy.azureus2.core3.util.StringInterner;
36 
37 /**
38  * @author parg
39  *
40  */
41 
42 
43 public class
44 PieceMapperImpl
45 	implements DMPieceMapper
46 {
47 	private TOTorrent			torrent;
48 
49 	private int				last_piece_length;
50 
51 	protected ArrayList<fileInfo> btFileList = new ArrayList<fileInfo>();
52 
53 
54 	public
PieceMapperImpl( TOTorrent _torrent )55 	PieceMapperImpl(
56 		TOTorrent		_torrent )
57 	{
58 		torrent 		= _torrent;
59 
60 		int piece_length	= (int)torrent.getPieceLength();
61 
62 		int piece_count		= torrent.getNumberOfPieces();
63 
64 		long total_length	= torrent.getSize();
65 
66 		last_piece_length  	= (int) (total_length - ((long) (piece_count - 1) * (long)piece_length));
67 	}
68 
69 	public void
construct( LocaleUtilDecoder _locale_decoder, String _save_name )70 	construct(
71 		LocaleUtilDecoder	_locale_decoder,
72 		String				_save_name )
73 
74 		throws UnsupportedEncodingException
75 	{
76 			//build something to hold the filenames/sizes
77 
78 		TOTorrentFile[] torrent_files = torrent.getFiles();
79 
80 		if ( torrent.isSimpleTorrent()){
81 
82 			buildFileLookupTables( torrent_files[0], _save_name );
83 
84 		}else{
85 
86 			buildFileLookupTables( torrent_files, _locale_decoder );
87 		}
88 	}
89 
90 	// method for simple torrents
91 
92 	private void
buildFileLookupTables( TOTorrentFile torrent_file, String fileName )93 	buildFileLookupTables(
94 		TOTorrentFile			torrent_file,
95 		String					fileName )
96 	{
97 		// not needed as fileName already normalised
98 		// fileName = FileUtil.convertOSSpecificChars( fileName,  false );
99 
100 		btFileList.add(new PieceMapperImpl.fileInfo(torrent_file,"", fileName ));
101 	}
102 
103 	private void
buildFileLookupTables( TOTorrentFile[] torrent_files, LocaleUtilDecoder locale_decoder )104 	buildFileLookupTables(
105 		TOTorrentFile[]			torrent_files,
106 		LocaleUtilDecoder 		locale_decoder )
107 
108 		throws UnsupportedEncodingException
109 	{
110 		char	separator = File.separatorChar;
111 
112 		 //for each file
113 
114 		for (int i = 0; i < torrent_files.length; i++) {
115 
116 			buildFileLookupTable(torrent_files[i], locale_decoder, separator);
117 		}
118 	}
119 
120 	/**
121 	 * Builds the path stored in fileDictionay, saving it in btFileList
122 	 * @param fileDictionay
123 	 * @param btFileList
124 	 * @param localeUtil
125 	 * @param separator
126 	 * @return the length of the file as stored in fileDictionay
127 	 */
128 	// refactored out of initialize() - Moti
129 	// code further refactored for readibility
130 
131 	private void
buildFileLookupTable( TOTorrentFile torrent_file, LocaleUtilDecoder locale_decoder, final char separator)132 	buildFileLookupTable(
133 		TOTorrentFile		torrent_file,
134 		LocaleUtilDecoder 	locale_decoder,
135 		final char 			separator)
136 
137 		throws UnsupportedEncodingException
138 	{
139 		//build the path
140 
141 		byte[][]	path_components = torrent_file.getPathComponents();
142 
143 		/* replaced the following two calls:
144 		StringBuffer pathBuffer = new StringBuffer(256);
145 		pathBuffer.setLength(0);
146 		*/
147 		StringBuffer pathBuffer = new StringBuffer(0);
148 
149 		int lastIndex = path_components.length - 1;
150 		for (int j = 0; j < lastIndex; j++) {
151 			//attach every element
152 
153 			String	comp = locale_decoder.decodeString( path_components[j]);
154 
155 			comp = FileUtil.convertOSSpecificChars( comp,  true);
156 
157 			pathBuffer.append(comp);
158 			pathBuffer.append(separator);
159 		}
160 
161 		//no, then we must be a part of the path
162 		//add the file entry to the file holder list
163 
164 		String	last_comp = locale_decoder.decodeString(path_components[lastIndex]);
165 
166 		last_comp = FileUtil.convertOSSpecificChars( last_comp, false );
167 
168 		btFileList.add(
169 			new fileInfo(
170 				torrent_file,
171 				pathBuffer.toString(),
172 				last_comp ));
173 	}
174 
175 
176 
177 	public DMPieceMap
getPieceMap()178 	getPieceMap()
179 	{
180 		if ( btFileList.size() == 1 ){
181 
182 				// optimise for the single file case
183 
184 			return( new DMPieceMapSimple( torrent, ((fileInfo)btFileList.get(0)).getFileInfo()));
185 
186 		}else{
187 			int piece_length	= (int)torrent.getPieceLength();
188 
189 			int piece_count		= torrent.getNumberOfPieces();
190 
191 			long total_length	= torrent.getSize();
192 
193 			DMPieceList[]	pieceMap = new DMPieceList[piece_count];
194 
195 
196 			//for every piece, except the last one
197 			//add files to the piece list until we have built enough space to hold the piece
198 			//see how much space is available in the file
199 			//if the space available isnt 0
200 			//add the file to the piece->file mapping list
201 			//if there is enough space available, stop
202 
203 				//fix for 1 piece torrents
204 
205 			int	modified_piece_length	= piece_length;
206 
207 			if (total_length < modified_piece_length) {
208 
209 				modified_piece_length = (int)total_length;
210 			}
211 
212 			long fileOffset = 0;
213 			int currentFile = 0;
214 			for (int i = 0;(1 == piece_count && i < piece_count) || i < piece_count - 1; i++) {
215 				ArrayList<PieceMapEntryImpl> pieceToFileList = new ArrayList<PieceMapEntryImpl>();
216 				int usedSpace = 0;
217 				while (modified_piece_length > usedSpace) {
218 					fileInfo tempFile = (fileInfo)btFileList.get(currentFile);
219 					long length = tempFile.getLength();
220 
221 					//get the available space
222 					long availableSpace = length - fileOffset;
223 
224 					PieceMapEntryImpl tempPieceEntry = null;
225 
226 					//how much space do we need to use?
227 					if (availableSpace <= (modified_piece_length - usedSpace)) {
228 						//use the rest of the file's space
229 							tempPieceEntry =
230 								new PieceMapEntryImpl(tempFile.getFileInfo(), fileOffset, (int)availableSpace //safe to convert here
231 		);
232 
233 						//update the used space
234 						usedSpace += availableSpace;
235 						//update the file offset
236 						fileOffset = 0;
237 						//move the the next file
238 						currentFile++;
239 					} else //we don't need to use the whole file
240 						{
241 						tempPieceEntry = new PieceMapEntryImpl(tempFile.getFileInfo(), fileOffset, modified_piece_length - usedSpace);
242 
243 						//update the file offset
244 						fileOffset += modified_piece_length - usedSpace;
245 						//udate the used space
246 						usedSpace += modified_piece_length - usedSpace;
247 					}
248 
249 					//add the temp pieceEntry to the piece list
250 					pieceToFileList.add(tempPieceEntry);
251 				}
252 
253 				//add the list to the map
254 				pieceMap[i] = PieceListImpl.convert(pieceToFileList);
255 			}
256 
257 			//take care of final piece if there was more than 1 piece in the torrent
258 			if (piece_count > 1) {
259 				pieceMap[piece_count - 1] =
260 					PieceListImpl.convert(
261 							buildLastPieceToFileList(
262 										btFileList,
263 										currentFile,
264 										fileOffset ));
265 
266 			}
267 
268 			return( new DMPieceMapImpl( pieceMap ));
269 		}
270 	}
271 
272 	private List<PieceMapEntryImpl>
buildLastPieceToFileList( List<fileInfo> file_list, int current_file, long file_offset )273 	buildLastPieceToFileList(
274 		List<fileInfo> 	file_list,
275 		int 			current_file,
276 		long 			file_offset )
277 	{
278 		ArrayList<PieceMapEntryImpl> piece_to_file_list = new ArrayList<PieceMapEntryImpl>();
279 
280 		for ( int i=current_file;i<file_list.size();i++){
281 
282 			fileInfo file = file_list.get( i );
283 
284 			long space_in_file = file.getLength() - file_offset;
285 
286 			PieceMapEntryImpl piece_entry = new PieceMapEntryImpl( file.getFileInfo(), file_offset, (int)space_in_file);
287 
288 			piece_to_file_list.add( piece_entry );
289 
290 			file_offset = 0;
291 		}
292 
293 		return( piece_to_file_list );
294 	}
295 
296 	public long
getTotalLength()297 	getTotalLength()
298 	{
299 		return( torrent.getSize());
300 	}
301 
302 	public int
getPieceLength()303 	getPieceLength()
304 	{
305 		return((int)torrent.getPieceLength());
306 	}
307 
308 	public int
getLastPieceLength()309 	getLastPieceLength()
310 	{
311 		return( last_piece_length );
312 	}
313 
314 	public DMPieceMapperFile[]
getFiles()315 	getFiles()
316 	{
317 		DMPieceMapperFile[]	res = new DMPieceMapperFile[ btFileList.size()];
318 
319 		btFileList.toArray( res );
320 
321 		return( res );
322 	}
323 
324 	protected static class
325 	fileInfo
326 		implements DMPieceMapperFile
327 	{
328 		private DiskManagerFileInfoImpl		file;
329 		private TOTorrentFile				torrent_file;
330 		private String 						path;
331 		private String 						name;
332 
333 		public
fileInfo( TOTorrentFile _torrent_file, String _path, String _name )334 		fileInfo(
335 			TOTorrentFile	_torrent_file,
336 			String 			_path,
337 			String 			_name )
338 		{
339 			torrent_file	= _torrent_file;
340 			path			= StringInterner.intern(_path);
341 			name 			= _name;
342 		}
343 
getLength()344 		public long getLength() {
345 			return torrent_file.getLength();
346 		}
347 		public File
getDataFile()348 		getDataFile()
349 		{
350 			return( new File( path, name ));
351 		}
352 		public TOTorrentFile
getTorrentFile()353 		getTorrentFile()
354 		{
355 			return( torrent_file );
356 		}
getFileInfo()357 		public DiskManagerFileInfoImpl getFileInfo() {
358 			return file;
359 		}
setFileInfo(DiskManagerFileInfoImpl _file)360 		public void setFileInfo(DiskManagerFileInfoImpl _file) {
361 			file = _file;
362 		}
363 	}
364 }
365