nx_compare_openapi.nx source
↩ module page · 268 lines · 11587 B
1// Added gateway helpers. Requires the nonallocating nx_json_next candidate.
2// Identity fields use unescaped ASCII; unsupported identity forms refuse the document.
3// Other values retain their exact raw bytes, including number precision and unknown schema fields.
4import "nx_json.nx"
5import "nx_compare_gw_lib.nx"
6const CGO_VALUE: i64 = 0
7const CGO_OBJECT_FIRST: i64 = 1
8const CGO_OBJECT_KEY: i64 = 2
9const CGO_OBJECT_COLON: i64 = 3
10const CGO_OBJECT_VALUE: i64 = 4
11const CGO_OBJECT_END: i64 = 5
12const CGO_ARRAY_FIRST: i64 = 6
13const CGO_ARRAY_VALUE: i64 = 7
14const CGO_ARRAY_END: i64 = 8
15
16func cgo_copy(out: *u8, cap: i64, o: i64, src: *u8, start: i64, end: i64) -> i64 {
17 if o < 0 { return 0 - 1 }
18 if start < 0 { return 0 - 1 }
19 if end < start { return 0 - 1 }
20 if o > cap { return 0 - 1 }
21 if end - start > cap - o { return 0 - 1 }
22 var i: i64 = start
23 var p: i64 = o
24 while i < end { out[p] = src[i]; p = p + 1; i = i + 1 }
25 return p
26}
27
28// Validate one complete value using the shared tokenizer. Leave the following token current.
29// The caller provides one byte of grammar state per input byte, so nesting uses no recursive stack.
30func cgo_value_end(t: *NxJsonTok, states: *u8, statecap: i64) -> i64 {
31 var depth: i64 = 1
32 var end: i64 = 0 - 1
33 states[0] = CGO_VALUE as u8
34 while depth > 0 {
35 let st: i64 = states[depth-1] as i64
36 let kind: i64 = t.kind
37 if st == CGO_VALUE {
38 if kind == NX_JSON_LBRACE { states[depth-1] = CGO_OBJECT_FIRST as u8; nx_json_next(t) }
39 else { if kind == NX_JSON_LBRACKET { states[depth-1] = CGO_ARRAY_FIRST as u8; nx_json_next(t) }
40 else {
41 var scalar: i64 = 0
42 if kind == NX_JSON_STRING { scalar = 1 }
43 if kind == NX_JSON_NUMBER { scalar = 1 }
44 if kind == NX_JSON_TRUE { scalar = 1 }
45 if kind == NX_JSON_FALSE { scalar = 1 }
46 if kind == NX_JSON_NULL { scalar = 1 }
47 if scalar == 0 { return 0 - 1 }
48 end = t.pos; depth = depth - 1; nx_json_next(t)
49 } }
50 } else { if st == CGO_OBJECT_FIRST {
51 if kind == NX_JSON_RBRACE { end = t.pos; depth = depth - 1; nx_json_next(t) }
52 else { if kind != NX_JSON_STRING { return 0 - 1 } states[depth-1] = CGO_OBJECT_COLON as u8; nx_json_next(t) }
53 } else { if st == CGO_OBJECT_KEY {
54 if kind != NX_JSON_STRING { return 0 - 1 }
55 states[depth-1] = CGO_OBJECT_COLON as u8; nx_json_next(t)
56 } else { if st == CGO_OBJECT_COLON {
57 if kind != NX_JSON_COLON { return 0 - 1 }
58 states[depth-1] = CGO_OBJECT_VALUE as u8; nx_json_next(t)
59 } else { if st == CGO_OBJECT_VALUE {
60 states[depth-1] = CGO_OBJECT_END as u8
61 if depth >= statecap { return 0 - 1 }
62 states[depth] = CGO_VALUE as u8; depth = depth + 1
63 } else { if st == CGO_OBJECT_END {
64 if kind == NX_JSON_RBRACE { end = t.pos; depth = depth - 1; nx_json_next(t) }
65 else { if kind != NX_JSON_COMMA { return 0 - 1 } states[depth-1] = CGO_OBJECT_KEY as u8; nx_json_next(t) }
66 } else { if st == CGO_ARRAY_FIRST {
67 if kind == NX_JSON_RBRACKET { end = t.pos; depth = depth - 1; nx_json_next(t) }
68 else { states[depth-1] = CGO_ARRAY_END as u8; if depth >= statecap { return 0 - 1 } states[depth] = CGO_VALUE as u8; depth = depth + 1 }
69 } else { if st == CGO_ARRAY_VALUE {
70 states[depth-1] = CGO_ARRAY_END as u8
71 if depth >= statecap { return 0 - 1 }
72 states[depth] = CGO_VALUE as u8; depth = depth + 1
73 } else { if st == CGO_ARRAY_END {
74 if kind == NX_JSON_RBRACKET { end = t.pos; depth = depth - 1; nx_json_next(t) }
75 else { if kind != NX_JSON_COMMA { return 0 - 1 } states[depth-1] = CGO_ARRAY_VALUE as u8; nx_json_next(t) }
76 } else { return 0 - 1 } } } } } } } } }
77 }
78 return end
79}
80
81func cgo_identity(t: *NxJsonTok, dst: *u8, cap: i64) -> i64 {
82 if t.kind != NX_JSON_STRING { return 0 - 1 }
83 let n: i64 = t.tok_len - 2
84 if n < 1 { return 0 - 1 }
85 if n >= cap { return 0 - 1 }
86 var i: i64 = 0
87 while i < n {
88 let ch: i64 = t.src[t.tok_off+1+i] as i64
89 if ch < 32 { return 0 - 1 }
90 if ch > 126 { return 0 - 1 }
91 if ch == 92 { return 0 - 1 }
92 dst[i] = ch as u8; i = i + 1
93 }
94 dst[n] = 0 as u8
95 return n
96}
97
98func cgo_domain_valid(dom: *u8, n: i64) -> i64 {
99 if n < 1 { return 0 }
100 var i: i64 = 0
101 while i < n {
102 let ch: i64 = dom[i] as i64
103 var valid: i64 = 0
104 if ch >= 97 { if ch <= 122 { valid = 1 } }
105 if ch >= 65 { if ch <= 90 { valid = 1 } }
106 if ch >= 48 { if ch <= 57 { valid = 1 } }
107 if ch == 45 { valid = 1 }
108 if ch == 95 { valid = 1 }
109 if valid == 0 { return 0 }
110 i = i + 1
111 }
112 return 1
113}
114
115func cgo_domains(t: *NxJsonTok, level: i64, conf: *u8, cn: i64, out: *u8, cap: i64, offset: i64, name: *u8, namecap: i64) -> i64 {
116 if t.kind != NX_JSON_LBRACKET { return 0 - 1 }
117 var o: i64 = cgo_copy(out, cap, offset, "[" as *u8, 0, 1)
118 var first: i64 = 1
119 nx_json_next(t)
120 if t.kind != NX_JSON_RBRACKET {
121 var more: i64 = 1
122 while more == 1 {
123 let n: i64 = cgo_identity(t, name, namecap)
124 if n < 0 { return 0 - 1 }
125 if cgo_domain_valid(name, n) == 0 { return 0 - 1 }
126 if level >= cg_required(conf, cn, name) {
127 if first == 0 { o = cgo_copy(out, cap, o, "," as *u8, 0, 1) }
128 o = cgo_copy(out, cap, o, t.src, t.tok_off, t.pos)
129 first = 0
130 }
131 nx_json_next(t)
132 if t.kind == NX_JSON_RBRACKET { more = 0 }
133 else { if t.kind != NX_JSON_COMMA { return 0 - 1 } nx_json_next(t) }
134 }
135 }
136 o = cgo_copy(out, cap, o, "]" as *u8, 0, 1)
137 nx_json_next(t)
138 return o
139}
140
141// Root contracts and the existing domain-template route stay visible. Every literal domain is gated.
142func cgo_path_visible(path: *u8, n: i64, level: i64, conf: *u8, cn: i64, dom: *u8, dcap: i64) -> i64 {
143 // OpenAPI Paths Object extensions are metadata, not literal domain routes.
144 if n >= 2 { if path[0] == (120 as u8) { if path[1] == (45 as u8) { return 1 } } }
145 if cg_streq(path, "/compare/api.json" as *u8) == 1 { return 1 }
146 if cg_streq(path, "/compare/openapi.json" as *u8) == 1 { return 1 }
147 let prefix: *u8 = "/compare/" as *u8
148 let pn: i64 = cg_slen(prefix)
149 if n <= pn { return 0 - 1 }
150 var i: i64 = 0
151 while i < pn { if path[i] != prefix[i] { return 0 - 1 } i = i + 1 }
152 var d: i64 = 0
153 while i < n {
154 if path[i] == (47 as u8) { break }
155 if d >= dcap - 1 { return 0 - 1 }
156 dom[d] = path[i]; d = d + 1; i = i + 1
157 }
158 dom[d] = 0 as u8
159 if cg_streq(dom, "{domain}" as *u8) == 1 { return 1 }
160 if cgo_domain_valid(dom, d) == 0 { return 0 - 1 }
161 if level >= cg_required(conf, cn, dom) { return 1 }
162 return 0
163}
164
165func cgo_paths(t: *NxJsonTok, level: i64, conf: *u8, cn: i64, out: *u8, cap: i64, offset: i64, name: *u8, namecap: i64, dom: *u8, dcap: i64, states: *u8, statecap: i64) -> i64 {
166 if t.kind != NX_JSON_LBRACE { return 0 - 1 }
167 var o: i64 = cgo_copy(out, cap, offset, "{" as *u8, 0, 1)
168 var first: i64 = 1
169 nx_json_next(t)
170 if t.kind != NX_JSON_RBRACE {
171 var more: i64 = 1
172 while more == 1 {
173 let start: i64 = t.tok_off
174 let n: i64 = cgo_identity(t, name, namecap)
175 if n < 0 { return 0 - 1 }
176 let visible: i64 = cgo_path_visible(name, n, level, conf, cn, dom, dcap)
177 if visible < 0 { return 0 - 1 }
178 if nx_json_next(t) != NX_JSON_COLON { return 0 - 1 }
179 nx_json_next(t)
180 let end: i64 = cgo_value_end(t, states, statecap)
181 if end < 0 { return 0 - 1 }
182 if visible == 1 {
183 if first == 0 { o = cgo_copy(out, cap, o, "," as *u8, 0, 1) }
184 o = cgo_copy(out, cap, o, t.src, start, end); first = 0
185 }
186 if t.kind == NX_JSON_RBRACE { more = 0 }
187 else { if t.kind != NX_JSON_COMMA { return 0 - 1 } nx_json_next(t) }
188 }
189 }
190 o = cgo_copy(out, cap, o, "}" as *u8, 0, 1)
191 nx_json_next(t)
192 return o
193}
194
195func cgo_project_tokens(t: *NxJsonTok, level: i64, conf: *u8, cn: i64, out: *u8, cap: i64, name: *u8, namecap: i64, dom: *u8, dcap: i64, states: *u8, statecap: i64) -> i64 {
196 if nx_json_next(t) != NX_JSON_LBRACE { return 0 - 1 }
197 var o: i64 = cgo_copy(out, cap, 0, "{" as *u8, 0, 1)
198 var first: i64 = 1
199 var inventory: i64 = 0
200 var paths: i64 = 0
201 var version: i64 = 0
202 nx_json_next(t)
203 var more: i64 = 1
204 while more == 1 {
205 let start: i64 = t.tok_off
206 if cgo_identity(t, name, namecap) < 0 { return 0 - 1 }
207 var mode: i64 = 0
208 if cg_streq(name, "x-available-domains" as *u8) == 1 { mode = 1; inventory = inventory + 1; if inventory > 1 { return 0 - 1 } }
209 if cg_streq(name, "paths" as *u8) == 1 { mode = 2; paths = paths + 1; if paths > 1 { return 0 - 1 } }
210 if cg_streq(name, "openapi" as *u8) == 1 { version = version + 1; if version > 1 { return 0 - 1 } }
211 if nx_json_next(t) != NX_JSON_COLON { return 0 - 1 }
212 let prefixend: i64 = t.pos
213 nx_json_next(t)
214 if first == 0 { o = cgo_copy(out, cap, o, "," as *u8, 0, 1) }
215 first = 0
216 if mode == 0 {
217 let end: i64 = cgo_value_end(t, states, statecap)
218 if end < 0 { return 0 - 1 }
219 o = cgo_copy(out, cap, o, t.src, start, end)
220 } else {
221 o = cgo_copy(out, cap, o, t.src, start, prefixend)
222 if mode == 1 { o = cgo_domains(t, level, conf, cn, out, cap, o, dom, dcap) }
223 if mode == 2 { o = cgo_paths(t, level, conf, cn, out, cap, o, name, namecap, dom, dcap, states, statecap) }
224 }
225 if o < 0 { return 0 - 1 }
226 if t.kind == NX_JSON_RBRACE { more = 0 }
227 else { if t.kind != NX_JSON_COMMA { return 0 - 1 } nx_json_next(t) }
228 }
229 if inventory != 1 { return 0 - 1 }
230 if paths != 1 { return 0 - 1 }
231 if version != 1 { return 0 - 1 }
232 o = cgo_copy(out, cap, o, "}" as *u8, 0, 1)
233 if nx_json_next(t) != NX_JSON_EOF { return 0 - 1 }
234 return o
235}
236
237func cgo_project(src: *u8, n: i64, level: i64, conf: *u8, cn: i64, out: *u8, cap: i64, name: *u8, namecap: i64, dom: *u8, dcap: i64) -> i64 {
238 if n <= 0 { return 0 - 1 }
239 if n > cap { return 0 - 1 }
240 let states: *u8 = sys_mmap(n)
241 if (states as i64) <= 0 { return 0 - 1 }
242 let t: *NxJsonTok = nx_json_new(src, n)
243 if (t as i64) <= 0 { sys_munmap(states, n); return 0 - 1 }
244 let result: i64 = cgo_project_tokens(t, level, conf, cn, out, cap, name, namecap, dom, dcap, states, n)
245 sys_munmap(t as *u8, NX_JSONTOK_BYTES)
246 sys_munmap(states, n)
247 return result
248}
249
250// Unlike a best-effort asset read, a contract read requires observed EOF and no read error.
251func cgo_read(path: *u8, out: *u8, cap: i64) -> i64 {
252 let fd: i64 = sys_openat_rd(path)
253 if fd < 0 { return 0 - 1 }
254 var total: i64 = 0
255 var result: i64 = 0 - 1
256 var go: i64 = 1
257 while go == 1 {
258 if total >= cap { go = 0 }
259 else {
260 let nr: i64 = sys_read(fd, ((out as i64) + total) as *u8, cap - total)
261 if nr < 0 { go = 0 }
262 if nr == 0 { result = total; go = 0 }
263 if nr > 0 { total = total + nr }
264 }
265 }
266 sys_close(fd)
267 return result
268}