code wiki / (root) / nx_piece_manager.nx

nx_piece_manager.nx source

↩ module page · 75 lines · 2902 B

1// nx_piece_manager.nx -- BitTorrent piece/block manager (bits-up, deterministic). 2// 3// module: nishi-core.torrent.piece_manager 4// depends: nx_syscalls.nx, nx_sha1.nx 5// capability: CORE_COMPUTE 6// wired_status: FULLY_WIRED 7// 8// The download brain: piece/block geometry (16 KiB blocks), per-piece integrity 9// verification (SHA-1 of the assembled piece == the metainfo piece hash; BEP-3), 10// and DETERMINISTIC rarest-first piece selection (lowest-index tie-break -- so a 11// session is bit-for-bit replayable, the determinism exceed thesis). Verified 12// pieces are written to nx_docstore. Reuses sha1 (our crypto); no new primitive. 13 14import "nx_syscalls.nx" 15import "nx_sha1.nx" 16 17const NX_PM_BLOCK_SIZE: i64 = 16384 // 16 KiB standard request block 18 19// ceil(a/b) 20func _pm_ceil_div(a: i64, b: i64) -> i64 { return (a + b - 1) / b } 21 22func nx_pm_num_pieces(total_length: i64, piece_length: i64) -> i64 { 23 if piece_length <= 0 { return 0 } 24 return _pm_ceil_div(total_length, piece_length) 25} 26 27// byte size of piece `index` (last piece is the remainder). 28func nx_pm_piece_size(index: i64, total_length: i64, piece_length: i64) -> i64 { 29 let np: i64 = nx_pm_num_pieces(total_length, piece_length) 30 if index < np - 1 { return piece_length } 31 return total_length - (np - 1) * piece_length 32} 33 34func nx_pm_num_blocks(piece_size: i64) -> i64 { return _pm_ceil_div(piece_size, NX_PM_BLOCK_SIZE) } 35 36// byte size of block `block_index` within a piece of piece_size (last is remainder). 37func nx_pm_block_size(block_index: i64, piece_size: i64) -> i64 { 38 let nb: i64 = nx_pm_num_blocks(piece_size) 39 if block_index < nb - 1 { return NX_PM_BLOCK_SIZE } 40 return piece_size - (nb - 1) * NX_PM_BLOCK_SIZE 41} 42 43// verify an assembled piece against the 20-byte expected SHA-1. Returns 1 if ok. 44func nx_pm_verify_piece(piece_data: *u8, piece_size: i64, expected_hash20: *u8) -> i64 { 45 let h: *u8 = sys_mmap(20) 46 sha1(piece_data, piece_size, h) 47 var i: i64 = 0 48 while i < 20 { if (h[i] as i64) != (expected_hash20[i] as i64) { return 0 } i = i + 1 } 49 return 1 50} 51 52// the 20-byte expected hash for piece `index` from the metainfo `pieces` string. 53func nx_pm_expected_hash(pieces: *u8, index: i64) -> *u8 { 54 return ((pieces as i64) + index * 20) as *u8 55} 56 57// DETERMINISTIC rarest-first: index of the rarest NEEDED piece 58// (have[i]==0 AND availability[i]>0), lowest index on a tie; -1 if none. 59func nx_pm_rarest(availability: *i64, have: *i64, n: i64) -> i64 { 60 var best: i64 = 0 - 1 61 var best_av: i64 = 0 62 var i: i64 = 0 63 while i < n { 64 if have[i] == 0 { 65 if availability[i] > 0 { 66 var take: i64 = 0 67 if best < 0 { take = 1 } 68 else { if availability[i] < best_av { take = 1 } } 69 if take == 1 { best = i; best_av = availability[i] } 70 } 71 } 72 i = i + 1 73 } 74 return best 75}