code wiki / (root) / nx_compare_openapi.nx

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}