2 // This file is part of the aMule Project.
4 // Copyright (c) 2008-2009 aMule Team ( admin@amule.org / http://www.amule.org )
6 // Any parts of this program derived from the xMule, lMule or eMule project,
7 // or contributed by third-party developers are copyrighted by their
10 // This program is free software; you can redistribute it and/or modify
11 // it under the terms of the GNU General Public License as published by
12 // the Free Software Foundation; either version 2 of the License, or
13 // (at your option) any later version.
15 // This program is distributed in the hope that it will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU General Public License for more details.
20 // You should have received a copy of the GNU General Public License
21 // along with this program; if not, write to the Free Software
22 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
27 #include <protocol/ed2k/Constants.h> // for PARTSIZE
31 #include <common/Format.h>
33 void CGapList::Init(uint64 fileSize
, bool isEmpty
)
35 m_filesize
= fileSize
;
36 m_iPartCount
= fileSize
/ PARTSIZE
+ 1;
37 m_sizeLastPart
= fileSize
% PARTSIZE
;
38 // file with size of n * PARTSIZE
39 if (m_sizeLastPart
== 0
40 && fileSize
) { // that's only for pre-init in ctor
41 m_sizeLastPart
= PARTSIZE
;
45 m_partsComplete
.clear();
47 m_partsComplete
.resize(m_iPartCount
, incomplete
);
48 AddGap(0, fileSize
- 1);
50 m_partsComplete
.resize(m_iPartCount
, complete
);
52 m_totalGapSizeValid
= false;
56 void CGapList::AddGap(uint64 gapstart
, uint64 gapend
)
58 if (!ArgCheck(gapstart
, gapend
)) {
62 // AddDebugLogLineN(logPartFile, CFormat(wxT(" AddGap: %5d - %5d")) % gapstart % gapend);
64 // mark involved part(s) as incomplete
65 uint16 partlast
= gapend
/ PARTSIZE
;
66 for (uint16 part
= gapstart
/ PARTSIZE
; part
<= partlast
; part
++) {
67 m_partsComplete
[part
] = incomplete
;
69 // total gap size has to be recalculated
70 m_totalGapSizeValid
= false;
72 // find a place to start:
73 // first gap which ends >= our gap start - 1
74 // (-1 so we can join adjacent gaps)
75 iterator it
= m_gaplist
.lower_bound(gapstart
> 0 ? gapstart
- 1 : 0);
76 while (it
!= m_gaplist
.end()) {
78 uint64 curGapStart
= it2
->second
;
79 uint64 curGapEnd
= it2
->first
;
81 if (curGapStart
>= gapstart
&& curGapEnd
<= gapend
) {
82 // this gap is inside the new gap - delete
84 } else if (curGapStart
>= gapstart
&& curGapStart
<= gapend
+ 1) {
85 // head of this gap is in the new gap, or this gap is
86 // directly behind the new gap - extend limit and delete
89 } else if (curGapEnd
<= gapend
&& curGapEnd
>= gapstart
- 1) {
90 // tail of this gap is in the new gap, or this gap is
91 // directly before the new gap - extend limit and delete
92 gapstart
= curGapStart
;
94 } else if (curGapStart
<= gapstart
&& curGapEnd
>= gapend
) {
95 // new gap is already inside this gap - return
97 // now all cases of overlap are ruled out
98 } else if (curGapStart
> gapstart
) {
99 // this gap is the first behind the new gap -> insert before it
104 // for fastest insertion point to the element AFTER which we want to insert
105 if (it
!= m_gaplist
.begin()) {
108 m_gaplist
.insert(it
, std::pair
<uint64
,uint64
>(gapend
, gapstart
));
111 void CGapList::AddGap(uint16 part
)
113 if (part
>= m_iPartCount
) {
117 uint64 gapstart
= part
* PARTSIZE
;
118 uint64 gapend
= gapstart
+ GetPartSize(part
) - 1;
119 AddGap(gapstart
, gapend
);
120 m_partsComplete
[part
] = incomplete
;
123 void CGapList::FillGap(uint64 partstart
, uint64 partend
)
125 if (!ArgCheck(partstart
, partend
)) {
129 // AddDebugLogLineN(logPartFile, CFormat(wxT(" FillGap: %5d - %5d")) % partstart % partend);
131 // mark involved part(s) to be reexamined for completeness
132 uint16 partlast
= partend
/ PARTSIZE
;
133 for (uint16 part
= partstart
/ PARTSIZE
; part
<= partlast
; part
++) {
134 m_partsComplete
[part
] = unknown
;
136 // also total gap size
137 m_totalGapSizeValid
= false;
139 // find a place to start:
140 // first gap which ends >= our part start
141 iterator it
= m_gaplist
.lower_bound(partstart
);
142 while (it
!= m_gaplist
.end()) {
144 uint64 curGapStart
= it2
->second
;
145 uint64 curGapEnd
= it2
->first
;
147 if (curGapStart
>= partstart
) {
148 if (curGapEnd
<= partend
) {
149 // our part fills this gap completly
150 m_gaplist
.erase(it2
);
151 } else if (curGapStart
<= partend
) {
152 // lower part of this gap is in the part - shrink gap:
153 // (this is the most common case: curGapStart == partstart && curGapEnd > partend)
154 it2
->second
= partend
+ 1;
155 // end of our part was in the gap: we're done
158 // gap starts behind our part end: we're done
162 // curGapStart < partstart
163 if (curGapEnd
> partend
) {
164 // our part is completely enclosed by the gap
165 // cut it in two, leaving our part out:
166 // shrink the gap so it becomes the second gap
167 it2
->second
= partend
+ 1;
168 // insert new first gap
170 if (it3
!= m_gaplist
.begin()) {
173 m_gaplist
.insert(it3
, std::pair
<uint64
,uint64
>(partstart
- 1, curGapStart
));
176 } else if (curGapEnd
>= partstart
) {
177 // upper part of this gap is in the part - shrink gap:
178 // insert shorter gap
180 if (it3
!= m_gaplist
.begin()) {
183 m_gaplist
.insert(it3
, std::pair
<uint64
,uint64
>(partstart
- 1, curGapStart
));
184 // and delete the old one
185 m_gaplist
.erase(it2
);
187 // else: gap is before our part start (should not happen)
192 void CGapList::FillGap(uint16 part
)
194 if (part
>= m_iPartCount
) {
198 uint64 gapstart
= part
* PARTSIZE
;
199 uint64 gapend
= gapstart
+ GetPartSize(part
) - 1;
200 FillGap(gapstart
, gapend
);
201 m_partsComplete
[part
] = complete
;
204 uint64
CGapList::GetGapSize()
206 if (!m_totalGapSizeValid
) {
207 m_totalGapSizeValid
= true;
210 ListType::const_iterator it
= m_gaplist
.begin();
211 for (; it
!= m_gaplist
.end(); it
++) {
212 m_totalGapSize
+= it
->first
- it
->second
+ 1;
215 return m_totalGapSize
;
218 uint32
CGapList::GetGapSize(uint16 part
) const
220 uint64 uRangeStart
= part
* PARTSIZE
;
221 uint64 uRangeEnd
= uRangeStart
+ GetPartSize(part
) - 1;
222 uint64 uTotalGapSize
= 0;
224 // find a place to start:
225 // first gap which ends >= our gap start
226 ListType::const_iterator it
= m_gaplist
.lower_bound(uRangeStart
);
227 for (; it
!= m_gaplist
.end(); ++it
) {
228 uint64 curGapStart
= it
->second
;
229 uint64 curGapEnd
= it
->first
;
231 if (curGapStart
<= uRangeStart
&& curGapEnd
>= uRangeEnd
) {
232 // total range is in this gap
233 uTotalGapSize
+= uRangeEnd
- uRangeStart
+ 1;
235 } else if (curGapStart
>= uRangeStart
) {
236 if (curGapStart
<= uRangeEnd
) {
237 // start of this gap is in our range
238 uTotalGapSize
+= std::min(curGapEnd
, uRangeEnd
) - curGapStart
+ 1;
240 // this gap starts behind our range
243 } else if (curGapEnd
>= uRangeStart
&& curGapEnd
<= uRangeEnd
) {
244 // end of this gap is in our range
245 uTotalGapSize
+= curGapEnd
- uRangeStart
+ 1;
249 wxASSERT( uTotalGapSize
<= uRangeEnd
- uRangeStart
+ 1 );
250 return uTotalGapSize
;
253 bool CGapList::IsComplete(uint64 gapstart
, uint64 gapend
) const
255 if (!ArgCheck(gapstart
, gapend
)) {
259 // find a place to start:
260 // first gap which ends >= our gap start
261 ListType::const_iterator it
= m_gaplist
.lower_bound(gapstart
);
262 for (; it
!= m_gaplist
.end(); ++it
) {
263 uint64 curGapStart
= it
->second
;
264 uint64 curGapEnd
= it
->first
;
266 if ( (curGapStart
>= gapstart
&& curGapEnd
<= gapend
)
267 ||(curGapStart
>= gapstart
&& curGapStart
<= gapend
)
268 ||(curGapEnd
<= gapend
&& curGapEnd
>= gapstart
)
269 ||(gapstart
>= curGapStart
&& gapend
<= curGapEnd
)) {
272 if (curGapStart
> gapend
) {
279 bool CGapList::IsComplete(uint16 part
)
281 // There is a bug in the ED2K protocol:
282 // For files of size n * PARTSIZE one part too much is transmitted in the availability bitfield.
283 // Allow completion detection of this dummy part, and always report it as complete
284 // (so it doesn't get asked for).
285 if (part
== m_iPartCount
&& m_sizeLastPart
== PARTSIZE
) {
288 // Remaining error check
289 if (part
>= m_iPartCount
) {
293 ePartComplete status
= (ePartComplete
) m_partsComplete
[part
];
294 if (status
== unknown
) {
295 uint64 partstart
= part
* PARTSIZE
;
296 uint64 partend
= partstart
+ GetPartSize(part
) - 1;
297 status
= IsComplete(partstart
, partend
) ? complete
: incomplete
;
298 m_partsComplete
[part
] = status
;
300 return status
== complete
;
303 void CGapList::DumpList()
306 for (const_iterator it
= begin(); it
!= end(); ++it
) {
307 AddDebugLogLineN(logPartFile
, CFormat(wxT(" %3d: %5d - %5d")) % i
++ % it
.start() % it
.end());
311 inline bool CGapList::ArgCheck(uint64 gapstart
, uint64
&gapend
) const
313 // end < start: serious error
314 if (gapend
< gapstart
) {
319 // gaps shouldn't go past file anymore either
320 if (gapend
>= m_filesize
) {
322 gapend
= m_filesize
- 1; // fix it