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}