2010-10-15 18:12:29 -04:00
/*
* libretroshare / src / ft : ftdata . cc
*
* File Transfer for RetroShare .
*
* Copyright 2010 by Cyril Soler
*
* This library is free software ; you can redistribute it and / or
* modify it under the terms of the GNU Library General Public
* License Version 2 as published by the Free Software Foundation .
*
* This library 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
* Library General Public License for more details .
*
* You should have received a copy of the GNU Library General Public
* License along with this library ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307
* USA .
*
* Please report all bugs and problems to " csoler@users.sourceforge.net " .
*
*/
/********
* # define DEBUG_FTCHUNK 1
* * * * * * * * */
2010-01-21 07:31:00 -05:00
# ifdef DEBUG_FTCHUNK
2009-12-08 17:29:52 -05:00
# include <assert.h>
2010-01-21 07:31:00 -05:00
# endif
# include <math.h>
2009-12-08 17:29:52 -05:00
# include <stdlib.h>
2010-08-06 05:40:23 -04:00
# include "retroshare/rspeers.h"
2009-11-19 03:35:11 -05:00
# include "ftchunkmap.h"
2010-01-20 17:02:43 -05:00
# include <time.h>
2009-11-19 03:35:11 -05:00
2013-12-09 08:35:06 -05:00
static const uint32_t SOURCE_CHUNK_MAP_UPDATE_PERIOD = 60 ; //! TTL for chunkmap info
static const uint32_t INACTIVE_CHUNK_TIME_LAPSE = 3600 ; //! TTL for an inactive chunk
static const uint32_t FT_CHUNKMAP_MAX_CHUNK_JUMP = 50 ; //! Maximum chunk jump in progressive DL mode
2010-01-11 11:00:42 -05:00
2009-12-08 17:29:52 -05:00
std : : ostream & operator < < ( std : : ostream & o , const ftChunk & c )
{
2010-12-07 12:09:21 -05:00
return o < < " \t Chunk [ " < < c . offset < < " ] size: " < < c . size < < " ChunkId: " < < c . id < < " Age: " < < time ( NULL ) - c . ts < < " , owner: " < < c . peer_id ;
2009-12-08 17:29:52 -05:00
}
// Chunk: very bold implementation for now. We should compress the bits to have
// 32 of them per uint32_t value, of course!
//
Chunk : : Chunk ( uint64_t start , uint32_t size )
: _start ( start ) , _offset ( start ) , _end ( ( uint64_t ) size + start )
2009-11-19 03:35:11 -05:00
{
2009-12-08 17:29:52 -05:00
}
2009-11-19 03:35:11 -05:00
2009-12-08 17:29:52 -05:00
void Chunk : : getSlice ( uint32_t size_hint , ftChunk & chunk )
{
// Take the current offset
chunk . offset = _offset ;
chunk . size = std : : min ( size_hint , ( uint32_t ) ( _end - _offset ) ) ;
chunk . id = _offset ;
chunk . ts = time ( NULL ) ;
// push the slice marker into currently handled slices.
_offset + = chunk . size ;
}
2010-11-22 15:57:53 -05:00
ChunkMap : : ChunkMap ( uint64_t s , bool availability )
2010-01-11 17:38:18 -05:00
: _file_size ( s ) ,
2010-11-22 15:57:53 -05:00
_chunk_size ( CHUNKMAP_FIXED_CHUNK_SIZE ) , // 1MB chunks
_assume_availability ( availability )
2009-12-08 17:29:52 -05:00
{
uint64_t n = s / ( uint64_t ) _chunk_size ;
if ( s % ( uint64_t ) _chunk_size ! = 0 )
2009-11-19 03:35:11 -05:00
+ + n ;
2009-12-08 17:29:52 -05:00
_map . resize ( n , FileChunksInfo : : CHUNK_OUTSTANDING ) ;
2013-02-28 15:42:01 -05:00
_strategy = FileChunksInfo : : CHUNK_STRATEGY_PROGRESSIVE ;
2010-02-17 17:10:12 -05:00
_total_downloaded = 0 ;
_file_is_complete = false ;
2009-12-08 17:29:52 -05:00
# ifdef DEBUG_FTCHUNK
std : : cerr < < " *** ChunkMap::ChunkMap: starting new chunkmap: " < < std : : endl ;
std : : cerr < < " File size: " < < s < < std : : endl ;
std : : cerr < < " Strategy: " < < _strategy < < std : : endl ;
std : : cerr < < " ChunkSize: " < < _chunk_size < < std : : endl ;
std : : cerr < < " Number of Chunks: " < < n < < std : : endl ;
2010-10-15 18:12:29 -04:00
std : : cerr < < " Data: " ;
2011-10-23 14:43:58 -04:00
for ( uint32_t i = 0 ; i < _map . size ( ) ; + + i )
2010-10-15 18:12:29 -04:00
std : : cerr < < _map [ i ] ;
std : : cerr < < std : : endl ;
2009-12-08 17:29:52 -05:00
# endif
}
2010-01-11 11:00:42 -05:00
void ChunkMap : : setAvailabilityMap ( const CompressedChunkMap & map )
2009-12-10 17:55:27 -05:00
{
2012-11-13 15:13:09 -05:00
// do some sanity check
//
if ( ( ( ( int ) _map . size ( ) - 1 ) > > 5 ) > = map . _map . size ( ) )
{
std : : cerr < < " ChunkMap::setPeerAvailabilityMap: Compressed chunkmap received is too small or corrupted. " < < std : : endl ;
return ;
}
// copy the map
//
2010-02-17 17:10:12 -05:00
_file_is_complete = true ;
_total_downloaded = 0 ;
2009-12-10 17:55:27 -05:00
for ( uint32_t i = 0 ; i < _map . size ( ) ; + + i )
2010-01-11 11:00:42 -05:00
if ( map [ i ] > 0 )
{
_map [ i ] = FileChunksInfo : : CHUNK_DONE ;
2010-02-02 17:15:42 -05:00
_total_downloaded + = sizeOfChunk ( i ) ;
2010-01-11 11:00:42 -05:00
}
else
2010-02-17 17:10:12 -05:00
{
2010-01-11 11:00:42 -05:00
_map [ i ] = FileChunksInfo : : CHUNK_OUTSTANDING ;
2010-02-17 17:10:12 -05:00
_file_is_complete = false ;
}
2009-12-10 17:55:27 -05:00
}
2009-12-08 17:29:52 -05:00
void ChunkMap : : dataReceived ( const ftChunk : : ChunkId & cid )
{
// 1 - find which chunk contains the received data.
//
// trick: cid is the chunk offset. So we use it to get the chunk number.
int n = ( uint64_t ) cid / _chunk_size ;
std : : map < ChunkNumber , ChunkDownloadInfo > : : iterator itc ( _slices_to_download . find ( n ) ) ;
if ( itc = = _slices_to_download . end ( ) )
{
std : : cerr < < " !!! ChunkMap::dataReceived: error: ChunkId " < < cid < < " corresponds to chunk number " < < n < < " , which is not being downloaded! " < < std : : endl ;
2010-01-21 07:31:00 -05:00
# ifdef DEBUG_FTCHUNK
2009-12-08 17:29:52 -05:00
assert ( false ) ;
2010-01-21 07:31:00 -05:00
# endif
2009-12-08 17:29:52 -05:00
return ;
}
std : : map < ftChunk : : ChunkId , uint32_t > : : iterator it ( itc - > second . _slices . find ( cid ) ) ;
if ( it = = itc - > second . _slices . end ( ) )
{
std : : cerr < < " !!! ChunkMap::dataReceived: chunk " < < cid < < " is not found in slice lst of chunk number " < < n < < std : : endl ;
2010-01-21 07:31:00 -05:00
# ifdef DEBUG_FTCHUNK
2009-12-08 17:29:52 -05:00
assert ( false ) ;
2010-01-21 07:31:00 -05:00
# endif
2009-12-08 17:29:52 -05:00
return ;
}
_total_downloaded + = it - > second ;
itc - > second . _remains - = it - > second ;
itc - > second . _slices . erase ( it ) ;
2010-01-21 07:31:00 -05:00
itc - > second . _last_data_received = time ( NULL ) ; // update time stamp
2009-12-08 17:29:52 -05:00
# ifdef DEBUG_FTCHUNK
std : : cerr < < " *** ChunkMap::dataReceived: received data chunk " < < cid < < " for chunk number " < < n < < " , local remains= " < < itc - > second . _remains < < " , total downloaded= " < < _total_downloaded < < " , remains= " < < _file_size - _total_downloaded < < std : : endl ;
# endif
if ( itc - > second . _remains = = 0 ) // the chunk was completely downloaded
{
# ifdef DEBUG_FTCHUNK
std : : cerr < < " *** ChunkMap::dataReceived: Chunk is complete. Removing it. " < < std : : endl ;
# endif
2012-03-24 10:45:33 -04:00
2012-12-27 16:16:33 -05:00
_map [ n ] = FileChunksInfo : : CHUNK_CHECKING ;
2012-03-24 10:45:33 -04:00
if ( n > 0 | | _file_size > CHUNKMAP_FIXED_CHUNK_SIZE ) // dont' put <1MB files into checking mode. This is useless.
_chunks_checking_queue . push_back ( n ) ;
2012-06-21 15:24:14 -04:00
else
_map [ n ] = FileChunksInfo : : CHUNK_DONE ;
2012-03-15 15:55:43 -04:00
2009-12-08 17:29:52 -05:00
_slices_to_download . erase ( itc ) ;
2010-02-17 17:10:12 -05:00
2012-03-15 15:55:43 -04:00
updateTotalDownloaded ( ) ;
}
}
void ChunkMap : : updateTotalDownloaded ( )
{
_total_downloaded = 0 ;
_file_is_complete = true ;
// First, round over chunk map to get the raw info.
//
for ( uint32_t i = 0 ; i < _map . size ( ) ; + + i )
switch ( _map [ i ] )
{
case FileChunksInfo : : CHUNK_CHECKING : _file_is_complete = false ;
case FileChunksInfo : : CHUNK_DONE : _total_downloaded + = sizeOfChunk ( i ) ;
break ;
default :
_file_is_complete = false ;
}
// Then go through active chunks.
//
for ( std : : map < ChunkNumber , ChunkDownloadInfo > : : const_iterator itc ( _slices_to_download . begin ( ) ) ; itc ! = _slices_to_download . end ( ) ; + + itc )
{
if ( _map [ itc - > first ] = = FileChunksInfo : : CHUNK_CHECKING )
_total_downloaded - = sizeOfChunk ( itc - > first ) ;
_total_downloaded + = sizeOfChunk ( itc - > first ) - itc - > second . _remains ;
if ( _file_is_complete )
std : : cerr < < " ChunkMap::updateTotalDownloaded(): ERROR: file still has pending slices but all chunks are marked as DONE !! " < < std : : endl ;
}
}
2012-07-19 16:52:04 -04:00
void ChunkMap : : getChunksToCheck ( std : : vector < uint32_t > & chunks_crc_to_ask )
2012-03-15 15:55:43 -04:00
{
chunks_crc_to_ask . clear ( ) ;
for ( uint32_t i = 0 ; i < _chunks_checking_queue . size ( ) ; )
{
2012-07-19 16:52:04 -04:00
chunks_crc_to_ask . push_back ( _chunks_checking_queue [ i ] ) ;
2012-03-15 15:55:43 -04:00
// remove that chunk from the queue
_chunks_checking_queue [ i ] = _chunks_checking_queue . back ( ) ;
_chunks_checking_queue . pop_back ( ) ;
}
}
void ChunkMap : : setChunkCheckingResult ( uint32_t chunk_number , bool check_succeeded )
{
// Find the chunk is the waiting queue. Remove it, and mark it as done.
//
if ( _map [ chunk_number ] ! = FileChunksInfo : : CHUNK_CHECKING )
{
std : : cerr < < " (EE) ChunkMap: asked to set checking result of chunk " < < chunk_number < < " that is not marked as being checked!! " < < std : : endl ;
return ;
}
if ( check_succeeded )
{
_map [ chunk_number ] = FileChunksInfo : : CHUNK_DONE ;
2010-02-17 17:10:12 -05:00
// We also check whether the file is complete or not.
_file_is_complete = true ;
for ( uint32_t i = 0 ; i < _map . size ( ) ; + + i )
if ( _map [ i ] ! = FileChunksInfo : : CHUNK_DONE )
{
_file_is_complete = false ;
break ;
}
2009-12-08 17:29:52 -05:00
}
2012-03-15 15:55:43 -04:00
else
{
_total_downloaded - = sizeOfChunk ( chunk_number ) ; // restore completion.
_map [ chunk_number ] = FileChunksInfo : : CHUNK_OUTSTANDING ;
}
2009-11-19 03:35:11 -05:00
}
2009-12-08 17:29:52 -05:00
// Warning: a chunk may be empty, but still being downloaded, so asking new slices from it
// will produce slices of size 0. This happens at the end of each chunk.
// --> I need to get slices from the next chunk, in such a case.
// --> solution:
// - have too chunks maps:
// 1 indexed by peer id to feed the getChunk method
// - chunks pushed when new chunks are needed
// - chunks removed when empty
// 1 indexed by chunk id to account for chunks being downloaded
// - chunks pushed when new chunks are needed
// - chunks removed when completely downloaded
//
2014-03-17 16:56:06 -04:00
bool ChunkMap : : getDataChunk ( const RsPeerId & peer_id , uint32_t size_hint , ftChunk & chunk , bool & source_chunk_map_needed )
2009-11-19 03:35:11 -05:00
{
2009-12-08 17:29:52 -05:00
# ifdef DEBUG_FTCHUNK
std : : cerr < < " *** ChunkMap::getDataChunk: size_hint = " < < size_hint < < std : : endl ;
# endif
// 1 - find if this peer already has an active chunk.
//
2014-03-17 16:56:06 -04:00
std : : map < RsPeerId , Chunk > : : iterator it = _active_chunks_feed . find ( peer_id ) ;
std : : map < RsPeerId , Chunk > : : iterator falsafe_it = _active_chunks_feed . end ( ) ;
2009-11-19 03:35:11 -05:00
2009-12-08 17:29:52 -05:00
if ( it = = _active_chunks_feed . end ( ) )
{
2012-02-16 16:10:46 -05:00
SourceChunksInfo * sci = getSourceChunksInfo ( peer_id ) ;
2012-05-09 17:31:05 -04:00
// 0 - Look into other pending chunks and slice from here. We only consider chunks with size smaller than
// the requested size,
2009-12-08 17:29:52 -05:00
//
2014-03-17 16:56:06 -04:00
for ( std : : map < RsPeerId , Chunk > : : iterator pit ( _active_chunks_feed . begin ( ) ) ; pit ! = _active_chunks_feed . end ( ) ; + + pit )
2011-10-23 14:43:58 -04:00
{
2012-05-09 17:31:05 -04:00
uint32_t c = pit - > second . _start / _chunk_size ;
if ( ! ( sci - > is_full | | sci - > cmap [ c ] ) ) // check that the chunk is available for requested peer.
continue ;
ChunkDownloadInfo & cdi ( _slices_to_download [ c ] ) ;
falsafe_it = pit ; // let's keep this one just in case.
if ( cdi . _slices . rbegin ( ) ! = cdi . _slices . rend ( ) & & cdi . _slices . rbegin ( ) - > second * 0.7 < = ( float ) size_hint )
2011-10-23 14:43:58 -04:00
{
it = pit ;
# ifdef DEBUG_FTCHUNK
std : : cerr < < " *** ChunkMap::getDataChunk: Sharing slice " < < pit - > second . _start < < " of peer " < < pit - > first < < " for peer " < < peer_id < < std : : endl ;
# endif
break ;
}
2012-05-09 17:31:05 -04:00
# ifdef DEBUG_FTCHUNK
else
std : : cerr < < " *** ChunkMap::getDataChunk: Not Sharing slice " < < pit - > second . _start < < " of peer " < < pit - > first < < " for peer " < < peer_id < < " , because current peer is too slow " < < std : : endl ;
# endif
2011-10-23 14:43:58 -04:00
}
2009-12-08 17:29:52 -05:00
2012-05-09 17:31:05 -04:00
if ( it = = _active_chunks_feed . end ( ) ) // not found. Find a new chunk.
2011-10-23 14:43:58 -04:00
{
// 1 - select an available chunk id for this peer.
//
uint32_t c = getAvailableChunk ( peer_id , source_chunk_map_needed ) ;
if ( c > = _map . size ( ) )
2012-05-09 17:31:05 -04:00
if ( falsafe_it ! = _active_chunks_feed . end ( ) ) // no chunk available. Let's see if we can still take an active--faster--chunk.
{
it = falsafe_it ;
2009-12-08 17:29:52 -05:00
# ifdef DEBUG_FTCHUNK
2012-05-09 17:31:05 -04:00
std : : cerr < < " *** ChunkMap::getDataChunk: Using falsafe chunk " < < it - > second . _start < < " of peer " < < it - > first < < " for peer " < < peer_id < < std : : endl ;
2009-12-08 17:29:52 -05:00
# endif
2012-05-09 17:31:05 -04:00
}
else
return false ; // no more availabel chunks, no falsafe case.
else
{
// 2 - add the chunk in the list of active chunks, and mark it as being downloaded
//
uint32_t soc = sizeOfChunk ( c ) ;
_active_chunks_feed [ peer_id ] = Chunk ( c * ( uint64_t ) _chunk_size , soc ) ;
_map [ c ] = FileChunksInfo : : CHUNK_ACTIVE ;
_slices_to_download [ c ] . _remains = soc ; // init the list of slices to download
it = _active_chunks_feed . find ( peer_id ) ;
# ifdef DEBUG_FTCHUNK
std : : cout < < " *** ChunkMap::getDataChunk: Allocating new chunk " < < c < < " for peer " < < peer_id < < std : : endl ;
# endif
}
2011-10-23 14:43:58 -04:00
}
2009-12-08 17:29:52 -05:00
}
# ifdef DEBUG_FTCHUNK
else
std : : cout < < " *** ChunkMap::getDataChunk: Re-using chunk " < < it - > second . _start / _chunk_size < < " for peer " < < peer_id < < std : : endl ;
# endif
// Get the first slice of the chunk, that is at most of length size
//
2011-10-23 14:43:58 -04:00
it - > second . getSlice ( size_hint , chunk ) ;
2009-12-08 17:29:52 -05:00
_slices_to_download [ chunk . offset / _chunk_size ] . _slices [ chunk . id ] = chunk . size ;
2010-01-21 07:31:00 -05:00
_slices_to_download [ chunk . offset / _chunk_size ] . _last_data_received = time ( NULL ) ;
2009-12-08 17:29:52 -05:00
2010-12-07 12:09:21 -05:00
chunk . peer_id = peer_id ;
2011-10-23 14:43:58 -04:00
2009-12-08 17:29:52 -05:00
# ifdef DEBUG_FTCHUNK
2011-10-23 14:43:58 -04:00
std : : cout < < " *** ChunkMap::getDataChunk: returning slice " < < chunk < < " for peer " < < it - > first < < std : : endl ;
2009-12-08 17:29:52 -05:00
# endif
2013-03-10 18:29:25 -04:00
if ( it - > second . empty ( ) )
_active_chunks_feed . erase ( it ) ;
2009-12-08 17:29:52 -05:00
return true ;
2009-11-19 03:35:11 -05:00
}
2010-01-21 07:31:00 -05:00
void ChunkMap : : removeInactiveChunks ( std : : vector < ftChunk : : ChunkId > & to_remove )
{
to_remove . clear ( ) ;
time_t now = time ( NULL ) ;
for ( std : : map < ChunkNumber , ChunkDownloadInfo > : : iterator it ( _slices_to_download . begin ( ) ) ; it ! = _slices_to_download . end ( ) ; )
if ( now - it - > second . _last_data_received > ( int ) INACTIVE_CHUNK_TIME_LAPSE )
{
# ifdef DEBUG_FTCHUNK
std : : cerr < < " ChunkMap::removeInactiveChunks(): removing inactive chunk " < < it - > first < < " , time lapse= " < < now - it - > second . _last_data_received < < std : : endl ;
# endif
// First, remove all slices from this chunk
//
std : : map < ChunkNumber , ChunkDownloadInfo > : : iterator tmp ( it ) ;
for ( std : : map < ftChunk : : ChunkId , uint32_t > : : const_iterator it2 ( it - > second . _slices . begin ( ) ) ; it2 ! = it - > second . _slices . end ( ) ; + + it2 )
to_remove . push_back ( it2 - > first ) ;
_map [ it - > first ] = FileChunksInfo : : CHUNK_OUTSTANDING ; // reset the chunk
2010-03-01 15:46:29 -05:00
_total_downloaded - = ( sizeOfChunk ( it - > first ) - it - > second . _remains ) ; // restore completion.
2010-01-21 07:31:00 -05:00
// Also remove the chunk from the chunk feed, to free the associated peer.
//
2014-03-17 16:56:06 -04:00
for ( std : : map < RsPeerId , Chunk > : : iterator it3 = _active_chunks_feed . begin ( ) ; it3 ! = _active_chunks_feed . end ( ) ; )
2010-01-21 07:31:00 -05:00
if ( it3 - > second . _start = = _chunk_size * uint64_t ( it - > first ) )
{
2014-03-17 16:56:06 -04:00
std : : map < RsPeerId , Chunk > : : iterator tmp3 = it3 ;
2010-01-21 07:31:00 -05:00
+ + it3 ;
_active_chunks_feed . erase ( tmp3 ) ;
}
else
+ + it3 ;
+ + it ;
_slices_to_download . erase ( tmp ) ;
}
else
+ + it ;
}
2015-03-08 09:46:07 -04:00
bool ChunkMap : : isChunkAvailable ( uint64_t offset , uint32_t chunk_size ) const
{
return hasChunkState ( offset , chunk_size , FileChunksInfo : : CHUNK_DONE ) ;
}
bool ChunkMap : : isChunkOutstanding ( uint64_t offset , uint32_t chunk_size ) const
{
return hasChunkState ( offset , chunk_size , FileChunksInfo : : CHUNK_OUTSTANDING ) ;
}
bool ChunkMap : : hasChunkState ( uint64_t offset , uint32_t chunk_size , FileChunksInfo : : ChunkState state ) const
2010-01-11 11:00:42 -05:00
{
uint32_t chunk_number_start = offset / ( uint64_t ) _chunk_size ;
uint32_t chunk_number_end = ( offset + ( uint64_t ) chunk_size ) / ( uint64_t ) _chunk_size ;
2010-10-15 18:12:29 -04:00
if ( ( offset + ( uint64_t ) chunk_size ) % ( uint64_t ) _chunk_size ! = 0 )
+ + chunk_number_end ;
2010-01-11 11:00:42 -05:00
// It's possible that chunk_number_start==chunk_number_end+1, but for this we need to have
// chunk_size=0, and offset%_chunk_size=0, so the response "true" is still valid.
//
2010-10-15 18:12:29 -04:00
for ( uint32_t i = chunk_number_start ; i < chunk_number_end ; + + i )
2015-03-08 09:46:07 -04:00
if ( _map [ i ] ! = state )
2010-10-15 18:12:29 -04:00
{
# ifdef DEBUG_FTCHUNK
2015-03-08 09:46:07 -04:00
std : : cerr < < " ChunkMap::hasChunkState(): ( " < < offset < < " , " < < chunk_size < < " ) has different state " < < std : : endl ;
2010-10-15 18:12:29 -04:00
# endif
2010-01-11 11:00:42 -05:00
return false ;
2010-10-15 18:12:29 -04:00
}
2010-01-11 11:00:42 -05:00
2010-10-15 18:12:29 -04:00
# ifdef DEBUG_FTCHUNK
2015-03-08 09:46:07 -04:00
std : : cerr < < " ChunkMap::hasChunkState(): ( " < < offset < < " , " < < chunk_size < < " ) check returns true " < < std : : endl ;
2010-10-15 18:12:29 -04:00
# endif
2010-01-11 11:00:42 -05:00
return true ;
}
2014-03-17 16:56:06 -04:00
void ChunkMap : : setPeerAvailabilityMap ( const RsPeerId & peer_id , const CompressedChunkMap & cmap )
2009-11-19 03:35:11 -05:00
{
2009-12-08 17:29:52 -05:00
# ifdef DEBUG_FTCHUNK
2009-12-28 16:11:00 -05:00
std : : cout < < " ChunkMap::Receiving new availability map for peer " < < peer_id < < std : : endl ;
2009-12-08 17:29:52 -05:00
# endif
2009-11-19 03:35:11 -05:00
2010-01-11 11:00:42 -05:00
if ( cmap . _map . size ( ) ! = _map . size ( ) / 32 + ( _map . size ( ) % 32 ! = 0 ) )
2009-12-28 16:11:00 -05:00
{
2010-01-11 11:00:42 -05:00
std : : cerr < < " ChunkMap::setPeerAvailabilityMap: chunk size / number of chunks is not correct. Dropping the info. cmap.size()= " < < cmap . _map . size ( ) < < " , _map/32+0/1 = " < < _map . size ( ) / 32 + ( _map . size ( ) % 32 ! = 0 ) < < std : : endl ;
2009-12-28 16:11:00 -05:00
return ;
}
// sets the map.
//
2010-01-11 11:00:42 -05:00
SourceChunksInfo & mi ( _peers_chunks_availability [ peer_id ] ) ;
mi . cmap = cmap ;
mi . TS = time ( NULL ) ;
mi . is_full = true ;
// Checks wether the map is full of not.
//
2010-01-11 15:10:04 -05:00
for ( uint32_t i = 0 ; i < _map . size ( ) ; + + i )
2010-01-11 11:00:42 -05:00
if ( ! cmap [ i ] )
{
mi . is_full = false ;
break ;
}
2009-12-28 16:11:00 -05:00
# ifdef DEBUG_FTCHUNK
std : : cerr < < " ChunkMap::setPeerAvailabilityMap: Setting chunk availability info for peer " < < peer_id < < std : : endl ;
2009-12-08 17:29:52 -05:00
# endif
2009-12-28 16:11:00 -05:00
}
2009-12-08 17:29:52 -05:00
uint32_t ChunkMap : : sizeOfChunk ( uint32_t cid ) const
{
if ( cid = = _map . size ( ) - 1 )
2011-11-04 17:29:51 -04:00
return _file_size - cid * _chunk_size ;
2009-12-08 17:29:52 -05:00
else
return _chunk_size ;
}
2014-03-17 16:56:06 -04:00
SourceChunksInfo * ChunkMap : : getSourceChunksInfo ( const RsPeerId & peer_id )
2009-12-08 17:29:52 -05:00
{
2014-03-17 16:56:06 -04:00
std : : map < RsPeerId , SourceChunksInfo > : : iterator it ( _peers_chunks_availability . find ( peer_id ) ) ;
2009-12-08 17:29:52 -05:00
2010-01-26 15:40:21 -05:00
// Do we have a chunk map for this file source ?
// - if yes, we use it
// - if no,
// - if availability is assumed, let's build a plain chunkmap for it
// - otherwise, refuse the transfer, but still ask for the chunkmap
//
// We first test whether the source has a record of not. If not, we fill a new record.
// For availability sources we fill it plain, otherwise, we fill it blank.
2009-12-08 17:29:52 -05:00
//
2010-01-26 15:40:21 -05:00
if ( it = = _peers_chunks_availability . end ( ) )
2009-12-08 17:29:52 -05:00
{
2010-01-26 15:40:21 -05:00
SourceChunksInfo & pchunks ( _peers_chunks_availability [ peer_id ] ) ;
2010-01-11 17:38:18 -05:00
2010-01-26 15:40:21 -05:00
// Ok, we don't have the info, so two cases:
// - peer_id is a not a turtle peer, so he is considered having the full file source, so we init with a plain chunkmap
// - otherwise, a source map needs to be obtained, so we init with a blank chunkmap
2010-01-11 17:38:18 -05:00
//
2010-11-22 15:57:53 -05:00
if ( _assume_availability )
2010-01-26 15:40:21 -05:00
{
pchunks . cmap . _map . resize ( CompressedChunkMap : : getCompressedSize ( _map . size ( ) ) , ~ ( uint32_t ) 0 ) ;
pchunks . TS = 0 ;
pchunks . is_full = true ;
}
else
{
pchunks . cmap . _map . resize ( CompressedChunkMap : : getCompressedSize ( _map . size ( ) ) , 0 ) ;
pchunks . TS = 0 ;
pchunks . is_full = false ;
}
it = _peers_chunks_availability . find ( peer_id ) ;
}
2011-10-23 14:43:58 -04:00
return & ( it - > second ) ;
}
2014-03-17 16:56:06 -04:00
void ChunkMap : : getSourcesList ( uint32_t chunk_number , std : : vector < RsPeerId > & sources )
2012-07-19 16:52:04 -04:00
{
sources . clear ( ) ;
2014-03-17 16:56:06 -04:00
for ( std : : map < RsPeerId , SourceChunksInfo > : : const_iterator it ( _peers_chunks_availability . begin ( ) ) ; it ! = _peers_chunks_availability . end ( ) ; + + it )
2012-07-19 16:52:04 -04:00
if ( it - > second . cmap [ chunk_number ] )
sources . push_back ( it - > first ) ;
}
2014-03-17 16:56:06 -04:00
uint32_t ChunkMap : : getAvailableChunk ( const RsPeerId & peer_id , bool & map_is_too_old )
2011-10-23 14:43:58 -04:00
{
// Quite simple strategy: Check for 1st availabe chunk for this peer starting from the given start location.
//
SourceChunksInfo * peer_chunks = getSourceChunksInfo ( peer_id ) ;
2010-01-26 15:40:21 -05:00
// If the info is too old, we ask for a new one. When the map is full, we ask 10 times less, as it's probably not
// useful to get a new map that will also be full, but because we need to be careful not to mislead information,
// we still keep asking.
//
time_t now = time ( NULL ) ;
2010-01-11 11:00:42 -05:00
2010-01-26 15:40:21 -05:00
if ( ( ! peer_chunks - > is_full ) & & ( ( int ) now - ( int ) peer_chunks - > TS > ( int ) SOURCE_CHUNK_MAP_UPDATE_PERIOD ) )
{
map_is_too_old = true ; // We will re-ask but not before some seconds.
2010-01-11 17:38:18 -05:00
peer_chunks - > TS = now ;
}
2010-01-26 15:40:21 -05:00
else
map_is_too_old = false ; // the map is not too old
2009-12-08 17:29:52 -05:00
2010-11-10 17:05:34 -05:00
uint32_t available_chunks = 0 ;
2013-02-28 15:42:01 -05:00
uint32_t available_chunks_before_max_dist = 0 ;
2010-11-08 17:09:43 -05:00
2009-12-09 07:42:31 -05:00
for ( unsigned int i = 0 ; i < _map . size ( ) ; + + i )
2013-02-28 15:42:01 -05:00
if ( _map [ i ] = = FileChunksInfo : : CHUNK_OUTSTANDING )
2009-12-08 17:29:52 -05:00
{
2013-02-28 15:42:01 -05:00
if ( peer_chunks - > is_full | | peer_chunks - > cmap [ i ] )
2010-11-10 17:05:34 -05:00
+ + available_chunks ;
}
2013-02-28 15:42:01 -05:00
else
available_chunks_before_max_dist = available_chunks ;
2010-11-08 17:09:43 -05:00
2010-11-10 17:05:34 -05:00
if ( available_chunks > 0 )
{
2013-02-28 15:42:01 -05:00
uint32_t chosen_chunk_number ;
switch ( _strategy )
{
2013-03-01 18:41:42 -05:00
case FileChunksInfo : : CHUNK_STRATEGY_STREAMING : chosen_chunk_number = 0 ;
2013-02-28 15:42:01 -05:00
break ;
2013-03-01 18:41:42 -05:00
case FileChunksInfo : : CHUNK_STRATEGY_RANDOM : chosen_chunk_number = rand ( ) % available_chunks ;
2013-02-28 15:42:01 -05:00
break ;
2013-03-01 18:41:42 -05:00
case FileChunksInfo : : CHUNK_STRATEGY_PROGRESSIVE : chosen_chunk_number = rand ( ) % std : : min ( available_chunks , available_chunks_before_max_dist + FT_CHUNKMAP_MAX_CHUNK_JUMP ) ;
2013-02-28 15:42:01 -05:00
break ;
default :
chosen_chunk_number = 0 ;
}
2010-11-10 17:05:34 -05:00
uint32_t j = 0 ;
2010-11-08 17:09:43 -05:00
2013-03-01 18:41:42 -05:00
for ( uint32_t i = 0 ; i < _map . size ( ) ; + + i )
2010-11-10 17:05:34 -05:00
if ( _map [ i ] = = FileChunksInfo : : CHUNK_OUTSTANDING & & ( peer_chunks - > is_full | | peer_chunks - > cmap [ i ] ) )
{
if ( j = = chosen_chunk_number )
{
2009-12-08 17:29:52 -05:00
# ifdef DEBUG_FTCHUNK
2010-11-10 17:05:34 -05:00
std : : cerr < < " ChunkMap::getAvailableChunk: returning chunk " < < i < < " for peer " < < peer_id < < std : : endl ;
2009-12-08 17:29:52 -05:00
# endif
2010-11-10 17:05:34 -05:00
return i ;
}
else
+ + j ;
2010-11-08 17:09:43 -05:00
}
2009-12-08 17:29:52 -05:00
}
# ifdef DEBUG_FTCHUNK
std : : cout < < " !!! ChunkMap::getAvailableChunk: No available chunk from peer " < < peer_id < < " : returning false " < < std : : endl ;
# endif
return _map . size ( ) ;
}
void ChunkMap : : getChunksInfo ( FileChunksInfo & info ) const
{
info . file_size = _file_size ;
info . chunk_size = _chunk_size ;
info . chunks = _map ;
2010-01-22 18:23:37 -05:00
info . strategy = _strategy ;
2009-12-28 16:11:00 -05:00
info . active_chunks . clear ( ) ;
for ( std : : map < ChunkNumber , ChunkDownloadInfo > : : const_iterator it ( _slices_to_download . begin ( ) ) ; it ! = _slices_to_download . end ( ) ; + + it )
info . active_chunks . push_back ( std : : pair < uint32_t , uint32_t > ( it - > first , it - > second . _remains ) ) ;
info . compressed_peer_availability_maps . clear ( ) ;
2014-03-17 16:56:06 -04:00
for ( std : : map < RsPeerId , SourceChunksInfo > : : const_iterator it ( _peers_chunks_availability . begin ( ) ) ; it ! = _peers_chunks_availability . end ( ) ; + + it )
2010-01-11 11:00:42 -05:00
info . compressed_peer_availability_maps [ it - > first ] = it - > second . cmap ;
2009-12-08 17:29:52 -05:00
}
2014-03-17 16:56:06 -04:00
void ChunkMap : : removeFileSource ( const RsPeerId & peer_id )
2010-03-07 09:42:13 -05:00
{
2014-03-17 16:56:06 -04:00
std : : map < RsPeerId , SourceChunksInfo > : : iterator it ( _peers_chunks_availability . find ( peer_id ) ) ;
2010-03-07 09:42:13 -05:00
if ( it = = _peers_chunks_availability . end ( ) )
return ;
_peers_chunks_availability . erase ( it ) ;
}
2010-01-11 11:00:42 -05:00
void ChunkMap : : getAvailabilityMap ( CompressedChunkMap & compressed_map ) const
2009-12-10 17:55:27 -05:00
{
2010-01-11 11:00:42 -05:00
compressed_map = CompressedChunkMap ( _map ) ;
2009-12-10 17:55:27 -05:00
# ifdef DEBUG_FTCHUNK
2010-01-11 11:00:42 -05:00
std : : cerr < < " ChunkMap:: retrieved availability map of size " < < _map . size ( ) < < " , chunk_size= " < < _chunk_size < < std : : endl ;
2009-12-10 17:55:27 -05:00
# endif
}
2012-03-15 15:55:43 -04:00
void ChunkMap : : forceCheck ( )
{
for ( uint32_t i = 0 ; i < _map . size ( ) ; + + i )
{
_map [ i ] = FileChunksInfo : : CHUNK_CHECKING ;
_chunks_checking_queue . push_back ( i ) ;
}
updateTotalDownloaded ( ) ;
}
2010-07-21 19:14:10 -04:00
uint32_t ChunkMap : : getNumberOfChunks ( uint64_t size )
2010-01-11 11:00:42 -05:00
{
2010-07-21 19:14:10 -04:00
uint64_t n = size / ( uint64_t ) CHUNKMAP_FIXED_CHUNK_SIZE ;
2010-01-11 11:00:42 -05:00
2010-07-21 19:14:10 -04:00
if ( size % ( uint64_t ) CHUNKMAP_FIXED_CHUNK_SIZE ! = 0 )
2010-01-11 11:00:42 -05:00
+ + n ;
2010-07-21 19:14:10 -04:00
uint32_t value = n & 0xffffffffull ;
if ( ( uint64_t ) value ! = n )
{
std : : cerr < < " ERROR: number of chunks is a totally absurd value. File size is " < < size < < " , chunks are " < < n < < " !! " < < std : : endl ;
return 0 ;
}
return value ;
2010-01-11 11:00:42 -05:00
}
2010-07-21 19:14:10 -04:00
void ChunkMap : : buildPlainMap ( uint64_t size , CompressedChunkMap & map )
{
uint32_t n = getNumberOfChunks ( size ) ;
2010-01-11 11:00:42 -05:00
2010-07-21 19:14:10 -04:00
map = CompressedChunkMap ( n , ~ uint32_t ( 0 ) ) ;
}
2009-12-08 17:29:52 -05:00