script folder

This commit is contained in:
2025-11-26 20:25:00 -06:00
parent e28e241485
commit b577e889a1
23 changed files with 29 additions and 120 deletions

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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, well 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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
// well 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 URLsafe, 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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}