code wiki / (root) / nx_bencode.nx

nx_bencode.nx source

↩ module page · 134 lines · 5353 B

1// nx_bencode.nx -- bencoding parser (bits-up), the BitTorrent keystone. 2// 3// module: nishi-core.torrent.bencode 4// depends: nx_syscalls.nx 5// capability: CORE_COMPUTE 6// wired_status: FULLY_WIRED 7// 8// genealogy_id: bittorrent_bep0003_bencoding 9// 10// The format ALL of BitTorrent is built on (.torrent metainfo, tracker 11// responses, the peer wire protocol's extended messages): integers (i<d>e), 12// byte strings (<len>:<bytes>), lists (l..e), dicts (d<key><val>..e). Parsing 13// is length-driven (strings carry their length), so a real parser is required 14// (you cannot byte-count 'e's -- strings contain arbitrary bytes). First piece 15// of the sovereign torrent client; the tracker-announce step reuses 16// nx_http_get_ua + nx_net_governor (the primitives extracted this session). 17// Iterative skip (no recursion); offsets returned, no copying. Deterministic. 18 19import "nx_syscalls.nx" 20 21const NX_BC_ERR: i64 = 0 22const NX_BC_INT: i64 = 1 23const NX_BC_STR: i64 = 2 24const NX_BC_LIST: i64 = 3 25const NX_BC_DICT: i64 = 4 26 27func _bc_is_digit(c: i64) -> i64 { if c >= 0x30 { if c <= 0x39 { return 1 } } return 0 } 28 29func _bc_bytes_eq(buf: *u8, off: i64, key: *u8, keylen: i64) -> i64 { 30 var i: i64 = 0 31 while i < keylen { if (buf[off + i] as i64) != (key[i] as i64) { return 0 } i = i + 1 } 32 return 1 33} 34 35// type of the bencoded value at off. 36func nx_bc_type(buf: *u8, off: i64, n: i64) -> i64 { 37 if off >= n { return NX_BC_ERR } 38 let c: i64 = buf[off] as i64 39 if c == 0x69 { return NX_BC_INT } // 'i' 40 if c == 0x6C { return NX_BC_LIST } // 'l' 41 if c == 0x64 { return NX_BC_DICT } // 'd' 42 if _bc_is_digit(c) == 1 { return NX_BC_STR } 43 return NX_BC_ERR 44} 45 46// parse i<digits>e at off; writes value to out_val[0]; returns next offset or -1. 47func nx_bc_int(buf: *u8, off: i64, n: i64, out_val: *i64) -> i64 { 48 if off >= n { return 0 - 1 } 49 if (buf[off] as i64) != 0x69 { return 0 - 1 } 50 var p: i64 = off + 1 51 var neg: i64 = 0 52 if p < n { if (buf[p] as i64) == 0x2D { neg = 1; p = p + 1 } } 53 var v: i64 = 0 54 var dr: i64 = 1 55 while dr == 1 { dr = 0; if p < n { if _bc_is_digit(buf[p] as i64) == 1 { v = v * 10 + ((buf[p] as i64) - 0x30); p = p + 1; dr = 1 } } } 56 if p >= n { return 0 - 1 } 57 if (buf[p] as i64) != 0x65 { return 0 - 1 } // 'e' 58 if neg == 1 { out_val[0] = 0 - v } else { out_val[0] = v } 59 return p + 1 60} 61 62// parse <len>:<bytes> at off; writes byte-offset to out_so[0], length to 63// out_sl[0]; returns next offset or -1. 64func nx_bc_str(buf: *u8, off: i64, n: i64, out_so: *i64, out_sl: *i64) -> i64 { 65 var p: i64 = off 66 var len: i64 = 0 67 var dr: i64 = 1 68 while dr == 1 { dr = 0; if p < n { if _bc_is_digit(buf[p] as i64) == 1 { len = len * 10 + ((buf[p] as i64) - 0x30); p = p + 1; dr = 1 } } } 69 if p >= n { return 0 - 1 } 70 if (buf[p] as i64) != 0x3A { return 0 - 1 } // ':' 71 p = p + 1 72 out_so[0] = p 73 out_sl[0] = len 74 return p + len 75} 76 77// skip the value at off; returns next offset or -1. Iterative (depth counter); 78// strings are skipped by their length so embedded i/l/d/e bytes are safe. 79func nx_bc_skip(buf: *u8, off: i64, n: i64) -> i64 { 80 var p: i64 = off 81 var depth: i64 = 0 82 var run: i64 = 1 83 while run == 1 { 84 run = 0 85 if p < n { 86 let c: i64 = buf[p] as i64 87 if c == 0x69 { // 'i' integer 88 p = p + 1 89 var ir: i64 = 1 90 while ir == 1 { ir = 0; if p < n { if (buf[p] as i64) != 0x65 { p = p + 1; ir = 1 } } } 91 if p < n { p = p + 1 } 92 if depth > 0 { run = 1 } 93 } 94 if c == 0x6C { depth = depth + 1; p = p + 1; run = 1 } // 'l' 95 if c == 0x64 { depth = depth + 1; p = p + 1; run = 1 } // 'd' 96 if c == 0x65 { depth = depth - 1; p = p + 1; if depth > 0 { run = 1 } } // 'e' 97 if _bc_is_digit(c) == 1 { // string 98 var len: i64 = 0 99 var dr: i64 = 1 100 while dr == 1 { dr = 0; if p < n { if _bc_is_digit(buf[p] as i64) == 1 { len = len * 10 + ((buf[p] as i64) - 0x30); p = p + 1; dr = 1 } } } 101 if p < n { if (buf[p] as i64) == 0x3A { p = p + 1 + len } } 102 if depth > 0 { run = 1 } 103 } 104 } 105 } 106 return p 107} 108 109// in the dict at dict_off, find `key`; returns the VALUE's offset, or -1. 110func nx_bc_dict_get(buf: *u8, dict_off: i64, n: i64, key: *u8, keylen: i64) -> i64 { 111 if dict_off >= n { return 0 - 1 } 112 if (buf[dict_off] as i64) != 0x64 { return 0 - 1 } // 'd' 113 var p: i64 = dict_off + 1 114 var run: i64 = 1 115 while run == 1 { 116 run = 0 117 if p < n { 118 if (buf[p] as i64) != 0x65 { // not 'e' 119 let sob: *i64 = sys_mmap(8) as *i64 120 let slb: *i64 = sys_mmap(8) as *i64 121 let p2: i64 = nx_bc_str(buf, p, n, sob, slb) 122 if p2 < 0 { return 0 - 1 } 123 var hit: i64 = 0 124 if slb[0] == keylen { if _bc_bytes_eq(buf, sob[0], key, keylen) == 1 { hit = 1 } } 125 if hit == 1 { return p2 } 126 let p3: i64 = nx_bc_skip(buf, p2, n) 127 if p3 < 0 { return 0 - 1 } 128 p = p3 129 run = 1 130 } 131 } 132 } 133 return 0 - 1 134}