129 using CompleteHandler = std::function<void(uint64_t
id,
bool success,
const std::string& path)>;
146 void accept(
const PeerId& from, uint64_t
id,
const std::string& dest_path);
171 using clock = std::chrono::steady_clock;
172 clock::time_point start{};
173 clock::time_point mark{};
174 uint64_t mark_bytes = 0;
175 double rate_bps = 0.0;
177 void sample(uint64_t bytes, clock::time_point now) {
178 constexpr int64_t kMinMs = 250, kMaxMs = 2000;
179 constexpr double kAlpha = 0.4;
180 if (start == clock::time_point{}) { start = mark = now; mark_bytes = bytes;
return; }
181 const int64_t dt = std::chrono::duration_cast<std::chrono::milliseconds>(now - mark).count();
182 if (dt < kMinMs)
return;
183 if (dt > kMaxMs) { mark = now; mark_bytes = bytes;
return; }
184 const double inst =
static_cast<double>(bytes - mark_bytes) * 1000.0 /
static_cast<double>(dt);
185 rate_bps = rate_bps <= 0.0 ? inst : kAlpha * inst + (1.0 - kAlpha) * rate_bps;
186 mark = now; mark_bytes = bytes;
189 void fill(Progress& p, uint64_t bytes, uint64_t total, clock::time_point now)
const {
190 if (start == clock::time_point{})
return;
191 const auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(now - start);
193 p.transfer_rate_bps = rate_bps;
195 p.average_rate_bps =
static_cast<double>(bytes) * 1000.0 /
static_cast<double>(ms.count());
196 if (rate_bps > 1.0 && total > bytes)
197 p.estimated_time_remaining = std::chrono::milliseconds(
198 static_cast<int64_t
>(
static_cast<double>(total - bytes) / rate_bps * 1000.0));
209 bool is_directory =
false;
210 std::vector<FileEntry> files;
211 std::vector<std::string> sources;
212 uint64_t total_bytes = 0;
215 std::condition_variable cv;
217 uint64_t cur_offset = 0;
218 uint64_t bytes_done = 0;
220 uint32_t files_done = 0;
222 bool worker_active =
false;
223 bool finished =
false;
224 sha256_context_t hash{};
225 std::chrono::steady_clock::time_point last_activity{};
229 struct IncomingFile {
230 std::string relative_path;
232 std::string final_path;
233 std::string temp_path;
234 uint64_t received = 0;
235 bool temp_created =
false;
236 bool sha_known =
false;
237 bool finalized =
false;
238 uint8_t expected_sha[SHA256_HASH_SIZE]{};
244 bool is_directory =
false;
245 std::string dest_root;
246 std::vector<IncomingFile> files;
249 size_t recv_file = 0;
250 uint64_t bytes_done = 0;
251 uint64_t last_ack = 0;
252 uint32_t files_done = 0;
254 bool finished =
false;
255 sha256_context_t hash{};
256 uint8_t computed_sha[SHA256_HASH_SIZE]{};
257 std::chrono::steady_clock::time_point last_activity{};
262 void on_message(
const Peer& peer, ByteView payload);
263 void handle_offer(
const PeerId& from, uint64_t
id,
bool is_dir, uint64_t total,
264 std::string name, std::vector<FileEntry> files);
265 void handle_chunk(
const PeerId& from, uint64_t
id, uint32_t fidx, uint64_t offset,
266 uint32_t crc, ByteView data);
267 void handle_file_end(
const PeerId& from, uint64_t
id, uint32_t fidx,
const uint8_t* sha);
270 uint64_t start_send(std::shared_ptr<Outgoing> t);
271 void queue_send(uint64_t
id);
273 void run_send(
const std::shared_ptr<Outgoing>& t);
276 void try_finalize_file(
const std::shared_ptr<Incoming>& t,
size_t file_index);
279 void maintenance_loop();
280 void finish_outgoing(
const std::shared_ptr<Outgoing>& t,
bool success);
281 void finish_incoming(
const std::shared_ptr<Incoming>& t,
bool success,
const std::string& error);
282 void emit_progress(
const std::shared_ptr<Outgoing>& t);
283 void emit_progress(
const std::shared_ptr<Incoming>& t);
285 std::shared_ptr<Outgoing> find_outgoing(uint64_t
id)
const;
286 std::shared_ptr<Incoming> find_incoming(
const PeerId& peer, uint64_t
id)
const;
288 void send_to(
const PeerId& peer,
const Bytes& msg) {
291 void send_simple(
const PeerId& peer, uint8_t op, uint64_t
id);
292 void send_complete(
const PeerId& peer, uint64_t
id,
bool ok);
294 PeerNetwork* network_ =
nullptr;
296 std::atomic<uint64_t> next_id_{1};
297 std::atomic<bool> running_{
false};
303 mutable std::mutex mutex_;
304 std::unordered_map<uint64_t, std::shared_ptr<Outgoing>> outgoing_;
305 std::unordered_map<PeerId, std::unordered_map<uint64_t, std::shared_ptr<Incoming>>,
306 PeerId::Hash> incoming_;
309 std::vector<std::thread> workers_;
310 std::mutex queue_mutex_;
311 std::condition_variable queue_cv_;
312 std::queue<uint64_t> send_queue_;
315 std::thread maintenance_thread_;
316 std::mutex maintenance_mutex_;
317 std::condition_variable maintenance_cv_;
319 mutable std::mutex stats_mutex_;
One file inside a transfer (a single-file transfer has exactly one).
std::string relative_path
POSIX path relative to the transfer root.