mirror of https://github.com/aria2/aria2
2009-03-07 Tatsuhiro Tsujikawa <t-tujikawa@users.sourceforge.net>
Added --index-out option to specify each file path for torrent. Here index shown in --show-files option is used to specify which file path should be altered. For example, to change the file path with index=2, use --index-out=2=aria2.tar.bz2. You can use this option multiple times: --index-out=1=aria2.tar.bz2 --index-out=2=aria2-opt.tar.bz2. The short hand form -O is also available. This option can be specified in -i list. * src/DefaultBtContext.cc * src/DefaultBtContext.h * src/MultiDiskAdaptor.cc * src/MultiDiskAdaptor.h * src/MultiFileAllocationIterator.cc * src/OptionHandlerFactory.cc * src/OptionHandlerImpl.h * src/Util.cc * src/Util.h * src/download_helper.cc * src/prefs.cc * src/prefs.h * src/usage_text.h * test/DefaultBtContextTest.cc * test/MultiDiskAdaptorTest.cc * test/MultiFileAllocationIteratorTest.cc * test/UtilTest.ccpull/1/head
parent
4aae48c9b2
commit
f44554a634
27
ChangeLog
27
ChangeLog
|
@ -1,3 +1,30 @@
|
|||
2009-03-07 Tatsuhiro Tsujikawa <t-tujikawa@users.sourceforge.net>
|
||||
|
||||
Added --index-out option to specify each file path for torrent.
|
||||
Here index shown in --show-files option is used to specify which
|
||||
file path should be altered. For example, to change the file path
|
||||
with index=2, use --index-out=2=aria2.tar.bz2. You can use this
|
||||
option multiple times: --index-out=1=aria2.tar.bz2
|
||||
--index-out=2=aria2-opt.tar.bz2. The short hand form -O is also
|
||||
available. This option can be specified in -i list.
|
||||
* src/DefaultBtContext.cc
|
||||
* src/DefaultBtContext.h
|
||||
* src/MultiDiskAdaptor.cc
|
||||
* src/MultiDiskAdaptor.h
|
||||
* src/MultiFileAllocationIterator.cc
|
||||
* src/OptionHandlerFactory.cc
|
||||
* src/OptionHandlerImpl.h
|
||||
* src/Util.cc
|
||||
* src/Util.h
|
||||
* src/download_helper.cc
|
||||
* src/prefs.cc
|
||||
* src/prefs.h
|
||||
* src/usage_text.h
|
||||
* test/DefaultBtContextTest.cc
|
||||
* test/MultiDiskAdaptorTest.cc
|
||||
* test/MultiFileAllocationIteratorTest.cc
|
||||
* test/UtilTest.cc
|
||||
|
||||
2009-03-05 Tatsuhiro Tsujikawa <t-tujikawa@users.sourceforge.net>
|
||||
|
||||
Use Util::pathJoin()
|
||||
|
|
|
@ -151,14 +151,20 @@ void DefaultBtContext::extractFileEntries(const bencode::BDE& infoDict,
|
|||
throw DlAbortEx("Path is empty.");
|
||||
}
|
||||
|
||||
std::vector<std::string> elements(pathList.size());
|
||||
std::transform(pathList.listBegin(), pathList.listEnd(), elements.begin(),
|
||||
std::vector<std::string> pathelem(pathList.size());
|
||||
std::transform(pathList.listBegin(), pathList.listEnd(), pathelem.begin(),
|
||||
std::mem_fun_ref(&bencode::BDE::s));
|
||||
std::string path = Util::joinPath(elements.begin(), elements.end());
|
||||
std::string path = Util::joinPath(pathelem.begin(), pathelem.end());
|
||||
// Split path with '/' again because each pathList element can
|
||||
// contain "/" inside.
|
||||
std::deque<std::string> elements;
|
||||
Util::slice(elements, path, '/');
|
||||
elements.push_front(name);
|
||||
path = Util::joinPath(elements.begin(), elements.end());
|
||||
|
||||
std::deque<std::string> uris;
|
||||
std::transform(urlList.begin(), urlList.end(), std::back_inserter(uris),
|
||||
std::bind2nd(std::plus<std::string>(), "/"+name+"/"+path));
|
||||
std::bind2nd(std::plus<std::string>(), "/"+path));
|
||||
FileEntryHandle fileEntry(new FileEntry(path, fileLengthData.i(),
|
||||
offset, uris));
|
||||
fileEntries.push_back(fileEntry);
|
||||
|
@ -475,4 +481,15 @@ void DefaultBtContext::setInfoHash(const unsigned char* infoHash)
|
|||
memcpy(this->infoHash, infoHash, sizeof(this->infoHash));
|
||||
}
|
||||
|
||||
void DefaultBtContext::setFilePathWithIndex
|
||||
(size_t index, const std::string& path)
|
||||
{
|
||||
if(0 < index && index <= fileEntries.size()) {
|
||||
fileEntries[index-1]->setPath(path);
|
||||
} else {
|
||||
throw DlAbortEx(StringFormat("No such file with index=%u",
|
||||
static_cast<unsigned int>(index)).str());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace aria2
|
||||
|
|
|
@ -182,6 +182,12 @@ private:
|
|||
|
||||
void setRandomizer(const SharedHandle<Randomizer>& randomizer);
|
||||
|
||||
// Sets file path for spcified index. index starts from 1. The index
|
||||
// is the same used in BtContext::setFileFilter(). Please note that
|
||||
// path is not the actual file path. The actual file path is
|
||||
// getDir()+"/"+path.
|
||||
void setFilePathWithIndex(size_t index, const std::string& path);
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& o, const DefaultBtContext& ctx);
|
||||
|
||||
static const std::string DEFAULT_PEER_ID_PREFIX;
|
||||
|
|
|
@ -58,15 +58,15 @@ DiskWriterEntry::DiskWriterEntry(const SharedHandle<FileEntry>& fileEntry):
|
|||
|
||||
DiskWriterEntry::~DiskWriterEntry() {}
|
||||
|
||||
std::string DiskWriterEntry::getFilePath(const std::string& topDir) const
|
||||
std::string DiskWriterEntry::getFilePath(const std::string& storeDir) const
|
||||
{
|
||||
return topDir+"/"+fileEntry->getPath();
|
||||
return storeDir+"/"+fileEntry->getPath();
|
||||
}
|
||||
|
||||
void DiskWriterEntry::initAndOpenFile(const std::string& topDir)
|
||||
void DiskWriterEntry::initAndOpenFile(const std::string& storeDir)
|
||||
{
|
||||
if(!diskWriter.isNull()) {
|
||||
diskWriter->initAndOpenFile(getFilePath(topDir), fileEntry->getLength());
|
||||
diskWriter->initAndOpenFile(getFilePath(storeDir), fileEntry->getLength());
|
||||
if(_directIO) {
|
||||
diskWriter->enableDirectIO();
|
||||
}
|
||||
|
@ -74,10 +74,10 @@ void DiskWriterEntry::initAndOpenFile(const std::string& topDir)
|
|||
}
|
||||
}
|
||||
|
||||
void DiskWriterEntry::openFile(const std::string& topDir)
|
||||
void DiskWriterEntry::openFile(const std::string& storeDir)
|
||||
{
|
||||
if(!diskWriter.isNull()) {
|
||||
diskWriter->openFile(getFilePath(topDir), fileEntry->getLength());
|
||||
diskWriter->openFile(getFilePath(storeDir), fileEntry->getLength());
|
||||
if(_directIO) {
|
||||
diskWriter->enableDirectIO();
|
||||
}
|
||||
|
@ -85,10 +85,10 @@ void DiskWriterEntry::openFile(const std::string& topDir)
|
|||
}
|
||||
}
|
||||
|
||||
void DiskWriterEntry::openExistingFile(const std::string& topDir)
|
||||
void DiskWriterEntry::openExistingFile(const std::string& storeDir)
|
||||
{
|
||||
if(!diskWriter.isNull()) {
|
||||
diskWriter->openExistingFile(getFilePath(topDir), fileEntry->getLength());
|
||||
diskWriter->openExistingFile(getFilePath(storeDir), fileEntry->getLength());
|
||||
if(_directIO) {
|
||||
diskWriter->enableDirectIO();
|
||||
}
|
||||
|
@ -109,9 +109,9 @@ void DiskWriterEntry::closeFile()
|
|||
}
|
||||
}
|
||||
|
||||
bool DiskWriterEntry::fileExists(const std::string& topDir)
|
||||
bool DiskWriterEntry::fileExists(const std::string& storeDir)
|
||||
{
|
||||
return File(getFilePath(topDir)).exists();
|
||||
return File(getFilePath(storeDir)).exists();
|
||||
}
|
||||
|
||||
uint64_t DiskWriterEntry::size() const
|
||||
|
@ -270,9 +270,9 @@ void MultiDiskAdaptor::resetDiskWriterEntries()
|
|||
diskWriterEntries.begin(); i != diskWriterEntries.end(); ++i) {
|
||||
if((*i)->needsFileAllocation() ||
|
||||
dwreq.find((*i)->getFileEntry()->getPath()) != dwreq.end() ||
|
||||
(*i)->fileExists(getTopDirPath())) {
|
||||
(*i)->fileExists(storeDir)) {
|
||||
logger->debug("Creating DiskWriter for filename=%s",
|
||||
(*i)->getFilePath(getTopDirPath()).c_str());
|
||||
(*i)->getFilePath(storeDir).c_str());
|
||||
(*i)->setDiskWriter(dwFactory.newDiskWriter());
|
||||
(*i)->getDiskWriter()->setDirectIOAllowed(_directIOAllowed);
|
||||
if(_readOnly) {
|
||||
|
@ -287,25 +287,25 @@ std::string MultiDiskAdaptor::getTopDirPath() const
|
|||
return storeDir+"/"+topDir;
|
||||
}
|
||||
|
||||
void MultiDiskAdaptor::mkdir(const std::string& topDirPath) const
|
||||
void MultiDiskAdaptor::mkdir(const std::string& storeDir) const
|
||||
{
|
||||
for(std::deque<SharedHandle<DiskWriterEntry> >::const_iterator i =
|
||||
diskWriterEntries.begin(); i != diskWriterEntries.end(); ++i) {
|
||||
(*i)->getFileEntry()->setupDir(topDirPath);
|
||||
(*i)->getFileEntry()->setupDir(storeDir);
|
||||
}
|
||||
}
|
||||
|
||||
void MultiDiskAdaptor::openIfNot
|
||||
(const SharedHandle<DiskWriterEntry>& entry,
|
||||
void (DiskWriterEntry::*open)(const std::string&),
|
||||
const std::string& topDirPath)
|
||||
const std::string& storeDir)
|
||||
{
|
||||
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);
|
||||
(entry.get()->*open)(storeDir);
|
||||
if(numOpened >= _maxOpenFiles) {
|
||||
// Cache is full.
|
||||
// Choose one DiskWriterEntry randomly and close it.
|
||||
|
@ -326,32 +326,29 @@ void MultiDiskAdaptor::openIfNot
|
|||
|
||||
void MultiDiskAdaptor::openFile()
|
||||
{
|
||||
_cachedTopDirPath = getTopDirPath();
|
||||
resetDiskWriterEntries();
|
||||
mkdir(_cachedTopDirPath);
|
||||
mkdir(storeDir);
|
||||
// Call DiskWriterEntry::openFile to make sure that zero-length files are
|
||||
// created.
|
||||
for(DiskWriterEntries::iterator itr = diskWriterEntries.begin();
|
||||
itr != diskWriterEntries.end(); ++itr) {
|
||||
openIfNot(*itr, &DiskWriterEntry::openFile, _cachedTopDirPath);
|
||||
openIfNot(*itr, &DiskWriterEntry::openFile, storeDir);
|
||||
}
|
||||
}
|
||||
|
||||
void MultiDiskAdaptor::initAndOpenFile()
|
||||
{
|
||||
_cachedTopDirPath = getTopDirPath();
|
||||
resetDiskWriterEntries();
|
||||
mkdir(_cachedTopDirPath);
|
||||
mkdir(storeDir);
|
||||
// Call DiskWriterEntry::initAndOpenFile to make files truncated.
|
||||
for(DiskWriterEntries::iterator itr = diskWriterEntries.begin();
|
||||
itr != diskWriterEntries.end(); ++itr) {
|
||||
openIfNot(*itr, &DiskWriterEntry::initAndOpenFile, _cachedTopDirPath);
|
||||
openIfNot(*itr, &DiskWriterEntry::initAndOpenFile, storeDir);
|
||||
}
|
||||
}
|
||||
|
||||
void MultiDiskAdaptor::openExistingFile()
|
||||
{
|
||||
_cachedTopDirPath = getTopDirPath();
|
||||
resetDiskWriterEntries();
|
||||
// Not need to call openIfNot here.
|
||||
}
|
||||
|
@ -416,12 +413,12 @@ findFirstDiskWriterEntry(const DiskWriterEntries& diskWriterEntries, off_t offse
|
|||
|
||||
static void throwOnDiskWriterNotOpened(const SharedHandle<DiskWriterEntry>& e,
|
||||
off_t offset,
|
||||
const std::string& topDirPath)
|
||||
const std::string& storeDir)
|
||||
{
|
||||
throw DlAbortEx
|
||||
(StringFormat("DiskWriter for offset=%s, filename=%s is not opened.",
|
||||
Util::itos(offset).c_str(),
|
||||
e->getFilePath(topDirPath).c_str()).str());
|
||||
e->getFilePath(storeDir).c_str()).str());
|
||||
}
|
||||
|
||||
void MultiDiskAdaptor::writeData(const unsigned char* data, size_t len,
|
||||
|
@ -434,10 +431,10 @@ void MultiDiskAdaptor::writeData(const unsigned char* data, size_t len,
|
|||
for(DiskWriterEntries::const_iterator i = first; i != diskWriterEntries.end(); ++i) {
|
||||
size_t writeLength = calculateLength(*i, fileOffset, rem);
|
||||
|
||||
openIfNot(*i, &DiskWriterEntry::openFile, _cachedTopDirPath);
|
||||
openIfNot(*i, &DiskWriterEntry::openFile, storeDir);
|
||||
|
||||
if(!(*i)->isOpen()) {
|
||||
throwOnDiskWriterNotOpened(*i, offset+(len-rem), _cachedTopDirPath);
|
||||
throwOnDiskWriterNotOpened(*i, offset+(len-rem), storeDir);
|
||||
}
|
||||
|
||||
(*i)->getDiskWriter()->writeData(data+(len-rem), writeLength, fileOffset);
|
||||
|
@ -459,10 +456,10 @@ ssize_t MultiDiskAdaptor::readData(unsigned char* data, size_t len, off_t offset
|
|||
for(DiskWriterEntries::const_iterator i = first; i != diskWriterEntries.end(); ++i) {
|
||||
size_t readLength = calculateLength(*i, fileOffset, rem);
|
||||
|
||||
openIfNot(*i, &DiskWriterEntry::openFile, _cachedTopDirPath);
|
||||
openIfNot(*i, &DiskWriterEntry::openFile, storeDir);
|
||||
|
||||
if(!(*i)->isOpen()) {
|
||||
throwOnDiskWriterNotOpened(*i, offset+(len-rem), _cachedTopDirPath);
|
||||
throwOnDiskWriterNotOpened(*i, offset+(len-rem), storeDir);
|
||||
}
|
||||
|
||||
totalReadLength +=
|
||||
|
@ -478,12 +475,13 @@ ssize_t MultiDiskAdaptor::readData(unsigned char* data, size_t len, off_t offset
|
|||
|
||||
bool MultiDiskAdaptor::fileExists()
|
||||
{
|
||||
// TODO Use FileEntry::fileExists() here.
|
||||
|
||||
// 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()).exists()) {
|
||||
if(File(storeDir+"/"+(*i)->getPath()).exists()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -496,7 +494,7 @@ uint64_t MultiDiskAdaptor::size()
|
|||
uint64_t size = 0;
|
||||
for(DiskWriterEntries::const_iterator itr = diskWriterEntries.begin();
|
||||
itr != diskWriterEntries.end(); ++itr) {
|
||||
openIfNot(*itr, &DiskWriterEntry::openFile, _cachedTopDirPath);
|
||||
openIfNot(*itr, &DiskWriterEntry::openFile, storeDir);
|
||||
size += (*itr)->size();
|
||||
}
|
||||
return size;
|
||||
|
@ -538,9 +536,9 @@ void MultiDiskAdaptor::cutTrailingGarbage()
|
|||
for(std::deque<SharedHandle<DiskWriterEntry> >::const_iterator i =
|
||||
diskWriterEntries.begin(); i != diskWriterEntries.end(); ++i) {
|
||||
uint64_t length = (*i)->getFileEntry()->getLength();
|
||||
if(File((*i)->getFilePath(_cachedTopDirPath)).size() > length) {
|
||||
if(File((*i)->getFilePath(storeDir)).size() > length) {
|
||||
// We need open file before calling DiskWriter::truncate(uint64_t)
|
||||
openIfNot(*i, &DiskWriterEntry::openFile, _cachedTopDirPath);
|
||||
openIfNot(*i, &DiskWriterEntry::openFile, storeDir);
|
||||
(*i)->getDiskWriter()->truncate(length);
|
||||
}
|
||||
}
|
||||
|
@ -557,7 +555,7 @@ size_t MultiDiskAdaptor::utime(const Time& actime, const Time& modtime)
|
|||
for(std::deque<SharedHandle<FileEntry> >::const_iterator i =
|
||||
fileEntries.begin(); i != fileEntries.end(); ++i) {
|
||||
if((*i)->isRequested()) {
|
||||
File f(getTopDirPath()+"/"+(*i)->getPath());
|
||||
File f(storeDir+"/"+(*i)->getPath());
|
||||
if(f.isFile() && f.utime(actime, modtime)) {
|
||||
++numOK;
|
||||
}
|
||||
|
@ -572,4 +570,9 @@ MultiDiskAdaptor::getDiskWriterEntries() const
|
|||
return diskWriterEntries;
|
||||
}
|
||||
|
||||
const std::string& MultiDiskAdaptor::getStoreDir() const
|
||||
{
|
||||
return storeDir;
|
||||
}
|
||||
|
||||
} // namespace aria2
|
||||
|
|
|
@ -55,19 +55,19 @@ public:
|
|||
|
||||
~DiskWriterEntry();
|
||||
|
||||
std::string getFilePath(const std::string& topDir) const;
|
||||
std::string getFilePath(const std::string& storeDir) const;
|
||||
|
||||
void initAndOpenFile(const std::string& topDir);
|
||||
void initAndOpenFile(const std::string& storeDir);
|
||||
|
||||
void openFile(const std::string& topDir);
|
||||
void openFile(const std::string& storeDir);
|
||||
|
||||
void openExistingFile(const std::string& topDir);
|
||||
void openExistingFile(const std::string& storeDir);
|
||||
|
||||
void closeFile();
|
||||
|
||||
bool isOpen() const;
|
||||
|
||||
bool fileExists(const std::string& topDir);
|
||||
bool fileExists(const std::string& storeDir);
|
||||
|
||||
uint64_t size() const;
|
||||
|
||||
|
@ -105,8 +105,6 @@ private:
|
|||
size_t pieceLength;
|
||||
DiskWriterEntries diskWriterEntries;
|
||||
|
||||
std::string _cachedTopDirPath;
|
||||
|
||||
std::deque<SharedHandle<DiskWriterEntry> > _openedDiskWriterEntries;
|
||||
|
||||
size_t _maxOpenFiles;
|
||||
|
@ -117,13 +115,13 @@ private:
|
|||
|
||||
void resetDiskWriterEntries();
|
||||
|
||||
void mkdir(const std::string& topDirPath) const;
|
||||
void mkdir(const std::string& storeDir) const;
|
||||
|
||||
std::string getTopDirPath() const;
|
||||
|
||||
void openIfNot(const SharedHandle<DiskWriterEntry>& entry,
|
||||
void (DiskWriterEntry::*f)(const std::string&),
|
||||
const std::string& topDirPath);
|
||||
const std::string& storeDir);
|
||||
|
||||
static const size_t DEFAULT_MAX_OPEN_FILES = 100;
|
||||
|
||||
|
@ -200,6 +198,8 @@ public:
|
|||
|
||||
const std::deque<SharedHandle<DiskWriterEntry> >&
|
||||
getDiskWriterEntries() const;
|
||||
|
||||
const std::string& getStoreDir() const;
|
||||
};
|
||||
|
||||
typedef SharedHandle<MultiDiskAdaptor> MultiDiskAdaptorHandle;
|
||||
|
|
|
@ -59,7 +59,7 @@ void MultiFileAllocationIterator::allocateChunk()
|
|||
FileEntryHandle fileEntry = entry->getFileEntry();
|
||||
// Open file before calling DiskWriterEntry::size()
|
||||
_diskAdaptor->openIfNot(entry, &DiskWriterEntry::openFile,
|
||||
_diskAdaptor->getTopDirPath());
|
||||
_diskAdaptor->getStoreDir());
|
||||
entry->enableDirectIO();
|
||||
if(entry->needsFileAllocation() && entry->size() < fileEntry->getLength()) {
|
||||
// Calling private function of MultiDiskAdaptor.
|
||||
|
|
|
@ -968,6 +968,14 @@ OptionHandlers OptionHandlerFactory::createOptionHandlers()
|
|||
op->addTag(TAG_BITTORRENT);
|
||||
handlers.push_back(op);
|
||||
}
|
||||
{
|
||||
SharedHandle<OptionHandler> op(new IndexOutOptionHandler
|
||||
(PREF_INDEX_OUT,
|
||||
TEXT_INDEX_OUT,
|
||||
'O'));
|
||||
op->addTag(TAG_BITTORRENT);
|
||||
handlers.push_back(op);
|
||||
}
|
||||
{
|
||||
SharedHandle<OptionHandler> op(new IntegerRangeOptionHandler
|
||||
(PREF_LISTEN_PORT,
|
||||
|
|
|
@ -366,6 +366,32 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
class IndexOutOptionHandler : public NameMatchOptionHandler {
|
||||
private:
|
||||
public:
|
||||
IndexOutOptionHandler(const std::string& optName,
|
||||
const std::string& description,
|
||||
char shortName = 0):
|
||||
NameMatchOptionHandler(optName, description, NO_DEFAULT_VALUE,
|
||||
OptionHandler::REQ_ARG, shortName) {}
|
||||
|
||||
virtual ~IndexOutOptionHandler() {}
|
||||
|
||||
virtual void parseArg(Option& option, const std::string& optarg)
|
||||
{
|
||||
// See optarg is in the fomrat of "INDEX=PATH"
|
||||
Util::parseIndexPath(optarg);
|
||||
std::string value = option.get(_optName);
|
||||
value += optarg+"\n";
|
||||
option.put(_optName, value);
|
||||
}
|
||||
|
||||
virtual std::string createPossibleValuesString() const
|
||||
{
|
||||
return "INDEX=PATH";
|
||||
}
|
||||
};
|
||||
|
||||
class ParameterOptionHandler : public NameMatchOptionHandler {
|
||||
private:
|
||||
std::deque<std::string> _validParamValues;
|
||||
|
|
22
src/Util.cc
22
src/Util.cc
|
@ -941,4 +941,26 @@ std::string Util::htmlEscape(const std::string& src)
|
|||
return dest;
|
||||
}
|
||||
|
||||
std::map<size_t, std::string>::value_type
|
||||
Util::parseIndexPath(const std::string& line)
|
||||
{
|
||||
std::pair<std::string, std::string> p = Util::split(line, "=");
|
||||
size_t index = parseUInt(p.first);
|
||||
if(p.second.empty()) {
|
||||
throw DlAbortEx(StringFormat("Path with index=%u is empty.",
|
||||
static_cast<unsigned int>(index)).str());
|
||||
}
|
||||
return std::map<size_t, std::string>::value_type(index, p.second);
|
||||
}
|
||||
|
||||
std::map<size_t, std::string> Util::createIndexPathMap(std::istream& i)
|
||||
{
|
||||
std::map<size_t, std::string> indexPathMap;
|
||||
std::string line;
|
||||
while(getline(i, line)) {
|
||||
indexPathMap.insert(indexPathMap.begin(), parseIndexPath(line));
|
||||
}
|
||||
return indexPathMap;
|
||||
}
|
||||
|
||||
} // namespace aria2
|
||||
|
|
|
@ -44,6 +44,7 @@
|
|||
#include <deque>
|
||||
#include <iosfwd>
|
||||
#include <numeric>
|
||||
#include <map>
|
||||
|
||||
#include "SharedHandle.h"
|
||||
#include "IntSequence.h"
|
||||
|
@ -306,6 +307,13 @@ public:
|
|||
return std::accumulate(elements.begin()+1, elements.end(), elements[0],
|
||||
Concat("/"));
|
||||
}
|
||||
|
||||
// Parses INDEX=PATH format string. INDEX must be an unsigned
|
||||
// integer.
|
||||
static std::map<size_t, std::string>::value_type
|
||||
parseIndexPath(const std::string& line);
|
||||
|
||||
static std::map<size_t, std::string> createIndexPathMap(std::istream& i);
|
||||
};
|
||||
|
||||
} // namespace aria2
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
|
||||
#include "RequestGroup.h"
|
||||
#include "Option.h"
|
||||
|
@ -124,6 +125,13 @@ createBtRequestGroup(const std::string& torrentFilePath,
|
|||
btContext->setDir(requestOption.get(PREF_DIR));
|
||||
btContext->setFileFilter
|
||||
(Util::parseIntRange(requestOption.get(PREF_SELECT_FILE)));
|
||||
std::istringstream indexOutIn(requestOption.get(PREF_INDEX_OUT));
|
||||
std::map<size_t, std::string> indexPathMap =
|
||||
Util::createIndexPathMap(indexOutIn);
|
||||
for(std::map<size_t, std::string>::const_iterator i = indexPathMap.begin();
|
||||
i != indexPathMap.end(); ++i) {
|
||||
btContext->setFilePathWithIndex((*i).first, (*i).second);
|
||||
}
|
||||
rg->setDownloadContext(btContext);
|
||||
btContext->setOwnerRequestGroup(rg.get());
|
||||
return rg;
|
||||
|
@ -295,11 +303,13 @@ static void createRequestGroupForUriList
|
|||
if(uris.empty()) {
|
||||
continue;
|
||||
}
|
||||
// TODO use OptionParser to validate the value in the options.
|
||||
// These options can be specified in input list(-i list).
|
||||
static const std::string REQUEST_OPTIONS[] = {
|
||||
PREF_DIR,
|
||||
PREF_OUT,
|
||||
PREF_SELECT_FILE
|
||||
PREF_SELECT_FILE,
|
||||
PREF_INDEX_OUT
|
||||
};
|
||||
foreachCopyIfndef(&REQUEST_OPTIONS[0],
|
||||
&REQUEST_OPTIONS[arrayLength(REQUEST_OPTIONS)],
|
||||
|
|
|
@ -283,6 +283,8 @@ const std::string PREF_BT_HASH_CHECK_SEED("bt-hash-check-seed");
|
|||
const std::string PREF_BT_MAX_PEERS("bt-max-peers");
|
||||
// values: a string (IP address)
|
||||
const std::string PREF_BT_EXTERNAL_IP("bt-external-ip");
|
||||
// values: 1*digit '=' a string that your file system recognizes as a file name.
|
||||
const std::string PREF_INDEX_OUT("index-out");
|
||||
|
||||
/**
|
||||
* Metalink related preferences
|
||||
|
|
|
@ -287,6 +287,8 @@ extern const std::string PREF_BT_HASH_CHECK_SEED;
|
|||
extern const std::string PREF_BT_MAX_PEERS;
|
||||
// values: a string (IP address)
|
||||
extern const std::string PREF_BT_EXTERNAL_IP;
|
||||
// values: 1*digit '=' a string that your file system recognizes as a file name.
|
||||
extern const std::string PREF_INDEX_OUT;
|
||||
|
||||
/**
|
||||
* Metalink related preferences
|
||||
|
|
|
@ -493,3 +493,9 @@ _(" --http-auth-challenge[=true|false] Send HTTP authorization header only when
|
|||
" are embedded in URI, authorization header is\n"\
|
||||
" always sent to the server regardless of this\n"\
|
||||
" option.")
|
||||
#define TEXT_INDEX_OUT \
|
||||
_(" -O, --index-out=INDEX=PATH Set file path for file with index=INDEX. You can\n"\
|
||||
" find the file index using the --show-files option.\n"\
|
||||
" PATH is a relative path to the path specified in\n"\
|
||||
" --dir option. You can use this option multiple\n"\
|
||||
" times.")
|
||||
|
|
|
@ -108,11 +108,11 @@ void DefaultBtContextTest::testGetFileEntries() {
|
|||
std::deque<SharedHandle<FileEntry> >::iterator itr = fileEntries.begin();
|
||||
|
||||
SharedHandle<FileEntry> fileEntry1 = *itr;
|
||||
CPPUNIT_ASSERT_EQUAL(std::string("aria2/src/aria2c"),
|
||||
CPPUNIT_ASSERT_EQUAL(std::string("aria2-test/aria2/src/aria2c"),
|
||||
fileEntry1->getPath());
|
||||
itr++;
|
||||
SharedHandle<FileEntry> fileEntry2 = *itr;
|
||||
CPPUNIT_ASSERT_EQUAL(std::string("aria2-0.2.2.tar.bz2"),
|
||||
CPPUNIT_ASSERT_EQUAL(std::string("aria2-test/aria2-0.2.2.tar.bz2"),
|
||||
fileEntry2->getPath());
|
||||
}
|
||||
|
||||
|
@ -289,7 +289,7 @@ void DefaultBtContextTest::testGetFileEntries_multiFileUrlList() {
|
|||
std::deque<SharedHandle<FileEntry> >::iterator itr = fileEntries.begin();
|
||||
|
||||
SharedHandle<FileEntry> fileEntry1 = *itr;
|
||||
CPPUNIT_ASSERT_EQUAL(std::string("aria2/src/aria2c"),
|
||||
CPPUNIT_ASSERT_EQUAL(std::string("aria2-test/aria2/src/aria2c"),
|
||||
fileEntry1->getPath());
|
||||
std::deque<std::string> uris1 = fileEntry1->getAssociatedUris();
|
||||
CPPUNIT_ASSERT_EQUAL((size_t)2, uris1.size());
|
||||
|
@ -300,7 +300,7 @@ void DefaultBtContextTest::testGetFileEntries_multiFileUrlList() {
|
|||
|
||||
itr++;
|
||||
SharedHandle<FileEntry> fileEntry2 = *itr;
|
||||
CPPUNIT_ASSERT_EQUAL(std::string("aria2-0.2.2.tar.bz2"),
|
||||
CPPUNIT_ASSERT_EQUAL(std::string("aria2-test/aria2-0.2.2.tar.bz2"),
|
||||
fileEntry2->getPath());
|
||||
std::deque<std::string> uris2 = fileEntry2->getAssociatedUris();
|
||||
CPPUNIT_ASSERT_EQUAL((size_t)2, uris2.size());
|
||||
|
|
|
@ -434,9 +434,7 @@ void MultiDiskAdaptorTest::testSize()
|
|||
|
||||
void MultiDiskAdaptorTest::testUtime()
|
||||
{
|
||||
std::string storeDir = "/tmp";
|
||||
std::string topDir = "aria2_MultiDiskAdaptorTest_testUtime";
|
||||
std::string prefix = storeDir+"/"+topDir;
|
||||
std::string storeDir = "/tmp/aria2_MultiDiskAdaptorTest_testUtime";
|
||||
SharedHandle<FileEntry> entries[] = {
|
||||
SharedHandle<FileEntry>(new FileEntry("requested", 0, 0)),
|
||||
SharedHandle<FileEntry>(new FileEntry("notFound", 0, 0)),
|
||||
|
@ -444,10 +442,10 @@ void MultiDiskAdaptorTest::testUtime()
|
|||
SharedHandle<FileEntry>(new FileEntry("anotherRequested", 0, 0)),
|
||||
};
|
||||
|
||||
createFile(prefix+"/"+entries[0]->getPath(), entries[0]->getLength());
|
||||
File(prefix+"/"+entries[1]->getPath()).remove();
|
||||
createFile(prefix+"/"+entries[2]->getPath(), entries[2]->getLength());
|
||||
createFile(prefix+"/"+entries[3]->getPath(), entries[3]->getLength());
|
||||
createFile(storeDir+"/"+entries[0]->getPath(), entries[0]->getLength());
|
||||
File(storeDir+"/"+entries[1]->getPath()).remove();
|
||||
createFile(storeDir+"/"+entries[2]->getPath(), entries[2]->getLength());
|
||||
createFile(storeDir+"/"+entries[3]->getPath(), entries[3]->getLength());
|
||||
|
||||
entries[2]->setRequested(false);
|
||||
|
||||
|
@ -455,7 +453,6 @@ void MultiDiskAdaptorTest::testUtime()
|
|||
(&entries[0], &entries[arrayLength(entries)]);
|
||||
MultiDiskAdaptor adaptor;
|
||||
adaptor.setStoreDir(storeDir);
|
||||
adaptor.setTopDir(topDir);
|
||||
adaptor.setFileEntries(fileEntries);
|
||||
|
||||
time_t atime = (time_t) 100000;
|
||||
|
@ -464,14 +461,14 @@ void MultiDiskAdaptorTest::testUtime()
|
|||
CPPUNIT_ASSERT_EQUAL((size_t)2, adaptor.utime(Time(atime), Time(mtime)));
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL((time_t)mtime,
|
||||
File(prefix+"/"+entries[0]->getPath())
|
||||
File(storeDir+"/"+entries[0]->getPath())
|
||||
.getModifiedTime().getTime());
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL((time_t)mtime,
|
||||
File(prefix+"/"+entries[3]->getPath())
|
||||
File(storeDir+"/"+entries[3]->getPath())
|
||||
.getModifiedTime().getTime());
|
||||
|
||||
CPPUNIT_ASSERT((time_t)mtime != File(prefix+"/"+entries[2]->getPath())
|
||||
CPPUNIT_ASSERT((time_t)mtime != File(storeDir+"/"+entries[2]->getPath())
|
||||
.getModifiedTime().getTime());
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
#include "MultiFileAllocationIterator.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
|
||||
#include <cppunit/extensions/HelperMacros.h>
|
||||
|
||||
#include "File.h"
|
||||
#include "MultiDiskAdaptor.h"
|
||||
#include "FileEntry.h"
|
||||
|
@ -6,9 +12,6 @@
|
|||
#include "array_fun.h"
|
||||
#include "TestUtil.h"
|
||||
#include "DiskWriter.h"
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <cppunit/extensions/HelperMacros.h>
|
||||
|
||||
namespace aria2 {
|
||||
|
||||
|
@ -53,20 +56,17 @@ void MultiFileAllocationIteratorTest::testMakeDiskWriterEntries()
|
|||
fs[8]->setRequested(false); // file9
|
||||
fs[9]->setRequested(false); // fileA
|
||||
|
||||
std::string topDir = "top";
|
||||
std::string storeDir = "/tmp/aria2_MultiFileAllocationIteratorTest"
|
||||
"_testMakeDiskWriterEntries";
|
||||
std::string prefix = storeDir+"/"+topDir;
|
||||
|
||||
// create empty file4
|
||||
createFile(prefix+std::string("/file4"), 0);
|
||||
createFile(storeDir+std::string("/file4"), 0);
|
||||
|
||||
SharedHandle<MultiDiskAdaptor> diskAdaptor(new MultiDiskAdaptor());
|
||||
diskAdaptor->setFileEntries
|
||||
(std::deque<SharedHandle<FileEntry> >(&fs[0], &fs[arrayLength(fs)]));
|
||||
diskAdaptor->setPieceLength(1024);
|
||||
diskAdaptor->setStoreDir(storeDir);
|
||||
diskAdaptor->setTopDir(topDir);
|
||||
diskAdaptor->openFile();
|
||||
|
||||
SharedHandle<MultiFileAllocationIterator> itr
|
||||
|
@ -78,67 +78,67 @@ void MultiFileAllocationIteratorTest::testMakeDiskWriterEntries()
|
|||
CPPUNIT_ASSERT_EQUAL((size_t)11, entries.size());
|
||||
|
||||
// file1
|
||||
CPPUNIT_ASSERT_EQUAL(prefix+std::string("/file1"),
|
||||
entries[0]->getFilePath(prefix));
|
||||
CPPUNIT_ASSERT_EQUAL(storeDir+std::string("/file1"),
|
||||
entries[0]->getFilePath(storeDir));
|
||||
CPPUNIT_ASSERT(entries[0]->needsFileAllocation());
|
||||
CPPUNIT_ASSERT(!entries[0]->getDiskWriter().isNull());
|
||||
// file2
|
||||
CPPUNIT_ASSERT_EQUAL(prefix+std::string("/file2"),
|
||||
entries[1]->getFilePath(prefix));
|
||||
CPPUNIT_ASSERT_EQUAL(storeDir+std::string("/file2"),
|
||||
entries[1]->getFilePath(storeDir));
|
||||
CPPUNIT_ASSERT(entries[1]->needsFileAllocation());
|
||||
CPPUNIT_ASSERT(!entries[1]->getDiskWriter().isNull());
|
||||
// file3
|
||||
CPPUNIT_ASSERT_EQUAL(prefix+std::string("/file3"),
|
||||
entries[2]->getFilePath(prefix));
|
||||
CPPUNIT_ASSERT_EQUAL(storeDir+std::string("/file3"),
|
||||
entries[2]->getFilePath(storeDir));
|
||||
CPPUNIT_ASSERT(entries[2]->needsFileAllocation());
|
||||
CPPUNIT_ASSERT(!entries[2]->getDiskWriter().isNull());
|
||||
// file4, diskWriter is not null, because file exists.
|
||||
CPPUNIT_ASSERT_EQUAL(prefix+std::string("/file4"),
|
||||
entries[3]->getFilePath(prefix));
|
||||
CPPUNIT_ASSERT_EQUAL(storeDir+std::string("/file4"),
|
||||
entries[3]->getFilePath(storeDir));
|
||||
CPPUNIT_ASSERT(!entries[3]->needsFileAllocation());
|
||||
CPPUNIT_ASSERT(!entries[3]->getDiskWriter().isNull());
|
||||
// file5
|
||||
CPPUNIT_ASSERT_EQUAL(prefix+std::string("/file5"),
|
||||
entries[4]->getFilePath(prefix));
|
||||
CPPUNIT_ASSERT_EQUAL(storeDir+std::string("/file5"),
|
||||
entries[4]->getFilePath(storeDir));
|
||||
CPPUNIT_ASSERT(!entries[4]->needsFileAllocation());
|
||||
CPPUNIT_ASSERT(entries[4]->getDiskWriter().isNull());
|
||||
// file6
|
||||
CPPUNIT_ASSERT_EQUAL(prefix+std::string("/file6"),
|
||||
entries[5]->getFilePath(prefix));
|
||||
CPPUNIT_ASSERT_EQUAL(storeDir+std::string("/file6"),
|
||||
entries[5]->getFilePath(storeDir));
|
||||
CPPUNIT_ASSERT(entries[5]->needsFileAllocation());
|
||||
CPPUNIT_ASSERT(!entries[5]->getDiskWriter().isNull());
|
||||
// file7
|
||||
CPPUNIT_ASSERT_EQUAL(prefix+std::string("/file7"),
|
||||
entries[6]->getFilePath(prefix));
|
||||
CPPUNIT_ASSERT_EQUAL(storeDir+std::string("/file7"),
|
||||
entries[6]->getFilePath(storeDir));
|
||||
CPPUNIT_ASSERT(entries[6]->needsFileAllocation());
|
||||
CPPUNIT_ASSERT(!entries[6]->getDiskWriter().isNull());
|
||||
// file8
|
||||
CPPUNIT_ASSERT_EQUAL(prefix+std::string("/file8"),
|
||||
entries[7]->getFilePath(prefix));
|
||||
CPPUNIT_ASSERT_EQUAL(storeDir+std::string("/file8"),
|
||||
entries[7]->getFilePath(storeDir));
|
||||
CPPUNIT_ASSERT(entries[7]->needsFileAllocation());
|
||||
CPPUNIT_ASSERT(!entries[7]->getDiskWriter().isNull());
|
||||
// file9
|
||||
CPPUNIT_ASSERT_EQUAL(prefix+std::string("/file9"),
|
||||
entries[8]->getFilePath(prefix));
|
||||
CPPUNIT_ASSERT_EQUAL(storeDir+std::string("/file9"),
|
||||
entries[8]->getFilePath(storeDir));
|
||||
CPPUNIT_ASSERT(!entries[8]->needsFileAllocation());
|
||||
CPPUNIT_ASSERT(!entries[8]->getDiskWriter().isNull());
|
||||
// fileA
|
||||
CPPUNIT_ASSERT_EQUAL(prefix+std::string("/fileA"),
|
||||
entries[9]->getFilePath(prefix));
|
||||
CPPUNIT_ASSERT_EQUAL(storeDir+std::string("/fileA"),
|
||||
entries[9]->getFilePath(storeDir));
|
||||
CPPUNIT_ASSERT(!entries[9]->needsFileAllocation());
|
||||
CPPUNIT_ASSERT(entries[9]->getDiskWriter().isNull());
|
||||
// fileB
|
||||
CPPUNIT_ASSERT_EQUAL(prefix+std::string("/fileB"),
|
||||
entries[10]->getFilePath(prefix));
|
||||
CPPUNIT_ASSERT_EQUAL(storeDir+std::string("/fileB"),
|
||||
entries[10]->getFilePath(storeDir));
|
||||
CPPUNIT_ASSERT(entries[10]->needsFileAllocation());
|
||||
CPPUNIT_ASSERT(!entries[10]->getDiskWriter().isNull());
|
||||
}
|
||||
|
||||
void MultiFileAllocationIteratorTest::testAllocate()
|
||||
{
|
||||
std::string dir = "/tmp";
|
||||
std::string topDir = "aria2_MultiFileAllocationIteratorTest_testAllocate";
|
||||
std::string prefix = dir+"/"+topDir;
|
||||
std::string storeDir =
|
||||
"/tmp/aria2_MultiFileAllocationIteratorTest_testAllocate";
|
||||
|
||||
std::string fname1 = "file1";
|
||||
std::string fname2 = "file2";
|
||||
std::string fname3 = "file3";
|
||||
|
@ -154,8 +154,7 @@ void MultiFileAllocationIteratorTest::testAllocate()
|
|||
|
||||
try {
|
||||
SharedHandle<MultiDiskAdaptor> diskAdaptor(new MultiDiskAdaptor());
|
||||
diskAdaptor->setStoreDir(dir);
|
||||
diskAdaptor->setTopDir(topDir);
|
||||
diskAdaptor->setStoreDir(storeDir);
|
||||
diskAdaptor->setPieceLength(1);
|
||||
|
||||
int64_t offset = 0;
|
||||
|
@ -199,12 +198,12 @@ void MultiFileAllocationIteratorTest::testAllocate()
|
|||
diskAdaptor->setFileEntries(fs);
|
||||
|
||||
|
||||
File(prefix+"/"+fname1).remove();
|
||||
File(prefix+"/"+fname2).remove();
|
||||
File(prefix+"/"+fname3).remove();
|
||||
File(prefix+"/"+fname4).remove();
|
||||
File(prefix+"/"+fname5).remove();
|
||||
File(prefix+"/"+fname6).remove();
|
||||
File(storeDir+"/"+fname1).remove();
|
||||
File(storeDir+"/"+fname2).remove();
|
||||
File(storeDir+"/"+fname3).remove();
|
||||
File(storeDir+"/"+fname4).remove();
|
||||
File(storeDir+"/"+fname5).remove();
|
||||
File(storeDir+"/"+fname6).remove();
|
||||
|
||||
// we have to open file first.
|
||||
diskAdaptor->initAndOpenFile();
|
||||
|
@ -214,13 +213,13 @@ void MultiFileAllocationIteratorTest::testAllocate()
|
|||
while(!itr->finished()) {
|
||||
itr->allocateChunk();
|
||||
}
|
||||
CPPUNIT_ASSERT_EQUAL((uint64_t)length1, File(prefix+"/"+fname1).size());
|
||||
CPPUNIT_ASSERT_EQUAL((uint64_t)length2, File(prefix+"/"+fname2).size());
|
||||
CPPUNIT_ASSERT_EQUAL((uint64_t)length3, File(prefix+"/"+fname3).size());
|
||||
CPPUNIT_ASSERT(!File(prefix+"/"+fname4).isFile());
|
||||
CPPUNIT_ASSERT_EQUAL((uint64_t)length1, File(storeDir+"/"+fname1).size());
|
||||
CPPUNIT_ASSERT_EQUAL((uint64_t)length2, File(storeDir+"/"+fname2).size());
|
||||
CPPUNIT_ASSERT_EQUAL((uint64_t)length3, File(storeDir+"/"+fname3).size());
|
||||
CPPUNIT_ASSERT(!File(storeDir+"/"+fname4).isFile());
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL((uint64_t)length5, File(prefix+"/"+fname5).size());
|
||||
CPPUNIT_ASSERT(!File(prefix+"/"+fname6).isFile());
|
||||
CPPUNIT_ASSERT_EQUAL((uint64_t)length5, File(storeDir+"/"+fname5).size());
|
||||
CPPUNIT_ASSERT(!File(storeDir+"/"+fname6).isFile());
|
||||
|
||||
} catch(Exception& e) {
|
||||
CPPUNIT_FAIL(e.stackTrace());
|
||||
|
|
|
@ -54,6 +54,8 @@ class UtilTest:public CppUnit::TestFixture {
|
|||
CPPUNIT_TEST(testUrlencode);
|
||||
CPPUNIT_TEST(testHtmlEscape);
|
||||
CPPUNIT_TEST(testJoinPath);
|
||||
CPPUNIT_TEST(testParseIndexPath);
|
||||
CPPUNIT_TEST(testCreateIndexPathMap);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
private:
|
||||
|
||||
|
@ -97,6 +99,8 @@ public:
|
|||
void testUrlencode();
|
||||
void testHtmlEscape();
|
||||
void testJoinPath();
|
||||
void testParseIndexPath();
|
||||
void testCreateIndexPathMap();
|
||||
};
|
||||
|
||||
|
||||
|
@ -778,4 +782,36 @@ void UtilTest::testJoinPath()
|
|||
&parentdot[arrayLength(parentdot)]));
|
||||
}
|
||||
|
||||
void UtilTest::testParseIndexPath()
|
||||
{
|
||||
std::map<size_t, std::string>::value_type p = Util::parseIndexPath("1=foo");
|
||||
CPPUNIT_ASSERT_EQUAL((size_t)1, p.first);
|
||||
CPPUNIT_ASSERT_EQUAL(std::string("foo"), p.second);
|
||||
try {
|
||||
Util::parseIndexPath("1X=foo");
|
||||
CPPUNIT_FAIL("exception must be thrown.");
|
||||
} catch(Exception& e) {
|
||||
// success
|
||||
}
|
||||
try {
|
||||
Util::parseIndexPath("1=");
|
||||
CPPUNIT_FAIL("exception must be thrown.");
|
||||
} catch(Exception& e) {
|
||||
// success
|
||||
}
|
||||
}
|
||||
|
||||
void UtilTest::testCreateIndexPathMap()
|
||||
{
|
||||
std::stringstream in
|
||||
("1=/tmp/myfile\n"
|
||||
"100=/myhome/mypicture.png\n");
|
||||
std::map<size_t, std::string> m = Util::createIndexPathMap(in);
|
||||
CPPUNIT_ASSERT_EQUAL((size_t)2, m.size());
|
||||
CPPUNIT_ASSERT(m.find(1) != m.end());
|
||||
CPPUNIT_ASSERT_EQUAL(std::string("/tmp/myfile"), m[1]);
|
||||
CPPUNIT_ASSERT(m.find(100) != m.end());
|
||||
CPPUNIT_ASSERT_EQUAL(std::string("/myhome/mypicture.png"), m[100]);
|
||||
}
|
||||
|
||||
} // namespace aria2
|
||||
|
|
Loading…
Reference in New Issue