From cfaaec31c706ab7d45ce2befeb9e676b4dc99c17 Mon Sep 17 00:00:00 2001 From: csoler Date: Mon, 11 Jan 2010 16:00:42 +0000 Subject: [PATCH] - Implemented chunk-based file transfer from partial sources. This in particular means: - exchange of chunk availability maps from different peers - correct handling of what is available to which source before asking the data - correct display of chunks in the progress bars - generalised the use of compressed chunk maps - removed the size parameters from the hash search functions - In addition: - suppressed a number of per-value transfers of std::string - improved the FileTransferInfo Widget, to show some additional info Still to be done: - chunk map exchange for non anonymous traffic (easy) - improve accuracy of completion for uploads (for now it's a integer number of chunks) - check compilation on windows git-svn-id: http://svn.code.sf.net/p/retroshare/code/trunk@1993 b45a01b8-16f6-495d-af2f-9b41ad6348cc --- libretroshare/src/ft/ftchunkmap.cc | 206 ++++++---- libretroshare/src/ft/ftchunkmap.h | 98 +++-- libretroshare/src/ft/ftcontroller.cc | 53 ++- libretroshare/src/ft/ftcontroller.h | 2 +- libretroshare/src/ft/ftdata.h | 35 +- libretroshare/src/ft/ftdatamultiplex.cc | 357 ++++++++++++------ libretroshare/src/ft/ftdatamultiplex.h | 32 +- libretroshare/src/ft/ftdbase.cc | 16 +- libretroshare/src/ft/ftdbase.h | 6 +- libretroshare/src/ft/ftextralist.cc | 4 +- libretroshare/src/ft/ftextralist.h | 2 +- libretroshare/src/ft/ftfilecreator.cc | 84 ++--- libretroshare/src/ft/ftfilecreator.h | 14 +- libretroshare/src/ft/ftfileprovider.cc | 43 +++ libretroshare/src/ft/ftfileprovider.h | 74 ++-- libretroshare/src/ft/ftfilesearch.cc | 12 +- libretroshare/src/ft/ftfilesearch.h | 2 +- libretroshare/src/ft/ftsearch.h | 4 +- libretroshare/src/ft/ftserver.cc | 49 ++- libretroshare/src/ft/ftserver.h | 14 +- libretroshare/src/ft/fttransfermodule.cc | 8 +- libretroshare/src/rsiface/rsfiles.h | 7 +- libretroshare/src/rsiface/rstypes.h | 44 ++- libretroshare/src/serialiser/rsconfigitems.cc | 18 +- libretroshare/src/serialiser/rsconfigitems.h | 5 +- libretroshare/src/turtle/p3turtle.cc | 183 +++++++-- libretroshare/src/turtle/p3turtle.h | 8 + libretroshare/src/turtle/rsturtleitem.cc | 120 +++++- libretroshare/src/turtle/rsturtleitem.h | 30 +- retroshare-gui/src/gui/DLListDelegate.cpp | 19 +- .../src/gui/FileTransferInfoWidget.cpp | 30 +- retroshare-gui/src/gui/TransfersDialog.cpp | 135 +++++-- retroshare-gui/src/gui/TransfersDialog.h | 7 +- retroshare-gui/src/gui/ULListDelegate.cpp | 38 +- retroshare-gui/src/gui/xprogressbar.cpp | 46 ++- retroshare-gui/src/gui/xprogressbar.h | 15 +- 36 files changed, 1247 insertions(+), 573 deletions(-) diff --git a/libretroshare/src/ft/ftchunkmap.cc b/libretroshare/src/ft/ftchunkmap.cc index e6cb6cf4a..6ac54336c 100644 --- a/libretroshare/src/ft/ftchunkmap.cc +++ b/libretroshare/src/ft/ftchunkmap.cc @@ -1,8 +1,11 @@ #include #include #include +#include #include "ftchunkmap.h" +static const uint32_t SOURCE_CHUNK_MAP_UPDATE_PERIOD = 60 ; //! TTL for chunkmap info + std::ostream& operator<<(std::ostream& o,const ftChunk& c) { return o << "\tChunk [" << c.offset << "] size: " << c.size << " ChunkId: " << c.id << " Age: " << time(NULL) - c.ts ; @@ -28,26 +31,6 @@ void Chunk::getSlice(uint32_t size_hint,ftChunk& chunk) _offset += chunk.size ; } -//uint32_t Chunk::dataReceived(const ftChunk::ChunkId cid) -//{ -//#ifdef DEBUG_FTCHUNK -// std::cerr << "*** Chunk::dataReceived: slice " << cid << " finished" << std::endl ; -//#endif -// std::map::iterator it( _slices_to_download.find(cid) ) ; -// -// if(it == _slices_to_download.end()) -// { -// std::cerr << "!!! Chunk::dataReceived: could not find chunk " << cid << ": probably a fatal error" << std::endl ; -// return 0 ; -// } -// else -// { -// uint32_t n = it->second ; -// _slices_to_download.erase(it) ; -// return n ; -// } -//} - ChunkMap::ChunkMap(uint64_t s) :_file_size(s),_chunk_size(1024*1024) // 1MB chunks { @@ -67,34 +50,46 @@ ChunkMap::ChunkMap(uint64_t s) #endif } -ChunkMap::ChunkMap(uint64_t file_size, - const std::vector& map, - uint32_t chunk_size, - uint32_t chunk_number, - FileChunksInfo::ChunkStrategy strategy) - - :_file_size(file_size),_chunk_size(chunk_size),_strategy(strategy) +void ChunkMap::setAvailabilityMap(const CompressedChunkMap& map) { -#ifdef DEBUG_FTCHUNK - std::cerr << "ChunkMap:: loading availability map of size " << map.size() << ", chunk_size=" << chunk_size << ", chunknumber = " << chunk_number << std::endl ; -#endif - - _map.clear() ; - _map.resize(chunk_number) ; - _total_downloaded = 0 ; - for(uint32_t i=0;i<_map.size();++i) - { - uint32_t j = i & 31 ; // i%32 - uint32_t k = i >> 5 ; // i/32 - - _map[i] = ( (map[k] & (1< 0)?(FileChunksInfo::CHUNK_DONE) : (FileChunksInfo::CHUNK_OUTSTANDING) ; - - if(_map[i] == FileChunksInfo::CHUNK_DONE) + if(map[i] > 0) + { + _map[i] = FileChunksInfo::CHUNK_DONE ; _total_downloaded += _chunk_size ; - } + } + else + _map[i] = FileChunksInfo::CHUNK_OUTSTANDING ; } +//ChunkMap::ChunkMap(uint64_t file_size, +// const std::vector& map, +// uint32_t chunk_size, +// uint32_t chunk_number, +// FileChunksInfo::ChunkStrategy strategy) +// +// :_file_size(file_size),_chunk_size(chunk_size),_strategy(strategy) +//{ +//#ifdef DEBUG_FTCHUNK +// std::cerr << "ChunkMap:: loading availability map of size " << map.size() << ", chunk_size=" << chunk_size << ", chunknumber = " << chunk_number << std::endl ; +//#endif +// +// _map.clear() ; +// _map.resize(chunk_number) ; +// _total_downloaded = 0 ; +// +// for(uint32_t i=0;i<_map.size();++i) +// { +// uint32_t j = i & 31 ; // i%32 +// uint32_t k = i >> 5 ; // i/32 +// +// _map[i] = ( (map[k] & (1< 0)?(FileChunksInfo::CHUNK_DONE) : (FileChunksInfo::CHUNK_OUTSTANDING) ; +// +// if(_map[i] == FileChunksInfo::CHUNK_DONE) +// _total_downloaded += _chunk_size ; +// } +//} + void ChunkMap::dataReceived(const ftChunk::ChunkId& cid) { @@ -151,7 +146,7 @@ void ChunkMap::dataReceived(const ftChunk::ChunkId& cid) // - chunks pushed when new chunks are needed // - chunks removed when completely downloaded // -bool ChunkMap::getDataChunk(const std::string& peer_id,uint32_t size_hint,ftChunk& chunk) +bool ChunkMap::getDataChunk(const std::string& peer_id,uint32_t size_hint,ftChunk& chunk,bool& source_chunk_map_needed) { #ifdef DEBUG_FTCHUNK std::cerr << "*** ChunkMap::getDataChunk: size_hint = " << size_hint << std::endl ; @@ -168,10 +163,10 @@ bool ChunkMap::getDataChunk(const std::string& peer_id,uint32_t size_hint,ftChun switch(_strategy) { - case FileChunksInfo::CHUNK_STRATEGY_STREAMING: c = getAvailableChunk(0,peer_id) ; // very bold!! + case FileChunksInfo::CHUNK_STRATEGY_STREAMING: c = getAvailableChunk(0,peer_id,source_chunk_map_needed) ; // very bold!! break ; - case FileChunksInfo::CHUNK_STRATEGY_RANDOM: c = getAvailableChunk(rand()%_map.size(),peer_id) ; + case FileChunksInfo::CHUNK_STRATEGY_RANDOM: c = getAvailableChunk(rand()%_map.size(),peer_id,source_chunk_map_needed) ; break ; default: #ifdef DEBUG_FTCHUNK @@ -212,23 +207,51 @@ bool ChunkMap::getDataChunk(const std::string& peer_id,uint32_t size_hint,ftChun return true ; } -void ChunkMap::setPeerAvailabilityMap(const std::string& peer_id,uint32_t chunk_size,uint32_t nb_chunks,const std::vector& compressed_peer_map) +bool ChunkMap::isChunkAvailable(uint64_t offset, uint32_t chunk_size) const +{ + uint32_t chunk_number_start = offset/(uint64_t)_chunk_size ; + uint32_t chunk_number_end = (offset+(uint64_t)chunk_size)/(uint64_t)_chunk_size ; + + if((offset+(uint64_t)chunk_size) % (uint64_t)_chunk_size == 0) + --chunk_number_end ; + + // 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. + // + for(uint32_t i=chunk_number_start;i!=chunk_number_end;++i) + if(_map[i] != FileChunksInfo::CHUNK_DONE) + return false ; + + return true ; +} + +void ChunkMap::setPeerAvailabilityMap(const std::string& peer_id,const CompressedChunkMap& cmap) { #ifdef DEBUG_FTCHUNK std::cout << "ChunkMap::Receiving new availability map for peer " << peer_id << std::endl ; #endif - // Check that the parameters are the same. Otherwise we should convert the info into the local format. - // If all RS instances have the same policy for deciding the sizes of chunks, this should not happen. - if(chunk_size != _chunk_size || nb_chunks != _map.size()) + if(cmap._map.size() != _map.size()/32+(_map.size()%32 != 0)) { - std::cerr << "ChunkMap::setPeerAvailabilityMap: chunk size / number of chunks is not correct. Dropping the info." << std::endl ; + 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 ; return ; } // sets the map. // - _peers_chunks_availability[peer_id] = compressed_peer_map ; + 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. + // + for(uint i=0;i<_map.size();++i) + if(!cmap[i]) + { + mi.is_full = false ; + break ; + } #ifdef DEBUG_FTCHUNK std::cerr << "ChunkMap::setPeerAvailabilityMap: Setting chunk availability info for peer " << peer_id << std::endl ; @@ -243,32 +266,54 @@ uint32_t ChunkMap::sizeOfChunk(uint32_t cid) const return _chunk_size ; } -uint32_t ChunkMap::getAvailableChunk(uint32_t start_location,const std::string& peer_id) +uint32_t ChunkMap::getAvailableChunk(uint32_t start_location,const std::string& peer_id,bool& map_is_too_old) { // Very bold algorithm: checks for 1st availabe chunk for this peer starting // from the given start location. - std::map >::const_iterator it(_peers_chunks_availability.find(peer_id)) ; + std::map::iterator it(_peers_chunks_availability.find(peer_id)) ; // Do we have records for this file source ? // if(it == _peers_chunks_availability.end()) { -#ifdef DEBUG_FTCHUNK - std::cout << "No chunk map for peer " << peer_id << ": supposing full data." << std::endl ; -#endif - std::vector& pchunks(_peers_chunks_availability[peer_id]) ; + SourceChunksInfo& pchunks(_peers_chunks_availability[peer_id]) ; - pchunks.resize( (_map.size() >> 5)+!!(_map.size() & 0x11111),~(uint32_t)0 ) ; + // Ok, we don't have the info, so two cases: + // - we are the actual source, so we can safely init the map to a full map + // - we are not the source, so we init it with an empty map, and set the time stamp to 0. + // + if(peer_id == rsPeers->getOwnId()) + { + 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) ; } - const std::vector& peer_chunks(it->second) ; + SourceChunksInfo& peer_chunks(it->second) ; + + // 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) ; + map_is_too_old = (int)now - (int)peer_chunks.TS > (int)SOURCE_CHUNK_MAP_UPDATE_PERIOD*(1+9*peer_chunks.is_full) ; + + // We will re-ask but not now seconds. + peer_chunks.TS = now ; for(unsigned int i=0;i<_map.size();++i) { uint32_t j = (start_location+i)%(int)_map.size() ; // index of the chunk - if(_map[j] == FileChunksInfo::CHUNK_OUTSTANDING && COMPRESSED_MAP_READ(peer_chunks,j)) + if(_map[j] == FileChunksInfo::CHUNK_OUTSTANDING && peer_chunks.cmap[j]) { #ifdef DEBUG_FTCHUNK std::cerr << "ChunkMap::getAvailableChunk: returning chunk " << j << " for peer " << peer_id << std::endl; @@ -296,35 +341,30 @@ void ChunkMap::getChunksInfo(FileChunksInfo& info) const info.compressed_peer_availability_maps.clear() ; - for(std::map >::const_iterator it(_peers_chunks_availability.begin());it!= _peers_chunks_availability.end();++it) - info.compressed_peer_availability_maps.push_back(std::pair >(it->first,it->second)) ; + for(std::map::const_iterator it(_peers_chunks_availability.begin());it!=_peers_chunks_availability.end();++it) + info.compressed_peer_availability_maps[it->first] = it->second.cmap ; } -void ChunkMap::buildAvailabilityMap(std::vector& map,uint32_t& chunk_size,uint32_t& chunk_number,FileChunksInfo::ChunkStrategy& strategy) const +void ChunkMap::getAvailabilityMap(CompressedChunkMap& compressed_map) const { - chunk_size = _chunk_size ; - chunk_number = _map.size() ; - strategy = _strategy ; + compressed_map = CompressedChunkMap(_map) ; - map.clear() ; - map.reserve((chunk_number >> 5)+1) ; - - uint32_t r=0 ; - for(uint32_t i=0;i<_map.size();++i) - { - uint32_t j = i & 31 ; - r |= (_map[i]==FileChunksInfo::CHUNK_DONE)?(1<file.hash << " in mDownloads list !" << std::endl ; } else - (fit->second).mCreator->loadAvailabilityMap(rsft->chunk_map,rsft->chunk_size,rsft->chunk_number,rsft->chunk_strategy) ; + { + (fit->second).mCreator->setAvailabilityMap(rsft->compressed_chunk_map) ; + (fit->second).mCreator->setChunkStrategy((FileChunksInfo::ChunkStrategy)(rsft->chunk_strategy)) ; + } delete rsft ; mPendingChunkMaps.erase(it) ; @@ -733,10 +740,7 @@ bool ftController::FileRequest(std::string fname, std::string hash, } else { - if (mSearch->search(hash, size, - RS_FILE_HINTS_LOCAL | - RS_FILE_HINTS_EXTRA | - RS_FILE_HINTS_SPEC_ONLY, info)) + if (mSearch->search(hash, RS_FILE_HINTS_LOCAL | RS_FILE_HINTS_EXTRA | RS_FILE_HINTS_SPEC_ONLY, info)) { /* have it already */ /* add in as completed transfer */ @@ -750,10 +754,7 @@ bool ftController::FileRequest(std::string fname, std::string hash, } /* do a source search - for any extra sources */ - if (mSearch->search(hash, size, - RS_FILE_HINTS_REMOTE | -// RS_FILE_HINTS_TURTLE | - RS_FILE_HINTS_SPEC_ONLY, info)) + if (mSearch->search(hash, RS_FILE_HINTS_REMOTE | RS_FILE_HINTS_SPEC_ONLY, info)) { /* do something with results */ #ifdef CONTROL_DEBUG @@ -817,8 +818,7 @@ bool ftController::FileRequest(std::string fname, std::string hash, ftTransferModule *tm = new ftTransferModule(fc, mDataplex,this); /* add into maps */ - ftFileControl ftfc(fname, savepath, destination, - size, hash, flags, fc, tm, callbackCode); + ftFileControl ftfc(fname, savepath, destination, size, hash, flags, fc, tm, callbackCode); ftfc.mCreateTime = time(NULL); #ifdef CONTROL_DEBUG @@ -1469,7 +1469,23 @@ std::list ftController::saveList(bool &cleanup) //rft->flags = fit->second.mFlags; fit->second.mTransfer->getFileSources(rft->allPeerIds.ids); - fit->second.mCreator->storeAvailabilityMap(rft->chunk_map,rft->chunk_size,rft->chunk_number,rft->chunk_strategy) ; + + // Remove turtle peers from sources, as they are not supposed to survive a reboot of RS, since they are dynamic sources. + // Otherwize, such sources are unknown from the turtle router, at restart, and never get removed. + // + for(std::list::iterator sit(rft->allPeerIds.ids.begin());sit!=rft->allPeerIds.ids.end();) + if(mTurtle->isTurtlePeer(*sit)) + { + std::list::iterator sittmp(sit) ; + ++sittmp ; + rft->allPeerIds.ids.erase(sit) ; + sit = sittmp ; + } + else + ++sit ; + + fit->second.mCreator->getAvailabilityMap(rft->compressed_chunk_map) ; + rft->chunk_strategy = fit->second.mCreator->getChunkStrategy() ; saveData.push_back(rft); } @@ -1528,7 +1544,10 @@ bool ftController::loadList(std::list load) continue ; // i.e. don't delete the item! } else - (fit->second).mCreator->loadAvailabilityMap(rsft->chunk_map,rsft->chunk_size,rsft->chunk_number,rsft->chunk_strategy) ; + { + (fit->second).mCreator->setAvailabilityMap(rsft->compressed_chunk_map) ; + (fit->second).mCreator->setChunkStrategy((FileChunksInfo::ChunkStrategy)(rsft->chunk_strategy)) ; + } } } diff --git a/libretroshare/src/ft/ftcontroller.h b/libretroshare/src/ft/ftcontroller.h index 92da29bad..dbe6b37ad 100644 --- a/libretroshare/src/ft/ftcontroller.h +++ b/libretroshare/src/ft/ftcontroller.h @@ -138,7 +138,7 @@ bool FileCancel(std::string hash); bool FileControl(std::string hash, uint32_t flags); bool FileClearCompleted(); bool FlagFileComplete(std::string hash); -bool getFileChunksDetails(const std::string& hash,FileChunksInfo& info); +bool getFileDownloadChunksDetails(const std::string& hash,FileChunksInfo& info); /* get Details of File Transfers */ bool FileDownloads(std::list &hashs); diff --git a/libretroshare/src/ft/ftdata.h b/libretroshare/src/ft/ftdata.h index 52c4581a2..0dd2f5896 100644 --- a/libretroshare/src/ft/ftdata.h +++ b/libretroshare/src/ft/ftdata.h @@ -40,19 +40,23 @@ /*************** SEND INTERFACE *******************/ +class CompressedChunkMap ; + class ftDataSend { public: -virtual ~ftDataSend() { return; } + virtual ~ftDataSend() { return; } - /* Client Send */ -virtual bool sendDataRequest(std::string peerId, std::string hash, - uint64_t size, uint64_t offset, uint32_t chunksize) = 0; + /* Client Send */ + virtual bool sendDataRequest(const std::string& peerId, const std::string& hash, uint64_t size, uint64_t offset, uint32_t chunksize) = 0; - /* Server Send */ -virtual bool sendData(std::string peerId, std::string hash, uint64_t size, - uint64_t offset, uint32_t chunksize, void *data) = 0; + /* Server Send */ + virtual bool sendData(const std::string& peerId, const std::string& hash, uint64_t size, uint64_t offset, uint32_t chunksize, void *data) = 0; + /// Send a request for a chunk map + virtual bool sendChunkMapRequest(const std::string& peer_id,const std::string& hash) = 0; + /// Send a chunk map + virtual bool sendChunkMap(const std::string& peer_id,const std::string& hash,const CompressedChunkMap& cmap) = 0; }; @@ -62,18 +66,19 @@ virtual bool sendData(std::string peerId, std::string hash, uint64_t size, class ftDataRecv { public: + virtual ~ftDataRecv() { return; } -virtual ~ftDataRecv() { return; } + /* Client Recv */ + virtual bool recvData(const std::string& peerId, const std::string& hash, uint64_t size, uint64_t offset, uint32_t chunksize, void *data) = 0; - /* Client Recv */ -virtual bool recvData(std::string peerId, std::string hash, uint64_t size, - uint64_t offset, uint32_t chunksize, void *data) = 0; - - /* Server Recv */ -virtual bool recvDataRequest(std::string peerId, std::string hash, - uint64_t size, uint64_t offset, uint32_t chunksize) = 0; + /* Server Recv */ + virtual bool recvDataRequest(const std::string& peerId, const std::string& hash, uint64_t size, uint64_t offset, uint32_t chunksize) = 0; + /// Send a request for a chunk map + virtual bool recvChunkMapRequest(const std::string& peer_id,const std::string& hash,bool is_client) = 0; + /// Send a chunk map + virtual bool recvChunkMap(const std::string& peer_id,const std::string& hash,const CompressedChunkMap& cmap,bool is_client) = 0; }; /******* Pair of Send/Recv (Only need to handle Send side) ******/ diff --git a/libretroshare/src/ft/ftdatamultiplex.cc b/libretroshare/src/ft/ftdatamultiplex.cc index 4c0a59db0..2146a338b 100644 --- a/libretroshare/src/ft/ftdatamultiplex.cc +++ b/libretroshare/src/ft/ftdatamultiplex.cc @@ -50,8 +50,10 @@ ftClient::ftClient(ftTransferModule *module, ftFileCreator *creator) return; } -const uint32_t FT_DATA = 0x0001; -const uint32_t FT_DATA_REQ = 0x0002; +const uint32_t FT_DATA = 0x0001; // data cuhnk to be stored +const uint32_t FT_DATA_REQ = 0x0002; // data request to be treated +const uint32_t FT_CLIENT_CHUNK_MAP_REQ = 0x0003; // chunk map request to be treated by client +const uint32_t FT_SERVER_CHUNK_MAP_REQ = 0x0004; // chunk map reuqest to be treated by server ftRequest::ftRequest(uint32_t type, std::string peerId, std::string hash, uint64_t size, uint64_t offset, uint32_t chunk, void *data) :mType(type), mPeerId(peerId), mHash(hash), mSize(size), @@ -179,8 +181,7 @@ bool ftDataMultiplex::FileDetails(std::string hash, uint32_t hintsflag, FileI /*************** SEND INTERFACE (calls ftDataSend) *******************/ /* Client Send */ -bool ftDataMultiplex::sendDataRequest(std::string peerId, - std::string hash, uint64_t size, uint64_t offset, uint32_t chunksize) +bool ftDataMultiplex::sendDataRequest(const std::string& peerId, const std::string& hash, uint64_t size, uint64_t offset, uint32_t chunksize) { #ifdef MPLEX_DEBUG std::cerr << "ftDataMultiplex::sendDataRequest() Client Send"; @@ -190,9 +191,7 @@ bool ftDataMultiplex::sendDataRequest(std::string peerId, } /* Server Send */ -bool ftDataMultiplex::sendData(std::string peerId, - std::string hash, uint64_t size, - uint64_t offset, uint32_t chunksize, void *data) +bool ftDataMultiplex::sendData(const std::string& peerId, const std::string& hash, uint64_t size, uint64_t offset, uint32_t chunksize, void *data) { #ifdef MPLEX_DEBUG std::cerr << "ftDataMultiplex::sendData() Server Send"; @@ -205,9 +204,7 @@ bool ftDataMultiplex::sendData(std::string peerId, /*************** RECV INTERFACE (provides ftDataRecv) ****************/ /* Client Recv */ -bool ftDataMultiplex::recvData(std::string peerId, - std::string hash, uint64_t size, - uint64_t offset, uint32_t chunksize, void *data) +bool ftDataMultiplex::recvData(const std::string& peerId, const std::string& hash, uint64_t size, uint64_t offset, uint32_t chunksize, void *data) { #ifdef MPLEX_DEBUG std::cerr << "ftDataMultiplex::recvData() Client Recv"; @@ -215,17 +212,14 @@ bool ftDataMultiplex::recvData(std::string peerId, #endif /* Store in Queue */ RsStackMutex stack(dataMtx); /******* LOCK MUTEX ******/ - mRequestQueue.push_back( - ftRequest(FT_DATA,peerId,hash,size,offset,chunksize,data)); + mRequestQueue.push_back(ftRequest(FT_DATA,peerId,hash,size,offset,chunksize,data)); return true; } /* Server Recv */ -bool ftDataMultiplex::recvDataRequest(std::string peerId, - std::string hash, uint64_t size, - uint64_t offset, uint32_t chunksize) +bool ftDataMultiplex::recvDataRequest(const std::string& peerId, const std::string& hash, uint64_t size, uint64_t offset, uint32_t chunksize) { #ifdef MPLEX_DEBUG std::cerr << "ftDataMultiplex::recvDataRequest() Server Recv"; @@ -239,6 +233,24 @@ bool ftDataMultiplex::recvDataRequest(std::string peerId, return true; } +bool ftDataMultiplex::recvChunkMapRequest(const std::string& peerId, const std::string& hash,bool is_client) +{ +#ifdef MPLEX_DEBUG + std::cerr << "ftDataMultiplex::recvChunkMapRequest() Server Recv"; + std::cerr << std::endl; +#endif + /* Store in Queue */ + RsStackMutex stack(dataMtx); /******* LOCK MUTEX ******/ + + if(is_client) + mRequestQueue.push_back(ftRequest(FT_CLIENT_CHUNK_MAP_REQ,peerId,hash,0,0,0,NULL)); + else + mRequestQueue.push_back(ftRequest(FT_SERVER_CHUNK_MAP_REQ,peerId,hash,0,0,0,NULL)); + + return true; +} + + /*********** BACKGROUND THREAD OPERATIONS ***********/ bool ftDataMultiplex::workQueued() @@ -282,30 +294,44 @@ bool ftDataMultiplex::doWork() switch(req.mType) { - case FT_DATA: + case FT_DATA: #ifdef MPLEX_DEBUG - std::cerr << "ftDataMultiplex::doWork() Handling FT_DATA"; - std::cerr << std::endl; + std::cerr << "ftDataMultiplex::doWork() Handling FT_DATA"; + std::cerr << std::endl; #endif - handleRecvData(req.mPeerId, req.mHash, req.mSize, - req.mOffset, req.mChunk, req.mData); - break; + handleRecvData(req.mPeerId, req.mHash, req.mSize, req.mOffset, req.mChunk, req.mData); + break; - case FT_DATA_REQ: + case FT_DATA_REQ: #ifdef MPLEX_DEBUG - std::cerr << "ftDataMultiplex::doWork() Handling FT_DATA_REQ"; - std::cerr << std::endl; + std::cerr << "ftDataMultiplex::doWork() Handling FT_DATA_REQ"; + std::cerr << std::endl; #endif - handleRecvDataRequest(req.mPeerId, req.mHash, - req.mSize, req.mOffset, req.mChunk); - break; + handleRecvDataRequest(req.mPeerId, req.mHash, req.mSize, req.mOffset, req.mChunk); + break; - default: + case FT_CLIENT_CHUNK_MAP_REQ: #ifdef MPLEX_DEBUG - std::cerr << "ftDataMultiplex::doWork() Ignoring UNKNOWN"; - std::cerr << std::endl; + std::cerr << "ftDataMultiplex::doWork() Handling FT_CLIENT_CHUNK_MAP_REQ"; + std::cerr << std::endl; #endif - break; + handleRecvClientChunkMapRequest(req.mPeerId,req.mHash) ; + break ; + + case FT_SERVER_CHUNK_MAP_REQ: +#ifdef MPLEX_DEBUG + std::cerr << "ftDataMultiplex::doWork() Handling FT_CLIENT_CHUNK_MAP_REQ"; + std::cerr << std::endl; +#endif + handleRecvServerChunkMapRequest(req.mPeerId,req.mHash) ; + break ; + + default: +#ifdef MPLEX_DEBUG + std::cerr << "ftDataMultiplex::doWork() Ignoring UNKNOWN"; + std::cerr << std::endl; +#endif + break; } } @@ -330,40 +356,137 @@ bool ftDataMultiplex::doWork() std::cerr << "ftDataMultiplex::doWork() Handling Search Request"; std::cerr << std::endl; #endif - handleSearchRequest(req.mPeerId, req.mHash, req.mSize, - req.mOffset, req.mChunk); + if(handleSearchRequest(req.mPeerId, req.mHash)) + handleRecvDataRequest(req.mPeerId, req.mHash, req.mSize, req.mOffset, req.mChunk) ; return true; } -bool ftDataMultiplex::recvFileMap(const std::string& peerId, const std::string& hash, uint32_t chunk_size, uint32_t nb_chunks, const std::vector& compressed_map) +// A chunk map has arrived. It can be two different situations: +// - an uploader has sent his chunk map, so we need to store it in the corresponding ftFileProvider +// - a source for a download has sent his chunk map, so we need to send it to the corresponding ftFileCreator. +// +bool ftDataMultiplex::recvChunkMap(const std::string& peerId, const std::string& hash,const CompressedChunkMap& compressed_map,bool client) { RsStackMutex stack(dataMtx); /******* LOCK MUTEX ******/ - std::map::iterator it; - if (mClients.end() == (it = mClients.find(hash))) + if(client) // is the chunk map for a client, or for a server ? { + std::map::iterator it = mClients.find(hash); + + if(it == mClients.end()) + { #ifdef MPLEX_DEBUG - std::cerr << "ftDataMultiplex::handleRecvMap() ERROR: No matching Client!"; + std::cerr << "ftDataMultiplex::recvChunkMap() ERROR: No matching Client for hash " << hash << " !"; + std::cerr << std::endl; +#endif + /* error */ + return false; + } + +#ifdef MPLEX_DEBUG + std::cerr << "ftDataMultiplex::recvChunkMap() Passing map of file " << hash << ", to FT Module"; std::cerr << std::endl; #endif - /* error */ - return false; + + (it->second).mCreator->setSourceMap(peerId, compressed_map); + return true ; + } + else + { + std::map::iterator it = mServers.find(hash) ; + + if(it == mServers.end()) + { +#ifdef MPLEX_DEBUG + std::cerr << "ftDataMultiplex::handleRecvChunkMap() ERROR: No matching file Provider for hash " << hash ; + std::cerr << std::endl; +#endif + } + + it->second->setClientMap(peerId, compressed_map); + return true ; } -#ifdef MPLEX_DEBUG - std::cerr << "ftDataMultiplex::handleRecvMap() Passing map to FT Module"; - std::cerr << std::endl; -#endif - - (it->second).mCreator->setSourceMap(peerId, chunk_size, nb_chunks,compressed_map); - - return true; - + return false; } -bool ftDataMultiplex::handleRecvData(std::string peerId, - std::string hash, uint64_t size, +bool ftDataMultiplex::handleRecvClientChunkMapRequest(const std::string& peerId, const std::string& hash) +{ + CompressedChunkMap cmap ; + + { + RsStackMutex stack(dataMtx); /******* LOCK MUTEX ******/ + + std::map::iterator it = mClients.find(hash); + + if(it == mClients.end()) + { + // If we can't find the client, it's not a problem. Chunk maps from + // clients are not essential, as they are only used for display. +#ifdef MPLEX_DEBUG + std::cerr << "ftDataMultiplex::handleRecvServerChunkMapRequest() ERROR: No matching Client for hash " << hash ; + std::cerr << ". Performing local search." << std::endl; +#endif + return false; + } + +#ifdef MPLEX_DEBUG + std::cerr << "ftDataMultiplex::handleRecvServerChunkMapRequest() Sending map of file " << hash << ", to peer " << peerId << std::endl; +#endif + + (it->second).mCreator->getAvailabilityMap(cmap); + } + + mDataSend->sendChunkMap(peerId,hash,cmap); + + return true ; +} + +bool ftDataMultiplex::handleRecvServerChunkMapRequest(const std::string& peerId, const std::string& hash) +{ + CompressedChunkMap cmap ; + std::map::iterator it ; + bool found = true ; + + { + RsStackMutex stack(dataMtx); /******* LOCK MUTEX ******/ + + it = mServers.find(hash) ; + + if(it == mServers.end()) + found = false ; + } + + if(!found) + { +#ifdef MPLEX_DEBUG + std::cerr << "ftDataMultiplex::handleRecvChunkMapReq() ERROR: No matching file Provider for hash " << hash ; + std::cerr << std::endl; +#endif + if(!handleSearchRequest(peerId,hash)) + return false ; + +#ifdef MPLEX_DEBUG + std::cerr << "ftDataMultiplex::handleRecvChunkMapReq() A new file Provider has been made up for hash " << hash ; + std::cerr << std::endl; +#endif + } + + { + RsStackMutex stack(dataMtx); /******* LOCK MUTEX ******/ + + it = mServers.find(hash) ; + it->second->getAvailabilityMap(cmap); + } + + mDataSend->sendChunkMap(peerId,hash,cmap); + + return true; +} + +bool ftDataMultiplex::handleRecvData(const std::string& peerId, + const std::string& hash, uint64_t size, uint64_t offset, uint32_t chunksize, void *data) { RsStackMutex stack(dataMtx); /******* LOCK MUTEX ******/ @@ -390,9 +513,7 @@ bool ftDataMultiplex::handleRecvData(std::string peerId, /* called by ftTransferModule */ -bool ftDataMultiplex::handleRecvDataRequest(std::string peerId, - std::string hash, uint64_t size, - uint64_t offset, uint32_t chunksize) +bool ftDataMultiplex::handleRecvDataRequest(const std::string& peerId, const std::string& hash, uint64_t size, uint64_t offset, uint32_t chunksize) { /**** Find Files *****/ @@ -412,8 +533,7 @@ bool ftDataMultiplex::handleRecvDataRequest(std::string peerId, std::cerr << "ftDataMultiplex::handleRecvData() Matched to a Client."; std::cerr << std::endl; #endif - locked_handleServerRequest((cit->second).mCreator, - peerId, hash, size, offset, chunksize); + locked_handleServerRequest((cit->second).mCreator, peerId, hash, size, offset, chunksize); return true; } @@ -424,8 +544,7 @@ bool ftDataMultiplex::handleRecvDataRequest(std::string peerId, std::cerr << "ftDataMultiplex::handleRecvData() Matched to a Provider."; std::cerr << std::endl; #endif - locked_handleServerRequest(sit->second, - peerId, hash, size, offset, chunksize); + locked_handleServerRequest(sit->second, peerId, hash, size, offset, chunksize); return true; } @@ -435,9 +554,7 @@ bool ftDataMultiplex::handleRecvDataRequest(std::string peerId, #endif /* Add to Search Queue */ - mSearchQueue.push_back( - ftRequest(FT_DATA_REQ, peerId, hash, - size, offset, chunksize, NULL)); + mSearchQueue.push_back( ftRequest(FT_DATA_REQ, peerId, hash, size, offset, chunksize, NULL)); return true; } @@ -481,6 +598,33 @@ bool ftDataMultiplex::locked_handleServerRequest(ftFileProvider *provider, return false; } +bool ftDataMultiplex::getClientChunkMap(const std::string& upload_hash,const std::string& peerId,CompressedChunkMap& cmap) +{ + bool too_old ; + { + RsStackMutex stack(dataMtx); /******* LOCK MUTEX ******/ + + std::map::iterator sit = mServers.find(upload_hash); + + if(mServers.end() == sit) + return false ; + + sit->second->getClientMap(peerId,cmap,too_old) ; + } + + // If the map is too old then we should ask an other map to the peer. + // + if(too_old) + sendChunkMapRequest(peerId,upload_hash); + + return true ; +} + +bool ftDataMultiplex::sendChunkMapRequest(const std::string& peer_id,const std::string& hash) +{ + return mDataSend->sendChunkMapRequest(peer_id,hash); +} + void ftDataMultiplex::deleteServers(const std::list& serv) { RsStackMutex stack(dataMtx); /******* LOCK MUTEX ******/ @@ -491,42 +635,42 @@ void ftDataMultiplex::deleteServers(const std::list& serv) if(mServers.end() != sit) { - delete sit->second; + // Only delete servers that are not also file creators! + // + if(dynamic_cast(sit->second) == NULL) + delete sit->second; + mServers.erase(sit); } } } -bool ftDataMultiplex::handleSearchRequest(std::string peerId, - std::string hash, uint64_t size, - uint64_t offset, uint32_t chunksize) +bool ftDataMultiplex::handleSearchRequest(const std::string& peerId, const std::string& hash) { - - #ifdef MPLEX_DEBUG std::cerr << "ftDataMultiplex::handleSearchRequest("; - std::cerr << peerId << ", " << hash << ", " << size << "...)"; + std::cerr << peerId << ", " << hash << "...)"; std::cerr << std::endl; #endif - { - RsStackMutex stack(dataMtx); /******* LOCK MUTEX ******/ - - /* Check for bad requests */ - std::map::iterator bit; - if (mUnknownHashs.end() != (bit = mUnknownHashs.find(hash))) - { - -#ifdef MPLEX_DEBUG - std::cerr << "ftDataMultiplex::handleSearchRequest("; - std::cerr << " Found Ignore Hash ... done"; - std::cerr << std::endl; -#endif - - /* We've previously rejected this one, so ignore */ - return false; - } - } +// { +// RsStackMutex stack(dataMtx); /******* LOCK MUTEX ******/ +// +// /* Check for bad requests */ +// std::map::iterator bit; +// if (mUnknownHashs.end() != (bit = mUnknownHashs.find(hash))) +// { +// +//#ifdef MPLEX_DEBUG +// std::cerr << "ftDataMultiplex::handleSearchRequest("; +// std::cerr << " Found Ignore Hash ... done"; +// std::cerr << std::endl; +//#endif +// +// /* We've previously rejected this one, so ignore */ +// return false; +// } +// } /* @@ -535,14 +679,10 @@ bool ftDataMultiplex::handleSearchRequest(std::string peerId, * (anywhere but remote really) */ - FileInfo info; - uint32_t hintflags = (RS_FILE_HINTS_CACHE | - RS_FILE_HINTS_EXTRA | - RS_FILE_HINTS_LOCAL | - RS_FILE_HINTS_SPEC_ONLY); + uint32_t hintflags = (RS_FILE_HINTS_CACHE | RS_FILE_HINTS_EXTRA | RS_FILE_HINTS_LOCAL | RS_FILE_HINTS_SPEC_ONLY); - if (mSearch->search(hash, size, hintflags, info)) + if (mSearch->search(hash, hintflags, info)) { #ifdef MPLEX_DEBUG @@ -551,28 +691,35 @@ bool ftDataMultiplex::handleSearchRequest(std::string peerId, std::cerr << std::endl; #endif - /* setup a new provider */ RsStackMutex stack(dataMtx); /******* LOCK MUTEX ******/ - ftFileProvider *provider = - new ftFileProvider(info.path, size, hash); + ftFileProvider *provider = new ftFileProvider(info.path, info.size, hash); mServers[hash] = provider; - /* handle request finally */ - locked_handleServerRequest(provider, - peerId, hash, size, offset, chunksize); - - - /* now we should should check if any further requests for the same - * file exists ... (can happen with caches!) - * - * but easier to check pre-search.... - */ - return true; } + // Now check wether the required file is actually being downloaded. In such a case, + // setup the file provider to be the file creator itself. Warning: this server should not + // be deleted when not used anymore. We need to restrict this to client peers that are + // not ourself, since the file transfer also handles the local cache traffic (this + // is something to be changed soon!!) + // + + if(peerId != mOwnId) + { + RsStackMutex stack(dataMtx); /******* LOCK MUTEX ******/ + + std::map::const_iterator it(mClients.find(hash)) ; + + if(it != mClients.end()) + { + mServers[hash] = it->second.mCreator ; + return true; + } + } + return false; } diff --git a/libretroshare/src/ft/ftdatamultiplex.h b/libretroshare/src/ft/ftdatamultiplex.h index 4213b8c65..b00d5b7cc 100644 --- a/libretroshare/src/ft/ftdatamultiplex.h +++ b/libretroshare/src/ft/ftdatamultiplex.h @@ -102,23 +102,31 @@ class ftDataMultiplex: public ftDataRecv, public RsQueueThread /*************** SEND INTERFACE (calls ftDataSend) *******************/ /* Client Send */ - bool sendDataRequest(std::string peerId, std::string hash, uint64_t size, - uint64_t offset, uint32_t chunksize); + bool sendDataRequest(const std::string& peerId, const std::string& hash, uint64_t size, uint64_t offset, uint32_t chunksize); /* Server Send */ - bool sendData(std::string peerId, std::string hash, uint64_t size, - uint64_t offset, uint32_t chunksize, void *data); + bool sendData(const std::string& peerId, const std::string& hash, uint64_t size, uint64_t offset, uint32_t chunksize, void *data); + /* Server/client Send */ + bool sendChunkMapRequest(const std::string& peerId, const std::string& hash) ; /*************** RECV INTERFACE (provides ftDataRecv) ****************/ /* Client Recv */ - virtual bool recvData(std::string peerId, std::string hash, uint64_t size, uint64_t offset, uint32_t chunksize, void *data); - virtual bool recvFileMap(const std::string& peerId, const std::string& hash, uint32_t chunk_size, uint32_t nb_chunks, const std::vector& compressed_map) ; + virtual bool recvData(const std::string& peerId, const std::string& hash, uint64_t size, uint64_t offset, uint32_t chunksize, void *data); + + /// Receive a request for a chunk map + virtual bool recvChunkMapRequest(const std::string& peer_id,const std::string& hash,bool is_client) ; + /// Receive a chunk map + virtual bool recvChunkMap(const std::string& peer_id,const std::string& hash,const CompressedChunkMap& cmap,bool is_client) ; /* Server Recv */ - virtual bool recvDataRequest(std::string peerId, std::string hash, uint64_t size, uint64_t offset, uint32_t chunksize); + virtual bool recvDataRequest(const std::string& peerId, const std::string& hash, uint64_t size, uint64_t offset, uint32_t chunksize); + // Returns the chunk map from the file uploading client. Also initiates a chunk map request if this + // map is too old. This supposes that the caller will ask again in a few seconds. + // + bool getClientChunkMap(const std::string& upload_hash,const std::string& peer_id,CompressedChunkMap& map) ; protected: @@ -129,9 +137,11 @@ class ftDataMultiplex: public ftDataRecv, public RsQueueThread private: /* Handling Job Queues */ - bool handleRecvData(std::string peerId, std::string hash, uint64_t size, uint64_t offset, uint32_t chunksize, void *data); - bool handleRecvDataRequest(std::string peerId, std::string hash, uint64_t size, uint64_t offset, uint32_t chunksize); - bool handleSearchRequest(std::string peerId, std::string hash, uint64_t size, uint64_t offset, uint32_t chunksize); + bool handleRecvData(const std::string& peerId, const std::string& hash, uint64_t size, uint64_t offset, uint32_t chunksize, void *data); + bool handleRecvDataRequest(const std::string& peerId, const std::string& hash, uint64_t size, uint64_t offset, uint32_t chunksize); + bool handleSearchRequest(const std::string& peerId, const std::string& hash); + bool handleRecvClientChunkMapRequest(const std::string& peerId, const std::string& hash) ; + bool handleRecvServerChunkMapRequest(const std::string& peerId, const std::string& hash) ; /* We end up doing the actual server job here */ bool locked_handleServerRequest(ftFileProvider *provider, std::string peerId, std::string hash, uint64_t size, uint64_t offset, uint32_t chunksize); @@ -143,7 +153,7 @@ class ftDataMultiplex: public ftDataRecv, public RsQueueThread std::list mRequestQueue; std::list mSearchQueue; - std::map mUnknownHashs; +// std::map mUnknownHashs; ftDataSend *mDataSend; ftSearch *mSearch; diff --git a/libretroshare/src/ft/ftdbase.cc b/libretroshare/src/ft/ftdbase.cc index f646c245a..d703fb174 100644 --- a/libretroshare/src/ft/ftdbase.cc +++ b/libretroshare/src/ft/ftdbase.cc @@ -37,7 +37,7 @@ ftFiStore::ftFiStore(CacheStrapper *cs, CacheTransfer *cft, NotifyBase *cb_in, return; } -bool ftFiStore::search(std::string hash, uint64_t size, uint32_t hintflags, FileInfo &info) const +bool ftFiStore::search(std::string hash, uint32_t hintflags, FileInfo &info) const { /* could use hintflags to specify which bits of fileinfo to use additionally. eg. hintflags & FT_SEARCH_PEER_ID, then only return matching peers + hash. @@ -47,7 +47,7 @@ bool ftFiStore::search(std::string hash, uint64_t size, uint32_t hintflags, File */ #ifdef DB_DEBUG - std::cerr << "ftFiStore::search(" << hash << "," << size << "," << hintflags; + std::cerr << "ftFiStore::search(" << hash << "," << hintflags; std::cerr << ")"; std::cerr << std::endl; #endif @@ -68,8 +68,8 @@ bool ftFiStore::search(std::string hash, uint64_t size, uint32_t hintflags, File #endif bool fullmatch = true; - if (it->size != size) - fullmatch = false; +// if (it->size != size) +// fullmatch = false; #if 0 @@ -125,13 +125,13 @@ ftFiMonitor::ftFiMonitor(CacheStrapper *cs,NotifyBase *cb_in, std::string cached return; } -bool ftFiMonitor::search(std::string hash, uint64_t size, uint32_t hintflags, FileInfo &info) const +bool ftFiMonitor::search(std::string hash, uint32_t hintflags, FileInfo &info) const { uint64_t fsize; std::string path; #ifdef DB_DEBUG - std::cerr << "ftFiMonitor::search(" << hash << "," << size << "," << hintflags; + std::cerr << "ftFiMonitor::search(" << hash << "," << hintflags; std::cerr << ")"; std::cerr << std::endl; #endif @@ -269,10 +269,10 @@ ftCacheStrapper::ftCacheStrapper(p3AuthMgr *am, p3ConnectMgr *cm) } /* overloaded search function */ -bool ftCacheStrapper::search(std::string hash, uint64_t size, uint32_t hintflags, FileInfo &info) const +bool ftCacheStrapper::search(std::string hash, uint32_t hintflags, FileInfo &info) const { #ifdef DB_DEBUG - std::cerr << "ftCacheStrapper::search(" << hash << "," << size << "," << hintflags; + std::cerr << "ftCacheStrapper::search(" << hash << "," << hintflags; std::cerr << ")"; std::cerr << std::endl; #endif diff --git a/libretroshare/src/ft/ftdbase.h b/libretroshare/src/ft/ftdbase.h index cce1b6b71..0c9f1dcc0 100644 --- a/libretroshare/src/ft/ftdbase.h +++ b/libretroshare/src/ft/ftdbase.h @@ -48,7 +48,7 @@ class ftFiStore: public FileIndexStore, public ftSearch RsPeerId ownid, std::string cachedir); /* overloaded search function */ -virtual bool search(std::string hash, uint64_t size, uint32_t hintflags, FileInfo &info) const; +virtual bool search(std::string hash, uint32_t hintflags, FileInfo &info) const; }; class ftFiMonitor: public FileIndexMonitor, public ftSearch, public p3Config @@ -57,7 +57,7 @@ class ftFiMonitor: public FileIndexMonitor, public ftSearch, public p3Config ftFiMonitor(CacheStrapper *cs,NotifyBase *cb_in, std::string cachedir, std::string pid); /* overloaded search function */ - virtual bool search(std::string hash, uint64_t size, uint32_t hintflags, FileInfo &info) const; + virtual bool search(std::string hash, uint32_t hintflags, FileInfo &info) const; /* overloaded set dirs enables config indication */ virtual void setSharedDirectories(std::list dirList); @@ -81,7 +81,7 @@ class ftCacheStrapper: public CacheStrapper, public ftSearch ftCacheStrapper(p3AuthMgr *am, p3ConnectMgr *cm); /* overloaded search function */ -virtual bool search(std::string hash, uint64_t size, uint32_t hintflags, FileInfo &info) const; +virtual bool search(std::string hash, uint32_t hintflags, FileInfo &info) const; }; diff --git a/libretroshare/src/ft/ftextralist.cc b/libretroshare/src/ft/ftextralist.cc index beba291bc..16a8b3624 100644 --- a/libretroshare/src/ft/ftextralist.cc +++ b/libretroshare/src/ft/ftextralist.cc @@ -318,14 +318,14 @@ bool ftExtraList::hashExtraFileDone(std::string path, FileInfo &info) } hash = it->second; } - return search(hash, 0, 0, info); + return search(hash, 0, info); } /*** * Search Function - used by File Transfer * **/ -bool ftExtraList::search(std::string hash, uint64_t size, uint32_t hintflags, FileInfo &info) const +bool ftExtraList::search(std::string hash, uint32_t hintflags, FileInfo &info) const { #ifdef DEBUG_ELIST diff --git a/libretroshare/src/ft/ftextralist.h b/libretroshare/src/ft/ftextralist.h index e09eb1087..7dc1a5843 100644 --- a/libretroshare/src/ft/ftextralist.h +++ b/libretroshare/src/ft/ftextralist.h @@ -138,7 +138,7 @@ bool hashExtraFileDone(std::string path, FileInfo &info); * implementation of ftSearch. * **/ -virtual bool search(std::string hash, uint64_t size, uint32_t hintflags, FileInfo &info) const; +virtual bool search(std::string hash, uint32_t hintflags, FileInfo &info) const; /*** * Thread Main Loop diff --git a/libretroshare/src/ft/ftfilecreator.cc b/libretroshare/src/ft/ftfilecreator.cc index 7d4d9c061..5e2c61505 100644 --- a/libretroshare/src/ft/ftfilecreator.cc +++ b/libretroshare/src/ft/ftfilecreator.cc @@ -38,16 +38,19 @@ ftFileCreator::ftFileCreator(std::string path, uint64_t size, std::string hash, bool ftFileCreator::getFileData(uint64_t offset, uint32_t &chunk_size, void *data) { + // Only send the data if we actually have it. + // + bool have_it = false ; { RsStackMutex stack(ftcMutex); /********** STACK LOCKED MTX ******/ - if (offset + chunk_size > mStart) - { - /* don't have the data */ - return false; - } + + have_it = chunkMap.isChunkAvailable(offset, chunk_size) ; } - return ftFileProvider::getFileData(offset, chunk_size, data); + if(have_it) + return ftFileProvider::getFileData(offset, chunk_size, data); + else + return false ; } uint64_t ftFileCreator::getRecvd() @@ -225,7 +228,6 @@ int ftFileCreator::locked_notifyReceived(uint64_t offset, uint32_t chunk_size) /* find the chunk */ std::map::iterator it = mChunks.find(offset); -// bool isFirst = false; if (it == mChunks.end()) { #ifdef FILE_DEBUG @@ -238,11 +240,6 @@ int ftFileCreator::locked_notifyReceived(uint64_t offset, uint32_t chunk_size) return 0; /* ignoring */ } -// if (it == mChunks.begin()) -// { -// isFirst = true; -// } - ftChunk chunk = it->second; mChunks.erase(it); @@ -256,25 +253,18 @@ int ftFileCreator::locked_notifyReceived(uint64_t offset, uint32_t chunk_size) else // notify the chunkmap that the slice is finished chunkMap.dataReceived(chunk.id) ; -// /* update how much has been completed */ -// if (isFirst) -// { -// mStart = offset + chunk_size; -// } - - // update chunk map - -// if (mChunks.size() == 0) -// { -// mStart = mEnd; -// } - /* otherwise there is another earlier block to go */ return 1; } +FileChunksInfo::ChunkStrategy ftFileCreator::getChunkStrategy() +{ + RsStackMutex stack(ftcMutex); /********** STACK LOCKED MTX ******/ + + return chunkMap.getStrategy() ; +} void ftFileCreator::setChunkStrategy(FileChunksInfo::ChunkStrategy s) { RsStackMutex stack(ftcMutex); /********** STACK LOCKED MTX ******/ @@ -289,7 +279,7 @@ void ftFileCreator::setChunkStrategy(FileChunksInfo::ChunkStrategy s) * But can return size = 0, if we are still waiting for the data. */ -bool ftFileCreator::getMissingChunk(const std::string& peer_id,uint32_t size_hint,uint64_t &offset, uint32_t& size) +bool ftFileCreator::getMissingChunk(const std::string& peer_id,uint32_t size_hint,uint64_t &offset, uint32_t& size,bool& source_chunk_map_needed) { RsStackMutex stack(ftcMutex); /********** STACK LOCKED MTX ******/ #ifdef FILE_DEBUG @@ -298,17 +288,7 @@ bool ftFileCreator::getMissingChunk(const std::string& peer_id,uint32_t size_hin std::cerr << std::endl; locked_printChunkMap(); #endif - - /* check start point */ - -// if(mStart == mSize) -// { -//#ifdef FILE_DEBUG -// std::cerr << "ffc::getMissingChunk() File Done"; -// std::cerr << std::endl; -//#endif -// return false; -// } + source_chunk_map_needed = false ; /* check for freed chunks */ time_t ts = time(NULL); @@ -338,30 +318,17 @@ bool ftFileCreator::getMissingChunk(const std::string& peer_id,uint32_t size_hin ftChunk chunk ; - if(!chunkMap.getDataChunk(peer_id,size_hint,chunk)) + if(!chunkMap.getDataChunk(peer_id,size_hint,chunk,source_chunk_map_needed)) return false ; -// if (mSize - mEnd < chunk) -// chunk = mSize - mEnd; -// -// offset = mEnd; -// mEnd += chunk; - -// if (chunk > 0) -// { #ifdef FILE_DEBUG std::cerr << "ffc::getMissingChunk() Retrieved new chunk: " << chunk << std::endl ; -// std::cerr << std::endl; -// std::cerr << " mStart: " << mStart << " mEnd: " << mEnd; -// std::cerr << "mSize: " << mSize; -// std::cerr << std::endl; #endif mChunks[chunk.offset] = chunk ; offset = chunk.offset ; size = chunk.size ; -// } return true; /* cos more data to get */ } @@ -382,7 +349,6 @@ bool ftFileCreator::locked_printChunkMap() #endif /* check start point */ -// std::cerr << "Size: " << mSize << " Start: " << mStart << " End: " << mEnd; std::cerr << "\tOutstanding Chunks:"; std::cerr << std::endl; @@ -394,23 +360,21 @@ bool ftFileCreator::locked_printChunkMap() return true; } -void ftFileCreator::loadAvailabilityMap(const std::vector& map,uint32_t chunk_size,uint32_t chunk_number,uint32_t strategy) +void ftFileCreator::setAvailabilityMap(const CompressedChunkMap& cmap) { RsStackMutex stack(ftcMutex); /********** STACK LOCKED MTX ******/ - chunkMap = ChunkMap(mSize,map,chunk_size,chunk_number,FileChunksInfo::ChunkStrategy(strategy)) ; + chunkMap.setAvailabilityMap(cmap) ; } -void ftFileCreator::storeAvailabilityMap(std::vector& map,uint32_t& chunk_size,uint32_t& chunk_number,uint32_t& strategy) +void ftFileCreator::getAvailabilityMap(CompressedChunkMap& map) { RsStackMutex stack(ftcMutex); /********** STACK LOCKED MTX ******/ - FileChunksInfo::ChunkStrategy strat ; - chunkMap.buildAvailabilityMap(map,chunk_size,chunk_number,strat) ; - strategy = (uint32_t)strat ; + chunkMap.getAvailabilityMap(map) ; } -void ftFileCreator::setSourceMap(const std::string& peer_id,uint32_t chunk_size,uint32_t nb_chunks,const std::vector& compressed_map) +void ftFileCreator::setSourceMap(const std::string& peer_id,const CompressedChunkMap& compressed_map) { RsStackMutex stack(ftcMutex); /********** STACK LOCKED MTX ******/ @@ -421,7 +385,7 @@ void ftFileCreator::setSourceMap(const std::string& peer_id,uint32_t chunk_size, // - then asking the chunkmap which chunks are being downloaded, but actually shouldn't // - cancelling them in the ftFileCreator, so that they can be re-asked later to another peer. // - chunkMap.setPeerAvailabilityMap(peer_id,chunk_size,nb_chunks,compressed_map) ; + chunkMap.setPeerAvailabilityMap(peer_id,compressed_map) ; } diff --git a/libretroshare/src/ft/ftfilecreator.h b/libretroshare/src/ft/ftfilecreator.h index 0ea6294f8..37f48f228 100644 --- a/libretroshare/src/ft/ftfilecreator.h +++ b/libretroshare/src/ft/ftfilecreator.h @@ -50,16 +50,20 @@ class ftFileCreator: public ftFileProvider uint64_t getRecvd(); void getChunkMap(FileChunksInfo& info) ; + void setChunkStrategy(FileChunksInfo::ChunkStrategy s) ; + FileChunksInfo::ChunkStrategy getChunkStrategy() ; /* * creation functions for FileCreator */ // Gets a new variable-sized chunk of size "size_hint" from the given peer id. The returned size, "size" is - // at most equal to size_hint. + // at most equal to size_hint. chunk_map_needed is set if + // - no chunkmap info is available. In such a case, the chunk info is irrelevant and false is returned. + // - the chunk info is too old. In tis case, true is returned, and the chunks info can be used. // - bool getMissingChunk(const std::string& peer_id,uint32_t size_hint,uint64_t& offset, uint32_t& size); + bool getMissingChunk(const std::string& peer_id,uint32_t size_hint,uint64_t& offset, uint32_t& size,bool& is_chunk_map_too_old); // actually store data in the file, and update chunks info // @@ -71,12 +75,12 @@ class ftFileCreator: public ftFileProvider // - getting info about current chunks for the GUI // - sending availability info to the peers for which we also are a source // - void loadAvailabilityMap(const std::vector& map,uint32_t chunk_size,uint32_t chunk_number,uint32_t chunk_strategy) ; - void storeAvailabilityMap(std::vector& map,uint32_t& chunk_size,uint32_t& chunk_number,uint32_t& chunk_strategy) ; + virtual void getAvailabilityMap(CompressedChunkMap& cmap) ; + void setAvailabilityMap(const CompressedChunkMap& cmap) ; // This is called when receiving the availability map from a source peer, for the file being handled. // - void setSourceMap(const std::string& peer_id,uint32_t chunk_size,uint32_t nb_chunks,const std::vector& map) ; + void setSourceMap(const std::string& peer_id,const CompressedChunkMap& map) ; protected: diff --git a/libretroshare/src/ft/ftfileprovider.cc b/libretroshare/src/ft/ftfileprovider.cc index d57036c2c..b3da9f4b7 100644 --- a/libretroshare/src/ft/ftfileprovider.cc +++ b/libretroshare/src/ft/ftfileprovider.cc @@ -1,9 +1,12 @@ #include "ftfileprovider.h" +#include "ftchunkmap.h" #include "util/rsdir.h" #include #include +static const time_t UPLOAD_CHUNK_MAPS_TIME = 30 ; // time to ask for a new chunkmap from uploaders in seconds. + ftFileProvider::ftFileProvider(std::string path, uint64_t size, std::string hash) : mSize(size), hash(hash), file_name(path), fd(NULL),transfer_rate(0),total_size(0) { @@ -73,6 +76,13 @@ bool ftFileProvider::FileDetails(FileInfo &info) return true; } +void ftFileProvider::getAvailabilityMap(CompressedChunkMap& cmap) +{ + // We are here because the file we deal with is complete. So we return a plain map. + // + ChunkMap::buildPlainMap(mSize,cmap) ; +} + bool ftFileProvider::getFileData(uint64_t offset, uint32_t &chunk_size, void *data) { @@ -168,6 +178,39 @@ bool ftFileProvider::getFileData(uint64_t offset, uint32_t &chunk_size, void *da return 1; } +void ftFileProvider::setClientMap(const std::string& peer_id,const CompressedChunkMap& cmap) +{ + RsStackMutex stack(ftcMutex); /********** STACK LOCKED MTX ******/ + + std::pair& map_info(clients_chunk_maps[peer_id]) ; + + map_info.first = cmap ; + map_info.second = time(NULL) ; +} + +void ftFileProvider::getClientMap(const std::string& peer_id,CompressedChunkMap& cmap,bool& map_is_too_old) +{ + RsStackMutex stack(ftcMutex); /********** STACK LOCKED MTX ******/ + + std::map >::iterator it(clients_chunk_maps.find(peer_id)) ; + + if(it == clients_chunk_maps.end()) + { + clients_chunk_maps[peer_id] = std::pair(CompressedChunkMap(),0) ; + it = clients_chunk_maps.find(peer_id) ; + } + + if(time(NULL) - it->second.second > UPLOAD_CHUNK_MAPS_TIME) + { + map_is_too_old = true ; + it->second.second = time(NULL) ; // to avoid re-asking before the TTL + } + else + map_is_too_old = false ; + + cmap = it->second.first ; +} + int ftFileProvider::initializeFileAttrs() { std::cerr << "ftFileProvider::initializeFileAttrs() Filename: "; diff --git a/libretroshare/src/ft/ftfileprovider.h b/libretroshare/src/ft/ftfileprovider.h index ab14664f8..e51718499 100644 --- a/libretroshare/src/ft/ftfileprovider.h +++ b/libretroshare/src/ft/ftfileprovider.h @@ -37,45 +37,57 @@ class ftFileProvider { -public: - ftFileProvider(std::string path, uint64_t size, std::string hash); - virtual ~ftFileProvider(); + public: + ftFileProvider(std::string path, uint64_t size, std::string hash); + virtual ~ftFileProvider(); - virtual bool getFileData(uint64_t offset, uint32_t &chunk_size, void *data); - virtual bool FileDetails(FileInfo &info); - std::string getHash(); - uint64_t getFileSize(); - bool fileOk(); + virtual bool getFileData(uint64_t offset, uint32_t &chunk_size, void *data); + virtual bool FileDetails(FileInfo &info); + std::string getHash(); + uint64_t getFileSize(); + bool fileOk(); - void setPeerId(const std::string& id) ; + void setPeerId(const std::string& id) ; + // Provides a client for the map of chunks actually present in the file. If the provider is also + // a file creator, because the file is actually being downloaded, then the map may be partially complete. + // Otherwize, a plain map is returned. + // + virtual void getAvailabilityMap(CompressedChunkMap& cmap) ; -protected: -virtual int initializeFileAttrs(); /* does for both */ + // a ftFileProvider feeds a distant peer. To display what the peers already has, we need to store/read this info. + void getClientMap(const std::string& peer_id,CompressedChunkMap& cmap,bool& map_is_too_old) ; + void setClientMap(const std::string& peer_id,const CompressedChunkMap& cmap) ; - uint64_t mSize; - std::string hash; - std::string file_name; - FILE *fd; + protected: + virtual int initializeFileAttrs(); /* does for both */ - /* - * Structure to gather statistics FIXME: lastRequestor - figure out a - * way to get last requestor (peerID) - */ - std::string lastRequestor; - uint64_t req_loc; - uint32_t req_size; - time_t lastTS; // used for checking if it's alive - time_t lastTS_t; // used for estimating transfer rate. + uint64_t mSize; + std::string hash; + std::string file_name; + FILE *fd; - // these two are used for speed estimation - float transfer_rate ; - uint32_t total_size ; + /* + * Structure to gather statistics FIXME: lastRequestor - figure out a + * way to get last requestor (peerID) + */ + std::string lastRequestor; + uint64_t req_loc; + uint32_t req_size; + time_t lastTS; // used for checking if it's alive + time_t lastTS_t; // used for estimating transfer rate. - /* - * Mutex Required for stuff below - */ - RsMutex ftcMutex; + // these two are used for speed estimation + float transfer_rate ; + uint32_t total_size ; + + // Info about what the downloading peer already has + std::map > clients_chunk_maps ; + + /* + * Mutex Required for stuff below + */ + RsMutex ftcMutex; }; diff --git a/libretroshare/src/ft/ftfilesearch.cc b/libretroshare/src/ft/ftfilesearch.cc index 8e380e761..24ac3b89f 100644 --- a/libretroshare/src/ft/ftfilesearch.cc +++ b/libretroshare/src/ft/ftfilesearch.cc @@ -75,12 +75,12 @@ bool ftFileSearch::addSearchMode(ftSearch *search, uint32_t hintflags) return false; } -bool ftFileSearch::search(std::string hash, uint64_t size, uint32_t hintflags, FileInfo &info) const +bool ftFileSearch::search(std::string hash, uint32_t hintflags, FileInfo &info) const { uint32_t hints, i; #ifdef DEBUG_SEARCH - std::cerr << "ftFileSearch::search(" << hash << ", " << size; + std::cerr << "ftFileSearch::search(" << hash ; std::cerr << ", " << hintflags << ");"; std::cerr << std::endl; #endif @@ -99,7 +99,7 @@ bool ftFileSearch::search(std::string hash, uint64_t size, uint32_t hintflags, F std::cerr << i; std::cerr << std::endl; #endif - if (search->search(hash, size, hintflags, info)) + if (search->search(hash, hintflags, info)) { #ifdef DEBUG_SEARCH std::cerr << "ftFileSearch::search() SLOT: "; @@ -155,7 +155,7 @@ bool ftFileSearch::search(std::string hash, uint64_t size, uint32_t hintflags, F std::cerr << "ftFileSearch::search() SLOT: " << i; std::cerr << std::endl; #endif - if (search->search(hash, size, hintflags, info)) + if (search->search(hash, hintflags, info)) { #ifdef DEBUG_SEARCH std::cerr << "ftFileSearch::search() SLOT: "; @@ -180,10 +180,10 @@ bool ftFileSearch::search(std::string hash, uint64_t size, uint32_t hintflags, F } -bool ftSearchDummy::search(std::string hash, uint64_t size, uint32_t hintflags, FileInfo &info) const +bool ftSearchDummy::search(std::string hash, uint32_t hintflags, FileInfo &info) const { #ifdef DEBUG_SEARCH - std::cerr << "ftSearchDummy::search(" << hash << ", " << size; + std::cerr << "ftSearchDummy::search(" << hash ; std::cerr << ", " << hintflags << ");"; std::cerr << std::endl; #endif diff --git a/libretroshare/src/ft/ftfilesearch.h b/libretroshare/src/ft/ftfilesearch.h index 24d7b8fb5..5b7340c06 100644 --- a/libretroshare/src/ft/ftfilesearch.h +++ b/libretroshare/src/ft/ftfilesearch.h @@ -45,7 +45,7 @@ class ftFileSearch: public ftSearch ftFileSearch(); bool addSearchMode(ftSearch *search, uint32_t hintflags); -virtual bool search(std::string hash, uint64_t size, uint32_t hintflags, FileInfo &info) const; +virtual bool search(std::string hash, uint32_t hintflags, FileInfo &info) const; private: diff --git a/libretroshare/src/ft/ftsearch.h b/libretroshare/src/ft/ftsearch.h index cb819ffd4..4c428a515 100644 --- a/libretroshare/src/ft/ftsearch.h +++ b/libretroshare/src/ft/ftsearch.h @@ -43,7 +43,7 @@ class ftSearch ftSearch() { return; } virtual ~ftSearch() { return; } -virtual bool search(std::string hash, uint64_t size, uint32_t hintflags, FileInfo &info) const = 0; +virtual bool search(std::string hash, uint32_t hintflags, FileInfo &info) const = 0; }; @@ -54,7 +54,7 @@ class ftSearchDummy: public ftSearch ftSearchDummy() { return; } virtual ~ftSearchDummy() { return; } -virtual bool search(std::string hash, uint64_t size, uint32_t hintflags, FileInfo &info) const; +virtual bool search(std::string hash, uint32_t hintflags, FileInfo &info) const; }; #endif diff --git a/libretroshare/src/ft/ftserver.cc b/libretroshare/src/ft/ftserver.cc index 124efbfa0..c660eea92 100644 --- a/libretroshare/src/ft/ftserver.cc +++ b/libretroshare/src/ft/ftserver.cc @@ -310,21 +310,9 @@ void ftServer::getDwlDetails(std::list & details) mFtDwlQueue->getDwlDetails(details); } -bool ftServer::FileChunksDetails(const std::string& hash,FileChunksInfo& info) +bool ftServer::FileDownloadChunksDetails(const std::string& hash,FileChunksInfo& info) { - return mFtController->getFileChunksDetails(hash,info); -// -// // for know put some dummy info. It's for display sake only. -// info.chunk_size = 1024*1024 ; -// info.file_size = 250*info.chunk_size - 123 ; // last chunk is not complete. -// info.chunks.resize(250,FileChunksInfo::CHUNK_DONE) ; -// int n = rand()%150 + 50 ; -// for(int i=0;i<10;++i) -// info.chunks[n+i] = FileChunksInfo::CHUNK_ACTIVE ; -// for(int i=n+10;i<250;++i) -// info.chunks[i] = FileChunksInfo::CHUNK_OUTSTANDING ; -// -// return true ; + return mFtController->getFileDownloadChunksDetails(hash,info); } /* Directory Handling */ @@ -360,6 +348,11 @@ bool ftServer::FileDownloads(std::list &hashs) //return mFtDataplex->FileDownloads(hashs); } +bool ftServer::FileUploadChunksDetails(const std::string& hash,const std::string& peer_id,CompressedChunkMap& cmap) +{ + return mFtDataplex->getClientChunkMap(hash,peer_id,cmap); +} + bool ftServer::FileUploads(std::list &hashs) { return mFtDataplex->FileUploads(hashs); @@ -376,7 +369,7 @@ bool ftServer::FileDetails(std::string hash, uint32_t hintflags, FileInfo &info) return true ; if(hintflags & ~(RS_FILE_HINTS_UPLOAD | RS_FILE_HINTS_DOWNLOAD)) - if(mFtSearch->search(hash, 0, hintflags, info)) + if(mFtSearch->search(hash, hintflags, info)) return true ; return false; @@ -652,8 +645,7 @@ bool ftServer::loadConfigMap(std::map &configMap) /***************************************************************/ /* Client Send */ -bool ftServer::sendDataRequest(std::string peerId, std::string hash, - uint64_t size, uint64_t offset, uint32_t chunksize) +bool ftServer::sendDataRequest(const std::string& peerId, const std::string& hash, uint64_t size, uint64_t offset, uint32_t chunksize) { if(mTurtleRouter->isTurtlePeer(peerId)) mTurtleRouter->sendDataRequest(peerId,hash,size,offset,chunksize) ; @@ -680,12 +672,33 @@ bool ftServer::sendDataRequest(std::string peerId, std::string hash, return true; } +bool ftServer::sendChunkMapRequest(const std::string& peerId,const std::string& hash) +{ + if(mTurtleRouter->isTurtlePeer(peerId)) + mTurtleRouter->sendChunkMapRequest(peerId,hash) ; + + // We only send chunkmap requests to turtle peers. This will be a problem at display time for + // direct friends, so I'll see later whether I code it or not. + return true ; +} + +bool ftServer::sendChunkMap(const std::string& peerId,const std::string& hash,const CompressedChunkMap& map) +{ + if(mTurtleRouter->isTurtlePeer(peerId)) + mTurtleRouter->sendChunkMap(peerId,hash,map) ; + + // We only send chunkmap requests to turtle peers. This will be a problem at display time for + // direct friends, so I'll see later whether I code it or not. + return true ; +} + + //const uint32_t MAX_FT_CHUNK = 32 * 1024; /* 32K */ //const uint32_t MAX_FT_CHUNK = 16 * 1024; /* 16K */ const uint32_t MAX_FT_CHUNK = 8 * 1024; /* 16K */ /* Server Send */ -bool ftServer::sendData(std::string peerId, std::string hash, uint64_t size, uint64_t baseoffset, uint32_t chunksize, void *data) +bool ftServer::sendData(const std::string& peerId, const std::string& hash, uint64_t size, uint64_t baseoffset, uint32_t chunksize, void *data) { /* create a packet */ /* push to networking part */ diff --git a/libretroshare/src/ft/ftserver.h b/libretroshare/src/ft/ftserver.h index 50380b502..24d3f470f 100644 --- a/libretroshare/src/ft/ftserver.h +++ b/libretroshare/src/ft/ftserver.h @@ -138,7 +138,9 @@ virtual void getDwlDetails(std::list & details); virtual bool FileDownloads(std::list &hashs); virtual bool FileUploads(std::list &hashs); virtual bool FileDetails(std::string hash, uint32_t hintflags, FileInfo &info); -virtual bool FileChunksDetails(const std::string& hash,FileChunksInfo& info) ; +virtual bool FileDownloadChunksDetails(const std::string& hash,FileChunksInfo& info) ; +virtual bool FileUploadChunksDetails(const std::string& hash,const std::string& peer_id,CompressedChunkMap& map) ; + /*** * Extra List Access @@ -196,11 +198,11 @@ virtual bool unshareDownloadDirectory(); /*************** Data Transfer Interface ***********************/ /***************************************************************/ public: -virtual bool sendData(std::string peerId, std::string hash, uint64_t size, - uint64_t offset, uint32_t chunksize, void *data); -virtual bool sendDataRequest(std::string peerId, - std::string hash, uint64_t size, - uint64_t offset, uint32_t chunksize); +virtual bool sendData(const std::string& peerId, const std::string& hash, uint64_t size, uint64_t offset, uint32_t chunksize, void *data); +virtual bool sendDataRequest(const std::string& peerId, const std::string& hash, uint64_t size, uint64_t offset, uint32_t chunksize); +virtual bool sendChunkMapRequest(const std::string& peer_id,const std::string& hash) ; +virtual bool sendChunkMap(const std::string& peer_id,const std::string& hash,const CompressedChunkMap& cmap) ; + /*************** Internal Transfer Fns *************************/ virtual int tick(); diff --git a/libretroshare/src/ft/fttransfermodule.cc b/libretroshare/src/ft/fttransfermodule.cc index 43cfe107b..62b4fa2ca 100644 --- a/libretroshare/src/ft/fttransfermodule.cc +++ b/libretroshare/src/ft/fttransfermodule.cc @@ -327,7 +327,11 @@ bool ftTransferModule::getChunk(const std::string& peer_id,uint32_t size_hint,ui std::cerr << std::endl; #endif - bool val = mFileCreator->getMissingChunk(peer_id,size_hint,offset, chunk_size); + bool source_peer_map_needed ; + bool val = mFileCreator->getMissingChunk(peer_id,size_hint,offset, chunk_size,source_peer_map_needed); + + if(source_peer_map_needed) + mMultiplexor->sendChunkMapRequest(peer_id, mHash) ; #ifdef FT_DEBUG if (val) @@ -338,12 +342,14 @@ bool ftTransferModule::getChunk(const std::string& peer_id,uint32_t size_hint,ui std::cerr << " size: " << mSize; std::cerr << " offset: " << offset; std::cerr << " chunk_size: " << chunk_size; + std::cerr << " peer map needed = " << source_peer_map_needed << std::endl ; std::cerr << std::endl; } else { std::cerr << "ftTransferModule::getChunk()"; std::cerr << " Answer: No Chunk Available"; + std::cerr << " peer map needed = " << source_peer_map_needed << std::endl ; std::cerr << std::endl; } #endif diff --git a/libretroshare/src/rsiface/rsfiles.h b/libretroshare/src/rsiface/rsfiles.h index 1c5eb5344..c1dcc1820 100644 --- a/libretroshare/src/rsiface/rsfiles.h +++ b/libretroshare/src/rsiface/rsfiles.h @@ -129,7 +129,12 @@ virtual void getDwlDetails(std::list & details) = 0; virtual bool FileDownloads(std::list &hashs) = 0; virtual bool FileUploads(std::list &hashs) = 0; virtual bool FileDetails(std::string hash, uint32_t hintflags, FileInfo &info) = 0; -virtual bool FileChunksDetails(const std::string& hash,FileChunksInfo& info) = 0 ; + +/// Gives chunk details about the downloaded file with given hash. +virtual bool FileDownloadChunksDetails(const std::string& hash,FileChunksInfo& info) = 0 ; + +/// details about the upload with given hash +virtual bool FileUploadChunksDetails(const std::string& hash,const std::string& peer_id,CompressedChunkMap& map) = 0 ; /*** * Extra List Access diff --git a/libretroshare/src/rsiface/rstypes.h b/libretroshare/src/rsiface/rstypes.h index 36b0442fa..4cf6bc1dd 100644 --- a/libretroshare/src/rsiface/rstypes.h +++ b/libretroshare/src/rsiface/rstypes.h @@ -28,6 +28,7 @@ #include +#include #include #include #include @@ -250,7 +251,10 @@ enum DwlPriority { Low = 0, Normal, High, Auto }; // Macro to read a bits array for compressed chunk maps // -#define COMPRESSED_MAP_READ(A,j) (A[j >> 5] & (1 << (j & 0x11111))) +//#define COMPRESSED_MAP_READ(A,j) (A[j >> 5] & (1 << (j & 0x11111))) +//#define COMPRESSED_MAP_WRITE(A,j,x) (A[j >> 5] |= (1 << (j & 0x11111))) + +class CompressedChunkMap ; class FileChunksInfo { @@ -265,12 +269,48 @@ class FileChunksInfo std::vector chunks ; // For each source peer, gives the compressed bit map of have/don't have sate - std::vector > > compressed_peer_availability_maps ; + std::map compressed_peer_availability_maps ; // For each chunk (by chunk number), gives the completion of the chunk. // std::vector > active_chunks ; +}; +class CompressedChunkMap +{ + public: + CompressedChunkMap() {} + + CompressedChunkMap(const std::vector& uncompressed_data) + { + _map.resize( getCompressedSize(uncompressed_data.size()),0 ) ; + + for(uint32_t i=0;i>5) + !!(size&31) ; } + + uint32_t filledChunks(uint32_t nbchks) + { + uint32_t res = 0 ; + for(uint32_t i=0;i> 5] & (1 << (i & 31))) > 0 ; } + + inline void set(uint32_t j) { _map[j >> 5] |= (1 << (j & 31)) ; } + inline void reset(uint32_t j) { _map[j >> 5] &= ~(1 << (j & 31)) ; } + + /// compressed map, one bit per chunk + std::vector _map ; }; /* class which encapsulates download details */ diff --git a/libretroshare/src/serialiser/rsconfigitems.cc b/libretroshare/src/serialiser/rsconfigitems.cc index 496df33ed..78adc4d4f 100644 --- a/libretroshare/src/serialiser/rsconfigitems.cc +++ b/libretroshare/src/serialiser/rsconfigitems.cc @@ -196,11 +196,9 @@ uint32_t RsFileConfigSerialiser::sizeTransfer(RsFileTransfer *item) s += 4; /* trate */ s += 4; /* lrate */ s += 4; /* ltransfer */ - s += 4; // chunk_size - s += 4; // chunk_number s += 4; // chunk_strategy s += 4; // chunk map size - s += 4*item->chunk_map.size(); // chunk_map + s += 4*item->compressed_chunk_map._map.size(); // compressed_chunk_map return s; } @@ -243,13 +241,11 @@ bool RsFileConfigSerialiser::serialiseTransfer(RsFileTransfer *item, void *d ok &= setRawUInt32(data, tlvsize, &offset, item->lrate); ok &= setRawUInt32(data, tlvsize, &offset, item->ltransfer); - ok &= setRawUInt32(data, tlvsize, &offset, item->chunk_size); - ok &= setRawUInt32(data, tlvsize, &offset, item->chunk_number); ok &= setRawUInt32(data, tlvsize, &offset, item->chunk_strategy); - ok &= setRawUInt32(data, tlvsize, &offset, item->chunk_map.size()); + ok &= setRawUInt32(data, tlvsize, &offset, item->compressed_chunk_map._map.size()); - for(uint32_t i=0;ichunk_map.size();++i) - ok &= setRawUInt32(data, tlvsize, &offset, item->chunk_map[i]); + for(uint32_t i=0;icompressed_chunk_map._map.size();++i) + ok &= setRawUInt32(data, tlvsize, &offset, item->compressed_chunk_map._map[i]); if (offset != tlvsize) { @@ -310,15 +306,13 @@ RsFileTransfer *RsFileConfigSerialiser::deserialiseTransfer(void *data, uint32_t ok &= getRawUInt32(data, rssize, &offset, &(item->lrate)); ok &= getRawUInt32(data, rssize, &offset, &(item->ltransfer)); - ok &= getRawUInt32(data, rssize, &offset, &(item->chunk_size)); - ok &= getRawUInt32(data, rssize, &offset, &(item->chunk_number)); ok &= getRawUInt32(data, rssize, &offset, &(item->chunk_strategy)); uint32_t map_size = 0 ; ok &= getRawUInt32(data, rssize, &offset, &map_size); - item->chunk_map.resize(map_size) ; + item->compressed_chunk_map._map.resize(map_size) ; for(uint32_t i=0;ichunk_map[i])); + ok &= getRawUInt32(data, rssize, &offset, &(item->compressed_chunk_map._map[i])); if (offset != rssize) { diff --git a/libretroshare/src/serialiser/rsconfigitems.h b/libretroshare/src/serialiser/rsconfigitems.h index c079d8279..2c68ab3ce 100644 --- a/libretroshare/src/serialiser/rsconfigitems.h +++ b/libretroshare/src/serialiser/rsconfigitems.h @@ -29,6 +29,7 @@ #include #include +#include #include "serialiser/rsserial.h" #include "serialiser/rstlvbase.h" #include "serialiser/rstlvtypes.h" @@ -204,10 +205,8 @@ class RsFileTransfer: public RsItem uint32_t ltransfer; // chunk information - uint32_t chunk_size ; // common size of chunks - uint32_t chunk_number ; // total number of chunks (this is not redondant, cause chunks are compressed) uint32_t chunk_strategy ; // strategy flags for chunks - std::vector chunk_map ; // chunk availability (bitwise) + CompressedChunkMap compressed_chunk_map ; // chunk availability (bitwise) }; /**************************************************************************/ diff --git a/libretroshare/src/turtle/p3turtle.cc b/libretroshare/src/turtle/p3turtle.cc index 3c85b7b8e..8c00ecfad 100644 --- a/libretroshare/src/turtle/p3turtle.cc +++ b/libretroshare/src/turtle/p3turtle.cc @@ -782,25 +782,24 @@ void p3turtle::routeGenericTunnelItem(RsTurtleGenericTunnelItem *item) // The packet was not forwarded, so it is for us. Let's treat it. // This is done off-mutex, to avoid various deadlocks // - if(direction == RsTurtleGenericTunnelItem::DIRECTION_SERVER) - switch(item->PacketSubType()) - { - case RS_TURTLE_SUBTYPE_FILE_REQUEST: handleRecvFileRequest(dynamic_cast(item)) ; - break ; - default: - std::cerr << "Unknown server packet type received: id=" << (void*)(item->PacketSubType()) << std::endl ; - exit(-1) ; - } + + switch(item->PacketSubType()) + { + case RS_TURTLE_SUBTYPE_FILE_REQUEST: handleRecvFileRequest(dynamic_cast(item)) ; + break ; - if(direction == RsTurtleGenericTunnelItem::DIRECTION_CLIENT) - switch(item->PacketSubType()) - { - case RS_TURTLE_SUBTYPE_FILE_DATA : handleRecvFileData(dynamic_cast(item)) ; - break ; - default: - std::cerr << "Unknown client packet type received: id=" << (void*)(item->PacketSubType()) << std::endl ; - exit(-1) ; - } + case RS_TURTLE_SUBTYPE_FILE_DATA : handleRecvFileData(dynamic_cast(item)) ; + break ; + + case RS_TURTLE_SUBTYPE_FILE_MAP : handleRecvFileMap(dynamic_cast(item)) ; + break ; + + case RS_TURTLE_SUBTYPE_FILE_MAP_REQUEST: handleRecvFileMapRequest(dynamic_cast(item)) ; + break ; + default: + std::cerr << "Unknown packet type received: id=" << (void*)(item->PacketSubType()) << std::endl ; + exit(-1) ; + } delete item ; } @@ -908,6 +907,42 @@ void p3turtle::handleRecvFileData(RsTurtleFileDataItem *item) // down _ft_server->getMultiplexer()->recvData()...in ftTransferModule::recvFileData } +void p3turtle::handleRecvFileMapRequest(RsTurtleFileMapRequestItem *item) +{ +#ifdef P3TURTLE_DEBUG + std::cerr << "p3Turtle: received file Map item:" << std::endl ; + item->print(std::cerr,1) ; +#endif + std::string hash,vpid ; + { + RsStackMutex stack(mTurtleMtx); /********** STACK LOCKED MTX ******/ + + std::map::iterator it2(_local_tunnels.find(item->tunnel_id)) ; + + if(it2 == _local_tunnels.end()) + { +#ifdef P3TURTLE_DEBUG + std::cerr << "p3turtle: got file data with unknown tunnel id " << (void*)item->tunnel_id << std::endl ; +#endif + return ; + } + + TurtleTunnel& tunnel(it2->second) ; +#ifdef P3TURTLE_DEBUG + assert(!tunnel.hash.empty()) ; + + std::cerr << " This is an endpoint for this file map request." << std::endl ; + std::cerr << " Forwarding data to the multiplexer." << std::endl ; + std::cerr << " using peer_id=" << tunnel.vpid << ", hash=" << tunnel.hash << std::endl ; +#endif + // we should check that there is no backward call to the turtle router! + // + hash = tunnel.hash ; + vpid = tunnel.vpid ; + } + + _ft_server->getMultiplexer()->recvChunkMapRequest(vpid,hash,item->direction == RsTurtleGenericTunnelItem::DIRECTION_CLIENT) ; +} void p3turtle::handleRecvFileMap(RsTurtleFileMapItem *item) { @@ -915,6 +950,7 @@ void p3turtle::handleRecvFileMap(RsTurtleFileMapItem *item) std::cerr << "p3Turtle: received file Map item:" << std::endl ; item->print(std::cerr,1) ; #endif + std::string hash,vpid ; { RsStackMutex stack(mTurtleMtx); /********** STACK LOCKED MTX ******/ @@ -930,31 +966,19 @@ void p3turtle::handleRecvFileMap(RsTurtleFileMapItem *item) TurtleTunnel& tunnel(it2->second) ; - std::map::iterator it( _incoming_file_hashes.find(tunnel.hash) ) ; #ifdef P3TURTLE_DEBUG assert(!tunnel.hash.empty()) ; -#endif - if(it==_incoming_file_hashes.end()) - { -#ifdef P3TURTLE_DEBUG - std::cerr << "No tunnel for incoming data. Maybe the tunnel is being closed." << std::endl ; -#endif - return ; - } - const TurtleFileHashInfo& hash_info(it->second) ; -#ifdef P3TURTLE_DEBUG std::cerr << " This is an endpoint for this file map." << std::endl ; std::cerr << " Forwarding data to the multiplexer." << std::endl ; std::cerr << " using peer_id=" << tunnel.vpid << ", hash=" << tunnel.hash << std::endl ; #endif - // also update the hash time stamp to show that it's actually being downloaded. - it->second.time_stamp = time(NULL) ; - - // we should check that there is no backward call to the turtle router! + // We should check that there is no backward call to the turtle router! // - _ft_server->getMultiplexer()->recvFileMap(tunnel.vpid,tunnel.hash,item->chunk_size,item->nb_chunks,item->compressed_map) ; + vpid = tunnel.vpid ; + hash = tunnel.hash ; } + _ft_server->getMultiplexer()->recvChunkMap(vpid,hash,item->compressed_map,item->direction == RsTurtleGenericTunnelItem::DIRECTION_CLIENT) ; } // Send a data request into the correct tunnel for the given file hash @@ -1032,6 +1056,95 @@ void p3turtle::sendFileData(const std::string& peerId, const std::string& hash, sendItem(item) ; } +void p3turtle::sendChunkMapRequest(const std::string& peerId,const std::string& hash) +{ + RsStackMutex stack(mTurtleMtx); /********** STACK LOCKED MTX ******/ + + // get the proper tunnel for this file hash and peer id. + std::map::const_iterator it(_virtual_peers.find(peerId)) ; + + if(it == _virtual_peers.end()) + { +#ifdef P3TURTLE_DEBUG + std::cerr << "p3turtle::senddataRequest: cannot find virtual peer " << peerId << " in VP list." << std::endl ; +#endif + return ; + } + TurtleTunnelId tunnel_id = it->second ; + TurtleTunnel& tunnel(_local_tunnels[tunnel_id]) ; + +#ifdef P3TURTLE_DEBUG + assert(hash == tunnel.hash) ; +#endif + RsTurtleFileMapRequestItem *item = new RsTurtleFileMapRequestItem ; + item->tunnel_id = tunnel_id ; + + std::string ownid = mConnMgr->getOwnId() ; + + if(tunnel.local_src == ownid) + { + item->direction = RsTurtleGenericTunnelItem::DIRECTION_SERVER ; + item->PeerId(tunnel.local_dst) ; + } + else if(tunnel.local_dst == ownid) + { + item->direction = RsTurtleGenericTunnelItem::DIRECTION_CLIENT ; + item->PeerId(tunnel.local_src) ; + } + else + std::cerr << "p3turtle::sendChunkMap: consistency error!" << std::endl ; + +#ifdef P3TURTLE_DEBUG + std::cerr << "p3turtle: sending chunk map req to peer " << peerId << ", hash=0x" << hash << ") through tunnel " << (void*)item->tunnel_id << ", next peer=" << item->PeerId() << std::endl ; +#endif + sendItem(item) ; +} + +void p3turtle::sendChunkMap(const std::string& peerId,const std::string& hash,const CompressedChunkMap& cmap) +{ + RsStackMutex stack(mTurtleMtx); /********** STACK LOCKED MTX ******/ + + // get the proper tunnel for this file hash and peer id. + std::map::const_iterator it(_virtual_peers.find(peerId)) ; + + if(it == _virtual_peers.end()) + { +#ifdef P3TURTLE_DEBUG + std::cerr << "p3turtle::senddataRequest: cannot find virtual peer " << peerId << " in VP list." << std::endl ; +#endif + return ; + } + TurtleTunnelId tunnel_id = it->second ; + TurtleTunnel& tunnel(_local_tunnels[tunnel_id]) ; + +#ifdef P3TURTLE_DEBUG + assert(hash == tunnel.hash) ; +#endif + RsTurtleFileMapItem *item = new RsTurtleFileMapItem ; + item->tunnel_id = tunnel_id ; + item->compressed_map = cmap ; + + std::string ownid = mConnMgr->getOwnId() ; + + if(tunnel.local_src == ownid) + { + item->direction = RsTurtleGenericTunnelItem::DIRECTION_SERVER ; + item->PeerId(tunnel.local_dst) ; + } + else if(tunnel.local_dst == ownid) + { + item->direction = RsTurtleGenericTunnelItem::DIRECTION_CLIENT ; + item->PeerId(tunnel.local_src) ; + } + else + std::cerr << "p3turtle::sendChunkMap: consistency error!" << std::endl ; + +#ifdef P3TURTLE_DEBUG + std::cerr << "p3turtle: sending chunk map to peer " << peerId << ", hash=0x" << hash << ") through tunnel " << (void*)item->tunnel_id << ", next peer=" << item->PeerId() << std::endl ; +#endif + sendItem(item) ; +} + // This function is actually not needed: Search request to the turtle router are: // - distant search requests, handled by the router // - search requests over files being downloaded, handled by rsFiles !! @@ -1573,7 +1686,7 @@ static std::string printNumber(uint64_t num,bool hex=false) if(hex) { char tmp[100] ; - sprintf(tmp,"0x%08x%08x", uint32_t(num >> 32),uint32_t(num & ( (1<<32)-1 ))) ; + sprintf(tmp,"%08x%08x", uint32_t(num >> 32),uint32_t(num & ( (1<<32)-1 ))) ; return std::string(tmp) ; } else diff --git a/libretroshare/src/turtle/p3turtle.h b/libretroshare/src/turtle/p3turtle.h index b8cd22c5c..5c2d9f452 100644 --- a/libretroshare/src/turtle/p3turtle.h +++ b/libretroshare/src/turtle/p3turtle.h @@ -280,6 +280,13 @@ class p3turtle: public p3Service, public pqiMonitor, public RsTurtle,/* public f /// Send file data into the correct tunnel for the given file hash void sendFileData(const std::string& peerId, const std::string& hash, uint64_t size, uint64_t baseoffset, uint32_t chunksize, void *data) ; + + /// Send a request for the chunk map of this file to the given peer + void sendChunkMapRequest(const std::string& peerId, const std::string& hash) ; + + /// Send a chunk map of this file to the given peer + void sendChunkMap(const std::string& peerId, const std::string& hash,const CompressedChunkMap& cmap) ; + private: //--------------------------- Admin/Helper functions -------------------------// @@ -321,6 +328,7 @@ class p3turtle: public p3Service, public pqiMonitor, public RsTurtle,/* public f void handleTunnelResult(RsTurtleTunnelOkItem *item); void handleRecvFileRequest(RsTurtleFileRequestItem *item); void handleRecvFileData(RsTurtleFileDataItem *item); + void handleRecvFileMapRequest(RsTurtleFileMapRequestItem*); void handleRecvFileMap(RsTurtleFileMapItem*); //------ Functions connecting the turtle router to other components.----------// diff --git a/libretroshare/src/turtle/rsturtleitem.cc b/libretroshare/src/turtle/rsturtleitem.cc index 37728f58b..bd0a7a518 100644 --- a/libretroshare/src/turtle/rsturtleitem.cc +++ b/libretroshare/src/turtle/rsturtleitem.cc @@ -114,17 +114,27 @@ uint32_t RsTurtleFileDataItem::serial_size() return s ; } +uint32_t RsTurtleFileMapRequestItem::serial_size() +{ + uint32_t s = 0 ; + + s += 8 ; // header + s += 4 ; // tunnel id + s += 4 ; // direction + + return s ; +} + uint32_t RsTurtleFileMapItem::serial_size() { uint32_t s = 0 ; s += 8 ; // header s += 4 ; // tunnel id - s += 4 ; // chunk_size - s += 4 ; // nb_chunks + s += 4 ; // direction s += 4 ; // compressed_map.size() - s += 4 * compressed_map.size() ; + s += 4 * compressed_map._map.size() ; return s ; } @@ -162,6 +172,7 @@ RsItem *RsTurtleSerialiser::deserialise(void *data, uint32_t *size) case RS_TURTLE_SUBTYPE_TUNNEL_OK : return new RsTurtleTunnelOkItem(data,*size) ; case RS_TURTLE_SUBTYPE_FILE_REQUEST : return new RsTurtleFileRequestItem(data,*size) ; case RS_TURTLE_SUBTYPE_FILE_DATA : return new RsTurtleFileDataItem(data,*size) ; + case RS_TURTLE_SUBTYPE_FILE_MAP_REQUEST : return new RsTurtleFileMapRequestItem(data,*size) ; case RS_TURTLE_SUBTYPE_FILE_MAP : return new RsTurtleFileMapItem(data,*size) ; default: @@ -179,6 +190,39 @@ RsItem *RsTurtleSerialiser::deserialise(void *data, uint32_t *size) } +bool RsTurtleFileMapRequestItem::serialize(void *data,uint32_t& pktsize) +{ + uint32_t tlvsize = serial_size(); + uint32_t offset = 0; + + if (pktsize < tlvsize) + return false; /* not enough space */ + + pktsize = tlvsize; + + bool ok = true; + + ok &= setRsItemHeader(data,tlvsize,PacketId(), tlvsize); + + /* skip the header */ + offset += 8; + + /* add mandatory parts first */ + + ok &= setRawUInt32(data, tlvsize, &offset, tunnel_id); + ok &= setRawUInt32(data, tlvsize, &offset, direction); + + if (offset != tlvsize) + { + ok = false; +#ifdef RSSERIAL_DEBUG + std::cerr << "RsFileConfigSerialiser::serialiseTransfer() Size Error! " << std::endl; +#endif + } + + return ok; +} + bool RsTurtleFileMapItem::serialize(void *data,uint32_t& pktsize) { uint32_t tlvsize = serial_size(); @@ -199,12 +243,11 @@ bool RsTurtleFileMapItem::serialize(void *data,uint32_t& pktsize) /* add mandatory parts first */ ok &= setRawUInt32(data, tlvsize, &offset, tunnel_id); - ok &= setRawUInt32(data, tlvsize, &offset, chunk_size); - ok &= setRawUInt32(data, tlvsize, &offset, nb_chunks); - ok &= setRawUInt32(data, tlvsize, &offset, compressed_map.size()); + ok &= setRawUInt32(data, tlvsize, &offset, direction); + ok &= setRawUInt32(data, tlvsize, &offset, compressed_map._map.size()); - for(uint32_t i=0;i compressed_map ; // Map info for the file in compressed format. Each *bit* in the array uint's says "I have" or "I don't have" + CompressedChunkMap compressed_map ; // Map info for the file in compressed format. Each *bit* in the array uint's says "I have" or "I don't have" // by default, we suppose the peer has all the chunks. This info will thus be and-ed // with the default file map for this source. diff --git a/retroshare-gui/src/gui/DLListDelegate.cpp b/retroshare-gui/src/gui/DLListDelegate.cpp index 69fc7eb38..06ae41c0e 100644 --- a/retroshare-gui/src/gui/DLListDelegate.cpp +++ b/retroshare-gui/src/gui/DLListDelegate.cpp @@ -19,6 +19,7 @@ * Boston, MA 02110-1301, USA. ****************************************************************/ +#include #include #include #include @@ -27,6 +28,8 @@ #include "DLListDelegate.h" +Q_DECLARE_METATYPE(FileProgressInfo) + DLListDelegate::DLListDelegate(QObject *parent) : QAbstractItemDelegate(parent) { ; @@ -144,14 +147,14 @@ void DLListDelegate::paint(QPainter * painter, const QStyleOptionViewItem & opti painter->drawText(option.rect, Qt::AlignRight, temp); break; case PROGRESS: - { - progress = index.data().toDouble(); - // create a xProgressBar - xProgressBar progressBar(option.rect, painter); // the 3rd param is the color schema (0 is the default value) - progressBar.setDisplayText(false); // should display % text? - progressBar.setValue(progress); // set the progress value - progressBar.setVerticalSpan(1); - progressBar.paint(); // paint the progress bar + { + // create a xProgressBar + FileProgressInfo pinfo = index.data().value() ; + xProgressBar progressBar(pinfo.cmap,option.rect, painter); // the 3rd param is the color schema (0 is the default value) + progressBar.setDisplayText(false); // should display % text? + progressBar.setValue(pinfo.progress); // set the progress value + progressBar.setVerticalSpan(1); + progressBar.paint(); // paint the progress bar } painter->drawText(option.rect, Qt::AlignCenter, newopt.text); break; diff --git a/retroshare-gui/src/gui/FileTransferInfoWidget.cpp b/retroshare-gui/src/gui/FileTransferInfoWidget.cpp index 4350d11fc..3ac252c8b 100644 --- a/retroshare-gui/src/gui/FileTransferInfoWidget.cpp +++ b/retroshare-gui/src/gui/FileTransferInfoWidget.cpp @@ -62,23 +62,16 @@ void FileTransferInfoWidget::updateDisplay() { std::cout << "In TaskGraphPainterWidget::updateDisplay()" << std::endl ; + bool ok=true ; FileInfo nfo ; if(!rsFiles->FileDetails(_file_hash, RS_FILE_HINTS_DOWNLOAD, nfo)) - return ; + ok = false ; FileChunksInfo info ; - if(!rsFiles->FileChunksDetails(_file_hash, info)) - return ; + if(!rsFiles->FileDownloadChunksDetails(_file_hash, info)) + ok = false ; std::cout << "got details for file " << nfo.fname << std::endl ; - uint64_t fileSize = info.file_size; - uint32_t blockSize = info.chunk_size ; - int blocks = info.chunks.size() ; - - int columns = maxWidth/chunk_square_size; - y = blocks/columns*chunk_square_size; - x = blocks%columns*chunk_square_size; - maxHeight = y+150+info.active_chunks.size()*(block_sep+text_height); // warning: this should be computed from the different size parameter and the number of objects drawn, otherwise the last objects to be displayed will be truncated. pixmap = QPixmap(size()); pixmap.fill(this, 0, 0); pixmap = QPixmap(maxWidth, maxHeight); @@ -88,7 +81,16 @@ void FileTransferInfoWidget::updateDisplay() QPainter painter(&pixmap); painter.initFrom(this); - draw(info,&painter) ; + if(ok) + { + int blocks = info.chunks.size() ; + int columns = maxWidth/chunk_square_size; + y = blocks/columns*chunk_square_size; + x = blocks%columns*chunk_square_size; + maxHeight = y+150+info.active_chunks.size()*(block_sep+text_height); // warning: this should be computed from the different size parameter and the number of objects drawn, otherwise the last objects to be displayed will be truncated. + + draw(info,&painter) ; + } pixmap2 = pixmap; } @@ -194,8 +196,8 @@ void FileTransferInfoWidget::draw(const FileChunksInfo& info,QPainter *painter) int nb_src = 0 ; int chunk_num = (int)floor(i/float(availability_map_size_X)*(nb_chunks-1)) ; - for(uint j=0;j::const_iterator it(info.compressed_peer_availability_maps.begin());it!=info.compressed_peer_availability_maps.end();++it) + nb_src += it->second[chunk_num] ; painter->setPen(QColor::fromHsv(200,50*nb_src,200)) ; // the more sources, the more saturated painter->drawLine(i,y,i,y+availability_map_size_Y) ; diff --git a/retroshare-gui/src/gui/TransfersDialog.cpp b/retroshare-gui/src/gui/TransfersDialog.cpp index 03b7b2f06..eec62d79e 100644 --- a/retroshare-gui/src/gui/TransfersDialog.cpp +++ b/retroshare-gui/src/gui/TransfersDialog.cpp @@ -31,6 +31,7 @@ #include "DLListDelegate.h" #include "ULListDelegate.h" #include "FileTransferInfoWidget.h" +#include "xprogressbar.h" #include #include @@ -69,6 +70,34 @@ #define IMAGE_PRIORITYHIGH ":/images/priorityhigh.png" #define IMAGE_PRIORITYAUTO ":/images/priorityauto.png" +//static const CompressedChunkMap *getMap_tmp() +//{ +// static CompressedChunkMap *cmap = NULL ; +// +// if(cmap == NULL) +// { +// cmap = new CompressedChunkMap ; // to be passed as a parameter +// // Initialize the chunkmap with a dummy value, for testing. To be removed... +// cmap->_nb_chunks = 700 ; +// cmap->_map.resize(cmap->_nb_chunks/32+1,0) ; +// cmap->_progress = 0.34 ; +// for(uint i=0;i<10;++i) +// { +// uint32_t start = rand()%cmap->_nb_chunks; +// uint32_t j = std::min((int)cmap->_nb_chunks,(int)start+10+(rand()%5)) ; +// +// for(uint32_t k=start;k_map,k,1) ; +// } +// +// std::cerr << "Built cmap = " << (void*)cmap << std::endl ; +// } +// return cmap ; +//} + + +Q_DECLARE_METATYPE(FileProgressInfo) + /** Constructor */ TransfersDialog::TransfersDialog(QWidget *parent) @@ -378,11 +407,11 @@ void TransfersDialog::playSelectedTransfer() void TransfersDialog::updateProgress(int value) { - for(int i = 0; i <= DLListModel->rowCount(); i++) { - if(selection->isRowSelected(i, QModelIndex())) { - editItem(i, PROGRESS, QVariant((double)value)); - } - } +// for(int i = 0; i <= DLListModel->rowCount(); i++) { +// if(selection->isRowSelected(i, QModelIndex())) { +// editItem(i, PROGRESS, QVariant((double)value)); +// } +// } } TransfersDialog::~TransfersDialog() @@ -391,8 +420,7 @@ TransfersDialog::~TransfersDialog() } - -int TransfersDialog::addItem(QString symbol, QString name, QString coreID, qlonglong fileSize, double progress, double dlspeed, +int TransfersDialog::addItem(QString symbol, QString name, QString coreID, qlonglong fileSize, const FileProgressInfo& pinfo, double dlspeed, QString sources, QString status, QString priority, qlonglong completed, qlonglong remaining) { int row; @@ -408,7 +436,7 @@ int TransfersDialog::addItem(QString symbol, QString name, QString coreID, qlong DLListModel->setData(DLListModel->index(row, SIZE), QVariant((qlonglong)fileSize)); DLListModel->setData(DLListModel->index(row, COMPLETED), QVariant((qlonglong)completed)); DLListModel->setData(DLListModel->index(row, DLSPEED), QVariant((double)dlspeed)); - DLListModel->setData(DLListModel->index(row, PROGRESS), QVariant((double)progress)); + DLListModel->setData(DLListModel->index(row, PROGRESS), QVariant::fromValue(pinfo)); DLListModel->setData(DLListModel->index(row, SOURCES), QVariant((QString)sources)); DLListModel->setData(DLListModel->index(row, STATUS), QVariant((QString)status)); DLListModel->setData(DLListModel->index(row, PRIORITY), QVariant((QString)priority)); @@ -469,7 +497,7 @@ int TransfersDialog::addItem(QString symbol, QString name, QString coreID, qlong return row; } -bool TransfersDialog::addPeerToItem(int row, QString symbol, QString name, QString coreID, qlonglong fileSize, double progress, double dlspeed, QString sources, QString status, qlonglong completed, qlonglong remaining) +bool TransfersDialog::addPeerToItem(int row, QString symbol, QString name, QString coreID, qlonglong fileSize, const FileProgressInfo& pinfo, double dlspeed, QString sources, QString status, qlonglong completed, qlonglong remaining) { QStandardItem *dlItem = DLListModel->item(row); if (!dlItem) return false; @@ -484,7 +512,7 @@ bool TransfersDialog::addPeerToItem(int row, QString symbol, QString name, QStri QStandardItem *i2 = new QStandardItem(); i2->setData(QVariant((qlonglong)fileSize), Qt::DisplayRole); QStandardItem *i3 = new QStandardItem(); i3->setData(QVariant((qlonglong)completed), Qt::DisplayRole); QStandardItem *i4 = new QStandardItem(); i4->setData(QVariant((double)dlspeed), Qt::DisplayRole); - QStandardItem *i5 = new QStandardItem(); i5->setData(QVariant((double)progress), Qt::DisplayRole); + QStandardItem *i5 = new QStandardItem(); i5->setData(QVariant::fromValue(pinfo), Qt::DisplayRole); QStandardItem *i6 = new QStandardItem(); i6->setData(QVariant((QString)sources), Qt::DisplayRole); QStandardItem *i7 = new QStandardItem(); i7->setData(QVariant((QString)status), Qt::DisplayRole); QStandardItem *i8 = new QStandardItem(); i8->setData(QVariant((QString)tr("")), Qt::DisplayRole); // blank field for priority @@ -522,7 +550,7 @@ bool TransfersDialog::addPeerToItem(int row, QString symbol, QString name, QStri } -int TransfersDialog::addUploadItem(QString symbol, QString name, QString coreID, qlonglong fileSize, double progress, double dlspeed, QString sources, QString status, qlonglong completed, qlonglong remaining) +int TransfersDialog::addUploadItem(QString symbol, QString name, QString coreID, qlonglong fileSize, const FileProgressInfo& pinfo, double dlspeed, QString source, QString status, qlonglong completed, qlonglong remaining) { int row; QString sl; @@ -535,9 +563,9 @@ int TransfersDialog::addUploadItem(QString symbol, QString name, QString coreID, ULListModel->setData(ULListModel->index(row, USIZE), QVariant((qlonglong)fileSize)); ULListModel->setData(ULListModel->index(row, UTRANSFERRED), QVariant((qlonglong)completed)); ULListModel->setData(ULListModel->index(row, ULSPEED), QVariant((double)dlspeed)); - ULListModel->setData(ULListModel->index(row, UPROGRESS), QVariant((double)progress)); + ULListModel->setData(ULListModel->index(row, UPROGRESS), QVariant::fromValue(pinfo)); ULListModel->setData(ULListModel->index(row, USTATUS), QVariant((QString)status)); - ULListModel->setData(ULListModel->index(row, USERNAME), QVariant((QString)sources)); + ULListModel->setData(ULListModel->index(row, USERNAME), QVariant((QString)source)); return row; } @@ -568,9 +596,9 @@ void TransfersDialog::editItem(int row, int column, QVariant data) case SIZE: DLListModel->setData(DLListModel->index(row, SIZE), data); break; - case PROGRESS: - DLListModel->setData(DLListModel->index(row, PROGRESS), data); - break; +// case PROGRESS: +// DLListModel->setData(DLListModel->index(row, PROGRESS), data); +// break; case DLSPEED: DLListModel->setData(DLListModel->index(row, DLSPEED), data); break; @@ -670,7 +698,8 @@ void TransfersDialog::insertTransfers() uint32_t ulCount = 0; std::list::iterator it; - for (it = downHashes.begin(); it != downHashes.end(); it++) { + for (it = downHashes.begin(); it != downHashes.end(); it++) + { FileInfo info; if (!rsFiles->FileDetails(*it, RS_FILE_HINTS_DOWNLOAD, info)) continue; //if file transfer is a cache file index file, don't show it @@ -713,7 +742,21 @@ void TransfersDialog::insertTransfers() completed = info.transfered; remaining = (info.size - info.transfered) / (info.tfRate * 1024.0); - int addedRow = addItem(symbol, name, coreId, fileSize, progress, dlspeed, sources, status, priority, completed, remaining); + FileChunksInfo fcinfo ; + if(!rsFiles->FileDownloadChunksDetails(*it,fcinfo)) + continue ; + + FileProgressInfo pinfo ; + pinfo.cmap = fcinfo.chunks ; + pinfo.progress = completed*100.0/info.size ; + +// std::cerr << "Converting fcinfo to compressed chunk map. Chunks=" << fcinfo.chunks.size() << std::endl ; +// std::cerr << "map data = " ; +// for(uint k=0;k select again */ if (selectedIds.end() != std::find(selectedIds.begin(), selectedIds.end(), info.hash)) { @@ -777,7 +820,11 @@ void TransfersDialog::insertTransfers() completed = info.transfered; remaining = (info.size - info.transfered) / (pit->tfRate * 1024.0); - if (!addPeerToItem(addedRow, symbol, name, coreId, fileSize, progress, dlspeed, sources, status, completed, remaining)) + FileProgressInfo pinfo ; + pinfo.cmap = fcinfo.compressed_peer_availability_maps[pit->peerId] ; + pinfo.progress = 0.0 ; // we don't display completion for sources. + + if (!addPeerToItem(addedRow, symbol, name, coreId, fileSize, pinfo, dlspeed, sources, status, completed, remaining)) continue; /* if peers found in selectedIds, select again */ @@ -826,7 +873,10 @@ void TransfersDialog::insertTransfers() break; } - addItem("", name, coreId, fileSize, progress, dlspeed, sources, status, priority, completed, remaining); + FileProgressInfo pinfo ; + pinfo.progress = 0.0 ; + + addItem("", name, coreId, fileSize, pinfo, dlspeed, sources, status, priority, completed, remaining); /* if found in selectedIds -> select again */ if (selectedIds.end() != std::find(selectedIds.begin(), selectedIds.end(), dit->hash)) { @@ -890,14 +940,36 @@ void TransfersDialog::insertTransfers() // status = "Complete"; // } + FileProgressInfo pinfo ; + + if(!rsFiles->FileUploadChunksDetails(*it,pit->peerId,pinfo.cmap) ) + continue ; + dlspeed = pit->tfRate * 1024.0; fileSize = info.size; completed = info.transfered; progress = info.transfered * 100.0 / info.size; remaining = (info.size - info.transfered) / (info.tfRate * 1024.0); - addUploadItem(symbol, name, coreId, fileSize, progress, - dlspeed, sources, status, completed, remaining); + // Estimate the completion. We need something more accurate, meaning that we need to + // transmit the completion info. + // + uint32_t chunk_size = 1024*1024 ; + uint32_t nb_chunks = (uint32_t)(info.size / (uint64_t)(chunk_size) ) ; + if((info.size % (uint64_t)chunk_size) != 0) + ++nb_chunks ; + + uint32_t filled_chunks = pinfo.cmap.filledChunks(nb_chunks) ; + + if(filled_chunks > 1) + { + pinfo.progress = filled_chunks*100.0/nb_chunks ; + completed = std::min(info.size,((uint64_t)filled_chunks)*chunk_size) ; + } + else + pinfo.progress = progress ; + + addUploadItem(symbol, name, coreId, fileSize, pinfo, dlspeed, sources, status, completed, remaining); ulCount++; } @@ -935,8 +1007,11 @@ void TransfersDialog::insertTransfers() progress = info.transfered * 100.0 / info.size; remaining = (info.size - info.transfered) / (info.tfRate * 1024.0); - addUploadItem(symbol, name, coreId, fileSize, progress, - dlspeed, sources, status, completed, remaining); + FileProgressInfo pinfo ; + pinfo.progress = progress ; + pinfo.cmap = CompressedChunkMap() ; + + addUploadItem(symbol, name, coreId, fileSize, pinfo, dlspeed, sources, status, completed, remaining); ulCount++; } } @@ -1411,6 +1486,8 @@ void TransfersDialog::showFileDetails() std::string file_hash ; int nb_select = 0 ; + std::cout << "new selection " << std::endl ; + for(int i = 0; i <= DLListModel->rowCount(); i++) if(selection->isRowSelected(i, QModelIndex())) { @@ -1418,15 +1495,19 @@ void TransfersDialog::showFileDetails() ++nb_select ; } if(nb_select != 1) - return ; + dynamic_cast(ui.fileTransferInfoWidget->widget())->setFileHash("") ; + else + dynamic_cast(ui.fileTransferInfoWidget->widget())->setFileHash(file_hash) ; - dynamic_cast(ui.fileTransferInfoWidget->widget())->setFileHash(file_hash) ; + std::cout << "calling update " << std::endl ; dynamic_cast(ui.fileTransferInfoWidget->widget())->updateDisplay() ; + std::cout << "done" << std::endl ; } double TransfersDialog::getProgress(int row, QStandardItemModel *model) { - return model->data(model->index(row, PROGRESS), Qt::DisplayRole).toDouble(); +// return model->data(model->index(row, PROGRESS), Qt::DisplayRole).toDouble(); +return 0.0 ; } double TransfersDialog::getSpeed(int row, QStandardItemModel *model) diff --git a/retroshare-gui/src/gui/TransfersDialog.h b/retroshare-gui/src/gui/TransfersDialog.h index c1fb75e44..ebf5d77b2 100644 --- a/retroshare-gui/src/gui/TransfersDialog.h +++ b/retroshare-gui/src/gui/TransfersDialog.h @@ -33,6 +33,7 @@ #include #include "mainpage.h" #include "RsAutoUpdatePage.h" +#include "xprogressbar.h" #include "ui_TransfersDialog.h" @@ -165,11 +166,11 @@ class TransfersDialog : public RsAutoUpdatePage Ui::TransfersDialog ui; public slots: - int addItem(QString symbol, QString name, QString coreID, qlonglong size, double progress, double dlspeed, QString sources, QString status, QString priority, qlonglong completed, qlonglong remaining); - bool addPeerToItem(int row, QString symbol, QString name, QString coreID, qlonglong fileSize, double progress, double dlspeed, QString sources, QString status, qlonglong completed, qlonglong remaining); + int addItem(QString symbol, QString name, QString coreID, qlonglong size, const FileProgressInfo& pinfo, double dlspeed, QString sources, QString status, QString priority, qlonglong completed, qlonglong remaining); + bool addPeerToItem(int row, QString symbol, QString name, QString coreID, qlonglong fileSize, const FileProgressInfo& pinfo, double dlspeed, QString sources, QString status, qlonglong completed, qlonglong remaining); void delItem(int row); - int addUploadItem(QString symbol, QString name, QString coreID, qlonglong size, double progress, double dlspeed, QString sources, QString status, qlonglong completed, qlonglong remaining); + int addUploadItem(QString symbol, QString name, QString coreID, qlonglong size, const FileProgressInfo& pinfo, double dlspeed, QString sources, QString status, qlonglong completed, qlonglong remaining); void delUploadItem(int row); void editItem(int row, int column, QVariant data); diff --git a/retroshare-gui/src/gui/ULListDelegate.cpp b/retroshare-gui/src/gui/ULListDelegate.cpp index b95e72e92..79789d7e0 100644 --- a/retroshare-gui/src/gui/ULListDelegate.cpp +++ b/retroshare-gui/src/gui/ULListDelegate.cpp @@ -19,6 +19,7 @@ * Boston, MA 02110-1301, USA. ****************************************************************/ +#include #include #include #include @@ -28,6 +29,8 @@ #include "ULListDelegate.h" +Q_DECLARE_METATYPE(FileProgressInfo) + ULListDelegate::ULListDelegate(QObject *parent) : QAbstractItemDelegate(parent) { ; @@ -123,25 +126,22 @@ void ULListDelegate::paint(QPainter * painter, const QStyleOptionViewItem & opti painter->drawText(option.rect, Qt::AlignRight, temp); break; case UPROGRESS: - { - progress = index.data().toDouble(); - // create a xProgressBar - xProgressBar progressBar(option.rect, painter );// the 3rd param is the color schema (0 is the default value) - - QString ext = QFileInfo(QString::fromStdString(index.sibling(index.row(), UNAME).data().toString().toStdString())).suffix();; - if (ext == "rsfc" || ext == "rsrl" || ext == "dist" || ext == "rsfb") - { - progressBar.setColorSchema( 9); - } - else - { - progressBar.setColorSchema( 8); - } - - progressBar.setDisplayText(false); // should display % text? - progressBar.setValue(progress); // set the progress value - progressBar.setVerticalSpan(1); - progressBar.paint(); // paint the progress bar + { + FileProgressInfo pinfo = index.data().value() ; + + // create a xProgressBar + xProgressBar progressBar(pinfo.cmap,option.rect,painter,0);// the 3rd param is the color schema (0 is the default value) + + QString ext = QFileInfo(QString::fromStdString(index.sibling(index.row(), UNAME).data().toString().toStdString())).suffix();; + if (ext == "rsfc" || ext == "rsrl" || ext == "dist" || ext == "rsfb") + progressBar.setColorSchema( 9); + else + progressBar.setColorSchema( 8); + + progressBar.setDisplayText(true); // should display % text? + progressBar.setValue(pinfo.progress); // set the progress value + progressBar.setVerticalSpan(1); + progressBar.paint(); // paint the progress bar } painter->drawText(option.rect, Qt::AlignCenter, newopt.text); break; diff --git a/retroshare-gui/src/gui/xprogressbar.cpp b/retroshare-gui/src/gui/xprogressbar.cpp index 0bb1bddb6..ecba3e2b3 100644 --- a/retroshare-gui/src/gui/xprogressbar.cpp +++ b/retroshare-gui/src/gui/xprogressbar.cpp @@ -24,9 +24,12 @@ * Boston, MA 02110-1301, USA. ****************************************************************/ +#include +#include #include "xprogressbar.h" -xProgressBar::xProgressBar(QRect rect, QPainter *painter, int schemaIndex) +xProgressBar::xProgressBar(const CompressedChunkMap& cmap,QRect rect, QPainter *painter, int schemaIndex) + : _cmap(cmap) { // assign internal data this->schemaIndex = schemaIndex; @@ -39,7 +42,7 @@ xProgressBar::xProgressBar(QRect rect, QPainter *painter, int schemaIndex) vSpan = 0; hSpan = 0; // text color - textColor = QColor("white"); + textColor = QColor("black"); } void xProgressBar::setColor() @@ -187,14 +190,41 @@ void xProgressBar::paint() linearGrad.setColorAt(1.00, gradColor1); painter->setPen(gradBorderColor); - // calculate progress value - int preWidth = static_cast((rect.width() - 1 - hSpan)*(progressValue/100)); - int progressWidth = rect.width() - preWidth; - if (progressWidth == rect.width() - hSpan) return; + int width = static_cast(rect.width()-1-2*hSpan) ; - // paint the progress painter->setBrush(linearGrad); - painter->drawRect(rect.x() + hSpan, rect.y() + vSpan, rect.width() - progressWidth - hSpan, rect.height() - 1 - vSpan * 2); + + uint32_t ss = _cmap._map.size()*32 ; + + if(ss > 1) // for small files we use a more progressive display + for(int i=0;i0) + { + float o = std::min(1.0f,j/(float)ss*width) ; + painter->setOpacity(o) ; + painter->drawRect(rect.x() + hSpan+(int)rint(i*width/(float)ss), rect.y() + vSpan, (int)ceil(j*width/(float)ss), rect.height() - 1 - vSpan * 2); + } + + i += j ; + } + else + { + // calculate progress value + int preWidth = static_cast((rect.width() - 1 - hSpan)*(progressValue/100)); + int progressWidth = rect.width() - preWidth; + if (progressWidth == rect.width() - hSpan) return; + + // paint the progress + painter->setBrush(linearGrad); + painter->drawRect(rect.x() + hSpan, rect.y() + vSpan, rect.width() - progressWidth - hSpan, rect.height() - 1 - vSpan * 2); + } + painter->setOpacity(1.0f) ; + // paint text? if (displayText) diff --git a/retroshare-gui/src/gui/xprogressbar.h b/retroshare-gui/src/gui/xprogressbar.h index 4d895e170..ade488135 100644 --- a/retroshare-gui/src/gui/xprogressbar.h +++ b/retroshare-gui/src/gui/xprogressbar.h @@ -27,11 +27,21 @@ #ifndef XPROGRESSBAR_H #define XPROGRESSBAR_H // +#include #include #include #include #include #include + +#include + +class FileProgressInfo +{ + public: + CompressedChunkMap cmap ; + float progress ; +}; // class xProgressBar : public QObject { @@ -56,8 +66,11 @@ Q_OBJECT QColor gradColor2; // configure the color void setColor(); + + const CompressedChunkMap& _cmap ; public: - xProgressBar(QRect rect, QPainter *painter, int schemaIndex = 0); + xProgressBar(const CompressedChunkMap& cmap,QRect rect, QPainter *painter, int schemaIndex = 0); + void paint(); void setColorSchema(const int value); void setValue(const float value);