code wiki / _hdl_build / nx_doctor_apply.nx

nx_doctor_apply.nx source

↩ module page · 98 lines · 5913 B

1// nx_doctor_apply.nx -- THE NISHI DOCTOR's "graceful apply WITHOUT competition" core rung. 2// 3// Operator: route a fix through the Doctor so it is applied gracefully without racing a workstream that is 4// concurrently editing the same shared organ. Synthesized from the Nishi Researcher's S-class corpus 5// (knowledge/fetched/doc_*.raw): LOCK (mutual exclusion, axis A: nx_arbiter fl_acquire) + EXACT-CONTEXT-MATCH 6// apply (axes B optimistic/CAS + C 3-way-merge/patch context: apply ONLY where the target region still matches 7// the CURRENT file exactly-once -> a concurrent edit that touched my region makes the match FAIL = REFUSE, never 8// clobber; an edit elsewhere leaves the match intact = apply = a clean merge for non-overlapping edits) + 9// ATOMIC tmp+rename (axis F: no torn write). Fail-closed: any ambiguity (no match / multi-match) -> REFUSE. 10// 11// This does NOT verify or admit its own work (RACI: the Engineer re-verifies, the Warden admits) -- it is the 12// safe APPLY primitive the Doctor's fix queue (next rung) drives one-at-a-time. 13// 14// usage: nx_doctor_apply <target> <oldstr-file> <newstr-file> [requester] 15// reads oldstr/newstr from files so multi-line patches work (NUL-free byte spans). 16// license_tier: ORIGINAL 17import "nx_syscalls.nx" 18import "nx_arbiter.nx" // fl_acquire / fl_release (the mutual-exclusion lease) 19const DA_MAGIC_100000: i64 = 100000 20const DA_MAGIC_1024: i64 = 1024 21 22const DA_APPLIED: i64 = 0 23const DA_CONFLICT: i64 = 1 // target region not found -> already-applied OR changed by another -> NOT clobbered 24const DA_AMBIG: i64 = 2 // oldstr matches >1 -> refuse (will not guess which) 25const DA_ERR: i64 = 3 26const DA_CAP: i64 = 8388608 // 8 MiB bound -- source files are small; NEVER sys_read_file's 4 GiB (fork-hazard) 27const DA_LOCK: *u8 = "doctor_apply" 28 29func da_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 30func da_w(s: *u8) -> i64 { return sys_write(1, s, da_slen(s)) } 31 32// bounded read (NOT sys_read_file). returns bytes, or -1. 33func da_read(path: *u8, buf: *u8, cap: i64) -> i64 { 34 let fd: i64 = sys_openat_rd(path) 35 if fd < 0 { return 0 - 1 } 36 var off: i64 = 0; var go: i64 = 1 37 while go == 1 { if off >= cap { go = 0 } else { let r: i64 = sys_read(fd, buf + off, cap - off); if r <= 0 { go = 0 } else { off = off + r } } } 38 sys_close(fd); return off 39} 40func da_match_at(hay: *u8, i: i64, needle: *u8, nlen: i64) -> i64 { 41 var j: i64 = 0; while j < nlen { if hay[i + j] != needle[j] { return 0 } j = j + 1 } return 1 42} 43// non-overlapping occurrence count of needle in hay[0..hn) 44func da_count(hay: *u8, hn: i64, needle: *u8, nlen: i64) -> i64 { 45 if nlen <= 0 { return 0 } 46 var c: i64 = 0; var i: i64 = 0 47 while i + nlen <= hn { if da_match_at(hay, i, needle, nlen) == 1 { c = c + 1; i = i + nlen } else { i = i + 1 } } 48 return c 49} 50func da_find(hay: *u8, hn: i64, needle: *u8, nlen: i64) -> i64 { 51 if nlen <= 0 { return 0 - 1 } 52 var i: i64 = 0 53 while i + nlen <= hn { if da_match_at(hay, i, needle, nlen) == 1 { return i } i = i + 1 } 54 return 0 - 1 55} 56 57// THE core: apply oldstr->newstr in `target`, conflict-safe + atomic, under the lock. Returns DA_*. 58func doc_apply_mem(target: *u8, oldstr: *u8, oldlen: i64, newstr: *u8, newlen: i64) -> i64 { 59 if oldlen <= 0 { return DA_ERR } 60 let lk: i64 = fl_acquire(DA_LOCK, DA_MAGIC_100000, 1) // mutual exclusion (serialize Doctor-vs-Doctor applies) 61 let buf: *u8 = sys_mmap(DA_CAP) 62 let n: i64 = da_read(target, buf, DA_CAP) 63 if n < 0 { fl_release(lk); return DA_ERR } 64 let cnt: i64 = da_count(buf, n, oldstr, oldlen) 65 if cnt == 0 { fl_release(lk); return DA_CONFLICT } // region gone/changed -> REFUSE (never clobber) 66 if cnt > 1 { fl_release(lk); return DA_AMBIG } // ambiguous -> REFUSE 67 let idx: i64 = da_find(buf, n, oldstr, oldlen) 68 let out: *u8 = sys_mmap(DA_CAP + newlen + 16) 69 var o: i64 = 0; var i: i64 = 0 70 while i < idx { out[o] = buf[i]; o = o + 1; i = i + 1 } // prefix 71 i = 0; while i < newlen { out[o] = newstr[i]; o = o + 1; i = i + 1 } // replacement 72 i = idx + oldlen; while i < n { out[o] = buf[i]; o = o + 1; i = i + 1 } // suffix 73 // atomic publish: write <target>.datmp then rename over target (no torn write; an interrupted write never 74 // leaves a half-file at the live path). 75 let tmp: *u8 = sys_mmap(DA_MAGIC_1024); var t: i64 = 0 76 while target[t] != (0 as u8) { tmp[t] = target[t]; t = t + 1 } 77 let sfx: *u8 = ".datmp"; var s: i64 = 0; while sfx[s] != (0 as u8) { tmp[t] = sfx[s]; t = t + 1; s = s + 1 } tmp[t] = 0 as u8 78 let wfd: i64 = sys_openat_wr(tmp, 0x1a4) 79 if wfd < 0 { fl_release(lk); return DA_ERR } 80 sys_write(wfd, out, o); sys_close(wfd) 81 sys_renameat(tmp, target) 82 fl_release(lk) 83 return DA_APPLIED 84} 85 86func main(argc: i64, argv: *i64) -> i64 { 87 if argc < 4 { da_w("usage: nx_doctor_apply <target> <oldstr-file> <newstr-file> [requester]\n" as *u8); return 2 } 88 let target: *u8 = argv[1] as *u8 89 let oldbuf: *u8 = sys_mmap(DA_CAP); let oldlen: i64 = da_read(argv[2] as *u8, oldbuf, DA_CAP) 90 let newbuf: *u8 = sys_mmap(DA_CAP); let newlen: i64 = da_read(argv[3] as *u8, newbuf, DA_CAP) 91 if oldlen <= 0 { da_w("DOCTOR-APPLY: ERROR empty/unreadable oldstr-file\n" as *u8); return 3 } 92 let code: i64 = doc_apply_mem(target, oldbuf, oldlen, newbuf, newlen) 93 if code == DA_APPLIED { da_w("DOCTOR-APPLY: APPLIED " as *u8); da_w(target); da_w("\n" as *u8) } 94 if code == DA_CONFLICT { da_w("DOCTOR-APPLY: CONFLICT (region not found in current file -> already-applied or changed by another; NOT clobbered)\n" as *u8) } 95 if code == DA_AMBIG { da_w("DOCTOR-APPLY: AMBIGUOUS (oldstr matches >1 -> refused, will not guess)\n" as *u8) } 96 if code == DA_ERR { da_w("DOCTOR-APPLY: ERROR\n" as *u8) } 97 return code 98}