1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
//
// This file is part of the aMule Project.
//
// Copyright (c) 2008-2011 aMule Team ( admin@amule.org / http://www.amule.org )
//
// Any parts of this program derived from the xMule, lMule or eMule project,
// or contributed by third-party developers are copyrighted by their
// respective authors.
//
// 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 St, Fifth Floor, Boston, MA  02110-1301, USA
//


#include "Types.h"
#include <protocol/ed2k/Constants.h>	// for PARTSIZE
#include "GapList.h"

#include "Logger.h"
#include <common/Format.h>

#if defined(_MSC_VER) && (_MSC_VER >= 1800) 
#include <algorithm> // for std::min and std::max 
#endif

void CGapList::Init(uint64 fileSize, bool isEmpty)
{
	m_filesize = fileSize;
	m_iPartCount = fileSize / PARTSIZE + 1;
	m_sizeLastPart = fileSize % PARTSIZE;
	// file with size of n * PARTSIZE
	if (m_sizeLastPart == 0
		&& fileSize) {  // that's only for pre-init in ctor
		m_sizeLastPart = PARTSIZE;
		m_iPartCount--;
	}
	m_gaplist.clear();
	m_partsComplete.clear();
	if (isEmpty) {
		m_partsComplete.resize(m_iPartCount, incomplete);
		AddGap(0, fileSize - 1);
		m_totalGapSize = fileSize;
	} else {
		m_partsComplete.resize(m_iPartCount, complete);
		m_totalGapSize = 0;
	}
	m_totalGapSizeValid = true;
}


void CGapList::AddGap(uint64 gapstart, uint64 gapend)
{
	if (!ArgCheck(gapstart, gapend)) {
		return;
	}

//	AddDebugLogLineN(logPartFile, CFormat(wxT("  AddGap: %5d - %5d")) % gapstart % gapend);

	// mark involved part(s) as incomplete
	uint16 partlast = gapend / PARTSIZE;
	for (uint16 part = gapstart / PARTSIZE; part <= partlast; part++) {
		m_partsComplete[part] = incomplete;
	}
	// total gap size has to be recalculated
	m_totalGapSizeValid = false;

	// find a place to start:
	// first gap which ends >= our gap start - 1
	// (-1 so we can join adjacent gaps)
	iterator it = m_gaplist.lower_bound(gapstart > 0 ? gapstart - 1 : 0);
	while (it != m_gaplist.end()) {
		iterator it2 = it++;
		uint64 curGapStart = it2->second;
		uint64 curGapEnd   = it2->first;

		if (curGapStart >= gapstart && curGapEnd <= gapend) {
			// this gap is inside the new gap - delete
			m_gaplist.erase(it2);
		} else if (curGapStart >= gapstart && curGapStart <= gapend + 1) {
			// head of this gap is in the new gap, or this gap is
			// directly behind the new gap - extend limit and delete
			gapend = curGapEnd;
			m_gaplist.erase(it2);
		} else if (curGapEnd <= gapend && curGapEnd >= gapstart - 1) {
			// tail of this gap is in the new gap, or this gap is
			// directly before the new gap - extend limit and delete
			gapstart = curGapStart;
			m_gaplist.erase(it2);
		} else if (curGapStart <= gapstart && curGapEnd >= gapend) {
			// new gap is already inside this gap - return
			return;
		// now all cases of overlap are ruled out
		} else if (curGapStart > gapstart) {
			// this gap is the first behind the new gap -> insert before it
			it = it2;
			break;
		}
	}
	// for fastest insertion point to the element AFTER which we want to insert
	if (it != m_gaplist.begin()) {
		--it;
	}
	m_gaplist.insert(it, std::pair<uint64,uint64>(gapend, gapstart));
}

void CGapList::AddGap(uint16 part)
{
	if (part >= m_iPartCount) {
		wxFAIL;
		return;
	}
	uint64 gapstart = part * PARTSIZE;
	uint64 gapend = gapstart + GetPartSize(part) - 1;
	AddGap(gapstart, gapend);
	m_partsComplete[part] = incomplete;
}

void CGapList::FillGap(uint64 partstart, uint64 partend)
{
	if (!ArgCheck(partstart, partend)) {
		return;
	}

//	AddDebugLogLineN(logPartFile, CFormat(wxT("  FillGap: %5d - %5d")) % partstart % partend);

	// mark involved part(s) to be reexamined for completeness
	uint16 partlast = partend / PARTSIZE;
	for (uint16 part = partstart / PARTSIZE; part <= partlast; part++) {
		m_partsComplete[part] = unknown;
	}
	// also total gap size
	m_totalGapSizeValid = false;

	// find a place to start:
	// first gap which ends >= our part start
	iterator it = m_gaplist.lower_bound(partstart);
	while (it != m_gaplist.end()) {
		iterator it2 = it++;
		uint64 curGapStart = it2->second;
		uint64 curGapEnd   = it2->first;

		if (curGapStart >= partstart) {
			if (curGapEnd <= partend) {
				// our part fills this gap completely
				m_gaplist.erase(it2);
			} else if (curGapStart <= partend) {
				// lower part of this gap is in the part - shrink gap:
				//   (this is the most common case: curGapStart == partstart && curGapEnd > partend)
				it2->second = partend + 1;
				// end of our part was in the gap: we're done
				break;
			} else {
				// gap starts behind our part end: we're done
				break;
			}
		} else {
			// curGapStart < partstart
			if (curGapEnd > partend) {
				// our part is completely enclosed by the gap
				// cut it in two, leaving our part out:
				// shrink the gap so it becomes the second gap
				it2->second = partend + 1;
				// insert new first gap
				iterator it3(it2);
				if (it3 != m_gaplist.begin()) {
					--it3;
				}
				m_gaplist.insert(it3, std::pair<uint64,uint64>(partstart - 1, curGapStart));
				// we're done
				break;
			} else if (curGapEnd >= partstart) {
				// upper part of this gap is in the part - shrink gap:
				// insert shorter gap
				iterator it3(it2);
				if (it3 != m_gaplist.begin()) {
					--it3;
				}
				m_gaplist.insert(it3, std::pair<uint64,uint64>(partstart - 1, curGapStart));
				// and delete the old one
				m_gaplist.erase(it2);
			}
			// else: gap is before our part start (should not happen)
		}
	}
}

void CGapList::FillGap(uint16 part)
{
	if (part >= m_iPartCount) {
		wxFAIL;
		return;
	}
	uint64 gapstart = part * PARTSIZE;
	uint64 gapend = gapstart + GetPartSize(part) - 1;
	FillGap(gapstart, gapend);
	m_partsComplete[part] = complete;
}

uint64 CGapList::GetGapSize()
{
	if (!m_totalGapSizeValid) {
		m_totalGapSizeValid = true;
		m_totalGapSize = 0;

		ListType::const_iterator it = m_gaplist.begin();
		for (; it != m_gaplist.end(); ++it) {
			m_totalGapSize += it->first - it->second + 1;
		}
	}
	return m_totalGapSize;
}

uint32 CGapList::GetGapSize(uint16 part) const
{
	uint64 uRangeStart = part * PARTSIZE;
	uint64 uRangeEnd = uRangeStart + GetPartSize(part) - 1;
	uint64 uTotalGapSize = 0;

	// find a place to start:
	// first gap which ends >= our gap start
	ListType::const_iterator it = m_gaplist.lower_bound(uRangeStart);
	for (; it != m_gaplist.end(); ++it) {
		uint64 curGapStart = it->second;
		uint64 curGapEnd   = it->first;

		if (curGapStart <= uRangeStart && curGapEnd >= uRangeEnd) {
			// total range is in this gap
			uTotalGapSize += uRangeEnd - uRangeStart + 1;
			break;
		} else if (curGapStart >= uRangeStart) {
			if (curGapStart <= uRangeEnd) {
				// start of this gap is in our range
				uTotalGapSize += std::min(curGapEnd, uRangeEnd) - curGapStart + 1;
			} else {
				// this gap starts behind our range
				break;
			}
		} else if (curGapEnd >= uRangeStart && curGapEnd <= uRangeEnd) {
			// end of this gap is in our range
			uTotalGapSize += curGapEnd - uRangeStart + 1;
		}
	}

	wxASSERT( uTotalGapSize <= uRangeEnd - uRangeStart + 1 );
	return uTotalGapSize;
}

bool CGapList::IsComplete(uint64 gapstart, uint64 gapend) const
{
	if (!ArgCheck(gapstart, gapend)) {
		return false;
	}

	// find a place to start:
	// first gap which ends >= our gap start
	ListType::const_iterator it = m_gaplist.lower_bound(gapstart);
	for (; it != m_gaplist.end(); ++it) {
		uint64 curGapStart = it->second;
		uint64 curGapEnd   = it->first;

		if (  (curGapStart >= gapstart    && curGapEnd   <= gapend)
			||(curGapStart >= gapstart    && curGapStart <= gapend)
			||(curGapEnd   <= gapend      && curGapEnd   >= gapstart)
			||(gapstart    >= curGapStart && gapend      <= curGapEnd)) {
			return false;
		}
		if (curGapStart > gapend) {
			break;
		}
	}
	return true;
}

bool CGapList::IsComplete(uint16 part)
{
// There is a bug in the ED2K protocol:
// For files of size n * PARTSIZE one part too much is transmitted in the availability bitfield.
// Allow completion detection of this dummy part, and always report it as complete
// (so it doesn't get asked for).
	if (part == m_iPartCount && m_sizeLastPart == PARTSIZE) {
		return true;
	}
// Remaining error check
	if (part >= m_iPartCount) {
		wxFAIL;
		return false;
	}
	ePartComplete status = (ePartComplete) m_partsComplete[part];
	if (status == unknown) {
		uint64 partstart = part * PARTSIZE;
		uint64 partend = partstart + GetPartSize(part) - 1;
		status = IsComplete(partstart, partend) ? complete : incomplete;
		m_partsComplete[part] = status;
	}
	return status == complete;
}

inline bool CGapList::ArgCheck(uint64 gapstart, uint64 &gapend) const
{
	// end < start: serious error
	if (gapend < gapstart) {
		wxFAIL;
		return false;
	}

	// gaps shouldn't go past file anymore either
	if (gapend >= m_filesize) {
		wxFAIL;
		gapend = m_filesize - 1;	// fix it
	}
	return true;
}