1 /*
2     SPDX-FileCopyrightText: 2005 Joris Guisson <joris.guisson@gmail.com>
3 
4     SPDX-License-Identifier: GPL-2.0-or-later
5 */
6 #ifndef BTCHUNKDOWNLOAD_H
7 #define BTCHUNKDOWNLOAD_H
8 
9 #include <QList>
10 #include <QObject>
11 #include <QSet>
12 #include <diskio/piecedata.h>
13 #include <interfaces/chunkdownloadinterface.h>
14 #include <util/bitset.h>
15 #include <util/ptrmap.h>
16 #include <util/sha1hashgen.h>
17 #include <util/timer.h>
18 
19 namespace bt
20 {
21 class File;
22 class Chunk;
23 class Piece;
24 class Peer;
25 class Request;
26 class PieceDownloader;
27 
28 struct ChunkDownloadHeader {
29     Uint32 index;
30     Uint32 num_bits;
31     Uint32 buffered;
32 };
33 
34 struct PieceHeader {
35     Uint32 piece;
36     Uint32 size;
37     Uint32 mapped;
38 };
39 
40 class DownloadStatus
41 {
42 public:
43     DownloadStatus();
44     ~DownloadStatus();
45 
46     void add(Uint32 p);
47     void remove(Uint32 p);
48     bool contains(Uint32 p);
49     void clear();
50 
timeout()51     void timeout()
52     {
53         timeouts++;
54     }
numTimeouts()55     Uint32 numTimeouts() const
56     {
57         return timeouts;
58     }
59 
60     typedef QSet<Uint32>::iterator iterator;
begin()61     iterator begin()
62     {
63         return status.begin();
64     }
end()65     iterator end()
66     {
67         return status.end();
68     }
69 
70 private:
71     Uint32 timeouts;
72     QSet<Uint32> status;
73 };
74 
75 /**
76  * @author Joris Guisson
77  * @brief Handles the download off one Chunk off a Peer
78  *
79  * This class handles the download of one Chunk.
80  */
81 class KTORRENT_EXPORT ChunkDownload : public QObject, public ChunkDownloadInterface
82 {
83 public:
84     /**
85      * Constructor, set the chunk and the PeerManager.
86      * @param chunk The Chunk
87      */
88     ChunkDownload(Chunk *chunk);
89 
90     ~ChunkDownload() override;
91 
92     /// Get the chunk
getChunk()93     Chunk *getChunk()
94     {
95         return chunk;
96     }
97 
98     /// Get the total number of pieces
getTotalPieces()99     Uint32 getTotalPieces() const
100     {
101         return num;
102     }
103 
104     /// Get the number of pieces downloaded
getPiecesDownloaded()105     Uint32 getPiecesDownloaded() const
106     {
107         return num_downloaded;
108     }
109 
110     /// Get the number of bytes downloaded.
111     Uint32 bytesDownloaded() const;
112 
113     /// Get the index of the chunk
114     Uint32 getChunkIndex() const;
115 
116     /// Get the PeerID of the current peer
117     QString getPieceDownloaderName() const;
118 
119     /// Get the download speed
120     Uint32 getDownloadSpeed() const;
121 
122     /// Get download stats
123     void getStats(Stats &s) override;
124 
125     /// See if a chunkdownload is idle (i.e. has no downloaders)
isIdle()126     bool isIdle() const
127     {
128         return pdown.count() == 0;
129     }
130 
131     /**
132      * A Piece has arived.
133      * @param p The Piece
134      * @param ok Whether or not the piece was needed
135      * @return true If Chunk is complete
136      */
137     bool piece(const Piece &p, bool &ok);
138 
139     /**
140      * Assign the downloader to download from.
141      * @param pd The downloader
142      * @return true if the peer was asigned, false if not
143      */
144     bool assign(PieceDownloader *pd);
145 
146     /**
147      * Release a downloader
148      * @param pd The downloader
149      */
150     void release(PieceDownloader *pd);
151 
152     /**
153      * A PieceDownloader has been killed. We need to remove it.
154      * @param pd The PieceDownloader
155      */
156     void killed(PieceDownloader *pd);
157 
158     /**
159      * Save to a File
160      * @param file The File
161      */
162     void save(File &file);
163 
164     /**
165      * Load from a File
166      * @param file The File
167      * @param hdr Header for the chunk
168      * @param update_hash Whether or not to update the hash
169      */
170     bool load(File &file, ChunkDownloadHeader &hdr, bool update_hash = true);
171 
172     /**
173      * Cancel all requests.
174      */
175     void cancelAll();
176 
177     /**
178      * When a Chunk is downloaded, this function checks if all
179      * pieces are delivered by the same peer and if so returns it.
180      * @return The PieceDownloader or 0 if there is no only peer
181      */
182     PieceDownloader *getOnlyDownloader();
183 
184     /// See if a PieceDownloader is assigned to this chunk
containsPeer(PieceDownloader * pd)185     bool containsPeer(PieceDownloader *pd)
186     {
187         return pdown.contains(pd);
188     }
189 
190     /// See if the download is choked (i.e. all downloaders are choked)
191     bool isChoked() const;
192 
193     /// Release all PD's and clear the requested chunks
194     void releaseAllPDs();
195 
196     /// Send requests to peers
197     void update();
198 
199     /// See if this CD hasn't been active in the last update
needsToBeUpdated()200     bool needsToBeUpdated() const
201     {
202         return timer.getElapsedSinceUpdate() > 60 * 1000;
203     }
204 
205     /// Get the SHA1 hash of the downloaded chunk
getHash()206     SHA1Hash getHash() const
207     {
208         return hash_gen.get();
209     }
210 
211     /// Get the number of downloaders
getNumDownloaders()212     Uint32 getNumDownloaders() const
213     {
214         return pdown.count();
215     }
216 
217 private:
218     void onTimeout(const bt::Request &r);
219     void onRejected(const bt::Request &r);
220 
221     void notDownloaded(const Request &r, bool reject);
222     void updateHash();
223     void sendRequests();
224     bool sendRequest(PieceDownloader *pd);
225     void sendCancels(PieceDownloader *pd);
226     void endgameCancel(const Piece &p);
227     Uint32 bestPiece(PieceDownloader *pd);
228 
229     BitSet pieces;
230     Chunk *chunk;
231     Uint32 num;
232     Uint32 num_downloaded;
233     Uint32 last_size;
234     Timer timer;
235     QList<PieceDownloader *> pdown;
236     PtrMap<PieceDownloader *, DownloadStatus> dstatus;
237     QSet<PieceDownloader *> piece_providers;
238     PieceData::Ptr *piece_data;
239     SHA1HashGen hash_gen;
240     Uint32 num_pieces_in_hash;
241 
242     friend File &operator<<(File &out, const ChunkDownload &cd);
243     friend File &operator>>(File &in, ChunkDownload &cd);
244 };
245 }
246 
247 #endif
248