nx_comparestale_lib.nx source
↩ module page · 191 lines · 11475 B
1// Consumer and dependency artifacts must be nonempty; matching empty digests are not usable artifacts.
2// One explicitly selected edge. Expected hashes are caller assertions, not qualification receipts.
3// Literal occurrence binds an observation to consumer bytes; it cannot prove runtime reachability.
4import "nx_fio.nx"
5import "nx_sha256.nx"
6import "json_emit.nx"
7
8// Current Nishi ABI: each declared pointer/i64 field occupies one8-byte slot.
9const CD_ABI_WORD_BYTES: i64=8
10const CD_REGION_BYTES: i64=9*CD_ABI_WORD_BYTES
11const CD_FILE_BYTES: i64=6*CD_ABI_WORD_BYTES
12const CD_WRITER_BYTES: i64=5*CD_ABI_WORD_BYTES
13const CD_HASH_CHARS: i64=64
14const CD_I64_MAX: i64=9223372036854775807
15const CD_SCHEMA_FIELDS: i64=32
16const CD_JSON_INTEGER_CHARS: i64=21
17const CD_JSON_ESCAPE_EXPANSION: i64=6
18struct CsDependencyFile {
19 code: i64, bytes: i64, read_bytes: i64, reference_found: i64,
20 hash: *u8, stage: *u8,
21}
22func cd_len(s: *u8) -> i64 { var n: i64=0;while s[n]!=(0 as u8){n=n+1};return n }
23func cd_equal(a: *u8,b: *u8) -> i64 { var i: i64=0;while a[i]==b[i]{if a[i]==(0 as u8){return 1};i=i+1};return 0 }
24func cd_hash_valid(s: *u8) -> i64 {
25 if cd_len(s)!=CD_HASH_CHARS {return 0};var i: i64=0
26 while i<CD_HASH_CHARS {let c: i64=s[i] as i64;if (c<48||c>57)&&(c<97||c>102){return 0};i=i+1};return 1
27}
28func cd_file_init(o: *CsDependencyFile,hex: *u8) -> i64 {
29 o.code=0;o.bytes=0-1;o.read_bytes=0;o.reference_found=0;o.hash=hex;o.hash[0]=0;o.stage="not-observed";return 0
30}
31// KMP keeps literal matching linear and preserves partial matches across transport windows.
32func cd_prefix(token: *u8,n: i64,pi: *i64) -> i64 {
33 if n==0{return 0};pi[0]=0;var i: i64=1;var j: i64=0
34 while i<n {while j>0&&token[i]!=token[j]{j=pi[j-1]};if token[i]==token[j]{j=j+1};pi[i]=j;i=i+1};return 0
35}
36func cd_scan(path: *u8,token: *u8,o: *CsDependencyFile) -> i64 {
37 cd_file_init(o,o.hash)
38 let wn: i64=sha256_workspace_bytes();let tn: i64=cd_len(token)
39 if tn>(CD_I64_MAX-8)/8 {o.code=0-22;o.stage="reference-length";return o.code}
40 let ws: *u8=sys_mmap_shared(wn);let buffer: *u8=sys_mmap_shared(wn)
41 let region: *NxFileReadRegion=sys_mmap_shared(CD_REGION_BYTES) as *NxFileReadRegion
42 let pi: *i64=sys_mmap_shared((tn+1)*8) as *i64
43 if (ws as i64)<=0||(buffer as i64)<=0||(region as i64)<=0||(pi as i64)<=0 {
44 if (ws as i64)>0{sys_munmap(ws,wn)};if (buffer as i64)>0{sys_munmap(buffer,wn)}
45 if (region as i64)>0{sys_munmap(region as *u8,CD_REGION_BYTES)};if (pi as i64)>0{sys_munmap(pi as *u8,(tn+1)*8)}
46 o.code=0-12;o.stage="allocation";return o.code
47 }
48 fio_region_init(region);var rc: i64=sha256_init_workspace(ws,wn)
49 if rc==0 {rc=fio_region_open(path,region)} else {o.stage="hash-workspace"}
50 if rc==0 {
51 o.bytes=region.total
52 if region.total>CD_I64_MAX/SHA256_BITS_PER_BYTE {rc=0-75;o.stage="hash-bit-length-overflow"}
53 }
54 cd_prefix(token,tn,pi);var matched: i64=0
55 while rc==0 {
56 let got: i64=fio_region_next(region,buffer,wn)
57 if got<0{rc=got;break};if got==0{break}
58 sha256_update(ws as *Sha256,buffer,got)
59 var i: i64=0
60 while i<got&&tn>0 {
61 while matched>0&&buffer[i]!=token[matched]{matched=pi[matched-1]}
62 if buffer[i]==token[matched]{matched=matched+1}
63 if matched==tn{o.reference_found=1;matched=pi[matched-1]};i=i+1
64 }
65 }
66 if rc==0 {
67 // Digest output reuses the transport workspace after the last checked read.
68 sha256_final(ws as *Sha256,buffer);let digits: *u8="0123456789abcdef";var i: i64=0
69 while i<SHA256_DIGEST_BYTES {let c: i64=buffer[i] as i64;o.hash[i*2]=digits[c/16];o.hash[i*2+1]=digits[c%16];i=i+1};o.hash[CD_HASH_CHARS]=0
70 }
71 fio_region_close(region);o.read_bytes=region.read_bytes
72 if rc==0&®ion.code!=0{rc=region.code}
73 if cd_equal(o.stage,"not-observed")==1{o.stage=region.stage}
74 var released: i64=0
75 if sys_munmap(pi as *u8,(tn+1)*8)!=0{released=1};if sys_munmap(region as *u8,CD_REGION_BYTES)!=0{released=1}
76 if sys_munmap(buffer,wn)!=0{released=1};if sys_munmap(ws,wn)!=0{released=1}
77 if rc==0&&released==1{rc=0-5;o.stage="workspace-release"}
78 o.code=rc;return rc
79}
80func cd_file_state(o: *CsDependencyFile,expected: *u8) -> *u8 {
81 if o.code==(0-2)&&cd_equal(o.stage,"open")==1{return "MISSING"}
82 if o.code!=0{return "UNOBSERVABLE"}
83 if o.bytes==0{return "EMPTY"}
84 if cd_equal(o.hash,expected)==0{return "IDENTITY_MISMATCH"}
85 return "IDENTITY_MATCH"
86}
87func cd_pair(j: *JsonWriter,key: *u8,value: *u8) -> i64 {
88 if json_emit_key(j,key,cd_len(key))<0{return 0-1};return json_emit_string(j,value,cd_len(value))
89}
90func cd_num(j: *JsonWriter,key: *u8,value: i64) -> i64 {
91 if json_emit_key(j,key,cd_len(key))<0{return 0-1};return json_emit_int(j,value)
92}
93func cd_file_json(j: *JsonWriter,key: *u8,path: *u8,expected: *u8,o: *CsDependencyFile) -> i64 {
94 var rc: i64=json_emit_key(j,key,cd_len(key));rc=rc|json_begin_object(j)
95 rc=rc|cd_pair(j,"path",path);rc=rc|cd_pair(j,"expected_sha256_caller_assertion",expected)
96 rc=rc|cd_pair(j,"observed_sha256",o.hash);rc=rc|cd_pair(j,"state",cd_file_state(o,expected))
97 rc=rc|cd_num(j,"bytes",o.bytes);rc=rc|cd_num(j,"read_bytes",o.read_bytes)
98 rc=rc|cd_pair(j,"io_stage",o.stage);rc=rc|cd_num(j,"io_code",o.code)
99 return rc|json_end_object(j)
100}
101func cd_report(cp: *u8,ch: *u8,dp: *u8,dh: *u8,token: *u8,c: *CsDependencyFile,d: *CsDependencyFile,buffer: *u8,capacity: i64,j: *JsonWriter,prior: *u8) -> i64 {
102 j.buf=buffer;j.pos=0;j.cap=capacity;j.depth=0;j.prior=prior
103 var i: i64=0;while i<JE_MAX_DEPTH{prior[i]=0;i=i+1}
104 var matched: i64=0
105 if cd_equal(cd_file_state(c,ch),"IDENTITY_MATCH")==1&&cd_equal(cd_file_state(d,dh),"IDENTITY_MATCH")==1&&c.reference_found==1{matched=1}
106 var rc: i64=json_begin_object(j)
107 rc=rc|cd_pair(j,"schema","estate-dependency-observation/v1")
108 rc=rc|cd_pair(j,"overall_state","PARTIAL_OBSERVATION")
109 rc=rc|cd_pair(j,"artifact_contract","consumer and dependency must be nonempty")
110 rc=rc|cd_pair(j,"scope","one caller-selected edge; literal presence is not runtime reachability or complete dependency coverage")
111 rc=rc|cd_num(j,"observed_unix",sys_now_realtime_sec())
112 rc=rc|cd_pair(j,"expected_identity_authority","caller assertion; qualified receipt linkage unknown")
113 rc=rc|cd_pair(j,"reference_token",token);rc=rc|cd_num(j,"literal_reference_present",c.reference_found)
114 rc=rc|cd_file_json(j,"consumer",cp,ch,c);rc=rc|cd_file_json(j,"dependency",dp,dh,d)
115 rc=rc|cd_num(j,"eligible_selected_edges",1);rc=rc|cd_num(j,"identity_and_literal_matches",matched)
116 var unknown: i64=0;if cd_equal(cd_file_state(c,ch),"UNOBSERVABLE")==1||cd_equal(cd_file_state(d,dh),"UNOBSERVABLE")==1{unknown=1}
117 rc=rc|cd_num(j,"unobservable_selected_edges",unknown)
118 rc=rc|cd_num(j,"observed_selected_edge_failures",1-matched-unknown)
119 rc=rc|cd_pair(j,"source_build_qualification","UNKNOWN")
120 rc=rc|cd_pair(j,"runtime_compatibility","UNKNOWN")
121 rc=rc|cd_pair(j,"configured_selection","UNKNOWN")
122 rc=rc|cd_pair(j,"executed_identity","UNKNOWN")
123 rc=rc|cd_pair(j,"served_identity","UNKNOWN")
124 rc=rc|cd_pair(j,"complete_dependency_coverage","UNKNOWN")
125 rc=rc|cd_pair(j,"freshness_policy","none applied; no timestamp-derived desired version")
126 rc=rc|cd_pair(j,"next_action","resolve named identity/I/O failures; join qualified receipt and observe actual consumer execution and served dependency")
127 rc=rc|json_end_object(j);if rc<0{return 0-1};return j.pos
128}
129func cd_buffer_command(argc: i64,argv: *i64,owned: *u8,owned_n: i64) -> i64 {
130 if argc!=7{cd_puts("{\"error\":\"usage: nx_comparestale dependency-check consumer-file expected-consumer-sha256 dependency-file expected-dependency-sha256 literal-reference\"}\n");return 2}
131 let cp: *u8=argv[2] as *u8;let ch: *u8=argv[3] as *u8;let dp: *u8=argv[4] as *u8;let dh: *u8=argv[5] as *u8;let token: *u8=argv[6] as *u8
132 if cd_hash_valid(ch)==0||cd_hash_valid(dh)==0||cd_len(token)==0||cd_len(cp)==0||cd_len(dp)==0{cd_puts("{\"error\":\"invalid dependency boundary\"}\n");return 2}
133 let total: i64=cd_len(cp)+cd_len(dp)+cd_len(token)
134 if total>(CD_I64_MAX/2){return 2}
135 // Worst-case escaped input plus fixed schema field/key/value envelope, not a file-size ceiling.
136 let fixed: i64=CD_SCHEMA_FIELDS*(CD_HASH_CHARS+CD_JSON_INTEGER_CHARS)*CD_JSON_ESCAPE_EXPANSION
137 if total>(CD_I64_MAX-fixed)/CD_JSON_ESCAPE_EXPANSION{return 2}
138 let cap: i64=total*CD_JSON_ESCAPE_EXPANSION+fixed
139 let buffer: *u8=sys_mmap_shared(cap);let j: *JsonWriter=sys_mmap_shared(CD_WRITER_BYTES) as *JsonWriter
140 let prior: *u8=sys_mmap_shared(JE_MAX_DEPTH);let c: *CsDependencyFile=sys_mmap_shared(CD_FILE_BYTES) as *CsDependencyFile
141 let d: *CsDependencyFile=sys_mmap_shared(CD_FILE_BYTES) as *CsDependencyFile
142 let hashes: *u8=sys_mmap_shared((CD_HASH_CHARS+1)*2)
143 var exitcode: i64=4
144 if (buffer as i64)>0&&(j as i64)>0&&(prior as i64)>0&&(c as i64)>0&&(d as i64)>0&&(hashes as i64)>0 {
145 cd_file_init(c,hashes);cd_file_init(d,hashes+CD_HASH_CHARS+1)
146 if (owned as i64)>0 {cd_scan_buffer(owned,owned_n,token,c)} else {cd_scan(cp,token,c)};cd_scan(dp,"",d)
147 let n: i64=cd_report(cp,ch,dp,dh,token,c,d,buffer,cap,j,prior)
148 if n>=0 {
149 var wrote: i64=0;while wrote<n{let rc: i64=sys_write(1,buffer+wrote,n-wrote);if rc<=0{break};wrote=wrote+rc}
150 if wrote==n{if sys_write(1,"\n",1)==1{
151 exitcode=0
152 if cd_equal(cd_file_state(c,ch),"UNOBSERVABLE")==1||cd_equal(cd_file_state(d,dh),"UNOBSERVABLE")==1{exitcode=4}else{if cd_equal(cd_file_state(c,ch),"IDENTITY_MATCH")==0||cd_equal(cd_file_state(d,dh),"IDENTITY_MATCH")==0||c.reference_found==0{exitcode=1}}
153 }}
154 }
155 } else {cd_puts("{\"error\":\"dependency observation allocation failed\"}\n")}
156 if (hashes as i64)>0{if sys_munmap(hashes,(CD_HASH_CHARS+1)*2)!=0{exitcode=4}}
157 if (d as i64)>0{if sys_munmap(d as *u8,CD_FILE_BYTES)!=0{exitcode=4}}
158 if (c as i64)>0{if sys_munmap(c as *u8,CD_FILE_BYTES)!=0{exitcode=4}}
159 if (prior as i64)>0{if sys_munmap(prior,JE_MAX_DEPTH)!=0{exitcode=4}}
160 if (j as i64)>0{if sys_munmap(j as *u8,CD_WRITER_BYTES)!=0{exitcode=4}}
161 if (buffer as i64)>0{if sys_munmap(buffer,cap)!=0{exitcode=4}}
162 return exitcode
163}
164
165// Caller owns this byte snapshot for the full check/use interval; no pathname is reopened.
166func cd_scan_buffer(body: *u8,n: i64,token: *u8,o: *CsDependencyFile) -> i64 {
167 cd_file_init(o,o.hash);o.stage="owned-buffer";o.bytes=n
168 if n<0||n>CD_I64_MAX/SHA256_BITS_PER_BYTE||(body as i64)<=0{o.code=0-22;return o.code}
169 let wn: i64=sha256_workspace_bytes();let tn: i64=cd_len(token)
170 if tn>(CD_I64_MAX-8)/8{o.code=0-22;return o.code}
171 let ws: *u8=sys_mmap_shared(wn);let raw: *u8=sys_mmap_shared(SHA256_DIGEST_BYTES)
172 let pi: *i64=sys_mmap_shared((tn+1)*8) as *i64
173 var rc: i64=0-12
174 if (ws as i64)>0&&(raw as i64)>0&&(pi as i64)>0 {
175 rc=sha256_init_workspace(ws,wn)
176 if rc==0 {
177 sha256_update(ws as *Sha256,body,n);sha256_final(ws as *Sha256,raw)
178 let digits: *u8="0123456789abcdef";var i: i64=0
179 while i<SHA256_DIGEST_BYTES{let c: i64=raw[i] as i64;o.hash[i*2]=digits[c/16];o.hash[i*2+1]=digits[c%16];i=i+1};o.hash[CD_HASH_CHARS]=0
180 cd_prefix(token,tn,pi);var matched: i64=0;i=0
181 while i<n&&tn>0{while matched>0&&body[i]!=token[matched]{matched=pi[matched-1]};if body[i]==token[matched]{matched=matched+1};if matched==tn{o.reference_found=1;matched=pi[matched-1]};i=i+1}
182 o.read_bytes=n
183 }
184 }
185 if (pi as i64)>0{if sys_munmap(pi as *u8,(tn+1)*8)!=0{rc=0-5}}
186 if (raw as i64)>0{if sys_munmap(raw,SHA256_DIGEST_BYTES)!=0{rc=0-5}}
187 if (ws as i64)>0{if sys_munmap(ws,wn)!=0{rc=0-5}}
188 o.code=rc;return rc
189}
190func cd_puts(s: *u8) -> i64 {return sys_write(1,s,cd_len(s))}
191func cd_command(argc: i64,argv: *i64) -> i64 {return cd_buffer_command(argc,argv,0 as *u8,0)}