1 /*
2  * Copyright (C) 2001-2012 Jacek Sieka, arnetheduck on gmail point com
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17  */
18 
19 #include "stdinc.h"
20 
21 #include "QueueItem.h"
22 #include "HashManager.h"
23 #include "Download.h"
24 #include "File.h"
25 #include "Util.h"
26 
27 namespace dcpp {
28 
29 namespace {
30     const string TEMP_EXTENSION = ".dctmp";
31 
getTempName(const string & aFileName,const TTHValue & aRoot)32     string getTempName(const string& aFileName, const TTHValue& aRoot) {
33         string tmp(aFileName);
34         tmp += "." + aRoot.toBase32();
35         tmp += TEMP_EXTENSION;
36         return tmp;
37     }
38 }
39 
countOnlineUsers() const40 int QueueItem::countOnlineUsers() const {
41     int n = 0;
42     for(auto i = sources.begin(), iend = sources.end(); i != iend; ++i) {
43         if(i->getUser().user->isOnline())
44         n++;
45     }
46     return n;
47 }
48 
getOnlineUsers(HintedUserList & l) const49 void QueueItem::getOnlineUsers(HintedUserList& l) const {
50     for(auto i = sources.begin(), iend = sources.end(); i != iend; ++i)
51         if(i->getUser().user->isOnline())
52             l.push_back(i->getUser());
53 }
54 
addSource(const HintedUser & aUser)55 void QueueItem::addSource(const HintedUser& aUser) {
56     dcassert(!isSource(aUser.user));
57     auto i = getBadSource(aUser);
58     if(i != badSources.end()) {
59         sources.push_back(*i);
60         badSources.erase(i);
61     } else {
62         sources.push_back(Source(aUser));
63     }
64 }
65 
removeSource(const UserPtr & aUser,int reason)66 void QueueItem::removeSource(const UserPtr& aUser, int reason) {
67     auto i = getSource(aUser);
68     dcassert(i != sources.end());
69     i->setFlag(reason);
70     badSources.push_back(*i);
71     sources.erase(i);
72 }
73 
getTempTarget()74 const string& QueueItem::getTempTarget() {
75     if(!isSet(QueueItem::FLAG_USER_LIST) && tempTarget.empty()) {
76         if(!SETTING(TEMP_DOWNLOAD_DIRECTORY).empty() && (File::getSize(getTarget()) == -1)) {
77             string tmp;
78 #ifdef _WIN32
79             dcpp::StringMap sm;
80             if(target.length() >= 3 && target[1] == ':' && target[2] == '\\')
81                 sm["targetdrive"] = target.substr(0, 3);
82             else
83                 sm["targetdrive"] = Util::getPath(Util::PATH_USER_LOCAL).substr(0, 3);
84             if (SETTING(NO_USE_TEMP_DIR))
85                 tmp = Util::formatParams(target, sm, false) + getTempName("", getTTH());
86             else
87                 tmp = Util::formatParams(SETTING(TEMP_DOWNLOAD_DIRECTORY), sm, false) + getTempName(getTargetFileName(), getTTH());
88 #else //_WIN32
89             if (SETTING(NO_USE_TEMP_DIR))
90                 tmp = target + getTempName("", getTTH());
91             else
92                 tmp = SETTING(TEMP_DOWNLOAD_DIRECTORY) + getTempName(getTargetFileName(), getTTH());
93 #endif //_WIN32
94             int len = Util::getFileName(tmp).size()+1;
95             if (len >= 255){
96                 string tmp1 = tmp.erase(tmp.find(".dctmp")-33,tmp.find(".dctmp")+5) + ".dctmp";
97                 tmp = tmp1;
98             }
99             len = Util::getFileName(tmp).size()+1;
100             if (len >= 255) {
101                 char tmp3[206];
102                 string tmp2 = tmp.erase(tmp.find(".dctmp")-7,tmp.find(".dctmp")+5);
103                 memcpy (tmp3, Util::getFileName(tmp2).c_str(), 206);
104                 tmp = Util::getFilePath(tmp) + string(tmp3) + "~." + getTTH().toBase32() + ".dctmp";
105             }
106             setTempTarget(tmp);
107         }
108     }
109     return tempTarget;
110 }
111 
getNextSegment(int64_t blockSize,int64_t wantedSize,int64_t lastSpeed,const PartialSource::Ptr partialSource) const112 Segment QueueItem::getNextSegment(int64_t blockSize, int64_t wantedSize, int64_t lastSpeed, const PartialSource::Ptr partialSource) const {
113     if(getSize() == -1 || blockSize == 0) {
114         return Segment(0, -1);
115     }
116 
117     if(!BOOLSETTING(SEGMENTED_DL)) {
118         if(!downloads.empty()) {
119             return Segment(0, 0);
120         }
121 
122         int64_t start = 0;
123         int64_t end = getSize();
124 
125         if(!done.empty()) {
126             const Segment& first = *done.begin();
127 
128             if(first.getStart() > 0) {
129                 end = Util::roundUp(first.getStart(), blockSize);
130             } else {
131                 start = Util::roundDown(first.getEnd(), blockSize);
132 
133                 if(done.size() > 1) {
134                     const Segment& second = *(++done.begin());
135                     end = Util::roundUp(second.getStart(), blockSize);
136                 }
137             }
138         }
139 
140         return Segment(start, std::min(getSize(), end) - start);
141     }
142 
143 
144     /* added for PFS */
145     vector<int64_t> posArray;
146     vector<Segment> neededParts;
147 
148     if(partialSource) {
149         posArray.reserve(partialSource->getPartialInfo().size());
150 
151         // Convert block index to file position
152         for(PartsInfo::const_iterator i = partialSource->getPartialInfo().begin(); i != partialSource->getPartialInfo().end(); ++i)
153             posArray.push_back(min(getSize(), (int64_t)(*i) * blockSize));
154     }
155 
156     /***************************/
157 
158     double donePart = static_cast<double>(getDownloadedBytes()) / getSize();
159 
160     // We want smaller blocks at the end of the transfer, squaring gives a nice curve...
161     int64_t targetSize = SETTING(SEGMENT_SIZE) > 0 ? (int64_t)(SETTING(SEGMENT_SIZE)*1024*1024) : wantedSize * std::max(0.25, (1. - (donePart * donePart)));
162 
163     if(targetSize > blockSize) {
164         // Round off to nearest block size
165                 targetSize = Util::roundDown(targetSize, blockSize);
166     } else {
167         targetSize = blockSize;
168     }
169 
170     int64_t start = 0;
171     int64_t curSize = targetSize;
172 
173     while(start < getSize()) {
174         int64_t end = std::min(getSize(), start + curSize);
175         Segment block(start, end - start);
176         bool overlaps = false;
177         for(auto i = done.begin(); !overlaps && i != done.end(); ++i) {
178             if(curSize <= blockSize) {
179                 int64_t dstart = i->getStart();
180                 int64_t dend = i->getEnd();
181                 // We accept partial overlaps, only consider the block done if it is fully consumed by the done block
182                 if(dstart <= start && dend >= end) {
183                     overlaps = true;
184                 }
185             } else {
186                 overlaps = block.overlaps(*i);
187             }
188         }
189 
190         for(auto i = downloads.begin(); !overlaps && i !=downloads.end(); ++i) {
191             overlaps = block.overlaps((*i)->getSegment());
192         }
193 
194         if(!overlaps) {
195             if(partialSource) {
196                 // store all chunks we could need
197                 for(vector<int64_t>::const_iterator j = posArray.begin(); j < posArray.end(); j += 2){
198                     if( (*j <= start && start < *(j+1)) || (start <= *j && *j < end) ) {
199                         int64_t b = max(start, *j);
200                         int64_t e = min(end, *(j+1));
201 
202                         // segment must be blockSize aligned
203                         dcassert(b % blockSize == 0);
204                         dcassert(e % blockSize == 0 || e == getSize());
205 
206                         bool merged = false;
207                         if(!neededParts.empty())
208                         {
209                             Segment& prev = neededParts.back();
210                             if(b == prev.getEnd() && e > prev.getEnd())
211                             {
212                                  prev.setSize(prev.getSize() + (e - b));
213                                  merged = true;
214                             }
215                         }
216 
217                         if(!merged)
218                             neededParts.push_back(Segment(b, e - b));
219                     }
220                 }
221             } else {
222                 return block;
223             }
224         }
225 
226         if(!partialSource && curSize > blockSize) {
227             curSize -= blockSize;
228         } else {
229             start = end;
230             curSize = targetSize;
231         }
232     }
233 
234     if(!neededParts.empty()) {
235         // select random chunk for PFS
236         dcdebug("Found partial chunks: %d\n", static_cast<int>(neededParts.size()));
237 
238         Segment& selected = neededParts[Util::rand(0, neededParts.size())];
239         selected.setSize(std::min(selected.getSize(), targetSize));     // request only wanted size
240 
241         return selected;
242     }
243 
244     if(partialSource == NULL && BOOLSETTING(OVERLAP_CHUNKS) && lastSpeed > 0) {
245         // overlap slow running chunk
246 
247         for(auto i = downloads.begin(); i != downloads.end(); ++i) {
248             Download* d = *i;
249 
250             // current chunk mustn't be already overlapped
251             if(d->getOverlapped())
252                     continue;
253 
254             // current chunk must be running at least for 2 seconds
255             if(d->getStart() == 0 || GET_TIME() - d->getStart() < 2000)
256                     continue;
257 
258             // current chunk mustn't be finished in next 10 seconds
259             if(d->getSecondsLeft() < 10)
260                     continue;
261 
262             // overlap current chunk at last block boundary
263             int64_t pos = d->getPos() - (d->getPos() % blockSize);
264             int64_t size = d->getSize() - pos;
265 
266             // new user should finish this chunk more than 2x faster
267             int64_t newChunkLeft = size / lastSpeed;
268             if(2 * newChunkLeft < d->getSecondsLeft()) {
269                 dcdebug("Overlapping... old user: %I64d s, new user: %I64d s\n", static_cast<int>(d->getSecondsLeft()), static_cast<int>(newChunkLeft));
270                 return Segment(d->getStartPos() + pos, size/*, true*/);//TODO bool
271             }
272         }
273     }
274 
275 
276     return Segment(0, 0);
277 }
278 
getDownloadedBytes() const279 int64_t QueueItem::getDownloadedBytes() const {
280     int64_t total = 0;
281     for(auto i = done.begin(); i != done.end(); ++i) {
282         total += i->getSize();
283     }
284     return total;
285 }
286 
addSegment(const Segment & segment)287 void QueueItem::addSegment(const Segment& segment) {
288     done.insert(segment);
289 
290     // Consolidate segments
291     if(done.size() == 1)
292         return;
293 
294     for(auto i = ++done.begin() ; i != done.end(); ) {
295         auto prev = i;
296         --prev;
297         if(prev->getEnd() >= i->getStart()) {
298             Segment big(prev->getStart(), i->getEnd() - prev->getStart());
299             done.erase(prev);
300             done.erase(i++);
301             done.insert(big);
302         } else {
303             ++i;
304         }
305     }
306 }
307 //Partial
isNeededPart(const PartsInfo & partsInfo,int64_t blockSize)308 bool QueueItem::isNeededPart(const PartsInfo& partsInfo, int64_t blockSize)
309 {
310     dcassert(partsInfo.size() % 2 == 0);
311 
312     SegmentConstIter i  = done.begin();
313     for(PartsInfo::const_iterator j = partsInfo.begin(); j != partsInfo.end(); j+=2) {
314         while(i != done.end() && (*i).getEnd() <= (*j) * blockSize)
315             ++i;
316 
317         if(i == done.end() || !((*i).getStart() <= (*j) * blockSize && (*i).getEnd() >= (*(j+1)) * blockSize))
318                 return true;
319     }
320 
321     return false;
322 
323 }
324 
getPartialInfo(PartsInfo & partialInfo,int64_t blockSize) const325 void QueueItem::getPartialInfo(PartsInfo& partialInfo, int64_t blockSize) const {
326     size_t maxSize = min(done.size() * 2, (size_t)510);
327     partialInfo.reserve(maxSize);
328 
329     SegmentConstIter i = done.begin();
330     for(; i != done.end() && partialInfo.size() < maxSize; ++i) {
331 
332         uint16_t s = (uint16_t)((*i).getStart() / blockSize);
333         uint16_t e = (uint16_t)(((*i).getEnd() - 1) / blockSize + 1);
334 
335         partialInfo.push_back(s);
336         partialInfo.push_back(e);
337     }
338 }
339 
340 }
341