code wiki / (root) / nx_pathstripe_test.nx

nx_pathstripe_test.nx source

↩ module page · 90 lines · 3785 B

1// nx_pathstripe_test.nx -- 1:1 KAT for multi-path striping / seamless 2// protection switching (nx_pathstripe.nx). Proves SMPTE-2022-7-class 3// behaviour: with two paths whose losses DISJOINT, every packet is 4// delivered exactly once in order with ZERO recovery time; a packet lost 5// on ALL paths is honestly reported lost (never blocks); duplicates are 6// deduped; and top-K striping picks the best-quality paths. 7// 8// expect_exit: 0 9// license_tier: ORIGINAL 10 11import "nx_pathstripe.nx" 12 13const PW: i64 = 64 // reorder window 14const PN: i64 = 16 // packets in the stream 15 16func dropped(seq: i64, d0: i64, d1: i64, d2: i64) -> i64 { 17 if seq == d0 { return 1 } 18 if seq == d1 { return 1 } 19 if seq == d2 { return 1 } 20 return 0 21} 22// feed one path's stream (all seqs except its 3 drops), draining as we go. 23func feed_path(rx: *StripeRx, slots: *i64, log: *i64, d0: i64, d1: i64, d2: i64) -> i64 { 24 var s: i64 = 0 25 while s < PN { 26 if dropped(s, d0, d1, d2) == 0 { 27 stripe_rx_arrive(rx, slots, PW, s) 28 stripe_rx_drain(rx, slots, PW, log) 29 } 30 s = s + 1 31 } 32 return 0 33} 34func mk_flow(srtt: i64, jit: i64, loss_q16: i64) -> *LinkFlow { 35 let f: *LinkFlow = lq_flow_new() 36 f.srtt_x8 = srtt << LQ_SRTT_SH 37 f.jitter_x16 = jit << LQ_JIT_SH 38 f.loss_frac_q16 = loss_q16 39 return f 40} 41 42func main() -> i64 { 43 let slots: *i64 = sys_mmap(PW * 8) as *i64 44 let log: *i64 = sys_mmap(64 * 8) as *i64 45 let rx: *StripeRx = sys_mmap(STRIPE_RX_BYTES) as *StripeRx 46 47 // ---- T1: DISJOINT drops -> seamless, zero loss, in-order, deduped ---- 48 stripe_rx_init(rx, slots, PW) 49 feed_path(rx, slots, log, 2, 5, 9) // path A drops {2,5,9} 50 feed_path(rx, slots, log, 3, 6, 11) // path B drops {3,6,11} (disjoint) 51 stripe_rx_finalize(rx, slots, PW, PN, log) 52 if rx.delivered != 16 { return 1 } 53 if rx.lost != 0 { return 2 } // every loss covered by the other path 54 var i: i64 = 0 55 while i < PN { if log[i] != i { return 3 } i = i + 1 } // exactly 0..15, once, in order 56 57 // ---- T2: lost on ALL paths -> honestly lost, rest delivered ---- 58 stripe_rx_init(rx, slots, PW) 59 feed_path(rx, slots, log, 5, 5, 5) // path A drops {5} 60 feed_path(rx, slots, log, 5, 5, 5) // path B also drops {5} 61 stripe_rx_finalize(rx, slots, PW, PN, log) 62 if rx.delivered != 15 { return 4 } 63 if rx.lost != 1 { return 5 } 64 if log[5] != 6 { return 6 } // seq 5 skipped -> 6th delivered is seq 6 65 66 // ---- T3: duplicate copies deduped, delivered once ---- 67 stripe_rx_init(rx, slots, PW) 68 if stripe_rx_arrive(rx, slots, PW, 0) != 1 { return 7 } // first copy accepted 69 if stripe_rx_arrive(rx, slots, PW, 0) != 0 { return 8 } // second copy = dup 70 stripe_rx_drain(rx, slots, PW, log) 71 if rx.delivered != 1 { return 9 } 72 73 // ---- T4: top-K striping picks the best-quality paths ---- 74 let flows: *i64 = sys_mmap(4 * 8) as *i64 75 flows[0] = (mk_flow(20, 0, 0)) as i64 // score 96 76 flows[1] = (mk_flow(200, 5, 0)) as i64 // score 50 77 flows[2] = (mk_flow(75, 5, 0)) as i64 // score 75 78 flows[3] = (mk_flow(300, 30, 6554)) as i64 // score 0 79 let topk: *i64 = sys_mmap(4 * 8) as *i64 80 let nk: i64 = stripe_select_topk(flows, 4, 2, topk) 81 if nk != 2 { return 10 } 82 if topk[0] != 0 { return 11 } // best = path0 (96) 83 if topk[1] != 2 { return 12 } // 2nd = path2 (75) 84 if stripe_k_for_loss(0, 4) != 1 { return 13 } 85 if stripe_k_for_loss(3, 4) != 2 { return 14 } 86 if stripe_k_for_loss(10, 4) != 3 { return 15 } 87 88 sys_write(1, "PATHSTRIPE KAT PASS (seamless zero-loss failover on disjoint loss + honest all-lost + dedup + top-K)\n", 100) 89 return 0 90}