nx_avail_map.nx
buildroot/runtime/nx_avail_map.nx
about
nx_avail_map.nx -- shared cross-peer piece-AVAILABILITY map for rarest-first selection.
module: nishi-core.torrent.avail_map
depends: nx_peerwire.nx (nx_pw_bitfield_has -- the peer-has filter),
nx_piece_manager.nx (nx_pm_rarest -- the CANONICAL rarest-first tie-break;
wiring it here un-orphans the smart layer, DRY: one picker),
nx_syscalls.nx (transitive: mmap/openat/lseek/read/write/close/FLOCK).
capability: SWARM_INTELLIGENCE
WHY this organ exists:
The download worker forks one child PER PEER, so each child only knows ITS OWN peer's
bitfield -- there is no in-process view of how rare each piece is across the swarm, which
is exactly the input rarest-first needs. This is that missing cross-PROCESS view: a small
on-disk array of per-piece peer counts that every child contributes to and reads back to
pick the rarest piece IT can actually serve. Replacing the worker's linear stride scan with
this is the Phase-2 "smart layer" rung (operator's rebuild order: reliability -> smart).
CORRECTNESS vs HEURISTIC (deliberate split):
The counts are a SELECTION heuristic, never a data-integrity invariant -- every received
piece is still SHA1-verified against the metadata before it touches disk. So:
* increments (read-modify-write) ARE locked (flock LOCK_EX) -- the proven framed-append
floor idiom; concurrent unlocked RMW silently LOSES counts (gate proves this), which
would bias selection, so we pay the lock on the (rare, once-per-peer) contribution path.
* reads (am_load / am_pick_rarest) are LOCK-FREE -- a momentarily-stale or slightly-torn
count only nudges the ordering, never corrupts data, and the pick is on the hot path.
FILE FORMAT: `npc` bytes; byte p = saturating count (0..255) of pool peers advertising piece p.
One byte/piece keeps even a 10,000-piece torrent's map at 10 KB.
dependencies 2 imports · 3 importers
imports: nx_peerwire.nxnx_piece_manager.nx
imported by: nx_avail_map_gate.nxnx_endgame.nxnx_torrent_seedeval.nx
structs
| none |
consts
| 33 | const AM_SAT: i64 = 255 // per-piece count ceiling (1 byte storage) |
functions
| 36 | func am_open(path: *u8) -> i64 { return __syscall(SYS_OPENAT, AT_FDCWD, path as i64, 0x42, 0x1a4, 0, 0) } |
| 41 | func am_init(path: *u8, npc: i64) -> i64 |
| 54 | func am_add_bitfield(path: *u8, peerbits: *u8, byte_len: i64, npc: i64) -> i64 |
| 78 | func am_add_bitfield_unlocked(path: *u8, peerbits: *u8, byte_len: i64, npc: i64) -> i64 |
| 98 | func am_add_have(path: *u8, piece_idx: i64) -> i64 |
| 115 | func am_load(path: *u8, out_counts: *i64, npc: i64) -> i64 |
| 139 | func am_pick_rarest(path: *u8, donebits: *u8, peerbits: *u8, byte_len: i64, havebits: i64, npc: i64, av: *i64, eff: *i64) -> i64 |