/* * peer.h - Legacy single-peer ABI, now a thin compatibility shim over the * multi-peer engine (engine.h). A peer_handle is a private 1-loop / 1-torrent / * 1-peer engine; the semantics below are unchanged so existing callers and * tests keep working. New code should use engine.h directly. */ #ifndef TORRENT_PEER_H #define TORRENT_PEER_H #include #include "engine.h" /* PEER_BLOCK_SIZE, peer_state, peer_error */ #ifdef __cplusplus extern "C" { #endif /* * Immutable configuration passed to peer_create(). The arrays are copied, so * the caller need not keep them alive afterwards. */ typedef struct { uint8_t info_hash[20]; /* torrent info-hash (BitTorrent v1, SHA-1) */ uint8_t peer_id[20]; /* our 20-byte peer id */ uint64_t piece_length; /* bytes per piece (last piece may be shorter) */ uint64_t total_size; /* total torrent payload size */ uint32_t num_pieces; /* number of pieces */ uint32_t num_slots; /* arena depth in 16 KiB slots (0 => default) */ uint32_t max_pipeline; /* cap on outstanding block requests (0 => def) */ uint32_t request_timeout_ms; /* re-request a block after this long with no * reply (0 => default). Guards against silent * request drops. */ uint32_t recv_buffer_bytes; /* SO_RCVBUF override; 0 => leave kernel * autotuning alone (recommended). */ } peer_config; /* * One received block. The payload lives at: * (uint8_t*)peer_arena_base(h) + (uint64_t)slot * PEER_BLOCK_SIZE * and stays valid until the slot is returned with peer_release_slot(). * 16 bytes, trivially copyable, no pointers (ABI/relocation friendly). */ typedef struct { uint32_t piece; /* piece index */ uint32_t begin; /* byte offset of this block within the piece */ uint32_t len; /* block length in bytes (<= PEER_BLOCK_SIZE) */ uint32_t slot; /* arena slot holding the payload */ } block_desc; /* Snapshot of peer progress; filled by peer_get_status(). */ typedef struct { int32_t state; /* peer_state */ int32_t error; /* peer_error */ uint64_t bytes_received; /* total payload bytes delivered to ready ring*/ uint64_t blocks_received; /* total blocks delivered */ uint32_t outstanding; /* requests sent but not yet received */ uint32_t free_slots; /* arena slots currently available */ uint32_t pipeline_target; /* current adaptive in-flight target (blocks) */ double rate_bps; /* EWMA download rate, bytes/sec */ double rtt_min_ms; /* smallest observed request->block RTT */ } peer_status; typedef struct peer_handle peer_handle; /* Lifecycle ------------------------------------------------------------- */ /* Allocate a peer and its arena/rings. Returns NULL on bad config or OOM. */ peer_handle *peer_create(const peer_config *cfg); /* Connect + handshake on a new network thread. ip is dotted-quad IPv4. * Returns 0 if the thread launched, negative on immediate failure. The actual * connection result surfaces asynchronously via peer_get_status(). */ int peer_start(peer_handle *h, const char *ip, uint16_t port); /* * Piece selection is priority-driven. The harness supplies one priority byte * per piece; the peer always works on the highest-priority piece that * (a) the remote peer actually HAS (per its bitfield/have messages), and * (b) has not already been fully requested, * breaking ties toward the lowest piece index. Priority 0 means "do not * request". Re-evaluation happens at every piece boundary, so updating * priorities while running steers the download live (sequential, rarest-first, * deadline ramps, or any mix). The peer never assumes the remote has a piece it * has not advertised, which is the key fix over a fixed in-order schedule. */ /* Replace the whole priority vector. `count` must equal num_pieces. Does not * touch the "already requested" state. Thread-safe. Returns 0, or negative if * count != num_pieces. */ int peer_set_priorities(peer_handle *h, const uint8_t *priorities, uint32_t count); /* Update one piece's priority. Thread-safe. Negative if out of range. */ int peer_set_priority(peer_handle *h, uint32_t piece_index, uint8_t priority); /* Re-arm a piece for (re-)download, clearing its internal "already requested" * mark so it becomes selectable again. Newly created peers start fully armed, * so this is only needed to retry a piece that failed hash verification. * Thread-safe. Negative if out of range. */ int peer_request_piece(peer_handle *h, uint32_t piece_index); /* Stop the network thread and close the socket. Idempotent. */ void peer_stop(peer_handle *h); /* Free the peer and its arena. The arena pointer is invalid afterwards. */ void peer_destroy(peer_handle *h); /* Data plane ------------------------------------------------------------ */ /* Base of the block arena. Wrap [base, base+peer_arena_bytes()) in a Python * memoryview once and slice it per block for zero-copy reads. */ void *peer_arena_base(const peer_handle *h); uint64_t peer_arena_bytes(const peer_handle *h); /* Drain up to max completed blocks into out[]. Returns the count (0..max), * never blocks. Call from the single harness consumer thread. */ uint32_t peer_poll_ready(peer_handle *h, block_desc *out, uint32_t max); /* Return a consumed slot so the peer can reuse it. This is what unblocks new * requests (credit-based flow control). */ void peer_release_slot(peer_handle *h, uint32_t slot); /* Block until ready blocks are available or timeout_ms elapses (negative = * wait forever). Returns 1 if readable, 0 on timeout, negative on error. * Optional: callers may instead poll peer_poll_ready() directly. */ int peer_wait(peer_handle *h, int timeout_ms); /* Fill *out with a status snapshot. */ void peer_get_status(const peer_handle *h, peer_status *out); #ifdef __cplusplus } #endif #endif /* TORRENT_PEER_H */