1 /*
2  * Created on 08-Oct-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;
21 
22 /**
23  * @author parg
24  * @author MjrTom
25  *			2005/Oct/08: startPriority/resumePriority handling and minor clock fixes
26  *			2006/Jan/02: refactoring, change booleans to statusFlags
27  */
28 
29 import org.gudy.azureus2.core3.disk.*;
30 import org.gudy.azureus2.core3.disk.impl.piecemapper.DMPieceList;
31 
32 public class DiskManagerPieceImpl
33 	implements DiskManagerPiece
34 {
35     //private static final LogIDs LOGID = LogIDs.PIECES;
36 
37 	private static final byte	PIECE_STATUS_NEEDED		= 0x01;	//want to have the piece
38 	private static final byte	PIECE_STATUS_WRITTEN	= 0x20;	//piece fully written to storage
39 	private static final byte	PIECE_STATUS_CHECKING	= 0x40;	//piece is being hash checked
40 
41     private static final byte PIECE_STATUS_MASK_DOWNLOADABLE	=
42     	PIECE_STATUS_CHECKING | PIECE_STATUS_WRITTEN | PIECE_STATUS_NEEDED;
43 
44     										// 0x65;    // Needed IS once again included in this
45 
46 	private static final byte PIECE_STATUS_MASK_NEEDS_CHECK 	= PIECE_STATUS_CHECKING | PIECE_STATUS_WRITTEN;
47 
48     //private static boolean statusTested =false;
49 
50     private final DiskManagerHelper	diskManager;
51 	private final int				pieceNumber;
52 
53 		/** the number of blocks in this piece: can be short as this gives up to .5GB piece sizes with 16K blocks */
54 
55 	private final short				nbBlocks;
56 
57 	// to save memory the "written" field is only maintained for pieces that are
58 	// downloading. A value of "null" means that either the piece hasn't started
59 	// download or that it is complete.
60 	// access to "written" is single-threaded (by the peer manager) apart from when
61 	// the disk manager is saving resume data.
62 	// actually this is not longer strictly true, as setDone is called asynchronously
63 	// however, this issue can be worked around by working on a reference to the written data
64 	// as problems only occur when switching from all-written to done=true, both of which signify
65 	// the same state of affairs.
66 
67 	protected volatile boolean[]	written;
68 
69     private byte         statusFlags;
70 
71 	/** it's *very* important to accurately maintain the "done" state of a piece. Currently the statusFlags
72 	 * are updated in a non-thread-safe manner so a 'done' field is maintained seperatly.  Synchronizing
73 	 * access to statusFlags or done would cause a tremendous performance hit.
74 	 */
75     private short		read_count;
76 
77     private boolean		done;
78 
DiskManagerPieceImpl(final DiskManagerHelper _disk_manager, final int pieceIndex, int length )79 	public DiskManagerPieceImpl(final DiskManagerHelper _disk_manager, final int pieceIndex, int length )
80 	{
81 		diskManager =_disk_manager;
82 		pieceNumber = pieceIndex;
83 
84 		nbBlocks =(short)((length +DiskManager.BLOCK_SIZE -1) /DiskManager.BLOCK_SIZE);
85 
86 		statusFlags = PIECE_STATUS_NEEDED;
87 	}
88 
getManager()89 	public DiskManager getManager()
90 	{
91 		return diskManager;
92 	}
93 
getPieceNumber()94 	public int getPieceNumber()
95 	{
96 		return pieceNumber;
97 	}
98 
99 	/**
100 	 * @return int number of bytes in the piece
101 	 */
getLength()102 	public int getLength()
103 	{
104 		return( diskManager.getPieceLength( pieceNumber ));
105 	}
106 
getNbBlocks()107 	public int getNbBlocks()
108 	{
109 		return nbBlocks;
110 	}
111 
112 	public short
getReadCount()113 	getReadCount()
114 	{
115 		return( read_count );
116 	}
117 
118 	public void
setReadCount( short c )119 	setReadCount(
120 		short	c )
121 	{
122 		read_count	= c;
123 	}
124 
getBlockSize(final int blockNumber)125     public int getBlockSize(final int blockNumber)
126     {
127         if ( blockNumber == nbBlocks -1 ){
128 
129         	int	len = getLength() % DiskManager.BLOCK_SIZE;
130 
131         	if ( len != 0 ){
132 
133         		return( len );
134         	}
135         }
136 
137         return DiskManager.BLOCK_SIZE;
138     }
139 
140     public boolean
isSkipped()141     isSkipped()
142     {
143 		final DMPieceList pieceList =diskManager.getPieceList(pieceNumber);
144 		for (int i =0; i <pieceList.size(); i++){
145 			final DiskManagerFileInfoImpl file =pieceList.get(i).getFile();
146 			if ( file == null ){
147 				return( false );	// can be null during diskmanager startup
148 			}
149 			if ( !file.isSkipped()){
150 				return( false );
151 			}
152 		}
153 		return( true );
154     }
155 
isNeeded()156 	public boolean isNeeded()
157 	{
158 		return (statusFlags &PIECE_STATUS_NEEDED) !=0;
159 	}
160 
calcNeeded()161 	public boolean calcNeeded()
162 	{
163 		boolean filesNeeded =false;
164 		final DMPieceList pieceList =diskManager.getPieceList(pieceNumber);
165 		for (int i =0; i <pieceList.size(); i++)
166 		{
167 			final DiskManagerFileInfoImpl file =pieceList.get(i).getFile();
168 			final long fileLength =file.getLength();
169 			filesNeeded |=fileLength >0 &&file.getDownloaded() <fileLength &&!file.isSkipped();
170 		}
171 		if (filesNeeded)
172 		{
173 			statusFlags |=PIECE_STATUS_NEEDED;
174 			return true;
175 		}
176 		statusFlags &=~PIECE_STATUS_NEEDED;
177 		return false;
178 	}
179 
180 	public boolean
181 	spansFiles()
182 	{
183 		DMPieceList pieceList = diskManager.getPieceList(pieceNumber);
184 
185 		return( pieceList.size() > 1 );
186 	}
187 
clearNeeded()188 	public void clearNeeded()
189 	{
190 		statusFlags &=~PIECE_STATUS_NEEDED;
191 	}
192 
setNeeded()193 	public void setNeeded()
194 	{
195 		statusFlags |=PIECE_STATUS_NEEDED;
196 	}
197 
setNeeded(boolean b)198 	public void setNeeded(boolean b)
199 	{
200 		if (b)
201 			statusFlags |=PIECE_STATUS_NEEDED;
202 		else
203 			statusFlags &=~PIECE_STATUS_NEEDED;
204 	}
205 
isWritten()206 	public boolean isWritten()
207 	{
208 		return (statusFlags &PIECE_STATUS_WRITTEN) !=0;
209 	}
210 
211 
212 	/** written[] can be null, in which case if the piece is Done,
213 	*  all blocks are complete otherwise no blocks are complete
214 	*/
getWritten()215 	public boolean[] getWritten()
216 	{
217 		return written;
218 	}
219 
isWritten(final int blockNumber)220 	public boolean isWritten(final int blockNumber)
221 	{
222 		if (done)
223 			return true;
224 		final boolean[] writtenRef =written;
225 		if (writtenRef ==null)
226 			return false;
227 		return writtenRef[blockNumber];
228 	}
229 
getNbWritten()230 	public int getNbWritten()
231 	{
232 		if (done)
233 			return nbBlocks;
234 		final boolean[] writtenRef =written;
235 		if (writtenRef ==null)
236 			return 0;
237 		int res =0;
238 		for (int i =0; i <nbBlocks; i++ )
239 		{
240 			if (writtenRef[i])
241 				res++;
242 		}
243 		return res;
244 	}
245 
setWritten(final int blockNumber)246 	public void setWritten(final int blockNumber)
247 	{
248 		if (written ==null)
249 			written =new boolean[nbBlocks];
250 		final boolean[] written_ref =written;
251 
252 		written_ref[blockNumber] =true;
253 		for (int i =0; i <nbBlocks; i++)
254 		{
255 			if (!written_ref[i])
256 				return;
257 		}
258 		statusFlags |=PIECE_STATUS_WRITTEN;
259 	}
260 
isChecking()261 	public boolean isChecking()
262 	{
263 		return (statusFlags &PIECE_STATUS_CHECKING) !=0;
264 	}
265 
setChecking()266 	public void setChecking()
267 	{
268 		statusFlags |=PIECE_STATUS_CHECKING;
269 	}
270 
isNeedsCheck()271     public boolean isNeedsCheck()
272     {
273     	return !done &&(statusFlags &PIECE_STATUS_MASK_NEEDS_CHECK) ==PIECE_STATUS_WRITTEN;
274     }
275 
276 
277     // this cannot be implemented the same as others could be
278 	// because the true state of Done is only determined by
279 	// having gone through setDoneSupport()
calcDone()280 	public boolean calcDone()
281 	{
282 		return done;
283 	}
284 
isDone()285 	public boolean isDone()
286 	{
287 		return done;
288 	}
289 
setDone(boolean b)290 	public void setDone(boolean b)
291 	{
292 		// we delegate this operation to the disk manager so it can synchronise the activity
293         if (b !=done)
294         {
295             diskManager.setPieceDone(this, b);
296         }
297 	}
298 
299 	/** this is ONLY used by the disk manager to update the done state while synchronized
300 	 *i.e. don't use it else where!
301 	 * @param b
302 	 */
303 
setDoneSupport(final boolean b)304 	public void setDoneSupport(final boolean b)
305 	{
306         done =b;
307         if (done)
308             written =null;
309 	}
310 
setDownloadable()311 	public void setDownloadable()
312 	{
313 		setDone(false);
314 		statusFlags &=~(PIECE_STATUS_MASK_DOWNLOADABLE);
315 		calcNeeded();	// Needed wouldn't have been calced before if couldn't download more
316 	}
317 
isDownloadable()318 	public boolean isDownloadable()
319 	{
320 		return !done &&(statusFlags &PIECE_STATUS_MASK_DOWNLOADABLE) == PIECE_STATUS_NEEDED;
321 	}
322 
323 	/**
324 	 * @return true if the piece is Needed and not Done
325 	 */
isInteresting()326 	public boolean isInteresting()
327 	{
328 		return !done &&(statusFlags &PIECE_STATUS_NEEDED) != 0;
329 	}
330 
reset()331 	public void reset()
332 	{
333 		setDownloadable();
334 		written =null;
335 	}
336 
reDownloadBlock(int blockNumber)337 	public void reDownloadBlock(int blockNumber)
338 	{
339 		final boolean[] written_ref = written;
340 		if (written_ref !=null)
341 		{
342 			written_ref[blockNumber] =false;
343 			setDownloadable();
344 		}
345 	}
346 
347 
348     /*
349     public static final void testStatus()
350     {
351         if (statusTested)
352             return;
353 
354         statusTested =true;
355         int originalStatus =statusFlags;
356 
357         for (int i =0; i <0x100; i++)
358         {
359             statusFlags =i;
360             Logger.log(new LogEvent(this, LOGID, LogEvent.LT_INFORMATION,
361                 "Done:" +isDone()
362                 +"  Checking:" +isChecking()
363                 +"  Written:" +isWritten()
364                 +"  Downloaded:" +isDownloaded()
365                 +"  Requested:" +isRequested()
366                 +"  Needed:" +isNeeded()
367                 +"  Interesting:" +isInteresting()
368                 +"  Requestable:" +isRequestable()
369                 +"  EGMActive:" +isEGMActive()
370                 +"  EGMIgnored:" +isEGMIgnored()
371             ));
372         }
373         statusFlags =originalStatus;
374     }
375     */
376 
377 	public String
getString()378 	getString()
379 	{
380 		String	text = "";
381 
382 		text += ( isNeeded()?"needed,":"" );
383 		text += ( isDone()?"done,":"" );
384 
385 		if ( !isDone()){
386 			text += ( isDownloadable()?"downable,":"" );
387 			text += ( isWritten()?"written":("written " + getNbWritten())) + ",";
388 			text += ( isChecking()?"checking":"" );
389 		}
390 
391 		if ( text.endsWith(",")){
392 			text = text.substring(0,text.length()-1);
393 		}
394 		return( text );
395 	}
396 }
397