add dmon and nota into source tree, out of subprojects; build on macos
All checks were successful
Build and Deploy / build-linux (push) Successful in 1m9s
Build and Deploy / build-windows (CLANG64) (push) Successful in 33m33s
Build and Deploy / package-dist (push) Has been skipped
Build and Deploy / deploy-itch (push) Has been skipped
Build and Deploy / deploy-gitea (push) Has been skipped
All checks were successful
Build and Deploy / build-linux (push) Successful in 1m9s
Build and Deploy / build-windows (CLANG64) (push) Successful in 33m33s
Build and Deploy / package-dist (push) Has been skipped
Build and Deploy / deploy-itch (push) Has been skipped
Build and Deploy / deploy-gitea (push) Has been skipped
This commit is contained in:
24
meson.build
24
meson.build
@@ -39,7 +39,24 @@ deps = []
|
||||
|
||||
if host_machine.system() == 'darwin'
|
||||
add_project_arguments('-x', 'objective-c', language: 'c')
|
||||
fworks = ['foundation', 'metal', 'audiotoolbox', 'metalkit', 'avfoundation', 'quartzcore', 'cocoa']
|
||||
fworks = [
|
||||
'foundation',
|
||||
'metal',
|
||||
'audiotoolbox',
|
||||
'metalkit',
|
||||
'avfoundation',
|
||||
'quartzcore',
|
||||
'cocoa',
|
||||
'coreaudio',
|
||||
'coremedia',
|
||||
'gamecontroller',
|
||||
'forcefeedback',
|
||||
'iokit',
|
||||
'corefoundation',
|
||||
'corehaptics',
|
||||
'carbon',
|
||||
'uniformtypeidentifiers'
|
||||
]
|
||||
foreach fkit : fworks
|
||||
deps += dependency('appleframeworks', modules: fkit)
|
||||
endforeach
|
||||
@@ -106,7 +123,6 @@ if storefront == 'steam'
|
||||
endif
|
||||
|
||||
deps += dependency('qjs-layout',static:true)
|
||||
deps += dependency('qjs-nota',static:true)
|
||||
deps += dependency('qjs-miniz',static:true)
|
||||
deps += dependency('qjs-soloud',static:true)
|
||||
deps += dependency('physfs', static:true)
|
||||
@@ -127,7 +143,7 @@ if get_option('enet')
|
||||
endif
|
||||
|
||||
sources = []
|
||||
src += ['anim.c', 'config.c', 'datastream.c','font.c','HandmadeMath.c','jsffi.c','model.c','render.c','script.c','simplex.c','spline.c', 'timer.c', 'transform.c','prosperon.c', 'wildmatch.c', 'sprite.c', 'rtree.c']
|
||||
src += ['anim.c', 'config.c', 'datastream.c','font.c','HandmadeMath.c','jsffi.c','model.c','render.c','script.c','simplex.c','spline.c', 'timer.c', 'transform.c','prosperon.c', 'wildmatch.c', 'sprite.c', 'rtree.c', 'qjs_dmon.c', 'qjs_nota.c']
|
||||
|
||||
imsrc = ['GraphEditor.cpp','ImCurveEdit.cpp','ImGradient.cpp','imgui_draw.cpp','imgui_tables.cpp','imgui_widgets.cpp','imgui.cpp','ImGuizmo.cpp','imnodes.cpp','implot_items.cpp','implot.cpp', 'imgui_impl_sdlrenderer3.cpp', 'imgui_impl_sdl3.cpp', 'imgui_impl_sdlgpu3.cpp']
|
||||
|
||||
@@ -146,8 +162,6 @@ if get_option('editor')
|
||||
foreach imgui : imsrc
|
||||
sources += tp / 'imgui' / imgui
|
||||
endforeach
|
||||
# sub_dmon = subproject('qjs-dmon')
|
||||
# dmon_dep = sub_dmon.get_variable('qjs_dmon_dep')
|
||||
endif
|
||||
|
||||
includers = []
|
||||
|
||||
1748
source/dmon.h
Normal file
1748
source/dmon.h
Normal file
File diff suppressed because it is too large
Load Diff
@@ -7584,8 +7584,12 @@ MISTUSE(video)
|
||||
MISTUSE(event)
|
||||
MISTUSE(camera)
|
||||
MISTUSE(debug)
|
||||
|
||||
JSValue js_imgui_use(JSContext *js);
|
||||
|
||||
#include "qjs_dmon.h"
|
||||
#include "qjs_nota.h"
|
||||
|
||||
#define MISTLINE(NAME) (ModuleEntry){#NAME, js_##NAME##_use}
|
||||
|
||||
void ffi_load(JSContext *js, int argc, char **argv) {
|
||||
@@ -7607,6 +7611,9 @@ void ffi_load(JSContext *js, int argc, char **argv) {
|
||||
arrput(module_registry, MISTLINE(imgui));
|
||||
arrput(module_registry, MISTLINE(camera));
|
||||
arrput(module_registry, MISTLINE(debug));
|
||||
arrput(module_registry, MISTLINE(dmon));
|
||||
arrput(module_registry, MISTLINE(nota));
|
||||
|
||||
#ifdef TRACY_ENABLE
|
||||
arrput(module_registry, MISTLINE(tracy));
|
||||
#endif
|
||||
|
||||
122
source/kim.h
Executable file
122
source/kim.h
Executable file
@@ -0,0 +1,122 @@
|
||||
#ifndef KIM_H
|
||||
#define KIM_H
|
||||
|
||||
// write number of runes from a kim stream int a utf8 stream
|
||||
void utf8_to_kim(const char **utf, char **kim);
|
||||
|
||||
// write number of runes from a kim stream int a utf8 stream
|
||||
void kim_to_utf8(char **kim, char **utf, int runes);
|
||||
|
||||
// Return the number of bytes a given utf-8 rune will have
|
||||
int utf8_bytes(char c);
|
||||
|
||||
// Return the number of runes in a utf8 string
|
||||
int utf8_count(const char *utf8);
|
||||
|
||||
#ifdef KIM_IMPLEMENTATION
|
||||
|
||||
#define KIM_CONT 0x80
|
||||
#define KIM_DATA 0x7f
|
||||
#define CONTINUE(CHAR) (CHAR>>7)
|
||||
|
||||
int decode_utf8(char **s);
|
||||
void encode_utf8(char **s, int code);
|
||||
static void encode_kim(char **s, int code);
|
||||
int decode_kim(char **s);
|
||||
|
||||
int utf8_bytes(char c)
|
||||
{
|
||||
int bytes = __builtin_clz(~(c));
|
||||
if (!bytes) return 1;
|
||||
return bytes-24;
|
||||
}
|
||||
|
||||
int utf8_count(const char *utf8)
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
while(*utf8) {
|
||||
count++;
|
||||
utf8 += utf8_bytes(*utf8);
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
// decode and advance s, returning the character rune
|
||||
int decode_utf8(char **s) {
|
||||
int k = **s ? __builtin_clz(~(**s << 24)) : 0; // Count # of leading 1 bits.
|
||||
int mask = (1 << (8 - k)) - 1; // All 1's with k leading 0's.
|
||||
int value = **s & mask;
|
||||
for (++(*s), --k; k > 0 && **s; --k, ++(*s)) { // Note that k = #total bytes, or 0.
|
||||
value <<= 6;
|
||||
value += (**s & 0x3F);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
// Write and advance s with rune in utf-8
|
||||
void encode_utf8(char **s, int rune) {
|
||||
char val[4];
|
||||
int lead_byte_max = 0x7F;
|
||||
int val_index = 0;
|
||||
while (rune > lead_byte_max) {
|
||||
val[val_index++] = (rune & 0x3F) | 0x80;
|
||||
rune >>= 6;
|
||||
lead_byte_max >>= (val_index == 1 ? 2 : 1);
|
||||
}
|
||||
val[val_index++] = (rune & lead_byte_max) | (~lead_byte_max << 1);
|
||||
while (val_index--) {
|
||||
**s = val[val_index];
|
||||
(*s)++;
|
||||
}
|
||||
}
|
||||
|
||||
// write and advance s with rune in kim
|
||||
static inline void encode_kim(char **s, int rune)
|
||||
{
|
||||
if (rune < KIM_CONT) {
|
||||
**s = 0 | (KIM_DATA & rune);
|
||||
(*s)++;
|
||||
return;
|
||||
}
|
||||
|
||||
int bits = ((32 - __builtin_clz(rune) + 6) / 7) * 7;
|
||||
|
||||
while (bits > 7) {
|
||||
bits -= 7;
|
||||
**s = KIM_CONT | KIM_DATA & (rune >> bits);
|
||||
(*s)++;
|
||||
}
|
||||
**s = KIM_DATA & rune;
|
||||
(*s)++;
|
||||
}
|
||||
|
||||
// decode and advance s, returning the character rune
|
||||
int decode_kim(char **s)
|
||||
{
|
||||
int rune = **s & KIM_DATA;
|
||||
while (CONTINUE(**s)) {
|
||||
rune <<= 7;
|
||||
(*s)++;
|
||||
rune |= **s & KIM_DATA;
|
||||
}
|
||||
(*s)++;
|
||||
return rune;
|
||||
}
|
||||
|
||||
void utf8_to_kim(const char **utf, char **kim)
|
||||
{
|
||||
char * str = *utf;
|
||||
while (*str)
|
||||
encode_kim(kim, decode_utf8(&str));
|
||||
}
|
||||
|
||||
void kim_to_utf8(char **kim, char **utf, int runes)
|
||||
{
|
||||
for (int i = 0; i < runes; i++)
|
||||
encode_utf8(utf, decode_kim(kim));
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
357
source/nota.h
Executable file
357
source/nota.h
Executable file
@@ -0,0 +1,357 @@
|
||||
#ifndef NOTA_H
|
||||
#define NOTA_H
|
||||
|
||||
#define NOTA_BLOB 0x00 // C 0 0 0
|
||||
#define NOTA_TEXT 0x10 //
|
||||
#define NOTA_ARR 0x20 // 0 1 0
|
||||
#define NOTA_REC 0x30 // C 0 1 1
|
||||
#define NOTA_FLOAT 0x40 // C 1 0
|
||||
#define NOTA_INT 0x60 // C 1 1 0
|
||||
#define NOTA_SYM 0x70 // C 1 1 1
|
||||
|
||||
#define NOTA_FALSE 0x00
|
||||
#define NOTA_TRUE 0x01
|
||||
#define NOTA_NULL 0x02
|
||||
#define NOTA_INF 0x03
|
||||
#define NOTA_PRIVATE 0x08
|
||||
#define NOTA_SYSTEM 0x09
|
||||
|
||||
// Returns the type NOTA_ of the byte at *nota
|
||||
int nota_type(char *nota);
|
||||
|
||||
// Functions take a pointer to a buffer *nota, read or write the value, and then return a pointer to the next byte of the stream
|
||||
|
||||
// Pass NULL into the read in variable to skip over it
|
||||
|
||||
char *nota_read_blob(long long *len, char *nota);
|
||||
// ALLOCATES! Uses strdup to return it via the text pointer
|
||||
char *nota_read_text(char **text, char *nota);
|
||||
char *nota_read_array(long long *len, char *nota);
|
||||
char *nota_read_record(long long *len, char *nota);
|
||||
char *nota_read_float(double *d, char *nota);
|
||||
char *nota_read_int(long long *l, char *nota);
|
||||
char *nota_read_sym(int *sym, char *nota);
|
||||
|
||||
char *nota_write_blob(unsigned long long n, char *nota);
|
||||
char *nota_write_text(const char *s, char *nota);
|
||||
char *nota_write_array(unsigned long long n, char *nota);
|
||||
char *nota_write_record(unsigned long long n, char *nota);
|
||||
char *nota_write_number(double n, char *nota);
|
||||
char *nota_write_sym(int sym, char *nota);
|
||||
|
||||
void print_nota_hex(char *nota);
|
||||
|
||||
#ifdef NOTA_IMPLEMENTATION
|
||||
|
||||
#include "stdio.h"
|
||||
#include "math.h"
|
||||
#include "string.h"
|
||||
#include "stdlib.h"
|
||||
#include "limits.h"
|
||||
#include "kim.h"
|
||||
|
||||
#define NOTA_CONT 0x80
|
||||
#define NOTA_DATA 0x7f
|
||||
#define NOTA_INT_DATA 0x07
|
||||
#define NOTA_INT_SIGN(CHAR) (CHAR & (1<<3))
|
||||
#define NOTA_SIG_SIGN(CHAR) (CHAR & (1<<3))
|
||||
#define NOTA_EXP_SIGN(CHAR) (CHAR & (1<<4))
|
||||
#define NOTA_TYPE 0x70
|
||||
#define NOTA_HEAD_DATA 0x0f
|
||||
#define CONTINUE(CHAR) (CHAR>>7)
|
||||
|
||||
#define UTF8_DATA 0x3f
|
||||
|
||||
/* define this to use native string instead of kim. Bytes are encoded instead of runes */
|
||||
#define NOTA_UTF8
|
||||
|
||||
int nota_type(char *nota) { return *nota & NOTA_TYPE; }
|
||||
|
||||
char *nota_skip(char *nota)
|
||||
{
|
||||
while (CONTINUE(*nota))
|
||||
nota++;
|
||||
|
||||
return nota+1;
|
||||
}
|
||||
|
||||
char *nota_read_num(long long *n, char *nota)
|
||||
{
|
||||
if (!n)
|
||||
return nota_skip(nota);
|
||||
|
||||
*n = 0;
|
||||
*n |= (*nota) & NOTA_HEAD_DATA;
|
||||
|
||||
while (CONTINUE(*(nota++)))
|
||||
*n = (*n<<7) | (*nota) & NOTA_DATA;
|
||||
|
||||
return nota;
|
||||
}
|
||||
|
||||
// Given a number n, and bits used in the first char sb, how many bits are needed
|
||||
int nota_bits(long long n, int sb)
|
||||
{
|
||||
if (n == 0) return sb;
|
||||
int bits = sizeof(n)*CHAR_BIT - __builtin_clzll(n);
|
||||
bits-=sb;
|
||||
int needed = ((bits + 6) / 7)*7 + sb;
|
||||
return needed;
|
||||
}
|
||||
|
||||
// write a number from n into *nota, with sb bits in the first char
|
||||
char *nota_continue_num(long long n, char *nota, int sb)
|
||||
{
|
||||
int bits = nota_bits(n, sb);
|
||||
bits -= sb;
|
||||
if (bits > 0)
|
||||
nota[0] |= NOTA_CONT;
|
||||
else
|
||||
nota[0] &= ~NOTA_CONT;
|
||||
|
||||
int shex = (~0) << sb;
|
||||
nota[0] &= shex; /* clear shex bits */
|
||||
nota[0] |= (~shex) & (n>>bits);
|
||||
|
||||
int i = 1;
|
||||
while (bits > 0) {
|
||||
bits -= 7;
|
||||
int head = bits == 0 ? 0 : NOTA_CONT;
|
||||
nota[i] = head | (NOTA_DATA & (n >> bits));
|
||||
i++;
|
||||
}
|
||||
|
||||
return ¬a[i];
|
||||
}
|
||||
|
||||
|
||||
void print_nota_hex(char *nota)
|
||||
{
|
||||
do {
|
||||
printf("%02X ", (unsigned char)(*nota));
|
||||
} while(CONTINUE(*(nota++)));
|
||||
printf("\n");
|
||||
|
||||
return;
|
||||
long long chars = 0;
|
||||
if (!((*nota>>4 & 0x07) ^ NOTA_TEXT>>4))
|
||||
nota_read_num(&chars, nota);
|
||||
|
||||
if ((*nota>>5) == 2 || (*nota>>5) == 6)
|
||||
chars = 1;
|
||||
|
||||
for (int i = 0; i < chars+1; i++) {
|
||||
do {
|
||||
printf("%02X ", (unsigned char)(*nota));
|
||||
} while(CONTINUE(*(nota++)));
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
char *nota_write_int(long long n, char *nota)
|
||||
{
|
||||
char sign = 0;
|
||||
|
||||
if (n < 0) {
|
||||
sign = 0x08;
|
||||
n *= -1;
|
||||
}
|
||||
|
||||
*nota = NOTA_INT | sign;
|
||||
|
||||
return nota_continue_num(n, nota, 3);
|
||||
}
|
||||
|
||||
#define NOTA_DBL_PREC 6
|
||||
#define xstr(s) str(s)
|
||||
#define str(s) #s
|
||||
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
|
||||
void extract_mantissa_coefficient(double num, long *mantissa, long* coefficient) {
|
||||
char buf[64];
|
||||
char *p, *dec_point;
|
||||
int exp = 0, coeff = 0;
|
||||
|
||||
// Convert double to string with maximum precision
|
||||
snprintf(buf, sizeof(buf), "%.17g", num);
|
||||
|
||||
// Find if 'e' or 'E' is present (scientific notation)
|
||||
p = strchr(buf, 'e');
|
||||
if (!p) p = strchr(buf, 'E');
|
||||
if (p) {
|
||||
// There is an exponent part
|
||||
exp = atol(p + 1);
|
||||
*p = '\0'; // Remove exponent part from the string
|
||||
}
|
||||
|
||||
// Find decimal point
|
||||
dec_point = strchr(buf, '.');
|
||||
if (dec_point) {
|
||||
// Count number of digits after decimal point
|
||||
int digits_after_point = strlen(dec_point + 1);
|
||||
coeff = digits_after_point;
|
||||
// Remove decimal point by shifting characters
|
||||
memmove(dec_point, dec_point + 1, strlen(dec_point));
|
||||
} else
|
||||
coeff = 0;
|
||||
|
||||
// Adjust coefficient with exponent from scientific notation
|
||||
coeff -= exp;
|
||||
|
||||
// Copy the mantissa
|
||||
*mantissa = atol(buf);
|
||||
|
||||
// Set coefficient
|
||||
*coefficient = coeff;
|
||||
}
|
||||
|
||||
char *nota_write_float(double n, char *nota)
|
||||
{
|
||||
int neg = n < 0;
|
||||
long digits;
|
||||
long coef;
|
||||
extract_mantissa_coefficient(n, &digits, &coef);
|
||||
|
||||
printf("Values of %g are %ld e %ld\n", n, digits, coef);
|
||||
if (coef == 0)
|
||||
return nota_write_int(coef * (neg ? -1 : 1), nota);
|
||||
|
||||
int expsign = coef < 0 ? ~0 : 0;
|
||||
coef = llabs(coef);
|
||||
|
||||
nota[0] = NOTA_FLOAT;
|
||||
nota[0] |= 0x10 & expsign;
|
||||
nota[0] |= 0x08 & neg;
|
||||
|
||||
char *c = nota_continue_num(coef, nota, 3);
|
||||
|
||||
return nota_continue_num(digits, c, 7);
|
||||
}
|
||||
|
||||
char *nota_read_float(double *d, char *nota)
|
||||
{
|
||||
long long sig = 0;
|
||||
long long e = 0;
|
||||
|
||||
char *c = nota;
|
||||
e = (*c) & NOTA_INT_DATA; /* first three bits */
|
||||
|
||||
while (CONTINUE(*c)) {
|
||||
e = (e<<7) | (*c) & NOTA_DATA;
|
||||
c++;
|
||||
}
|
||||
|
||||
c++;
|
||||
|
||||
do
|
||||
sig = (sig<<7) | *c & NOTA_DATA;
|
||||
while (CONTINUE(*(c++)));
|
||||
|
||||
if (NOTA_SIG_SIGN(*nota)) sig *= -1;
|
||||
if (NOTA_EXP_SIGN(*nota)) e *= -1;
|
||||
|
||||
*d = (double)sig * pow(10.0, e);
|
||||
return c;
|
||||
}
|
||||
|
||||
char *nota_write_number(double n, char *nota)
|
||||
{
|
||||
if (n < (double)INT64_MIN || n > (double)INT64_MAX) return nota_write_float(n, nota);
|
||||
if (floor(n) == n)
|
||||
return nota_write_int(n, nota);
|
||||
return nota_write_float(n, nota);
|
||||
}
|
||||
|
||||
char *nota_read_int(long long *n, char *nota)
|
||||
{
|
||||
if (!n)
|
||||
return nota_skip(nota);
|
||||
|
||||
*n = 0;
|
||||
char *c = nota;
|
||||
*n |= (*c) & NOTA_INT_DATA; /* first three bits */
|
||||
while (CONTINUE(*(c++)))
|
||||
*n = (*n<<7) | (*c) & NOTA_DATA;
|
||||
|
||||
if (NOTA_INT_SIGN(*nota)) *n *= -1;
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
/* n is the number of bits */
|
||||
char *nota_write_blob(unsigned long long n, char *nota)
|
||||
{
|
||||
nota[0] = NOTA_BLOB;
|
||||
return nota_continue_num(n, nota, 4);
|
||||
}
|
||||
|
||||
char *nota_write_array(unsigned long long n, char *nota)
|
||||
{
|
||||
nota[0] = NOTA_ARR;
|
||||
return nota_continue_num(n, nota, 4);
|
||||
}
|
||||
|
||||
char *nota_read_array(long long *len, char *nota)
|
||||
{
|
||||
if (!len) return nota;
|
||||
return nota_read_num(len, nota);
|
||||
}
|
||||
|
||||
char *nota_read_record(long long *len, char *nota)
|
||||
{
|
||||
if (!len) return nota;
|
||||
return nota_read_num(len, nota);
|
||||
}
|
||||
|
||||
char *nota_read_blob(long long *len, char *nota)
|
||||
{
|
||||
if (!len) return nota;
|
||||
return nota_read_num(len, nota);
|
||||
}
|
||||
|
||||
char *nota_write_record(unsigned long long n, char *nota)
|
||||
{
|
||||
nota[0] = NOTA_REC;
|
||||
return nota_continue_num(n, nota, 4);
|
||||
}
|
||||
|
||||
char *nota_write_sym(int sym, char *nota)
|
||||
{
|
||||
*nota = NOTA_SYM | sym;
|
||||
return nota+1;
|
||||
}
|
||||
|
||||
char *nota_read_sym(int *sym, char *nota)
|
||||
{
|
||||
if (*sym) *sym = (*nota) & 0x0f;
|
||||
return nota+1;
|
||||
}
|
||||
|
||||
char *nota_read_text(char **text, char *nota)
|
||||
{
|
||||
long long chars;
|
||||
nota = nota_read_num(&chars, nota);
|
||||
|
||||
char utf[chars*4]; // enough for the worst case scenario
|
||||
char *pp = utf;
|
||||
kim_to_utf8(¬a, &pp, chars);
|
||||
*pp = 0;
|
||||
*text = strdup(utf);
|
||||
|
||||
return nota;
|
||||
}
|
||||
|
||||
char *nota_write_text(const char *s, char *nota)
|
||||
{
|
||||
nota[0] = NOTA_TEXT;
|
||||
long long n = utf8_count(s);
|
||||
nota = nota_continue_num(n,nota,4);
|
||||
utf8_to_kim(&s, ¬a);
|
||||
return nota;
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
150
source/qjs_dmon.c
Normal file
150
source/qjs_dmon.c
Normal file
@@ -0,0 +1,150 @@
|
||||
#include "quickjs.h"
|
||||
|
||||
#define DMON_IMPL
|
||||
#include "dmon.h"
|
||||
|
||||
// Define the file event structure and completion queue
|
||||
typedef struct {
|
||||
dmon_action action;
|
||||
char rootdir[256];
|
||||
char filepath[256];
|
||||
char oldfilepath[256];
|
||||
} FileEvent;
|
||||
|
||||
typedef struct EventNode {
|
||||
FileEvent event;
|
||||
struct EventNode *next;
|
||||
} EventNode;
|
||||
|
||||
typedef struct {
|
||||
EventNode *head;
|
||||
EventNode *tail;
|
||||
} CompletionQueue;
|
||||
|
||||
CompletionQueue completionQueue = { NULL, NULL };
|
||||
|
||||
// Helper functions for the completion queue
|
||||
void enqueue_event(FileEvent event) {
|
||||
EventNode *node = malloc(sizeof(EventNode));
|
||||
node->event = event;
|
||||
node->next = NULL;
|
||||
|
||||
if (completionQueue.tail) {
|
||||
completionQueue.tail->next = node;
|
||||
} else {
|
||||
completionQueue.head = node;
|
||||
}
|
||||
completionQueue.tail = node;
|
||||
}
|
||||
|
||||
int dequeue_event(FileEvent *event) {
|
||||
if (!completionQueue.head) {
|
||||
return 0; // No event
|
||||
}
|
||||
EventNode *node = completionQueue.head;
|
||||
*event = node->event;
|
||||
completionQueue.head = node->next;
|
||||
if (!completionQueue.head) {
|
||||
completionQueue.tail = NULL;
|
||||
}
|
||||
free(node);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void watch_cb(dmon_watch_id id, dmon_action action, const char *rootdir, const char *filepath, const char *oldfilepath, void *user)
|
||||
{
|
||||
FileEvent event;
|
||||
event.action = action;
|
||||
strncpy(event.rootdir, rootdir, sizeof(event.rootdir) - 1);
|
||||
strncpy(event.filepath, filepath, sizeof(event.filepath) - 1);
|
||||
if (oldfilepath) {
|
||||
strncpy(event.oldfilepath, oldfilepath, sizeof(event.oldfilepath) - 1);
|
||||
} else {
|
||||
event.oldfilepath[0] = '\0';
|
||||
}
|
||||
enqueue_event(event); // Add event to completion queue
|
||||
}
|
||||
|
||||
static dmon_watch_id watched = {0};
|
||||
|
||||
JSValue js_dmon_watch(JSContext *js, JSValue self, int argc, JSValue *argv)
|
||||
{
|
||||
if (watched.id)
|
||||
return JS_ThrowReferenceError(js, "Already watching a directory.");
|
||||
|
||||
const char *dir = JS_ToCString(js,argv[0]);
|
||||
watched = dmon_watch(dir,watch_cb, DMON_WATCHFLAGS_RECURSIVE, NULL);
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
JSValue js_dmon_unwatch(JSContext *js, JSValue self, int argc, JSValue *argv)
|
||||
{
|
||||
if (!watched.id)
|
||||
return JS_ThrowReferenceError(js, "Not watching a directory.");
|
||||
|
||||
dmon_unwatch(watched);
|
||||
watched.id = 0;
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
JSValue js_dmon_poll(JSContext *js, JSValueConst this_val, int argc, JSValueConst *argv) {
|
||||
FileEvent event;
|
||||
while (dequeue_event(&event)) {
|
||||
JSValue jsevent = JS_NewObject(js);
|
||||
JSValue action;
|
||||
switch(event.action) {
|
||||
case DMON_ACTION_CREATE:
|
||||
action = JS_NewAtomString(js, "create");
|
||||
break;
|
||||
case DMON_ACTION_DELETE:
|
||||
action = JS_NewAtomString(js, "delete");
|
||||
break;
|
||||
case DMON_ACTION_MODIFY:
|
||||
action = JS_NewAtomString(js, "modify");
|
||||
break;
|
||||
case DMON_ACTION_MOVE:
|
||||
action = JS_NewAtomString(js, "move");
|
||||
break;
|
||||
}
|
||||
JS_SetPropertyStr(js, jsevent, "action", action);
|
||||
JS_SetPropertyStr(js, jsevent, "root", JS_NewString(js, event.rootdir));
|
||||
JS_SetPropertyStr(js, jsevent, "file", JS_NewString(js, event.filepath));
|
||||
JS_SetPropertyStr(js, jsevent, "old", JS_NewString(js, event.oldfilepath));
|
||||
JS_Call(js, argv[0], JS_UNDEFINED, 1, &jsevent);
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
static const JSCFunctionListEntry js_dmon_funcs[] = {
|
||||
JS_CFUNC_DEF("watch", 1, js_dmon_watch),
|
||||
JS_CFUNC_DEF("unwatch", 0, js_dmon_unwatch),
|
||||
JS_CFUNC_DEF("poll", 1, js_dmon_poll)
|
||||
};
|
||||
|
||||
JSValue js_dmon_use(JSContext *js)
|
||||
{
|
||||
JSValue export = JS_NewObject(js);
|
||||
JS_SetPropertyFunctionList(js, export, js_dmon_funcs, sizeof(js_dmon_funcs)/sizeof(JSCFunctionListEntry));
|
||||
|
||||
dmon_init();
|
||||
|
||||
return export;
|
||||
}
|
||||
|
||||
static int js_dmon_init(JSContext *js, JSModuleDef *m) {
|
||||
JS_SetModuleExport(js, m, "default",js_dmon_use(js));
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef JS_SHARED_LIBRARY
|
||||
#define JS_INIT_MODULE js_init_module
|
||||
#else
|
||||
#define JS_INIT_MODULE js_init_module_dmon
|
||||
#endif
|
||||
|
||||
JSModuleDef *JS_INIT_MODULE(JSContext *js, const char *module_name) {
|
||||
JSModuleDef *m = JS_NewCModule(js, module_name, js_dmon_init);
|
||||
if (!m) return NULL;
|
||||
JS_AddModuleExport(js, m, "default");
|
||||
return m;
|
||||
}
|
||||
8
source/qjs_dmon.h
Normal file
8
source/qjs_dmon.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#ifndef QJS_DMON_H
|
||||
#define QJS_DMON_H
|
||||
|
||||
#include "quickjs.h"
|
||||
|
||||
JSValue js_dmon_use(JSContext *js);
|
||||
|
||||
#endif
|
||||
180
source/qjs_nota.c
Executable file
180
source/qjs_nota.c
Executable file
@@ -0,0 +1,180 @@
|
||||
#include "quickjs.h"
|
||||
|
||||
#define KIM_IMPLEMENTATION
|
||||
#define NOTA_IMPLEMENTATION
|
||||
#include "nota.h"
|
||||
|
||||
char *js_do_nota_decode(JSContext *js, JSValue *tmp, char *nota)
|
||||
{
|
||||
int type = nota_type(nota);
|
||||
JSValue ret2;
|
||||
long long n;
|
||||
double d;
|
||||
int b;
|
||||
char *str;
|
||||
|
||||
switch(type) {
|
||||
case NOTA_BLOB:
|
||||
break;
|
||||
case NOTA_TEXT:
|
||||
nota = nota_read_text(&str, nota);
|
||||
*tmp = JS_NewString(js, str);
|
||||
/* TODO: Avoid malloc and free here */
|
||||
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);
|
||||
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);
|
||||
nota = js_do_nota_decode(js, &ret2, nota);
|
||||
JS_SetPropertyStr(js, *tmp, str, ret2);
|
||||
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_NULL) *tmp = JS_UNDEFINED;
|
||||
else
|
||||
*tmp = JS_NewBool(js,b);
|
||||
break;
|
||||
default:
|
||||
case NOTA_FLOAT:
|
||||
nota = nota_read_float(&d, nota);
|
||||
*tmp = JS_NewFloat64(js,d);
|
||||
break;
|
||||
}
|
||||
|
||||
return nota;
|
||||
}
|
||||
|
||||
// Writers the JSValue v into the buffer of char *nota, returning a pointer to the next byte in nota to be written
|
||||
char *js_do_nota_encode(JSContext *js, JSValue v, char *nota)
|
||||
{
|
||||
int tag = JS_VALUE_GET_TAG(v);
|
||||
const char *str = NULL;
|
||||
JSPropertyEnum *ptab;
|
||||
uint32_t plen;
|
||||
int n;
|
||||
double nval;
|
||||
JSValue val;
|
||||
|
||||
switch(tag) {
|
||||
case JS_TAG_FLOAT64:
|
||||
case JS_TAG_INT:
|
||||
JS_ToFloat64(js, &nval, v);
|
||||
return nota_write_number(nval, nota);
|
||||
case JS_TAG_STRING:
|
||||
str = JS_ToCString(js, v);
|
||||
nota = nota_write_text(str, nota);
|
||||
JS_FreeCString(js, str);
|
||||
return nota;
|
||||
case JS_TAG_BOOL:
|
||||
return nota_write_sym(JS_VALUE_GET_BOOL(v), nota);
|
||||
case JS_TAG_UNDEFINED:
|
||||
return nota_write_sym(NOTA_NULL, nota);
|
||||
case JS_TAG_NULL:
|
||||
return nota_write_sym(NOTA_NULL, nota);
|
||||
case JS_TAG_OBJECT:
|
||||
if (JS_IsArray(js, v)) {
|
||||
int n;
|
||||
JS_ToInt32(js, &n, JS_GetPropertyStr(js, v, "length"));
|
||||
nota = nota_write_array(n, nota);
|
||||
for (int i = 0; i < n; i++)
|
||||
nota = js_do_nota_encode(js, JS_GetPropertyUint32(js, v, i), nota);
|
||||
return nota;
|
||||
}
|
||||
n = JS_GetOwnPropertyNames(js, &ptab, &plen, v, JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK);
|
||||
nota = nota_write_record(plen, nota);
|
||||
|
||||
for (int i = 0; i < plen; i++) {
|
||||
val = JS_GetProperty(js,v,ptab[i].atom);
|
||||
str = JS_AtomToCString(js, ptab[i].atom);
|
||||
JS_FreeAtom(js, ptab[i].atom);
|
||||
|
||||
nota = nota_write_text(str, nota);
|
||||
JS_FreeCString(js, str);
|
||||
|
||||
nota = js_do_nota_encode(js, val, nota);
|
||||
JS_FreeValue(js,val);
|
||||
}
|
||||
js_free(js, ptab);
|
||||
return nota;
|
||||
default:
|
||||
return nota;
|
||||
}
|
||||
return nota;
|
||||
}
|
||||
|
||||
JSValue js_nota_encode(JSContext *js, JSValue self, int argc, JSValue *argv)
|
||||
{
|
||||
if (argc < 1)
|
||||
JS_ThrowInternalError(js, "Expected at least one argument to encode.");
|
||||
|
||||
JSValue obj = argv[0];
|
||||
char nota[1024*1024];
|
||||
char *e = js_do_nota_encode(js, obj, nota);
|
||||
return JS_NewArrayBufferCopy(js, (unsigned char*)nota, e-nota);
|
||||
}
|
||||
|
||||
JSValue js_nota_decode(JSContext *js, JSValue self, int argc, JSValue *argv)
|
||||
{
|
||||
if (argc < 1) return JS_UNDEFINED;
|
||||
|
||||
size_t len;
|
||||
unsigned char *nota = JS_GetArrayBuffer(js, &len, argv[0]);
|
||||
JSValue ret;
|
||||
js_do_nota_decode(js, &ret, (char*)nota);
|
||||
return ret;
|
||||
}
|
||||
|
||||
JSValue js_nota_hex(JSContext *js, JSValue self, int argc, JSValue *argv)
|
||||
{
|
||||
size_t len;
|
||||
unsigned char *nota = JS_GetArrayBuffer(js, &len, argv[0]);
|
||||
print_nota_hex(nota);
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
static const JSCFunctionListEntry js_nota_funcs[] = {
|
||||
JS_CFUNC_DEF("encode", 1, js_nota_encode),
|
||||
JS_CFUNC_DEF("decode", 1, js_nota_decode),
|
||||
JS_CFUNC_DEF("hex", 1, js_nota_hex),
|
||||
};
|
||||
|
||||
static int js_nota_init(JSContext *ctx, JSModuleDef *m) {
|
||||
JS_SetModuleExportList(ctx, m, js_nota_funcs, sizeof(js_nota_funcs)/sizeof(JSCFunctionListEntry));
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
#ifdef JS_SHARED_LIBRARY
|
||||
#define JS_INIT_MODULE js_init_module
|
||||
#else
|
||||
#define JS_INIT_MODULE js_init_module_nota
|
||||
#endif
|
||||
|
||||
JSModuleDef *JS_INIT_MODULE(JSContext *ctx, const char *module_name) {
|
||||
JSModuleDef *m = JS_NewCModule(ctx, module_name, js_nota_init);
|
||||
if (!m) return NULL;
|
||||
JS_AddModuleExportList(ctx, m, js_nota_funcs, sizeof(js_nota_funcs)/sizeof(JSCFunctionListEntry));
|
||||
return m;
|
||||
}
|
||||
8
source/qjs_nota.h
Normal file
8
source/qjs_nota.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#ifndef QJS_NOTA_H
|
||||
#define QJS_NOTA_H
|
||||
|
||||
#include "quickjs.h"
|
||||
|
||||
JSValue js_nota_use(JSContext*);
|
||||
|
||||
#endif
|
||||
@@ -1,7 +0,0 @@
|
||||
[wrap-git]
|
||||
url = https://github.com/johnalanbrook/qjs-dmon.git
|
||||
revision = head
|
||||
depth = 1
|
||||
|
||||
[provide]
|
||||
qjs-dmon = qjs_dmon_dep
|
||||
@@ -1,7 +0,0 @@
|
||||
[wrap-git]
|
||||
url = https://github.com/johnalanbrook/cnota.git
|
||||
revision = head
|
||||
depth = 1
|
||||
|
||||
[provide]
|
||||
qjs-nota = qjs_nota_dep
|
||||
126
tests/nota.js
Normal file
126
tests/nota.js
Normal file
@@ -0,0 +1,126 @@
|
||||
var nota = use('nota');
|
||||
var os = use('os');
|
||||
|
||||
// Helper function to convert hex string to ArrayBuffer
|
||||
function hexToBuffer(hex) {
|
||||
let bytes = new Uint8Array(hex.length / 2);
|
||||
for (let i = 0; i < hex.length; i += 2) {
|
||||
bytes[i/2] = parseInt(hex.substr(i, 2), 16);
|
||||
}
|
||||
return bytes.buffer;
|
||||
}
|
||||
|
||||
// Helper function to convert ArrayBuffer to hex string
|
||||
function bufferToHex(buffer) {
|
||||
return Array.from(new Uint8Array(buffer))
|
||||
.map(b => b.toString(16).padStart(2, '0'))
|
||||
.join('')
|
||||
.toLowerCase();
|
||||
}
|
||||
|
||||
// Test cases
|
||||
var testCases = [
|
||||
{ input: 0, expectedHex: "60" },
|
||||
{ input: 2023, expectedHex: "e08f67" },
|
||||
{ input: -1, expectedHex: "69" },
|
||||
{ input: true, expectedHex: "71" },
|
||||
{ input: false, expectedHex: "72" },
|
||||
{ input: null, expectedHex: "73" },
|
||||
{ input: -1.01, expectedHex: "5a65" },
|
||||
{ input: 98.6, expectedHex: "51875a" },
|
||||
{ input: "", expectedHex: "10" },
|
||||
{ input: "cat", expectedHex: "13636174" },
|
||||
{ input: [], expectedHex: "20" },
|
||||
{ input: [1, 2, 3], expectedHex: "23616263" },
|
||||
{ input: {}, expectedHex: "30" },
|
||||
{ input: {a: 1, b: 2}, expectedHex: "32116161116262" },
|
||||
{ input: new Uint8Array([0xFF, 0xAA]).buffer, expectedHex: "020280ffaa" },
|
||||
{ input: {
|
||||
num: 42,
|
||||
arr: [1, -1, 2.5],
|
||||
str: "test",
|
||||
obj: {x: true}
|
||||
},
|
||||
expectedHex: "34216e756d622a2173747214746573742161727223616965235840216f626a21117873"
|
||||
}
|
||||
];
|
||||
|
||||
// Run tests and collect results
|
||||
let testCount = 0;
|
||||
let failedCount = 0;
|
||||
let results = [];
|
||||
|
||||
for (let test of testCases) {
|
||||
testCount++;
|
||||
let testName = `Test ${testCount}: ${JSON.stringify(test.input)}`;
|
||||
let passed = true;
|
||||
let messages = [];
|
||||
|
||||
console.log(`Running ${testName}`);
|
||||
|
||||
// Test encoding
|
||||
let encoded = nota.encode(test.input);
|
||||
if (!(encoded instanceof ArrayBuffer)) {
|
||||
passed = false;
|
||||
messages.push("Encode should return ArrayBuffer");
|
||||
} else {
|
||||
let encodedHex = bufferToHex(encoded);
|
||||
if (encodedHex !== test.expectedHex.toLowerCase()) {
|
||||
passed = false;
|
||||
messages.push(
|
||||
`Encoding failed\n` +
|
||||
`Expected: ${test.expectedHex}\n` +
|
||||
`Got: ${encodedHex}`
|
||||
);
|
||||
}
|
||||
|
||||
// Test decoding
|
||||
let decoded = nota.decode(encoded);
|
||||
let inputStr = JSON.stringify(test.input instanceof ArrayBuffer ?
|
||||
Array.from(new Uint8Array(test.input)) : test.input);
|
||||
let decodedStr = JSON.stringify(decoded);
|
||||
|
||||
if (inputStr !== decodedStr) {
|
||||
passed = false;
|
||||
messages.push(
|
||||
`Decoding failed\n` +
|
||||
`Expected: ${inputStr}\n` +
|
||||
`Got: ${decodedStr}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Record result
|
||||
let status = passed ? "PASSED" : "FAILED";
|
||||
results.push({ testName, status, messages });
|
||||
if (!passed) failedCount++;
|
||||
|
||||
// Print immediate feedback
|
||||
console.log(`${testName} - ${status}`);
|
||||
if (!passed) {
|
||||
console.log(messages.join("\n"));
|
||||
}
|
||||
console.log(""); // Empty line between tests
|
||||
}
|
||||
|
||||
// Summary
|
||||
console.log("Test Summary:");
|
||||
console.log(`Total tests: ${testCount}`);
|
||||
console.log(`Passed: ${testCount - failedCount}`);
|
||||
console.log(`Failed: ${failedCount}`);
|
||||
console.log("\nDetailed Results:");
|
||||
results.forEach(result => {
|
||||
console.log(`${result.testName} - ${result.status}`);
|
||||
if (result.messages.length > 0) {
|
||||
console.log(result.messages.join("\n"));
|
||||
console.log("");
|
||||
}
|
||||
});
|
||||
|
||||
if (failedCount > 0) {
|
||||
console.log("Overall result: FAILED");
|
||||
os.exit(1);
|
||||
} else {
|
||||
console.log("Overall result: PASSED");
|
||||
os.exit(0);
|
||||
}
|
||||
Reference in New Issue
Block a user