/* * 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 #include 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; }