script folder
This commit is contained in:
@@ -9,13 +9,11 @@
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// blob fns
|
||||
JSValue js_blob_use(JSContext *js);
|
||||
// makes a new stone blob from data, copying the data over
|
||||
JSValue js_new_blob_stoned_copy(JSContext *js, void *data, size_t bytes);
|
||||
|
||||
// returns undefined if the blob is not stone
|
||||
void *js_get_blob_data(JSContext *js, size_t *size, JSValue v);
|
||||
|
||||
int js_is_blob(JSContext *js, JSValue v);
|
||||
|
||||
#ifdef HAVE_MIMALLOC
|
||||
|
||||
@@ -7,71 +7,6 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
// External function declarations
|
||||
JSValue js_actor_delay(JSContext *js, JSValue self, int argc, JSValue *argv);
|
||||
JSValue js_actor_removetimer(JSContext *js, JSValue self, int argc, JSValue *argv);
|
||||
|
||||
cell_rt *js2actor(JSContext *js, JSValue v)
|
||||
{
|
||||
if (!JS_IsObject(v))
|
||||
return NULL;
|
||||
|
||||
cell_rt *crt = JS_GetContextOpaque(js);
|
||||
JSValue actor_data = JS_GetProperty(js, v, crt->actor_sym);
|
||||
|
||||
if (JS_IsNull(actor_data)) {
|
||||
JS_FreeValue(js, actor_data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
JSValue id_val = JS_GetPropertyStr(js, actor_data, "id");
|
||||
JS_FreeValue(js, actor_data);
|
||||
|
||||
if (JS_IsNull(id_val)) {
|
||||
JS_FreeValue(js, id_val);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *id = JS_ToCString(js, id_val);
|
||||
JS_FreeValue(js, id_val);
|
||||
|
||||
if (!id)
|
||||
return NULL;
|
||||
|
||||
cell_rt *actor = get_actor((char*)id);
|
||||
JS_FreeCString(js, id);
|
||||
|
||||
return actor;
|
||||
}
|
||||
|
||||
JSValue actor2js(JSContext *js, cell_rt *actor)
|
||||
{
|
||||
if (!actor)
|
||||
return JS_NULL;
|
||||
|
||||
JSValue actor_obj = JS_NewObject(js);
|
||||
if (JS_IsException(actor_obj))
|
||||
return actor_obj;
|
||||
|
||||
JSValue actor_data = JS_NewObject(js);
|
||||
if (JS_IsException(actor_data)) {
|
||||
JS_FreeValue(js, actor_obj);
|
||||
return actor_data;
|
||||
}
|
||||
|
||||
JS_SetPropertyStr(js, actor_data, "id", JS_NewString(js, actor->id));
|
||||
|
||||
/* TODO: If the actor has network info, we could add address and port here */
|
||||
/* JS_SetPropertyStr(js, actor_data, "address", JS_NewString(js, actor->address)); */
|
||||
/* JS_SetPropertyStr(js, actor_data, "port", JS_NewInt32(js, actor->port)); */
|
||||
|
||||
cell_rt *crt = JS_GetContextOpaque(js);
|
||||
|
||||
JS_SetProperty(js, actor_obj, crt->actor_sym, actor_data);
|
||||
|
||||
return actor_obj;
|
||||
}
|
||||
|
||||
JSC_CCALL(os_createactor,
|
||||
cell_rt *rt = JS_GetContextOpaque(js);
|
||||
if (rt->disrupt)
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
#ifndef QJS_ACTOR_H
|
||||
#define QJS_ACTOR_H
|
||||
|
||||
#include "cell.h"
|
||||
|
||||
JSValue js_actor_use(JSContext *js);
|
||||
cell_rt *js2actor(JSContext *js, JSValue v);
|
||||
JSValue actor2js(JSContext *js, cell_rt *actor);
|
||||
JSValue js_actor_set_symbol(JSContext *js, JSValue self, int argc, JSValue *argv);
|
||||
|
||||
#endif
|
||||
@@ -1,258 +0,0 @@
|
||||
#include "cell.h"
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "monocypher.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#if defined(_WIN32)
|
||||
// ------- Windows: use BCryptGenRandom -------
|
||||
#include <windows.h>
|
||||
#include <bcrypt.h>
|
||||
|
||||
int randombytes(void *buf, size_t n) {
|
||||
NTSTATUS status = BCryptGenRandom(NULL, (PUCHAR)buf, (ULONG)n, BCRYPT_USE_SYSTEM_PREFERRED_RNG);
|
||||
return (status == 0) ? 0 : -1;
|
||||
}
|
||||
|
||||
#elif defined(__linux__)
|
||||
// ------- Linux: try getrandom, fall back to /dev/urandom -------
|
||||
#include <unistd.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
// If we have a new enough libc and kernel, getrandom is available.
|
||||
// Otherwise, we’ll do a /dev/urandom fallback.
|
||||
#include <sys/stat.h>
|
||||
|
||||
static int randombytes_fallback(void *buf, size_t n) {
|
||||
int fd = open("/dev/urandom", O_RDONLY);
|
||||
if (fd < 0) return -1;
|
||||
ssize_t r = read(fd, buf, n);
|
||||
close(fd);
|
||||
return (r == (ssize_t)n) ? 0 : -1;
|
||||
}
|
||||
|
||||
int randombytes(void *buf, size_t n) {
|
||||
#ifdef SYS_getrandom
|
||||
// Try getrandom(2) if available
|
||||
ssize_t ret = syscall(SYS_getrandom, buf, n, 0);
|
||||
if (ret < 0) {
|
||||
// If getrandom is not supported or fails, fall back
|
||||
if (errno == ENOSYS) {
|
||||
return randombytes_fallback(buf, n);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
return (ret == (ssize_t)n) ? 0 : -1;
|
||||
#else
|
||||
// getrandom not available, just fallback
|
||||
return randombytes_fallback(buf, n);
|
||||
#endif
|
||||
}
|
||||
|
||||
#else
|
||||
// ------- Other Unix: read from /dev/urandom -------
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int randombytes(void *buf, size_t n) {
|
||||
int fd = open("/dev/urandom", O_RDONLY);
|
||||
if (fd < 0) return -1;
|
||||
ssize_t r = read(fd, buf, n);
|
||||
close(fd);
|
||||
return (r == (ssize_t)n) ? 0 : -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline void to_hex(const uint8_t *in, size_t in_len, char *out)
|
||||
{
|
||||
static const char hexchars[] = "0123456789abcdef";
|
||||
for (size_t i = 0; i < in_len; i++) {
|
||||
out[2*i ] = hexchars[(in[i] >> 4) & 0x0F];
|
||||
out[2*i + 1] = hexchars[ in[i] & 0x0F];
|
||||
}
|
||||
out[2 * in_len] = '\0'; // null-terminate
|
||||
}
|
||||
|
||||
static inline int nibble_from_char(char c, uint8_t *nibble)
|
||||
{
|
||||
if (c >= '0' && c <= '9') { *nibble = (uint8_t)(c - '0'); return 0; }
|
||||
if (c >= 'a' && c <= 'f') { *nibble = (uint8_t)(c - 'a' + 10); return 0; }
|
||||
if (c >= 'A' && c <= 'F') { *nibble = (uint8_t)(c - 'A' + 10); return 0; }
|
||||
return -1; // invalid char
|
||||
}
|
||||
|
||||
static inline int from_hex(const char *hex, uint8_t *out, size_t out_len)
|
||||
{
|
||||
for (size_t i = 0; i < out_len; i++) {
|
||||
uint8_t hi, lo;
|
||||
if (nibble_from_char(hex[2*i], &hi) < 0) return -1;
|
||||
if (nibble_from_char(hex[2*i + 1], &lo) < 0) return -1;
|
||||
out[i] = (uint8_t)((hi << 4) | lo);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Convert a JSValue containing a 64-character hex string into a 32-byte array.
|
||||
static inline void js2crypto(JSContext *js, JSValue v, uint8_t *crypto)
|
||||
{
|
||||
size_t hex_len;
|
||||
const char *hex_str = JS_ToCStringLen(js, &hex_len, v);
|
||||
if (!hex_str)
|
||||
return;
|
||||
|
||||
if (hex_len != 64) {
|
||||
JS_FreeCString(js, hex_str);
|
||||
JS_ThrowTypeError(js, "js2crypto: expected 64-hex-char string");
|
||||
return;
|
||||
}
|
||||
|
||||
if (from_hex(hex_str, crypto, 32) < 0) {
|
||||
JS_FreeCString(js, hex_str);
|
||||
JS_ThrowTypeError(js, "js2crypto: invalid hex encoding");
|
||||
return;
|
||||
}
|
||||
|
||||
JS_FreeCString(js, hex_str);
|
||||
}
|
||||
|
||||
static inline JSValue crypto2js(JSContext *js, const uint8_t *crypto)
|
||||
{
|
||||
char hex[65]; // 32*2 + 1 for null terminator
|
||||
to_hex(crypto, 32, hex);
|
||||
return JS_NewString(js, hex);
|
||||
}
|
||||
|
||||
JSValue js_crypto_keypair(JSContext *js, JSValue self, int argc, JSValue *argv) {
|
||||
JSValue ret = JS_NewObject(js);
|
||||
|
||||
uint8_t public[32];
|
||||
uint8_t private[32];
|
||||
|
||||
randombytes(private,32);
|
||||
|
||||
private[0] &= 248;
|
||||
private[31] &= 127;
|
||||
private[31] |= 64;
|
||||
|
||||
crypto_x25519_public_key(public,private);
|
||||
|
||||
JS_SetPropertyStr(js, ret, "public", crypto2js(js, public));
|
||||
JS_SetPropertyStr(js, ret, "private", crypto2js(js,private));
|
||||
return ret;
|
||||
}
|
||||
|
||||
JSValue js_crypto_shared(JSContext *js, JSValue self, int argc, JSValue *argv)
|
||||
{
|
||||
if (argc < 1 || !JS_IsObject(argv[0])) {
|
||||
return JS_ThrowTypeError(js, "crypto.shared: expected an object argument");
|
||||
}
|
||||
|
||||
JSValue obj = argv[0];
|
||||
|
||||
JSValue val_pub = JS_GetPropertyStr(js, obj, "public");
|
||||
if (JS_IsException(val_pub)) {
|
||||
JS_FreeValue(js, val_pub);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
JSValue val_priv = JS_GetPropertyStr(js, obj, "private");
|
||||
if (JS_IsException(val_priv)) {
|
||||
JS_FreeValue(js, val_pub);
|
||||
JS_FreeValue(js, val_priv);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
uint8_t pub[32], priv[32];
|
||||
js2crypto(js, val_pub, pub);
|
||||
js2crypto(js, val_priv, priv);
|
||||
|
||||
JS_FreeValue(js, val_pub);
|
||||
JS_FreeValue(js, val_priv);
|
||||
|
||||
uint8_t shared[32];
|
||||
crypto_x25519(shared, priv, pub);
|
||||
|
||||
return crypto2js(js, shared);
|
||||
}
|
||||
|
||||
JSValue js_crypto_random(JSContext *js, JSValue self, int argc, JSValue *argv)
|
||||
{
|
||||
// 1) Pull 64 bits of cryptographically secure randomness
|
||||
uint64_t r;
|
||||
if (randombytes(&r, sizeof(r)) != 0) {
|
||||
// If something fails (extremely rare), throw an error
|
||||
return JS_ThrowInternalError(js, "crypto.random: unable to get random bytes");
|
||||
}
|
||||
|
||||
// 2) Convert r to a double in the range [0,1).
|
||||
// We divide by (UINT64_MAX + 1.0) to ensure we never produce exactly 1.0.
|
||||
double val = (double)r / ((double)UINT64_MAX + 1.0);
|
||||
|
||||
// 3) Return that as a JavaScript number
|
||||
return JS_NewFloat64(js, val);
|
||||
}
|
||||
|
||||
JSValue js_crypto_random_fit(JSContext *js, JSValue self, int argc, JSValue *argv)
|
||||
{
|
||||
int32_t r;
|
||||
if (randombytes(&r, sizeof(r)) != 0) {
|
||||
return JS_ThrowInternalError(js, "crypto.random: unable to get random bytes");
|
||||
}
|
||||
|
||||
return JS_NewInt32(js, r);
|
||||
}
|
||||
|
||||
JSValue js_crypto_hash(JSContext *js, JSValue self, int argc, JSValue *argv)
|
||||
{
|
||||
if (argc < 1)
|
||||
return JS_ThrowTypeError(js, "hash requires at least one argument");
|
||||
|
||||
// Get input data
|
||||
size_t data_len;
|
||||
void *data = js_get_blob_data(js, &data_len, argv[0]);
|
||||
if (!data)
|
||||
return JS_ThrowTypeError(js, "hash: first argument must be an ArrayBuffer");
|
||||
|
||||
// Get hash length (default 32)
|
||||
int32_t hash_len = 32;
|
||||
if (argc > 1) {
|
||||
if (JS_ToInt32(js, &hash_len, argv[1]))
|
||||
return JS_EXCEPTION;
|
||||
if (hash_len < 1 || hash_len > 64)
|
||||
return JS_ThrowRangeError(js, "hash length must be between 1 and 64");
|
||||
}
|
||||
|
||||
// Allocate output buffer
|
||||
uint8_t *hash = js_malloc(js, hash_len);
|
||||
if (!hash)
|
||||
return JS_EXCEPTION;
|
||||
|
||||
// Compute BLAKE2b hash
|
||||
crypto_blake2b(hash, hash_len, data, data_len);
|
||||
|
||||
// Return as blob
|
||||
JSValue result = js_new_blob_stoned_copy(js, hash, hash_len);
|
||||
js_free(js, hash);
|
||||
return result;
|
||||
}
|
||||
|
||||
static const JSCFunctionListEntry js_crypto_funcs[] = {
|
||||
JS_CFUNC_DEF("keypair", 0, js_crypto_keypair),
|
||||
JS_CFUNC_DEF("shared", 1, js_crypto_shared),
|
||||
JS_CFUNC_DEF("random", 0, js_crypto_random),
|
||||
JS_CFUNC_DEF("random_fit", 0, js_crypto_random_fit),
|
||||
JS_CFUNC_DEF("hash", 2, js_crypto_hash),
|
||||
};
|
||||
|
||||
JSValue js_crypto_use(JSContext *js)
|
||||
{
|
||||
JSValue obj = JS_NewObject(js);
|
||||
JS_SetPropertyFunctionList(js, obj, js_crypto_funcs, sizeof(js_crypto_funcs)/sizeof(js_crypto_funcs[0]));
|
||||
return obj;
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
#include "cell.h"
|
||||
#include "qjs_macros.h"
|
||||
|
||||
JSC_CCALL(debug_stack_depth, return number2js(js,js_debugger_stack_depth(js)))
|
||||
JSC_CCALL(debug_build_backtrace, return js_debugger_build_backtrace(js,NULL))
|
||||
JSC_CCALL(debug_closure_vars, return js_debugger_closure_variables(js,argv[0]))
|
||||
JSC_CCALL(debug_local_vars, return js_debugger_local_variables(js, js2number(js,argv[0])))
|
||||
JSC_CCALL(debug_fn_info, return js_debugger_fn_info(js, argv[0]))
|
||||
JSC_CCALL(debug_backtrace_fns, return js_debugger_backtrace_fns(js,NULL))
|
||||
|
||||
static const JSCFunctionListEntry js_debug_funcs[] = {
|
||||
MIST_FUNC_DEF(debug, stack_depth, 0),
|
||||
MIST_FUNC_DEF(debug, build_backtrace, 0),
|
||||
MIST_FUNC_DEF(debug, closure_vars, 1),
|
||||
MIST_FUNC_DEF(debug, local_vars, 1),
|
||||
MIST_FUNC_DEF(debug, fn_info, 1),
|
||||
MIST_FUNC_DEF(debug, backtrace_fns,0),
|
||||
};
|
||||
|
||||
JSValue js_debug_use(JSContext *js) {
|
||||
JSValue mod = JS_NewObject(js);
|
||||
JS_SetPropertyFunctionList(js,mod,js_debug_funcs,countof(js_debug_funcs));
|
||||
return mod;
|
||||
}
|
||||
@@ -1,544 +0,0 @@
|
||||
#include "cell.h"
|
||||
#include <enet/enet.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
#define countof(a) (sizeof(a)/sizeof(*(a)))
|
||||
|
||||
static JSClassID enet_host_id;
|
||||
static JSClassID enet_peer_class_id;
|
||||
|
||||
/* Finalizers */
|
||||
static void js_enet_host_finalizer(JSRuntime *rt, JSValue val)
|
||||
{
|
||||
ENetHost *host = JS_GetOpaque(val, enet_host_id);
|
||||
if (host) enet_host_destroy(host);
|
||||
}
|
||||
|
||||
static void js_enet_peer_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func)
|
||||
{
|
||||
ENetPeer *peer = JS_GetOpaque(val, enet_peer_class_id);
|
||||
JS_MarkValue(rt, *(JSValue*)peer->data, mark_func);
|
||||
}
|
||||
|
||||
static void js_enet_peer_finalizer(JSRuntime *rt, JSValue val)
|
||||
{
|
||||
ENetPeer *peer = JS_GetOpaque(val, enet_peer_class_id);
|
||||
JS_FreeValueRT(rt, *(JSValue*)peer->data);
|
||||
free(peer->data);
|
||||
}
|
||||
|
||||
/* ENet init/deinit */
|
||||
static JSValue js_enet_initialize(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
|
||||
{
|
||||
if (enet_initialize() != 0) return JS_ThrowInternalError(ctx, "Error initializing ENet");
|
||||
return JS_NULL;
|
||||
}
|
||||
|
||||
static JSValue js_enet_deinitialize(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
|
||||
{
|
||||
enet_deinitialize();
|
||||
return JS_NULL;
|
||||
}
|
||||
|
||||
static JSValue js_enet_host_create(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
|
||||
{
|
||||
ENetHost *host;
|
||||
ENetAddress address;
|
||||
ENetAddress *send = &address;
|
||||
size_t peer_count = 1000;
|
||||
size_t channel_limit = 0;
|
||||
enet_uint32 incoming_bandwidth = 0;
|
||||
enet_uint32 outgoing_bandwidth = 0;
|
||||
JSValue obj;
|
||||
|
||||
if (argc < 1 || !JS_IsObject(argv[0])) {
|
||||
host = enet_host_create(NULL, peer_count, channel_limit, incoming_bandwidth, outgoing_bandwidth);
|
||||
if (!host) return JS_ThrowInternalError(ctx, "Failed to create ENet client host");
|
||||
goto wrap;
|
||||
}
|
||||
|
||||
JSValue config_obj = argv[0];
|
||||
JSValue addr_val = JS_GetPropertyStr(ctx, config_obj, "address");
|
||||
const char *addr_str = JS_IsString(addr_val) ? JS_ToCString(ctx, addr_val) : NULL;
|
||||
JS_FreeValue(ctx, addr_val);
|
||||
|
||||
if (!addr_str)
|
||||
send = NULL;
|
||||
else {
|
||||
JSValue port_val = JS_GetPropertyStr(ctx, config_obj, "port");
|
||||
int32_t port32 = 0;
|
||||
JS_ToInt32(ctx, &port32, port_val);
|
||||
JS_FreeValue(ctx, port_val);
|
||||
|
||||
if (strcmp(addr_str, "any") == 0)
|
||||
address.host = ENET_HOST_ANY;
|
||||
else if (strcmp(addr_str, "broadcast") == 0)
|
||||
address.host = ENET_HOST_BROADCAST;
|
||||
else {
|
||||
int err = enet_address_set_host_ip(&address, addr_str);
|
||||
if (err != 0) {
|
||||
JS_FreeCString(ctx, addr_str);
|
||||
return JS_ThrowInternalError(ctx, "Failed to set host IP from '%s'. Error: %d", addr_str, err);
|
||||
}
|
||||
}
|
||||
address.port = (enet_uint16)port32;
|
||||
JS_FreeCString(ctx, addr_str);
|
||||
}
|
||||
|
||||
JSValue chan_val = JS_GetPropertyStr(ctx, config_obj, "channels");
|
||||
JS_ToUint32(ctx, &channel_limit, chan_val);
|
||||
JS_FreeValue(ctx, chan_val);
|
||||
|
||||
JSValue in_bw_val = JS_GetPropertyStr(ctx, config_obj, "incoming_bandwidth");
|
||||
JS_ToUint32(ctx, &incoming_bandwidth, in_bw_val);
|
||||
JS_FreeValue(ctx, in_bw_val);
|
||||
|
||||
JSValue out_bw_val = JS_GetPropertyStr(ctx, config_obj, "outgoing_bandwidth");
|
||||
JS_ToUint32(ctx, &outgoing_bandwidth, out_bw_val);
|
||||
JS_FreeValue(ctx, out_bw_val);
|
||||
|
||||
host = enet_host_create(send, peer_count, channel_limit, incoming_bandwidth, outgoing_bandwidth);
|
||||
if (!host) return JS_ThrowInternalError(ctx, "Failed to create ENet host");
|
||||
|
||||
wrap:
|
||||
obj = JS_NewObjectClass(ctx, enet_host_id);
|
||||
if (JS_IsException(obj)) {
|
||||
enet_host_destroy(host);
|
||||
return obj;
|
||||
}
|
||||
JS_SetOpaque(obj, host);
|
||||
return obj;
|
||||
}
|
||||
|
||||
static JSValue peer_get_value(JSContext *ctx, ENetPeer *peer)
|
||||
{
|
||||
if (!peer->data) {
|
||||
peer->data = malloc(sizeof(JSValue));
|
||||
*(JSValue*)peer->data = JS_NewObjectClass(ctx, enet_peer_class_id);
|
||||
JS_SetOpaque(*(JSValue*)peer->data, peer);
|
||||
}
|
||||
return JS_DupValue(ctx, *(JSValue*)peer->data);
|
||||
}
|
||||
|
||||
/* Host service: poll for events */
|
||||
static JSValue js_enet_host_service(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
|
||||
{
|
||||
ENetHost *host = JS_GetOpaque(this_val, enet_host_id);
|
||||
if (!host) return JS_EXCEPTION;
|
||||
|
||||
if (argc < 1 || !JS_IsFunction(ctx, argv[0])) return JS_ThrowTypeError(ctx, "Expected a callback function as first argument");
|
||||
JSValue callback = JS_DupValue(ctx, argv[0]);
|
||||
|
||||
double secs;
|
||||
JS_ToFloat64(ctx, &secs, argv[1]);
|
||||
|
||||
ENetEvent event;
|
||||
while (enet_host_service(host, &event, secs*1000.0f) > 0) {
|
||||
JSValue event_obj = JS_NewObject(ctx);
|
||||
JS_SetPropertyStr(ctx, event_obj, "peer", peer_get_value(ctx, event.peer));
|
||||
|
||||
switch (event.type) {
|
||||
case ENET_EVENT_TYPE_CONNECT:
|
||||
JS_SetPropertyStr(ctx, event_obj, "type", JS_NewString(ctx, "connect"));
|
||||
break;
|
||||
case ENET_EVENT_TYPE_RECEIVE:
|
||||
JS_SetPropertyStr(ctx, event_obj, "type", JS_NewString(ctx, "receive"));
|
||||
JS_SetPropertyStr(ctx, event_obj, "channelID", JS_NewInt32(ctx, event.channelID));
|
||||
|
||||
// Pass raw data as string or ArrayBuffer
|
||||
if (event.packet->dataLength > 0) {
|
||||
JSValue data_val = js_new_blob_stoned_copy(ctx, event.packet->data, event.packet->dataLength);
|
||||
JS_SetPropertyStr(ctx, event_obj, "data", data_val);
|
||||
}
|
||||
enet_packet_destroy(event.packet);
|
||||
break;
|
||||
case ENET_EVENT_TYPE_DISCONNECT:
|
||||
JS_SetPropertyStr(ctx, event_obj, "type", JS_NewString(ctx, "disconnect"));
|
||||
break;
|
||||
case ENET_EVENT_TYPE_NONE:
|
||||
JS_SetPropertyStr(ctx, event_obj, "type", JS_NewString(ctx, "none"));
|
||||
break;
|
||||
}
|
||||
|
||||
uncaught_exception(ctx, JS_Call(ctx, callback, JS_NULL, 1, &event_obj));
|
||||
JS_FreeValue(ctx, event_obj);
|
||||
}
|
||||
|
||||
JS_FreeValue(ctx, callback);
|
||||
return JS_NULL;
|
||||
}
|
||||
|
||||
/* Host connect: client -> connect to server */
|
||||
static JSValue js_enet_host_connect(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
|
||||
{
|
||||
ENetHost *host = JS_GetOpaque(this_val, enet_host_id);
|
||||
if (!host) return JS_EXCEPTION;
|
||||
|
||||
if (argc < 2) return JS_ThrowTypeError(ctx, "Expected 2 arguments: hostname, port");
|
||||
|
||||
const char *hostname = JS_ToCString(ctx, argv[0]);
|
||||
if (!hostname) return JS_EXCEPTION;
|
||||
int port;
|
||||
JS_ToInt32(ctx, &port, argv[1]);
|
||||
|
||||
ENetAddress address;
|
||||
enet_address_set_host(&address, hostname);
|
||||
JS_FreeCString(ctx, hostname);
|
||||
address.port = port;
|
||||
|
||||
ENetPeer *peer = enet_host_connect(host, &address, 2, 0);
|
||||
if (!peer) return JS_ThrowInternalError(ctx, "No available peers for initiating an ENet connection");
|
||||
|
||||
return peer_get_value(ctx, peer);
|
||||
}
|
||||
|
||||
/* Flush queued packets */
|
||||
static JSValue js_enet_host_flush(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
|
||||
{
|
||||
ENetHost *host = JS_GetOpaque(this_val, enet_host_id);
|
||||
if (!host) return JS_EXCEPTION;
|
||||
enet_host_flush(host);
|
||||
return JS_NULL;
|
||||
}
|
||||
|
||||
/* Broadcast a string or ArrayBuffer */
|
||||
static JSValue js_enet_host_broadcast(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
|
||||
{
|
||||
ENetHost *host = JS_GetOpaque(this_val, enet_host_id);
|
||||
if (!host) return JS_EXCEPTION;
|
||||
|
||||
if (argc < 1) return JS_ThrowTypeError(ctx, "Expected a string or ArrayBuffer to broadcast");
|
||||
|
||||
const char *data_str = NULL;
|
||||
size_t data_len = 0;
|
||||
uint8_t *buf = NULL;
|
||||
|
||||
if (JS_IsString(argv[0])) {
|
||||
data_str = JS_ToCStringLen(ctx, &data_len, argv[0]);
|
||||
if (!data_str) return JS_EXCEPTION;
|
||||
} else if (js_is_blob(ctx,argv[0])) {
|
||||
buf = js_get_blob_data(ctx, &data_len, argv[0]);
|
||||
if (!buf) return JS_EXCEPTION;
|
||||
} else {
|
||||
return JS_ThrowTypeError(ctx, "broadcast() only accepts a string or ArrayBuffer");
|
||||
}
|
||||
|
||||
ENetPacket *packet = enet_packet_create(data_str ? data_str : buf, data_len, ENET_PACKET_FLAG_RELIABLE);
|
||||
if (data_str) JS_FreeCString(ctx, data_str);
|
||||
if (!packet) return JS_ThrowInternalError(ctx, "Failed to create ENet packet");
|
||||
|
||||
enet_host_broadcast(host, 0, packet);
|
||||
return JS_NULL;
|
||||
}
|
||||
|
||||
static JSValue js_enet_host_get_port(JSContext *js, JSValueConst self, int argc, JSValueConst *argv)
|
||||
{
|
||||
ENetHost *host = JS_GetOpaque(self, enet_host_id);
|
||||
if (!host) return JS_EXCEPTION;
|
||||
return JS_NewInt32(js, host->address.port);
|
||||
}
|
||||
|
||||
static JSValue js_enet_host_get_address(JSContext *js, JSValueConst self, int argc, JSValueConst *argv)
|
||||
{
|
||||
ENetHost *me = JS_GetOpaque(self, enet_host_id);
|
||||
if (!me) return JS_EXCEPTION;
|
||||
|
||||
uint32_t host = ntohl(me->address.host);
|
||||
if (host == 0x7F000001) return JS_NewString(js, "localhost");
|
||||
|
||||
char ip_str[16];
|
||||
snprintf(ip_str, sizeof(ip_str), "%u.%u.%u.%u",
|
||||
(host >> 24) & 0xFF,
|
||||
(host >> 16) & 0xFF,
|
||||
(host >> 8) & 0xFF,
|
||||
host & 0xFF);
|
||||
return JS_NewString(js, ip_str);
|
||||
}
|
||||
|
||||
/* Peer-level operations */
|
||||
static JSValue js_enet_peer_disconnect(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
|
||||
{
|
||||
ENetPeer *peer = JS_GetOpaque(this_val, enet_peer_class_id);
|
||||
if (!peer) return JS_EXCEPTION;
|
||||
enet_peer_disconnect(peer, 0);
|
||||
return JS_NULL;
|
||||
}
|
||||
|
||||
/* Peer send must only accept string or ArrayBuffer */
|
||||
static JSValue js_enet_peer_send(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
|
||||
{
|
||||
ENetPeer *peer = JS_GetOpaque(this_val, enet_peer_class_id);
|
||||
if (!peer) return JS_EXCEPTION;
|
||||
|
||||
if (argc < 1) return JS_ThrowTypeError(ctx, "Expected a string or ArrayBuffer to send");
|
||||
|
||||
const char *data_str = NULL;
|
||||
size_t data_len = 0;
|
||||
uint8_t *buf = NULL;
|
||||
|
||||
if (JS_IsString(argv[0])) {
|
||||
data_str = JS_ToCStringLen(ctx, &data_len, argv[0]);
|
||||
if (!data_str) return JS_EXCEPTION;
|
||||
} else if (js_is_blob(ctx,argv[0])) {
|
||||
buf = js_get_blob_data(ctx, &data_len, argv[0]);
|
||||
if (!buf) return JS_EXCEPTION;
|
||||
} else {
|
||||
return JS_ThrowTypeError(ctx, "send() only accepts a string or ArrayBuffer");
|
||||
}
|
||||
|
||||
ENetPacket *packet = enet_packet_create(data_str ? data_str : buf, data_len, ENET_PACKET_FLAG_RELIABLE);
|
||||
if (data_str) JS_FreeCString(ctx, data_str);
|
||||
if (!packet) return JS_ThrowInternalError(ctx, "Failed to create ENet packet");
|
||||
|
||||
if (enet_peer_send(peer, 0, packet) < 0) return JS_ThrowInternalError(ctx, "Unable to send packet");
|
||||
return JS_NULL;
|
||||
}
|
||||
|
||||
static JSValue js_enet_peer_disconnect_now(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
|
||||
{
|
||||
ENetPeer *peer = JS_GetOpaque(this_val, enet_peer_class_id);
|
||||
if (!peer) return JS_EXCEPTION;
|
||||
enet_peer_disconnect_now(peer, 0);
|
||||
return JS_NULL;
|
||||
}
|
||||
|
||||
static JSValue js_enet_peer_disconnect_later(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
|
||||
{
|
||||
ENetPeer *peer = JS_GetOpaque(this_val, enet_peer_class_id);
|
||||
if (!peer) return JS_EXCEPTION;
|
||||
enet_peer_disconnect_later(peer, 0);
|
||||
return JS_NULL;
|
||||
}
|
||||
|
||||
static JSValue js_enet_peer_reset(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
|
||||
{
|
||||
ENetPeer *peer = JS_GetOpaque(this_val, enet_peer_class_id);
|
||||
if (!peer) return JS_EXCEPTION;
|
||||
enet_peer_reset(peer);
|
||||
return JS_NULL;
|
||||
}
|
||||
|
||||
static JSValue js_enet_peer_ping(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
|
||||
{
|
||||
ENetPeer *peer = JS_GetOpaque(this_val, enet_peer_class_id);
|
||||
if (!peer) return JS_EXCEPTION;
|
||||
enet_peer_ping(peer);
|
||||
return JS_NULL;
|
||||
}
|
||||
|
||||
static JSValue js_enet_peer_throttle_configure(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
|
||||
{
|
||||
ENetPeer *peer = JS_GetOpaque(this_val, enet_peer_class_id);
|
||||
if (!peer) return JS_EXCEPTION;
|
||||
|
||||
int interval, acceleration, deceleration;
|
||||
if (argc < 3 || JS_ToInt32(ctx, &interval, argv[0]) || JS_ToInt32(ctx, &acceleration, argv[1]) || JS_ToInt32(ctx, &deceleration, argv[2]))
|
||||
return JS_ThrowTypeError(ctx, "Expected 3 int arguments: interval, acceleration, deceleration");
|
||||
|
||||
enet_peer_throttle_configure(peer, interval, acceleration, deceleration);
|
||||
return JS_NULL;
|
||||
}
|
||||
|
||||
static JSValue js_enet_peer_timeout(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
|
||||
{
|
||||
ENetPeer *peer = JS_GetOpaque(this_val, enet_peer_class_id);
|
||||
if (!peer) return JS_EXCEPTION;
|
||||
|
||||
int timeout_limit, timeout_min, timeout_max;
|
||||
if (argc < 3 || JS_ToInt32(ctx, &timeout_limit, argv[0]) || JS_ToInt32(ctx, &timeout_min, argv[1]) || JS_ToInt32(ctx, &timeout_max, argv[2]))
|
||||
return JS_ThrowTypeError(ctx, "Expected 3 integer arguments: timeout_limit, timeout_min, timeout_max");
|
||||
|
||||
enet_peer_timeout(peer, timeout_limit, timeout_min, timeout_max);
|
||||
return JS_NULL;
|
||||
}
|
||||
|
||||
/* Class definitions */
|
||||
static JSClassDef enet_host = {
|
||||
"ENetHost",
|
||||
.finalizer = js_enet_host_finalizer,
|
||||
};
|
||||
|
||||
static JSClassDef enet_peer_class = {
|
||||
"ENetPeer",
|
||||
.finalizer = js_enet_peer_finalizer,
|
||||
.gc_mark = js_enet_peer_mark
|
||||
};
|
||||
|
||||
JSValue js_enet_resolve_hostname(JSContext *js, JSValue self, int argc, JSValue *argv)
|
||||
{
|
||||
// TODO: implement
|
||||
const char *hostname = JS_ToCString(js, argv[0]);
|
||||
JS_FreeCString(js, hostname);
|
||||
return JS_NULL;
|
||||
}
|
||||
|
||||
/* Function lists */
|
||||
static const JSCFunctionListEntry js_enet_funcs[] = {
|
||||
JS_CFUNC_DEF("initialize", 0, js_enet_initialize),
|
||||
JS_CFUNC_DEF("deinitialize", 0, js_enet_deinitialize),
|
||||
JS_CFUNC_DEF("create_host", 1, js_enet_host_create),
|
||||
JS_CFUNC_DEF("resolve_hostname", 1, js_enet_resolve_hostname),
|
||||
};
|
||||
|
||||
static const JSCFunctionListEntry js_enet_host_funcs[] = {
|
||||
JS_CFUNC_DEF("service", 2, js_enet_host_service),
|
||||
JS_CFUNC_DEF("connect", 2, js_enet_host_connect),
|
||||
JS_CFUNC_DEF("flush", 0, js_enet_host_flush),
|
||||
JS_CFUNC_DEF("broadcast", 1, js_enet_host_broadcast),
|
||||
JS_CGETSET_DEF("port", js_enet_host_get_port, NULL),
|
||||
JS_CGETSET_DEF("address", js_enet_host_get_address, NULL),
|
||||
};
|
||||
|
||||
/* Peer getters */
|
||||
static JSValue js_enet_peer_get_rtt(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
|
||||
{
|
||||
ENetPeer *peer = JS_GetOpaque(this_val, enet_peer_class_id);
|
||||
if (!peer) return JS_EXCEPTION;
|
||||
return JS_NewInt32(ctx, peer->roundTripTime);
|
||||
}
|
||||
|
||||
static JSValue js_enet_peer_get_incoming_bandwidth(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
|
||||
{
|
||||
ENetPeer *peer = JS_GetOpaque(this_val, enet_peer_class_id);
|
||||
if (!peer) return JS_EXCEPTION;
|
||||
if (peer->incomingBandwidth == 0) return JS_NewFloat64(ctx, INFINITY);
|
||||
return JS_NewInt32(ctx, peer->incomingBandwidth);
|
||||
}
|
||||
|
||||
static JSValue js_enet_peer_get_outgoing_bandwidth(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
|
||||
{
|
||||
ENetPeer *peer = JS_GetOpaque(this_val, enet_peer_class_id);
|
||||
if (!peer) return JS_EXCEPTION;
|
||||
if (peer->outgoingBandwidth == 0) return JS_NewFloat64(ctx, INFINITY);
|
||||
return JS_NewInt32(ctx, peer->outgoingBandwidth);
|
||||
}
|
||||
|
||||
static JSValue js_enet_peer_get_last_send_time(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
|
||||
{
|
||||
ENetPeer *peer = JS_GetOpaque(this_val, enet_peer_class_id);
|
||||
if (!peer) return JS_EXCEPTION;
|
||||
return JS_NewInt32(ctx, peer->lastSendTime);
|
||||
}
|
||||
|
||||
static JSValue js_enet_peer_get_last_receive_time(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
|
||||
{
|
||||
ENetPeer *peer = JS_GetOpaque(this_val, enet_peer_class_id);
|
||||
if (!peer) return JS_EXCEPTION;
|
||||
return JS_NewInt32(ctx, peer->lastReceiveTime);
|
||||
}
|
||||
|
||||
static JSValue js_enet_peer_get_mtu(JSContext *ctx, JSValueConst this_val)
|
||||
{
|
||||
ENetPeer *peer = JS_GetOpaque(this_val, enet_peer_class_id);
|
||||
if (!peer) return JS_NewFloat64(ctx, INFINITY);
|
||||
return JS_NewInt32(ctx, peer->mtu);
|
||||
}
|
||||
|
||||
static JSValue js_enet_peer_get_outgoing_data_total(JSContext *ctx, JSValueConst this_val)
|
||||
{
|
||||
ENetPeer *peer = JS_GetOpaque(this_val, enet_peer_class_id);
|
||||
if (!peer) return JS_NewFloat64(ctx, INFINITY);
|
||||
return JS_NewInt32(ctx, peer->outgoingDataTotal);
|
||||
}
|
||||
|
||||
static JSValue js_enet_peer_get_incoming_data_total(JSContext *ctx, JSValueConst this_val)
|
||||
{
|
||||
ENetPeer *peer = JS_GetOpaque(this_val, enet_peer_class_id);
|
||||
if (!peer) return JS_NewFloat64(ctx, INFINITY);
|
||||
return JS_NewInt32(ctx, peer->incomingDataTotal);
|
||||
}
|
||||
|
||||
static JSValue js_enet_peer_get_rtt_variance(JSContext *ctx, JSValueConst this_val)
|
||||
{
|
||||
ENetPeer *peer = JS_GetOpaque(this_val, enet_peer_class_id);
|
||||
if (!peer) return JS_NewFloat64(ctx, INFINITY);
|
||||
return JS_NewInt32(ctx, peer->roundTripTimeVariance);
|
||||
}
|
||||
|
||||
static JSValue js_enet_peer_get_packet_loss(JSContext *ctx, JSValueConst this_val)
|
||||
{
|
||||
ENetPeer *peer = JS_GetOpaque(this_val, enet_peer_class_id);
|
||||
if (!peer) return JS_NewFloat64(ctx, INFINITY);
|
||||
return JS_NewInt32(ctx, peer->packetLoss);
|
||||
}
|
||||
|
||||
static JSValue js_enet_peer_get_state(JSContext *ctx, JSValueConst this_val)
|
||||
{
|
||||
ENetPeer *peer = JS_GetOpaque(this_val, enet_peer_class_id);
|
||||
if (!peer) return JS_NewInt32(ctx, -1);
|
||||
return JS_NewInt32(ctx, peer->state);
|
||||
}
|
||||
|
||||
static JSValue js_enet_peer_get_reliable_data_in_transit(JSContext *ctx, JSValueConst this_val)
|
||||
{
|
||||
ENetPeer *peer = JS_GetOpaque(this_val, enet_peer_class_id);
|
||||
if (!peer) return JS_NewFloat64(ctx, INFINITY);
|
||||
return JS_NewInt32(ctx, peer->reliableDataInTransit);
|
||||
}
|
||||
|
||||
static JSValue js_enet_peer_get_port(JSContext *js, JSValueConst self)
|
||||
{
|
||||
ENetPeer *peer = JS_GetOpaque(self, enet_peer_class_id);
|
||||
return JS_NewUint32(js, peer->address.port);
|
||||
}
|
||||
|
||||
static JSValue js_enet_peer_get_address(JSContext *js, JSValueConst self)
|
||||
{
|
||||
ENetPeer *peer = JS_GetOpaque(self, enet_peer_class_id);
|
||||
uint32_t host = ntohl(peer->address.host);
|
||||
if (host == 0x7F000001) return JS_NewString(js, "localhost");
|
||||
|
||||
char ip_str[16];
|
||||
snprintf(ip_str, sizeof(ip_str), "%u.%u.%u.%u",
|
||||
(host >> 24) & 0xFF,
|
||||
(host >> 16) & 0xFF,
|
||||
(host >> 8) & 0xFF,
|
||||
host & 0xFF);
|
||||
return JS_NewString(js, ip_str);
|
||||
}
|
||||
|
||||
static const JSCFunctionListEntry js_enet_peer_funcs[] = {
|
||||
JS_CFUNC_DEF("send", 1, js_enet_peer_send),
|
||||
JS_CFUNC_DEF("disconnect", 0, js_enet_peer_disconnect),
|
||||
JS_CFUNC_DEF("disconnect_now", 0, js_enet_peer_disconnect_now),
|
||||
JS_CFUNC_DEF("disconnect_later", 0, js_enet_peer_disconnect_later),
|
||||
JS_CFUNC_DEF("reset", 0, js_enet_peer_reset),
|
||||
JS_CFUNC_DEF("ping", 0, js_enet_peer_ping),
|
||||
JS_CFUNC_DEF("throttle_configure", 3, js_enet_peer_throttle_configure),
|
||||
JS_CFUNC_DEF("timeout", 3, js_enet_peer_timeout),
|
||||
JS_CGETSET_DEF("rtt", js_enet_peer_get_rtt, NULL),
|
||||
JS_CGETSET_DEF("incoming_bandwidth", js_enet_peer_get_incoming_bandwidth, NULL),
|
||||
JS_CGETSET_DEF("outgoing_bandwidth", js_enet_peer_get_outgoing_bandwidth, NULL),
|
||||
JS_CGETSET_DEF("last_send_time", js_enet_peer_get_last_send_time, NULL),
|
||||
JS_CGETSET_DEF("last_receive_time", js_enet_peer_get_last_receive_time, NULL),
|
||||
JS_CGETSET_DEF("mtu", js_enet_peer_get_mtu, NULL),
|
||||
JS_CGETSET_DEF("outgoing_data_total", js_enet_peer_get_outgoing_data_total, NULL),
|
||||
JS_CGETSET_DEF("incoming_data_total", js_enet_peer_get_incoming_data_total, NULL),
|
||||
JS_CGETSET_DEF("rtt_variance", js_enet_peer_get_rtt_variance, NULL),
|
||||
JS_CGETSET_DEF("packet_loss", js_enet_peer_get_packet_loss, NULL),
|
||||
JS_CGETSET_DEF("state", js_enet_peer_get_state, NULL),
|
||||
JS_CGETSET_DEF("reliable_data_in_transit", js_enet_peer_get_reliable_data_in_transit, NULL),
|
||||
JS_CGETSET_DEF("port", js_enet_peer_get_port, NULL),
|
||||
JS_CGETSET_DEF("address", js_enet_peer_get_address, NULL),
|
||||
};
|
||||
|
||||
JSValue js_enet_use(JSContext *ctx)
|
||||
{
|
||||
JS_NewClassID(&enet_host_id);
|
||||
JS_NewClass(JS_GetRuntime(ctx), enet_host_id, &enet_host);
|
||||
JSValue host_proto = JS_NewObject(ctx);
|
||||
JS_SetPropertyFunctionList(ctx, host_proto, js_enet_host_funcs, countof(js_enet_host_funcs));
|
||||
JS_SetClassProto(ctx, enet_host_id, host_proto);
|
||||
|
||||
JS_NewClassID(&enet_peer_class_id);
|
||||
JS_NewClass(JS_GetRuntime(ctx), enet_peer_class_id, &enet_peer_class);
|
||||
JSValue peer_proto = JS_NewObject(ctx);
|
||||
JS_SetPropertyFunctionList(ctx, peer_proto, js_enet_peer_funcs, countof(js_enet_peer_funcs));
|
||||
JS_SetClassProto(ctx, enet_peer_class_id, peer_proto);
|
||||
|
||||
JSValue export_obj = JS_NewObject(ctx);
|
||||
JS_SetPropertyFunctionList(ctx, export_obj, js_enet_funcs, countof(js_enet_funcs));
|
||||
return export_obj;
|
||||
}
|
||||
437
source/qjs_fd.c
437
source/qjs_fd.c
@@ -1,437 +0,0 @@
|
||||
#include "cell.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <limits.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <io.h>
|
||||
#include <direct.h>
|
||||
#include <windows.h>
|
||||
#define mkdir(path, mode) _mkdir(path)
|
||||
#define rmdir _rmdir
|
||||
#define unlink _unlink
|
||||
#define getcwd _getcwd
|
||||
#define PATH_MAX _MAX_PATH
|
||||
#define fsync(fd) _commit(fd)
|
||||
#define S_ISLNK(m) 0
|
||||
#define S_ISSOCK(m) 0
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
|
||||
// Helper to convert JS value to file descriptor
|
||||
static int js2fd(JSContext *ctx, JSValueConst val)
|
||||
{
|
||||
int fd;
|
||||
if (JS_ToInt32(ctx, &fd, val) < 0) {
|
||||
JS_ThrowTypeError(ctx, "Expected file descriptor number");
|
||||
return -1;
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
// Helper function for writing
|
||||
static ssize_t js_fd_write_helper(JSContext *js, int fd, JSValue val)
|
||||
{
|
||||
size_t len;
|
||||
ssize_t wrote;
|
||||
if (JS_IsString(val)) {
|
||||
const char *data = JS_ToCStringLen(js, &len, val);
|
||||
wrote = write(fd, data, len);
|
||||
JS_FreeCString(js, data);
|
||||
} else {
|
||||
unsigned char *data = js_get_blob_data(js, &len, val);
|
||||
wrote = write(fd, data, len);
|
||||
}
|
||||
return wrote;
|
||||
}
|
||||
|
||||
|
||||
// POSIX FILE DESCRIPTOR FUNCTIONS
|
||||
|
||||
JSC_SCALL(fd_open,
|
||||
int flags = O_RDWR | O_CREAT;
|
||||
mode_t mode = 0644;
|
||||
|
||||
// Parse optional flags argument
|
||||
if (argc > 1 && JS_IsString(argv[1])) {
|
||||
const char *flag_str = JS_ToCString(js, argv[1]);
|
||||
flags = 0;
|
||||
|
||||
if (strchr(flag_str, 'r')) flags |= O_RDONLY;
|
||||
if (strchr(flag_str, 'w')) flags |= O_WRONLY | O_CREAT | O_TRUNC;
|
||||
if (strchr(flag_str, 'a')) flags |= O_WRONLY | O_CREAT | O_APPEND;
|
||||
if (strchr(flag_str, '+')) {
|
||||
flags &= ~(O_RDONLY | O_WRONLY);
|
||||
flags |= O_RDWR;
|
||||
}
|
||||
|
||||
JS_FreeCString(js, flag_str);
|
||||
}
|
||||
|
||||
int fd = open(str, flags, mode);
|
||||
if (fd < 0)
|
||||
ret = JS_ThrowInternalError(js, "open failed: %s", strerror(errno));
|
||||
else
|
||||
ret = JS_NewInt32(js, fd);
|
||||
)
|
||||
|
||||
JSC_CCALL(fd_write,
|
||||
int fd = js2fd(js, argv[0]);
|
||||
if (fd < 0) return JS_EXCEPTION;
|
||||
|
||||
ssize_t wrote = js_fd_write_helper(js, fd, argv[1]);
|
||||
if (wrote < 0)
|
||||
return JS_ThrowInternalError(js, "write failed: %s", strerror(errno));
|
||||
|
||||
return JS_NewInt64(js, wrote);
|
||||
)
|
||||
|
||||
JSC_CCALL(fd_read,
|
||||
int fd = js2fd(js, argv[0]);
|
||||
if (fd < 0) return JS_EXCEPTION;
|
||||
|
||||
size_t size = 4096;
|
||||
if (argc > 1)
|
||||
size = js2number(js, argv[1]);
|
||||
|
||||
void *buf = malloc(size);
|
||||
if (!buf)
|
||||
return JS_ThrowInternalError(js, "malloc failed");
|
||||
|
||||
ssize_t bytes_read = read(fd, buf, size);
|
||||
if (bytes_read < 0) {
|
||||
free(buf);
|
||||
return JS_ThrowInternalError(js, "read failed: %s", strerror(errno));
|
||||
}
|
||||
|
||||
ret = js_new_blob_stoned_copy(js, buf, bytes_read);
|
||||
free(buf);
|
||||
return ret;
|
||||
)
|
||||
|
||||
JSC_SCALL(fd_slurp,
|
||||
struct stat st;
|
||||
if (stat(str, &st) != 0)
|
||||
return JS_ThrowInternalError(js, "stat failed: %s", strerror(errno));
|
||||
|
||||
if (!S_ISREG(st.st_mode))
|
||||
return JS_ThrowTypeError(js, "path is not a regular file");
|
||||
|
||||
size_t size = st.st_size;
|
||||
if (size == 0)
|
||||
return js_new_blob_stoned_copy(js, NULL, 0);
|
||||
|
||||
#ifndef _WIN32
|
||||
int fd = open(str, O_RDONLY);
|
||||
if (fd < 0)
|
||||
return JS_ThrowInternalError(js, "open failed: %s", strerror(errno));
|
||||
|
||||
void *data = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
|
||||
if (data == MAP_FAILED) {
|
||||
close(fd);
|
||||
return JS_ThrowInternalError(js, "mmap failed: %s", strerror(errno));
|
||||
}
|
||||
ret = js_new_blob_stoned_copy(js, data, size);
|
||||
munmap(data, size);
|
||||
close(fd);
|
||||
#else
|
||||
// Windows: use memory mapping for optimal performance
|
||||
HANDLE hFile = CreateFileA(str, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (hFile == INVALID_HANDLE_VALUE)
|
||||
return JS_ThrowInternalError(js, "CreateFile failed: %lu", GetLastError());
|
||||
|
||||
HANDLE hMapping = CreateFileMappingA(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
|
||||
if (hMapping == NULL) {
|
||||
CloseHandle(hFile);
|
||||
return JS_ThrowInternalError(js, "CreateFileMapping failed: %lu", GetLastError());
|
||||
}
|
||||
|
||||
void *data = MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, 0);
|
||||
if (data == NULL) {
|
||||
CloseHandle(hMapping);
|
||||
CloseHandle(hFile);
|
||||
return JS_ThrowInternalError(js, "MapViewOfFile failed: %lu", GetLastError());
|
||||
}
|
||||
|
||||
ret = js_new_blob_stoned_copy(js, data, size);
|
||||
UnmapViewOfFile(data);
|
||||
CloseHandle(hMapping);
|
||||
CloseHandle(hFile);
|
||||
#endif
|
||||
)
|
||||
|
||||
JSC_CCALL(fd_lseek,
|
||||
int fd = js2fd(js, argv[0]);
|
||||
if (fd < 0) return JS_EXCEPTION;
|
||||
|
||||
off_t offset = js2number(js, argv[1]);
|
||||
int whence = SEEK_SET;
|
||||
|
||||
if (argc > 2) {
|
||||
const char *whence_str = JS_ToCString(js, argv[2]);
|
||||
if (strcmp(whence_str, "cur") == 0) whence = SEEK_CUR;
|
||||
else if (strcmp(whence_str, "end") == 0) whence = SEEK_END;
|
||||
JS_FreeCString(js, whence_str);
|
||||
}
|
||||
|
||||
off_t new_pos = lseek(fd, offset, whence);
|
||||
if (new_pos < 0)
|
||||
return JS_ThrowInternalError(js, "lseek failed: %s", strerror(errno));
|
||||
|
||||
return JS_NewInt64(js, new_pos);
|
||||
)
|
||||
|
||||
JSC_CCALL(fd_getcwd,
|
||||
char buf[PATH_MAX];
|
||||
if (getcwd(buf, sizeof(buf)) == NULL)
|
||||
return JS_ThrowInternalError(js, "getcwd failed: %s", strerror(errno));
|
||||
return JS_NewString(js, buf);
|
||||
)
|
||||
|
||||
JSC_SCALL(fd_rmdir,
|
||||
if (rmdir(str) != 0)
|
||||
ret = JS_ThrowInternalError(js, "could not remove directory %s: %s", str, strerror(errno));
|
||||
)
|
||||
|
||||
JSC_SCALL(fd_mkdir,
|
||||
if (mkdir(str, 0755) != 0)
|
||||
ret = JS_ThrowInternalError(js, "could not make directory %s: %s", str, strerror(errno));
|
||||
)
|
||||
|
||||
JSC_SCALL(fd_unlink,
|
||||
if (unlink(str) != 0)
|
||||
ret = JS_ThrowInternalError(js, "could not remove file %s: %s", str, strerror(errno));
|
||||
)
|
||||
|
||||
JSC_SCALL(fd_mv,
|
||||
if (argc < 2)
|
||||
ret = JS_ThrowTypeError(js, "fd.mv requires 2 arguments: old path and new path");
|
||||
else if (!JS_IsString(argv[1]))
|
||||
ret = JS_ThrowTypeError(js, "second argument must be a string (new path)");
|
||||
else {
|
||||
const char *new_path = JS_ToCString(js, argv[1]);
|
||||
if (rename(str, new_path) != 0)
|
||||
ret = JS_ThrowInternalError(js, "could not rename %s to %s: %s", str, new_path, strerror(errno));
|
||||
JS_FreeCString(js, new_path);
|
||||
}
|
||||
)
|
||||
|
||||
// Helper function for recursive removal
|
||||
static int remove_recursive(const char *path) {
|
||||
struct stat st;
|
||||
if (stat(path, &st) != 0)
|
||||
return -1;
|
||||
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
// Directory: remove contents first
|
||||
#ifdef _WIN32
|
||||
WIN32_FIND_DATA ffd;
|
||||
char search_path[PATH_MAX];
|
||||
snprintf(search_path, sizeof(search_path), "%s\\*", path);
|
||||
HANDLE hFind = FindFirstFile(search_path, &ffd);
|
||||
if (hFind != INVALID_HANDLE_VALUE) {
|
||||
do {
|
||||
if (strcmp(ffd.cFileName, ".") == 0 || strcmp(ffd.cFileName, "..") == 0) continue;
|
||||
char child_path[PATH_MAX];
|
||||
snprintf(child_path, sizeof(child_path), "%s\\%s", path, ffd.cFileName);
|
||||
if (remove_recursive(child_path) != 0) {
|
||||
FindClose(hFind);
|
||||
return -1;
|
||||
}
|
||||
} while (FindNextFile(hFind, &ffd) != 0);
|
||||
FindClose(hFind);
|
||||
}
|
||||
#else
|
||||
DIR *d = opendir(path);
|
||||
if (d) {
|
||||
struct dirent *dir;
|
||||
while ((dir = readdir(d)) != NULL) {
|
||||
if (strcmp(dir->d_name, ".") == 0 || strcmp(dir->d_name, "..") == 0) continue;
|
||||
char child_path[PATH_MAX];
|
||||
snprintf(child_path, sizeof(child_path), "%s/%s", path, dir->d_name);
|
||||
if (remove_recursive(child_path) != 0) {
|
||||
closedir(d);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
closedir(d);
|
||||
}
|
||||
#endif
|
||||
// Remove the now-empty directory
|
||||
return rmdir(path);
|
||||
} else {
|
||||
// File: just unlink
|
||||
return unlink(path);
|
||||
}
|
||||
}
|
||||
|
||||
JSC_SCALL(fd_rm,
|
||||
if (remove_recursive(str) != 0)
|
||||
ret = JS_ThrowInternalError(js, "could not remove %s: %s", str, strerror(errno));
|
||||
)
|
||||
|
||||
JSC_CCALL(fd_fsync,
|
||||
int fd = js2fd(js, argv[0]);
|
||||
if (fd < 0) return JS_EXCEPTION;
|
||||
|
||||
if (fsync(fd) != 0)
|
||||
return JS_ThrowInternalError(js, "fsync failed: %s", strerror(errno));
|
||||
|
||||
return JS_NULL;
|
||||
)
|
||||
|
||||
JSC_CCALL(fd_close,
|
||||
int fd = js2fd(js, argv[0]);
|
||||
if (fd < 0) return JS_EXCEPTION;
|
||||
|
||||
if (close(fd) != 0)
|
||||
return JS_ThrowInternalError(js, "close failed: %s", strerror(errno));
|
||||
|
||||
return JS_NULL;
|
||||
)
|
||||
|
||||
JSC_CCALL(fd_fstat,
|
||||
int fd = js2fd(js, argv[0]);
|
||||
if (fd < 0) return JS_EXCEPTION;
|
||||
|
||||
struct stat st;
|
||||
if (fstat(fd, &st) != 0)
|
||||
return JS_ThrowInternalError(js, "fstat failed: %s", strerror(errno));
|
||||
|
||||
printf("fstat on %s\n", argv[0]);
|
||||
JSValue obj = JS_NewObject(js);
|
||||
JS_SetPropertyStr(js, obj, "size", JS_NewInt64(js, st.st_size));
|
||||
JS_SetPropertyStr(js, obj, "mode", JS_NewInt32(js, st.st_mode));
|
||||
JS_SetPropertyStr(js, obj, "uid", JS_NewInt32(js, st.st_uid));
|
||||
JS_SetPropertyStr(js, obj, "gid", JS_NewInt32(js, st.st_gid));
|
||||
JS_SetPropertyStr(js, obj, "atime", JS_NewInt64(js, st.st_atime));
|
||||
JS_SetPropertyStr(js, obj, "mtime", JS_NewInt64(js, st.st_mtime));
|
||||
JS_SetPropertyStr(js, obj, "ctime", JS_NewInt64(js, st.st_ctime));
|
||||
JS_SetPropertyStr(js, obj, "nlink", JS_NewInt32(js, st.st_nlink));
|
||||
JS_SetPropertyStr(js, obj, "ino", JS_NewInt64(js, st.st_ino));
|
||||
JS_SetPropertyStr(js, obj, "dev", JS_NewInt32(js, st.st_dev));
|
||||
JS_SetPropertyStr(js, obj, "rdev", JS_NewInt32(js, st.st_rdev));
|
||||
#ifndef _WIN32
|
||||
JS_SetPropertyStr(js, obj, "blksize", JS_NewInt32(js, st.st_blksize));
|
||||
JS_SetPropertyStr(js, obj, "blocks", JS_NewInt64(js, st.st_blocks));
|
||||
#else
|
||||
JS_SetPropertyStr(js, obj, "blksize", JS_NewInt32(js, 4096));
|
||||
JS_SetPropertyStr(js, obj, "blocks", JS_NewInt64(js, st.st_size / 512));
|
||||
#endif
|
||||
|
||||
// Add boolean properties for file type
|
||||
JS_SetPropertyStr(js, obj, "isFile", JS_NewBool(js, S_ISREG(st.st_mode)));
|
||||
JS_SetPropertyStr(js, obj, "isDirectory", JS_NewBool(js, S_ISDIR(st.st_mode)));
|
||||
JS_SetPropertyStr(js, obj, "isSymlink", JS_NewBool(js, S_ISLNK(st.st_mode)));
|
||||
JS_SetPropertyStr(js, obj, "isFIFO", JS_NewBool(js, S_ISFIFO(st.st_mode)));
|
||||
JS_SetPropertyStr(js, obj, "isSocket", JS_NewBool(js, S_ISSOCK(st.st_mode)));
|
||||
JS_SetPropertyStr(js, obj, "isCharDevice", JS_NewBool(js, S_ISCHR(st.st_mode)));
|
||||
JS_SetPropertyStr(js, obj, "isBlockDevice", JS_NewBool(js, S_ISBLK(st.st_mode)));
|
||||
|
||||
return obj;
|
||||
)
|
||||
|
||||
JSC_SCALL(fd_stat,
|
||||
struct stat st;
|
||||
if (stat(str, &st) != 0)
|
||||
return JS_NewObject(js);
|
||||
|
||||
JSValue obj = JS_NewObject(js);
|
||||
JS_SetPropertyStr(js, obj, "size", JS_NewInt64(js, st.st_size));
|
||||
JS_SetPropertyStr(js, obj, "mode", JS_NewInt32(js, st.st_mode));
|
||||
JS_SetPropertyStr(js, obj, "uid", JS_NewInt32(js, st.st_uid));
|
||||
JS_SetPropertyStr(js, obj, "gid", JS_NewInt32(js, st.st_gid));
|
||||
JS_SetPropertyStr(js, obj, "atime", JS_NewInt64(js, st.st_atime));
|
||||
JS_SetPropertyStr(js, obj, "mtime", JS_NewInt64(js, st.st_mtime));
|
||||
JS_SetPropertyStr(js, obj, "ctime", JS_NewInt64(js, st.st_ctime));
|
||||
JS_SetPropertyStr(js, obj, "nlink", JS_NewInt32(js, st.st_nlink));
|
||||
JS_SetPropertyStr(js, obj, "ino", JS_NewInt64(js, st.st_ino));
|
||||
JS_SetPropertyStr(js, obj, "dev", JS_NewInt32(js, st.st_dev));
|
||||
JS_SetPropertyStr(js, obj, "rdev", JS_NewInt32(js, st.st_rdev));
|
||||
#ifndef _WIN32
|
||||
JS_SetPropertyStr(js, obj, "blksize", JS_NewInt32(js, st.st_blksize));
|
||||
JS_SetPropertyStr(js, obj, "blocks", JS_NewInt64(js, st.st_blocks));
|
||||
#else
|
||||
JS_SetPropertyStr(js, obj, "blksize", JS_NewInt32(js, 4096));
|
||||
JS_SetPropertyStr(js, obj, "blocks", JS_NewInt64(js, st.st_size / 512));
|
||||
#endif
|
||||
|
||||
// Add boolean properties for file type
|
||||
JS_SetPropertyStr(js, obj, "isFile", JS_NewBool(js, S_ISREG(st.st_mode)));
|
||||
JS_SetPropertyStr(js, obj, "isDirectory", JS_NewBool(js, S_ISDIR(st.st_mode)));
|
||||
JS_SetPropertyStr(js, obj, "isSymlink", JS_NewBool(js, S_ISLNK(st.st_mode)));
|
||||
JS_SetPropertyStr(js, obj, "isFIFO", JS_NewBool(js, S_ISFIFO(st.st_mode)));
|
||||
JS_SetPropertyStr(js, obj, "isSocket", JS_NewBool(js, S_ISSOCK(st.st_mode)));
|
||||
JS_SetPropertyStr(js, obj, "isCharDevice", JS_NewBool(js, S_ISCHR(st.st_mode)));
|
||||
JS_SetPropertyStr(js, obj, "isBlockDevice", JS_NewBool(js, S_ISBLK(st.st_mode)));
|
||||
|
||||
return obj;
|
||||
)
|
||||
|
||||
JSC_SCALL(fd_readdir,
|
||||
#ifdef _WIN32
|
||||
WIN32_FIND_DATA ffd;
|
||||
char path[PATH_MAX];
|
||||
snprintf(path, sizeof(path), "%s\\*", str);
|
||||
HANDLE hFind = FindFirstFile(path, &ffd);
|
||||
if (hFind == INVALID_HANDLE_VALUE) {
|
||||
ret = JS_ThrowInternalError(js, "FindFirstFile failed for %s", path);
|
||||
} else {
|
||||
ret = JS_NewArray(js);
|
||||
int i = 0;
|
||||
do {
|
||||
if (strcmp(ffd.cFileName, ".") == 0 || strcmp(ffd.cFileName, "..") == 0) continue;
|
||||
JS_SetPropertyUint32(js, ret, i++, JS_NewString(js, ffd.cFileName));
|
||||
} while (FindNextFile(hFind, &ffd) != 0);
|
||||
FindClose(hFind);
|
||||
}
|
||||
#else
|
||||
DIR *d;
|
||||
struct dirent *dir;
|
||||
d = opendir(str);
|
||||
if (d) {
|
||||
ret = JS_NewArray(js);
|
||||
int i = 0;
|
||||
while ((dir = readdir(d)) != NULL) {
|
||||
if (strcmp(dir->d_name, ".") == 0 || strcmp(dir->d_name, "..") == 0) continue;
|
||||
JS_SetPropertyUint32(js, ret, i++, JS_NewString(js, dir->d_name));
|
||||
}
|
||||
closedir(d);
|
||||
} else {
|
||||
ret = JS_ThrowInternalError(js, "opendir failed for %s: %s", str, strerror(errno));
|
||||
}
|
||||
#endif
|
||||
)
|
||||
|
||||
static const JSCFunctionListEntry js_fd_funcs[] = {
|
||||
MIST_FUNC_DEF(fd, open, 2),
|
||||
MIST_FUNC_DEF(fd, write, 2),
|
||||
MIST_FUNC_DEF(fd, read, 2),
|
||||
MIST_FUNC_DEF(fd, slurp, 1),
|
||||
MIST_FUNC_DEF(fd, lseek, 3),
|
||||
MIST_FUNC_DEF(fd, getcwd, 0),
|
||||
MIST_FUNC_DEF(fd, rmdir, 1),
|
||||
MIST_FUNC_DEF(fd, unlink, 1),
|
||||
MIST_FUNC_DEF(fd, mkdir, 1),
|
||||
MIST_FUNC_DEF(fd, mv, 2),
|
||||
MIST_FUNC_DEF(fd, rm, 1),
|
||||
MIST_FUNC_DEF(fd, fsync, 1),
|
||||
MIST_FUNC_DEF(fd, close, 1),
|
||||
MIST_FUNC_DEF(fd, stat, 1),
|
||||
MIST_FUNC_DEF(fd, fstat, 1),
|
||||
MIST_FUNC_DEF(fd, readdir, 1),
|
||||
};
|
||||
|
||||
JSValue js_fd_use(JSContext *js) {
|
||||
JSValue mod = JS_NewObject(js);
|
||||
JS_SetPropertyFunctionList(js, mod, js_fd_funcs, countof(js_fd_funcs));
|
||||
return mod;
|
||||
}
|
||||
256
source/qjs_fit.c
256
source/qjs_fit.c
@@ -1,256 +0,0 @@
|
||||
#include "cell.h"
|
||||
#include <stdint.h>
|
||||
#include <limits.h>
|
||||
|
||||
#define FIT_BITS 56
|
||||
#define FIT_MAX ((1LL << 55) - 1)
|
||||
#define FIT_MIN (-(1LL << 55))
|
||||
#define FIT_MASK ((1ULL << 56) - 1)
|
||||
|
||||
static int is_fit(int64_t n) {
|
||||
return n >= FIT_MIN && n <= FIT_MAX;
|
||||
}
|
||||
|
||||
static int64_t to_fit_int(JSContext *js, JSValue val) {
|
||||
if (!JS_IsNumber(val)) return LLONG_MAX;
|
||||
|
||||
int64_t n;
|
||||
if (JS_ToInt64(js, &n, val) < 0) return LLONG_MAX;
|
||||
|
||||
if (!is_fit(n)) return LLONG_MAX;
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
static JSValue fit_result(JSContext *js, int64_t result) {
|
||||
if (!is_fit(result)) return JS_NULL;
|
||||
return JS_NewInt64(js, result);
|
||||
}
|
||||
|
||||
JSC_CCALL(fit_and,
|
||||
int64_t first = to_fit_int(js, argv[0]);
|
||||
int64_t second = to_fit_int(js, argv[1]);
|
||||
|
||||
if (first == LLONG_MAX || second == LLONG_MAX) return JS_NULL;
|
||||
|
||||
int64_t result = first & second;
|
||||
return fit_result(js, result);
|
||||
)
|
||||
|
||||
JSC_CCALL(fit_left,
|
||||
int64_t first = to_fit_int(js, argv[0]);
|
||||
int64_t second = to_fit_int(js, argv[1]);
|
||||
|
||||
if (first == LLONG_MAX || second == LLONG_MAX) return JS_NULL;
|
||||
if (second < 0 || second >= FIT_BITS) return JS_NULL;
|
||||
|
||||
// Mask to 56 bits then sign extend
|
||||
uint64_t unsigned_first = (uint64_t)first & FIT_MASK;
|
||||
uint64_t result = (unsigned_first << second) & FIT_MASK;
|
||||
|
||||
// Sign extend from bit 55
|
||||
if (result & (1ULL << 55)) {
|
||||
result |= ~FIT_MASK;
|
||||
}
|
||||
|
||||
return JS_NewInt64(js, (int64_t)result);
|
||||
)
|
||||
|
||||
JSC_CCALL(fit_mask,
|
||||
if (!JS_IsNumber(argv[0])) return JS_NULL;
|
||||
|
||||
int32_t n;
|
||||
if (JS_ToInt32(js, &n, argv[0]) < 0) return JS_NULL;
|
||||
|
||||
if (n > FIT_BITS || n < -FIT_BITS) return JS_NULL;
|
||||
|
||||
if (n == 0) return JS_NewInt64(js, 0);
|
||||
|
||||
int64_t result;
|
||||
if (n > 0) {
|
||||
// Create n ones
|
||||
if (n == FIT_BITS) {
|
||||
result = -1;
|
||||
} else {
|
||||
result = (1LL << n) - 1;
|
||||
}
|
||||
} else {
|
||||
// Create -n zeros (which is 56+n ones)
|
||||
int ones = FIT_BITS + n;
|
||||
if (ones == 0) {
|
||||
result = 0;
|
||||
} else if (ones == FIT_BITS) {
|
||||
result = -1;
|
||||
} else {
|
||||
uint64_t mask = (1ULL << ones) - 1;
|
||||
result = ~mask;
|
||||
// Ensure result is within 56-bit range
|
||||
result = (int64_t)((uint64_t)result & FIT_MASK);
|
||||
// Sign extend
|
||||
if (result & (1LL << 55)) {
|
||||
result |= ~FIT_MASK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return JS_NewInt64(js, result);
|
||||
)
|
||||
|
||||
JSC_CCALL(fit_not,
|
||||
int64_t val = to_fit_int(js, argv[0]);
|
||||
if (val == LLONG_MAX) return JS_NULL;
|
||||
|
||||
uint64_t result = ~(uint64_t)val & FIT_MASK;
|
||||
|
||||
// Sign extend
|
||||
if (result & (1ULL << 55)) {
|
||||
result |= ~FIT_MASK;
|
||||
}
|
||||
|
||||
return JS_NewInt64(js, (int64_t)result);
|
||||
)
|
||||
|
||||
JSC_CCALL(fit_ones,
|
||||
int64_t val = to_fit_int(js, argv[0]);
|
||||
if (val == LLONG_MAX) return JS_NULL;
|
||||
|
||||
uint64_t uval = (uint64_t)val & FIT_MASK;
|
||||
int count = 0;
|
||||
|
||||
for (int i = 0; i < FIT_BITS; i++) {
|
||||
if (uval & (1ULL << i)) count++;
|
||||
}
|
||||
|
||||
return JS_NewInt32(js, count);
|
||||
)
|
||||
|
||||
JSC_CCALL(fit_or,
|
||||
int64_t first = to_fit_int(js, argv[0]);
|
||||
int64_t second = to_fit_int(js, argv[1]);
|
||||
|
||||
if (first == LLONG_MAX || second == LLONG_MAX) return JS_NULL;
|
||||
|
||||
int64_t result = first | second;
|
||||
return fit_result(js, result);
|
||||
)
|
||||
|
||||
JSC_CCALL(fit_reverse,
|
||||
int64_t val = to_fit_int(js, argv[0]);
|
||||
if (val == LLONG_MAX) return JS_NULL;
|
||||
|
||||
uint64_t uval = (uint64_t)val & FIT_MASK;
|
||||
uint64_t result = 0;
|
||||
|
||||
for (int i = 0; i < FIT_BITS; i++) {
|
||||
if (uval & (1ULL << i)) {
|
||||
result |= (1ULL << (FIT_BITS - 1 - i));
|
||||
}
|
||||
}
|
||||
|
||||
// Sign extend
|
||||
if (result & (1ULL << 55)) {
|
||||
result |= ~FIT_MASK;
|
||||
}
|
||||
|
||||
return JS_NewInt64(js, (int64_t)result);
|
||||
)
|
||||
|
||||
JSC_CCALL(fit_right,
|
||||
int64_t first = to_fit_int(js, argv[0]);
|
||||
int64_t second = to_fit_int(js, argv[1]);
|
||||
|
||||
if (first == LLONG_MAX || second == LLONG_MAX) return JS_NULL;
|
||||
if (second < 0 || second >= FIT_BITS) return JS_NULL;
|
||||
|
||||
// Zero fill right shift
|
||||
uint64_t ufirst = (uint64_t)first & FIT_MASK;
|
||||
uint64_t result = ufirst >> second;
|
||||
|
||||
return JS_NewInt64(js, (int64_t)result);
|
||||
)
|
||||
|
||||
JSC_CCALL(fit_right_signed,
|
||||
int64_t first = to_fit_int(js, argv[0]);
|
||||
int64_t second = to_fit_int(js, argv[1]);
|
||||
|
||||
if (first == LLONG_MAX || second == LLONG_MAX) return JS_NULL;
|
||||
if (second < 0 || second >= FIT_BITS) return JS_NULL;
|
||||
|
||||
// Sign extend to 64 bits for arithmetic shift
|
||||
int64_t extended = first;
|
||||
if (first & (1LL << 55)) {
|
||||
extended |= ~FIT_MASK;
|
||||
}
|
||||
|
||||
int64_t result = extended >> second;
|
||||
|
||||
// Ensure result is within fit range
|
||||
return fit_result(js, result);
|
||||
)
|
||||
|
||||
JSC_CCALL(fit_rotate,
|
||||
int64_t first = to_fit_int(js, argv[0]);
|
||||
int64_t second = to_fit_int(js, argv[1]);
|
||||
|
||||
if (first == LLONG_MAX || second == LLONG_MAX) return JS_NULL;
|
||||
|
||||
// Normalize rotation amount
|
||||
int rotation = ((int)second % FIT_BITS + FIT_BITS) % FIT_BITS;
|
||||
|
||||
uint64_t ufirst = (uint64_t)first & FIT_MASK;
|
||||
uint64_t result = ((ufirst << rotation) | (ufirst >> (FIT_BITS - rotation))) & FIT_MASK;
|
||||
|
||||
// Sign extend
|
||||
if (result & (1ULL << 55)) {
|
||||
result |= ~FIT_MASK;
|
||||
}
|
||||
|
||||
return JS_NewInt64(js, (int64_t)result);
|
||||
)
|
||||
|
||||
JSC_CCALL(fit_xor,
|
||||
int64_t first = to_fit_int(js, argv[0]);
|
||||
int64_t second = to_fit_int(js, argv[1]);
|
||||
|
||||
if (first == LLONG_MAX || second == LLONG_MAX) return JS_NULL;
|
||||
|
||||
int64_t result = first ^ second;
|
||||
return fit_result(js, result);
|
||||
)
|
||||
|
||||
JSC_CCALL(fit_zeros,
|
||||
int64_t val = to_fit_int(js, argv[0]);
|
||||
if (val == LLONG_MAX) return JS_NULL;
|
||||
|
||||
uint64_t uval = (uint64_t)val & FIT_MASK;
|
||||
|
||||
int count = 0;
|
||||
for (int i = FIT_BITS - 1; i >= 0; i--) {
|
||||
if (uval & (1ULL << i)) break;
|
||||
count++;
|
||||
}
|
||||
|
||||
return JS_NewInt32(js, count);
|
||||
)
|
||||
|
||||
static const JSCFunctionListEntry js_fit_funcs[] = {
|
||||
MIST_FUNC_DEF(fit, and, 2),
|
||||
MIST_FUNC_DEF(fit, left, 2),
|
||||
MIST_FUNC_DEF(fit, mask, 1),
|
||||
MIST_FUNC_DEF(fit, not, 1),
|
||||
MIST_FUNC_DEF(fit, ones, 1),
|
||||
MIST_FUNC_DEF(fit, or, 2),
|
||||
MIST_FUNC_DEF(fit, reverse, 1),
|
||||
MIST_FUNC_DEF(fit, right, 2),
|
||||
MIST_FUNC_DEF(fit, right_signed, 2),
|
||||
MIST_FUNC_DEF(fit, rotate, 2),
|
||||
MIST_FUNC_DEF(fit, xor, 2),
|
||||
MIST_FUNC_DEF(fit, zeros, 1),
|
||||
};
|
||||
|
||||
JSValue js_fit_use(JSContext *js)
|
||||
{
|
||||
JSValue mod = JS_NewObject(js);
|
||||
JS_SetPropertyFunctionList(js, mod, js_fit_funcs, countof(js_fit_funcs));
|
||||
return mod;
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
#include "cell.h"
|
||||
#define PAR_EASYCURL_IMPLEMENTATION
|
||||
#include "par_easycurl.h"
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
// Performs a blocking HTTP GET and returns a QuickJS Blob of the body
|
||||
static JSValue js_fetch_picoparser(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv) {
|
||||
if (argc < 1 || !JS_IsString(argv[0]))
|
||||
return JS_ThrowTypeError(ctx, "fetch: URL string required");
|
||||
|
||||
const char *url = JS_ToCString(ctx, argv[0]);
|
||||
if (!url) return JS_ThrowTypeError(ctx, "fetch: invalid URL");
|
||||
|
||||
par_byte *data = NULL;
|
||||
int nbytes = 0;
|
||||
if (!par_easycurl_to_memory(url, &data, &nbytes)) {
|
||||
JS_FreeCString(ctx, url);
|
||||
return JS_ThrowTypeError(ctx, "fetch: failed to fetch URL");
|
||||
}
|
||||
|
||||
JS_FreeCString(ctx, url);
|
||||
|
||||
// Return a Blob wrapping the data
|
||||
JSValue blob = js_new_blob_stoned_copy(ctx, data, (size_t)nbytes);
|
||||
free(data); // par_easycurl allocates with malloc, so we free it
|
||||
return blob;
|
||||
}
|
||||
|
||||
static const JSCFunctionListEntry js_http_funcs[] = {
|
||||
JS_CFUNC_DEF("fetch", 2, js_fetch_picoparser),
|
||||
};
|
||||
|
||||
JSValue js_http_use(JSContext *js) {
|
||||
par_easycurl_init(0); // Initialize curl
|
||||
JSValue obj = JS_NewObject(js);
|
||||
JS_SetPropertyFunctionList(js, obj, js_http_funcs,
|
||||
sizeof(js_http_funcs)/sizeof(js_http_funcs[0]));
|
||||
return obj;
|
||||
}
|
||||
101
source/qjs_js.c
101
source/qjs_js.c
@@ -1,101 +0,0 @@
|
||||
#include "cell.h"
|
||||
|
||||
JSC_CCALL(os_gc, JS_RunGC(JS_GetRuntime(js)) )
|
||||
JSC_CCALL(os_mem_limit, JS_SetMemoryLimit(JS_GetRuntime(js), js2number(js,argv[0])))
|
||||
JSC_CCALL(os_gc_threshold, JS_SetGCThreshold(JS_GetRuntime(js), js2number(js,argv[0])))
|
||||
JSC_CCALL(os_max_stacksize, JS_SetMaxStackSize(JS_GetRuntime(js), js2number(js,argv[0])))
|
||||
|
||||
JSC_CCALL(os_calc_mem,
|
||||
JSMemoryUsage mu;
|
||||
JS_ComputeMemoryUsage(JS_GetRuntime(js),&mu);
|
||||
ret = JS_NewObject(js);
|
||||
JS_SetPropertyStr(js,ret,"malloc_size",number2js(js,mu.malloc_size));
|
||||
JS_SetPropertyStr(js,ret,"malloc_limit",number2js(js,mu.malloc_limit));
|
||||
JS_SetPropertyStr(js,ret,"memory_used_size",number2js(js,mu.memory_used_size));
|
||||
JS_SetPropertyStr(js,ret,"malloc_count",number2js(js,mu.malloc_count));
|
||||
JS_SetPropertyStr(js,ret,"memory_used_count",number2js(js,mu.memory_used_count));
|
||||
JS_SetPropertyStr(js,ret,"atom_count",number2js(js,mu.atom_count));
|
||||
JS_SetPropertyStr(js,ret,"atom_size",number2js(js,mu.atom_size));
|
||||
JS_SetPropertyStr(js,ret,"str_count",number2js(js,mu.str_count));
|
||||
JS_SetPropertyStr(js,ret,"str_size",number2js(js,mu.str_size));
|
||||
JS_SetPropertyStr(js,ret,"obj_count",number2js(js,mu.obj_count));
|
||||
JS_SetPropertyStr(js,ret,"obj_size",number2js(js,mu.obj_size));
|
||||
JS_SetPropertyStr(js,ret,"prop_count",number2js(js,mu.prop_count));
|
||||
JS_SetPropertyStr(js,ret,"prop_size",number2js(js,mu.prop_size));
|
||||
JS_SetPropertyStr(js,ret,"shape_count",number2js(js,mu.shape_count));
|
||||
JS_SetPropertyStr(js,ret,"shape_size",number2js(js,mu.shape_size));
|
||||
JS_SetPropertyStr(js,ret,"js_func_count",number2js(js,mu.js_func_count));
|
||||
JS_SetPropertyStr(js,ret,"js_func_size",number2js(js,mu.js_func_size));
|
||||
JS_SetPropertyStr(js,ret,"js_func_code_size",number2js(js,mu.js_func_code_size));
|
||||
JS_SetPropertyStr(js,ret,"js_func_pc2line_count",number2js(js,mu.js_func_pc2line_count));
|
||||
JS_SetPropertyStr(js,ret,"js_func_pc2line_size",number2js(js,mu.js_func_pc2line_size));
|
||||
JS_SetPropertyStr(js,ret,"c_func_count",number2js(js,mu.c_func_count));
|
||||
JS_SetPropertyStr(js,ret,"array_count",number2js(js,mu.array_count));
|
||||
JS_SetPropertyStr(js,ret,"fast_array_count",number2js(js,mu.fast_array_count));
|
||||
JS_SetPropertyStr(js,ret,"fast_array_elements",number2js(js,mu.fast_array_elements));
|
||||
JS_SetPropertyStr(js,ret,"binary_object_count",number2js(js,mu.binary_object_count));
|
||||
JS_SetPropertyStr(js,ret,"binary_object_size",number2js(js,mu.binary_object_size));
|
||||
)
|
||||
|
||||
JSC_SSCALL(os_eval,
|
||||
ret = JS_Eval(js,str2,strlen(str2),str, 0);
|
||||
)
|
||||
|
||||
JSC_SSCALL(js_compile,
|
||||
ret = JS_Eval(js, str2, strlen(str2), str, JS_EVAL_FLAG_COMPILE_ONLY);
|
||||
)
|
||||
|
||||
JSC_CCALL(js_eval_compile,
|
||||
JS_DupValue(js,argv[0]);
|
||||
ret = JS_EvalFunction(js, argv[0]);
|
||||
)
|
||||
|
||||
JSC_CCALL(js_compile_blob,
|
||||
JSRuntime *rt = JS_GetRuntime(js);
|
||||
// JS_SetStripInfo(rt, JS_STRIP_SOURCE);
|
||||
// JS_SetStripInfo(rt, JS_STRIP_DEBUG);
|
||||
size_t size;
|
||||
uint8_t *data = JS_WriteObject(js, &size, argv[0], JS_WRITE_OBJ_BYTECODE);
|
||||
if (!data) {
|
||||
return JS_ThrowInternalError(js, "Failed to serialize bytecode");
|
||||
}
|
||||
ret = js_new_blob_stoned_copy(js, data, size);
|
||||
js_free(js, data);
|
||||
)
|
||||
|
||||
JSC_CCALL(js_compile_unblob,
|
||||
size_t size;
|
||||
void *data = js_get_blob_data(js, &size, argv[0]);
|
||||
if (!data) return JS_ThrowReferenceError(js, "Must be a stoned blob.");
|
||||
|
||||
return JS_ReadObject(js, data, size, JS_READ_OBJ_BYTECODE);
|
||||
)
|
||||
|
||||
JSC_CCALL(js_disassemble,
|
||||
return js_debugger_fn_bytecode(js, argv[0]);
|
||||
)
|
||||
|
||||
JSC_CCALL(js_fn_info,
|
||||
return js_debugger_fn_info(js, argv[0]);
|
||||
)
|
||||
|
||||
static const JSCFunctionListEntry js_js_funcs[] = {
|
||||
MIST_FUNC_DEF(os, calc_mem, 0),
|
||||
MIST_FUNC_DEF(os, mem_limit, 1),
|
||||
MIST_FUNC_DEF(os, gc_threshold, 1),
|
||||
MIST_FUNC_DEF(os, max_stacksize, 1),
|
||||
MIST_FUNC_DEF(os, gc, 0),
|
||||
MIST_FUNC_DEF(os, eval, 2),
|
||||
MIST_FUNC_DEF(js, compile, 2),
|
||||
MIST_FUNC_DEF(js, eval_compile, 1),
|
||||
MIST_FUNC_DEF(js, compile_blob, 1),
|
||||
MIST_FUNC_DEF(js, compile_unblob, 1),
|
||||
MIST_FUNC_DEF(js, disassemble, 1),
|
||||
MIST_FUNC_DEF(js, fn_info, 1),
|
||||
};
|
||||
|
||||
JSValue js_js_use(JSContext *js) {
|
||||
JSValue mod = JS_NewObject(js);
|
||||
JS_SetPropertyFunctionList(js,mod,js_js_funcs,countof(js_js_funcs));
|
||||
return mod;
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
#include "cell.h"
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define KIM_IMPLEMENTATION
|
||||
#include "kim.h"
|
||||
|
||||
JSC_CCALL(kim_encode,
|
||||
const char *utf8_str = JS_ToCString(js, argv[0]);
|
||||
if (!utf8_str) return JS_EXCEPTION;
|
||||
|
||||
// Count runes to estimate kim buffer size
|
||||
int rune_count = utf8_count(utf8_str);
|
||||
|
||||
// Allocate kim buffer (worst case: 5 bytes per rune)
|
||||
size_t kim_size = rune_count * 5;
|
||||
char *kim_buffer = malloc(kim_size);
|
||||
char *kim_ptr = kim_buffer;
|
||||
|
||||
// Encode utf8 to kim
|
||||
long long runes_encoded;
|
||||
utf8_to_kim(&utf8_str, &kim_ptr, &runes_encoded);
|
||||
|
||||
// Calculate actual size used
|
||||
size_t actual_size = kim_ptr - kim_buffer;
|
||||
|
||||
// Create blob with the encoded data
|
||||
ret = js_new_blob_stoned_copy(js, kim_buffer, actual_size);
|
||||
|
||||
free(kim_buffer);
|
||||
JS_FreeCString(js, utf8_str);
|
||||
)
|
||||
|
||||
JSC_CCALL(kim_decode,
|
||||
size_t kim_len;
|
||||
void *kim_data = js_get_blob_data(js, &kim_len, argv[0]);
|
||||
if (!kim_data) return JS_ThrowTypeError(js, "Expected blob");
|
||||
|
||||
// Allocate UTF-8 buffer (worst case: 4 bytes per kim byte)
|
||||
size_t utf8_size = kim_len * 4;
|
||||
char *utf8_buffer = malloc(utf8_size + 1); // +1 for null terminator
|
||||
char *utf8_ptr = utf8_buffer;
|
||||
|
||||
// Copy kim data since kim_to_utf8 modifies the pointer
|
||||
char *kim_copy = malloc(kim_len);
|
||||
memcpy(kim_copy, kim_data, kim_len);
|
||||
char *kim_ptr = kim_copy;
|
||||
|
||||
// Count runes in kim data
|
||||
int rune_count = 0;
|
||||
char *temp_ptr = kim_copy;
|
||||
while (temp_ptr < kim_copy + kim_len) {
|
||||
decode_kim(&temp_ptr);
|
||||
rune_count++;
|
||||
}
|
||||
|
||||
// Reset pointer and decode
|
||||
kim_ptr = kim_copy;
|
||||
kim_to_utf8(&kim_ptr, &utf8_ptr, rune_count);
|
||||
|
||||
// Null terminate
|
||||
*utf8_ptr = '\0';
|
||||
|
||||
ret = JS_NewString(js, utf8_buffer);
|
||||
|
||||
free(utf8_buffer);
|
||||
free(kim_copy);
|
||||
)
|
||||
|
||||
static const JSCFunctionListEntry js_kim_funcs[] = {
|
||||
MIST_FUNC_DEF(kim, encode, 1),
|
||||
MIST_FUNC_DEF(kim, decode, 1),
|
||||
};
|
||||
|
||||
JSValue js_kim_use(JSContext *js)
|
||||
{
|
||||
JSValue mod = JS_NewObject(js);
|
||||
JS_SetPropertyFunctionList(js, mod, js_kim_funcs, countof(js_kim_funcs));
|
||||
return mod;
|
||||
}
|
||||
@@ -1,399 +0,0 @@
|
||||
#include "quickjs.h"
|
||||
#include "miniz.h"
|
||||
#include "cell.h"
|
||||
|
||||
static JSClassID js_reader_class_id;
|
||||
static JSClassID js_writer_class_id;
|
||||
|
||||
static void js_reader_finalizer(JSRuntime *rt, JSValue val) {
|
||||
mz_zip_archive *zip = JS_GetOpaque(val, js_reader_class_id);
|
||||
mz_zip_reader_end(zip);
|
||||
js_free_rt(rt,zip);
|
||||
}
|
||||
|
||||
static void js_writer_finalizer(JSRuntime *rt, JSValue val) {
|
||||
mz_zip_archive *zip = JS_GetOpaque(val, js_writer_class_id);
|
||||
mz_zip_writer_finalize_archive(zip);
|
||||
mz_zip_writer_end(zip);
|
||||
js_free_rt(rt,zip);
|
||||
}
|
||||
|
||||
static JSClassDef js_reader_class = {
|
||||
"zip reader",
|
||||
.finalizer = js_reader_finalizer,
|
||||
};
|
||||
|
||||
static JSClassDef js_writer_class = {
|
||||
"zip writer",
|
||||
.finalizer = js_writer_finalizer,
|
||||
};
|
||||
|
||||
static mz_zip_archive *js2reader(JSContext *js, JSValue v)
|
||||
{
|
||||
return JS_GetOpaque(v, js_reader_class_id);
|
||||
}
|
||||
|
||||
static mz_zip_archive *js2writer(JSContext *js, JSValue v)
|
||||
{
|
||||
return JS_GetOpaque(v, js_writer_class_id);
|
||||
}
|
||||
|
||||
static JSValue js_miniz_read(JSContext *js, JSValue self, int argc, JSValue *argv)
|
||||
{
|
||||
size_t len;
|
||||
void *data = js_get_blob_data(js, &len, argv[0]);
|
||||
if (!data)
|
||||
return JS_ThrowReferenceError(js, "Could not create data.\n");
|
||||
|
||||
mz_zip_archive *zip = calloc(sizeof(*zip),1);
|
||||
int success = mz_zip_reader_init_mem(zip, data, len, 0);
|
||||
int err = mz_zip_get_last_error(zip);
|
||||
if (err)
|
||||
return JS_ThrowInternalError(js, "miniz error: %s\n", mz_zip_get_error_string(err));
|
||||
|
||||
JSValue jszip = JS_NewObjectClass(js, js_reader_class_id);
|
||||
JS_SetOpaque(jszip, zip);
|
||||
return jszip;
|
||||
}
|
||||
|
||||
static JSValue js_miniz_write(JSContext *js, JSValue self, int argc, JSValue *argv)
|
||||
{
|
||||
const char *file = JS_ToCString(js, argv[0]);
|
||||
if (!file)
|
||||
return JS_EXCEPTION;
|
||||
|
||||
mz_zip_archive *zip = calloc(sizeof(*zip), 1);
|
||||
if (!zip) {
|
||||
JS_FreeCString(js, file);
|
||||
return JS_ThrowOutOfMemory(js);
|
||||
}
|
||||
|
||||
mz_bool success = mz_zip_writer_init_file(zip, file, 0);
|
||||
JS_FreeCString(js, file);
|
||||
|
||||
if (!success) {
|
||||
int err = mz_zip_get_last_error(zip);
|
||||
mz_zip_writer_end(zip);
|
||||
free(zip);
|
||||
return JS_ThrowInternalError(js, "Failed to initialize zip writer: %s", mz_zip_get_error_string(err));
|
||||
}
|
||||
|
||||
JSValue jszip = JS_NewObjectClass(js, js_writer_class_id);
|
||||
JS_SetOpaque(jszip, zip);
|
||||
return jszip;
|
||||
}
|
||||
|
||||
static JSValue js_miniz_compress(JSContext *js, JSValue this_val,
|
||||
int argc, JSValueConst *argv)
|
||||
{
|
||||
if (argc < 1)
|
||||
return JS_ThrowTypeError(js,
|
||||
"compress needs a string or ArrayBuffer");
|
||||
|
||||
/* ─── 1. Grab the input data ──────────────────────────────── */
|
||||
const char *cstring = NULL;
|
||||
size_t in_len = 0;
|
||||
const void *in_ptr = NULL;
|
||||
|
||||
if (JS_IsString(argv[0])) {
|
||||
/* String → UTF-8 bytes without the terminating NUL */
|
||||
cstring = JS_ToCStringLen(js, &in_len, argv[0]);
|
||||
if (!cstring)
|
||||
return JS_EXCEPTION;
|
||||
in_ptr = cstring;
|
||||
} else {
|
||||
in_ptr = js_get_blob_data(js, &in_len, argv[0]);
|
||||
if (!in_ptr)
|
||||
return JS_ThrowTypeError(js,
|
||||
"Argument must be a string or ArrayBuffer");
|
||||
}
|
||||
|
||||
/* ─── 2. Allocate an output buffer big enough ────────────── */
|
||||
mz_ulong out_len_est = mz_compressBound(in_len);
|
||||
void *out_buf = js_malloc(js, out_len_est);
|
||||
if (!out_buf) {
|
||||
if (cstring) JS_FreeCString(js, cstring);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
/* ─── 3. Do the compression (MZ_DEFAULT_COMPRESSION = level 6) */
|
||||
mz_ulong out_len = out_len_est;
|
||||
int st = mz_compress2(out_buf, &out_len,
|
||||
in_ptr, in_len, MZ_DEFAULT_COMPRESSION);
|
||||
|
||||
/* clean-up for string input */
|
||||
if (cstring) JS_FreeCString(js, cstring);
|
||||
|
||||
if (st != MZ_OK) {
|
||||
js_free(js, out_buf);
|
||||
return JS_ThrowInternalError(js,
|
||||
"miniz: compression failed (%d)", st);
|
||||
}
|
||||
|
||||
/* ─── 4. Hand JavaScript a copy of the compressed data ────── */
|
||||
JSValue abuf = js_new_blob_stoned_copy(js, out_buf, out_len);
|
||||
js_free(js, out_buf);
|
||||
return abuf;
|
||||
}
|
||||
|
||||
static JSValue js_miniz_decompress(JSContext *js,
|
||||
JSValueConst this_val,
|
||||
int argc,
|
||||
JSValueConst *argv)
|
||||
{
|
||||
if (argc < 1)
|
||||
return JS_ThrowTypeError(js,
|
||||
"decompress: need compressed ArrayBuffer");
|
||||
|
||||
/* grab compressed data */
|
||||
size_t in_len;
|
||||
void *in_ptr = js_get_blob_data(js, &in_len, argv[0]);
|
||||
if (!in_ptr)
|
||||
return JS_ThrowTypeError(js,
|
||||
"decompress: first arg must be an ArrayBuffer");
|
||||
|
||||
/* zlib header present → tell tinfl to parse it */
|
||||
size_t out_len = 0;
|
||||
void *out_ptr = tinfl_decompress_mem_to_heap(
|
||||
in_ptr, in_len, &out_len,
|
||||
TINFL_FLAG_PARSE_ZLIB_HEADER);
|
||||
|
||||
if (!out_ptr)
|
||||
return JS_ThrowInternalError(js,
|
||||
"miniz: decompression failed");
|
||||
|
||||
/* wrap for JS */
|
||||
JSValue ret;
|
||||
int asString = (argc > 1) && JS_ToBool(js, argv[1]);
|
||||
|
||||
// if (asString)
|
||||
ret = JS_NewStringLen(js, (const char *)out_ptr, out_len);
|
||||
// else
|
||||
// ret = JS_NewArrayBufferCopy(js, out_ptr, out_len);
|
||||
|
||||
#ifdef MZ_FREE
|
||||
MZ_FREE(out_ptr);
|
||||
#else
|
||||
free(out_ptr);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static const JSCFunctionListEntry js_miniz_funcs[] = {
|
||||
JS_CFUNC_DEF("read", 1, js_miniz_read),
|
||||
JS_CFUNC_DEF("write", 1, js_miniz_write),
|
||||
JS_CFUNC_DEF("compress", 1, js_miniz_compress),
|
||||
JS_CFUNC_DEF("decompress", 1, js_miniz_decompress),
|
||||
};
|
||||
|
||||
JSValue js_writer_add_file(JSContext *js, JSValue self, int argc, JSValue *argv)
|
||||
{
|
||||
if (argc < 2)
|
||||
return JS_ThrowTypeError(js, "add_file requires (path, arrayBuffer)");
|
||||
|
||||
mz_zip_archive *zip = js2writer(js, self);
|
||||
const char *pathInZip = JS_ToCString(js, argv[0]);
|
||||
if (!pathInZip)
|
||||
return JS_ThrowTypeError(js, "Could not parse path argument");
|
||||
|
||||
size_t dataLen;
|
||||
void *data = js_get_blob_data(js, &dataLen, argv[1]);
|
||||
if (!data) {
|
||||
JS_FreeCString(js, pathInZip);
|
||||
return JS_ThrowTypeError(js, "Second argument must be an ArrayBuffer");
|
||||
}
|
||||
|
||||
int success = mz_zip_writer_add_mem(zip, pathInZip, data, dataLen, MZ_DEFAULT_COMPRESSION);
|
||||
JS_FreeCString(js, pathInZip);
|
||||
|
||||
if (!success)
|
||||
return JS_ThrowInternalError(js, "Failed to add memory to zip");
|
||||
|
||||
return JS_NULL;
|
||||
}
|
||||
|
||||
|
||||
static const JSCFunctionListEntry js_writer_funcs[] = {
|
||||
JS_CFUNC_DEF("add_file", 1, js_writer_add_file),
|
||||
};
|
||||
|
||||
JSValue js_reader_mod(JSContext *js, JSValue self, int argc, JSValue *argv)
|
||||
{
|
||||
const char *file = JS_ToCString(js,argv[0]);
|
||||
if (!file)
|
||||
return JS_EXCEPTION;
|
||||
|
||||
mz_zip_archive *zip = js2reader(js, self);
|
||||
if (!zip) {
|
||||
JS_FreeCString(js, file);
|
||||
return JS_ThrowInternalError(js, "Invalid zip reader");
|
||||
}
|
||||
|
||||
mz_zip_archive_file_stat pstat;
|
||||
mz_uint index = mz_zip_reader_locate_file(zip, file, NULL, 0);
|
||||
|
||||
if (index == (mz_uint)-1) {
|
||||
JS_FreeCString(js, file);
|
||||
return JS_ThrowReferenceError(js, "File '%s' not found in archive", file);
|
||||
}
|
||||
|
||||
JS_FreeCString(js, file);
|
||||
|
||||
if (!mz_zip_reader_file_stat(zip, index, &pstat)) {
|
||||
int err = mz_zip_get_last_error(zip);
|
||||
return JS_ThrowInternalError(js, "Failed to get file stats: %s", mz_zip_get_error_string(err));
|
||||
}
|
||||
|
||||
return JS_NewFloat64(js, pstat.m_time);
|
||||
}
|
||||
|
||||
JSValue js_reader_exists(JSContext *js, JSValue self, int argc, JSValue *argv)
|
||||
{
|
||||
const char *file = JS_ToCString(js,argv[0]);
|
||||
if (!file)
|
||||
return JS_EXCEPTION;
|
||||
|
||||
mz_zip_archive *zip = js2reader(js, self);
|
||||
if (!zip) {
|
||||
JS_FreeCString(js, file);
|
||||
return JS_ThrowInternalError(js, "Invalid zip reader");
|
||||
}
|
||||
|
||||
mz_uint index = mz_zip_reader_locate_file(zip, file, NULL, 0);
|
||||
JS_FreeCString(js,file);
|
||||
if (index == (mz_uint)-1) return JS_NewBool(js, 0);
|
||||
return JS_NewBool(js, 1);
|
||||
}
|
||||
|
||||
JSValue js_reader_slurp(JSContext *js, JSValue self, int argc, JSValue *argv)
|
||||
{
|
||||
const char *file = JS_ToCString(js,argv[0]);
|
||||
if (!file)
|
||||
return JS_EXCEPTION;
|
||||
|
||||
mz_zip_archive *zip = js2reader(js, self);
|
||||
if (!zip) {
|
||||
JS_FreeCString(js, file);
|
||||
return JS_ThrowInternalError(js, "Invalid zip reader");
|
||||
}
|
||||
|
||||
size_t len;
|
||||
void *data = mz_zip_reader_extract_file_to_heap(zip, file, &len, 0);
|
||||
|
||||
if (!data) {
|
||||
int err = mz_zip_get_last_error(zip);
|
||||
const char *filename = file;
|
||||
JS_FreeCString(js, file);
|
||||
return JS_ThrowInternalError(js, "Failed to extract file '%s': %s", filename, mz_zip_get_error_string(err));
|
||||
}
|
||||
|
||||
JS_FreeCString(js, file);
|
||||
|
||||
JSValue ret = js_new_blob_stoned_copy(js, data, len);
|
||||
free(data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
JSValue js_reader_list(JSContext *js, JSValue self, int argc, JSValue *argv)
|
||||
{
|
||||
mz_zip_archive *zip = js2reader(js, self);
|
||||
if (!zip)
|
||||
return JS_ThrowInternalError(js, "Invalid zip reader");
|
||||
|
||||
mz_uint num_files = mz_zip_reader_get_num_files(zip);
|
||||
|
||||
JSValue arr = JS_NewArray(js);
|
||||
if (JS_IsException(arr))
|
||||
return arr;
|
||||
|
||||
mz_uint arr_index = 0;
|
||||
for (mz_uint i = 0; i < num_files; i++) {
|
||||
mz_zip_archive_file_stat file_stat;
|
||||
if (!mz_zip_reader_file_stat(zip, i, &file_stat))
|
||||
continue;
|
||||
|
||||
JSValue filename = JS_NewString(js, file_stat.m_filename);
|
||||
if (JS_IsException(filename)) {
|
||||
JS_FreeValue(js, arr);
|
||||
return filename;
|
||||
}
|
||||
JS_SetPropertyUint32(js, arr, arr_index++, filename);
|
||||
}
|
||||
|
||||
return arr;
|
||||
}
|
||||
|
||||
JSValue js_reader_is_directory(JSContext *js, JSValue self, int argc, JSValue *argv)
|
||||
{
|
||||
if (argc < 1)
|
||||
return JS_ThrowTypeError(js, "is_directory requires a file index");
|
||||
|
||||
int32_t index;
|
||||
if (JS_ToInt32(js, &index, argv[0]))
|
||||
return JS_EXCEPTION;
|
||||
|
||||
mz_zip_archive *zip = js2reader(js, self);
|
||||
if (!zip)
|
||||
return JS_ThrowInternalError(js, "Invalid zip reader");
|
||||
|
||||
return JS_NewBool(js, mz_zip_reader_is_file_a_directory(zip, index));
|
||||
}
|
||||
|
||||
JSValue js_reader_get_filename(JSContext *js, JSValue self, int argc, JSValue *argv)
|
||||
{
|
||||
if (argc < 1)
|
||||
return JS_ThrowTypeError(js, "get_filename requires a file index");
|
||||
|
||||
int32_t index;
|
||||
if (JS_ToInt32(js, &index, argv[0]))
|
||||
return JS_EXCEPTION;
|
||||
|
||||
mz_zip_archive *zip = js2reader(js, self);
|
||||
if (!zip)
|
||||
return JS_ThrowInternalError(js, "Invalid zip reader");
|
||||
|
||||
mz_zip_archive_file_stat file_stat;
|
||||
if (!mz_zip_reader_file_stat(zip, index, &file_stat))
|
||||
return JS_ThrowInternalError(js, "Failed to get file stats");
|
||||
|
||||
return JS_NewString(js, file_stat.m_filename);
|
||||
}
|
||||
|
||||
JSValue js_reader_count(JSContext *js, JSValue self, int argc, JSValue *argv)
|
||||
{
|
||||
mz_zip_archive *zip = js2reader(js, self);
|
||||
if (!zip)
|
||||
return JS_ThrowInternalError(js, "Invalid zip reader");
|
||||
|
||||
return JS_NewUint32(js, mz_zip_reader_get_num_files(zip));
|
||||
}
|
||||
|
||||
static const JSCFunctionListEntry js_reader_funcs[] = {
|
||||
JS_CFUNC_DEF("mod", 1, js_reader_mod),
|
||||
JS_CFUNC_DEF("exists", 1, js_reader_exists),
|
||||
JS_CFUNC_DEF("slurp", 1, js_reader_slurp),
|
||||
JS_CFUNC_DEF("list", 0, js_reader_list),
|
||||
JS_CFUNC_DEF("is_directory", 1, js_reader_is_directory),
|
||||
JS_CFUNC_DEF("get_filename", 1, js_reader_get_filename),
|
||||
JS_CFUNC_DEF("count", 0, js_reader_count),
|
||||
};
|
||||
|
||||
JSValue js_miniz_use(JSContext *js)
|
||||
{
|
||||
JS_NewClassID(&js_reader_class_id);
|
||||
JS_NewClass(JS_GetRuntime(js), js_reader_class_id, &js_reader_class);
|
||||
JSValue reader_proto = JS_NewObject(js);
|
||||
JS_SetPropertyFunctionList(js, reader_proto, js_reader_funcs, sizeof(js_reader_funcs) / sizeof(JSCFunctionListEntry));
|
||||
JS_SetClassProto(js, js_reader_class_id, reader_proto);
|
||||
|
||||
JS_NewClassID(&js_writer_class_id);
|
||||
JS_NewClass(JS_GetRuntime(js), js_writer_class_id, &js_writer_class);
|
||||
JSValue writer_proto = JS_NewObject(js);
|
||||
JS_SetPropertyFunctionList(js, writer_proto, js_writer_funcs, sizeof(js_writer_funcs) / sizeof(JSCFunctionListEntry));
|
||||
JS_SetClassProto(js, js_writer_class_id, writer_proto);
|
||||
|
||||
JSValue export = JS_NewObject(js);
|
||||
JS_SetPropertyFunctionList(js, export, js_miniz_funcs, sizeof(js_miniz_funcs)/sizeof(JSCFunctionListEntry));
|
||||
return export;
|
||||
}
|
||||
@@ -1,349 +0,0 @@
|
||||
#include "qjs_nota.h"
|
||||
#include "cell.h"
|
||||
|
||||
#define NOTA_IMPLEMENTATION
|
||||
#include "nota.h"
|
||||
|
||||
typedef struct NotaEncodeContext {
|
||||
JSContext *ctx;
|
||||
JSValue visitedStack;
|
||||
NotaBuffer nb;
|
||||
int cycle;
|
||||
JSValue replacer;
|
||||
} NotaEncodeContext;
|
||||
|
||||
static void nota_stack_push(NotaEncodeContext *enc, JSValueConst val)
|
||||
{
|
||||
JSContext *ctx = enc->ctx;
|
||||
int len = JS_ArrayLength(ctx, enc->visitedStack);
|
||||
JS_SetPropertyInt64(ctx, enc->visitedStack, len, JS_DupValue(ctx, val));
|
||||
}
|
||||
|
||||
static void nota_stack_pop(NotaEncodeContext *enc)
|
||||
{
|
||||
JSContext *ctx = enc->ctx;
|
||||
int len = JS_ArrayLength(ctx, enc->visitedStack);
|
||||
JS_SetPropertyStr(ctx, enc->visitedStack, "length", JS_NewUint32(ctx, len - 1));
|
||||
}
|
||||
|
||||
static int nota_stack_has(NotaEncodeContext *enc, JSValueConst val)
|
||||
{
|
||||
JSContext *ctx = enc->ctx;
|
||||
int len = JS_ArrayLength(ctx, enc->visitedStack);
|
||||
for (int i = 0; i < len; i++) {
|
||||
JSValue elem = JS_GetPropertyUint32(ctx, enc->visitedStack, i);
|
||||
if (JS_IsObject(elem) && JS_IsObject(val)) {
|
||||
if (JS_StrictEq(ctx, elem, val)) {
|
||||
JS_FreeValue(ctx, elem);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
JS_FreeValue(ctx, elem);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static JSValue apply_replacer(NotaEncodeContext *enc, JSValueConst holder, JSValueConst key, JSValueConst val) {
|
||||
if (JS_IsNull(enc->replacer)) return JS_DupValue(enc->ctx, val);
|
||||
|
||||
JSValue args[2] = { JS_DupValue(enc->ctx, key), JS_DupValue(enc->ctx, val) };
|
||||
JSValue result = JS_Call(enc->ctx, enc->replacer, holder, 2, args);
|
||||
JS_FreeValue(enc->ctx, args[0]);
|
||||
JS_FreeValue(enc->ctx, args[1]);
|
||||
|
||||
if (JS_IsException(result)) return JS_DupValue(enc->ctx, val);
|
||||
return result;
|
||||
}
|
||||
|
||||
char *js_do_nota_decode(JSContext *js, JSValue *tmp, char *nota, JSValue holder, JSValue key, JSValue reviver) {
|
||||
int type = nota_type(nota);
|
||||
JSValue ret2;
|
||||
long long n;
|
||||
double d;
|
||||
int b;
|
||||
char *str;
|
||||
uint8_t *blob;
|
||||
|
||||
switch(type) {
|
||||
case NOTA_BLOB:
|
||||
nota = nota_read_blob(&n, (char**)&blob, nota);
|
||||
*tmp = js_new_blob_stoned_copy(js, blob, n);
|
||||
free(blob);
|
||||
break;
|
||||
case NOTA_TEXT:
|
||||
nota = nota_read_text(&str, nota);
|
||||
*tmp = JS_NewString(js, str);
|
||||
free(str);
|
||||
break;
|
||||
case NOTA_ARR:
|
||||
nota = nota_read_array(&n, nota);
|
||||
*tmp = JS_NewArray(js);
|
||||
for (int i = 0; i < n; i++) {
|
||||
nota = js_do_nota_decode(js, &ret2, nota, *tmp, JS_NewInt32(js, i), reviver);
|
||||
JS_SetPropertyInt64(js, *tmp, i, ret2);
|
||||
}
|
||||
break;
|
||||
case NOTA_REC:
|
||||
nota = nota_read_record(&n, nota);
|
||||
*tmp = JS_NewObject(js);
|
||||
for (int i = 0; i < n; i++) {
|
||||
nota = nota_read_text(&str, nota);
|
||||
JSValue prop_key = JS_NewString(js, str);
|
||||
nota = js_do_nota_decode(js, &ret2, nota, *tmp, prop_key, reviver);
|
||||
JS_SetPropertyStr(js, *tmp, str, ret2);
|
||||
JS_FreeValue(js, prop_key);
|
||||
free(str);
|
||||
}
|
||||
break;
|
||||
case NOTA_INT:
|
||||
nota = nota_read_int(&n, nota);
|
||||
*tmp = JS_NewInt64(js, n);
|
||||
break;
|
||||
case NOTA_SYM:
|
||||
nota = nota_read_sym(&b, nota);
|
||||
if (b == NOTA_PRIVATE) {
|
||||
JSValue inner;
|
||||
nota = js_do_nota_decode(js, &inner, nota, holder, JS_NULL, reviver);
|
||||
JSValue obj = JS_NewObject(js);
|
||||
cell_rt *crt = JS_GetContextOpaque(js);
|
||||
JS_SetProperty(js, obj, crt->actor_sym, inner);
|
||||
*tmp = obj;
|
||||
} else {
|
||||
switch(b) {
|
||||
case NOTA_NULL: *tmp = JS_NULL; break;
|
||||
case NOTA_FALSE: *tmp = JS_NewBool(js, 0); break;
|
||||
case NOTA_TRUE: *tmp = JS_NewBool(js, 1); break;
|
||||
default: *tmp = JS_NULL; break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
case NOTA_FLOAT:
|
||||
nota = nota_read_float(&d, nota);
|
||||
*tmp = JS_NewFloat64(js, d);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!JS_IsNull(reviver)) {
|
||||
JSValue args[2] = { JS_DupValue(js, key), JS_DupValue(js, *tmp) };
|
||||
JSValue revived = JS_Call(js, reviver, holder, 2, args);
|
||||
JS_FreeValue(js, args[0]);
|
||||
JS_FreeValue(js, args[1]);
|
||||
if (!JS_IsException(revived)) {
|
||||
JS_FreeValue(js, *tmp);
|
||||
*tmp = revived;
|
||||
} else {
|
||||
JS_FreeValue(js, revived);
|
||||
}
|
||||
}
|
||||
|
||||
return nota;
|
||||
}
|
||||
|
||||
static void nota_encode_value(NotaEncodeContext *enc, JSValueConst val, JSValueConst holder, JSValueConst key) {
|
||||
JSContext *ctx = enc->ctx;
|
||||
JSValue replaced = apply_replacer(enc, holder, key, val);
|
||||
int tag = JS_VALUE_GET_TAG(replaced);
|
||||
|
||||
switch (tag) {
|
||||
case JS_TAG_INT:
|
||||
case JS_TAG_FLOAT64: {
|
||||
double d;
|
||||
JS_ToFloat64(ctx, &d, replaced);
|
||||
nota_write_number(&enc->nb, d);
|
||||
break;
|
||||
}
|
||||
case JS_TAG_STRING: {
|
||||
const char *str = JS_ToCString(ctx, replaced);
|
||||
nota_write_text(&enc->nb, str);
|
||||
JS_FreeCString(ctx, str);
|
||||
break;
|
||||
}
|
||||
case JS_TAG_BOOL:
|
||||
if (JS_VALUE_GET_BOOL(replaced)) nota_write_sym(&enc->nb, NOTA_TRUE);
|
||||
else nota_write_sym(&enc->nb, NOTA_FALSE);
|
||||
break;
|
||||
case JS_TAG_NULL:
|
||||
nota_write_sym(&enc->nb, NOTA_NULL);
|
||||
break;
|
||||
case JS_TAG_OBJECT: {
|
||||
if (js_is_blob(ctx, replaced)) {
|
||||
size_t buf_len;
|
||||
void *buf_data = js_get_blob_data(ctx, &buf_len, replaced);
|
||||
nota_write_blob(&enc->nb, (unsigned long long)buf_len * 8, (const char*)buf_data);
|
||||
break;
|
||||
}
|
||||
|
||||
if (JS_IsArray(ctx, replaced)) {
|
||||
if (nota_stack_has(enc, replaced)) {
|
||||
enc->cycle = 1;
|
||||
break;
|
||||
}
|
||||
nota_stack_push(enc, replaced);
|
||||
int arr_len = JS_ArrayLength(ctx, replaced);
|
||||
nota_write_array(&enc->nb, arr_len);
|
||||
for (int i = 0; i < arr_len; i++) {
|
||||
JSValue elem_val = JS_GetPropertyUint32(ctx, replaced, i);
|
||||
JSValue elem_key = JS_NewInt32(ctx, i);
|
||||
nota_encode_value(enc, elem_val, replaced, elem_key);
|
||||
JS_FreeValue(ctx, elem_val);
|
||||
JS_FreeValue(ctx, elem_key);
|
||||
}
|
||||
nota_stack_pop(enc);
|
||||
break;
|
||||
}
|
||||
|
||||
cell_rt *crt = JS_GetContextOpaque(ctx);
|
||||
JSValue adata = JS_GetProperty(ctx, replaced, crt->actor_sym);
|
||||
if (!JS_IsNull(adata)) {
|
||||
nota_write_sym(&enc->nb, NOTA_PRIVATE);
|
||||
nota_encode_value(enc, adata, replaced, JS_NULL);
|
||||
JS_FreeValue(ctx, adata);
|
||||
break;
|
||||
}
|
||||
JS_FreeValue(ctx, adata);
|
||||
if (nota_stack_has(enc, replaced)) {
|
||||
enc->cycle = 1;
|
||||
break;
|
||||
}
|
||||
nota_stack_push(enc, replaced);
|
||||
|
||||
JSValue to_json = JS_GetPropertyStr(ctx, replaced, "toJSON");
|
||||
if (JS_IsFunction(ctx, to_json)) {
|
||||
JSValue result = JS_Call(ctx, to_json, replaced, 0, NULL);
|
||||
JS_FreeValue(ctx, to_json);
|
||||
if (!JS_IsException(result)) {
|
||||
nota_encode_value(enc, result, holder, key);
|
||||
JS_FreeValue(ctx, result);
|
||||
} else {
|
||||
nota_write_sym(&enc->nb, NOTA_NULL);
|
||||
}
|
||||
nota_stack_pop(enc);
|
||||
break;
|
||||
}
|
||||
JS_FreeValue(ctx, to_json);
|
||||
|
||||
JSPropertyEnum *ptab;
|
||||
uint32_t plen;
|
||||
if (JS_GetOwnPropertyNames(ctx, &ptab, &plen, replaced, JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK) < 0) {
|
||||
nota_write_sym(&enc->nb, NOTA_NULL);
|
||||
nota_stack_pop(enc);
|
||||
break;
|
||||
}
|
||||
|
||||
uint32_t non_function_count = 0;
|
||||
for (uint32_t i = 0; i < plen; i++) {
|
||||
JSValue prop_val = JS_GetProperty(ctx, replaced, ptab[i].atom);
|
||||
if (!JS_IsFunction(ctx, prop_val)) non_function_count++;
|
||||
JS_FreeValue(ctx, prop_val);
|
||||
}
|
||||
|
||||
nota_write_record(&enc->nb, non_function_count);
|
||||
for (uint32_t i = 0; i < plen; i++) {
|
||||
JSValue prop_val = JS_GetProperty(ctx, replaced, ptab[i].atom);
|
||||
if (!JS_IsFunction(ctx, prop_val)) {
|
||||
const char *prop_name = JS_AtomToCString(ctx, ptab[i].atom);
|
||||
JSValue prop_key = JS_AtomToValue(ctx, ptab[i].atom);
|
||||
nota_write_text(&enc->nb, prop_name);
|
||||
nota_encode_value(enc, prop_val, replaced, prop_key);
|
||||
JS_FreeCString(ctx, prop_name);
|
||||
JS_FreeValue(ctx, prop_key);
|
||||
}
|
||||
JS_FreeValue(ctx, prop_val);
|
||||
JS_FreeAtom(ctx, ptab[i].atom);
|
||||
}
|
||||
js_free(ctx, ptab);
|
||||
nota_stack_pop(enc);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
nota_write_sym(&enc->nb, NOTA_NULL);
|
||||
break;
|
||||
}
|
||||
JS_FreeValue(ctx, replaced);
|
||||
}
|
||||
|
||||
void *value2nota(JSContext *ctx, JSValue v) {
|
||||
NotaEncodeContext enc_s, *enc = &enc_s;
|
||||
enc->ctx = ctx;
|
||||
enc->visitedStack = JS_NewArray(ctx);
|
||||
enc->cycle = 0;
|
||||
enc->replacer = JS_NULL;
|
||||
|
||||
nota_buffer_init(&enc->nb, 128);
|
||||
nota_encode_value(enc, v, JS_NULL, JS_NewString(ctx, ""));
|
||||
|
||||
if (enc->cycle) {
|
||||
JS_FreeValue(ctx, enc->visitedStack);
|
||||
nota_buffer_free(&enc->nb);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
JS_FreeValue(ctx, enc->visitedStack);
|
||||
void *data_ptr = enc->nb.data;
|
||||
enc->nb.data = NULL;
|
||||
nota_buffer_free(&enc->nb);
|
||||
return data_ptr;
|
||||
}
|
||||
|
||||
JSValue nota2value(JSContext *js, void *nota) {
|
||||
if (!nota) return JS_NULL;
|
||||
JSValue ret;
|
||||
JSValue holder = JS_NewObject(js);
|
||||
js_do_nota_decode(js, &ret, nota, holder, JS_NewString(js, ""), JS_NULL);
|
||||
JS_FreeValue(js, holder);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static JSValue js_nota_encode(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
|
||||
if (argc < 1) return JS_ThrowTypeError(ctx, "nota.encode requires at least 1 argument");
|
||||
|
||||
NotaEncodeContext enc_s, *enc = &enc_s;
|
||||
enc->ctx = ctx;
|
||||
enc->visitedStack = JS_NewArray(ctx);
|
||||
enc->cycle = 0;
|
||||
enc->replacer = (argc > 1 && JS_IsFunction(ctx, argv[1])) ? argv[1] : JS_NULL;
|
||||
|
||||
nota_buffer_init(&enc->nb, 128);
|
||||
nota_encode_value(enc, argv[0], JS_NULL, JS_NewString(ctx, ""));
|
||||
|
||||
if (enc->cycle) {
|
||||
JS_FreeValue(ctx, enc->visitedStack);
|
||||
nota_buffer_free(&enc->nb);
|
||||
return JS_ThrowReferenceError(ctx, "Tried to encode something to nota with a cycle.");
|
||||
}
|
||||
|
||||
JS_FreeValue(ctx, enc->visitedStack);
|
||||
size_t total_len = enc->nb.size;
|
||||
void *data_ptr = enc->nb.data;
|
||||
JSValue ret = js_new_blob_stoned_copy(ctx, (uint8_t*)data_ptr, total_len);
|
||||
|
||||
nota_buffer_free(&enc->nb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static JSValue js_nota_decode(JSContext *js, JSValueConst self, int argc, JSValueConst *argv) {
|
||||
if (argc < 1) return JS_NULL;
|
||||
|
||||
size_t len;
|
||||
unsigned char *nota = js_get_blob_data(js, &len, argv[0]);
|
||||
if (!nota) return JS_NULL;
|
||||
|
||||
JSValue reviver = (argc > 1 && JS_IsFunction(js, argv[1])) ? argv[1] : JS_NULL;
|
||||
JSValue ret;
|
||||
JSValue holder = JS_NewObject(js);
|
||||
js_do_nota_decode(js, &ret, (char*)nota, holder, JS_NewString(js, ""), reviver);
|
||||
JS_FreeValue(js, holder);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const JSCFunctionListEntry js_nota_funcs[] = {
|
||||
JS_CFUNC_DEF("encode", 1, js_nota_encode),
|
||||
JS_CFUNC_DEF("decode", 1, js_nota_decode),
|
||||
};
|
||||
|
||||
JSValue js_nota_use(JSContext *js) {
|
||||
JSValue export = JS_NewObject(js);
|
||||
JS_SetPropertyFunctionList(js, export, js_nota_funcs, sizeof(js_nota_funcs)/sizeof(JSCFunctionListEntry));
|
||||
return export;
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
#ifndef QJS_NOTA_H
|
||||
#define QJS_NOTA_H
|
||||
|
||||
#include "cell.h"
|
||||
|
||||
JSValue js_nota_use(JSContext*);
|
||||
|
||||
void *value2nota(JSContext*, JSValue);
|
||||
JSValue nota2value(JSContext*, void*);
|
||||
|
||||
#endif
|
||||
699
source/qjs_num.c
699
source/qjs_num.c
@@ -1,699 +0,0 @@
|
||||
#include "quickjs.h"
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <Accelerate/Accelerate.h>
|
||||
#else
|
||||
#include <cblas.h>
|
||||
#include <lapacke.h>
|
||||
#endif
|
||||
|
||||
static JSClassID js_matrix_class_id;
|
||||
static JSClassID js_array_class_id;
|
||||
|
||||
typedef struct {
|
||||
double *data;
|
||||
int rows;
|
||||
int cols;
|
||||
} matrix_t;
|
||||
|
||||
typedef struct {
|
||||
double *data;
|
||||
int size;
|
||||
} array_t;
|
||||
|
||||
static void js_matrix_finalizer(JSRuntime *rt, JSValue val) {
|
||||
matrix_t *mat = JS_GetOpaque(val, js_matrix_class_id);
|
||||
if (mat) {
|
||||
js_free_rt(rt, mat->data);
|
||||
js_free_rt(rt, mat);
|
||||
}
|
||||
}
|
||||
|
||||
static void js_array_finalizer(JSRuntime *rt, JSValue val) {
|
||||
array_t *arr = JS_GetOpaque(val, js_array_class_id);
|
||||
if (arr) {
|
||||
js_free_rt(rt, arr->data);
|
||||
js_free_rt(rt, arr);
|
||||
}
|
||||
}
|
||||
|
||||
static JSClassDef js_matrix_class = {
|
||||
"Matrix",
|
||||
.finalizer = js_matrix_finalizer,
|
||||
};
|
||||
|
||||
static JSClassDef js_array_class = {
|
||||
"Array",
|
||||
.finalizer = js_array_finalizer,
|
||||
};
|
||||
|
||||
static matrix_t *js2matrix(JSContext *ctx, JSValue v) {
|
||||
return JS_GetOpaque(v, js_matrix_class_id);
|
||||
}
|
||||
|
||||
static array_t *js2array(JSContext *ctx, JSValue v) {
|
||||
return JS_GetOpaque(v, js_array_class_id);
|
||||
}
|
||||
|
||||
static JSValue js_matrix_constructor(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
|
||||
if (argc < 1)
|
||||
return JS_ThrowTypeError(ctx, "Matrix constructor requires an array argument");
|
||||
|
||||
if (!JS_IsArray(ctx, argv[0]))
|
||||
return JS_ThrowTypeError(ctx, "Matrix constructor requires an array argument");
|
||||
|
||||
JSValue length_val = JS_GetPropertyStr(ctx, argv[0], "length");
|
||||
int32_t rows;
|
||||
JS_ToInt32(ctx, &rows, length_val);
|
||||
JS_FreeValue(ctx, length_val);
|
||||
|
||||
if (rows == 0)
|
||||
return JS_ThrowRangeError(ctx, "Matrix cannot have 0 rows");
|
||||
|
||||
// Get first row to determine columns
|
||||
JSValue first_row = JS_GetPropertyUint32(ctx, argv[0], 0);
|
||||
if (!JS_IsArray(ctx, first_row)) {
|
||||
JS_FreeValue(ctx, first_row);
|
||||
return JS_ThrowTypeError(ctx, "Matrix rows must be arrays");
|
||||
}
|
||||
|
||||
JSValue col_length_val = JS_GetPropertyStr(ctx, first_row, "length");
|
||||
int32_t cols;
|
||||
JS_ToInt32(ctx, &cols, col_length_val);
|
||||
JS_FreeValue(ctx, col_length_val);
|
||||
JS_FreeValue(ctx, first_row);
|
||||
|
||||
if (cols == 0)
|
||||
return JS_ThrowRangeError(ctx, "Matrix cannot have 0 columns");
|
||||
|
||||
matrix_t *mat = js_malloc(ctx, sizeof(matrix_t));
|
||||
if (!mat)
|
||||
return JS_EXCEPTION;
|
||||
|
||||
mat->rows = rows;
|
||||
mat->cols = cols;
|
||||
mat->data = js_malloc(ctx, sizeof(double) * rows * cols);
|
||||
if (!mat->data) {
|
||||
js_free(ctx, mat);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
// Fill matrix data
|
||||
for (int i = 0; i < rows; i++) {
|
||||
JSValue row = JS_GetPropertyUint32(ctx, argv[0], i);
|
||||
if (!JS_IsArray(ctx, row)) {
|
||||
js_free(ctx, mat->data);
|
||||
js_free(ctx, mat);
|
||||
JS_FreeValue(ctx, row);
|
||||
return JS_ThrowTypeError(ctx, "All matrix rows must be arrays");
|
||||
}
|
||||
|
||||
JSValue row_length_val = JS_GetPropertyStr(ctx, row, "length");
|
||||
int32_t row_cols;
|
||||
JS_ToInt32(ctx, &row_cols, row_length_val);
|
||||
JS_FreeValue(ctx, row_length_val);
|
||||
|
||||
if (row_cols != cols) {
|
||||
js_free(ctx, mat->data);
|
||||
js_free(ctx, mat);
|
||||
JS_FreeValue(ctx, row);
|
||||
return JS_ThrowTypeError(ctx, "All matrix rows must have the same length");
|
||||
}
|
||||
|
||||
for (int j = 0; j < cols; j++) {
|
||||
JSValue elem = JS_GetPropertyUint32(ctx, row, j);
|
||||
double val;
|
||||
if (JS_ToFloat64(ctx, &val, elem)) {
|
||||
js_free(ctx, mat->data);
|
||||
js_free(ctx, mat);
|
||||
JS_FreeValue(ctx, elem);
|
||||
JS_FreeValue(ctx, row);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
mat->data[i * cols + j] = val;
|
||||
JS_FreeValue(ctx, elem);
|
||||
}
|
||||
JS_FreeValue(ctx, row);
|
||||
}
|
||||
|
||||
JSValue obj = JS_NewObjectClass(ctx, js_matrix_class_id);
|
||||
JS_SetOpaque(obj, mat);
|
||||
return obj;
|
||||
}
|
||||
|
||||
static JSValue js_array_constructor(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
|
||||
if (argc < 1)
|
||||
return JS_ThrowTypeError(ctx, "Array constructor requires an array argument");
|
||||
|
||||
if (!JS_IsArray(ctx, argv[0]))
|
||||
return JS_ThrowTypeError(ctx, "Array constructor requires an array argument");
|
||||
|
||||
JSValue length_val = JS_GetPropertyStr(ctx, argv[0], "length");
|
||||
int32_t size;
|
||||
JS_ToInt32(ctx, &size, length_val);
|
||||
JS_FreeValue(ctx, length_val);
|
||||
|
||||
if (size == 0)
|
||||
return JS_ThrowRangeError(ctx, "Array cannot have 0 elements");
|
||||
|
||||
array_t *arr = js_malloc(ctx, sizeof(array_t));
|
||||
if (!arr)
|
||||
return JS_EXCEPTION;
|
||||
|
||||
arr->size = size;
|
||||
arr->data = js_malloc(ctx, sizeof(double) * size);
|
||||
if (!arr->data) {
|
||||
js_free(ctx, arr);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
// Fill array data
|
||||
for (int i = 0; i < size; i++) {
|
||||
JSValue elem = JS_GetPropertyUint32(ctx, argv[0], i);
|
||||
double val;
|
||||
if (JS_ToFloat64(ctx, &val, elem)) {
|
||||
js_free(ctx, arr->data);
|
||||
js_free(ctx, arr);
|
||||
JS_FreeValue(ctx, elem);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
arr->data[i] = val;
|
||||
JS_FreeValue(ctx, elem);
|
||||
}
|
||||
|
||||
JSValue obj = JS_NewObjectClass(ctx, js_array_class_id);
|
||||
JS_SetOpaque(obj, arr);
|
||||
return obj;
|
||||
}
|
||||
|
||||
static JSValue js_matrix_inverse(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
|
||||
matrix_t *mat = js2matrix(ctx, this_val);
|
||||
if (!mat)
|
||||
return JS_EXCEPTION;
|
||||
|
||||
if (mat->rows != mat->cols)
|
||||
return JS_ThrowTypeError(ctx, "Matrix must be square for inversion");
|
||||
|
||||
int n = mat->rows;
|
||||
|
||||
// Create a copy of the matrix
|
||||
matrix_t *result = js_malloc(ctx, sizeof(matrix_t));
|
||||
if (!result)
|
||||
return JS_EXCEPTION;
|
||||
|
||||
result->rows = n;
|
||||
result->cols = n;
|
||||
result->data = js_malloc(ctx, sizeof(double) * n * n);
|
||||
if (!result->data) {
|
||||
js_free(ctx, result);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
memcpy(result->data, mat->data, sizeof(double) * n * n);
|
||||
|
||||
// Allocate pivot array
|
||||
int *ipiv = js_malloc(ctx, sizeof(int) * n);
|
||||
if (!ipiv) {
|
||||
js_free(ctx, result->data);
|
||||
js_free(ctx, result);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
// LU decomposition
|
||||
#ifdef __APPLE__
|
||||
// For macOS with ILP64, use 64-bit integers
|
||||
__LAPACK_int nn = n;
|
||||
__LAPACK_int info = 0;
|
||||
__LAPACK_int *ipiv_lapack = js_malloc(ctx, sizeof(__LAPACK_int) * n);
|
||||
if (!ipiv_lapack) {
|
||||
js_free(ctx, ipiv);
|
||||
js_free(ctx, result->data);
|
||||
js_free(ctx, result);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
// LAPACK uses column-major, but we'll transpose the result
|
||||
__LAPACK_int lda = n;
|
||||
dgetrf_(&nn, &nn, result->data, &lda, ipiv_lapack, &info);
|
||||
|
||||
if (info != 0) {
|
||||
js_free(ctx, ipiv_lapack);
|
||||
js_free(ctx, ipiv);
|
||||
js_free(ctx, result->data);
|
||||
js_free(ctx, result);
|
||||
return JS_ThrowInternalError(ctx, "Matrix LU decomposition failed");
|
||||
}
|
||||
|
||||
// Compute inverse
|
||||
__LAPACK_int lwork = n * n;
|
||||
double *work = js_malloc(ctx, sizeof(double) * lwork);
|
||||
if (!work) {
|
||||
js_free(ctx, ipiv_lapack);
|
||||
js_free(ctx, ipiv);
|
||||
js_free(ctx, result->data);
|
||||
js_free(ctx, result);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
dgetri_(&nn, result->data, &lda, ipiv_lapack, work, &lwork, &info);
|
||||
js_free(ctx, work);
|
||||
js_free(ctx, ipiv_lapack);
|
||||
#else
|
||||
int info = LAPACKE_dgetrf(LAPACK_ROW_MAJOR, n, n, result->data, n, ipiv);
|
||||
if (info != 0) {
|
||||
js_free(ctx, ipiv);
|
||||
js_free(ctx, result->data);
|
||||
js_free(ctx, result);
|
||||
return JS_ThrowInternalError(ctx, "Matrix LU decomposition failed");
|
||||
}
|
||||
|
||||
// Compute inverse
|
||||
info = LAPACKE_dgetri(LAPACK_ROW_MAJOR, n, result->data, n, ipiv);
|
||||
#endif
|
||||
js_free(ctx, ipiv);
|
||||
|
||||
if (info != 0) {
|
||||
js_free(ctx, result->data);
|
||||
js_free(ctx, result);
|
||||
return JS_ThrowInternalError(ctx, "Matrix inversion failed");
|
||||
}
|
||||
|
||||
JSValue obj = JS_NewObjectClass(ctx, js_matrix_class_id);
|
||||
JS_SetOpaque(obj, result);
|
||||
return obj;
|
||||
}
|
||||
|
||||
static JSValue js_matrix_multiply(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
|
||||
if (argc < 1)
|
||||
return JS_ThrowTypeError(ctx, "multiply requires an argument");
|
||||
|
||||
matrix_t *A = js2matrix(ctx, this_val);
|
||||
if (!A)
|
||||
return JS_EXCEPTION;
|
||||
|
||||
// Check if multiplying by another matrix
|
||||
matrix_t *B = js2matrix(ctx, argv[0]);
|
||||
if (B) {
|
||||
if (A->cols != B->rows)
|
||||
return JS_ThrowTypeError(ctx, "Matrix dimensions incompatible for multiplication");
|
||||
|
||||
matrix_t *C = js_malloc(ctx, sizeof(matrix_t));
|
||||
if (!C)
|
||||
return JS_EXCEPTION;
|
||||
|
||||
C->rows = A->rows;
|
||||
C->cols = B->cols;
|
||||
C->data = js_malloc(ctx, sizeof(double) * C->rows * C->cols);
|
||||
if (!C->data) {
|
||||
js_free(ctx, C);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
// C = A * B
|
||||
cblas_dgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans,
|
||||
A->rows, B->cols, A->cols,
|
||||
1.0, A->data, A->cols,
|
||||
B->data, B->cols,
|
||||
0.0, C->data, C->cols);
|
||||
|
||||
JSValue obj = JS_NewObjectClass(ctx, js_matrix_class_id);
|
||||
JS_SetOpaque(obj, C);
|
||||
return obj;
|
||||
}
|
||||
|
||||
// Check if multiplying by an array (matrix-vector multiplication)
|
||||
array_t *x = js2array(ctx, argv[0]);
|
||||
if (x) {
|
||||
if (A->cols != x->size)
|
||||
return JS_ThrowTypeError(ctx, "Matrix columns must match array size");
|
||||
|
||||
array_t *y = js_malloc(ctx, sizeof(array_t));
|
||||
if (!y)
|
||||
return JS_EXCEPTION;
|
||||
|
||||
y->size = A->rows;
|
||||
y->data = js_malloc(ctx, sizeof(double) * y->size);
|
||||
if (!y->data) {
|
||||
js_free(ctx, y);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
// y = A * x
|
||||
cblas_dgemv(CblasRowMajor, CblasNoTrans,
|
||||
A->rows, A->cols,
|
||||
1.0, A->data, A->cols,
|
||||
x->data, 1,
|
||||
0.0, y->data, 1);
|
||||
|
||||
JSValue obj = JS_NewObjectClass(ctx, js_array_class_id);
|
||||
JS_SetOpaque(obj, y);
|
||||
return obj;
|
||||
}
|
||||
|
||||
return JS_ThrowTypeError(ctx, "multiply requires a Matrix or Array argument");
|
||||
}
|
||||
|
||||
static JSValue js_matrix_to_array(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
|
||||
matrix_t *mat = js2matrix(ctx, this_val);
|
||||
if (!mat)
|
||||
return JS_EXCEPTION;
|
||||
|
||||
JSValue arr = JS_NewArray(ctx);
|
||||
if (JS_IsException(arr))
|
||||
return arr;
|
||||
|
||||
for (int i = 0; i < mat->rows; i++) {
|
||||
JSValue row = JS_NewArray(ctx);
|
||||
if (JS_IsException(row)) {
|
||||
JS_FreeValue(ctx, arr);
|
||||
return row;
|
||||
}
|
||||
for (int j = 0; j < mat->cols; j++) {
|
||||
JS_SetPropertyUint32(ctx, row, j, JS_NewFloat64(ctx, mat->data[i * mat->cols + j]));
|
||||
}
|
||||
JS_SetPropertyUint32(ctx, arr, i, row);
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
static JSValue js_array_to_array(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
|
||||
array_t *arr = js2array(ctx, this_val);
|
||||
if (!arr)
|
||||
return JS_EXCEPTION;
|
||||
|
||||
JSValue jsarr = JS_NewArray(ctx);
|
||||
for (int i = 0; i < arr->size; i++) {
|
||||
JS_SetPropertyUint32(ctx, jsarr, i, JS_NewFloat64(ctx, arr->data[i]));
|
||||
}
|
||||
return jsarr;
|
||||
}
|
||||
|
||||
static JSValue js_array_dot(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
|
||||
if (argc < 1)
|
||||
return JS_ThrowTypeError(ctx, "dot requires an argument");
|
||||
|
||||
array_t *a = js2array(ctx, this_val);
|
||||
if (!a)
|
||||
return JS_EXCEPTION;
|
||||
|
||||
array_t *b = js2array(ctx, argv[0]);
|
||||
if (!b)
|
||||
return JS_ThrowTypeError(ctx, "dot requires an Array argument");
|
||||
|
||||
if (a->size != b->size)
|
||||
return JS_ThrowTypeError(ctx, "Arrays must have the same size for dot product");
|
||||
|
||||
double result = cblas_ddot(a->size, a->data, 1, b->data, 1);
|
||||
return JS_NewFloat64(ctx, result);
|
||||
}
|
||||
|
||||
static JSValue js_array_norm(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
|
||||
array_t *arr = js2array(ctx, this_val);
|
||||
if (!arr)
|
||||
return JS_EXCEPTION;
|
||||
|
||||
double norm = cblas_dnrm2(arr->size, arr->data, 1);
|
||||
return JS_NewFloat64(ctx, norm);
|
||||
}
|
||||
|
||||
static JSValue js_array_multiply(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
|
||||
if (argc < 1)
|
||||
return JS_ThrowTypeError(ctx, "multiply requires an argument");
|
||||
|
||||
array_t *arr = js2array(ctx, this_val);
|
||||
if (!arr)
|
||||
return JS_EXCEPTION;
|
||||
|
||||
// Create result array
|
||||
array_t *result = js_malloc(ctx, sizeof(array_t));
|
||||
if (!result)
|
||||
return JS_EXCEPTION;
|
||||
|
||||
result->size = arr->size;
|
||||
result->data = js_malloc(ctx, sizeof(double) * result->size);
|
||||
if (!result->data) {
|
||||
js_free(ctx, result);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
// Copy original data
|
||||
memcpy(result->data, arr->data, sizeof(double) * arr->size);
|
||||
|
||||
// Check if multiplying by scalar
|
||||
if (JS_IsNumber(argv[0])) {
|
||||
double scalar;
|
||||
if (JS_ToFloat64(ctx, &scalar, argv[0]))
|
||||
return JS_EXCEPTION;
|
||||
|
||||
// Multiply each element by scalar
|
||||
if (result->size <= 4)
|
||||
for (int i = 0; i < result->size; i++)
|
||||
result->data[i] *= scalar;
|
||||
else
|
||||
cblas_dscal(result->size, scalar, result->data, 1);
|
||||
} else {
|
||||
js_free(ctx, result->data);
|
||||
js_free(ctx, result);
|
||||
return JS_ThrowTypeError(ctx, "multiply requires a number argument");
|
||||
}
|
||||
|
||||
JSValue obj = JS_NewObjectClass(ctx, js_array_class_id);
|
||||
JS_SetOpaque(obj, result);
|
||||
return obj;
|
||||
}
|
||||
|
||||
static JSValue js_array_add(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
|
||||
if (argc < 1)
|
||||
return JS_ThrowTypeError(ctx, "add requires an argument");
|
||||
|
||||
array_t *arr = js2array(ctx, this_val);
|
||||
if (!arr)
|
||||
return JS_EXCEPTION;
|
||||
|
||||
// Create result array
|
||||
array_t *result = js_malloc(ctx, sizeof(array_t));
|
||||
if (!result)
|
||||
return JS_EXCEPTION;
|
||||
|
||||
result->size = arr->size;
|
||||
result->data = js_malloc(ctx, sizeof(double) * result->size);
|
||||
if (!result->data) {
|
||||
js_free(ctx, result);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
// Copy original data
|
||||
memcpy(result->data, arr->data, sizeof(double) * arr->size);
|
||||
|
||||
// Check if adding scalar
|
||||
if (JS_IsNumber(argv[0])) {
|
||||
double scalar;
|
||||
if (JS_ToFloat64(ctx, &scalar, argv[0]))
|
||||
return JS_EXCEPTION;
|
||||
|
||||
// Add scalar to each element
|
||||
for (int i = 0; i < result->size; i++)
|
||||
result->data[i] += scalar;
|
||||
} else {
|
||||
// Check if adding another array
|
||||
array_t *other = js2array(ctx, argv[0]);
|
||||
if (other) {
|
||||
if (arr->size != other->size) {
|
||||
js_free(ctx, result->data);
|
||||
js_free(ctx, result);
|
||||
return JS_ThrowTypeError(ctx, "Arrays must have the same size for element-wise addition");
|
||||
}
|
||||
|
||||
if (arr->size <= 4) {
|
||||
for (int i = 0; i < arr->size; i++)
|
||||
result->data[i] += other->data[i];
|
||||
} else
|
||||
cblas_daxpy(result->size, 1.0, other->data, 1, result->data, 1);
|
||||
} else {
|
||||
js_free(ctx, result->data);
|
||||
js_free(ctx, result);
|
||||
return JS_ThrowTypeError(ctx, "add requires a number or Array argument");
|
||||
}
|
||||
}
|
||||
|
||||
JSValue obj = JS_NewObjectClass(ctx, js_array_class_id);
|
||||
JS_SetOpaque(obj, result);
|
||||
return obj;
|
||||
}
|
||||
|
||||
static JSValue js_array_slice(JSContext *js, JSValueConst self, int argc, JSValueConst *argv)
|
||||
{
|
||||
array_t *arr = js2array(js, self);
|
||||
if (!arr)
|
||||
return JS_EXCEPTION;
|
||||
|
||||
int32_t start = 0;
|
||||
int32_t end = arr->size;
|
||||
|
||||
// Parse start argument
|
||||
if (argc >= 1 && !JS_IsNull(argv[0])) {
|
||||
if (JS_ToInt32(js, &start, argv[0]))
|
||||
return JS_EXCEPTION;
|
||||
|
||||
// Handle negative indices
|
||||
if (start < 0) {
|
||||
start = arr->size + start;
|
||||
if (start < 0) start = 0;
|
||||
} else if (start > arr->size) {
|
||||
start = arr->size;
|
||||
}
|
||||
}
|
||||
|
||||
// Parse end argument
|
||||
if (argc >= 2 && !JS_IsNull(argv[1])) {
|
||||
if (JS_ToInt32(js, &end, argv[1]))
|
||||
return JS_EXCEPTION;
|
||||
|
||||
// Handle negative indices
|
||||
if (end < 0) {
|
||||
end = arr->size + end;
|
||||
if (end < 0) end = 0;
|
||||
} else if (end > arr->size) {
|
||||
end = arr->size;
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure start <= end
|
||||
if (start > end) {
|
||||
end = start; // Return empty array
|
||||
}
|
||||
|
||||
// Create JavaScript array
|
||||
JSValue jsarr = JS_NewArray(js);
|
||||
if (JS_IsException(jsarr))
|
||||
return jsarr;
|
||||
|
||||
// Fill with sliced values
|
||||
for (int32_t i = start; i < end; i++) {
|
||||
JS_SetPropertyUint32(js, jsarr, i - start, JS_NewFloat64(js, arr->data[i]));
|
||||
}
|
||||
|
||||
return jsarr;
|
||||
}
|
||||
|
||||
static JSValue js_array_multiplyf(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
|
||||
if (argc < 1)
|
||||
return JS_ThrowTypeError(ctx, "multiplyf requires an argument");
|
||||
|
||||
array_t *arr = js2array(ctx, this_val);
|
||||
if (!arr)
|
||||
return JS_EXCEPTION;
|
||||
|
||||
// Check if multiplying by scalar
|
||||
if (JS_IsNumber(argv[0])) {
|
||||
double scalar;
|
||||
if (JS_ToFloat64(ctx, &scalar, argv[0]))
|
||||
return JS_EXCEPTION;
|
||||
|
||||
// Multiply each element by scalar in-place
|
||||
if (arr->size <= 4) {
|
||||
for (int i = 0; i < arr->size; i++)
|
||||
arr->data[i] *= scalar;
|
||||
} else {
|
||||
cblas_dscal(arr->size, scalar, arr->data, 1);
|
||||
}
|
||||
|
||||
return JS_DupValue(ctx, this_val);
|
||||
} else {
|
||||
return JS_ThrowTypeError(ctx, "multiplyf requires a number argument");
|
||||
}
|
||||
}
|
||||
|
||||
static JSValue js_array_addf(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
|
||||
if (argc < 1)
|
||||
return JS_ThrowTypeError(ctx, "addf requires an argument");
|
||||
|
||||
array_t *arr = js2array(ctx, this_val);
|
||||
if (!arr)
|
||||
return JS_EXCEPTION;
|
||||
|
||||
// Check if adding scalar
|
||||
if (JS_IsNumber(argv[0])) {
|
||||
double scalar;
|
||||
if (JS_ToFloat64(ctx, &scalar, argv[0]))
|
||||
return JS_EXCEPTION;
|
||||
|
||||
// Add scalar to each element in-place
|
||||
for (int i = 0; i < arr->size; i++)
|
||||
arr->data[i] += scalar;
|
||||
} else {
|
||||
// Check if adding another array
|
||||
array_t *other = js2array(ctx, argv[0]);
|
||||
if (other) {
|
||||
if (arr->size != other->size) {
|
||||
return JS_ThrowTypeError(ctx, "Arrays must have the same size for element-wise addition");
|
||||
}
|
||||
|
||||
// Add arrays element-wise in-place
|
||||
if (arr->size <= 4) {
|
||||
for (int i = 0; i < arr->size; i++)
|
||||
arr->data[i] += other->data[i];
|
||||
} else {
|
||||
cblas_daxpy(arr->size, 1.0, other->data, 1, arr->data, 1);
|
||||
}
|
||||
} else {
|
||||
return JS_ThrowTypeError(ctx, "addf requires a number or Array argument");
|
||||
}
|
||||
}
|
||||
|
||||
return JS_DupValue(ctx, this_val);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static const JSCFunctionListEntry js_matrix_proto_funcs[] = {
|
||||
JS_CFUNC_DEF("inverse", 0, js_matrix_inverse),
|
||||
JS_CFUNC_DEF("multiply", 1, js_matrix_multiply),
|
||||
JS_CFUNC_DEF("toArray", 0, js_matrix_to_array),
|
||||
};
|
||||
|
||||
static const JSCFunctionListEntry js_array_proto_funcs[] = {
|
||||
JS_CFUNC_DEF("dot", 1, js_array_dot),
|
||||
JS_CFUNC_DEF("norm", 0, js_array_norm),
|
||||
JS_CFUNC_DEF("multiply", 1, js_array_multiply),
|
||||
JS_CFUNC_DEF("add", 1, js_array_add),
|
||||
JS_CFUNC_DEF("multiplyf", 1, js_array_multiplyf),
|
||||
JS_CFUNC_DEF("addf", 1, js_array_addf),
|
||||
JS_CFUNC_DEF("toArray", 0, js_array_to_array),
|
||||
JS_CFUNC_DEF("toJSON", 0, js_array_to_array), /* ← for JSON.stringify */
|
||||
JS_CFUNC_DEF("slice", 2, js_array_slice),
|
||||
};
|
||||
|
||||
JSValue js_num_use(JSContext *ctx) {
|
||||
// Register Matrix class
|
||||
JS_NewClassID(&js_matrix_class_id);
|
||||
JS_NewClass(JS_GetRuntime(ctx), js_matrix_class_id, &js_matrix_class);
|
||||
JSValue matrix_proto = JS_NewObject(ctx);
|
||||
JS_SetPropertyFunctionList(ctx, matrix_proto, js_matrix_proto_funcs,
|
||||
sizeof(js_matrix_proto_funcs) / sizeof(JSCFunctionListEntry));
|
||||
JS_SetClassProto(ctx, js_matrix_class_id, matrix_proto);
|
||||
|
||||
// Create Matrix constructor
|
||||
JSValue matrix_ctor = JS_NewCFunction2(ctx, js_matrix_constructor, "Matrix", 1, JS_CFUNC_constructor, 0);
|
||||
JS_SetConstructor(ctx, matrix_ctor, matrix_proto);
|
||||
|
||||
// Register Array class
|
||||
JS_NewClassID(&js_array_class_id);
|
||||
JS_NewClass(JS_GetRuntime(ctx), js_array_class_id, &js_array_class);
|
||||
JSValue array_proto = JS_NewObject(ctx);
|
||||
JS_SetPropertyFunctionList(ctx, array_proto, js_array_proto_funcs,
|
||||
sizeof(js_array_proto_funcs) / sizeof(JSCFunctionListEntry));
|
||||
JS_SetClassProto(ctx, js_array_class_id, array_proto);
|
||||
|
||||
// Create Array constructor
|
||||
JSValue array_ctor = JS_NewCFunction2(ctx, js_array_constructor, "Array", 1, JS_CFUNC_constructor, 0);
|
||||
JS_SetConstructor(ctx, array_ctor, array_proto);
|
||||
|
||||
JSValue export = JS_NewObject(ctx);
|
||||
JS_SetPropertyStr(ctx, export, "Matrix", matrix_ctor);
|
||||
JS_SetPropertyStr(ctx, export, "Array", array_ctor);
|
||||
return export;
|
||||
}
|
||||
|
||||
303
source/qjs_os.c
303
source/qjs_os.c
@@ -1,303 +0,0 @@
|
||||
#include "cell.h"
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
#include <signal.h>
|
||||
#include <time.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <sys/utsname.h>
|
||||
#ifdef __linux__
|
||||
#include <sys/sysinfo.h>
|
||||
#include <sys/resource.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
JSC_CCALL(os_now, return number2js(js, (double)SDL_GetTicksNS()/1000000000.0))
|
||||
|
||||
JSC_CCALL(os_sleep,
|
||||
double secs = js2number(js,argv[0]);
|
||||
int ms = secs*1000;
|
||||
SDL_Delay(ms);
|
||||
)
|
||||
|
||||
JSC_CCALL(os_power_state,
|
||||
int pct, secs;
|
||||
SDL_PowerState state = SDL_GetPowerInfo(&secs, &pct);
|
||||
const char *statestr = "unknown";
|
||||
switch(state) {
|
||||
case SDL_POWERSTATE_ON_BATTERY: statestr = "battery"; break;
|
||||
case SDL_POWERSTATE_NO_BATTERY: statestr = "no battery"; break;
|
||||
case SDL_POWERSTATE_CHARGING: statestr = "charging"; break;
|
||||
case SDL_POWERSTATE_CHARGED: statestr = "charged"; break;
|
||||
}
|
||||
ret = JS_NewObject(js);
|
||||
JS_SetPropertyStr(js,ret,"state",JS_NewString(js,statestr));
|
||||
JS_SetPropertyStr(js,ret,"percent",number2js(js,pct));
|
||||
JS_SetPropertyStr(js,ret,"seconds",number2js(js,secs));
|
||||
)
|
||||
|
||||
JSC_CCALL(os_totalmem, return number2js(js, SDL_GetSystemRAM()))
|
||||
JSC_CCALL(os_platform, return JS_NewString(js,SDL_GetPlatform()))
|
||||
|
||||
static JSValue js_os_hostname(JSContext *js, JSValue self, int argc, JSValue *argv) {
|
||||
JSValue ret = JS_NULL;
|
||||
#ifdef _WIN32
|
||||
TCHAR buffer[256] = TEXT("");
|
||||
DWORD size = sizeof(buffer) / sizeof(TCHAR);
|
||||
GetComputerName(buffer, &size);
|
||||
return JS_NewString(js, buffer);
|
||||
#else
|
||||
struct utsname buffer;
|
||||
if (uname(&buffer) != 0) return JS_ThrowReferenceError(js,"Could not get hostname.");
|
||||
return JS_NewString(js, buffer.nodename);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
static JSValue js_os_arch(JSContext *js, JSValue self, int argc, JSValue *argv) {
|
||||
JSValue ret = JS_NULL;
|
||||
#if defined(__x86_64__) || defined(_M_X64)
|
||||
return JS_NewString(js,"x64");
|
||||
#elif defined(__aarch64__) || defined(_M_ARM64)
|
||||
return JS_NewString(js,"arm64");
|
||||
#elif defined(__arm__) || defined(_M_ARM)
|
||||
return JS_NewString(js,"arm");
|
||||
#elif defined(__i386__) || defined(_M_IX86)
|
||||
return JS_NewString(js,"ia32");
|
||||
#elif defined(__loongarch__) || defined(__loongarch32) || defined(__loongarch64)
|
||||
return JS_NewString(js,"loong64");
|
||||
#elif defined(__mips__) || defined(__mips) || defined(_M_MIPS)
|
||||
// You might want to distinguish mips vs mipsel
|
||||
return JS_NewString(js,"mips");
|
||||
#elif defined(__ppc64__) || defined(__powerpc64__) || defined(_M_PPC)
|
||||
// You might want to distinguish ppc vs ppc64, big-endian vs little-endian
|
||||
return JS_NewString(js,"ppc64");
|
||||
#elif defined(__riscv) && __riscv_xlen == 64
|
||||
return JS_NewString(js,"riscv64");
|
||||
#elif defined(__s390x__)
|
||||
return JS_NewString(js,"s390x");
|
||||
#else
|
||||
return JS_NewString(js,"unknown");
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
static JSValue js_os_freemem(JSContext *js, JSValue self, int argc, JSValue *argv) {
|
||||
JSValue ret = JS_NULL;
|
||||
#ifdef _WIN32
|
||||
MEMORYSTATUSEX statex;
|
||||
statex.dwLength = sizeof(statex);
|
||||
if (!GlobalMemoryStatusEx(&statex)) return JS_ThrowInternalError(js,"GlobalMemoryStatusEx failed");
|
||||
return JS_NewInt64(js,(int64_t)statex.ullAvailPhys);
|
||||
#elif defined(__linux__)
|
||||
struct sysinfo info;
|
||||
if (sysinfo(&info) == 0)
|
||||
return JS_NewInt64(js,(int64_t)info.freeram * info.mem_unit);
|
||||
return JS_NewInt64(js,0);
|
||||
#elif defined(__FreeBSD__) || defined(__OpenBSD__)
|
||||
// A very rough fallback using the same sysconf approach
|
||||
// (macOS or *BSD typically need specialized APIs to get free mem accurately)
|
||||
// This is often only "unused" pages, ignoring caches, etc.
|
||||
long pages = sysconf(_SC_AVPHYS_PAGES);
|
||||
long page_size = sysconf(_SC_PAGE_SIZE);
|
||||
if (pages < 0 || page_size < 0) return JS_NewInt64(js,0);
|
||||
return JS_NewInt64(js,(int64_t)pages * (int64_t)page_size);
|
||||
#else
|
||||
// Fallback: unknown
|
||||
return JS_NewInt64(js,0);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
static JSValue js_os_version(JSContext *js, JSValue self, int argc, JSValue *argv) {
|
||||
JSValue ret = JS_NULL;
|
||||
#ifdef _WIN32
|
||||
typedef LONG (WINAPI *RtlGetVersionPtr)(PRTL_OSVERSIONINFOW);
|
||||
HMODULE h = GetModuleHandleA("ntdll.dll");
|
||||
if (h) {
|
||||
RtlGetVersionPtr fx = (RtlGetVersionPtr)GetProcAddress(h, "RtlGetVersion");
|
||||
if (fx) {
|
||||
RTL_OSVERSIONINFOW ver;
|
||||
memset(&ver, 0, sizeof(ver));
|
||||
ver.dwOSVersionInfoSize = sizeof(ver);
|
||||
if (!fx(&ver)) {
|
||||
char buf[128];
|
||||
sprintf(buf, "%u.%u.%u",
|
||||
(unsigned)ver.dwMajorVersion,
|
||||
(unsigned)ver.dwMinorVersion,
|
||||
(unsigned)ver.dwBuildNumber
|
||||
);
|
||||
return JS_NewString(js, buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
OSVERSIONINFOW wver;
|
||||
memset(&wver, 0, sizeof(wver));
|
||||
wver.dwOSVersionInfoSize = sizeof(wver);
|
||||
if (GetVersionExW(&wver)) {
|
||||
char buf[128];
|
||||
sprintf(buf, "%u.%u.%u",
|
||||
(unsigned)wver.dwMajorVersion,
|
||||
(unsigned)wver.dwMinorVersion,
|
||||
(unsigned)wver.dwBuildNumber
|
||||
);
|
||||
return JS_NewString(js, buf);
|
||||
}
|
||||
return JS_NewString(js, "Windows_Unknown");
|
||||
#else
|
||||
struct utsname info;
|
||||
if (!uname(&info)) return JS_NewString(js, info.release);
|
||||
return JS_NewString(js, "");
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
JSC_CCALL(os_buffer2string,
|
||||
if (argc < 1) {
|
||||
return JS_ThrowTypeError(js, "buffer2string expects an ArrayBuffer");
|
||||
}
|
||||
|
||||
size_t len;
|
||||
uint8_t *buf = js_get_blob_data(js, &len, argv[0]);
|
||||
if (!buf) {
|
||||
return JS_ThrowTypeError(js, "First argument must be an ArrayBuffer");
|
||||
}
|
||||
|
||||
// Create a null-terminated string from the buffer
|
||||
char *str = js_malloc(js, len + 1);
|
||||
if (!str) {
|
||||
return JS_ThrowInternalError(js, "Failed to allocate memory");
|
||||
}
|
||||
|
||||
memcpy(str, buf, len);
|
||||
str[len] = '\0';
|
||||
|
||||
JSValue result = JS_NewString(js, str);
|
||||
js_free(js, str);
|
||||
|
||||
return result;
|
||||
)
|
||||
|
||||
#define JSOBJ_ADD_FIELD(OBJ, STRUCT, FIELD, TYPE) \
|
||||
JS_SetPropertyStr(js, OBJ, #FIELD, TYPE##2js(js,STRUCT.FIELD));\
|
||||
|
||||
#define JSJMEMRET(FIELD) JSOBJ_ADD_FIELD(ret, jsmem, FIELD, number)
|
||||
|
||||
JSC_CCALL(os_mallinfo,
|
||||
ret = JS_NULL;
|
||||
/*struct mallinfo jsmem = mallinfo();
|
||||
ret = JS_NewObject(js);
|
||||
JSJMEMRET(arena);
|
||||
JSJMEMRET(ordblks);
|
||||
JSJMEMRET(smblks);
|
||||
JSJMEMRET(hblks);
|
||||
JSJMEMRET(hblkhd);
|
||||
JSJMEMRET(usmblks);
|
||||
JSJMEMRET(fsmblks);
|
||||
JSJMEMRET(uordblks);
|
||||
JSJMEMRET(fordblks);
|
||||
JSJMEMRET(keepcost);*/
|
||||
)
|
||||
|
||||
static JSValue js_os_rusage(JSContext *js, JSValue self, int argc, JSValue *argv) {
|
||||
JSValue ret = JS_NULL;
|
||||
ret = JS_NewObject(js);
|
||||
|
||||
#if defined(__linux__) || defined(__APPLE__)
|
||||
struct rusage jsmem;
|
||||
getrusage(RUSAGE_SELF, &jsmem);
|
||||
JSJMEMRET(ru_maxrss);
|
||||
JSJMEMRET(ru_ixrss);
|
||||
JSJMEMRET(ru_idrss);
|
||||
JSJMEMRET(ru_isrss);
|
||||
JSJMEMRET(ru_minflt);
|
||||
JSJMEMRET(ru_majflt);
|
||||
JSJMEMRET(ru_nswap);
|
||||
JSJMEMRET(ru_inblock);
|
||||
JSJMEMRET(ru_oublock);
|
||||
JSJMEMRET(ru_msgsnd);
|
||||
JSJMEMRET(ru_msgrcv);
|
||||
JSJMEMRET(ru_nsignals);
|
||||
JSJMEMRET(ru_nvcsw);
|
||||
JSJMEMRET(ru_nivcsw);
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef TRACY_ENABLE
|
||||
#include <tracy/TracyC.h>
|
||||
JSC_CCALL(os_frame,
|
||||
if (!tracy_profiling_enabled) return JS_NULL;
|
||||
TracyCFrameMark
|
||||
)
|
||||
|
||||
JSC_CCALL(os_trace_img,
|
||||
if (!tracy_profiling_enabled) return JS_NULL;
|
||||
size_t len;
|
||||
double width, height;
|
||||
JS_ToFloat64(js,&width,argv[1]);
|
||||
JS_ToFloat64(js,&height,argv[2]);
|
||||
___tracy_emit_frame_image(js_get_blob_data(js, &len, argv[0]), width, height, 0, 0);
|
||||
)
|
||||
|
||||
JSC_CCALL(os_trace_message,
|
||||
if (!tracy_profiling_enabled) return JS_NULL;
|
||||
size_t len;
|
||||
const char *str = JS_ToCStringLen(js, &len, argv[0]);
|
||||
TracyCMessage(str,len);
|
||||
JS_FreeCString(js,str);
|
||||
)
|
||||
#else
|
||||
JSC_CCALL(os_frame,
|
||||
)
|
||||
|
||||
JSC_CCALL(os_trace_img,
|
||||
)
|
||||
|
||||
JSC_CCALL(os_trace_message,
|
||||
)
|
||||
#endif
|
||||
|
||||
JSC_SCALL(os_system,
|
||||
int err = system(str);
|
||||
ret = number2js(js,err);
|
||||
)
|
||||
|
||||
JSC_SCALL(os_exit,
|
||||
printf("exit\n");
|
||||
exit(0);
|
||||
)
|
||||
|
||||
static const JSCFunctionListEntry js_os_funcs[] = {
|
||||
MIST_FUNC_DEF(os, platform, 0),
|
||||
MIST_FUNC_DEF(os, arch, 0),
|
||||
MIST_FUNC_DEF(os, totalmem, 0),
|
||||
MIST_FUNC_DEF(os, freemem, 0),
|
||||
MIST_FUNC_DEF(os, hostname, 0),
|
||||
MIST_FUNC_DEF(os, version, 0),
|
||||
MIST_FUNC_DEF(os, now, 0),
|
||||
MIST_FUNC_DEF(os, power_state, 0),
|
||||
MIST_FUNC_DEF(os, rusage, 0),
|
||||
MIST_FUNC_DEF(os, mallinfo, 0),
|
||||
MIST_FUNC_DEF(os, buffer2string, 1),
|
||||
MIST_FUNC_DEF(os, frame, 0),
|
||||
MIST_FUNC_DEF(os, system, 1),
|
||||
MIST_FUNC_DEF(os, exit, 0),
|
||||
};
|
||||
|
||||
JSValue js_os_use(JSContext *js) {
|
||||
JSValue mod = JS_NewObject(js);
|
||||
JS_SetPropertyFunctionList(js,mod,js_os_funcs,countof(js_os_funcs));
|
||||
return mod;
|
||||
}
|
||||
477
source/qjs_qop.c
477
source/qjs_qop.c
@@ -1,477 +0,0 @@
|
||||
#include "qop.h"
|
||||
#include "cell.h"
|
||||
|
||||
static JSClassID js_qop_archive_class_id;
|
||||
|
||||
static void js_qop_archive_finalizer(JSRuntime *rt, JSValue val) {
|
||||
qop_desc *qop = JS_GetOpaque(val, js_qop_archive_class_id);
|
||||
if (qop) {
|
||||
if (qop->hashmap) {
|
||||
js_free_rt(rt, qop->hashmap);
|
||||
}
|
||||
qop_close(qop);
|
||||
js_free_rt(rt, qop);
|
||||
}
|
||||
}
|
||||
|
||||
static JSClassDef js_qop_archive_class = {
|
||||
"qop archive",
|
||||
.finalizer = js_qop_archive_finalizer,
|
||||
};
|
||||
|
||||
static JSClassID js_qop_writer_class_id;
|
||||
|
||||
typedef struct {
|
||||
FILE *fh;
|
||||
qop_file *files;
|
||||
int len;
|
||||
int capacity;
|
||||
unsigned int size;
|
||||
} qop_writer;
|
||||
|
||||
static void js_qop_writer_finalizer(JSRuntime *rt, JSValue val) {
|
||||
qop_writer *w = JS_GetOpaque(val, js_qop_writer_class_id);
|
||||
if (w) {
|
||||
if (w->fh) fclose(w->fh);
|
||||
if (w->files) js_free_rt(rt, w->files);
|
||||
js_free_rt(rt, w);
|
||||
}
|
||||
}
|
||||
|
||||
static JSClassDef js_qop_writer_class = {
|
||||
"qop writer",
|
||||
.finalizer = js_qop_writer_finalizer,
|
||||
};
|
||||
|
||||
static qop_writer *js2writer(JSContext *js, JSValue v) {
|
||||
return JS_GetOpaque(v, js_qop_writer_class_id);
|
||||
}
|
||||
|
||||
// Helper functions for writer
|
||||
static void write_16(unsigned int v, FILE *fh) {
|
||||
unsigned char b[2];
|
||||
b[0] = 0xff & (v);
|
||||
b[1] = 0xff & (v >> 8);
|
||||
fwrite(b, 2, 1, fh);
|
||||
}
|
||||
|
||||
static void write_32(unsigned int v, FILE *fh) {
|
||||
unsigned char b[4];
|
||||
b[0] = 0xff & (v);
|
||||
b[1] = 0xff & (v >> 8);
|
||||
b[2] = 0xff & (v >> 16);
|
||||
b[3] = 0xff & (v >> 24);
|
||||
fwrite(b, 4, 1, fh);
|
||||
}
|
||||
|
||||
static void write_64(unsigned long long v, FILE *fh) {
|
||||
unsigned char b[8];
|
||||
b[0] = 0xff & (v);
|
||||
b[1] = 0xff & (v >> 8);
|
||||
b[2] = 0xff & (v >> 16);
|
||||
b[3] = 0xff & (v >> 24);
|
||||
b[4] = 0xff & (v >> 32);
|
||||
b[5] = 0xff & (v >> 40);
|
||||
b[6] = 0xff & (v >> 48);
|
||||
b[7] = 0xff & (v >> 56);
|
||||
fwrite(b, 8, 1, fh);
|
||||
}
|
||||
|
||||
static unsigned long long qop_hash(const char *key) {
|
||||
unsigned long long h = 525201411107845655ull;
|
||||
for (;*key;++key) {
|
||||
h ^= (unsigned char)*key;
|
||||
h *= 0x5bd1e9955bd1e995ull;
|
||||
h ^= h >> 47;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
static qop_desc *js2qop(JSContext *js, JSValue v) {
|
||||
return JS_GetOpaque(v, js_qop_archive_class_id);
|
||||
}
|
||||
|
||||
static int js_qop_ensure_index(JSContext *js, qop_desc *qop) {
|
||||
if (qop->hashmap != NULL) return 1;
|
||||
void *buffer = js_malloc(js, qop->hashmap_size);
|
||||
if (!buffer) return 0;
|
||||
int num = qop_read_index(qop, buffer);
|
||||
if (num == 0) {
|
||||
js_free(js, buffer);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
JSC_SCALL(qop_open,
|
||||
size_t len;
|
||||
void *data = js_get_blob_data(js, &len, argv[0]);
|
||||
if (!data)
|
||||
ret = JS_ThrowReferenceError(js, "Could not get blob data.\n");
|
||||
else {
|
||||
qop_desc *qop = js_malloc(js, sizeof(qop_desc));
|
||||
if (!qop)
|
||||
ret = JS_ThrowOutOfMemory(js);
|
||||
else {
|
||||
int size = qop_open_data((const unsigned char *)data, len, qop);
|
||||
if (size == 0) {
|
||||
js_free(js, qop);
|
||||
ret = JS_ThrowReferenceError(js, "Failed to open QOP archive from blob");
|
||||
} else {
|
||||
JSValue obj = JS_NewObjectClass(js, js_qop_archive_class_id);
|
||||
JS_SetOpaque(obj, qop);
|
||||
ret = obj;
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
JSC_SCALL(qop_write,
|
||||
const char *path = JS_ToCString(js, argv[0]);
|
||||
if (!path) return JS_EXCEPTION;
|
||||
|
||||
FILE *fh = fopen(path, "wb");
|
||||
JS_FreeCString(js, path);
|
||||
if (!fh) return JS_ThrowInternalError(js, "Could not open file for writing");
|
||||
|
||||
qop_writer *w = js_malloc(js, sizeof(qop_writer));
|
||||
if (!w) {
|
||||
fclose(fh);
|
||||
return JS_ThrowOutOfMemory(js);
|
||||
}
|
||||
|
||||
w->fh = fh;
|
||||
w->capacity = 1024;
|
||||
w->len = 0;
|
||||
w->size = 0;
|
||||
w->files = js_malloc(js, sizeof(qop_file) * w->capacity);
|
||||
if (!w->files) {
|
||||
fclose(fh);
|
||||
js_free(js, w);
|
||||
return JS_ThrowOutOfMemory(js);
|
||||
}
|
||||
|
||||
JSValue obj = JS_NewObjectClass(js, js_qop_writer_class_id);
|
||||
JS_SetOpaque(obj, w);
|
||||
ret = obj;
|
||||
)
|
||||
|
||||
|
||||
static JSValue js_qop_close(JSContext *js, JSValue self, int argc, JSValue *argv) {
|
||||
qop_desc *qop = js2qop(js, self);
|
||||
if (!qop)
|
||||
return JS_ThrowInternalError(js, "Invalid QOP archive");
|
||||
|
||||
qop_close(qop);
|
||||
JS_SetOpaque(self, NULL); // Prevent double free
|
||||
return JS_NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static JSValue js_qop_read(JSContext *js, JSValue self, int argc, JSValue *argv) {
|
||||
qop_desc *qop = js2qop(js, self);
|
||||
if (!qop)
|
||||
return JS_ThrowInternalError(js, "Invalid QOP archive");
|
||||
|
||||
const char *path = JS_ToCString(js, argv[0]);
|
||||
if (!path)
|
||||
return JS_EXCEPTION;
|
||||
|
||||
if (!js_qop_ensure_index(js, qop)) {
|
||||
JS_FreeCString(js, path);
|
||||
return JS_ThrowReferenceError(js, "Failed to read QOP index");
|
||||
}
|
||||
|
||||
qop_file *file = qop_find(qop, path);
|
||||
JS_FreeCString(js, path);
|
||||
|
||||
if (!file)
|
||||
return JS_NULL;
|
||||
|
||||
unsigned char *dest = js_malloc(js, file->size);
|
||||
if (!dest)
|
||||
return JS_ThrowOutOfMemory(js);
|
||||
|
||||
int bytes = qop_read(qop, file, dest);
|
||||
if (bytes == 0) {
|
||||
js_free(js, dest);
|
||||
return JS_ThrowReferenceError(js, "Failed to read file");
|
||||
}
|
||||
|
||||
JSValue blob = js_new_blob_stoned_copy(js, dest, bytes);
|
||||
js_free(js, dest);
|
||||
return blob;
|
||||
}
|
||||
|
||||
static JSValue js_qop_read_ex(JSContext *js, JSValue self, int argc, JSValue *argv) {
|
||||
qop_desc *qop = js2qop(js, self);
|
||||
if (!qop)
|
||||
return JS_ThrowInternalError(js, "Invalid QOP archive");
|
||||
|
||||
const char *path = JS_ToCString(js, argv[0]);
|
||||
if (!path)
|
||||
return JS_EXCEPTION;
|
||||
|
||||
if (!js_qop_ensure_index(js, qop)) {
|
||||
JS_FreeCString(js, path);
|
||||
return JS_ThrowReferenceError(js, "Failed to read QOP index");
|
||||
}
|
||||
|
||||
qop_file *file = qop_find(qop, path);
|
||||
JS_FreeCString(js, path);
|
||||
|
||||
if (!file)
|
||||
return JS_NULL;
|
||||
|
||||
unsigned int start;
|
||||
unsigned int len;
|
||||
if (JS_ToUint32(js, &start, argv[1]) < 0 || JS_ToUint32(js, &len, argv[2]) < 0)
|
||||
return JS_ThrowTypeError(js, "Invalid start or len");
|
||||
|
||||
unsigned char *dest = js_malloc(js, len);
|
||||
if (!dest)
|
||||
return JS_ThrowOutOfMemory(js);
|
||||
|
||||
int bytes = qop_read_ex(qop, file, dest, start, len);
|
||||
if (bytes == 0) {
|
||||
js_free(js, dest);
|
||||
return JS_ThrowReferenceError(js, "Failed to read file part");
|
||||
}
|
||||
|
||||
JSValue blob = js_new_blob_stoned_copy(js, dest, bytes);
|
||||
js_free(js, dest);
|
||||
return blob;
|
||||
}
|
||||
|
||||
static JSValue js_qop_list(JSContext *js, JSValue self, int argc, JSValue *argv) {
|
||||
qop_desc *qop = js2qop(js, self);
|
||||
if (!qop)
|
||||
return JS_ThrowInternalError(js, "Invalid QOP archive");
|
||||
|
||||
if (!js_qop_ensure_index(js, qop)) {
|
||||
return JS_ThrowReferenceError(js, "Failed to read QOP index");
|
||||
}
|
||||
|
||||
JSValue arr = JS_NewArray(js);
|
||||
int count = 0;
|
||||
|
||||
for (unsigned int i = 0; i < qop->hashmap_len; i++) {
|
||||
qop_file *file = &qop->hashmap[i];
|
||||
if (file->size == 0) continue; // empty slot
|
||||
|
||||
char *path = js_malloc(js, file->path_len);
|
||||
if (!path) {
|
||||
return JS_ThrowOutOfMemory(js);
|
||||
}
|
||||
|
||||
int len = qop_read_path(qop, file, path);
|
||||
if (len == 0) {
|
||||
js_free(js, path);
|
||||
continue; // skip on error
|
||||
}
|
||||
|
||||
JSValue str = JS_NewStringLen(js, path, len - 1); // -1 for null terminator
|
||||
js_free(js, path);
|
||||
JS_SetPropertyUint32(js, arr, count++, str);
|
||||
}
|
||||
|
||||
return arr;
|
||||
}
|
||||
|
||||
static JSValue js_qop_stat(JSContext *js, JSValue self, int argc, JSValue *argv) {
|
||||
qop_desc *qop = js2qop(js, self);
|
||||
if (!qop) return JS_ThrowInternalError(js, "Invalid QOP archive");
|
||||
|
||||
const char *path = JS_ToCString(js, argv[0]);
|
||||
if (!path) return JS_EXCEPTION;
|
||||
|
||||
if (!js_qop_ensure_index(js, qop)) {
|
||||
JS_FreeCString(js, path);
|
||||
return JS_ThrowReferenceError(js, "Failed to read QOP index");
|
||||
}
|
||||
|
||||
qop_file *file = qop_find(qop, path);
|
||||
JS_FreeCString(js, path);
|
||||
|
||||
if (!file) return JS_NULL;
|
||||
|
||||
JSValue obj = JS_NewObject(js);
|
||||
JS_SetPropertyStr(js, obj, "size", JS_NewInt64(js, file->size));
|
||||
JS_SetPropertyStr(js, obj, "modtime", JS_NewInt64(js, 0));
|
||||
JS_SetPropertyStr(js, obj, "isDirectory", JS_NewBool(js, 0));
|
||||
return obj;
|
||||
}
|
||||
|
||||
static JSValue js_qop_is_directory(JSContext *js, JSValue self, int argc, JSValue *argv) {
|
||||
qop_desc *qop = js2qop(js, self);
|
||||
if (!qop) return JS_ThrowInternalError(js, "Invalid QOP archive");
|
||||
|
||||
const char *path = JS_ToCString(js, argv[0]);
|
||||
if (!path) return JS_EXCEPTION;
|
||||
|
||||
if (!js_qop_ensure_index(js, qop)) {
|
||||
JS_FreeCString(js, path);
|
||||
return JS_ThrowReferenceError(js, "Failed to read QOP index");
|
||||
}
|
||||
|
||||
// Check if any file starts with path + "/"
|
||||
size_t path_len = strlen(path);
|
||||
char *prefix = js_malloc(js, path_len + 2);
|
||||
memcpy(prefix, path, path_len);
|
||||
prefix[path_len] = '/';
|
||||
prefix[path_len + 1] = '\0';
|
||||
|
||||
int found = 0;
|
||||
|
||||
// This is inefficient but simple. QOP doesn't have a directory structure.
|
||||
// We iterate all files to check prefixes.
|
||||
for (unsigned int i = 0; i < qop->hashmap_len; i++) {
|
||||
qop_file *file = &qop->hashmap[i];
|
||||
if (file->size == 0) continue;
|
||||
|
||||
// We need to read the path to check it
|
||||
// Optimization: check if we can read just the prefix?
|
||||
// qop_read_path reads the whole path.
|
||||
|
||||
// Let's read the path.
|
||||
char file_path[1024]; // MAX_PATH_LEN
|
||||
if (file->path_len > 1024) continue; // Should not happen based on spec
|
||||
|
||||
qop_read_path(qop, file, file_path);
|
||||
if (strncmp(file_path, prefix, path_len + 1) == 0) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
js_free(js, prefix);
|
||||
JS_FreeCString(js, path);
|
||||
return JS_NewBool(js, found);
|
||||
}
|
||||
|
||||
// Writer methods
|
||||
|
||||
static JSValue js_writer_add_file(JSContext *js, JSValue self, int argc, JSValue *argv) {
|
||||
qop_writer *w = js2writer(js, self);
|
||||
if (!w) return JS_ThrowInternalError(js, "Invalid QOP writer");
|
||||
|
||||
const char *path = JS_ToCString(js, argv[0]);
|
||||
if (!path) return JS_EXCEPTION;
|
||||
|
||||
size_t data_len;
|
||||
void *data = js_get_blob_data(js, &data_len, argv[1]);
|
||||
if (!data) {
|
||||
JS_FreeCString(js, path);
|
||||
return JS_ThrowTypeError(js, "Second argument must be a blob");
|
||||
}
|
||||
|
||||
if (w->len >= w->capacity) {
|
||||
w->capacity *= 2;
|
||||
qop_file *new_files = js_realloc(js, w->files, sizeof(qop_file) * w->capacity);
|
||||
if (!new_files) {
|
||||
JS_FreeCString(js, path);
|
||||
return JS_ThrowOutOfMemory(js);
|
||||
}
|
||||
w->files = new_files;
|
||||
}
|
||||
|
||||
// Strip leading "./"
|
||||
const char *archive_path = path;
|
||||
if (path[0] == '.' && path[1] == '/') {
|
||||
archive_path = path + 2;
|
||||
}
|
||||
|
||||
unsigned long long hash = qop_hash(archive_path);
|
||||
int path_len = strlen(archive_path) + 1;
|
||||
|
||||
// Write path
|
||||
fwrite(archive_path, 1, path_len, w->fh);
|
||||
|
||||
// Write data
|
||||
fwrite(data, 1, data_len, w->fh);
|
||||
|
||||
w->files[w->len] = (qop_file){
|
||||
.hash = hash,
|
||||
.offset = w->size,
|
||||
.size = (unsigned int)data_len,
|
||||
.path_len = (unsigned short)path_len,
|
||||
.flags = QOP_FLAG_NONE
|
||||
};
|
||||
|
||||
w->size += data_len + path_len;
|
||||
w->len++;
|
||||
|
||||
JS_FreeCString(js, path);
|
||||
return JS_NULL;
|
||||
}
|
||||
|
||||
static JSValue js_writer_finalize(JSContext *js, JSValue self, int argc, JSValue *argv) {
|
||||
qop_writer *w = js2writer(js, self);
|
||||
if (!w || !w->fh) return JS_ThrowInternalError(js, "Invalid QOP writer or already closed");
|
||||
|
||||
unsigned int total_size = w->size + 12; // Header size
|
||||
|
||||
for (int i = 0; i < w->len; i++) {
|
||||
write_64(w->files[i].hash, w->fh);
|
||||
write_32(w->files[i].offset, w->fh);
|
||||
write_32(w->files[i].size, w->fh);
|
||||
write_16(w->files[i].path_len, w->fh);
|
||||
write_16(w->files[i].flags, w->fh);
|
||||
total_size += 20;
|
||||
}
|
||||
|
||||
// Magic "qopf"
|
||||
unsigned int magic = (((unsigned int)'q') << 0 | ((unsigned int)'o') << 8 |
|
||||
((unsigned int)'p') << 16 | ((unsigned int)'f') << 24);
|
||||
|
||||
write_32(w->len, w->fh);
|
||||
write_32(total_size, w->fh);
|
||||
write_32(magic, w->fh);
|
||||
|
||||
fclose(w->fh);
|
||||
w->fh = NULL;
|
||||
|
||||
return JS_NULL;
|
||||
}
|
||||
|
||||
static const JSCFunctionListEntry js_qop_archive_funcs[] = {
|
||||
JS_CFUNC_DEF("close", 0, js_qop_close),
|
||||
JS_CFUNC_DEF("list", 0, js_qop_list),
|
||||
JS_CFUNC_DEF("read", 1, js_qop_read),
|
||||
JS_CFUNC_DEF("read_ex", 3, js_qop_read_ex),
|
||||
JS_CFUNC_DEF("stat", 1, js_qop_stat),
|
||||
JS_CFUNC_DEF("is_directory", 1, js_qop_is_directory),
|
||||
};
|
||||
|
||||
static const JSCFunctionListEntry js_qop_writer_funcs[] = {
|
||||
JS_CFUNC_DEF("add_file", 2, js_writer_add_file),
|
||||
JS_CFUNC_DEF("finalize", 0, js_writer_finalize),
|
||||
};
|
||||
|
||||
static const JSCFunctionListEntry js_qop_funcs[] = {
|
||||
MIST_FUNC_DEF(qop, open, 1),
|
||||
MIST_FUNC_DEF(qop, write, 1),
|
||||
JS_PROP_INT32_DEF("FLAG_NONE", QOP_FLAG_NONE, JS_PROP_ENUMERABLE),
|
||||
JS_PROP_INT32_DEF("FLAG_COMPRESSED_ZSTD", QOP_FLAG_COMPRESSED_ZSTD, JS_PROP_ENUMERABLE),
|
||||
JS_PROP_INT32_DEF("FLAG_COMPRESSED_DEFLATE", QOP_FLAG_COMPRESSED_DEFLATE, JS_PROP_ENUMERABLE),
|
||||
JS_PROP_INT32_DEF("FLAG_ENCRYPTED", QOP_FLAG_ENCRYPTED, JS_PROP_ENUMERABLE),
|
||||
};
|
||||
|
||||
JSValue js_qop_use(JSContext *js) {
|
||||
JS_NewClassID(&js_qop_archive_class_id);
|
||||
JS_NewClass(JS_GetRuntime(js), js_qop_archive_class_id, &js_qop_archive_class);
|
||||
JSValue archive_proto = JS_NewObject(js);
|
||||
JS_SetPropertyFunctionList(js, archive_proto, js_qop_archive_funcs, countof(js_qop_archive_funcs));
|
||||
JS_SetClassProto(js, js_qop_archive_class_id, archive_proto);
|
||||
|
||||
JS_NewClassID(&js_qop_writer_class_id);
|
||||
JS_NewClass(JS_GetRuntime(js), js_qop_writer_class_id, &js_qop_writer_class);
|
||||
JSValue writer_proto = JS_NewObject(js);
|
||||
JS_SetPropertyFunctionList(js, writer_proto, js_qop_writer_funcs, countof(js_qop_writer_funcs));
|
||||
JS_SetClassProto(js, js_qop_writer_class_id, writer_proto);
|
||||
|
||||
JSValue mod = JS_NewObject(js);
|
||||
JS_SetPropertyFunctionList(js, mod, js_qop_funcs, countof(js_qop_funcs));
|
||||
return mod;
|
||||
}
|
||||
@@ -1,608 +0,0 @@
|
||||
#include "cell.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <windows.h>
|
||||
#pragma comment(lib, "ws2_32.lib")
|
||||
#define close closesocket
|
||||
#define SHUT_RD SD_RECEIVE
|
||||
#define SHUT_WR SD_SEND
|
||||
#define SHUT_RDWR SD_BOTH
|
||||
#ifndef AF_UNIX
|
||||
#define AF_UNIX 1
|
||||
#endif
|
||||
#else
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
|
||||
// Helper to convert JS value to file descriptor
|
||||
static int js2fd(JSContext *ctx, JSValueConst val)
|
||||
{
|
||||
int fd;
|
||||
if (JS_ToInt32(ctx, &fd, val) < 0) {
|
||||
JS_ThrowTypeError(ctx, "Expected file descriptor number");
|
||||
return -1;
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
// SOCKET FUNCTIONS
|
||||
|
||||
JSC_CCALL(socket_getaddrinfo,
|
||||
const char *node = NULL;
|
||||
const char *service = NULL;
|
||||
struct addrinfo hints, *res;
|
||||
|
||||
memset(&hints, 0, sizeof hints);
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
|
||||
if (!JS_IsNull(argv[0]) && !JS_IsNull(argv[0]))
|
||||
node = JS_ToCString(js, argv[0]);
|
||||
|
||||
if (!JS_IsNull(argv[1]) && !JS_IsNull(argv[1]))
|
||||
service = JS_ToCString(js, argv[1]);
|
||||
|
||||
// Parse optional hints object
|
||||
if (argc > 2 && JS_IsObject(argv[2])) {
|
||||
JSValue val;
|
||||
|
||||
val = JS_GetPropertyStr(js, argv[2], "family");
|
||||
if (!JS_IsNull(val)) {
|
||||
const char *family = JS_ToCString(js, val);
|
||||
if (strcmp(family, "AF_INET") == 0) hints.ai_family = AF_INET;
|
||||
else if (strcmp(family, "AF_INET6") == 0) hints.ai_family = AF_INET6;
|
||||
JS_FreeCString(js, family);
|
||||
}
|
||||
JS_FreeValue(js, val);
|
||||
|
||||
val = JS_GetPropertyStr(js, argv[2], "socktype");
|
||||
if (!JS_IsNull(val)) {
|
||||
const char *socktype = JS_ToCString(js, val);
|
||||
if (strcmp(socktype, "SOCK_STREAM") == 0) hints.ai_socktype = SOCK_STREAM;
|
||||
else if (strcmp(socktype, "SOCK_DGRAM") == 0) hints.ai_socktype = SOCK_DGRAM;
|
||||
JS_FreeCString(js, socktype);
|
||||
}
|
||||
JS_FreeValue(js, val);
|
||||
|
||||
val = JS_GetPropertyStr(js, argv[2], "flags");
|
||||
if (!JS_IsNull(val)) {
|
||||
hints.ai_flags = js2number(js, val);
|
||||
}
|
||||
JS_FreeValue(js, val);
|
||||
|
||||
val = JS_GetPropertyStr(js, argv[2], "passive");
|
||||
if (JS_ToBool(js, val)) {
|
||||
hints.ai_flags |= AI_PASSIVE;
|
||||
}
|
||||
JS_FreeValue(js, val);
|
||||
}
|
||||
|
||||
int status = getaddrinfo(node, service, &hints, &res);
|
||||
|
||||
if (node) JS_FreeCString(js, node);
|
||||
if (service) JS_FreeCString(js, service);
|
||||
|
||||
if (status != 0) {
|
||||
return JS_ThrowReferenceError(js, "getaddrinfo error: %s", gai_strerror(status));
|
||||
}
|
||||
|
||||
// Convert linked list to JS array
|
||||
ret = JS_NewArray(js);
|
||||
int idx = 0;
|
||||
for (struct addrinfo *p = res; p != NULL; p = p->ai_next) {
|
||||
JSValue info = JS_NewObject(js);
|
||||
JS_SetPropertyStr(js, info, "family", JS_NewInt32(js, p->ai_family));
|
||||
JS_SetPropertyStr(js, info, "socktype", JS_NewInt32(js, p->ai_socktype));
|
||||
JS_SetPropertyStr(js, info, "protocol", JS_NewInt32(js, p->ai_protocol));
|
||||
|
||||
// Convert address to string
|
||||
char ipstr[INET6_ADDRSTRLEN];
|
||||
void *addr;
|
||||
if (p->ai_family == AF_INET) {
|
||||
struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr;
|
||||
addr = &(ipv4->sin_addr);
|
||||
} else {
|
||||
struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr;
|
||||
addr = &(ipv6->sin6_addr);
|
||||
}
|
||||
inet_ntop(p->ai_family, addr, ipstr, sizeof ipstr);
|
||||
JS_SetPropertyStr(js, info, "address", JS_NewString(js, ipstr));
|
||||
|
||||
// Store the addrinfo for later use
|
||||
struct addrinfo *copy = malloc(sizeof(struct addrinfo));
|
||||
memcpy(copy, p, sizeof(struct addrinfo));
|
||||
copy->ai_addr = malloc(p->ai_addrlen);
|
||||
memcpy(copy->ai_addr, p->ai_addr, p->ai_addrlen);
|
||||
copy->ai_next = NULL;
|
||||
if (p->ai_canonname) {
|
||||
copy->ai_canonname = strdup(p->ai_canonname);
|
||||
}
|
||||
|
||||
// Store the addrinfo pointer as an internal property
|
||||
// We'll need to handle this differently since we can't wrap it
|
||||
// For now, we'll skip storing the raw addrinfo
|
||||
JS_SetPropertyUint32(js, ret, idx++, info);
|
||||
}
|
||||
|
||||
freeaddrinfo(res);
|
||||
)
|
||||
|
||||
JSC_CCALL(socket_socket,
|
||||
int domain = AF_INET;
|
||||
int type = SOCK_STREAM;
|
||||
int protocol = 0;
|
||||
|
||||
// Parse domain
|
||||
if (JS_IsString(argv[0])) {
|
||||
const char *domain_str = JS_ToCString(js, argv[0]);
|
||||
if (strcmp(domain_str, "AF_INET") == 0) domain = AF_INET;
|
||||
else if (strcmp(domain_str, "AF_INET6") == 0) domain = AF_INET6;
|
||||
else if (strcmp(domain_str, "AF_UNIX") == 0) domain = AF_UNIX;
|
||||
JS_FreeCString(js, domain_str);
|
||||
} else if (JS_IsNumber(argv[0])) {
|
||||
domain = js2number(js, argv[0]);
|
||||
}
|
||||
|
||||
// Parse type
|
||||
if (argc > 1) {
|
||||
if (JS_IsString(argv[1])) {
|
||||
const char *type_str = JS_ToCString(js, argv[1]);
|
||||
if (strcmp(type_str, "SOCK_STREAM") == 0) type = SOCK_STREAM;
|
||||
else if (strcmp(type_str, "SOCK_DGRAM") == 0) type = SOCK_DGRAM;
|
||||
JS_FreeCString(js, type_str);
|
||||
} else if (JS_IsNumber(argv[1])) {
|
||||
type = js2number(js, argv[1]);
|
||||
}
|
||||
}
|
||||
|
||||
// Parse protocol
|
||||
if (argc > 2) {
|
||||
protocol = js2number(js, argv[2]);
|
||||
}
|
||||
|
||||
int sockfd = socket(domain, type, protocol);
|
||||
if (sockfd < 0) {
|
||||
return JS_ThrowReferenceError(js, "socket failed: %s", strerror(errno));
|
||||
}
|
||||
|
||||
return JS_NewInt32(js, sockfd);
|
||||
)
|
||||
|
||||
JSC_CCALL(socket_bind,
|
||||
int sockfd = js2fd(js, argv[0]);
|
||||
if (sockfd < 0) return JS_EXCEPTION;
|
||||
|
||||
// For now, we'll only support manual address parsing
|
||||
{
|
||||
// Try to parse address and port manually
|
||||
const char *addr_str = JS_ToCString(js, JS_GetPropertyStr(js, argv[1], "address"));
|
||||
int port = js2number(js, JS_GetPropertyStr(js, argv[1], "port"));
|
||||
|
||||
struct sockaddr_in addr;
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(port);
|
||||
if (inet_pton(AF_INET, addr_str, &addr.sin_addr) <= 0) {
|
||||
JS_FreeCString(js, addr_str);
|
||||
return JS_ThrowReferenceError(js, "Invalid address");
|
||||
}
|
||||
JS_FreeCString(js, addr_str);
|
||||
|
||||
if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
|
||||
return JS_ThrowReferenceError(js, "bind failed: %s", strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
return JS_NULL;
|
||||
)
|
||||
|
||||
JSC_CCALL(socket_connect,
|
||||
int sockfd = js2fd(js, argv[0]);
|
||||
if (sockfd < 0) return JS_EXCEPTION;
|
||||
|
||||
// For now, we'll only support manual address parsing
|
||||
{
|
||||
// Try to parse address and port manually
|
||||
const char *addr_str = JS_ToCString(js, JS_GetPropertyStr(js, argv[1], "address"));
|
||||
int port = js2number(js, JS_GetPropertyStr(js, argv[1], "port"));
|
||||
|
||||
struct sockaddr_in addr;
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(port);
|
||||
if (inet_pton(AF_INET, addr_str, &addr.sin_addr) <= 0) {
|
||||
JS_FreeCString(js, addr_str);
|
||||
return JS_ThrowReferenceError(js, "Invalid address");
|
||||
}
|
||||
JS_FreeCString(js, addr_str);
|
||||
|
||||
if (connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
|
||||
return JS_ThrowReferenceError(js, "connect failed: %s", strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
return JS_NULL;
|
||||
)
|
||||
|
||||
JSC_CCALL(socket_listen,
|
||||
int sockfd = js2fd(js, argv[0]);
|
||||
if (sockfd < 0) return JS_EXCEPTION;
|
||||
|
||||
int backlog = 10;
|
||||
if (argc > 1) {
|
||||
backlog = js2number(js, argv[1]);
|
||||
}
|
||||
|
||||
if (listen(sockfd, backlog) < 0) {
|
||||
return JS_ThrowReferenceError(js, "listen failed: %s", strerror(errno));
|
||||
}
|
||||
|
||||
return JS_NULL;
|
||||
)
|
||||
|
||||
JSC_CCALL(socket_accept,
|
||||
int sockfd = js2fd(js, argv[0]);
|
||||
if (sockfd < 0) return JS_EXCEPTION;
|
||||
|
||||
struct sockaddr_storage their_addr;
|
||||
socklen_t addr_size = sizeof their_addr;
|
||||
|
||||
int new_sockfd = accept(sockfd, (struct sockaddr *)&their_addr, &addr_size);
|
||||
if (new_sockfd < 0) {
|
||||
return JS_ThrowReferenceError(js, "accept failed: %s", strerror(errno));
|
||||
}
|
||||
|
||||
ret = JS_NewObject(js);
|
||||
JS_SetPropertyStr(js, ret, "socket", JS_NewInt32(js, new_sockfd));
|
||||
|
||||
// Get peer address info
|
||||
char ipstr[INET6_ADDRSTRLEN];
|
||||
int port;
|
||||
if (their_addr.ss_family == AF_INET) {
|
||||
struct sockaddr_in *s = (struct sockaddr_in *)&their_addr;
|
||||
inet_ntop(AF_INET, &s->sin_addr, ipstr, sizeof ipstr);
|
||||
port = ntohs(s->sin_port);
|
||||
} else {
|
||||
struct sockaddr_in6 *s = (struct sockaddr_in6 *)&their_addr;
|
||||
inet_ntop(AF_INET6, &s->sin6_addr, ipstr, sizeof ipstr);
|
||||
port = ntohs(s->sin6_port);
|
||||
}
|
||||
|
||||
JSValue addr_info = JS_NewObject(js);
|
||||
JS_SetPropertyStr(js, addr_info, "address", JS_NewString(js, ipstr));
|
||||
JS_SetPropertyStr(js, addr_info, "port", JS_NewInt32(js, port));
|
||||
JS_SetPropertyStr(js, ret, "address", addr_info);
|
||||
)
|
||||
|
||||
JSC_CCALL(socket_send,
|
||||
int sockfd = js2fd(js, argv[0]);
|
||||
if (sockfd < 0) return JS_EXCEPTION;
|
||||
|
||||
size_t len;
|
||||
ssize_t sent;
|
||||
int flags = 0;
|
||||
|
||||
if (argc > 2) {
|
||||
flags = js2number(js, argv[2]);
|
||||
}
|
||||
|
||||
if (JS_IsString(argv[1])) {
|
||||
const char *data = JS_ToCStringLen(js, &len, argv[1]);
|
||||
sent = send(sockfd, data, len, flags);
|
||||
JS_FreeCString(js, data);
|
||||
} else {
|
||||
unsigned char *data = js_get_blob_data(js, &len, argv[1]);
|
||||
sent = send(sockfd, (const char *)data, len, flags);
|
||||
}
|
||||
|
||||
if (sent < 0) {
|
||||
return JS_ThrowReferenceError(js, "send failed: %s", strerror(errno));
|
||||
}
|
||||
|
||||
return JS_NewInt64(js, sent);
|
||||
)
|
||||
|
||||
JSC_CCALL(socket_recv,
|
||||
int sockfd = js2fd(js, argv[0]);
|
||||
if (sockfd < 0) return JS_EXCEPTION;
|
||||
|
||||
size_t len = 4096;
|
||||
if (argc > 1) {
|
||||
len = js2number(js, argv[1]);
|
||||
}
|
||||
|
||||
int flags = 0;
|
||||
if (argc > 2) {
|
||||
flags = js2number(js, argv[2]);
|
||||
}
|
||||
|
||||
void *buf = malloc(len);
|
||||
if (!buf) {
|
||||
return JS_ThrowReferenceError(js, "malloc failed");
|
||||
}
|
||||
|
||||
ssize_t received = recv(sockfd, buf, len, flags);
|
||||
if (received < 0) {
|
||||
free(buf);
|
||||
return JS_ThrowReferenceError(js, "recv failed: %s", strerror(errno));
|
||||
}
|
||||
|
||||
ret = js_new_blob_stoned_copy(js, buf, received);
|
||||
free(buf);
|
||||
return ret;
|
||||
)
|
||||
|
||||
JSC_CCALL(socket_sendto,
|
||||
int sockfd = js2fd(js, argv[0]);
|
||||
if (sockfd < 0) return JS_EXCEPTION;
|
||||
|
||||
size_t len;
|
||||
ssize_t sent;
|
||||
int flags = 0;
|
||||
|
||||
if (argc > 3) {
|
||||
flags = js2number(js, argv[3]);
|
||||
}
|
||||
|
||||
// Get destination address
|
||||
struct sockaddr *to_addr;
|
||||
socklen_t to_len;
|
||||
|
||||
// For now, we'll only support manual address parsing
|
||||
{
|
||||
// Try to parse address and port manually
|
||||
const char *addr_str = JS_ToCString(js, JS_GetPropertyStr(js, argv[2], "address"));
|
||||
int port = js2number(js, JS_GetPropertyStr(js, argv[2], "port"));
|
||||
|
||||
static struct sockaddr_in addr;
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(port);
|
||||
if (inet_pton(AF_INET, addr_str, &addr.sin_addr) <= 0) {
|
||||
JS_FreeCString(js, addr_str);
|
||||
return JS_ThrowReferenceError(js, "Invalid address");
|
||||
}
|
||||
JS_FreeCString(js, addr_str);
|
||||
|
||||
to_addr = (struct sockaddr *)&addr;
|
||||
to_len = sizeof(addr);
|
||||
}
|
||||
|
||||
if (JS_IsString(argv[1])) {
|
||||
const char *data = JS_ToCStringLen(js, &len, argv[1]);
|
||||
sent = sendto(sockfd, data, len, flags, to_addr, to_len);
|
||||
JS_FreeCString(js, data);
|
||||
} else {
|
||||
unsigned char *data = js_get_blob_data(js, &len, argv[1]);
|
||||
sent = sendto(sockfd, (const char *)data, len, flags, to_addr, to_len);
|
||||
}
|
||||
|
||||
if (sent < 0) {
|
||||
return JS_ThrowReferenceError(js, "sendto failed: %s", strerror(errno));
|
||||
}
|
||||
|
||||
return JS_NewInt64(js, sent);
|
||||
)
|
||||
|
||||
JSC_CCALL(socket_recvfrom,
|
||||
int sockfd = js2fd(js, argv[0]);
|
||||
if (sockfd < 0) return JS_EXCEPTION;
|
||||
|
||||
size_t len = 4096;
|
||||
if (argc > 1) {
|
||||
len = js2number(js, argv[1]);
|
||||
}
|
||||
|
||||
int flags = 0;
|
||||
if (argc > 2) {
|
||||
flags = js2number(js, argv[2]);
|
||||
}
|
||||
|
||||
void *buf = malloc(len);
|
||||
if (!buf) {
|
||||
return JS_ThrowReferenceError(js, "malloc failed");
|
||||
}
|
||||
|
||||
struct sockaddr_storage from_addr;
|
||||
socklen_t from_len = sizeof from_addr;
|
||||
|
||||
ssize_t received = recvfrom(sockfd, buf, len, flags,
|
||||
(struct sockaddr *)&from_addr, &from_len);
|
||||
if (received < 0) {
|
||||
free(buf);
|
||||
return JS_ThrowReferenceError(js, "recvfrom failed: %s", strerror(errno));
|
||||
}
|
||||
|
||||
ret = JS_NewObject(js);
|
||||
JS_SetPropertyStr(js, ret, "data", js_new_blob_stoned_copy(js, buf, received));
|
||||
free(buf);
|
||||
|
||||
// Get source address info
|
||||
char ipstr[INET6_ADDRSTRLEN];
|
||||
int port;
|
||||
if (from_addr.ss_family == AF_INET) {
|
||||
struct sockaddr_in *s = (struct sockaddr_in *)&from_addr;
|
||||
inet_ntop(AF_INET, &s->sin_addr, ipstr, sizeof ipstr);
|
||||
port = ntohs(s->sin_port);
|
||||
} else {
|
||||
struct sockaddr_in6 *s = (struct sockaddr_in6 *)&from_addr;
|
||||
inet_ntop(AF_INET6, &s->sin6_addr, ipstr, sizeof ipstr);
|
||||
port = ntohs(s->sin6_port);
|
||||
}
|
||||
|
||||
JSValue addr_info = JS_NewObject(js);
|
||||
JS_SetPropertyStr(js, addr_info, "address", JS_NewString(js, ipstr));
|
||||
JS_SetPropertyStr(js, addr_info, "port", JS_NewInt32(js, port));
|
||||
JS_SetPropertyStr(js, ret, "address", addr_info);
|
||||
)
|
||||
|
||||
JSC_CCALL(socket_shutdown,
|
||||
int sockfd = js2fd(js, argv[0]);
|
||||
if (sockfd < 0) return JS_EXCEPTION;
|
||||
|
||||
int how = SHUT_RDWR;
|
||||
if (argc > 1) {
|
||||
how = js2number(js, argv[1]);
|
||||
}
|
||||
|
||||
if (shutdown(sockfd, how) < 0) {
|
||||
return JS_ThrowReferenceError(js, "shutdown failed: %s", strerror(errno));
|
||||
}
|
||||
|
||||
return JS_NULL;
|
||||
)
|
||||
|
||||
JSC_CCALL(socket_getpeername,
|
||||
int sockfd = js2fd(js, argv[0]);
|
||||
if (sockfd < 0) return JS_EXCEPTION;
|
||||
|
||||
struct sockaddr_storage addr;
|
||||
socklen_t len = sizeof addr;
|
||||
|
||||
if (getpeername(sockfd, (struct sockaddr *)&addr, &len) < 0) {
|
||||
return JS_ThrowReferenceError(js, "getpeername failed: %s", strerror(errno));
|
||||
}
|
||||
|
||||
char ipstr[INET6_ADDRSTRLEN];
|
||||
int port;
|
||||
if (addr.ss_family == AF_INET) {
|
||||
struct sockaddr_in *s = (struct sockaddr_in *)&addr;
|
||||
inet_ntop(AF_INET, &s->sin_addr, ipstr, sizeof ipstr);
|
||||
port = ntohs(s->sin_port);
|
||||
} else {
|
||||
struct sockaddr_in6 *s = (struct sockaddr_in6 *)&addr;
|
||||
inet_ntop(AF_INET6, &s->sin6_addr, ipstr, sizeof ipstr);
|
||||
port = ntohs(s->sin6_port);
|
||||
}
|
||||
|
||||
ret = JS_NewObject(js);
|
||||
JS_SetPropertyStr(js, ret, "address", JS_NewString(js, ipstr));
|
||||
JS_SetPropertyStr(js, ret, "port", JS_NewInt32(js, port));
|
||||
)
|
||||
|
||||
JSC_CCALL(socket_gethostname,
|
||||
char hostname[256];
|
||||
if (gethostname(hostname, sizeof(hostname)) < 0) {
|
||||
return JS_ThrowReferenceError(js, "gethostname failed: %s", strerror(errno));
|
||||
}
|
||||
return JS_NewString(js, hostname);
|
||||
)
|
||||
|
||||
|
||||
JSC_CCALL(socket_gai_strerror,
|
||||
int errcode = js2number(js, argv[0]);
|
||||
return JS_NewString(js, gai_strerror(errcode));
|
||||
)
|
||||
|
||||
JSC_CCALL(socket_setsockopt,
|
||||
int sockfd = js2fd(js, argv[0]);
|
||||
if (sockfd < 0) return JS_EXCEPTION;
|
||||
|
||||
int level = SOL_SOCKET;
|
||||
int optname = 0;
|
||||
|
||||
// Parse level
|
||||
if (JS_IsString(argv[1])) {
|
||||
const char *level_str = JS_ToCString(js, argv[1]);
|
||||
if (strcmp(level_str, "SOL_SOCKET") == 0) level = SOL_SOCKET;
|
||||
else if (strcmp(level_str, "IPPROTO_TCP") == 0) level = IPPROTO_TCP;
|
||||
else if (strcmp(level_str, "IPPROTO_IP") == 0) level = IPPROTO_IP;
|
||||
else if (strcmp(level_str, "IPPROTO_IPV6") == 0) level = IPPROTO_IPV6;
|
||||
JS_FreeCString(js, level_str);
|
||||
} else {
|
||||
level = js2number(js, argv[1]);
|
||||
}
|
||||
|
||||
// Parse option name
|
||||
if (JS_IsString(argv[2])) {
|
||||
const char *opt_str = JS_ToCString(js, argv[2]);
|
||||
if (strcmp(opt_str, "SO_REUSEADDR") == 0) optname = SO_REUSEADDR;
|
||||
else if (strcmp(opt_str, "SO_KEEPALIVE") == 0) optname = SO_KEEPALIVE;
|
||||
else if (strcmp(opt_str, "SO_BROADCAST") == 0) optname = SO_BROADCAST;
|
||||
JS_FreeCString(js, opt_str);
|
||||
} else {
|
||||
optname = js2number(js, argv[2]);
|
||||
}
|
||||
|
||||
// Parse option value
|
||||
if (JS_IsBool(argv[3])) {
|
||||
int optval = JS_ToBool(js, argv[3]);
|
||||
if (setsockopt(sockfd, level, optname, &optval, sizeof(optval)) < 0) {
|
||||
return JS_ThrowReferenceError(js, "setsockopt failed: %s", strerror(errno));
|
||||
}
|
||||
} else if (JS_IsNumber(argv[3])) {
|
||||
int optval = js2number(js, argv[3]);
|
||||
if (setsockopt(sockfd, level, optname, &optval, sizeof(optval)) < 0) {
|
||||
return JS_ThrowReferenceError(js, "setsockopt failed: %s", strerror(errno));
|
||||
}
|
||||
} else {
|
||||
return JS_ThrowTypeError(js, "Invalid option value");
|
||||
}
|
||||
|
||||
return JS_NULL;
|
||||
)
|
||||
|
||||
JSC_CCALL(socket_close,
|
||||
int sockfd = js2fd(js, argv[0]);
|
||||
if (sockfd < 0) return JS_EXCEPTION;
|
||||
|
||||
if (close(sockfd) != 0)
|
||||
return JS_ThrowReferenceError(js, "close failed: %s", strerror(errno));
|
||||
|
||||
return JS_NULL;
|
||||
)
|
||||
|
||||
static const JSCFunctionListEntry js_socket_funcs[] = {
|
||||
MIST_FUNC_DEF(socket, getaddrinfo, 3),
|
||||
MIST_FUNC_DEF(socket, socket, 3),
|
||||
MIST_FUNC_DEF(socket, bind, 2),
|
||||
MIST_FUNC_DEF(socket, connect, 2),
|
||||
MIST_FUNC_DEF(socket, listen, 2),
|
||||
MIST_FUNC_DEF(socket, accept, 1),
|
||||
MIST_FUNC_DEF(socket, send, 3),
|
||||
MIST_FUNC_DEF(socket, recv, 3),
|
||||
MIST_FUNC_DEF(socket, sendto, 4),
|
||||
MIST_FUNC_DEF(socket, recvfrom, 3),
|
||||
MIST_FUNC_DEF(socket, shutdown, 2),
|
||||
MIST_FUNC_DEF(socket, getpeername, 1),
|
||||
MIST_FUNC_DEF(socket, gethostname, 0),
|
||||
MIST_FUNC_DEF(socket, gai_strerror, 1),
|
||||
MIST_FUNC_DEF(socket, setsockopt, 4),
|
||||
MIST_FUNC_DEF(socket, close, 1),
|
||||
};
|
||||
|
||||
JSValue js_socket_use(JSContext *js) {
|
||||
JSValue mod = JS_NewObject(js);
|
||||
JS_SetPropertyFunctionList(js, mod, js_socket_funcs, countof(js_socket_funcs));
|
||||
|
||||
// Add constants
|
||||
JS_SetPropertyStr(js, mod, "AF_UNSPEC", JS_NewInt32(js, AF_UNSPEC));
|
||||
JS_SetPropertyStr(js, mod, "AF_INET", JS_NewInt32(js, AF_INET));
|
||||
JS_SetPropertyStr(js, mod, "AF_INET6", JS_NewInt32(js, AF_INET6));
|
||||
JS_SetPropertyStr(js, mod, "AF_UNIX", JS_NewInt32(js, AF_UNIX));
|
||||
|
||||
JS_SetPropertyStr(js, mod, "SOCK_STREAM", JS_NewInt32(js, SOCK_STREAM));
|
||||
JS_SetPropertyStr(js, mod, "SOCK_DGRAM", JS_NewInt32(js, SOCK_DGRAM));
|
||||
|
||||
JS_SetPropertyStr(js, mod, "AI_PASSIVE", JS_NewInt32(js, AI_PASSIVE));
|
||||
|
||||
JS_SetPropertyStr(js, mod, "SHUT_RD", JS_NewInt32(js, SHUT_RD));
|
||||
JS_SetPropertyStr(js, mod, "SHUT_WR", JS_NewInt32(js, SHUT_WR));
|
||||
JS_SetPropertyStr(js, mod, "SHUT_RDWR", JS_NewInt32(js, SHUT_RDWR));
|
||||
|
||||
JS_SetPropertyStr(js, mod, "SOL_SOCKET", JS_NewInt32(js, SOL_SOCKET));
|
||||
JS_SetPropertyStr(js, mod, "SO_REUSEADDR", JS_NewInt32(js, SO_REUSEADDR));
|
||||
|
||||
return mod;
|
||||
}
|
||||
@@ -1,307 +0,0 @@
|
||||
#include "cell.h"
|
||||
#include "blob.h"
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
JSC_CCALL(text_blob_to_hex,
|
||||
size_t blob_len;
|
||||
void *blob_data = js_get_blob_data(js, &blob_len, argv[0]);
|
||||
if (!blob_data) return JS_ThrowTypeError(js, "Expected stone blob");
|
||||
uint8_t *bytes = (uint8_t *)blob_data;
|
||||
size_t hex_len = blob_len * 2;
|
||||
char *hex_str = malloc(hex_len + 1);
|
||||
if (!hex_str) return JS_ThrowOutOfMemory(js);
|
||||
static const char hex_digits[] = "0123456789ABCDEF";
|
||||
for (size_t i = 0; i < blob_len; ++i) {
|
||||
hex_str[i * 2] = hex_digits[(bytes[i] >> 4) & 0xF];
|
||||
hex_str[i * 2 + 1] = hex_digits[bytes[i] & 0xF];
|
||||
}
|
||||
hex_str[hex_len] = '\0';
|
||||
JSValue val = JS_NewString(js, hex_str);
|
||||
free(hex_str);
|
||||
return val;
|
||||
)
|
||||
|
||||
JSC_CCALL(text_blob_to_base32,
|
||||
size_t blob_len;
|
||||
void *blob_data = js_get_blob_data(js, &blob_len, argv[0]);
|
||||
if (!blob_data) return JS_ThrowTypeError(js, "Expected stone blob");
|
||||
uint8_t *bytes = (uint8_t *)blob_data;
|
||||
static const char b32_digits[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
|
||||
|
||||
// Calculate exact output length needed
|
||||
size_t groups = (blob_len + 4) / 5; // Round up to next group of 5
|
||||
size_t b32_len = groups * 8;
|
||||
char *b32_str = malloc(b32_len + 1);
|
||||
if (!b32_str) return JS_ThrowOutOfMemory(js);
|
||||
|
||||
size_t in_idx = 0;
|
||||
size_t out_idx = 0;
|
||||
|
||||
while (in_idx < blob_len) {
|
||||
// Read up to 5 bytes into a 40-bit buffer
|
||||
uint64_t buf = 0;
|
||||
int bytes_to_read = (blob_len - in_idx < 5) ? (blob_len - in_idx) : 5;
|
||||
|
||||
for (int i = 0; i < bytes_to_read; ++i) {
|
||||
buf = (buf << 8) | bytes[in_idx++];
|
||||
}
|
||||
|
||||
// Pad buffer to 40 bits if we read fewer than 5 bytes
|
||||
buf <<= 8 * (5 - bytes_to_read);
|
||||
|
||||
// Extract 8 groups of 5 bits each
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
b32_str[out_idx++] = b32_digits[(buf >> (35 - i * 5)) & 0x1F];
|
||||
}
|
||||
}
|
||||
|
||||
// Add padding if necessary
|
||||
if (blob_len % 5 != 0) {
|
||||
static const int pad_count[] = {0, 6, 4, 3, 1}; // padding for 0,1,2,3,4 bytes
|
||||
int padding = pad_count[blob_len % 5];
|
||||
for (int i = 0; i < padding; ++i) {
|
||||
b32_str[b32_len - 1 - i] = '=';
|
||||
}
|
||||
}
|
||||
|
||||
b32_str[b32_len] = '\0';
|
||||
JSValue val = JS_NewString(js, b32_str);
|
||||
free(b32_str);
|
||||
return val;
|
||||
)
|
||||
|
||||
static int base32_char_to_val(char c) {
|
||||
if (c >= 'A' && c <= 'Z') return c - 'A';
|
||||
if (c >= 'a' && c <= 'z') return c - 'a';
|
||||
if (c >= '2' && c <= '7') return c - '2' + 26;
|
||||
return -1;
|
||||
}
|
||||
|
||||
JSC_CCALL(text_base32_to_blob,
|
||||
const char *str = JS_ToCString(js, argv[0]);
|
||||
if (!str) return JS_ThrowTypeError(js, "Expected string");
|
||||
size_t str_len = strlen(str);
|
||||
if (str_len == 0) {
|
||||
JS_FreeCString(js, str);
|
||||
return JS_ThrowTypeError(js, "Empty base32 string");
|
||||
}
|
||||
|
||||
// Remove padding to get effective length
|
||||
size_t effective_len = str_len;
|
||||
while (effective_len > 0 && str[effective_len - 1] == '=') {
|
||||
effective_len--;
|
||||
}
|
||||
|
||||
// Calculate output length: each group of 8 base32 chars -> 5 bytes
|
||||
size_t output_len = (effective_len * 5) / 8;
|
||||
uint8_t *output = malloc(output_len);
|
||||
if (!output) {
|
||||
JS_FreeCString(js, str);
|
||||
return JS_ThrowOutOfMemory(js);
|
||||
}
|
||||
|
||||
size_t in_idx = 0;
|
||||
size_t out_idx = 0;
|
||||
|
||||
// Process in groups of 8 characters (40 bits -> 5 bytes)
|
||||
while (in_idx < effective_len) {
|
||||
uint64_t buf = 0;
|
||||
int chars_to_read = (effective_len - in_idx < 8) ? (effective_len - in_idx) : 8;
|
||||
|
||||
// Read up to 8 base32 characters into buffer
|
||||
for (int i = 0; i < chars_to_read; ++i) {
|
||||
int val = base32_char_to_val(str[in_idx++]);
|
||||
if (val < 0) {
|
||||
free(output);
|
||||
JS_FreeCString(js, str);
|
||||
return JS_ThrowTypeError(js, "Invalid base32 character");
|
||||
}
|
||||
buf = (buf << 5) | val;
|
||||
}
|
||||
|
||||
// Calculate how many bytes we can extract
|
||||
int bytes_to_extract = (chars_to_read * 5) / 8;
|
||||
|
||||
// Shift buffer to align the most significant bits
|
||||
buf <<= (40 - chars_to_read * 5);
|
||||
|
||||
// Extract bytes from most significant to least significant
|
||||
for (int i = 0; i < bytes_to_extract && out_idx < output_len; ++i) {
|
||||
output[out_idx++] = (buf >> (32 - i * 8)) & 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
JSValue val = js_new_blob_stoned_copy(js, output, output_len);
|
||||
free(output);
|
||||
JS_FreeCString(js, str);
|
||||
return val;
|
||||
)
|
||||
|
||||
static int base64_char_to_val_standard(char c) {
|
||||
if (c >= 'A' && c <= 'Z') return c - 'A';
|
||||
if (c >= 'a' && c <= 'z') return c - 'a' + 26;
|
||||
if (c >= '0' && c <= '9') return c - '0' + 52;
|
||||
if (c == '+') return 62;
|
||||
if (c == '/') return 63;
|
||||
return -1;
|
||||
}
|
||||
static int base64_char_to_val_url(char c) {
|
||||
if (c >= 'A' && c <= 'Z') return c - 'A';
|
||||
if (c >= 'a' && c <= 'z') return c - 'a' + 26;
|
||||
if (c >= '0' && c <= '9') return c - '0' + 52;
|
||||
if (c == '-') return 62;
|
||||
if (c == '_') return 63;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*─── blob → Base64 (standard, with ‘+’ and ‘/’, padded) ───────────────────*/
|
||||
JSC_CCALL(text_blob_to_base64,
|
||||
size_t blob_len;
|
||||
void *blob_data = js_get_blob_data(js, &blob_len, argv[0]);
|
||||
if (!blob_data) return JS_ThrowTypeError(js, "Expected stone blob");
|
||||
const uint8_t *bytes = blob_data;
|
||||
static const char b64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz"
|
||||
"0123456789+/";
|
||||
size_t out_len = ((blob_len + 2) / 3) * 4;
|
||||
char *out = malloc(out_len + 1);
|
||||
if (!out) return JS_ThrowOutOfMemory(js);
|
||||
|
||||
size_t in_i = 0, out_i = 0;
|
||||
while (in_i < blob_len) {
|
||||
uint32_t buf = 0;
|
||||
int to_read = (blob_len - in_i < 3 ? blob_len - in_i : 3);
|
||||
for (int j = 0; j < to_read; ++j) {
|
||||
buf = (buf << 8) | bytes[in_i++];
|
||||
}
|
||||
buf <<= 8 * (3 - to_read);
|
||||
out[out_i++] = b64[(buf >> 18) & 0x3F];
|
||||
out[out_i++] = b64[(buf >> 12) & 0x3F];
|
||||
out[out_i++] = (to_read > 1 ? b64[(buf >> 6) & 0x3F] : '=');
|
||||
out[out_i++] = (to_read > 2 ? b64[ buf & 0x3F] : '=');
|
||||
}
|
||||
out[out_len] = '\0';
|
||||
JSValue v = JS_NewString(js, out);
|
||||
free(out);
|
||||
return v;
|
||||
)
|
||||
|
||||
/*─── Base64 → blob (standard, expects ‘+’ and ‘/’, pads allowed) ────────────*/
|
||||
JSC_CCALL(text_base64_to_blob,
|
||||
const char *str = JS_ToCString(js, argv[0]);
|
||||
if (!str) return JS_ThrowTypeError(js, "Expected string");
|
||||
size_t len = strlen(str);
|
||||
// strip padding for length calculation
|
||||
size_t eff = len;
|
||||
while (eff > 0 && str[eff-1] == '=') eff--;
|
||||
size_t out_len = (eff * 6) / 8;
|
||||
uint8_t *out = malloc(out_len);
|
||||
if (!out) { JS_FreeCString(js, str); return JS_ThrowOutOfMemory(js); }
|
||||
|
||||
size_t in_i = 0, out_i = 0;
|
||||
while (in_i < eff) {
|
||||
uint32_t buf = 0;
|
||||
int to_read = (eff - in_i < 4 ? eff - in_i : 4);
|
||||
for (int j = 0; j < to_read; ++j) {
|
||||
int v = base64_char_to_val_standard(str[in_i++]);
|
||||
if (v < 0) { free(out); JS_FreeCString(js, str);
|
||||
return JS_ThrowTypeError(js, "Invalid base64 character"); }
|
||||
buf = (buf << 6) | v;
|
||||
}
|
||||
buf <<= 6 * (4 - to_read);
|
||||
int bytes_out = (to_read * 6) / 8;
|
||||
for (int j = 0; j < bytes_out && out_i < out_len; ++j) {
|
||||
out[out_i++] = (buf >> (16 - 8*j)) & 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
JSValue v = js_new_blob_stoned_copy(js, out, out_len);
|
||||
free(out);
|
||||
JS_FreeCString(js, str);
|
||||
return v;
|
||||
)
|
||||
|
||||
/*─── blob → Base64URL (no padding, ‘-’ and ‘_’) ─────────────────────────────*/
|
||||
JSC_CCALL(text_blob_to_base64url,
|
||||
size_t blob_len;
|
||||
void *blob_data = js_get_blob_data(js, &blob_len, argv[0]);
|
||||
if (!blob_data) return JS_ThrowTypeError(js, "Expected stone blob");
|
||||
const uint8_t *bytes = blob_data;
|
||||
static const char b64url[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz"
|
||||
"0123456789-_";
|
||||
size_t raw_len = ((blob_len + 2) / 3) * 4;
|
||||
// we’ll drop any trailing '='
|
||||
char *out = malloc(raw_len + 1);
|
||||
if (!out) return JS_ThrowOutOfMemory(js);
|
||||
|
||||
size_t in_i = 0, out_i = 0;
|
||||
while (in_i < blob_len) {
|
||||
uint32_t buf = 0;
|
||||
int to_read = (blob_len - in_i < 3 ? blob_len - in_i : 3);
|
||||
for (int j = 0; j < to_read; ++j) {
|
||||
buf = (buf << 8) | bytes[in_i++];
|
||||
}
|
||||
buf <<= 8 * (3 - to_read);
|
||||
out[out_i++] = b64url[(buf >> 18) & 0x3F];
|
||||
out[out_i++] = b64url[(buf >> 12) & 0x3F];
|
||||
if (to_read > 1) out[out_i++] = b64url[(buf >> 6) & 0x3F];
|
||||
if (to_read > 2) out[out_i++] = b64url[ buf & 0x3F];
|
||||
}
|
||||
out[out_i] = '\0';
|
||||
JSValue v = JS_NewString(js, out);
|
||||
free(out);
|
||||
return v;
|
||||
)
|
||||
|
||||
/*─── Base64URL → blob (accepts ‘-’ / ‘_’, no padding needed) ─────────────────*/
|
||||
JSC_CCALL(text_base64url_to_blob,
|
||||
const char *str = JS_ToCString(js, argv[0]);
|
||||
if (!str) return JS_ThrowTypeError(js, "Expected string");
|
||||
size_t len = strlen(str);
|
||||
size_t eff = len; // no '=' in URL‐safe, but strip if present
|
||||
while (eff > 0 && str[eff-1] == '=') eff--;
|
||||
size_t out_len = (eff * 6) / 8;
|
||||
uint8_t *out = malloc(out_len);
|
||||
if (!out) { JS_FreeCString(js, str); return JS_ThrowOutOfMemory(js); }
|
||||
|
||||
size_t in_i = 0, out_i = 0;
|
||||
while (in_i < eff) {
|
||||
uint32_t buf = 0;
|
||||
int to_read = (eff - in_i < 4 ? eff - in_i : 4);
|
||||
for (int j = 0; j < to_read; ++j) {
|
||||
int v = base64_char_to_val_url(str[in_i++]);
|
||||
if (v < 0) { free(out); JS_FreeCString(js, str);
|
||||
return JS_ThrowTypeError(js, "Invalid base64url character"); }
|
||||
buf = (buf << 6) | v;
|
||||
}
|
||||
buf <<= 6 * (4 - to_read);
|
||||
int bytes_out = (to_read * 6) / 8;
|
||||
for (int j = 0; j < bytes_out && out_i < out_len; ++j) {
|
||||
out[out_i++] = (buf >> (16 - 8*j)) & 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
JSValue v = js_new_blob_stoned_copy(js, out, out_len);
|
||||
free(out);
|
||||
JS_FreeCString(js, str);
|
||||
return v;
|
||||
)
|
||||
|
||||
static const JSCFunctionListEntry js_text_funcs[] = {
|
||||
MIST_FUNC_DEF(text, blob_to_hex, 1),
|
||||
MIST_FUNC_DEF(text, blob_to_base32, 1),
|
||||
MIST_FUNC_DEF(text, base32_to_blob, 1),
|
||||
MIST_FUNC_DEF(text, blob_to_base64, 1),
|
||||
MIST_FUNC_DEF(text, base64_to_blob, 1),
|
||||
MIST_FUNC_DEF(text, blob_to_base64url, 1),
|
||||
MIST_FUNC_DEF(text, base64url_to_blob, 1),
|
||||
};
|
||||
|
||||
JSValue js_text_use(JSContext *js)
|
||||
{
|
||||
JSValue mod = JS_NewObject(js);
|
||||
JS_SetPropertyFunctionList(js, mod, js_text_funcs, countof(js_text_funcs));
|
||||
return mod;
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
#include "cell.h"
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
/* ---------------------------------------------------------------- *\
|
||||
Helpers
|
||||
\* ---------------------------------------------------------------- */
|
||||
|
||||
/* Seconds from Misty epoch (year-0) so JS “number()” stays consistent.
|
||||
62167219200 = seconds between 0000-01-01 00:00 UTC and 1970-01-01 00:00 UTC. */
|
||||
static inline double misty_now(void)
|
||||
{
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
return (double)tv.tv_sec + tv.tv_usec / 1000000.0;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------- *\
|
||||
JS bindings
|
||||
\* ---------------------------------------------------------------- */
|
||||
|
||||
static JSValue
|
||||
js_time_now(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv)
|
||||
{
|
||||
return JS_NewFloat64(ctx, misty_now());
|
||||
}
|
||||
|
||||
static JSValue
|
||||
js_time_computer_dst(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv)
|
||||
{
|
||||
time_t t = time(NULL);
|
||||
struct tm *lt = localtime(&t);
|
||||
return JS_NewBool(ctx, lt && lt->tm_isdst > 0);
|
||||
}
|
||||
|
||||
static JSValue
|
||||
js_time_computer_zone(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv)
|
||||
{
|
||||
time_t t = time(NULL);
|
||||
|
||||
#ifdef __USE_BSD /* tm_gmtoff / tm_zone available */
|
||||
struct tm lt = *localtime(&t);
|
||||
long offset_sec = lt.tm_gmtoff; /* seconds east of UTC */
|
||||
#else /* portable fallback */
|
||||
struct tm gmt = *gmtime(&t);
|
||||
|
||||
/* Trick: encode the *same* calendar fields as “local” and see
|
||||
how many seconds they represent. */
|
||||
gmt.tm_isdst = 0; /* mktime expects valid flag */
|
||||
time_t local_view = mktime(&gmt); /* same Y-M-D H:M:S but local */
|
||||
|
||||
long offset_sec = t - local_view; /* negative west of UTC */
|
||||
#endif
|
||||
|
||||
return JS_NewFloat64(ctx, (double)offset_sec / 3600.0);
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------- *\
|
||||
registration
|
||||
\* ---------------------------------------------------------------- */
|
||||
|
||||
static const JSCFunctionListEntry js_time_funcs[] = {
|
||||
JS_CFUNC_DEF("now", 0, js_time_now),
|
||||
JS_CFUNC_DEF("computer_dst", 0, js_time_computer_dst),
|
||||
JS_CFUNC_DEF("computer_zone", 0, js_time_computer_zone),
|
||||
};
|
||||
|
||||
JSValue
|
||||
js_time_use(JSContext *ctx)
|
||||
{
|
||||
JSValue obj = JS_NewObject(ctx);
|
||||
JS_SetPropertyFunctionList(ctx, obj,
|
||||
js_time_funcs,
|
||||
sizeof(js_time_funcs) /
|
||||
sizeof(js_time_funcs[0]));
|
||||
return obj;
|
||||
}
|
||||
@@ -1,209 +0,0 @@
|
||||
#include "cell.h"
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "kim.h"
|
||||
|
||||
// Get codepoints from a UTF-8 string
|
||||
JSC_CCALL(utf8_codepoints,
|
||||
const char *str = JS_ToCString(js, argv[0]);
|
||||
if (!str) return JS_EXCEPTION;
|
||||
|
||||
JSValue arr = JS_NewArray(js);
|
||||
int idx = 0;
|
||||
|
||||
char *ptr = (char*)str;
|
||||
while (*ptr) {
|
||||
int codepoint = decode_utf8(&ptr);
|
||||
JS_SetPropertyUint32(js, arr, idx++, JS_NewInt32(js, codepoint));
|
||||
}
|
||||
|
||||
JS_FreeCString(js, str);
|
||||
ret = arr;
|
||||
)
|
||||
|
||||
// Create UTF-8 string from codepoints
|
||||
JSC_CCALL(utf8_from_codepoints,
|
||||
int len = JS_ArrayLength(js, argv[0]);
|
||||
|
||||
// Allocate buffer (worst case: 4 bytes per codepoint + null)
|
||||
char *buffer = malloc(len * 4 + 1);
|
||||
char *ptr = buffer;
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
JSValue val = JS_GetPropertyUint32(js, argv[0], i);
|
||||
int codepoint;
|
||||
JS_ToInt32(js, &codepoint, val);
|
||||
JS_FreeValue(js, val);
|
||||
|
||||
encode_utf8(&ptr, codepoint);
|
||||
}
|
||||
|
||||
*ptr = '\0';
|
||||
ret = JS_NewString(js, buffer);
|
||||
free(buffer);
|
||||
)
|
||||
|
||||
// Count UTF-8 characters (runes) in a string
|
||||
JSC_SCALL(utf8_length,
|
||||
int count = utf8_count(str);
|
||||
ret = JS_NewInt32(js, count);
|
||||
)
|
||||
|
||||
// Validate UTF-8 string
|
||||
JSC_SCALL(utf8_validate,
|
||||
char *ptr = (char*)str;
|
||||
int valid = 1;
|
||||
|
||||
while (*ptr) {
|
||||
int start_pos = ptr - str;
|
||||
int codepoint = decode_utf8(&ptr);
|
||||
|
||||
// Check for invalid sequences
|
||||
if (codepoint < 0 || codepoint > 0x10FFFF ||
|
||||
(codepoint >= 0xD800 && codepoint <= 0xDFFF)) {
|
||||
valid = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
// Check for overlong encodings
|
||||
int bytes_used = ptr - (str + start_pos);
|
||||
if ((codepoint <= 0x7F && bytes_used != 1) ||
|
||||
(codepoint <= 0x7FF && bytes_used != 2) ||
|
||||
(codepoint <= 0xFFFF && bytes_used != 3) ||
|
||||
(codepoint <= 0x10FFFF && bytes_used != 4)) {
|
||||
valid = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ret = JS_NewBool(js, valid);
|
||||
)
|
||||
|
||||
// Get byte length of UTF-8 string
|
||||
JSC_SCALL(utf8_byte_length,
|
||||
ret = JS_NewInt32(js, strlen(str));
|
||||
)
|
||||
|
||||
// Encode string to UTF-8 bytes
|
||||
JSC_SCALL(utf8_encode,
|
||||
size_t len = strlen(str);
|
||||
ret = js_new_blob_stoned_copy(js, str, len);
|
||||
)
|
||||
|
||||
// Decode UTF-8 bytes to string
|
||||
JSC_CCALL(utf8_decode,
|
||||
size_t len;
|
||||
void *data = js_get_blob_data(js, &len, argv[0]);
|
||||
if (!data) return JS_ThrowTypeError(js, "Expected blob");
|
||||
|
||||
// Create null-terminated string
|
||||
char *str = malloc(len + 1);
|
||||
memcpy(str, data, len);
|
||||
str[len] = '\0';
|
||||
|
||||
ret = JS_NewString(js, str);
|
||||
free(str);
|
||||
)
|
||||
|
||||
// Slice UTF-8 string by character indices (not byte indices)
|
||||
JSC_CCALL(utf8_slice,
|
||||
const char *str = JS_ToCString(js, argv[0]);
|
||||
if (!str) return JS_EXCEPTION;
|
||||
|
||||
int start = 0;
|
||||
int end = utf8_count(str);
|
||||
|
||||
if (argc > 1) JS_ToInt32(js, &start, argv[1]);
|
||||
if (argc > 2) JS_ToInt32(js, &end, argv[2]);
|
||||
|
||||
// Handle negative indices
|
||||
int total = end;
|
||||
if (start < 0) start = total + start;
|
||||
if (end < 0) end = total + end;
|
||||
|
||||
// Clamp values
|
||||
if (start < 0) start = 0;
|
||||
if (end > total) end = total;
|
||||
if (start >= end) {
|
||||
JS_FreeCString(js, str);
|
||||
return JS_NewString(js, "");
|
||||
}
|
||||
|
||||
// Find start position
|
||||
char *ptr = (char*)str;
|
||||
for (int i = 0; i < start && *ptr; i++) {
|
||||
decode_utf8(&ptr);
|
||||
}
|
||||
char *start_ptr = ptr;
|
||||
|
||||
// Find end position
|
||||
for (int i = start; i < end && *ptr; i++) {
|
||||
decode_utf8(&ptr);
|
||||
}
|
||||
|
||||
// Create substring
|
||||
size_t slice_len = ptr - start_ptr;
|
||||
char *slice = malloc(slice_len + 1);
|
||||
memcpy(slice, start_ptr, slice_len);
|
||||
slice[slice_len] = '\0';
|
||||
|
||||
ret = JS_NewString(js, slice);
|
||||
free(slice);
|
||||
JS_FreeCString(js, str);
|
||||
)
|
||||
|
||||
// Get character at index
|
||||
JSC_CCALL(utf8_char_at,
|
||||
const char *str = JS_ToCString(js, argv[0]);
|
||||
if (!str) return JS_EXCEPTION;
|
||||
|
||||
int index;
|
||||
JS_ToInt32(js, &index, argv[1]);
|
||||
|
||||
char *ptr = (char*)str;
|
||||
int count = 0;
|
||||
|
||||
// Skip to index
|
||||
while (*ptr && count < index) {
|
||||
decode_utf8(&ptr);
|
||||
count++;
|
||||
}
|
||||
|
||||
if (!*ptr || count != index) {
|
||||
JS_FreeCString(js, str);
|
||||
return JS_NULL;
|
||||
}
|
||||
|
||||
// Get the character
|
||||
char *char_start = ptr;
|
||||
decode_utf8(&ptr);
|
||||
|
||||
size_t char_len = ptr - char_start;
|
||||
char *result = malloc(char_len + 1);
|
||||
memcpy(result, char_start, char_len);
|
||||
result[char_len] = '\0';
|
||||
|
||||
ret = JS_NewString(js, result);
|
||||
free(result);
|
||||
JS_FreeCString(js, str);
|
||||
)
|
||||
|
||||
static const JSCFunctionListEntry js_utf8_funcs[] = {
|
||||
MIST_FUNC_DEF(utf8, codepoints, 1),
|
||||
MIST_FUNC_DEF(utf8, from_codepoints, 1),
|
||||
MIST_FUNC_DEF(utf8, length, 1),
|
||||
MIST_FUNC_DEF(utf8, validate, 1),
|
||||
MIST_FUNC_DEF(utf8, byte_length, 1),
|
||||
MIST_FUNC_DEF(utf8, encode, 1),
|
||||
MIST_FUNC_DEF(utf8, decode, 1),
|
||||
MIST_FUNC_DEF(utf8, slice, 3),
|
||||
MIST_FUNC_DEF(utf8, char_at, 2),
|
||||
};
|
||||
|
||||
JSValue js_utf8_use(JSContext *js)
|
||||
{
|
||||
JSValue mod = JS_NewObject(js);
|
||||
JS_SetPropertyFunctionList(js, mod, js_utf8_funcs, countof(js_utf8_funcs));
|
||||
return mod;
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
#include "cell.h"
|
||||
#include "wildmatch.h"
|
||||
#include "qjs_macros.h"
|
||||
|
||||
JSC_CCALL(wildstar_match,
|
||||
const char *pattern = JS_ToCString(js, argv[0]);
|
||||
const char *string = JS_ToCString(js, argv[1]);
|
||||
int flags = 0;
|
||||
if (argc > 2)
|
||||
flags = js2number(js, argv[2]);
|
||||
|
||||
int result = wildmatch(pattern, string, flags);
|
||||
|
||||
JS_FreeCString(js, pattern);
|
||||
JS_FreeCString(js, string);
|
||||
|
||||
return JS_NewBool(js, result == WM_MATCH);
|
||||
)
|
||||
|
||||
static const JSCFunctionListEntry js_wildstar_funcs[] = {
|
||||
MIST_FUNC_DEF(wildstar, match, 3),
|
||||
JS_PROP_INT32_DEF("WM_MATCH", WM_MATCH, JS_PROP_CONFIGURABLE),
|
||||
JS_PROP_INT32_DEF("WM_NOMATCH", WM_NOMATCH, JS_PROP_CONFIGURABLE),
|
||||
JS_PROP_INT32_DEF("WM_NOESCAPE", WM_NOESCAPE, JS_PROP_CONFIGURABLE),
|
||||
JS_PROP_INT32_DEF("WM_PATHNAME", WM_PATHNAME, JS_PROP_CONFIGURABLE),
|
||||
JS_PROP_INT32_DEF("WM_PERIOD", WM_PERIOD, JS_PROP_CONFIGURABLE),
|
||||
JS_PROP_INT32_DEF("WM_LEADING_DIR", WM_LEADING_DIR, JS_PROP_CONFIGURABLE),
|
||||
JS_PROP_INT32_DEF("WM_CASEFOLD", WM_CASEFOLD, JS_PROP_CONFIGURABLE),
|
||||
JS_PROP_INT32_DEF("WM_WILDSTAR", WM_WILDSTAR, JS_PROP_CONFIGURABLE),
|
||||
};
|
||||
|
||||
JSValue js_wildstar_use(JSContext *js) {
|
||||
JSValue mod = JS_NewObject(js);
|
||||
JS_SetPropertyFunctionList(js, mod, js_wildstar_funcs, countof(js_wildstar_funcs));
|
||||
return mod;
|
||||
}
|
||||
Reference in New Issue
Block a user