Initial commit: multi-peer torrent download engine
Reactor/loop-pool engine with TCP/µTP/MSE transports, per-connection pipelining, priority-driven piece selection with endgame, and the Python FFI test harness. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
commit
d8208685a2
55 changed files with 9989 additions and 0 deletions
126
src/peer_compat.c
Normal file
126
src/peer_compat.c
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
/*
|
||||
* peer_compat.c - Legacy single-peer peer_* ABI implemented as a thin wrapper
|
||||
* over the multi-peer engine. A peer_handle is a private engine configured with
|
||||
* exactly one loop, holding one torrent with one connection. The semantics match
|
||||
* the old standalone peer, so existing callers and tests keep working unchanged.
|
||||
*/
|
||||
#include "../include/peer.h"
|
||||
#include "../include/engine.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
struct peer_handle {
|
||||
engine *eng;
|
||||
uint32_t tid;
|
||||
uint32_t num_pieces;
|
||||
};
|
||||
|
||||
peer_handle *peer_create(const peer_config *cfg) {
|
||||
if (!cfg || cfg->num_pieces == 0 || cfg->piece_length == 0 ||
|
||||
cfg->total_size == 0)
|
||||
return NULL;
|
||||
|
||||
peer_handle *h = calloc(1, sizeof *h);
|
||||
if (!h) return NULL;
|
||||
|
||||
engine_config ec;
|
||||
memset(&ec, 0, sizeof ec);
|
||||
ec.loop_count = 1; /* single loop = single peer */
|
||||
ec.slots_per_loop = cfg->num_slots; /* 0 => engine default */
|
||||
ec.max_pipeline = cfg->max_pipeline;
|
||||
ec.request_timeout_ms = cfg->request_timeout_ms;
|
||||
ec.recv_buffer_bytes = cfg->recv_buffer_bytes;
|
||||
|
||||
h->eng = engine_create(&ec);
|
||||
if (!h->eng) { free(h); return NULL; }
|
||||
|
||||
int32_t id = engine_add_torrent(h->eng, cfg->info_hash, cfg->peer_id,
|
||||
cfg->piece_length, cfg->total_size,
|
||||
cfg->num_pieces);
|
||||
if (id < 0) { engine_destroy(h->eng); free(h); return NULL; }
|
||||
h->tid = (uint32_t)id;
|
||||
h->num_pieces = cfg->num_pieces;
|
||||
return h;
|
||||
}
|
||||
|
||||
int peer_start(peer_handle *h, const char *ip, uint16_t port) {
|
||||
if (!h || !ip) return -1;
|
||||
return engine_add_peer(h->eng, h->tid, ip, port);
|
||||
}
|
||||
|
||||
int peer_set_priorities(peer_handle *h, const uint8_t *priorities, uint32_t count) {
|
||||
if (!h) return -1;
|
||||
return engine_set_priorities(h->eng, h->tid, priorities, count);
|
||||
}
|
||||
|
||||
int peer_set_priority(peer_handle *h, uint32_t piece_index, uint8_t priority) {
|
||||
if (!h) return -1;
|
||||
return engine_set_priority(h->eng, h->tid, piece_index, priority);
|
||||
}
|
||||
|
||||
int peer_request_piece(peer_handle *h, uint32_t piece_index) {
|
||||
if (!h) return -1;
|
||||
return engine_request_piece(h->eng, h->tid, piece_index);
|
||||
}
|
||||
|
||||
void peer_stop(peer_handle *h) {
|
||||
/* The engine has no per-torrent stop; teardown happens in peer_destroy.
|
||||
* Connections idle harmlessly until then. */
|
||||
(void)h;
|
||||
}
|
||||
|
||||
void peer_destroy(peer_handle *h) {
|
||||
if (!h) return;
|
||||
if (h->eng) engine_destroy(h->eng);
|
||||
free(h);
|
||||
}
|
||||
|
||||
void *peer_arena_base(const peer_handle *h) {
|
||||
return engine_arena_base(h->eng, 0);
|
||||
}
|
||||
|
||||
uint64_t peer_arena_bytes(const peer_handle *h) {
|
||||
return engine_arena_bytes(h->eng, 0);
|
||||
}
|
||||
|
||||
uint32_t peer_poll_ready(peer_handle *h, block_desc *out, uint32_t max) {
|
||||
uint32_t total = 0;
|
||||
engine_block tmp[256];
|
||||
while (total < max) {
|
||||
uint32_t want = max - total;
|
||||
if (want > 256) want = 256;
|
||||
uint32_t n = engine_poll_ready(h->eng, tmp, want);
|
||||
for (uint32_t i = 0; i < n; i++) {
|
||||
out[total].piece = tmp[i].piece;
|
||||
out[total].begin = tmp[i].begin;
|
||||
out[total].len = tmp[i].len;
|
||||
out[total].slot = tmp[i].slot; /* single loop => loop index 0 */
|
||||
total++;
|
||||
}
|
||||
if (n < want) break;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
void peer_release_slot(peer_handle *h, uint32_t slot) {
|
||||
engine_release_slot(h->eng, 0, slot);
|
||||
}
|
||||
|
||||
int peer_wait(peer_handle *h, int timeout_ms) {
|
||||
return engine_wait(h->eng, timeout_ms);
|
||||
}
|
||||
|
||||
void peer_get_status(const peer_handle *h, peer_status *out) {
|
||||
torrent_status ts;
|
||||
engine_torrent_status(h->eng, h->tid, &ts);
|
||||
out->state = ts.state;
|
||||
out->error = ts.error;
|
||||
out->bytes_received = ts.bytes_received;
|
||||
out->blocks_received = ts.blocks_received;
|
||||
out->outstanding = ts.outstanding;
|
||||
out->free_slots = ts.free_slots;
|
||||
out->pipeline_target = ts.pipeline_target;
|
||||
out->rate_bps = ts.rate_bps;
|
||||
out->rtt_min_ms = ts.rtt_min_ms;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue