| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449 | /* <!-- copyright *//* * aria2 - The high speed download utility * * Copyright (C) 2006 Tatsuhiro Tsujikawa * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the * OpenSSL library under certain conditions as described in each * individual source file, and distribute linked combinations * including the two. * You must obey the GNU General Public License in all respects * for all of the code used other than OpenSSL.  If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so.  If you * do not wish to do so, delete this exception statement from your * version.  If you delete this exception statement from all source * files in the program, then also delete it here. *//* copyright --> */#include "MultiDiskAdaptor.h"#include "DefaultDiskWriter.h"#include "message.h"#include "Util.h"#include "FileEntry.h"#include "MultiFileAllocationIterator.h"#include "DefaultDiskWriterFactory.h"#include "DlAbortEx.h"#include "File.h"#include "StringFormat.h"#include "Logger.h"#include "SimpleRandomizer.h"#include <algorithm>namespace aria2 {DiskWriterEntry::DiskWriterEntry(const SharedHandle<FileEntry>& fileEntry):  fileEntry(fileEntry), _open(false), _directIO(false) {}DiskWriterEntry::~DiskWriterEntry() {}std::string DiskWriterEntry::getFilePath(const std::string& topDir) const{  return topDir+"/"+fileEntry->getPath();}void DiskWriterEntry::initAndOpenFile(const std::string& topDir){  diskWriter->initAndOpenFile(getFilePath(topDir), fileEntry->getLength());  if(_directIO) {    diskWriter->enableDirectIO();  }  _open = true;}void DiskWriterEntry::openFile(const std::string& topDir){  diskWriter->openFile(getFilePath(topDir), fileEntry->getLength());  if(_directIO) {    diskWriter->enableDirectIO();  }  _open = true;}void DiskWriterEntry::openExistingFile(const std::string& topDir){  diskWriter->openExistingFile(getFilePath(topDir), fileEntry->getLength());  if(_directIO) {    diskWriter->enableDirectIO();  }  _open = true;}bool DiskWriterEntry::isOpen() const{  return _open;}void DiskWriterEntry::closeFile(){  if(_open) {    diskWriter->closeFile();    _open = false;  }}bool DiskWriterEntry::fileExists(const std::string& topDir){  return File(getFilePath(topDir)).exists();}uint64_t DiskWriterEntry::size() const{  return diskWriter->size();}SharedHandle<FileEntry> DiskWriterEntry::getFileEntry() const{  return fileEntry;}void DiskWriterEntry::setDiskWriter(const SharedHandle<DiskWriter>& diskWriter){  this->diskWriter = diskWriter;}SharedHandle<DiskWriter> DiskWriterEntry::getDiskWriter() const{  return diskWriter;}bool DiskWriterEntry::operator<(const DiskWriterEntry& entry) const{  return fileEntry < entry.fileEntry;}void DiskWriterEntry::enableDirectIO(){  if(_open) {    diskWriter->enableDirectIO();  }  _directIO = true;}void DiskWriterEntry::disableDirectIO(){  if(_open) {    diskWriter->disableDirectIO();  }  _directIO = false;}MultiDiskAdaptor::MultiDiskAdaptor():  pieceLength(0), _maxOpenFiles(DEFAULT_MAX_OPEN_FILES) {}MultiDiskAdaptor::~MultiDiskAdaptor() {}static SharedHandle<DiskWriterEntry> createDiskWriterEntry(const SharedHandle<FileEntry>& fileEntry, DiskWriterFactory& dwFactory, bool directIOAllowed){  SharedHandle<DiskWriterEntry> entry(new DiskWriterEntry(fileEntry));  entry->setDiskWriter(dwFactory.newDiskWriter());  entry->getDiskWriter()->setDirectIOAllowed(directIOAllowed);    return entry;} void MultiDiskAdaptor::resetDiskWriterEntries(){  diskWriterEntries.clear();  if(fileEntries.empty()) {    return;  }  DefaultDiskWriterFactory dwFactory;  if(pieceLength == 0) {    for(std::deque<SharedHandle<FileEntry> >::const_iterator itr =	  fileEntries.begin(); itr != fileEntries.end(); ++itr) {      if((*itr)->isRequested()) {	diskWriterEntries.push_back	  (createDiskWriterEntry(*itr, dwFactory, _directIOAllowed));      }    }  } else {    std::deque<SharedHandle<FileEntry> >::const_iterator done = fileEntries.begin();    for(std::deque<SharedHandle<FileEntry> >::const_iterator itr =	  fileEntries.begin(); itr != fileEntries.end();) {      if(!(*itr)->isRequested()) {	++itr;	continue;      }      off_t pieceStartOffset = ((*itr)->getOffset()/pieceLength)*pieceLength;      std::deque<SharedHandle<DiskWriterEntry> >::iterator insertionPoint =	diskWriterEntries.end();      if(itr != fileEntries.begin()) {	for(std::deque<SharedHandle<FileEntry> >::const_iterator i = itr-1;	    i != done; --i) {	  if((uint64_t)pieceStartOffset < (*i)->getOffset()+(*i)->getLength()) {	    insertionPoint = diskWriterEntries.insert	      (insertionPoint,	       createDiskWriterEntry(*i, dwFactory, _directIOAllowed));	  } else {	    break;	  }	}      }      diskWriterEntries.push_back	(createDiskWriterEntry(*itr, dwFactory, _directIOAllowed));      ++itr;      for(; itr != fileEntries.end(); ++itr) {	if((*itr)->getOffset() < pieceStartOffset+pieceLength) {	  diskWriterEntries.push_back	    (createDiskWriterEntry(*itr, dwFactory, _directIOAllowed));	} else {	  break;	}      }      done = itr-1;    }  }}std::string MultiDiskAdaptor::getTopDirPath() const{  return storeDir+"/"+topDir;}void MultiDiskAdaptor::mkdir(const std::string& topDirPath) const{  for(std::deque<SharedHandle<DiskWriterEntry> >::const_iterator i =	diskWriterEntries.begin(); i != diskWriterEntries.end(); ++i) {    (*i)->getFileEntry()->setupDir(topDirPath);  }}void MultiDiskAdaptor::openIfNot(const SharedHandle<DiskWriterEntry>& entry, void (DiskWriterEntry::*open)(const std::string&), const std::string& topDirPath){  if(!entry->isOpen()) {//     logger->debug("DiskWriterEntry: Cache MISS. offset=%s",// 		  Util::itos(entry->getFileEntry()->getOffset()).c_str());     size_t numOpened = _openedDiskWriterEntries.size();    (entry.get()->*open)(topDirPath);    if(numOpened >= _maxOpenFiles) {      // Cache is full.       // Choose one DiskWriterEntry randomly and close it.      size_t index = SimpleRandomizer::getInstance()->getRandomNumber(numOpened);      std::deque<SharedHandle<DiskWriterEntry> >::iterator i =	_openedDiskWriterEntries.begin();      std::advance(i, index);      (*i)->closeFile();      (*i) = entry;    } else {      _openedDiskWriterEntries.push_back(entry);    }   } else {//     logger->debug("DiskWriterEntry: Cache HIT. offset=%s",// 		  Util::itos(entry->getFileEntry()->getOffset()).c_str());  }}void MultiDiskAdaptor::openFile(){  _cachedTopDirPath = getTopDirPath();  resetDiskWriterEntries();  mkdir(_cachedTopDirPath);  // TODO we should call openIfNot here?}void MultiDiskAdaptor::initAndOpenFile(){  _cachedTopDirPath = getTopDirPath();  resetDiskWriterEntries();  mkdir(_cachedTopDirPath);  // Call DiskWriterEntry::initAndOpenFile to make files truncated.  for(DiskWriterEntries::iterator itr = diskWriterEntries.begin();      itr != diskWriterEntries.end(); ++itr) {    openIfNot(*itr, &DiskWriterEntry::initAndOpenFile, _cachedTopDirPath);  }}void MultiDiskAdaptor::openExistingFile(){  _cachedTopDirPath = getTopDirPath();  resetDiskWriterEntries();  // Not need to call openIfNot here.}void MultiDiskAdaptor::closeFile(){  for(DiskWriterEntries::iterator itr = diskWriterEntries.begin();      itr != diskWriterEntries.end(); ++itr) {    (*itr)->closeFile();  }}void MultiDiskAdaptor::onDownloadComplete(){  closeFile();  openFile();}static bool isInRange(const DiskWriterEntryHandle entry, off_t offset){  return entry->getFileEntry()->getOffset() <= offset &&    (uint64_t)offset < entry->getFileEntry()->getOffset()+entry->getFileEntry()->getLength();}static size_t calculateLength(const DiskWriterEntryHandle entry,			      off_t fileOffset, size_t rem){  size_t length;  if(entry->getFileEntry()->getLength() < (uint64_t)fileOffset+rem) {    length = entry->getFileEntry()->getLength()-fileOffset;  } else {    length = rem;  }  return length;}class OffsetCompare {public:  bool operator()(off_t offset, const SharedHandle<DiskWriterEntry>& dwe)  {    return offset < dwe->getFileEntry()->getOffset();  }};static DiskWriterEntries::const_iteratorfindFirstDiskWriterEntry(const DiskWriterEntries& diskWriterEntries, off_t offset){  DiskWriterEntries::const_iterator first =    std::upper_bound(diskWriterEntries.begin(), diskWriterEntries.end(),		     offset, OffsetCompare());  --first;  // In case when offset is out-of-range  if(!isInRange(*first, offset)) {    throw DlAbortEx      (StringFormat(EX_FILE_OFFSET_OUT_OF_RANGE,		    Util::itos(offset, true).c_str()).str());  }  return first;}void MultiDiskAdaptor::writeData(const unsigned char* data, size_t len,				 off_t offset){  DiskWriterEntries::const_iterator first = findFirstDiskWriterEntry(diskWriterEntries, offset);  size_t rem = len;  off_t fileOffset = offset-(*first)->getFileEntry()->getOffset();  for(DiskWriterEntries::const_iterator i = first; i != diskWriterEntries.end(); ++i) {    size_t writeLength = calculateLength(*i, fileOffset, rem);    openIfNot(*i, &DiskWriterEntry::openFile, _cachedTopDirPath);    (*i)->getDiskWriter()->writeData(data+(len-rem), writeLength, fileOffset);    rem -= writeLength;    fileOffset = 0;    if(rem == 0) {      break;    }  }}ssize_t MultiDiskAdaptor::readData(unsigned char* data, size_t len, off_t offset){  DiskWriterEntries::const_iterator first = findFirstDiskWriterEntry(diskWriterEntries, offset);  size_t rem = len;  size_t totalReadLength = 0;  off_t fileOffset = offset-(*first)->getFileEntry()->getOffset();  for(DiskWriterEntries::const_iterator i = first; i != diskWriterEntries.end(); ++i) {    size_t readLength = calculateLength(*i, fileOffset, rem);    openIfNot(*i, &DiskWriterEntry::openFile, _cachedTopDirPath);    totalReadLength +=      (*i)->getDiskWriter()->readData(data+(len-rem), readLength, fileOffset);    rem -= readLength;    fileOffset = 0;    if(rem == 0) {      break;    }  }  return totalReadLength;}bool MultiDiskAdaptor::fileExists(){  // Don't use _cachedTopDirPath because they are initialized after opening files.  // This method could be called before opening files.  std::string topDirPath = getTopDirPath();  for(std::deque<SharedHandle<FileEntry> >::iterator i =	fileEntries.begin(); i != fileEntries.end(); ++i) {        if(File(topDirPath+"/"+(*i)->getPath()).isFile()) {      return true;    }  }  return false;}// TODO call DiskWriter::openFile() before calling this function.uint64_t MultiDiskAdaptor::size() const{  uint64_t size = 0;  for(DiskWriterEntries::const_iterator itr = diskWriterEntries.begin();      itr != diskWriterEntries.end(); ++itr) {    size += (*itr)->size();  }  return size;}FileAllocationIteratorHandle MultiDiskAdaptor::fileAllocationIterator(){  return SharedHandle<FileAllocationIterator>(new MultiFileAllocationIterator(this));}void MultiDiskAdaptor::enableDirectIO(){  for(DiskWriterEntries::const_iterator itr = diskWriterEntries.begin();      itr != diskWriterEntries.end(); ++itr) {    (*itr)->enableDirectIO();  }}void MultiDiskAdaptor::disableDirectIO(){  for(DiskWriterEntries::const_iterator itr = diskWriterEntries.begin();      itr != diskWriterEntries.end(); ++itr) {    (*itr)->disableDirectIO();  }}void MultiDiskAdaptor::setMaxOpenFiles(size_t maxOpenFiles){  _maxOpenFiles = maxOpenFiles;}} // namespace aria2
 |