xbintsc 0.2.8 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (311) hide show
  1. package/README.md +17 -2
  2. package/README.zh-CN.md +16 -2
  3. package/bin/xbintsc.js +49 -10
  4. package/dist/src/ast/expressions.d.ts +2 -0
  5. package/dist/src/binder/binder.d.ts +24 -98
  6. package/dist/src/binder/binder.js +13 -559
  7. package/dist/src/binder/binder.js.map +1 -1
  8. package/dist/src/binder/declarations.d.ts +16 -0
  9. package/dist/src/binder/declarations.js +168 -0
  10. package/dist/src/binder/declarations.js.map +1 -0
  11. package/dist/src/binder/helpers.d.ts +11 -0
  12. package/dist/src/binder/helpers.js +46 -0
  13. package/dist/src/binder/helpers.js.map +1 -0
  14. package/dist/src/binder/references.d.ts +21 -0
  15. package/dist/src/binder/references.js +349 -0
  16. package/dist/src/binder/references.js.map +1 -0
  17. package/dist/src/binder/types.d.ts +114 -0
  18. package/dist/src/binder/types.js +27 -0
  19. package/dist/src/binder/types.js.map +1 -0
  20. package/dist/src/cli/main.d.ts +1 -0
  21. package/dist/src/cli/main.js +40 -0
  22. package/dist/src/cli/main.js.map +1 -1
  23. package/dist/src/codegen/generator/calls/access.d.ts +13 -0
  24. package/dist/src/codegen/generator/calls/access.js +115 -0
  25. package/dist/src/codegen/generator/calls/access.js.map +1 -0
  26. package/dist/src/codegen/generator/calls/closures.d.ts +11 -0
  27. package/dist/src/codegen/generator/calls/closures.js +50 -0
  28. package/dist/src/codegen/generator/calls/closures.js.map +1 -0
  29. package/dist/src/codegen/generator/calls/invocation.d.ts +18 -0
  30. package/dist/src/codegen/generator/calls/invocation.js +242 -0
  31. package/dist/src/codegen/generator/calls/invocation.js.map +1 -0
  32. package/dist/src/codegen/generator/calls/literals.d.ts +10 -0
  33. package/dist/src/codegen/generator/calls/literals.js +69 -0
  34. package/dist/src/codegen/generator/calls/literals.js.map +1 -0
  35. package/dist/src/codegen/generator/calls/super.d.ts +10 -0
  36. package/dist/src/codegen/generator/calls/super.js +47 -0
  37. package/dist/src/codegen/generator/calls/super.js.map +1 -0
  38. package/dist/src/codegen/generator/calls.d.ts +15 -24
  39. package/dist/src/codegen/generator/calls.js +15 -482
  40. package/dist/src/codegen/generator/calls.js.map +1 -1
  41. package/dist/src/codegen/generator/context.d.ts +1 -1
  42. package/dist/src/codegen/generator/context.js +2 -2
  43. package/dist/src/codegen/generator/context.js.map +1 -1
  44. package/dist/src/codegen/generator/expressions/assignment.d.ts +13 -0
  45. package/dist/src/codegen/generator/expressions/assignment.js +203 -0
  46. package/dist/src/codegen/generator/expressions/assignment.js.map +1 -0
  47. package/dist/src/codegen/generator/expressions/operators.d.ts +14 -0
  48. package/dist/src/codegen/generator/expressions/operators.js +163 -0
  49. package/dist/src/codegen/generator/expressions/operators.js.map +1 -0
  50. package/dist/src/codegen/generator/expressions/primary.d.ts +17 -0
  51. package/dist/src/codegen/generator/expressions/primary.js +260 -0
  52. package/dist/src/codegen/generator/expressions/primary.js.map +1 -0
  53. package/dist/src/codegen/generator/expressions.d.ts +11 -21
  54. package/dist/src/codegen/generator/expressions.js +11 -494
  55. package/dist/src/codegen/generator/expressions.js.map +1 -1
  56. package/dist/src/codegen/generator/module.js +16 -4
  57. package/dist/src/codegen/generator/module.js.map +1 -1
  58. package/dist/src/codegen/generator/state.js +8 -0
  59. package/dist/src/codegen/generator/state.js.map +1 -1
  60. package/dist/src/codegen/generator/statements/control-flow.d.ts +14 -0
  61. package/dist/src/codegen/generator/statements/control-flow.js +199 -0
  62. package/dist/src/codegen/generator/statements/control-flow.js.map +1 -0
  63. package/dist/src/codegen/generator/statements/dispatch.d.ts +12 -0
  64. package/dist/src/codegen/generator/statements/dispatch.js +100 -0
  65. package/dist/src/codegen/generator/statements/dispatch.js.map +1 -0
  66. package/dist/src/codegen/generator/statements/loops.d.ts +13 -0
  67. package/dist/src/codegen/generator/statements/loops.js +152 -0
  68. package/dist/src/codegen/generator/statements/loops.js.map +1 -0
  69. package/dist/src/codegen/generator/statements/variables.d.ts +13 -0
  70. package/dist/src/codegen/generator/statements/variables.js +146 -0
  71. package/dist/src/codegen/generator/statements/variables.js.map +1 -0
  72. package/dist/src/codegen/generator/statements.d.ts +13 -21
  73. package/dist/src/codegen/generator/statements.js +13 -568
  74. package/dist/src/codegen/generator/statements.js.map +1 -1
  75. package/dist/src/codegen/generator/tables.js +7 -0
  76. package/dist/src/codegen/generator/tables.js.map +1 -1
  77. package/dist/src/codegen/values.d.ts +17 -17
  78. package/dist/src/codegen/values.js +21 -57
  79. package/dist/src/codegen/values.js.map +1 -1
  80. package/dist/src/diagnostics/diagnostic.d.ts +1 -0
  81. package/dist/src/diagnostics/diagnostic.js +1 -0
  82. package/dist/src/diagnostics/diagnostic.js.map +1 -1
  83. package/dist/src/diagnostics/source.d.ts +1 -1
  84. package/dist/src/diagnostics/source.js +7 -12
  85. package/dist/src/diagnostics/source.js.map +1 -1
  86. package/dist/src/driver/compiler.d.ts +4 -0
  87. package/dist/src/driver/compiler.js +51 -16
  88. package/dist/src/driver/compiler.js.map +1 -1
  89. package/dist/src/driver/index.d.ts +5 -2
  90. package/dist/src/driver/index.js +5 -2
  91. package/dist/src/driver/index.js.map +1 -1
  92. package/dist/src/driver/paths.d.ts +20 -0
  93. package/dist/src/driver/paths.js +49 -0
  94. package/dist/src/driver/paths.js.map +1 -1
  95. package/dist/src/driver/runtime-lib.d.ts +18 -0
  96. package/dist/src/driver/runtime-lib.js +31 -0
  97. package/dist/src/driver/runtime-lib.js.map +1 -0
  98. package/dist/src/driver/toolchain-download.d.ts +50 -0
  99. package/dist/src/driver/toolchain-download.js +76 -0
  100. package/dist/src/driver/toolchain-download.js.map +1 -0
  101. package/dist/src/driver/toolchain-provider.d.ts +33 -0
  102. package/dist/src/driver/toolchain-provider.js +71 -0
  103. package/dist/src/driver/toolchain-provider.js.map +1 -0
  104. package/dist/src/driver/toolchain.d.ts +9 -4
  105. package/dist/src/driver/toolchain.js +6 -10
  106. package/dist/src/driver/toolchain.js.map +1 -1
  107. package/dist/src/extensions/node/child_process/index.d.ts +4 -2
  108. package/dist/src/extensions/node/child_process/index.js +4 -2
  109. package/dist/src/extensions/node/child_process/index.js.map +1 -1
  110. package/dist/src/extensions/node/events/index.d.ts +10 -0
  111. package/dist/src/extensions/node/events/index.js +22 -0
  112. package/dist/src/extensions/node/events/index.js.map +1 -0
  113. package/dist/src/extensions/node/index.js +6 -0
  114. package/dist/src/extensions/node/index.js.map +1 -1
  115. package/dist/src/extensions/node/querystring/index.d.ts +10 -0
  116. package/dist/src/extensions/node/querystring/index.js +25 -0
  117. package/dist/src/extensions/node/querystring/index.js.map +1 -0
  118. package/dist/src/extensions/node/util/index.d.ts +11 -0
  119. package/dist/src/extensions/node/util/index.js +41 -0
  120. package/dist/src/extensions/node/util/index.js.map +1 -0
  121. package/dist/src/extensions/registry.d.ts +6 -0
  122. package/dist/src/extensions/registry.js.map +1 -1
  123. package/dist/src/index.d.ts +1 -1
  124. package/dist/src/index.js +1 -1
  125. package/dist/src/index.js.map +1 -1
  126. package/dist/src/lexer/scanner/identifiers.d.ts +9 -0
  127. package/dist/src/lexer/scanner/identifiers.js +33 -0
  128. package/dist/src/lexer/scanner/identifiers.js.map +1 -0
  129. package/dist/src/lexer/scanner/numbers.d.ts +10 -0
  130. package/dist/src/lexer/scanner/numbers.js +89 -0
  131. package/dist/src/lexer/scanner/numbers.js.map +1 -0
  132. package/dist/src/lexer/scanner/punctuation.d.ts +9 -0
  133. package/dist/src/lexer/scanner/punctuation.js +151 -0
  134. package/dist/src/lexer/scanner/punctuation.js.map +1 -0
  135. package/dist/src/lexer/scanner/regex.d.ts +11 -0
  136. package/dist/src/lexer/scanner/regex.js +69 -0
  137. package/dist/src/lexer/scanner/regex.js.map +1 -0
  138. package/dist/src/lexer/scanner/strings.d.ts +13 -0
  139. package/dist/src/lexer/scanner/strings.js +118 -0
  140. package/dist/src/lexer/scanner/strings.js.map +1 -0
  141. package/dist/src/lexer/scanner.d.ts +20 -27
  142. package/dist/src/lexer/scanner.js +11 -415
  143. package/dist/src/lexer/scanner.js.map +1 -1
  144. package/dist/src/parser/declarations/classes.d.ts +14 -0
  145. package/dist/src/parser/declarations/classes.js +191 -0
  146. package/dist/src/parser/declarations/classes.js.map +1 -0
  147. package/dist/src/parser/declarations/functions.d.ts +17 -0
  148. package/dist/src/parser/declarations/functions.js +241 -0
  149. package/dist/src/parser/declarations/functions.js.map +1 -0
  150. package/dist/src/parser/declarations/type-members.d.ts +16 -0
  151. package/dist/src/parser/declarations/type-members.js +156 -0
  152. package/dist/src/parser/declarations/type-members.js.map +1 -0
  153. package/dist/src/parser/declarations.d.ts +13 -24
  154. package/dist/src/parser/declarations.js +13 -571
  155. package/dist/src/parser/declarations.js.map +1 -1
  156. package/dist/src/parser/helpers.js +13 -4
  157. package/dist/src/parser/helpers.js.map +1 -1
  158. package/dist/src/parser/literals.js +10 -1
  159. package/dist/src/parser/literals.js.map +1 -1
  160. package/dist/tests/codegen/llvm.test.js +24 -0
  161. package/dist/tests/codegen/llvm.test.js.map +1 -1
  162. package/dist/tests/driver/compiler.test.js +3 -0
  163. package/dist/tests/driver/compiler.test.js.map +1 -1
  164. package/dist/tests/driver/paths.test.d.ts +1 -0
  165. package/dist/tests/driver/paths.test.js +26 -0
  166. package/dist/tests/driver/paths.test.js.map +1 -0
  167. package/dist/tests/driver/toolchain-download.test.d.ts +1 -0
  168. package/dist/tests/driver/toolchain-download.test.js +38 -0
  169. package/dist/tests/driver/toolchain-download.test.js.map +1 -0
  170. package/dist/tests/driver/toolchain-provider.test.d.ts +1 -0
  171. package/dist/tests/driver/toolchain-provider.test.js +37 -0
  172. package/dist/tests/driver/toolchain-provider.test.js.map +1 -0
  173. package/dist/tests/e2e/compile-node.test.d.ts +7 -0
  174. package/dist/tests/e2e/compile-node.test.js +123 -0
  175. package/dist/tests/e2e/compile-node.test.js.map +1 -0
  176. package/dist/tests/e2e/compile.test.d.ts +4 -1
  177. package/dist/tests/e2e/compile.test.js +90 -130
  178. package/dist/tests/e2e/compile.test.js.map +1 -1
  179. package/dist/tests/e2e/harness.d.ts +36 -0
  180. package/dist/tests/e2e/harness.js +64 -0
  181. package/dist/tests/e2e/harness.js.map +1 -0
  182. package/dist/tests/e2e/node-modules.test.js +60 -0
  183. package/dist/tests/e2e/node-modules.test.js.map +1 -1
  184. package/dist/tests/extensions/registry.test.js +4 -0
  185. package/dist/tests/extensions/registry.test.js.map +1 -1
  186. package/package.json +5 -3
  187. package/runtime/ext_node/buffer/buffer.c +12 -552
  188. package/runtime/ext_node/buffer/parts/byte-access.inc +35 -0
  189. package/runtime/ext_node/buffer/parts/construction.inc +60 -0
  190. package/runtime/ext_node/buffer/parts/integer-io.inc +119 -0
  191. package/runtime/ext_node/buffer/parts/methods.inc +135 -0
  192. package/runtime/ext_node/buffer/parts/prototype.inc +54 -0
  193. package/runtime/ext_node/buffer/parts/search.inc +86 -0
  194. package/runtime/ext_node/buffer/parts/static-api.inc +58 -0
  195. package/runtime/ext_node/child_process/child_process.c +37 -3
  196. package/runtime/ext_node/events/events.c +285 -0
  197. package/runtime/ext_node/http/http.c +11 -736
  198. package/runtime/ext_node/http/parts/client.inc +290 -0
  199. package/runtime/ext_node/http/parts/helpers.inc +45 -0
  200. package/runtime/ext_node/http/parts/prototypes.inc +57 -0
  201. package/runtime/ext_node/http/parts/response.inc +149 -0
  202. package/runtime/ext_node/http/parts/server.inc +133 -0
  203. package/runtime/ext_node/http/parts/string-builder.inc +58 -0
  204. package/runtime/ext_node/net/net.c +12 -504
  205. package/runtime/ext_node/net/parts/construction.inc +22 -0
  206. package/runtime/ext_node/net/parts/platform.inc +60 -0
  207. package/runtime/ext_node/net/parts/prototypes.inc +35 -0
  208. package/runtime/ext_node/net/parts/read-loop.inc +58 -0
  209. package/runtime/ext_node/net/parts/server.inc +131 -0
  210. package/runtime/ext_node/net/parts/socket-methods.inc +100 -0
  211. package/runtime/ext_node/net/parts/static-api.inc +93 -0
  212. package/runtime/ext_node/querystring/querystring.c +251 -0
  213. package/runtime/ext_node/util/util.c +456 -0
  214. package/runtime/rt.h +8 -2
  215. package/runtime/rt_internal.h +34 -0
  216. package/runtime/xt_bigint/arithmetic.inc +135 -0
  217. package/runtime/xt_bigint/bits.inc +98 -0
  218. package/runtime/xt_bigint/bitwise.inc +103 -0
  219. package/runtime/xt_bigint/compare.inc +30 -0
  220. package/runtime/xt_bigint/conversions.inc +51 -0
  221. package/runtime/xt_bigint/division.inc +85 -0
  222. package/runtime/xt_bigint/format.inc +145 -0
  223. package/runtime/xt_bigint/helpers.inc +92 -0
  224. package/runtime/xt_bigint/magnitude.inc +88 -0
  225. package/runtime/xt_bigint/public.inc +59 -0
  226. package/runtime/xt_bigint/small-arithmetic.inc +58 -0
  227. package/runtime/xt_bigint.c +42 -0
  228. package/runtime/xt_builtins.c +40 -5
  229. package/runtime/xt_io.c +7 -0
  230. package/runtime/xt_stdlib/arrays.inc +161 -0
  231. package/runtime/xt_stdlib/dispatch.inc +116 -0
  232. package/runtime/xt_stdlib/helpers.inc +23 -0
  233. package/runtime/xt_stdlib/math.inc +72 -0
  234. package/runtime/xt_stdlib/strings.inc +178 -0
  235. package/runtime/xt_stdlib.c +10 -549
  236. package/runtime/xt_stdlib2/array-statics.inc +37 -0
  237. package/runtime/xt_stdlib2/arrays.inc +189 -0
  238. package/runtime/xt_stdlib2/container.inc +23 -0
  239. package/runtime/xt_stdlib2/date.inc +72 -0
  240. package/runtime/xt_stdlib2/error.inc +22 -0
  241. package/runtime/xt_stdlib2/helpers.inc +43 -0
  242. package/runtime/xt_stdlib2/json.inc +213 -0
  243. package/runtime/xt_stdlib2/map.inc +95 -0
  244. package/runtime/xt_stdlib2/number-string-statics.inc +73 -0
  245. package/runtime/xt_stdlib2/numbers.inc +38 -0
  246. package/runtime/xt_stdlib2/objects.inc +41 -0
  247. package/runtime/xt_stdlib2/regexp-match.inc +204 -0
  248. package/runtime/xt_stdlib2/regexp.inc +20 -0
  249. package/runtime/xt_stdlib2/set.inc +105 -0
  250. package/runtime/xt_stdlib2/strings.inc +94 -0
  251. package/runtime/xt_stdlib2.c +19 -1278
  252. package/runtime/xt_values.c +155 -12
  253. package/scripts/build-runtime.ts +109 -0
  254. package/scripts/fetch-toolchain.ts +101 -0
  255. package/scripts/package-release.ts +192 -0
  256. package/src/ast/expressions.ts +2 -0
  257. package/src/binder/binder.ts +40 -725
  258. package/src/binder/declarations.ts +203 -0
  259. package/src/binder/helpers.ts +47 -0
  260. package/src/binder/references.ts +410 -0
  261. package/src/binder/types.ts +134 -0
  262. package/src/cli/main.ts +43 -0
  263. package/src/codegen/generator/calls/access.ts +139 -0
  264. package/src/codegen/generator/calls/closures.ts +60 -0
  265. package/src/codegen/generator/calls/invocation.ts +282 -0
  266. package/src/codegen/generator/calls/literals.ts +77 -0
  267. package/src/codegen/generator/calls/super.ts +61 -0
  268. package/src/codegen/generator/calls.ts +30 -552
  269. package/src/codegen/generator/context.ts +2 -2
  270. package/src/codegen/generator/expressions/assignment.ts +226 -0
  271. package/src/codegen/generator/expressions/operators.ts +182 -0
  272. package/src/codegen/generator/expressions/primary.ts +304 -0
  273. package/src/codegen/generator/expressions.ts +22 -553
  274. package/src/codegen/generator/module.ts +14 -4
  275. package/src/codegen/generator/state.ts +8 -0
  276. package/src/codegen/generator/statements/control-flow.ts +225 -0
  277. package/src/codegen/generator/statements/dispatch.ts +122 -0
  278. package/src/codegen/generator/statements/loops.ts +173 -0
  279. package/src/codegen/generator/statements/variables.ts +152 -0
  280. package/src/codegen/generator/statements.ts +26 -612
  281. package/src/codegen/generator/tables.ts +7 -0
  282. package/src/codegen/values.ts +22 -57
  283. package/src/diagnostics/diagnostic.ts +1 -0
  284. package/src/diagnostics/source.ts +7 -12
  285. package/src/driver/compiler.ts +61 -18
  286. package/src/driver/index.ts +17 -2
  287. package/src/driver/paths.ts +54 -0
  288. package/src/driver/runtime-lib.ts +32 -0
  289. package/src/driver/toolchain-download.ts +108 -0
  290. package/src/driver/toolchain-provider.ts +91 -0
  291. package/src/driver/toolchain.ts +29 -11
  292. package/src/extensions/node/child_process/index.ts +4 -2
  293. package/src/extensions/node/events/index.ts +27 -0
  294. package/src/extensions/node/index.ts +6 -0
  295. package/src/extensions/node/querystring/index.ts +29 -0
  296. package/src/extensions/node/util/index.ts +48 -0
  297. package/src/extensions/registry.ts +6 -0
  298. package/src/index.ts +1 -0
  299. package/src/lexer/scanner/identifiers.ts +37 -0
  300. package/src/lexer/scanner/numbers.ts +84 -0
  301. package/src/lexer/scanner/punctuation.ts +132 -0
  302. package/src/lexer/scanner/regex.ts +79 -0
  303. package/src/lexer/scanner/strings.ts +128 -0
  304. package/src/lexer/scanner.ts +35 -401
  305. package/src/parser/declarations/classes.ts +215 -0
  306. package/src/parser/declarations/functions.ts +254 -0
  307. package/src/parser/declarations/type-members.ts +181 -0
  308. package/src/parser/declarations.ts +32 -609
  309. package/src/parser/helpers.ts +13 -6
  310. package/src/parser/literals.ts +10 -1
  311. package/scripts/build-runtime.mjs +0 -55
@@ -0,0 +1,58 @@
1
+ /* -- socket read loop ----------------------------------------------------- */
2
+
3
+ static void xt_net_socket_read(void *userdata, int events) {
4
+ (void)events;
5
+ xt_value socket = *(xt_value *)userdata;
6
+ int fd = xt_net_fd(socket);
7
+ if (fd < 0) return;
8
+
9
+ unsigned char buffer[65536];
10
+ #if defined(_WIN32)
11
+ int count = recv(fd, (char *)buffer, (int)sizeof(buffer), 0);
12
+ #else
13
+ ssize_t count = recv(fd, buffer, sizeof(buffer), 0);
14
+ #endif
15
+ if (count > 0) {
16
+ const char *encoding = xt_node_encoding(xt_object_get_cstr(socket, "__encoding"));
17
+ xt_value chunk = xt_node_encode(buffer, (size_t)count, encoding);
18
+ xt_node_emit1(socket, "data", chunk);
19
+ return;
20
+ }
21
+ if (count == 0) {
22
+ xt_loop_remove(fd);
23
+ xt_close_socket(fd);
24
+ xt_node_set(socket, "__fd", xt_number(-1));
25
+ xt_node_set(socket, "__closed", xt_bool(1));
26
+ xt_node_emit0(socket, "end");
27
+ xt_node_emit0(socket, "close");
28
+ return;
29
+ }
30
+ if (xt_net_would_block()) return;
31
+ xt_loop_remove(fd);
32
+ xt_close_socket(fd);
33
+ xt_node_set(socket, "__fd", xt_number(-1));
34
+ xt_node_set(socket, "__closed", xt_bool(1));
35
+ xt_value error = xt_string_from_cstr("ECONNRESET");
36
+ xt_node_emit1(socket, "error", error);
37
+ xt_node_emit0(socket, "close");
38
+ }
39
+
40
+ static xt_value xt_net_socket_from_fd(int fd, const struct sockaddr *peer, xt_socklen_t peerLength) {
41
+ xt_value socket = xt_object_new_with_proto(xt_net_socket_proto());
42
+ xt_node_set(socket, "__fd", xt_number((double)fd));
43
+ xt_node_set(socket, "__connected", xt_bool(1));
44
+ xt_node_set(socket, "__readable", xt_array_new(0, NULL));
45
+
46
+ char host[NI_MAXHOST];
47
+ char service[NI_MAXSERV];
48
+ if (peer && getnameinfo(peer, peerLength, host, sizeof(host), service, sizeof(service),
49
+ NI_NUMERICHOST | NI_NUMERICSERV) == 0) {
50
+ xt_node_set(socket, "remoteAddress", xt_string_from_cstr(host));
51
+ xt_node_set(socket, "remotePort", xt_number((double)atoi(service)));
52
+ xt_node_set(socket, "remoteFamily", xt_string_from_cstr(strchr(host, ':') ? "IPv6" : "IPv4"));
53
+ }
54
+
55
+ xt_net_set_nonblocking(fd);
56
+ xt_loop_add(fd, XT_IO_READ, xt_net_socket_read, xt_net_box(socket));
57
+ return socket;
58
+ }
@@ -0,0 +1,131 @@
1
+ /* -- server --------------------------------------------------------------- */
2
+
3
+ static void xt_net_server_accept(void *userdata, int events) {
4
+ (void)events;
5
+ xt_value server = *(xt_value *)userdata;
6
+ int listenFd = xt_net_fd(server);
7
+ if (listenFd < 0) return;
8
+ for (;;) {
9
+ struct sockaddr_storage storage;
10
+ xt_socklen_t length = sizeof(storage);
11
+ int fd = accept(listenFd, (struct sockaddr *)&storage, &length);
12
+ if (fd < 0) break;
13
+ xt_value socket = xt_net_socket_from_fd(fd, (struct sockaddr *)&storage, length);
14
+ xt_node_emit1(server, "connection", socket);
15
+ }
16
+ }
17
+
18
+ /* Parse the many listen() signatures into a port + host. */
19
+ static void xt_net_parse_listen(int32_t argc, xt_value *argv, int *port, const char **host, xt_value *callback) {
20
+ *port = 0;
21
+ *host = "0.0.0.0";
22
+ *callback = XT_UNDEFINED;
23
+ int index = 0;
24
+ if (argc > 0 && XT_IS_OBJECT(argv[0])) {
25
+ xt_value portValue = xt_object_get_cstr(argv[0], "port");
26
+ if (!xt_truthy(xt_is_nullish(portValue))) *port = (int)xt_to_number(portValue);
27
+ xt_value hostValue = xt_object_get_cstr(argv[0], "host");
28
+ if (XT_IS_STRING(hostValue)) *host = xt_string_data(hostValue);
29
+ index = 1;
30
+ } else if (argc > 0 && XT_IS_NUMBER(argv[0])) {
31
+ *port = (int)xt_to_number(argv[0]);
32
+ index = 1;
33
+ if (argc > 1 && XT_IS_STRING(argv[1])) {
34
+ *host = xt_string_data(argv[1]);
35
+ index = 2;
36
+ }
37
+ }
38
+ for (int32_t i = index; i < argc; i++) {
39
+ if (XT_IS_FUNCTION(argv[i])) *callback = argv[i];
40
+ }
41
+ }
42
+
43
+ static xt_value xt_net_server_listen(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
44
+ (void)env;
45
+ int port = 0;
46
+ const char *host = "0.0.0.0";
47
+ xt_value callback = XT_UNDEFINED;
48
+ xt_net_parse_listen(argc, argv, &port, &host, &callback);
49
+
50
+ char service[16];
51
+ snprintf(service, sizeof(service), "%d", port);
52
+ struct addrinfo hints;
53
+ memset(&hints, 0, sizeof(hints));
54
+ hints.ai_family = AF_UNSPEC;
55
+ hints.ai_socktype = SOCK_STREAM;
56
+ hints.ai_flags = AI_PASSIVE;
57
+ struct addrinfo *result = NULL;
58
+ if (getaddrinfo(host, service, &hints, &result) != 0) {
59
+ if (XT_IS_FUNCTION(callback)) {
60
+ xt_value error = xt_string_from_cstr("EADDRNOTAVAIL");
61
+ xt_call_with_this(callback, xt_undefined(), 1, &error);
62
+ }
63
+ return thisValue;
64
+ }
65
+
66
+ int listenFd = -1;
67
+ for (struct addrinfo *info = result; info; info = info->ai_next) {
68
+ int fd = socket(info->ai_family, info->ai_socktype, info->ai_protocol);
69
+ if (fd < 0) continue;
70
+ int yes = 1;
71
+ setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const char *)&yes, sizeof(yes));
72
+ if (bind(fd, info->ai_addr, info->ai_addrlen) == 0 && listen(fd, 511) == 0) {
73
+ listenFd = fd;
74
+ break;
75
+ }
76
+ xt_close_socket(fd);
77
+ }
78
+ freeaddrinfo(result);
79
+
80
+ if (listenFd < 0) {
81
+ if (XT_IS_FUNCTION(callback)) {
82
+ xt_value error = xt_string_from_cstr("EADDRINUSE");
83
+ xt_call_with_this(callback, xt_undefined(), 1, &error);
84
+ }
85
+ return thisValue;
86
+ }
87
+
88
+ xt_net_set_nonblocking(listenFd);
89
+ xt_node_set(thisValue, "__fd", xt_number((double)listenFd));
90
+ xt_node_set(thisValue, "__listening", xt_bool(1));
91
+ xt_loop_add(listenFd, XT_IO_READ, xt_net_server_accept, xt_net_box(thisValue));
92
+
93
+ xt_node_emit0(thisValue, "listening");
94
+ if (XT_IS_FUNCTION(callback)) xt_call_with_this(callback, xt_undefined(), 0, NULL);
95
+ return thisValue;
96
+ }
97
+
98
+ static xt_value xt_net_server_close(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
99
+ (void)env;
100
+ int fd = xt_net_fd(thisValue);
101
+ if (fd >= 0) {
102
+ xt_loop_remove(fd);
103
+ xt_close_socket(fd);
104
+ }
105
+ xt_node_set(thisValue, "__fd", xt_number(-1));
106
+ xt_node_set(thisValue, "__listening", xt_bool(0));
107
+ xt_value callback = XT_IS_FUNCTION(xt_arg(argc, argv, 0)) ? xt_arg(argc, argv, 0) : XT_UNDEFINED;
108
+ xt_node_emit0(thisValue, "close");
109
+ if (XT_IS_FUNCTION(callback)) xt_call_with_this(callback, xt_undefined(), 0, NULL);
110
+ return thisValue;
111
+ }
112
+
113
+ static xt_value xt_net_server_address(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
114
+ return xt_net_socket_address(thisValue, env, argc, argv);
115
+ }
116
+
117
+ static xt_value xt_net_server_get_connections(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
118
+ (void)thisValue;
119
+ (void)env;
120
+ (void)argc;
121
+ (void)argv;
122
+ xt_value callback = xt_arg(argc, argv, 0);
123
+ if (XT_IS_FUNCTION(callback)) {
124
+ xt_value count = xt_number(0);
125
+ xt_value args[2];
126
+ args[0] = xt_null();
127
+ args[1] = count;
128
+ xt_call_with_this(callback, xt_undefined(), 2, args);
129
+ }
130
+ return xt_undefined();
131
+ }
@@ -0,0 +1,100 @@
1
+ /* -- socket methods ------------------------------------------------------- */
2
+
3
+ static xt_value xt_net_socket_write(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
4
+ (void)env;
5
+ int fd = xt_net_fd(thisValue);
6
+ if (fd < 0) {
7
+ xt_value callback = xt_arg(argc, argv, argc > 1 ? 2 : 1);
8
+ xt_value error = xt_string_from_cstr("ERR_SOCKET_CLOSED");
9
+ if (XT_IS_FUNCTION(callback)) xt_call_with_this(callback, xt_undefined(), 1, &error);
10
+ return xt_bool(0);
11
+ }
12
+ size_t length = 0;
13
+ unsigned char *bytes = xt_net_bytes(xt_arg(argc, argv, 0), &length);
14
+ xt_net_send_all(fd, bytes, length);
15
+ free(bytes);
16
+ xt_value callback = XT_IS_FUNCTION(xt_arg(argc, argv, 1)) ? xt_arg(argc, argv, 1) : xt_arg(argc, argv, 2);
17
+ if (XT_IS_FUNCTION(callback)) xt_call_with_this(callback, xt_undefined(), 0, NULL);
18
+ return xt_bool(1);
19
+ }
20
+
21
+ static xt_value xt_net_socket_end(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
22
+ (void)env;
23
+ int fd = xt_net_fd(thisValue);
24
+ if (fd < 0) return thisValue;
25
+ if (argc > 0 && !xt_truthy(xt_is_nullish(argv[0]))) {
26
+ xt_value writeFn = xt_object_get_cstr(thisValue, "write");
27
+ if (XT_IS_FUNCTION(writeFn)) xt_call_with_this(writeFn, thisValue, 1, &argv[0]);
28
+ }
29
+ #if defined(_WIN32)
30
+ shutdown(fd, SD_SEND);
31
+ #else
32
+ shutdown(fd, SHUT_WR);
33
+ #endif
34
+ return thisValue;
35
+ }
36
+
37
+ static xt_value xt_net_socket_destroy(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
38
+ (void)env;
39
+ (void)argc;
40
+ (void)argv;
41
+ int fd = xt_net_fd(thisValue);
42
+ if (fd >= 0) {
43
+ xt_loop_remove(fd);
44
+ xt_close_socket(fd);
45
+ }
46
+ xt_node_set(thisValue, "__fd", xt_number(-1));
47
+ if (!xt_truthy(xt_object_get_cstr(thisValue, "__closed"))) {
48
+ xt_node_set(thisValue, "__closed", xt_bool(1));
49
+ xt_node_emit0(thisValue, "close");
50
+ }
51
+ return thisValue;
52
+ }
53
+
54
+ static xt_value xt_net_socket_set_encoding(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
55
+ (void)env;
56
+ xt_node_set(thisValue, "__encoding", xt_arg(argc, argv, 0));
57
+ return thisValue;
58
+ }
59
+
60
+ static xt_value xt_net_socket_pause(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
61
+ (void)env;
62
+ (void)argc;
63
+ (void)argv;
64
+ return thisValue;
65
+ }
66
+
67
+ static xt_value xt_net_socket_resume(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
68
+ (void)env;
69
+ (void)argc;
70
+ (void)argv;
71
+ return thisValue;
72
+ }
73
+
74
+ static xt_value xt_net_socket_noop(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
75
+ (void)env;
76
+ (void)argc;
77
+ (void)argv;
78
+ return thisValue;
79
+ }
80
+
81
+ static xt_value xt_net_socket_address(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
82
+ (void)env;
83
+ (void)argc;
84
+ (void)argv;
85
+ int fd = xt_net_fd(thisValue);
86
+ if (fd < 0) return xt_null();
87
+ struct sockaddr_storage storage;
88
+ xt_socklen_t length = sizeof(storage);
89
+ if (getsockname(fd, (struct sockaddr *)&storage, &length) != 0) return xt_null();
90
+ char host[NI_MAXHOST];
91
+ char service[NI_MAXSERV];
92
+ if (getnameinfo((struct sockaddr *)&storage, length, host, sizeof(host), service, sizeof(service),
93
+ NI_NUMERICHOST | NI_NUMERICSERV) != 0)
94
+ return xt_null();
95
+ xt_value object = xt_object_new();
96
+ xt_node_set(object, "address", xt_string_from_cstr(host));
97
+ xt_node_set(object, "port", xt_number((double)atoi(service)));
98
+ xt_node_set(object, "family", xt_string_from_cstr(strchr(host, ':') ? "IPv6" : "IPv4"));
99
+ return object;
100
+ }
@@ -0,0 +1,93 @@
1
+ /* -- connect / static API ------------------------------------------------- */
2
+
3
+ static int xt_net_connect_fd(const char *host, int port) {
4
+ char service[16];
5
+ snprintf(service, sizeof(service), "%d", port);
6
+ struct addrinfo hints;
7
+ memset(&hints, 0, sizeof(hints));
8
+ hints.ai_family = AF_UNSPEC;
9
+ hints.ai_socktype = SOCK_STREAM;
10
+ struct addrinfo *result = NULL;
11
+ if (getaddrinfo(host, service, &hints, &result) != 0) return -1;
12
+ int fd = -1;
13
+ for (struct addrinfo *info = result; info; info = info->ai_next) {
14
+ int candidate = socket(info->ai_family, info->ai_socktype, info->ai_protocol);
15
+ if (candidate < 0) continue;
16
+ if (connect(candidate, info->ai_addr, info->ai_addrlen) == 0) {
17
+ fd = candidate;
18
+ break;
19
+ }
20
+ xt_close_socket(candidate);
21
+ }
22
+ freeaddrinfo(result);
23
+ return fd;
24
+ }
25
+
26
+ xt_value xt_node_net_connect(int32_t argc, xt_value *argv) {
27
+ int port = 0;
28
+ const char *host = "127.0.0.1";
29
+ xt_value callback = XT_UNDEFINED;
30
+ int index = 0;
31
+ if (argc > 0 && XT_IS_OBJECT(argv[0])) {
32
+ xt_value portValue = xt_object_get_cstr(argv[0], "port");
33
+ if (!xt_truthy(xt_is_nullish(portValue))) port = (int)xt_to_number(portValue);
34
+ xt_value hostValue = xt_object_get_cstr(argv[0], "host");
35
+ if (XT_IS_STRING(hostValue)) host = xt_string_data(hostValue);
36
+ index = 1;
37
+ } else if (argc > 0 && XT_IS_NUMBER(argv[0])) {
38
+ port = (int)xt_to_number(argv[0]);
39
+ index = 1;
40
+ if (argc > 1 && XT_IS_STRING(argv[1])) {
41
+ host = xt_string_data(argv[1]);
42
+ index = 2;
43
+ }
44
+ }
45
+ for (int32_t i = index; i < argc; i++) {
46
+ if (XT_IS_FUNCTION(argv[i])) callback = argv[i];
47
+ }
48
+
49
+ int fd = xt_net_connect_fd(host, port);
50
+ if (fd < 0) {
51
+ xt_value socket = xt_net_socket_ctor(0, NULL);
52
+ xt_value error = xt_string_from_cstr("ECONNREFUSED");
53
+ if (XT_IS_FUNCTION(callback)) xt_call_with_this(callback, xt_undefined(), 1, &error);
54
+ else xt_node_emit1(socket, "error", error);
55
+ return socket;
56
+ }
57
+
58
+ struct sockaddr_storage storage;
59
+ xt_socklen_t length = sizeof(storage);
60
+ getpeername(fd, (struct sockaddr *)&storage, &length);
61
+ xt_value socket = xt_net_socket_from_fd(fd, (struct sockaddr *)&storage, length);
62
+ xt_node_set(socket, "__connecting", xt_bool(0));
63
+ xt_node_emit0(socket, "connect");
64
+ xt_node_emit0(socket, "ready");
65
+ if (XT_IS_FUNCTION(callback)) xt_call_with_this(callback, xt_undefined(), 0, NULL);
66
+ return socket;
67
+ }
68
+
69
+ xt_value xt_net_static(xt_value name, int32_t argc, xt_value *argv) {
70
+ const char *fn = xt_string_data(name);
71
+ if (!fn) return xt_undefined();
72
+ if (strcmp(fn, "createServer") == 0) {
73
+ xt_value server = xt_net_server_ctor(0, NULL);
74
+ for (int32_t i = 0; i < argc; i++) {
75
+ if (XT_IS_FUNCTION(argv[i])) {
76
+ xt_array_push(xt_node_listeners(server, "connection", 1), argv[i]);
77
+ break;
78
+ }
79
+ }
80
+ return server;
81
+ }
82
+ if (strcmp(fn, "connect") == 0 || strcmp(fn, "createConnection") == 0) return xt_node_net_connect(argc, argv);
83
+ if (strcmp(fn, "isIP") == 0 || strcmp(fn, "isIPv4") == 0 || strcmp(fn, "isIPv6") == 0) {
84
+ const char *input = xt_string_data(xt_to_string(xt_arg(argc, argv, 0)));
85
+ if (!input) return xt_number(0);
86
+ if (strcmp(fn, "isIPv4") == 0) return xt_bool(strchr(input, '.') && !strchr(input, ':'));
87
+ if (strcmp(fn, "isIPv6") == 0) return xt_bool(strchr(input, ':') != NULL);
88
+ if (strchr(input, ':')) return xt_number(6);
89
+ if (strchr(input, '.')) return xt_number(4);
90
+ return xt_number(0);
91
+ }
92
+ return xt_undefined();
93
+ }
@@ -0,0 +1,251 @@
1
+ /*
2
+ * Node.js `querystring` module for xbintsc.
3
+ *
4
+ * Implements `parse` / `stringify` (and their `decode` / `encode` aliases)
5
+ * plus `escape` / `unescape`. Percent-encoding follows
6
+ * `encodeURIComponent`/`decodeURIComponent` semantics and, as Node does for
7
+ * form data, `+` decodes to a space.
8
+ */
9
+
10
+ #include "../node_common.h"
11
+
12
+ /* -- growable string buffer ---------------------------------------------- */
13
+
14
+ typedef struct {
15
+ char *data;
16
+ size_t length;
17
+ size_t capacity;
18
+ } qs_buf;
19
+
20
+ static void qs_buf_init(qs_buf *buf) {
21
+ buf->capacity = 64;
22
+ buf->length = 0;
23
+ buf->data = (char *)malloc(buf->capacity);
24
+ if (buf->data) buf->data[0] = '\0';
25
+ }
26
+
27
+ static void qs_buf_putc(qs_buf *buf, char c) {
28
+ if (!buf->data) return;
29
+ if (buf->length + 2 > buf->capacity) {
30
+ while (buf->length + 2 > buf->capacity) buf->capacity *= 2;
31
+ buf->data = (char *)realloc(buf->data, buf->capacity);
32
+ if (!buf->data) return;
33
+ }
34
+ buf->data[buf->length++] = c;
35
+ buf->data[buf->length] = '\0';
36
+ }
37
+
38
+ static void qs_buf_puts(qs_buf *buf, const char *text, size_t length) {
39
+ for (size_t i = 0; i < length; i++) qs_buf_putc(buf, text[i]);
40
+ }
41
+
42
+ static xt_value qs_buf_finish(qs_buf *buf) {
43
+ xt_value result = buf->data ? xt_string_new(buf->data, buf->length) : xt_string_from_cstr("");
44
+ free(buf->data);
45
+ buf->data = NULL;
46
+ return result;
47
+ }
48
+
49
+ /* -- percent encoding ----------------------------------------------------- */
50
+
51
+ static int qs_unreserved(unsigned char c) {
52
+ return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '-' ||
53
+ c == '_' || c == '.' || c == '!' || c == '~' || c == '*' || c == '\'' || c == '(' || c == ')';
54
+ }
55
+
56
+ static int qs_hex(int c) {
57
+ if (c >= '0' && c <= '9') return c - '0';
58
+ if (c >= 'a' && c <= 'f') return c - 'a' + 10;
59
+ if (c >= 'A' && c <= 'F') return c - 'A' + 10;
60
+ return -1;
61
+ }
62
+
63
+ /* encodeURIComponent-style escaping of `length` bytes. */
64
+ static xt_value qs_escape_bytes(const char *text, size_t length) {
65
+ static const char digits[] = "0123456789ABCDEF";
66
+ qs_buf buf;
67
+ qs_buf_init(&buf);
68
+ for (size_t i = 0; i < length; i++) {
69
+ unsigned char c = (unsigned char)text[i];
70
+ if (qs_unreserved(c)) {
71
+ qs_buf_putc(&buf, (char)c);
72
+ } else {
73
+ qs_buf_putc(&buf, '%');
74
+ qs_buf_putc(&buf, digits[c >> 4]);
75
+ qs_buf_putc(&buf, digits[c & 0x0f]);
76
+ }
77
+ }
78
+ return qs_buf_finish(&buf);
79
+ }
80
+
81
+ /* decodeURIComponent-style unescaping, with `+` meaning space. */
82
+ static xt_value qs_unescape_bytes(const char *text, size_t length) {
83
+ char *out = (char *)malloc(length + 1);
84
+ if (!out) return xt_string_from_cstr("");
85
+ size_t written = 0;
86
+ for (size_t i = 0; i < length; i++) {
87
+ char c = text[i];
88
+ if (c == '+') {
89
+ out[written++] = ' ';
90
+ } else if (c == '%' && i + 2 < length && qs_hex(text[i + 1]) >= 0 && qs_hex(text[i + 2]) >= 0) {
91
+ out[written++] = (char)((qs_hex(text[i + 1]) << 4) | qs_hex(text[i + 2]));
92
+ i += 2;
93
+ } else {
94
+ out[written++] = c;
95
+ }
96
+ }
97
+ out[written] = '\0';
98
+ xt_value result = xt_string_new(out, written);
99
+ free(out);
100
+ return result;
101
+ }
102
+
103
+ /* -- options -------------------------------------------------------------- */
104
+
105
+ static const char *qs_option(int32_t argc, xt_value *argv, int32_t index, const char *fallback) {
106
+ if (index >= argc) return fallback;
107
+ xt_value value = argv[index];
108
+ if (XT_IS_STRING(value)) {
109
+ const char *data = xt_string_data(value);
110
+ return data ? data : fallback;
111
+ }
112
+ return fallback;
113
+ }
114
+
115
+ /* -- parse ---------------------------------------------------------------- */
116
+
117
+ static void qs_assign(xt_value result, xt_value key, xt_value value) {
118
+ if (!xt_truthy(xt_object_has_own(result, key))) {
119
+ xt_object_set(result, key, value);
120
+ return;
121
+ }
122
+ xt_value existing = xt_object_get(result, key);
123
+ if (XT_IS_ARRAY(existing)) {
124
+ xt_array_push(existing, value);
125
+ return;
126
+ }
127
+ xt_value array = xt_array_new(1, &existing);
128
+ xt_array_push(array, value);
129
+ xt_object_set(result, key, array);
130
+ }
131
+
132
+ static void qs_parse_pair(xt_value result, const char *pair, size_t length, const char *eq, size_t eqLength) {
133
+ size_t eqIndex = length;
134
+ if (eqLength > 0) {
135
+ for (size_t i = 0; i + eqLength <= length; i++) {
136
+ if (memcmp(pair + i, eq, eqLength) == 0) {
137
+ eqIndex = i;
138
+ break;
139
+ }
140
+ }
141
+ }
142
+ size_t keyLength = eqIndex;
143
+ const char *valueStart = pair + length;
144
+ size_t valueLength = 0;
145
+ if (eqIndex < length) {
146
+ valueStart = pair + eqIndex + eqLength;
147
+ valueLength = length - eqIndex - eqLength;
148
+ }
149
+ xt_value key = qs_unescape_bytes(pair, keyLength);
150
+ xt_value value = qs_unescape_bytes(valueStart, valueLength);
151
+ qs_assign(result, key, value);
152
+ }
153
+
154
+ xt_value xt_querystring_parse(int32_t argc, xt_value *argv) {
155
+ xt_value result = xt_object_new();
156
+ xt_value input = xt_arg(argc, argv, 0);
157
+ if (!XT_IS_STRING(input)) return result;
158
+ const char *text = xt_string_data(input);
159
+ size_t length = (size_t)xt_string_length_value(input);
160
+ const char *sep = qs_option(argc, argv, 1, "&");
161
+ const char *eq = qs_option(argc, argv, 2, "=");
162
+ size_t sepLength = strlen(sep);
163
+ size_t eqLength = strlen(eq);
164
+ size_t start = 0;
165
+ while (start <= length) {
166
+ size_t end = start;
167
+ while (end < length && !(sepLength > 0 && end + sepLength <= length && memcmp(text + end, sep, sepLength) == 0)) {
168
+ end++;
169
+ }
170
+ qs_parse_pair(result, text + start, end - start, eq, eqLength);
171
+ if (end >= length) break;
172
+ start = end + sepLength;
173
+ }
174
+ return result;
175
+ }
176
+
177
+ /* -- stringify ------------------------------------------------------------ */
178
+
179
+ static void qs_stringify_scalar(qs_buf *buf, xt_value key, xt_value value, const char *eq) {
180
+ const char *keyData = xt_string_data(key);
181
+ size_t keyLength = (size_t)xt_string_length_value(key);
182
+ xt_value escapedKey = qs_escape_bytes(keyData, keyLength);
183
+ qs_buf_puts(buf, xt_string_data(escapedKey), (size_t)xt_string_length_value(escapedKey));
184
+ qs_buf_puts(buf, eq, strlen(eq));
185
+
186
+ xt_value text = value;
187
+ if (xt_truthy(xt_is_nullish(value))) {
188
+ return; /* null / undefined stringify to the empty string */
189
+ }
190
+ if (!XT_IS_STRING(text)) text = xt_to_string(text);
191
+ const char *data = xt_string_data(text);
192
+ size_t length = (size_t)xt_string_length_value(text);
193
+ xt_value escaped = qs_escape_bytes(data, length);
194
+ qs_buf_puts(buf, xt_string_data(escaped), (size_t)xt_string_length_value(escaped));
195
+ }
196
+
197
+ xt_value xt_querystring_stringify(int32_t argc, xt_value *argv) {
198
+ xt_value object = xt_arg(argc, argv, 0);
199
+ if (!XT_IS_OBJECT(object) || XT_IS_ARRAY(object)) return xt_string_from_cstr("");
200
+ const char *sep = qs_option(argc, argv, 1, "&");
201
+ const char *eq = qs_option(argc, argv, 2, "=");
202
+
203
+ qs_buf buf;
204
+ qs_buf_init(&buf);
205
+ xt_value keys = xt_object_keys(object);
206
+ int32_t count = (int32_t)xt_to_number(xt_array_length(keys));
207
+ int first = 1;
208
+ for (int32_t i = 0; i < count; i++) {
209
+ xt_value key = xt_array_get(keys, xt_number((double)i));
210
+ xt_value value = xt_object_get(object, key);
211
+ if (XT_IS_ARRAY(value)) {
212
+ int32_t items = (int32_t)xt_to_number(xt_array_length(value));
213
+ for (int32_t j = 0; j < items; j++) {
214
+ if (!first) qs_buf_puts(&buf, sep, strlen(sep));
215
+ first = 0;
216
+ qs_stringify_scalar(&buf, key, xt_array_get(value, xt_number((double)j)), eq);
217
+ }
218
+ } else {
219
+ if (!first) qs_buf_puts(&buf, sep, strlen(sep));
220
+ first = 0;
221
+ qs_stringify_scalar(&buf, key, value, eq);
222
+ }
223
+ }
224
+ return qs_buf_finish(&buf);
225
+ }
226
+
227
+ /* -- escape / unescape ---------------------------------------------------- */
228
+
229
+ xt_value xt_querystring_escape(int32_t argc, xt_value *argv) {
230
+ xt_value text = xt_arg(argc, argv, 0);
231
+ if (!XT_IS_STRING(text)) text = xt_to_string(text);
232
+ return qs_escape_bytes(xt_string_data(text), (size_t)xt_string_length_value(text));
233
+ }
234
+
235
+ xt_value xt_querystring_unescape(int32_t argc, xt_value *argv) {
236
+ xt_value text = xt_arg(argc, argv, 0);
237
+ if (!XT_IS_STRING(text)) text = xt_to_string(text);
238
+ return qs_unescape_bytes(xt_string_data(text), (size_t)xt_string_length_value(text));
239
+ }
240
+
241
+ /* -- namespace dispatcher ------------------------------------------------- */
242
+
243
+ xt_value xt_querystring_static(xt_value name, int32_t argc, xt_value *argv) {
244
+ const char *fn = xt_string_data(name);
245
+ if (!fn) return xt_undefined();
246
+ if (strcmp(fn, "parse") == 0 || strcmp(fn, "decode") == 0) return xt_querystring_parse(argc, argv);
247
+ if (strcmp(fn, "stringify") == 0 || strcmp(fn, "encode") == 0) return xt_querystring_stringify(argc, argv);
248
+ if (strcmp(fn, "escape") == 0) return xt_querystring_escape(argc, argv);
249
+ if (strcmp(fn, "unescape") == 0) return xt_querystring_unescape(argc, argv);
250
+ return xt_undefined();
251
+ }