xbintsc 0.1.9 → 0.1.11
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.
- package/README.md +12 -7
- package/dist/src/codegen/generator/calls.js +11 -1
- package/dist/src/codegen/generator/calls.js.map +1 -1
- package/dist/src/codegen/generator/module.js +2 -0
- package/dist/src/codegen/generator/module.js.map +1 -1
- package/dist/src/codegen/generator/state.js +19 -0
- package/dist/src/codegen/generator/state.js.map +1 -1
- package/dist/src/codegen/generator/tables.d.ts +2 -0
- package/dist/src/codegen/generator/tables.js +25 -0
- package/dist/src/codegen/generator/tables.js.map +1 -1
- package/dist/src/driver/compiler.js +1 -0
- package/dist/src/driver/compiler.js.map +1 -1
- package/dist/src/extensions/node/buffer/index.d.ts +10 -0
- package/dist/src/extensions/node/buffer/index.js +15 -0
- package/dist/src/extensions/node/buffer/index.js.map +1 -0
- package/dist/src/extensions/node/dgram/index.d.ts +8 -0
- package/dist/src/extensions/node/dgram/index.js +13 -0
- package/dist/src/extensions/node/dgram/index.js.map +1 -0
- package/dist/src/extensions/node/fs/fs-ops.d.ts +6 -0
- package/dist/src/extensions/node/fs/fs-ops.js +18 -0
- package/dist/src/extensions/node/fs/fs-ops.js.map +1 -0
- package/dist/src/extensions/node/fs/index.d.ts +3 -1
- package/dist/src/extensions/node/fs/index.js +11 -3
- package/dist/src/extensions/node/fs/index.js.map +1 -1
- package/dist/src/extensions/node/fs/write-file.d.ts +5 -0
- package/dist/src/extensions/node/fs/write-file.js +8 -0
- package/dist/src/extensions/node/fs/write-file.js.map +1 -0
- package/dist/src/extensions/node/fs-promises/index.d.ts +10 -0
- package/dist/src/extensions/node/fs-promises/index.js +37 -0
- package/dist/src/extensions/node/fs-promises/index.js.map +1 -0
- package/dist/src/extensions/node/http/index.d.ts +10 -0
- package/dist/src/extensions/node/http/index.js +18 -0
- package/dist/src/extensions/node/http/index.js.map +1 -0
- package/dist/src/extensions/node/index.d.ts +3 -1
- package/dist/src/extensions/node/index.js +28 -4
- package/dist/src/extensions/node/index.js.map +1 -1
- package/dist/src/extensions/node/net/index.d.ts +9 -0
- package/dist/src/extensions/node/net/index.js +14 -0
- package/dist/src/extensions/node/net/index.js.map +1 -0
- package/dist/src/extensions/node/os/index.d.ts +9 -0
- package/dist/src/extensions/node/os/index.js +14 -0
- package/dist/src/extensions/node/os/index.js.map +1 -0
- package/dist/src/extensions/node/path/index.d.ts +10 -0
- package/dist/src/extensions/node/path/index.js +15 -0
- package/dist/src/extensions/node/path/index.js.map +1 -0
- package/dist/src/extensions/node/process/index.d.ts +10 -0
- package/dist/src/extensions/node/process/index.js +15 -0
- package/dist/src/extensions/node/process/index.js.map +1 -0
- package/dist/src/extensions/node/stream/index.d.ts +10 -0
- package/dist/src/extensions/node/stream/index.js +15 -0
- package/dist/src/extensions/node/stream/index.js.map +1 -0
- package/dist/tests/e2e/compile.test.js +55 -0
- package/dist/tests/e2e/compile.test.js.map +1 -1
- package/dist/tests/e2e/node-modules.test.d.ts +9 -0
- package/dist/tests/e2e/node-modules.test.js +179 -0
- package/dist/tests/e2e/node-modules.test.js.map +1 -0
- package/dist/tests/extensions/registry.test.js +9 -3
- package/dist/tests/extensions/registry.test.js.map +1 -1
- package/package.json +1 -1
- package/runtime/ext_node/buffer/buffer.c +569 -0
- package/runtime/ext_node/dgram/dgram.c +337 -0
- package/runtime/ext_node/fs/fs_common.h +166 -0
- package/runtime/ext_node/fs/fs_ops.c +342 -0
- package/runtime/ext_node/fs/promises.c +60 -0
- package/runtime/ext_node/fs/read_file.c +12 -6
- package/runtime/ext_node/fs/write_file.c +47 -0
- package/runtime/ext_node/http/http.c +760 -0
- package/runtime/ext_node/net/net.c +546 -0
- package/runtime/ext_node/node_common.h +305 -0
- package/runtime/ext_node/os/os.c +165 -0
- package/runtime/ext_node/path/path.c +324 -0
- package/runtime/ext_node/process/process.c +137 -0
- package/runtime/ext_node/stream/stream.c +348 -0
- package/runtime/rt.h +19 -0
- package/runtime/xt_containers.c +2 -0
- package/runtime/xt_io.c +9 -0
- package/runtime/xt_loop.c +117 -0
- package/scripts/build-runtime.mjs +14 -0
- package/src/codegen/generator/calls.ts +13 -0
- package/src/codegen/generator/module.ts +2 -0
- package/src/codegen/generator/state.ts +19 -0
- package/src/codegen/generator/tables.ts +26 -0
- package/src/driver/compiler.ts +1 -0
- package/src/extensions/node/buffer/index.ts +17 -0
- package/src/extensions/node/dgram/index.ts +15 -0
- package/src/extensions/node/fs/fs-ops.ts +20 -0
- package/src/extensions/node/fs/index.ts +11 -3
- package/src/extensions/node/fs/write-file.ts +10 -0
- package/src/extensions/node/fs-promises/index.ts +41 -0
- package/src/extensions/node/http/index.ts +20 -0
- package/src/extensions/node/index.ts +28 -4
- package/src/extensions/node/net/index.ts +16 -0
- package/src/extensions/node/os/index.ts +16 -0
- package/src/extensions/node/path/index.ts +17 -0
- package/src/extensions/node/process/index.ts +17 -0
- package/src/extensions/node/stream/index.ts +17 -0
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Node.js `dgram` for xbintsc.
|
|
3
|
+
*
|
|
4
|
+
* UDP sockets driven by the core event loop. `createSocket(...)` returns an
|
|
5
|
+
* EventEmitter with `bind`, `send`, `close` and `address`; inbound datagrams
|
|
6
|
+
* are delivered to `message` listeners together with an `rinfo` object.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
#include "../node_common.h"
|
|
10
|
+
|
|
11
|
+
#include <errno.h>
|
|
12
|
+
#include <string.h>
|
|
13
|
+
|
|
14
|
+
#if defined(_WIN32)
|
|
15
|
+
#include <winsock2.h>
|
|
16
|
+
#include <ws2tcpip.h>
|
|
17
|
+
typedef int xt_socklen_t;
|
|
18
|
+
#define xt_close_socket closesocket
|
|
19
|
+
#else
|
|
20
|
+
#include <arpa/inet.h>
|
|
21
|
+
#include <fcntl.h>
|
|
22
|
+
#include <netdb.h>
|
|
23
|
+
#include <netinet/in.h>
|
|
24
|
+
#include <sys/socket.h>
|
|
25
|
+
#include <sys/types.h>
|
|
26
|
+
#include <unistd.h>
|
|
27
|
+
typedef socklen_t xt_socklen_t;
|
|
28
|
+
#define xt_close_socket close
|
|
29
|
+
#endif
|
|
30
|
+
|
|
31
|
+
int xt_node_is_buffer(xt_value value);
|
|
32
|
+
unsigned char *xt_node_buffer_bytes(xt_value value, size_t *outLength);
|
|
33
|
+
|
|
34
|
+
static xt_value xt_dgram_socket_proto(void);
|
|
35
|
+
|
|
36
|
+
static void xt_dgram_set_nonblocking(int fd) {
|
|
37
|
+
#if defined(_WIN32)
|
|
38
|
+
u_long mode = 1;
|
|
39
|
+
ioctlsocket(fd, FIONBIO, &mode);
|
|
40
|
+
#else
|
|
41
|
+
int flags = fcntl(fd, F_GETFL, 0);
|
|
42
|
+
if (flags >= 0) fcntl(fd, F_SETFL, flags | O_NONBLOCK);
|
|
43
|
+
#endif
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
static xt_value *xt_dgram_box(xt_value value) {
|
|
47
|
+
xt_value *handle = (xt_value *)malloc(sizeof(xt_value));
|
|
48
|
+
*handle = value;
|
|
49
|
+
return handle;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
static int xt_dgram_fd(xt_value socket) { return (int)xt_to_number(xt_object_get_cstr(socket, "__fd")); }
|
|
53
|
+
|
|
54
|
+
static unsigned char *xt_dgram_bytes(xt_value data, size_t *outLength) {
|
|
55
|
+
if (xt_node_is_buffer(data)) return xt_node_buffer_bytes(data, outLength);
|
|
56
|
+
const char *text = xt_string_data(xt_to_string(data));
|
|
57
|
+
size_t length = text ? strlen(text) : 0;
|
|
58
|
+
unsigned char *bytes = (unsigned char *)malloc(length + 1);
|
|
59
|
+
if (length > 0 && text) memcpy(bytes, text, length);
|
|
60
|
+
bytes[length] = '\0';
|
|
61
|
+
*outLength = length;
|
|
62
|
+
return bytes;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
static void xt_dgram_read(void *userdata, int events) {
|
|
66
|
+
(void)events;
|
|
67
|
+
xt_value socket = *(xt_value *)userdata;
|
|
68
|
+
int fd = xt_dgram_fd(socket);
|
|
69
|
+
if (fd < 0) return;
|
|
70
|
+
|
|
71
|
+
unsigned char buffer[65536];
|
|
72
|
+
struct sockaddr_storage storage;
|
|
73
|
+
xt_socklen_t length = sizeof(storage);
|
|
74
|
+
#if defined(_WIN32)
|
|
75
|
+
int count = recvfrom(fd, (char *)buffer, (int)sizeof(buffer), 0, (struct sockaddr *)&storage, &length);
|
|
76
|
+
#else
|
|
77
|
+
ssize_t count = recvfrom(fd, buffer, sizeof(buffer), 0, (struct sockaddr *)&storage, &length);
|
|
78
|
+
#endif
|
|
79
|
+
if (count < 0) return;
|
|
80
|
+
|
|
81
|
+
char host[NI_MAXHOST];
|
|
82
|
+
char service[NI_MAXSERV];
|
|
83
|
+
if (getnameinfo((struct sockaddr *)&storage, length, host, sizeof(host), service, sizeof(service),
|
|
84
|
+
NI_NUMERICHOST | NI_NUMERICSERV) != 0)
|
|
85
|
+
return;
|
|
86
|
+
|
|
87
|
+
xt_value rinfo = xt_object_new();
|
|
88
|
+
xt_node_set(rinfo, "address", xt_string_from_cstr(host));
|
|
89
|
+
xt_node_set(rinfo, "port", xt_number((double)atoi(service)));
|
|
90
|
+
xt_node_set(rinfo, "family", xt_string_from_cstr(strchr(host, ':') ? "IPv6" : "IPv4"));
|
|
91
|
+
xt_node_set(rinfo, "size", xt_number((double)count));
|
|
92
|
+
|
|
93
|
+
const char *encoding = xt_node_encoding(xt_object_get_cstr(socket, "__encoding"));
|
|
94
|
+
xt_value chunk = xt_node_encode(buffer, (size_t)count, encoding);
|
|
95
|
+
xt_node_emit(socket, "message", 2, (xt_value[]){chunk, rinfo});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/* bind([port][, address][, callback]) | bind(options[, callback]) */
|
|
99
|
+
static xt_value xt_dgram_bind(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
|
|
100
|
+
(void)env;
|
|
101
|
+
int port = 0;
|
|
102
|
+
const char *address = NULL;
|
|
103
|
+
xt_value callback = XT_UNDEFINED;
|
|
104
|
+
int index = 0;
|
|
105
|
+
if (argc > 0 && XT_IS_OBJECT(argv[0])) {
|
|
106
|
+
xt_value portValue = xt_object_get_cstr(argv[0], "port");
|
|
107
|
+
if (!xt_truthy(xt_is_nullish(portValue))) port = (int)xt_to_number(portValue);
|
|
108
|
+
xt_value addressValue = xt_object_get_cstr(argv[0], "address");
|
|
109
|
+
if (XT_IS_STRING(addressValue)) address = xt_string_data(addressValue);
|
|
110
|
+
index = 1;
|
|
111
|
+
} else if (argc > 0 && XT_IS_NUMBER(argv[0])) {
|
|
112
|
+
port = (int)xt_to_number(argv[0]);
|
|
113
|
+
index = 1;
|
|
114
|
+
if (argc > 1 && XT_IS_STRING(argv[1])) {
|
|
115
|
+
address = xt_string_data(argv[1]);
|
|
116
|
+
index = 2;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
for (int32_t i = index; i < argc; i++) {
|
|
120
|
+
if (XT_IS_FUNCTION(argv[i])) callback = argv[i];
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const char *type = xt_string_data(xt_to_string(xt_object_get_cstr(thisValue, "__type")));
|
|
124
|
+
int family = (type && strcmp(type, "udp6") == 0) ? AF_INET6 : AF_INET;
|
|
125
|
+
if (address && strchr(address, ':')) family = AF_INET6;
|
|
126
|
+
|
|
127
|
+
int fd = socket(family, SOCK_DGRAM, 0);
|
|
128
|
+
if (fd < 0) return thisValue;
|
|
129
|
+
int yes = 1;
|
|
130
|
+
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const char *)&yes, sizeof(yes));
|
|
131
|
+
|
|
132
|
+
char service[16];
|
|
133
|
+
snprintf(service, sizeof(service), "%d", port);
|
|
134
|
+
struct addrinfo hints;
|
|
135
|
+
memset(&hints, 0, sizeof(hints));
|
|
136
|
+
hints.ai_family = family;
|
|
137
|
+
hints.ai_socktype = SOCK_DGRAM;
|
|
138
|
+
hints.ai_flags = AI_PASSIVE;
|
|
139
|
+
struct addrinfo *result = NULL;
|
|
140
|
+
int bound = -1;
|
|
141
|
+
if (getaddrinfo(address, service, &hints, &result) == 0) {
|
|
142
|
+
for (struct addrinfo *info = result; info; info = info->ai_next) {
|
|
143
|
+
if (bind(fd, info->ai_addr, info->ai_addrlen) == 0) {
|
|
144
|
+
bound = 0;
|
|
145
|
+
break;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
freeaddrinfo(result);
|
|
149
|
+
}
|
|
150
|
+
if (bound != 0) {
|
|
151
|
+
xt_close_socket(fd);
|
|
152
|
+
xt_value error = xt_string_from_cstr("EADDRINUSE");
|
|
153
|
+
xt_node_emit1(thisValue, "error", error);
|
|
154
|
+
if (XT_IS_FUNCTION(callback)) xt_call_with_this(callback, xt_undefined(), 1, &error);
|
|
155
|
+
return thisValue;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
xt_dgram_set_nonblocking(fd);
|
|
159
|
+
xt_node_set(thisValue, "__fd", xt_number((double)fd));
|
|
160
|
+
xt_node_set(thisValue, "__bound", xt_bool(1));
|
|
161
|
+
xt_loop_add(fd, XT_IO_READ, xt_dgram_read, xt_dgram_box(thisValue));
|
|
162
|
+
|
|
163
|
+
xt_node_emit0(thisValue, "listening");
|
|
164
|
+
if (XT_IS_FUNCTION(callback)) xt_call_with_this(callback, xt_undefined(), 0, NULL);
|
|
165
|
+
return thisValue;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
static int xt_dgram_resolve(const char *host, int port, struct sockaddr_storage *storage, xt_socklen_t *length) {
|
|
169
|
+
char service[16];
|
|
170
|
+
snprintf(service, sizeof(service), "%d", port);
|
|
171
|
+
struct addrinfo hints;
|
|
172
|
+
memset(&hints, 0, sizeof(hints));
|
|
173
|
+
hints.ai_family = AF_UNSPEC;
|
|
174
|
+
hints.ai_socktype = SOCK_DGRAM;
|
|
175
|
+
struct addrinfo *result = NULL;
|
|
176
|
+
if (getaddrinfo(host, service, &hints, &result) != 0) return -1;
|
|
177
|
+
int status = -1;
|
|
178
|
+
if (result) {
|
|
179
|
+
memcpy(storage, result->ai_addr, result->ai_addrlen);
|
|
180
|
+
*length = (xt_socklen_t)result->ai_addrlen;
|
|
181
|
+
status = 0;
|
|
182
|
+
}
|
|
183
|
+
freeaddrinfo(result);
|
|
184
|
+
return status;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/* send(msg[, offset, length], port[, address][, callback]) */
|
|
188
|
+
static xt_value xt_dgram_send(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
|
|
189
|
+
int fd = xt_dgram_fd(thisValue);
|
|
190
|
+
/* Node auto-binds an unbound socket on the first send. */
|
|
191
|
+
if (fd < 0) {
|
|
192
|
+
xt_dgram_bind(thisValue, env, 0, NULL);
|
|
193
|
+
fd = xt_dgram_fd(thisValue);
|
|
194
|
+
}
|
|
195
|
+
size_t length = 0;
|
|
196
|
+
unsigned char *bytes = xt_dgram_bytes(xt_arg(argc, argv, 0), &length);
|
|
197
|
+
|
|
198
|
+
int port = 0;
|
|
199
|
+
const char *address = "127.0.0.1";
|
|
200
|
+
xt_value callback = XT_UNDEFINED;
|
|
201
|
+
/* The final numeric argument is the destination port; any earlier numbers
|
|
202
|
+
* are the optional `offset`/`length` (currently unused, the whole message is
|
|
203
|
+
* sent). The first string is the address and the first function a callback. */
|
|
204
|
+
for (int32_t i = 1; i < argc; i++) {
|
|
205
|
+
if (XT_IS_NUMBER(argv[i])) port = (int)xt_to_number(argv[i]);
|
|
206
|
+
else if (XT_IS_STRING(argv[i]) && strcmp(address, "127.0.0.1") == 0) address = xt_string_data(argv[i]);
|
|
207
|
+
else if (XT_IS_FUNCTION(argv[i])) callback = argv[i];
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
xt_value error = XT_UNDEFINED;
|
|
211
|
+
if (fd < 0) {
|
|
212
|
+
error = xt_string_from_cstr("ERR_SOCKET_DGRAM_NOT_RUNNING");
|
|
213
|
+
} else {
|
|
214
|
+
struct sockaddr_storage storage;
|
|
215
|
+
xt_socklen_t storageLength = 0;
|
|
216
|
+
if (xt_dgram_resolve(address, port, &storage, &storageLength) != 0) {
|
|
217
|
+
error = xt_string_from_cstr("EAI_FAIL");
|
|
218
|
+
} else if (sendto(fd, (const char *)bytes, length, 0, (struct sockaddr *)&storage, storageLength) < 0) {
|
|
219
|
+
error = xt_string_from_cstr("EIO");
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
free(bytes);
|
|
223
|
+
|
|
224
|
+
if (!xt_truthy(xt_is_nullish(error))) {
|
|
225
|
+
xt_node_emit1(thisValue, "error", error);
|
|
226
|
+
if (XT_IS_FUNCTION(callback)) xt_call_with_this(callback, xt_undefined(), 1, &error);
|
|
227
|
+
} else if (XT_IS_FUNCTION(callback)) {
|
|
228
|
+
xt_call_with_this(callback, xt_undefined(), 0, NULL);
|
|
229
|
+
}
|
|
230
|
+
return thisValue;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
static xt_value xt_dgram_close(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
|
|
234
|
+
(void)env;
|
|
235
|
+
int fd = xt_dgram_fd(thisValue);
|
|
236
|
+
if (fd >= 0) {
|
|
237
|
+
xt_loop_remove(fd);
|
|
238
|
+
xt_close_socket(fd);
|
|
239
|
+
}
|
|
240
|
+
xt_node_set(thisValue, "__fd", xt_number(-1));
|
|
241
|
+
xt_value callback = XT_IS_FUNCTION(xt_arg(argc, argv, 0)) ? xt_arg(argc, argv, 0) : XT_UNDEFINED;
|
|
242
|
+
xt_node_emit0(thisValue, "close");
|
|
243
|
+
if (XT_IS_FUNCTION(callback)) xt_call_with_this(callback, xt_undefined(), 0, NULL);
|
|
244
|
+
return thisValue;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
static xt_value xt_dgram_address(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
|
|
248
|
+
(void)env;
|
|
249
|
+
(void)argc;
|
|
250
|
+
(void)argv;
|
|
251
|
+
int fd = xt_dgram_fd(thisValue);
|
|
252
|
+
if (fd < 0) return xt_null();
|
|
253
|
+
struct sockaddr_storage storage;
|
|
254
|
+
xt_socklen_t length = sizeof(storage);
|
|
255
|
+
if (getsockname(fd, (struct sockaddr *)&storage, &length) != 0) return xt_null();
|
|
256
|
+
char host[NI_MAXHOST];
|
|
257
|
+
char service[NI_MAXSERV];
|
|
258
|
+
if (getnameinfo((struct sockaddr *)&storage, length, host, sizeof(host), service, sizeof(service),
|
|
259
|
+
NI_NUMERICHOST | NI_NUMERICSERV) != 0)
|
|
260
|
+
return xt_null();
|
|
261
|
+
xt_value object = xt_object_new();
|
|
262
|
+
xt_node_set(object, "address", xt_string_from_cstr(host));
|
|
263
|
+
xt_node_set(object, "port", xt_number((double)atoi(service)));
|
|
264
|
+
xt_node_set(object, "family", xt_string_from_cstr(strchr(host, ':') ? "IPv6" : "IPv4"));
|
|
265
|
+
return object;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
static xt_value xt_dgram_set_broadcast(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
|
|
269
|
+
(void)env;
|
|
270
|
+
int fd = xt_dgram_fd(thisValue);
|
|
271
|
+
int value = argc > 0 ? (int)xt_to_number(argv[0]) : 1;
|
|
272
|
+
if (fd >= 0) setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (const char *)&value, sizeof(value));
|
|
273
|
+
return thisValue;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
static xt_value xt_dgram_set_ttl(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
|
|
277
|
+
(void)env;
|
|
278
|
+
int fd = xt_dgram_fd(thisValue);
|
|
279
|
+
int value = argc > 0 ? (int)xt_to_number(argv[0]) : 1;
|
|
280
|
+
if (fd >= 0) setsockopt(fd, IPPROTO_IP, IP_TTL, (const char *)&value, sizeof(value));
|
|
281
|
+
return thisValue;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
static xt_value xt_dgram_noop(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
|
|
285
|
+
(void)env;
|
|
286
|
+
(void)argc;
|
|
287
|
+
(void)argv;
|
|
288
|
+
return thisValue;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
xt_value xt_node_dgram_create_socket(int32_t argc, xt_value *argv) {
|
|
292
|
+
const char *type = "udp4";
|
|
293
|
+
xt_value callback = XT_UNDEFINED;
|
|
294
|
+
if (argc > 0 && XT_IS_STRING(argv[0])) type = xt_string_data(argv[0]);
|
|
295
|
+
else if (argc > 0 && XT_IS_OBJECT(argv[0])) {
|
|
296
|
+
xt_value typeValue = xt_object_get_cstr(argv[0], "type");
|
|
297
|
+
if (XT_IS_STRING(typeValue)) type = xt_string_data(typeValue);
|
|
298
|
+
}
|
|
299
|
+
for (int32_t i = 0; i < argc; i++) {
|
|
300
|
+
if (XT_IS_FUNCTION(argv[i])) callback = argv[i];
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
xt_value socket = xt_object_new_with_proto(xt_dgram_socket_proto());
|
|
304
|
+
xt_node_set(socket, "__fd", xt_number(-1));
|
|
305
|
+
xt_node_set(socket, "__type", xt_string_from_cstr(type));
|
|
306
|
+
if (XT_IS_FUNCTION(callback)) xt_array_push(xt_node_listeners(socket, "message", 1), callback);
|
|
307
|
+
return socket;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
xt_value xt_dgram_static(xt_value name, int32_t argc, xt_value *argv) {
|
|
311
|
+
const char *fn = xt_string_data(name);
|
|
312
|
+
if (!fn) return xt_undefined();
|
|
313
|
+
if (strcmp(fn, "createSocket") == 0) return xt_node_dgram_create_socket(argc, argv);
|
|
314
|
+
return xt_undefined();
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
static xt_value xt_dgram_socket_proto(void) {
|
|
318
|
+
static xt_value proto = 0;
|
|
319
|
+
if (proto) return proto;
|
|
320
|
+
proto = xt_object_new();
|
|
321
|
+
xt_node_install_emitter(proto);
|
|
322
|
+
xt_node_define_method(proto, "bind", (void *)xt_dgram_bind);
|
|
323
|
+
xt_node_define_method(proto, "send", (void *)xt_dgram_send);
|
|
324
|
+
xt_node_define_method(proto, "close", (void *)xt_dgram_close);
|
|
325
|
+
xt_node_define_method(proto, "address", (void *)xt_dgram_address);
|
|
326
|
+
xt_node_define_method(proto, "setBroadcast", (void *)xt_dgram_set_broadcast);
|
|
327
|
+
xt_node_define_method(proto, "setTTL", (void *)xt_dgram_set_ttl);
|
|
328
|
+
xt_node_define_method(proto, "setMulticastTTL", (void *)xt_dgram_set_ttl);
|
|
329
|
+
xt_node_define_method(proto, "setMulticastLoopback", (void *)xt_dgram_set_broadcast);
|
|
330
|
+
xt_node_define_method(proto, "setRecvBufferSize", (void *)xt_dgram_noop);
|
|
331
|
+
xt_node_define_method(proto, "setSendBufferSize", (void *)xt_dgram_noop);
|
|
332
|
+
xt_node_define_method(proto, "addMembership", (void *)xt_dgram_noop);
|
|
333
|
+
xt_node_define_method(proto, "dropMembership", (void *)xt_dgram_noop);
|
|
334
|
+
xt_node_define_method(proto, "ref", (void *)xt_dgram_noop);
|
|
335
|
+
xt_node_define_method(proto, "unref", (void *)xt_dgram_noop);
|
|
336
|
+
return proto;
|
|
337
|
+
}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Shared helpers for the xbintsc Node `fs` runtime.
|
|
3
|
+
*
|
|
4
|
+
* The `fs` builtins live in several translation units (read_file.c,
|
|
5
|
+
* write_file.c, fs_ops.c) but agree on encoding handling and error reporting
|
|
6
|
+
* through these static inline helpers. Encoding support is deliberately
|
|
7
|
+
* buffer-free: `base64`/`hex` are encoded to strings directly, since xbintsc
|
|
8
|
+
* has no `Buffer` value type yet.
|
|
9
|
+
*/
|
|
10
|
+
#ifndef XT_NODE_FS_COMMON_H
|
|
11
|
+
#define XT_NODE_FS_COMMON_H
|
|
12
|
+
|
|
13
|
+
#include "rt.h"
|
|
14
|
+
|
|
15
|
+
#include <stdio.h>
|
|
16
|
+
#include <stdlib.h>
|
|
17
|
+
#include <string.h>
|
|
18
|
+
|
|
19
|
+
static const char xt_fs_base64_alphabet[] =
|
|
20
|
+
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
21
|
+
|
|
22
|
+
/* Accepts a bare encoding string or an options object with `encoding`. */
|
|
23
|
+
static inline const char *xt_fs_encoding(xt_value options) {
|
|
24
|
+
if (XT_IS_STRING(options)) return xt_string_data(options);
|
|
25
|
+
if (XT_IS_OBJECT(options)) {
|
|
26
|
+
xt_value encoding = xt_object_get_cstr(options, "encoding");
|
|
27
|
+
if (XT_IS_STRING(encoding)) return xt_string_data(encoding);
|
|
28
|
+
}
|
|
29
|
+
return NULL;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
static inline int xt_fs_is_hex(const char *encoding) {
|
|
33
|
+
return encoding && strcmp(encoding, "hex") == 0;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
static inline int xt_fs_is_base64(const char *encoding) {
|
|
37
|
+
return encoding && strcmp(encoding, "base64") == 0;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
static inline xt_value xt_fs_encode_hex(const unsigned char *data, size_t length) {
|
|
41
|
+
static const char digits[] = "0123456789abcdef";
|
|
42
|
+
char *buffer = (char *)malloc(length * 2 + 1);
|
|
43
|
+
if (!buffer) return xt_undefined();
|
|
44
|
+
for (size_t i = 0; i < length; i++) {
|
|
45
|
+
buffer[i * 2] = digits[data[i] >> 4];
|
|
46
|
+
buffer[i * 2 + 1] = digits[data[i] & 0x0f];
|
|
47
|
+
}
|
|
48
|
+
xt_value result = xt_string_new(buffer, length * 2);
|
|
49
|
+
free(buffer);
|
|
50
|
+
return result;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
static inline xt_value xt_fs_encode_base64(const unsigned char *data, size_t length) {
|
|
54
|
+
size_t outLength = ((length + 2) / 3) * 4;
|
|
55
|
+
char *buffer = (char *)malloc(outLength + 1);
|
|
56
|
+
if (!buffer) return xt_undefined();
|
|
57
|
+
size_t i = 0, j = 0;
|
|
58
|
+
while (i + 2 < length) {
|
|
59
|
+
uint32_t n = ((uint32_t)data[i] << 16) | ((uint32_t)data[i + 1] << 8) | (uint32_t)data[i + 2];
|
|
60
|
+
buffer[j++] = xt_fs_base64_alphabet[(n >> 18) & 63];
|
|
61
|
+
buffer[j++] = xt_fs_base64_alphabet[(n >> 12) & 63];
|
|
62
|
+
buffer[j++] = xt_fs_base64_alphabet[(n >> 6) & 63];
|
|
63
|
+
buffer[j++] = xt_fs_base64_alphabet[n & 63];
|
|
64
|
+
i += 3;
|
|
65
|
+
}
|
|
66
|
+
if (length - i == 1) {
|
|
67
|
+
uint32_t n = (uint32_t)data[i] << 16;
|
|
68
|
+
buffer[j++] = xt_fs_base64_alphabet[(n >> 18) & 63];
|
|
69
|
+
buffer[j++] = xt_fs_base64_alphabet[(n >> 12) & 63];
|
|
70
|
+
buffer[j++] = '=';
|
|
71
|
+
buffer[j++] = '=';
|
|
72
|
+
} else if (length - i == 2) {
|
|
73
|
+
uint32_t n = ((uint32_t)data[i] << 16) | ((uint32_t)data[i + 1] << 8);
|
|
74
|
+
buffer[j++] = xt_fs_base64_alphabet[(n >> 18) & 63];
|
|
75
|
+
buffer[j++] = xt_fs_base64_alphabet[(n >> 12) & 63];
|
|
76
|
+
buffer[j++] = xt_fs_base64_alphabet[(n >> 6) & 63];
|
|
77
|
+
buffer[j++] = '=';
|
|
78
|
+
}
|
|
79
|
+
xt_value result = xt_string_new(buffer, j);
|
|
80
|
+
free(buffer);
|
|
81
|
+
return result;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/* Encode bytes for `readFileSync` according to `encoding` (default raw UTF-8). */
|
|
85
|
+
static inline xt_value xt_fs_encode_bytes(const unsigned char *data, size_t length, const char *encoding) {
|
|
86
|
+
if (xt_fs_is_hex(encoding)) return xt_fs_encode_hex(data, length);
|
|
87
|
+
if (xt_fs_is_base64(encoding)) return xt_fs_encode_base64(data, length);
|
|
88
|
+
return xt_string_new((const char *)data, length);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
static inline int xt_fs_base64_value(char c) {
|
|
92
|
+
if (c >= 'A' && c <= 'Z') return c - 'A';
|
|
93
|
+
if (c >= 'a' && c <= 'z') return c - 'a' + 26;
|
|
94
|
+
if (c >= '0' && c <= '9') return c - '0' + 52;
|
|
95
|
+
if (c == '+') return 62;
|
|
96
|
+
if (c == '/') return 63;
|
|
97
|
+
return -1;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
static inline int xt_fs_hex_value(char c) {
|
|
101
|
+
if (c >= '0' && c <= '9') return c - '0';
|
|
102
|
+
if (c >= 'a' && c <= 'f') return c - 'a' + 10;
|
|
103
|
+
if (c >= 'A' && c <= 'F') return c - 'A' + 10;
|
|
104
|
+
return -1;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/*
|
|
108
|
+
* Decode text for `writeFileSync` according to `encoding`. Returns a malloc'd
|
|
109
|
+
* byte buffer (never NULL unless allocation failed) and stores its length in
|
|
110
|
+
* `*outLength`; the caller frees it.
|
|
111
|
+
*/
|
|
112
|
+
static inline unsigned char *xt_fs_decode_text(const char *text, size_t length, const char *encoding, size_t *outLength) {
|
|
113
|
+
unsigned char *bytes = NULL;
|
|
114
|
+
if (xt_fs_is_base64(encoding)) {
|
|
115
|
+
size_t capacity = (length / 4 + 1) * 3;
|
|
116
|
+
bytes = (unsigned char *)malloc(capacity + 1);
|
|
117
|
+
if (!bytes) return NULL;
|
|
118
|
+
size_t written = 0;
|
|
119
|
+
int accumulator = 0, bits = 0;
|
|
120
|
+
for (size_t i = 0; i < length; i++) {
|
|
121
|
+
if (text[i] == '=') break;
|
|
122
|
+
int value = xt_fs_base64_value(text[i]);
|
|
123
|
+
if (value < 0) continue;
|
|
124
|
+
accumulator = (accumulator << 6) | value;
|
|
125
|
+
bits += 6;
|
|
126
|
+
if (bits >= 8) {
|
|
127
|
+
bits -= 8;
|
|
128
|
+
bytes[written++] = (unsigned char)((accumulator >> bits) & 0xff);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
bytes[written] = '\0';
|
|
132
|
+
*outLength = written;
|
|
133
|
+
return bytes;
|
|
134
|
+
}
|
|
135
|
+
if (xt_fs_is_hex(encoding)) {
|
|
136
|
+
bytes = (unsigned char *)malloc(length / 2 + 1);
|
|
137
|
+
if (!bytes) return NULL;
|
|
138
|
+
size_t written = 0;
|
|
139
|
+
int high = -1;
|
|
140
|
+
for (size_t i = 0; i < length; i++) {
|
|
141
|
+
int value = xt_fs_hex_value(text[i]);
|
|
142
|
+
if (value < 0) continue;
|
|
143
|
+
if (high < 0) {
|
|
144
|
+
high = value;
|
|
145
|
+
} else {
|
|
146
|
+
bytes[written++] = (unsigned char)((high << 4) | value);
|
|
147
|
+
high = -1;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
bytes[written] = '\0';
|
|
151
|
+
*outLength = written;
|
|
152
|
+
return bytes;
|
|
153
|
+
}
|
|
154
|
+
bytes = (unsigned char *)malloc(length + 1);
|
|
155
|
+
if (!bytes) return NULL;
|
|
156
|
+
if (length > 0) memcpy(bytes, text, length);
|
|
157
|
+
bytes[length] = '\0';
|
|
158
|
+
*outLength = length;
|
|
159
|
+
return bytes;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
static inline void xt_fs_error(const char *action, const char *path) {
|
|
163
|
+
fprintf(stderr, "xbintsc: cannot %s '%s'\n", action, path ? path : "");
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
#endif /* XT_NODE_FS_COMMON_H */
|