Naut-Peer/src/peer_compat.c
ookami125 d8208685a2 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>
2026-06-21 23:12:32 -04:00

126 lines
3.9 KiB
C

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