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